Skip to main content

agentkit_core/
lib.rs

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}