1use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::path::PathBuf;
6
7#[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 #[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 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#[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#[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}