systemprompt_models/artifacts/chart/
mod.rs1use crate::artifacts::metadata::ArtifactProvenance;
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::{McpExecutionId, SkillId, SkillName};
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(default)]
47 pub chart_type: ChartType,
48 #[serde(default)]
49 pub title: String,
50 #[serde(default)]
51 pub x_axis_label: String,
52 #[serde(default)]
53 pub y_axis_label: String,
54 #[serde(default)]
55 pub x_axis_type: AxisType,
56 #[serde(default)]
57 pub y_axis_type: AxisType,
58 #[serde(skip)]
59 #[schemars(skip)]
60 metadata: ArtifactProvenance,
61}
62
63impl ChartArtifact {
64 pub const ARTIFACT_TYPE_STR: &'static str = "chart";
65
66 pub fn new(title: impl Into<String>, chart_type: ChartType) -> Self {
67 Self {
68 artifact_type: "chart".to_owned(),
69 labels: Vec::new(),
70 datasets: Vec::new(),
71 chart_type,
72 title: title.into(),
73 x_axis_label: "X".to_owned(),
74 y_axis_label: "Y".to_owned(),
75 x_axis_type: AxisType::Category,
76 y_axis_type: AxisType::Linear,
77 metadata: ArtifactProvenance::default(),
78 }
79 }
80
81 pub fn with_request(mut self, ctx: &RequestContext) -> Self {
82 self.metadata.set_request(ctx);
83 self
84 }
85
86 pub fn with_x_axis_labels(mut self, labels: Vec<String>) -> Self {
87 self.labels = labels;
88 self
89 }
90
91 pub fn with_labels(self, labels: Vec<String>) -> Self {
92 self.with_x_axis_labels(labels)
93 }
94
95 pub fn with_datasets(mut self, datasets: Vec<ChartDataset>) -> Self {
96 self.datasets = datasets;
97 self
98 }
99
100 pub fn add_dataset(mut self, dataset: ChartDataset) -> Self {
101 self.datasets.push(dataset);
102 self
103 }
104
105 pub const fn with_x_axis_type(mut self, axis_type: AxisType) -> Self {
106 self.x_axis_type = axis_type;
107 self
108 }
109
110 pub const fn with_y_axis_type(mut self, axis_type: AxisType) -> Self {
111 self.y_axis_type = axis_type;
112 self
113 }
114
115 pub fn with_axes(mut self, x_label: impl Into<String>, y_label: impl Into<String>) -> Self {
116 self.x_axis_label = x_label.into();
117 self.y_axis_label = y_label.into();
118 self
119 }
120
121 pub fn with_execution_id(mut self, id: McpExecutionId) -> Self {
122 self.metadata.set_execution_id(id);
123 self
124 }
125
126 pub fn with_skill(mut self, skill_id: SkillId, skill_name: SkillName) -> Self {
127 self.metadata.set_skill(skill_id, skill_name);
128 self
129 }
130}
131
132impl Artifact for ChartArtifact {
133 fn artifact_type(&self) -> ArtifactType {
134 ArtifactType::Chart
135 }
136
137 fn to_schema(&self) -> JsonValue {
139 json!({
140 "type": "object",
141 "properties": {
142 "labels": {
143 "type": "array",
144 "items": {"type": "string"}
145 },
146 "datasets": {
147 "type": "array",
148 "items": {
149 "type": "object",
150 "properties": {
151 "label": {"type": "string"},
152 "data": {"type": "array", "items": {"type": "number"}}
153 }
154 }
155 },
156 "chart_type": {"type": "string"},
157 "title": {"type": "string"},
158 "x_axis_label": {"type": "string"},
159 "y_axis_label": {"type": "string"},
160 "x_axis_type": {"type": "string"},
161 "y_axis_type": {"type": "string"},
162 "_execution_id": {"type": "string"}
163 },
164 "required": ["labels", "datasets"],
165 "x-artifact-type": "chart"
166 })
167 }
168}