Skip to main content

soma_codemode/types/
catalog.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::snippet::store::{SnippetInfo, SnippetInputSpec};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
7#[serde(rename_all = "snake_case")]
8pub enum CodeModeCatalogKind {
9    Tool,
10    Snippet,
11}
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub struct CodeModeSnippetInputEntry {
15    pub name: String,
16    #[serde(flatten)]
17    pub spec: SnippetInputSpec,
18}
19
20#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
21pub struct ToolDescriptor {
22    pub kind: CodeModeCatalogKind,
23    pub id: String,
24    pub name: String,
25    pub namespace: String,
26    pub description: String,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub schema: Option<Value>,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub output_schema: Option<Value>,
31    pub signature: String,
32    pub dts: String,
33    #[serde(default, skip_serializing_if = "Vec::is_empty")]
34    pub tags: Vec<String>,
35    #[serde(default, skip_serializing_if = "Vec::is_empty")]
36    pub inputs: Vec<CodeModeSnippetInputEntry>,
37}
38
39impl ToolDescriptor {
40    #[must_use]
41    pub fn tool(
42        namespace: &str,
43        tool: &str,
44        description: &str,
45        schema: Option<Value>,
46        output_schema: Option<Value>,
47    ) -> Self {
48        let types = crate::ts_signatures::generate_tool_types(
49            namespace,
50            tool,
51            description,
52            schema.as_ref(),
53            output_schema.as_ref(),
54        );
55        Self {
56            kind: CodeModeCatalogKind::Tool,
57            id: super::id::namespaced_tool_id(namespace, tool),
58            name: tool.to_string(),
59            namespace: namespace.to_string(),
60            description: description.to_string(),
61            schema,
62            output_schema,
63            signature: types.signature,
64            dts: types.dts,
65            tags: Vec::new(),
66            inputs: Vec::new(),
67        }
68    }
69
70    #[must_use]
71    pub fn snippet(info: &SnippetInfo) -> Self {
72        let description = info
73            .description
74            .clone()
75            .unwrap_or_else(|| format!("Code Mode snippet `{}`", info.name));
76        let inputs = info
77            .inputs
78            .iter()
79            .map(|(name, spec)| CodeModeSnippetInputEntry {
80                name: name.clone(),
81                spec: spec.clone(),
82            })
83            .collect();
84        Self {
85            kind: CodeModeCatalogKind::Snippet,
86            id: super::id::namespaced_tool_id("snippet", &info.name),
87            name: info.name.clone(),
88            namespace: "snippet".into(),
89            description,
90            schema: None,
91            output_schema: None,
92            signature: format!("codemode.run({:?}, input?)", info.name),
93            dts: String::new(),
94            tags: Vec::new(),
95            inputs,
96        }
97    }
98}
99
100#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
101pub struct UiLink {
102    pub url: String,
103    #[serde(default, skip_serializing_if = "Option::is_none")]
104    pub title: Option<String>,
105}
106
107pub fn destructive_permitted(value: &Value) -> bool {
108    value
109        .get("confirm")
110        .and_then(Value::as_bool)
111        .unwrap_or(false)
112}