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