1use std::collections::BTreeMap;
2use std::fmt;
3use std::sync::Arc;
4use std::sync::atomic::{AtomicU64, Ordering};
5use std::time::Duration;
6
7use futures_timer::Delay;
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use thiserror::Error;
11
12macro_rules! id_newtype {
13 ($name:ident) => {
14 #[derive(
15 Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
16 )]
17 pub struct $name(pub String);
18
19 impl $name {
20 pub fn new(value: impl Into<String>) -> Self {
21 Self(value.into())
22 }
23 }
24
25 impl fmt::Display for $name {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 self.0.fmt(f)
28 }
29 }
30
31 impl From<&str> for $name {
32 fn from(value: &str) -> Self {
33 Self::new(value)
34 }
35 }
36
37 impl From<String> for $name {
38 fn from(value: String) -> Self {
39 Self(value)
40 }
41 }
42 };
43}
44
45id_newtype!(SessionId);
46id_newtype!(TurnId);
47id_newtype!(MessageId);
48id_newtype!(ToolCallId);
49id_newtype!(ToolResultId);
50id_newtype!(ProviderMessageId);
51id_newtype!(ArtifactId);
52id_newtype!(PartId);
53id_newtype!(ApprovalId);
54
55pub type MetadataMap = BTreeMap<String, Value>;
56
57#[derive(Default)]
58struct CancellationState {
59 generation: AtomicU64,
60}
61
62#[derive(Clone, Default)]
63pub struct CancellationController {
64 state: Arc<CancellationState>,
65}
66
67#[derive(Clone, Default)]
68pub struct CancellationHandle {
69 state: Arc<CancellationState>,
70}
71
72#[derive(Clone, Default)]
73pub struct TurnCancellation {
74 handle: CancellationHandle,
75 generation: u64,
76}
77
78impl CancellationController {
79 pub fn new() -> Self {
80 Self::default()
81 }
82
83 pub fn handle(&self) -> CancellationHandle {
84 CancellationHandle {
85 state: Arc::clone(&self.state),
86 }
87 }
88
89 pub fn interrupt(&self) -> u64 {
90 self.state.generation.fetch_add(1, Ordering::SeqCst) + 1
91 }
92}
93
94impl CancellationHandle {
95 pub fn generation(&self) -> u64 {
96 self.state.generation.load(Ordering::SeqCst)
97 }
98
99 pub fn checkpoint(&self) -> TurnCancellation {
100 TurnCancellation {
101 handle: self.clone(),
102 generation: self.generation(),
103 }
104 }
105
106 pub fn is_cancelled_since(&self, generation: u64) -> bool {
107 self.generation() != generation
108 }
109
110 pub async fn cancelled_since(&self, generation: u64) {
111 while !self.is_cancelled_since(generation) {
112 Delay::new(Duration::from_millis(10)).await;
113 }
114 }
115}
116
117impl TurnCancellation {
118 pub fn new(handle: CancellationHandle) -> Self {
119 handle.checkpoint()
120 }
121
122 pub fn generation(&self) -> u64 {
123 self.generation
124 }
125
126 pub fn is_cancelled(&self) -> bool {
127 self.handle.is_cancelled_since(self.generation)
128 }
129
130 pub async fn cancelled(&self) {
131 self.handle.cancelled_since(self.generation).await;
132 }
133
134 pub fn handle(&self) -> &CancellationHandle {
135 &self.handle
136 }
137}
138
139#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
140pub struct Item {
141 pub id: Option<MessageId>,
142 pub kind: ItemKind,
143 pub parts: Vec<Part>,
144 pub metadata: MetadataMap,
145}
146
147#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
148pub enum ItemKind {
149 System,
150 Developer,
151 User,
152 Assistant,
153 Tool,
154 Context,
155}
156
157#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
158pub enum Part {
159 Text(TextPart),
160 Media(MediaPart),
161 File(FilePart),
162 Structured(StructuredPart),
163 Reasoning(ReasoningPart),
164 ToolCall(ToolCallPart),
165 ToolResult(ToolResultPart),
166 Custom(CustomPart),
167}
168
169#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
170pub enum PartKind {
171 Text,
172 Media,
173 File,
174 Structured,
175 Reasoning,
176 ToolCall,
177 ToolResult,
178 Custom,
179}
180
181#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
182pub struct TextPart {
183 pub text: String,
184 pub metadata: MetadataMap,
185}
186
187#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
188pub struct MediaPart {
189 pub modality: Modality,
190 pub mime_type: String,
191 pub data: DataRef,
192 pub metadata: MetadataMap,
193}
194
195#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
196pub enum Modality {
197 Audio,
198 Image,
199 Video,
200 Binary,
201}
202
203#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
204pub enum DataRef {
205 InlineText(String),
206 InlineBytes(Vec<u8>),
207 Uri(String),
208 Handle(ArtifactId),
209}
210
211#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
212pub struct FilePart {
213 pub name: Option<String>,
214 pub mime_type: Option<String>,
215 pub data: DataRef,
216 pub metadata: MetadataMap,
217}
218
219#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
220pub struct StructuredPart {
221 pub value: Value,
222 pub schema: Option<Value>,
223 pub metadata: MetadataMap,
224}
225
226#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
227pub struct ReasoningPart {
228 pub summary: Option<String>,
229 pub data: Option<DataRef>,
230 pub redacted: bool,
231 pub metadata: MetadataMap,
232}
233
234#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
235pub struct ToolCallPart {
236 pub id: ToolCallId,
237 pub name: String,
238 pub input: Value,
239 pub metadata: MetadataMap,
240}
241
242#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
243pub struct ToolResultPart {
244 pub call_id: ToolCallId,
245 pub output: ToolOutput,
246 pub is_error: bool,
247 pub metadata: MetadataMap,
248}
249
250#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
251pub enum ToolOutput {
252 Text(String),
253 Structured(Value),
254 Parts(Vec<Part>),
255 Files(Vec<FilePart>),
256}
257
258#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
259pub struct CustomPart {
260 pub kind: String,
261 pub data: Option<DataRef>,
262 pub value: Option<Value>,
263 pub metadata: MetadataMap,
264}
265
266#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
267pub enum Delta {
268 BeginPart {
269 part_id: PartId,
270 kind: PartKind,
271 },
272 AppendText {
273 part_id: PartId,
274 chunk: String,
275 },
276 AppendBytes {
277 part_id: PartId,
278 chunk: Vec<u8>,
279 },
280 ReplaceStructured {
281 part_id: PartId,
282 value: Value,
283 },
284 SetMetadata {
285 part_id: PartId,
286 metadata: MetadataMap,
287 },
288 CommitPart {
289 part: Part,
290 },
291}
292
293#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
294pub struct Usage {
295 pub tokens: Option<TokenUsage>,
296 pub cost: Option<CostUsage>,
297 pub metadata: MetadataMap,
298}
299
300#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
301pub struct TokenUsage {
302 pub input_tokens: u64,
303 pub output_tokens: u64,
304 pub reasoning_tokens: Option<u64>,
305 pub cached_input_tokens: Option<u64>,
306}
307
308#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
309pub struct CostUsage {
310 pub amount: f64,
311 pub currency: String,
312 pub provider_amount: Option<String>,
313}
314
315#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
316pub enum FinishReason {
317 Completed,
318 ToolCall,
319 MaxTokens,
320 Cancelled,
321 Blocked,
322 Error,
323 Other(String),
324}
325
326pub trait ItemView {
327 fn kind(&self) -> ItemKind;
328 fn parts(&self) -> &[Part];
329 fn metadata(&self) -> &MetadataMap;
330}
331
332impl ItemView for Item {
333 fn kind(&self) -> ItemKind {
334 self.kind
335 }
336
337 fn parts(&self) -> &[Part] {
338 &self.parts
339 }
340
341 fn metadata(&self) -> &MetadataMap {
342 &self.metadata
343 }
344}
345
346#[derive(Debug, Error)]
347pub enum NormalizeError {
348 #[error("unsupported content shape: {0}")]
349 Unsupported(String),
350}
351
352#[derive(Debug, Error)]
353pub enum ProtocolError {
354 #[error("invalid protocol state: {0}")]
355 InvalidState(String),
356}
357
358#[derive(Debug, Error)]
359pub enum AgentError {
360 #[error(transparent)]
361 Normalize(#[from] NormalizeError),
362 #[error(transparent)]
363 Protocol(#[from] ProtocolError),
364}