1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4
5#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
6#[serde(rename_all = "snake_case")]
7pub enum Role {
8 System,
9 User,
10 Assistant,
11 Tool,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
15pub struct ChatMessage {
16 pub role: Role,
17 pub content: String,
18 #[serde(skip_serializing_if = "Option::is_none")]
20 pub tool_call_id: Option<String>,
21}
22
23impl ChatMessage {
24 pub fn system(content: impl Into<String>) -> Self {
25 Self {
26 role: Role::System,
27 content: content.into(),
28 tool_call_id: None,
29 }
30 }
31
32 pub fn user(content: impl Into<String>) -> Self {
33 Self {
34 role: Role::User,
35 content: content.into(),
36 tool_call_id: None,
37 }
38 }
39
40 pub fn assistant(content: impl Into<String>) -> Self {
41 Self {
42 role: Role::Assistant,
43 content: content.into(),
44 tool_call_id: None,
45 }
46 }
47
48 pub fn tool(content: impl Into<String>) -> Self {
49 Self {
50 role: Role::Tool,
51 content: content.into(),
52 tool_call_id: None,
53 }
54 }
55
56 pub fn tool_result(call_id: impl Into<String>, content: impl Into<String>) -> Self {
57 Self {
58 role: Role::Tool,
59 content: content.into(),
60 tool_call_id: Some(call_id.into()),
61 }
62 }
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)]
67pub struct ToolAnnotations {
68 #[serde(default)]
70 pub read_only: bool,
71 #[serde(default)]
73 pub destructive: bool,
74 #[serde(default)]
76 pub idempotent: bool,
77 #[serde(default)]
79 pub open_world: bool,
80 #[serde(default)]
82 pub requires_confirmation: bool,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
86pub struct ToolDefinition {
87 pub name: String,
88 pub description: String,
89 pub input_schema: Value,
90
91 #[serde(default, skip_serializing_if = "Option::is_none")]
94 pub title: Option<String>,
95 #[serde(default, skip_serializing_if = "Option::is_none")]
97 pub output_schema: Option<Value>,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
100 pub annotations: Option<ToolAnnotations>,
101
102 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub category: Option<String>,
106 #[serde(default, skip_serializing_if = "Vec::is_empty")]
108 pub tags: Vec<String>,
109 #[serde(default, skip_serializing_if = "Option::is_none")]
111 pub timeout_secs: Option<u32>,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
116#[serde(tag = "type", rename_all = "snake_case")]
117pub enum ToolContent {
118 Text { text: String },
119 Image { data: String, mime_type: String },
120 Json { value: Value },
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
124pub struct ToolCall {
125 pub call_id: String,
126 pub tool_name: String,
127 #[serde(default)]
128 pub input: Value,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
132pub struct ToolResult {
133 pub call_id: String,
134 pub tool_name: String,
135 #[serde(default)]
136 pub output: Value,
137 #[serde(default, skip_serializing_if = "Option::is_none")]
139 pub content: Option<Vec<ToolContent>>,
140 #[serde(default)]
142 pub is_error: bool,
143 pub state_patch: Option<StatePatch>,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
147pub struct ToolResultSummary {
148 pub call_id: String,
149 pub tool_name: String,
150 #[serde(default)]
151 pub output: Value,
152}
153
154impl From<&ToolResult> for ToolResultSummary {
155 fn from(value: &ToolResult) -> Self {
156 Self {
157 call_id: value.call_id.clone(),
158 tool_name: value.tool_name.clone(),
159 output: value.output.clone(),
160 }
161 }
162}
163
164#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
165#[serde(rename_all = "snake_case")]
166pub enum StatePatchFormat {
167 JsonPatch,
168 MergePatch,
169}
170
171#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
172#[serde(rename_all = "snake_case")]
173pub enum StatePatchSource {
174 Model,
175 Tool,
176 System,
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
180pub struct StatePatch {
181 pub format: StatePatchFormat,
182 #[serde(default)]
183 pub patch: Value,
184 pub source: StatePatchSource,
185}
186
187#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
188#[serde(rename_all = "snake_case")]
189pub enum ModelStopReason {
190 EndTurn,
191 ToolUse,
192 NeedsUser,
193 MaxTokens,
194 Safety,
195 Unknown,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
199#[serde(tag = "kind", rename_all = "snake_case")]
200pub enum ModelDirective {
201 Text { delta: String },
202 ToolCall { call: ToolCall },
203 StatePatch { patch: StatePatch },
204 FinalAnswer { text: String },
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
208pub struct ModelTurn {
209 pub directives: Vec<ModelDirective>,
210 pub stop_reason: ModelStopReason,
211}
212
213#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
214#[serde(rename_all = "snake_case")]
215pub enum RunStopReason {
216 Completed,
217 NeedsUser,
218 BlockedByPolicy,
219 BudgetExceeded,
220 Error,
221}
222
223#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
224#[serde(tag = "part_type", rename_all = "snake_case")]
225pub enum AgentEvent {
226 RunStarted {
227 run_id: String,
228 session_id: String,
229 provider: String,
230 max_iterations: u32,
231 },
232 IterationStarted {
233 run_id: String,
234 session_id: String,
235 iteration: u32,
236 },
237 ModelOutput {
238 run_id: String,
239 session_id: String,
240 iteration: u32,
241 stop_reason: ModelStopReason,
242 directive_count: usize,
243 },
244 TextDelta {
245 run_id: String,
246 session_id: String,
247 iteration: u32,
248 delta: String,
249 },
250 ToolCallRequested {
251 run_id: String,
252 session_id: String,
253 iteration: u32,
254 call: ToolCall,
255 },
256 ToolCallCompleted {
257 run_id: String,
258 session_id: String,
259 iteration: u32,
260 result: ToolResultSummary,
261 },
262 ToolCallFailed {
263 run_id: String,
264 session_id: String,
265 iteration: u32,
266 call_id: String,
267 tool_name: String,
268 error: String,
269 },
270 StatePatched {
271 run_id: String,
272 session_id: String,
273 iteration: u32,
274 patch: StatePatch,
275 revision: u64,
276 },
277 RunErrored {
278 run_id: String,
279 session_id: String,
280 error: String,
281 },
282 RunFinished {
283 run_id: String,
284 session_id: String,
285 reason: RunStopReason,
286 total_iterations: u32,
287 final_answer: Option<String>,
288 },
289}
290
291impl AgentEvent {
292 pub fn as_sse_data(&self) -> Result<String, serde_json::Error> {
293 let payload = serde_json::to_string(self)?;
294 Ok(format!("data: {payload}\n\n"))
295 }
296}