systemprompt_models/artifacts/tool_result/
mod.rs1use schemars::JsonSchema;
16use serde::{Deserialize, Serialize};
17use serde_json::{Value as JsonValue, json};
18
19use crate::artifacts::traits::Artifact;
20use crate::artifacts::types::ArtifactType;
21
22fn default_artifact_type() -> String {
23 ToolResultArtifact::ARTIFACT_TYPE_STR.to_owned()
24}
25
26#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
29#[serde(tag = "type", rename_all = "snake_case")]
30pub enum ToolResultBlock {
31 Text {
32 text: String,
33 },
34 Image {
35 mime_type: String,
36 byte_len: u64,
37 sha256: String,
38 },
39 Audio {
40 mime_type: String,
41 byte_len: u64,
42 sha256: String,
43 },
44 Resource {
45 uri: String,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
47 mime_type: Option<String>,
48 #[serde(default, skip_serializing_if = "Option::is_none")]
49 text: Option<String>,
50 #[serde(default, skip_serializing_if = "Option::is_none")]
51 blob_byte_len: Option<u64>,
52 #[serde(default, skip_serializing_if = "Option::is_none")]
53 blob_sha256: Option<String>,
54 },
55 ResourceLink {
56 uri: String,
57 name: String,
58 #[serde(default, skip_serializing_if = "Option::is_none")]
59 mime_type: Option<String>,
60 },
61}
62
63impl ToolResultBlock {
64 #[must_use]
65 pub fn is_ui_resource(&self) -> bool {
66 match self {
67 Self::Resource { uri, .. } | Self::ResourceLink { uri, .. } => uri.starts_with("ui://"),
68 Self::Text { .. } | Self::Image { .. } | Self::Audio { .. } => false,
69 }
70 }
71}
72
73#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
74pub struct ToolResultArtifact {
75 #[serde(rename = "x-artifact-type")]
76 #[serde(default = "default_artifact_type")]
77 pub artifact_type: String,
78 pub tool_name: String,
79 #[serde(default, skip_serializing_if = "Option::is_none")]
80 pub server_name: Option<String>,
81 #[serde(default)]
82 pub is_error: bool,
83 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
84 pub truncated: bool,
85 #[serde(default)]
86 pub blocks: Vec<ToolResultBlock>,
87 #[serde(default, skip_serializing_if = "Option::is_none")]
89 pub structured_content: Option<JsonValue>,
90}
91
92impl ToolResultArtifact {
93 pub const ARTIFACT_TYPE_STR: &'static str = "tool_result";
94
95 #[must_use]
96 pub fn new(tool_name: impl Into<String>) -> Self {
97 Self {
98 artifact_type: default_artifact_type(),
99 tool_name: tool_name.into(),
100 server_name: None,
101 is_error: false,
102 truncated: false,
103 blocks: Vec::new(),
104 structured_content: None,
105 }
106 }
107
108 #[must_use]
109 pub fn with_server(mut self, server_name: impl Into<String>) -> Self {
110 self.server_name = Some(server_name.into());
111 self
112 }
113
114 #[must_use]
115 pub const fn with_error(mut self, is_error: bool) -> Self {
116 self.is_error = is_error;
117 self
118 }
119
120 #[must_use]
121 pub fn with_blocks(mut self, blocks: Vec<ToolResultBlock>) -> Self {
122 self.blocks = blocks;
123 self
124 }
125
126 #[must_use]
127 pub fn with_structured_content(mut self, value: Option<JsonValue>) -> Self {
128 self.structured_content = value;
129 self
130 }
131
132 #[must_use]
133 pub fn has_ui_resource(&self) -> bool {
134 self.blocks.iter().any(ToolResultBlock::is_ui_resource)
135 }
136}
137
138impl Artifact for ToolResultArtifact {
139 fn artifact_type(&self) -> ArtifactType {
140 ArtifactType::ToolResult
141 }
142
143 fn to_schema(&self) -> JsonValue {
145 json!({
146 "type": "object",
147 "properties": {
148 "tool_name": { "type": "string" },
149 "server_name": { "type": "string" },
150 "is_error": { "type": "boolean" },
151 "blocks": { "type": "array", "items": { "type": "object" } },
152 "structured_content": { "type": "object" }
153 },
154 "required": ["tool_name", "blocks"],
155 "x-artifact-type": Self::ARTIFACT_TYPE_STR
156 })
157 }
158}