Skip to main content

systemprompt_agent/models/
skill.rs

1use anyhow::{anyhow, Result};
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    pub skill_id: SkillId,
10    pub file_path: String,
11    pub name: String,
12    pub description: String,
13    pub instructions: String,
14    pub enabled: bool,
15    pub tags: Vec<String>,
16    pub category_id: Option<CategoryId>,
17    pub source_id: SourceId,
18    pub created_at: DateTime<Utc>,
19    pub updated_at: DateTime<Utc>,
20}
21
22impl Skill {
23    pub fn from_json_row(row: &JsonRow) -> Result<Self> {
24        let skill_id = SkillId::new(
25            row.get("skill_id")
26                .and_then(|v| v.as_str())
27                .ok_or_else(|| anyhow!("Missing skill_id"))?,
28        );
29
30        let file_path = row
31            .get("file_path")
32            .and_then(|v| v.as_str())
33            .ok_or_else(|| anyhow!("Missing file_path"))?
34            .to_string();
35
36        let name = row
37            .get("name")
38            .and_then(|v| v.as_str())
39            .ok_or_else(|| anyhow!("Missing name"))?
40            .to_string();
41
42        let description = row
43            .get("description")
44            .and_then(|v| v.as_str())
45            .ok_or_else(|| anyhow!("Missing description"))?
46            .to_string();
47
48        let instructions = row
49            .get("instructions")
50            .and_then(|v| v.as_str())
51            .ok_or_else(|| anyhow!("Missing instructions"))?
52            .to_string();
53
54        let enabled = row
55            .get("enabled")
56            .and_then(|v| v.as_bool())
57            .ok_or_else(|| anyhow!("Missing enabled"))?;
58
59        let tags = row
60            .get("tags")
61            .and_then(|v| v.as_array())
62            .map(|arr| {
63                arr.iter()
64                    .filter_map(|v| v.as_str().map(String::from))
65                    .collect()
66            })
67            .unwrap_or_else(Vec::new);
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(|v| systemprompt_database::parse_database_datetime(v))
83            .ok_or_else(|| anyhow!("Missing or invalid created_at"))?;
84
85        let updated_at = row
86            .get("updated_at")
87            .and_then(|v| systemprompt_database::parse_database_datetime(v))
88            .ok_or_else(|| anyhow!("Missing or invalid updated_at"))?;
89
90        Ok(Self {
91            skill_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: String,
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}