mermaid_cli/models/
model.rs1use async_trait::async_trait;
8use std::sync::Arc;
9
10use super::backend::Backend;
11use super::config::{BackendConfig, ModelConfig};
12use super::error::Result;
13use super::router::BackendRouter;
14use super::traits::Model;
15use super::types::{ChatMessage, ModelResponse, ProjectContext, StreamCallback};
16
17pub struct UnifiedModel {
22 model_spec: String,
24
25 router: Arc<BackendRouter>,
27
28 cached_backend: Option<(Arc<dyn Backend>, String)>,
30}
31
32impl UnifiedModel {
33 pub fn new(model_spec: &str, config: BackendConfig) -> Self {
35 Self {
36 model_spec: model_spec.to_string(),
37 router: Arc::new(BackendRouter::new(config)),
38 cached_backend: None,
39 }
40 }
41
42 async fn get_backend(&mut self) -> Result<(Arc<dyn Backend>, String)> {
44 if let Some(ref cached) = self.cached_backend {
46 return Ok(cached.clone());
47 }
48
49 let (backend, model_name) = self.router.resolve_model(&self.model_spec).await?;
51
52 self.cached_backend = Some((backend.clone(), model_name.clone()));
54
55 Ok((backend, model_name))
56 }
57}
58
59#[async_trait]
60impl Model for UnifiedModel {
61 async fn chat(
62 &mut self,
63 messages: &[ChatMessage],
64 context: &ProjectContext,
65 config: &ModelConfig,
66 stream_callback: Option<StreamCallback>,
67 ) -> Result<ModelResponse> {
68 let (backend, model_name) = self.get_backend().await?;
70
71 backend.chat(&model_name, messages, context, config, stream_callback).await
73 }
74
75 fn name(&self) -> &str {
76 &self.model_spec
77 }
78
79 fn is_local(&self) -> bool {
80 if let Some((ref backend, _)) = self.cached_backend {
83 backend.metadata().is_local
84 } else {
85 !self.model_spec.starts_with("openai/")
87 && !self.model_spec.starts_with("anthropic/")
88 && !self.model_spec.starts_with("groq/")
89 }
90 }
91
92 async fn validate_connection(&self) -> Result<bool> {
93 if let Some((ref backend, _)) = self.cached_backend {
95 Ok(backend.health_check().await.is_ok())
96 } else {
97 Ok(true)
100 }
101 }
102}
103
104pub async fn create_model(model_spec: &str, backend_config: BackendConfig) -> Result<Box<dyn Model>> {
113 Ok(Box::new(UnifiedModel::new(model_spec, backend_config)))
114}
115
116pub async fn create_model_default(model_spec: &str) -> Result<Box<dyn Model>> {
118 create_model(model_spec, BackendConfig::default()).await
119}