systemprompt_models/artifacts/chart/
mod.rs1use crate::artifacts::metadata::ExecutionMetadata;
12use crate::artifacts::traits::Artifact;
13use crate::artifacts::types::{ArtifactType, AxisType, ChartType};
14use crate::execution::context::RequestContext;
15use schemars::JsonSchema;
16use serde::{Deserialize, Serialize};
17use serde_json::{Value as JsonValue, json};
18use systemprompt_identifiers::SkillId;
19
20fn default_artifact_type() -> String {
21 "chart".to_owned()
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
25pub struct ChartDataset {
26 pub label: String,
27 pub data: Vec<f64>,
28}
29
30impl ChartDataset {
31 pub fn new(label: impl Into<String>, data: Vec<f64>) -> Self {
32 Self {
33 label: label.into(),
34 data,
35 }
36 }
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
40pub struct ChartArtifact {
41 #[serde(rename = "x-artifact-type")]
42 #[serde(default = "default_artifact_type")]
43 pub artifact_type: String,
44 pub labels: Vec<String>,
45 pub datasets: Vec<ChartDataset>,
46 #[serde(skip)]
47 #[schemars(skip)]
48 chart_type: ChartType,
49 #[serde(skip)]
50 #[schemars(skip)]
51 title: String,
52 #[serde(skip)]
53 #[schemars(skip)]
54 x_axis_label: String,
55 #[serde(skip)]
56 #[schemars(skip)]
57 y_axis_label: String,
58 #[serde(skip)]
59 #[schemars(skip)]
60 x_axis_type: AxisType,
61 #[serde(skip)]
62 #[schemars(skip)]
63 y_axis_type: AxisType,
64 #[serde(skip)]
65 #[schemars(skip)]
66 metadata: ExecutionMetadata,
67}
68
69impl ChartArtifact {
70 pub const ARTIFACT_TYPE_STR: &'static str = "chart";
71
72 pub fn new(title: impl Into<String>, chart_type: ChartType) -> Self {
73 Self {
74 artifact_type: "chart".to_owned(),
75 labels: Vec::new(),
76 datasets: Vec::new(),
77 chart_type,
78 title: title.into(),
79 x_axis_label: "X".to_owned(),
80 y_axis_label: "Y".to_owned(),
81 x_axis_type: AxisType::Category,
82 y_axis_type: AxisType::Linear,
83 metadata: ExecutionMetadata::default(),
84 }
85 }
86
87 pub fn with_request(mut self, ctx: &RequestContext) -> Self {
88 self.metadata = ExecutionMetadata::with_request(ctx);
89 self
90 }
91
92 pub fn with_x_axis_labels(mut self, labels: Vec<String>) -> Self {
93 self.labels = labels;
94 self
95 }
96
97 pub fn with_labels(self, labels: Vec<String>) -> Self {
98 self.with_x_axis_labels(labels)
99 }
100
101 pub fn with_datasets(mut self, datasets: Vec<ChartDataset>) -> Self {
102 self.datasets = datasets;
103 self
104 }
105
106 pub fn add_dataset(mut self, dataset: ChartDataset) -> Self {
107 self.datasets.push(dataset);
108 self
109 }
110
111 pub const fn with_x_axis_type(mut self, axis_type: AxisType) -> Self {
112 self.x_axis_type = axis_type;
113 self
114 }
115
116 pub const fn with_y_axis_type(mut self, axis_type: AxisType) -> Self {
117 self.y_axis_type = axis_type;
118 self
119 }
120
121 pub fn with_axes(mut self, x_label: impl Into<String>, y_label: impl Into<String>) -> Self {
122 self.x_axis_label = x_label.into();
123 self.y_axis_label = y_label.into();
124 self
125 }
126
127 pub fn with_execution_id(mut self, id: impl Into<String>) -> Self {
128 self.metadata.execution_id = Some(id.into());
129 self
130 }
131
132 pub fn with_skill(
133 mut self,
134 skill_id: impl Into<SkillId>,
135 skill_name: impl Into<String>,
136 ) -> Self {
137 self.metadata.skill_id = Some(skill_id.into());
138 self.metadata.skill_name = Some(skill_name.into());
139 self
140 }
141}
142
143impl Artifact for ChartArtifact {
144 fn artifact_type(&self) -> ArtifactType {
145 ArtifactType::Chart
146 }
147
148 fn to_schema(&self) -> JsonValue {
149 json!({
150 "type": "object",
151 "properties": {
152 "labels": {
153 "type": "array",
154 "items": {"type": "string"}
155 },
156 "datasets": {
157 "type": "array",
158 "items": {
159 "type": "object",
160 "properties": {
161 "label": {"type": "string"},
162 "data": {"type": "array", "items": {"type": "number"}}
163 }
164 }
165 },
166 "_execution_id": {"type": "string"}
167 },
168 "required": ["labels", "datasets"],
169 "x-artifact-type": "chart",
170 "x-chart-hints": {
171 "chart_type": self.chart_type,
172 "title": self.title,
173 "x_axis": {
174 "label": self.x_axis_label,
175 "type": self.x_axis_type
176 },
177 "y_axis": {
178 "label": self.y_axis_label,
179 "type": self.y_axis_type
180 }
181 }
182 })
183 }
184}