Skip to main content

sz_rust_capability/
capability.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2024-2026 SZ-Rust Team
3//
4use async_trait::async_trait;
5use serde::Serialize;
6
7use crate::error::CapResult;
8use crate::source::CapabilitySource;
9
10/// 统一能力抽象 trait。
11///
12/// Skills(AI 内置能力)和 Plugins(业务插件)都实现此 trait,
13/// 通过 [`CapabilityRegistry`](crate::CapabilityRegistry) 统一注册、发现和调用。
14///
15/// # 实现示例
16///
17/// ```
18/// use async_trait::async_trait;
19/// use serde_json::{json, Value};
20/// use sz_rust_capability::{Capability, CapabilitySource, CapResult};
21///
22/// struct SearchCustomerCapability;
23///
24/// #[async_trait]
25/// impl Capability for SearchCustomerCapability {
26///     fn name(&self) -> &'static str { "crm.search_customer" }
27///     fn description(&self) -> &'static str { "搜索客户" }
28///     fn schema(&self) -> Value {
29///         json!({
30///             "type": "object",
31///             "properties": { "keyword": { "type": "string" } },
32///             "required": ["keyword"]
33///         })
34///     }
35///     fn tags(&self) -> &[&'static str] { &["crm", "search", "read"] }
36///     fn source(&self) -> CapabilitySource { CapabilitySource::Plugin }
37///     async fn call(&self, args: Value) -> CapResult<Value> {
38///         let keyword = args.get("keyword").and_then(|v| v.as_str()).unwrap_or("");
39///         Ok(json!({ "results": [keyword] }))
40///     }
41/// }
42/// ```
43#[async_trait]
44pub trait Capability: Send + Sync + 'static {
45    /// 能力名称,全局唯一,格式建议 `{source_prefix}.{capability_name}`。
46    fn name(&self) -> &'static str;
47
48    /// 人类可读的能力描述。
49    fn description(&self) -> &'static str;
50
51    /// 参数 JSON Schema,描述 `call` 方法的输入参数格式。
52    fn schema(&self) -> serde_json::Value;
53
54    /// 能力标签,用于 `find_by_tags` 搜索。多标签 AND 逻辑。
55    fn tags(&self) -> &[&'static str];
56
57    /// 能力来源类型(Skill/Plugin/Service)。
58    fn source(&self) -> CapabilitySource;
59
60    /// 执行能力,接受 JSON 参数,返回 JSON 结果。
61    async fn call(&self, args: serde_json::Value) -> CapResult<serde_json::Value>;
62
63    /// 能力版本,默认 "1.0.0"。
64    fn version(&self) -> &'static str {
65        "1.0.0"
66    }
67
68    /// 是否需要人工确认(HITL),默认 false。
69    fn requires_confirmation(&self) -> bool {
70        false
71    }
72
73    /// 参数校验,默认实现委托 [`validate_json_schema`](crate::registry::validate_json_schema) 做轻量校验。
74    /// 能力可覆盖此方法做完整 JSON Schema 校验。
75    async fn validate_args(&self, args: &serde_json::Value) -> CapResult<()> {
76        crate::registry::validate_json_schema(&self.schema(), args)
77    }
78}
79
80/// 能力元信息快照,用于列表/搜索返回。
81#[derive(Debug, Clone, Serialize)]
82pub struct CapabilityInfo {
83    pub name: &'static str,
84    pub description: &'static str,
85    pub tags: Vec<&'static str>,
86    pub source: CapabilitySource,
87    pub version: &'static str,
88    pub requires_confirmation: bool,
89}
90
91impl CapabilityInfo {
92    /// 从 Capability trait 对象提取元信息快照。
93    pub fn from_trait(cap: &dyn Capability) -> Self {
94        Self {
95            name: cap.name(),
96            description: cap.description(),
97            tags: cap.tags().to_vec(),
98            source: cap.source(),
99            version: cap.version(),
100            requires_confirmation: cap.requires_confirmation(),
101        }
102    }
103}
104#[cfg(test)]
105mod tests {
106    use super::*;
107    use async_trait::async_trait;
108    use serde_json::json;
109
110    struct TestCap;
111
112    #[async_trait]
113    impl Capability for TestCap {
114        fn name(&self) -> &'static str {
115            "test.cap"
116        }
117        fn description(&self) -> &'static str {
118            "测试能力"
119        }
120        fn schema(&self) -> serde_json::Value {
121            json!({"type": "object"})
122        }
123        fn tags(&self) -> &[&'static str] {
124            &["test", "demo"]
125        }
126        fn source(&self) -> CapabilitySource {
127            CapabilitySource::Skill
128        }
129        async fn call(&self, args: serde_json::Value) -> CapResult<serde_json::Value> {
130            Ok(args)
131        }
132    }
133
134    struct CustomVersionCap;
135
136    #[async_trait]
137    impl Capability for CustomVersionCap {
138        fn name(&self) -> &'static str {
139            "custom.version"
140        }
141        fn description(&self) -> &'static str {
142            "自定义版本能力"
143        }
144        fn schema(&self) -> serde_json::Value {
145            json!({})
146        }
147        fn tags(&self) -> &[&'static str] {
148            &[]
149        }
150        fn source(&self) -> CapabilitySource {
151            CapabilitySource::Plugin
152        }
153        fn version(&self) -> &'static str {
154            "2.0.0"
155        }
156        fn requires_confirmation(&self) -> bool {
157            true
158        }
159        async fn call(&self, _: serde_json::Value) -> CapResult<serde_json::Value> {
160            Ok(json!({}))
161        }
162    }
163
164    #[test]
165    fn test_capability_info_from_trait_defaults() {
166        let cap = TestCap;
167        let info = CapabilityInfo::from_trait(&cap);
168        assert_eq!(info.name, "test.cap");
169        assert_eq!(info.description, "测试能力");
170        assert_eq!(info.tags, vec!["test", "demo"]);
171        assert_eq!(info.source, CapabilitySource::Skill);
172        assert_eq!(info.version, "1.0.0");
173        assert!(!info.requires_confirmation);
174    }
175
176    #[test]
177    fn test_capability_info_from_trait_custom() {
178        let cap = CustomVersionCap;
179        let info = CapabilityInfo::from_trait(&cap);
180        assert_eq!(info.name, "custom.version");
181        assert_eq!(info.version, "2.0.0");
182        assert!(info.requires_confirmation);
183        assert_eq!(info.source, CapabilitySource::Plugin);
184        assert!(info.tags.is_empty());
185    }
186
187    #[tokio::test]
188    async fn test_capability_default_validate_args() {
189        let cap = TestCap;
190        let result = cap.validate_args(&json!({"key": "value"})).await;
191        assert!(result.is_ok());
192    }
193
194    #[test]
195    fn test_capability_info_serialize() {
196        let cap = TestCap;
197        let info = CapabilityInfo::from_trait(&cap);
198        let json = serde_json::to_string(&info).expect("序列化失败");
199        assert!(json.contains("test.cap"));
200        assert!(json.contains("测试能力"));
201    }
202}