Skip to main content

systemprompt_models/artifacts/cli/
mod.rs

1pub mod conversion;
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5use serde_json::Value as JsonValue;
6use std::collections::HashMap;
7use thiserror::Error;
8
9use super::{
10    AudioArtifact, ChartArtifact, CopyPasteTextArtifact, DashboardArtifact, ImageArtifact,
11    ListArtifact, PresentationCardArtifact, TableArtifact, TextArtifact, VideoArtifact,
12};
13
14#[derive(Debug, Error)]
15pub enum ConversionError {
16    #[error("Missing columns hint for table artifact")]
17    MissingColumns,
18
19    #[error("No array found in data for table/list conversion")]
20    NoArrayFound,
21
22    #[error("JSON serialization error: {0}")]
23    Json(#[from] serde_json::Error),
24
25    #[error("Unsupported artifact type: {0}")]
26    UnsupportedType(String),
27}
28
29#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
30#[serde(rename_all = "snake_case")]
31pub enum CliArtifactType {
32    Table,
33    List,
34    PresentationCard,
35    Text,
36    CopyPasteText,
37    Chart,
38    Form,
39    Dashboard,
40}
41
42#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
43pub struct RenderingHints {
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub columns: Option<Vec<String>>,
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub chart_type: Option<String>,
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub theme: Option<String>,
50    #[serde(flatten)]
51    pub extra: HashMap<String, JsonValue>,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
55pub struct CommandResultRaw {
56    pub data: JsonValue,
57    pub artifact_type: CliArtifactType,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub title: Option<String>,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub hints: Option<RenderingHints>,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
65#[serde(tag = "artifact_type", rename_all = "snake_case")]
66pub enum CliArtifact {
67    Table {
68        #[serde(flatten)]
69        artifact: TableArtifact,
70    },
71    List {
72        #[serde(flatten)]
73        artifact: ListArtifact,
74    },
75    Text {
76        #[serde(flatten)]
77        artifact: TextArtifact,
78    },
79    #[serde(rename = "copy_paste_text")]
80    CopyPasteText {
81        #[serde(flatten)]
82        artifact: CopyPasteTextArtifact,
83    },
84    Dashboard {
85        #[serde(flatten)]
86        artifact: DashboardArtifact,
87    },
88    Chart {
89        #[serde(flatten)]
90        artifact: ChartArtifact,
91    },
92    Audio {
93        #[serde(flatten)]
94        artifact: AudioArtifact,
95    },
96    Image {
97        #[serde(flatten)]
98        artifact: ImageArtifact,
99    },
100    Video {
101        #[serde(flatten)]
102        artifact: VideoArtifact,
103    },
104    #[serde(rename = "presentation_card")]
105    PresentationCard {
106        #[serde(flatten)]
107        artifact: PresentationCardArtifact,
108    },
109}
110
111impl CliArtifact {
112    #[must_use]
113    pub const fn artifact_type_str(&self) -> &'static str {
114        match self {
115            Self::Table { .. } => TableArtifact::ARTIFACT_TYPE_STR,
116            Self::List { .. } => ListArtifact::ARTIFACT_TYPE_STR,
117            Self::Text { .. } => TextArtifact::ARTIFACT_TYPE_STR,
118            Self::CopyPasteText { .. } => CopyPasteTextArtifact::ARTIFACT_TYPE_STR,
119            Self::Dashboard { .. } => DashboardArtifact::ARTIFACT_TYPE_STR,
120            Self::Chart { .. } => ChartArtifact::ARTIFACT_TYPE_STR,
121            Self::Audio { .. } => AudioArtifact::ARTIFACT_TYPE_STR,
122            Self::Image { .. } => ImageArtifact::ARTIFACT_TYPE_STR,
123            Self::Video { .. } => VideoArtifact::ARTIFACT_TYPE_STR,
124            Self::PresentationCard { .. } => PresentationCardArtifact::ARTIFACT_TYPE_STR,
125        }
126    }
127
128    #[must_use]
129    pub fn title(&self) -> Option<String> {
130        match self {
131            Self::Text { artifact } => artifact.title.clone(),
132            Self::CopyPasteText { artifact } => artifact.title.clone(),
133            Self::Dashboard { artifact } => Some(artifact.title.clone()),
134            Self::Audio { artifact } => artifact.title.clone(),
135            Self::PresentationCard { artifact } => Some(artifact.title.clone()),
136            Self::Table { .. }
137            | Self::List { .. }
138            | Self::Chart { .. }
139            | Self::Image { .. }
140            | Self::Video { .. } => None,
141        }
142    }
143
144    #[must_use]
145    pub const fn table(artifact: TableArtifact) -> Self {
146        Self::Table { artifact }
147    }
148
149    #[must_use]
150    pub const fn list(artifact: ListArtifact) -> Self {
151        Self::List { artifact }
152    }
153
154    #[must_use]
155    pub const fn text(artifact: TextArtifact) -> Self {
156        Self::Text { artifact }
157    }
158
159    #[must_use]
160    pub const fn copy_paste_text(artifact: CopyPasteTextArtifact) -> Self {
161        Self::CopyPasteText { artifact }
162    }
163
164    #[must_use]
165    pub const fn dashboard(artifact: DashboardArtifact) -> Self {
166        Self::Dashboard { artifact }
167    }
168
169    #[must_use]
170    pub const fn chart(artifact: ChartArtifact) -> Self {
171        Self::Chart { artifact }
172    }
173
174    #[must_use]
175    pub const fn audio(artifact: AudioArtifact) -> Self {
176        Self::Audio { artifact }
177    }
178
179    #[must_use]
180    pub const fn image(artifact: ImageArtifact) -> Self {
181        Self::Image { artifact }
182    }
183
184    #[must_use]
185    pub const fn video(artifact: VideoArtifact) -> Self {
186        Self::Video { artifact }
187    }
188
189    #[must_use]
190    pub const fn presentation_card(artifact: PresentationCardArtifact) -> Self {
191        Self::PresentationCard { artifact }
192    }
193}