mixtape_core/provider/
mod.rs1#[cfg(feature = "anthropic")]
7pub mod anthropic;
8#[cfg(feature = "bedrock")]
9pub mod bedrock;
10pub mod retry;
11
12use crate::events::TokenUsage;
13use crate::types::{Message, StopReason, ToolDefinition, ToolUseBlock};
14use futures::stream::BoxStream;
15use std::error::Error;
16
17#[cfg(feature = "anthropic")]
19pub use anthropic::AnthropicProvider;
20#[cfg(feature = "bedrock")]
21pub use bedrock::{
22 BedrockChatCompletionsProvider, BedrockProvider, BedrockResponsesProvider, InferenceProfile,
23};
24pub use retry::{RetryCallback, RetryConfig, RetryInfo};
25
26pub use crate::model::ModelResponse;
28
29#[derive(Debug, Clone)]
31pub enum StreamEvent {
32 TextDelta(String),
34 ToolUse(ToolUseBlock),
36 ThinkingDelta(String),
38 ContentBlock(crate::types::ContentBlock),
42 Stop {
44 stop_reason: StopReason,
46 usage: Option<TokenUsage>,
48 },
49}
50
51#[derive(Debug, thiserror::Error)]
53pub enum ProviderError {
54 #[error("Authentication failed: {0}")]
56 Authentication(String),
57
58 #[error("Rate limited: {0}")]
60 RateLimited(String),
61
62 #[error("Network error: {0}")]
64 Network(String),
65
66 #[error("Model error: {0}")]
68 Model(String),
69
70 #[error("Service unavailable: {0}")]
72 ServiceUnavailable(String),
73
74 #[error("Invalid configuration: {0}")]
76 Configuration(String),
77
78 #[error("{0}")]
80 Other(String),
81
82 #[error("Communication error: {0}")]
84 Communication(#[from] Box<dyn Error + Send + Sync>),
85}
86
87#[async_trait::async_trait]
102pub trait ModelProvider: Send + Sync {
103 fn name(&self) -> &str;
105
106 fn max_context_tokens(&self) -> usize;
108
109 fn max_output_tokens(&self) -> usize;
111
112 fn estimate_token_count(&self, text: &str) -> usize {
117 text.len().div_ceil(4)
118 }
119
120 fn estimate_message_tokens(&self, messages: &[Message]) -> usize {
122 let mut total = 0;
123 for message in messages {
124 total += 4; for block in &message.content {
126 total += self.estimate_token_count(&format!("{:?}", block));
127 }
128 }
129 total
130 }
131
132 async fn generate(
139 &self,
140 messages: Vec<Message>,
141 tools: Vec<ToolDefinition>,
142 system_prompt: Option<String>,
143 ) -> Result<ModelResponse, ProviderError>;
144
145 async fn generate_stream(
152 &self,
153 messages: Vec<Message>,
154 tools: Vec<ToolDefinition>,
155 system_prompt: Option<String>,
156 ) -> Result<BoxStream<'static, Result<StreamEvent, ProviderError>>, ProviderError> {
157 let response = self.generate(messages, tools, system_prompt).await?;
159
160 let mut events = Vec::new();
162 for block in response.message.content {
163 match &block {
164 crate::types::ContentBlock::Text(text) => {
165 events.push(Ok(StreamEvent::TextDelta(text.clone())));
166 }
167 crate::types::ContentBlock::ToolUse(tool_use) => {
168 events.push(Ok(StreamEvent::ToolUse(tool_use.clone())));
169 }
170 _ => {}
171 }
172 events.push(Ok(StreamEvent::ContentBlock(block)));
173 }
174 events.push(Ok(StreamEvent::Stop {
175 stop_reason: response.stop_reason,
176 usage: response.usage,
177 }));
178
179 Ok(Box::pin(futures::stream::iter(events)))
180 }
181}
182
183#[async_trait::async_trait]
185impl ModelProvider for std::sync::Arc<dyn ModelProvider> {
186 fn name(&self) -> &str {
187 (**self).name()
188 }
189
190 fn max_context_tokens(&self) -> usize {
191 (**self).max_context_tokens()
192 }
193
194 fn max_output_tokens(&self) -> usize {
195 (**self).max_output_tokens()
196 }
197
198 fn estimate_token_count(&self, text: &str) -> usize {
199 (**self).estimate_token_count(text)
200 }
201
202 fn estimate_message_tokens(&self, messages: &[Message]) -> usize {
203 (**self).estimate_message_tokens(messages)
204 }
205
206 async fn generate(
207 &self,
208 messages: Vec<Message>,
209 tools: Vec<ToolDefinition>,
210 system_prompt: Option<String>,
211 ) -> Result<ModelResponse, ProviderError> {
212 (**self).generate(messages, tools, system_prompt).await
213 }
214
215 async fn generate_stream(
216 &self,
217 messages: Vec<Message>,
218 tools: Vec<ToolDefinition>,
219 system_prompt: Option<String>,
220 ) -> Result<BoxStream<'static, Result<StreamEvent, ProviderError>>, ProviderError> {
221 (**self)
222 .generate_stream(messages, tools, system_prompt)
223 .await
224 }
225}