Skip to main content

systemprompt_agent/models/
skill.rs

1use anyhow::{Result, anyhow};
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use systemprompt_database::JsonRow;
5use systemprompt_identifiers::{CategoryId, SkillId, SourceId};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Skill {
9    #[serde(rename = "skill_id")]
10    pub id: SkillId,
11    pub file_path: String,
12    pub name: String,
13    pub description: String,
14    pub instructions: String,
15    pub enabled: bool,
16    pub tags: Vec<String>,
17    pub category_id: Option<CategoryId>,
18    pub source_id: SourceId,
19    pub created_at: DateTime<Utc>,
20    pub updated_at: DateTime<Utc>,
21}
22
23impl Skill {
24    pub fn from_json_row(row: &JsonRow) -> Result<Self> {
25        let id = SkillId::new(
26            row.get("skill_id")
27                .and_then(|v| v.as_str())
28                .ok_or_else(|| anyhow!("Missing skill_id"))?,
29        );
30
31        let file_path = row
32            .get("file_path")
33            .and_then(|v| v.as_str())
34            .ok_or_else(|| anyhow!("Missing file_path"))?
35            .to_string();
36
37        let name = row
38            .get("name")
39            .and_then(|v| v.as_str())
40            .ok_or_else(|| anyhow!("Missing name"))?
41            .to_string();
42
43        let description = row
44            .get("description")
45            .and_then(|v| v.as_str())
46            .ok_or_else(|| anyhow!("Missing description"))?
47            .to_string();
48
49        let instructions = row
50            .get("instructions")
51            .and_then(|v| v.as_str())
52            .ok_or_else(|| anyhow!("Missing instructions"))?
53            .to_string();
54
55        let enabled = row
56            .get("enabled")
57            .and_then(serde_json::Value::as_bool)
58            .ok_or_else(|| anyhow!("Missing enabled"))?;
59
60        let tags = row
61            .get("tags")
62            .and_then(|v| v.as_array())
63            .map_or_else(Vec::new, |arr| {
64                arr.iter()
65                    .filter_map(|v| v.as_str().map(String::from))
66                    .collect()
67            });
68
69        let category_id = row
70            .get("category_id")
71            .and_then(|v| v.as_str())
72            .map(CategoryId::new);
73
74        let source_id = SourceId::new(
75            row.get("source_id")
76                .and_then(|v| v.as_str())
77                .ok_or_else(|| anyhow!("Missing source_id"))?,
78        );
79
80        let created_at = row
81            .get("created_at")
82            .and_then(systemprompt_database::parse_database_datetime)
83            .ok_or_else(|| anyhow!("Missing or invalid created_at"))?;
84
85        let updated_at = row
86            .get("updated_at")
87            .and_then(systemprompt_database::parse_database_datetime)
88            .ok_or_else(|| anyhow!("Missing or invalid updated_at"))?;
89
90        Ok(Self {
91            id,
92            file_path,
93            name,
94            description,
95            instructions,
96            enabled,
97            tags,
98            category_id,
99            source_id,
100            created_at,
101            updated_at,
102        })
103    }
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct SkillMetadata {
108    pub id: SkillId,
109    pub name: String,
110    pub description: String,
111    pub enabled: bool,
112    pub file: String,
113    pub assigned_agents: Vec<String>,
114    pub tags: Vec<String>,
115}