mermaid_cli/providers/model/
anthropic.rs1use async_trait::async_trait;
14
15use mermaid_domain::ChatRequest;
16use mermaid_model::models::adapters::anthropic::AnthropicAdapter;
17use mermaid_model::models::{Model, ModelConfig, ModelError, Result};
18
19use super::super::ctx::{FinalResponse, StreamContext, StreamEvent};
20use super::{ContextSizing, ModelProvider, RejectionCache, resolve_limits_cached};
21use mermaid_model::models::ModelCapabilities;
22
23pub const DEFAULT_BASE_URL: &str = "https://api.anthropic.com/v1";
29pub const DEFAULT_API_KEY_ENV: &str = "ANTHROPIC_API_KEY";
30
31pub struct AnthropicProvider {
33 adapter: AnthropicAdapter,
34 capabilities: ModelCapabilities,
35 rejections: RejectionCache,
37}
38
39impl AnthropicProvider {
40 pub fn new(api_key: String, model_name: String, base_url: String) -> Result<Self> {
48 let adapter = AnthropicAdapter::new(api_key, model_name, base_url)?;
49 let capabilities = adapter.capabilities().clone().with_provider_continuation();
50 Ok(Self {
51 adapter,
52 capabilities,
53 rejections: RejectionCache::default(),
54 })
55 }
56}
57
58#[async_trait]
59impl ModelProvider for AnthropicProvider {
60 fn capabilities(&self) -> &ModelCapabilities {
61 &self.capabilities
62 }
63
64 async fn resolve_context_window(&self, request: &ChatRequest) -> ContextSizing {
71 let _ = request;
72 let model = Model::name(&self.adapter).to_string();
73 let limits =
74 resolve_limits_cached("anthropic", &model, || self.adapter.fetch_model_limits()).await;
75 let window = limits.as_ref().and_then(|l| l.max_context_tokens);
76 self.rejections
77 .seed("anthropic", &model, self.adapter.param_memory())
78 .await;
79 ContextSizing {
80 model_max: window,
81 effective: window,
82 source: None,
83 max_output: limits.as_ref().and_then(|l| l.max_output_tokens),
84 compacts_natively: self.adapter.compacts_natively(),
85 }
86 }
87
88 async fn chat(&self, request: ChatRequest, ctx: StreamContext) -> Result<FinalResponse> {
89 let config = ModelConfig::from(&request);
90 let chat_fut = self
91 .adapter
92 .chat(&request.messages, &config, Some(ctx.sink.clone()));
93
94 let learned_model = Model::name(&self.adapter).to_string();
97 let memory = self.adapter.param_memory();
98 self.rejections
99 .seed("anthropic", &learned_model, memory)
100 .await;
101
102 let response = tokio::select! {
103 biased;
104 _ = ctx.token.cancelled() => {
105 return Err(ModelError::Cancelled);
106 },
107 r = chat_fut => {
108 self.rejections.persist("anthropic", &learned_model, memory).await;
109 r?
110 },
111 };
112
113 let usage = response.usage.clone();
114 let provider_continuation = response.provider_continuation.clone();
115 let stop_reason = response.stop_reason.clone();
116 let _ = ctx
122 .sink
123 .send(StreamEvent::Done {
124 usage: usage.clone(),
125 provider_continuation: provider_continuation.clone(),
126 stop_reason: stop_reason.clone(),
127 })
128 .await;
129
130 Ok(FinalResponse {
131 usage,
132 provider_continuation,
133 tool_calls: response.tool_calls.unwrap_or_default(),
134 stop_reason,
135 })
136 }
137}