Skip to main content

codetether_agent/provider/
types.rs

1//! Core types shared across all AI providers.
2//!
3//! Defines the message, request, response, and streaming types that every
4//! provider implementation must accept or produce.
5//!
6//! # Examples
7//!
8//! ```rust
9//! use codetether_agent::provider::{Message, Role, ContentPart};
10//!
11//! let msg = Message {
12//!     role: Role::User,
13//!     content: vec![ContentPart::Text { text: "hello".into() }],
14//! };
15//! assert_eq!(msg.role, Role::User);
16//! ```
17
18use serde::{Deserialize, Serialize};
19
20/// A message in a conversation.
21///
22/// # Examples
23///
24/// ```rust
25/// use codetether_agent::provider::{Message, Role, ContentPart};
26/// let msg = Message {
27///     role: Role::User,
28///     content: vec![ContentPart::Text { text: "hello".into() }],
29/// };
30/// assert_eq!(msg.role, Role::User);
31/// ```
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct Message {
34    /// Who sent this message.
35    pub role: Role,
36    /// Ordered content blocks (text, images, tool calls, etc.).
37    pub content: Vec<ContentPart>,
38}
39
40/// Participant role in a conversation.
41#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
42#[serde(rename_all = "lowercase")]
43pub enum Role {
44    /// System prompt / instructions.
45    System,
46    /// End-user input.
47    User,
48    /// Model response.
49    Assistant,
50    /// Tool result to be fed back to the model.
51    Tool,
52}
53
54/// One content block within a [`Message`].
55///
56/// # Examples
57///
58/// ```rust
59/// use codetether_agent::provider::ContentPart;
60/// let text = ContentPart::Text { text: "hi".into() };
61/// let tool_result = ContentPart::ToolResult {
62///     tool_call_id: "call_1".into(),
63///     content: "ok".into(),
64/// };
65/// ```
66#[derive(Debug, Clone, Serialize, Deserialize)]
67#[serde(tag = "type", rename_all = "snake_case")]
68pub enum ContentPart {
69    /// Plain text.
70    Text { text: String },
71    /// Image referenced by URL.
72    Image {
73        url: String,
74        mime_type: Option<String>,
75    },
76    /// File attachment.
77    File {
78        path: String,
79        mime_type: Option<String>,
80    },
81    /// A tool call made by the model.
82    ToolCall {
83        id: String,
84        name: String,
85        arguments: String,
86        /// Thought signature for Gemini 3.x models.
87        #[serde(skip_serializing_if = "Option::is_none")]
88        thought_signature: Option<String>,
89    },
90    /// Tool execution result to return to the model.
91    ToolResult {
92        tool_call_id: String,
93        content: String,
94    },
95    /// Extended thinking / reasoning output.
96    Thinking { text: String },
97}
98
99/// Schema-driven tool definition passed to the model.
100///
101/// # Examples
102///
103/// ```rust
104/// use codetether_agent::provider::ToolDefinition;
105/// let tool = ToolDefinition {
106///     name: "bash".into(),
107///     description: "Run a shell command".into(),
108///     parameters: serde_json::json!({"type": "object"}),
109/// };
110/// ```
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct ToolDefinition {
113    /// Tool function name (e.g. `"bash"`).
114    pub name: String,
115    /// Human-readable description the model uses to decide when to call.
116    pub description: String,
117    /// JSON Schema for the tool's input parameters.
118    pub parameters: serde_json::Value,
119}
120
121/// Request to generate a completion.
122///
123/// # Examples
124///
125/// ```rust
126/// use codetether_agent::provider::{CompletionRequest, Message, Role, ContentPart};
127/// let req = CompletionRequest {
128///     messages: vec![Message {
129///         role: Role::User,
130///         content: vec![ContentPart::Text { text: "hello".into() }],
131///     }],
132///     tools: vec![],
133///     model: "gpt-4o".into(),
134///     temperature: None,
135///     top_p: None,
136///     max_tokens: None,
137///     stop: vec![],
138/// };
139/// assert_eq!(req.model, "gpt-4o");
140/// ```
141#[derive(Debug, Clone)]
142pub struct CompletionRequest {
143    /// Conversation history (system + user + assistant + tool).
144    pub messages: Vec<Message>,
145    /// Tools the model may invoke.
146    pub tools: Vec<ToolDefinition>,
147    /// Model identifier (provider-alias or full ID).
148    pub model: String,
149    /// Sampling temperature (0–2).
150    pub temperature: Option<f32>,
151    /// Nucleus sampling threshold.
152    pub top_p: Option<f32>,
153    /// Maximum tokens the model should generate.
154    pub max_tokens: Option<usize>,
155    /// Strings that cause the model to stop generating.
156    pub stop: Vec<String>,
157}
158
159/// Request to generate embeddings.
160///
161/// # Examples
162///
163/// ```rust
164/// use codetether_agent::provider::EmbeddingRequest;
165/// let req = EmbeddingRequest {
166///     model: "text-embedding-3-small".into(),
167///     inputs: vec!["hello".into()],
168/// };
169/// ```
170#[derive(Debug, Clone)]
171pub struct EmbeddingRequest {
172    /// Embedding model identifier.
173    pub model: String,
174    /// Text inputs to embed.
175    pub inputs: Vec<String>,
176}
177
178/// Response from an embedding request.
179///
180/// # Examples
181///
182/// ```rust
183/// use codetether_agent::provider::{EmbeddingResponse, Usage};
184/// let resp = EmbeddingResponse {
185///     embeddings: vec![vec![0.1, 0.2]],
186///     usage: Usage::default(),
187/// };
188/// assert_eq!(resp.embeddings.len(), 1);
189/// ```
190#[derive(Debug, Clone)]
191pub struct EmbeddingResponse {
192    /// One float vector per input.
193    pub embeddings: Vec<Vec<f32>>,
194    /// Token usage.
195    pub usage: Usage,
196}
197
198/// A streaming chunk produced by [`Provider::complete_stream`](super::Provider::complete_stream).
199///
200/// # Examples
201///
202/// ```rust
203/// use codetether_agent::provider::StreamChunk;
204/// let chunk = StreamChunk::Text("hello".into());
205/// assert!(matches!(chunk, StreamChunk::Text(_)));
206/// ```
207#[derive(Debug, Clone)]
208pub enum StreamChunk {
209    /// Incremental text delta.
210    Text(String),
211    /// Model thinking/reasoning content.
212    Thinking(String),
213    /// Beginning of a tool call.
214    ToolCallStart { id: String, name: String },
215    /// Partial tool-call arguments.
216    ToolCallDelta { id: String, arguments_delta: String },
217    /// End of a tool call.
218    ToolCallEnd { id: String },
219    /// Stream finished.
220    Done { usage: Option<Usage> },
221    /// Recoverable error (stream continues).
222    Error(String),
223}
224
225/// Token usage statistics.
226///
227/// # Examples
228///
229/// ```rust
230/// use codetether_agent::provider::Usage;
231/// let usage = Usage { prompt_tokens: 10, completion_tokens: 5, ..Usage::default() };
232/// assert_eq!(usage.total_tokens, 0); // default
233/// ```
234#[derive(Debug, Clone, Default, Serialize, Deserialize)]
235pub struct Usage {
236    /// Tokens in the prompt.
237    pub prompt_tokens: usize,
238    /// Tokens in the completion.
239    pub completion_tokens: usize,
240    /// Total (prompt + completion).
241    pub total_tokens: usize,
242    /// Prompt-cache read hits (Anthropic / Bedrock).
243    pub cache_read_tokens: Option<usize>,
244    /// Prompt-cache write misses.
245    pub cache_write_tokens: Option<usize>,
246}
247
248/// Response from a completion request.
249///
250/// # Examples
251///
252/// ```rust
253/// use codetether_agent::provider::{CompletionResponse, FinishReason, Message, Role, ContentPart, Usage};
254/// let resp = CompletionResponse {
255///     message: Message { role: Role::Assistant, content: vec![] },
256///     usage: Usage::default(),
257///     finish_reason: FinishReason::Stop,
258/// };
259/// assert_eq!(resp.finish_reason, FinishReason::Stop);
260/// ```
261#[derive(Debug, Clone)]
262pub struct CompletionResponse {
263    /// The model's reply message.
264    pub message: Message,
265    /// Token usage for this request.
266    pub usage: Usage,
267    /// Why the model stopped generating.
268    pub finish_reason: FinishReason,
269}
270
271/// Reason the model stopped generating.
272#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
273#[serde(rename_all = "snake_case")]
274pub enum FinishReason {
275    /// Natural stop or stop sequence hit.
276    Stop,
277    /// Hit the `max_tokens` limit.
278    Length,
279    /// The model requested tool execution.
280    ToolCalls,
281    /// Content was filtered by safety systems.
282    ContentFilter,
283    /// An error occurred.
284    Error,
285}