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