Skip to main content

rucora_core/skill/
mod.rs

1//! Skill(技能)核心定义模块
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::path::PathBuf;
6
7/// Skill 定义
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct SkillDefinition {
10    pub name: String,
11    pub description: String,
12    #[serde(default = "default_version")]
13    pub version: String,
14    #[serde(default)]
15    pub author: Option<String>,
16    #[serde(default)]
17    pub tags: Vec<String>,
18    #[serde(default = "default_timeout")]
19    pub timeout: u64,
20    #[serde(default)]
21    pub input_schema: Value,
22    #[serde(default)]
23    pub output_schema: Value,
24    #[serde(default)]
25    pub homepage: Option<String>,
26    #[serde(default)]
27    pub metadata: Option<Value>,
28    /// 技能来源目录,仅用于运行时定位本地实现文件。
29    #[serde(skip)]
30    pub location: Option<PathBuf>,
31}
32
33fn default_version() -> String {
34    "1.0.0".to_string()
35}
36
37fn default_timeout() -> u64 {
38    30
39}
40
41impl SkillDefinition {
42    pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
43        Self {
44            name: name.into(),
45            description: description.into(),
46            version: default_version(),
47            author: None,
48            tags: Vec::new(),
49            timeout: default_timeout(),
50            input_schema: Value::Null,
51            output_schema: Value::Null,
52            homepage: None,
53            metadata: None,
54            location: None,
55        }
56    }
57
58    pub fn to_tool_description(&self) -> Value {
59        serde_json::json!({
60            "type": "function",
61            "function": {
62                "name": self.name,
63                "description": self.description,
64                "parameters": self.input_schema
65            }
66        })
67    }
68
69    /// 验证输入数据是否符合 schema。
70    ///
71    /// # Errors
72    ///
73    /// 当输入数据不符合 schema 要求时返回错误描述。
74    pub fn validate_input(&self, input: &Value) -> Result<(), String> {
75        if self.input_schema.is_null() {
76            return Ok(());
77        }
78
79        if let Some(_props) = self.input_schema.get("properties")
80            && let Some(required) = self.input_schema.get("required").and_then(|v| v.as_array())
81        {
82            for req_field in required {
83                if let Some(field_name) = req_field.as_str()
84                    && input.get(field_name).is_none()
85                {
86                    return Err(format!("缺少必需字段:{field_name}"));
87                }
88            }
89        }
90
91        Ok(())
92    }
93}
94
95/// Skill 执行结果
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct SkillResult {
98    pub success: bool,
99    #[serde(default)]
100    pub data: Option<Value>,
101    #[serde(default)]
102    pub error: Option<String>,
103    #[serde(default)]
104    pub execution_time_ms: Option<u64>,
105}
106
107impl SkillResult {
108    pub fn success(data: Value) -> Self {
109        Self {
110            success: true,
111            data: Some(data),
112            error: None,
113            execution_time_ms: None,
114        }
115    }
116
117    pub fn error(message: impl Into<String>) -> Self {
118        Self {
119            success: false,
120            data: None,
121            error: Some(message.into()),
122            execution_time_ms: None,
123        }
124    }
125
126    pub fn to_json(&self) -> Value {
127        serde_json::to_value(self).unwrap_or_else(|_| {
128            serde_json::json!({
129                "success": false,
130                "error": "序列化失败"
131            })
132        })
133    }
134}
135
136/// Skill 执行上下文
137#[derive(Debug, Clone)]
138pub struct SkillContext {
139    pub input: Value,
140    pub definition: SkillDefinition,
141    pub env: std::collections::HashMap<String, String>,
142    pub work_dir: Option<std::path::PathBuf>,
143}
144
145impl SkillContext {
146    pub fn new(input: Value, definition: SkillDefinition) -> Self {
147        Self {
148            input,
149            definition,
150            env: std::env::vars().collect(),
151            work_dir: None,
152        }
153    }
154
155    pub fn with_work_dir(mut self, dir: impl Into<std::path::PathBuf>) -> Self {
156        self.work_dir = Some(dir.into());
157        self
158    }
159}