systemprompt_models/artifacts/table/
mod.rs1pub mod column;
11pub mod hints;
12
13pub use column::Column;
14pub use hints::TableHints;
15
16use crate::artifacts::metadata::ExecutionMetadata;
17use crate::artifacts::traits::Artifact;
18use crate::artifacts::types::ArtifactType;
19use crate::execution::context::RequestContext;
20use schemars::JsonSchema;
21use serde::{Deserialize, Serialize};
22use serde_json::{Value as JsonValue, json};
23use systemprompt_identifiers::SkillId;
24
25#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
26pub struct TableResponse {
27 #[serde(rename = "x-artifact-type")]
28 pub artifact_type: String,
29 pub columns: Vec<Column>,
30 pub items: Vec<JsonValue>,
31 pub count: usize,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub execution_id: Option<String>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 #[schemars(with = "Option<JsonValue>")]
36 pub hints: Option<JsonValue>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
40pub struct TableArtifact {
41 #[serde(rename = "x-artifact-type")]
42 #[serde(default = "default_artifact_type")]
43 pub artifact_type: String,
44 pub columns: Vec<Column>,
45 pub items: Vec<JsonValue>,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 #[schemars(with = "Option<JsonValue>")]
48 pub hints: Option<JsonValue>,
49 #[serde(skip)]
50 #[schemars(skip)]
51 hints_builder: TableHints,
52 #[serde(skip)]
53 #[schemars(skip)]
54 metadata: ExecutionMetadata,
55}
56
57fn default_artifact_type() -> String {
58 "table".to_owned()
59}
60
61impl TableArtifact {
62 pub const ARTIFACT_TYPE_STR: &'static str = "table";
63
64 pub fn new(columns: Vec<Column>) -> Self {
65 Self {
66 artifact_type: "table".to_owned(),
67 columns,
68 items: Vec::new(),
69 hints: None,
70 hints_builder: TableHints::default(),
71 metadata: ExecutionMetadata::default(),
72 }
73 }
74
75 pub fn with_request(mut self, ctx: &RequestContext) -> Self {
76 self.metadata = ExecutionMetadata::with_request(ctx);
77 self
78 }
79
80 pub fn with_rows(mut self, items: Vec<JsonValue>) -> Self {
81 self.items = items;
82 self
83 }
84
85 pub fn with_hints(mut self, hints: TableHints) -> Self {
86 use crate::artifacts::traits::ArtifactSchema;
87 self.hints = Some(hints.generate_schema());
88 self.hints_builder = hints;
89 self
90 }
91
92 pub fn with_metadata(mut self, metadata: ExecutionMetadata) -> Self {
93 self.metadata = metadata;
94 self
95 }
96
97 pub fn with_execution_id(mut self, id: impl Into<String>) -> Self {
98 self.metadata.execution_id = Some(id.into());
99 self
100 }
101
102 pub fn with_skill(
103 mut self,
104 skill_id: impl Into<SkillId>,
105 skill_name: impl Into<String>,
106 ) -> Self {
107 self.metadata.skill_id = Some(skill_id.into());
108 self.metadata.skill_name = Some(skill_name.into());
109 self
110 }
111
112 pub fn to_response(&self) -> JsonValue {
113 use crate::artifacts::traits::ArtifactSchema;
114
115 let response = TableResponse {
116 artifact_type: "table".to_owned(),
117 columns: self.columns.clone(),
118 items: self.items.clone(),
119 count: self.items.len(),
120 execution_id: self.metadata.execution_id.clone(),
121 hints: Some(self.hints_builder.generate_schema()),
122 };
123 match serde_json::to_value(response) {
124 Ok(v) => v,
125 Err(e) => {
126 tracing::error!(error = %e, "Failed to serialize table response");
127 JsonValue::Null
128 },
129 }
130 }
131}
132
133impl Artifact for TableArtifact {
134 fn artifact_type(&self) -> ArtifactType {
135 ArtifactType::Table
136 }
137
138 fn to_schema(&self) -> JsonValue {
139 use crate::artifacts::traits::ArtifactSchema;
140
141 json!({
142 "type": "object",
143 "properties": {
144 "columns": {
145 "type": "array",
146 "description": "Column definitions"
147 },
148 "items": {
149 "type": "array",
150 "description": "Array of data records"
151 },
152 "count": {
153 "type": "integer",
154 "description": "Total number of records"
155 },
156 "_execution_id": {
157 "type": "string",
158 "description": "Execution ID for tracking"
159 }
160 },
161 "required": ["columns", "items"],
162 "x-artifact-type": "table",
163 "x-table-hints": self.hints_builder.generate_schema()
164 })
165 }
166}