Skip to main content

tenzro_types/
resource.rs

1//! Unified resource discovery + invocation type.
2//!
3//! Tenzro Network's resource registries (tools, skills, knowledge,
4//! workflow templates, agent templates, models) are each first-class
5//! catalogs with their own type model. The discovery layer collapses
6//! them into a single shape so an agent can ask "what resources match
7//! this filter?" once and pick from a single result set.
8//!
9//! The `ResourceDescriptor` is the cross-registry projection. It
10//! carries enough metadata for an agent to decide whether to use the
11//! resource — class, name, capabilities, price, reputation — and
12//! enough to invoke it via `tenzro_useResource(resource_id, params)`.
13
14use crate::primitives::Address;
15use serde::{Deserialize, Serialize};
16
17/// Which registry a resource lives in. Drives the dispatch target
18/// for `tenzro_useResource`.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)]
20#[serde(rename_all = "snake_case")]
21pub enum ResourceClass {
22    /// `CF_TOOLS` — invocable MCP servers, API tools, native ops.
23    Tool,
24    /// `CF_SKILLS` — declarative capability descriptors.
25    Skill,
26    /// `CF_KNOWLEDGE` — queryable data resources (vector DBs, feeds, etc).
27    Knowledge,
28    /// `CF_WORKFLOW_TEMPLATES` — reusable workflow blueprints.
29    WorkflowTemplate,
30    /// `CF_AGENT_TEMPLATES` — reusable agent specs.
31    AgentTemplate,
32    /// `CF_MODELS` — AI inference models.
33    Model,
34}
35
36impl ResourceClass {
37    pub fn as_str(&self) -> &'static str {
38        match self {
39            ResourceClass::Tool => "tool",
40            ResourceClass::Skill => "skill",
41            ResourceClass::Knowledge => "knowledge",
42            ResourceClass::WorkflowTemplate => "workflow_template",
43            ResourceClass::AgentTemplate => "agent_template",
44            ResourceClass::Model => "model",
45        }
46    }
47
48    pub fn parse_str(s: &str) -> Option<Self> {
49        match s {
50            "tool" => Some(ResourceClass::Tool),
51            "skill" => Some(ResourceClass::Skill),
52            "knowledge" => Some(ResourceClass::Knowledge),
53            "workflow_template" => Some(ResourceClass::WorkflowTemplate),
54            "agent_template" => Some(ResourceClass::AgentTemplate),
55            "model" => Some(ResourceClass::Model),
56            _ => None,
57        }
58    }
59}
60
61/// Cross-registry projection of a single resource.
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct ResourceDescriptor {
64    /// Which registry this resource lives in.
65    pub class: ResourceClass,
66
67    /// Unique id in the source registry. Caller passes this back to
68    /// `tenzro_useResource(resource_id, params)` for invocation.
69    pub resource_id: String,
70
71    pub name: String,
72    pub version: String,
73    pub description: String,
74    pub category: String,
75
76    /// Capability tags lifted from the source registry. Used for the
77    /// post-filter on `tenzro_listResources(capability_tags=...)`.
78    pub capabilities: Vec<String>,
79
80    pub creator_did: Option<String>,
81
82    /// Operator payout wallet. Tenants pay TNZO; the protocol splits
83    /// 5% to treasury, 95% to this wallet.
84    pub creator_wallet: Option<Address>,
85
86    /// Per-invocation cost in atto-TNZO. `0` = free.
87    pub price_per_call: u128,
88
89    /// `true` when the resource is `Active` in the source registry.
90    pub is_available: bool,
91
92    /// Last liveness heartbeat (seconds). Helps the discovery layer
93    /// surface staleness without filtering aggressively.
94    pub last_seen_at: u64,
95
96    /// Optional kind / sub-type from the source registry, projected
97    /// as a plain string. E.g. for `Tool` this is the transport mode
98    /// (`mcp` / `mcp-stdio` / `mcp-sse` / `api` / `native`); for
99    /// `Knowledge` this is the kind (`vector_index` / `feed` / ...).
100    pub subtype: Option<String>,
101
102    /// Reputation score (0..=1000). `None` when the source registry
103    /// doesn't track reputation (only Models do today).
104    pub reputation: Option<u64>,
105}
106
107/// Filter for `tenzro_listResources`.
108#[derive(Debug, Clone, Default, Serialize, Deserialize)]
109pub struct ResourceFilter {
110    /// Subset of resource classes to query. Empty = all classes.
111    #[serde(default)]
112    pub classes: Vec<String>,
113
114    /// Free-text query — matches name, description, capabilities.
115    #[serde(default)]
116    pub query: Option<String>,
117
118    /// Capability tags — AND-match. Empty = no filter.
119    #[serde(default)]
120    pub capability_tags: Vec<String>,
121
122    /// Optional category filter.
123    #[serde(default)]
124    pub category: Option<String>,
125
126    /// Cost ceiling in atto-TNZO. Resources whose `price_per_call`
127    /// exceeds this are filtered out. `None` = no ceiling.
128    #[serde(default)]
129    pub max_tnzo_price: Option<u128>,
130
131    /// Filter by creator DID.
132    #[serde(default)]
133    pub creator_did: Option<String>,
134
135    #[serde(default)]
136    pub limit: Option<usize>,
137
138    #[serde(default)]
139    pub offset: Option<usize>,
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn resource_class_roundtrip() {
148        for c in [
149            ResourceClass::Tool,
150            ResourceClass::Skill,
151            ResourceClass::Knowledge,
152            ResourceClass::WorkflowTemplate,
153            ResourceClass::AgentTemplate,
154            ResourceClass::Model,
155        ] {
156            assert_eq!(ResourceClass::parse_str(c.as_str()), Some(c));
157        }
158        assert_eq!(ResourceClass::parse_str("nope"), None);
159    }
160}