1use crate::normalizer::{Client, Model, Response};
2use crate::settings::LlmBackendSettings;
3use anyhow::Result;
4use llm_connector::types::Tool;
5use llm_connector::StreamFormat;
6use tokio_stream::wrappers::UnboundedReceiverStream;
7
8pub struct Service {
16 client: Client,
17 #[allow(dead_code)]
18 model: String,
19}
20
21impl Service {
22 pub fn new(config: &LlmBackendSettings) -> Result<Self> {
24 let client = Client::new(config)?;
25 let model = match config {
26 LlmBackendSettings::OpenAI { model, .. } => model.clone(),
27 LlmBackendSettings::Anthropic { model, .. } => model.clone(),
28 LlmBackendSettings::Ollama { model, .. } => model.clone(),
29 LlmBackendSettings::Aliyun { model, .. } => model.clone(),
30 LlmBackendSettings::Zhipu { model, .. } => model.clone(),
31 LlmBackendSettings::Volcengine { model, .. } => model.clone(),
32 LlmBackendSettings::Tencent { model, .. } => model.clone(),
33 LlmBackendSettings::Longcat { model, .. } => model.clone(),
34 LlmBackendSettings::Moonshot { model, .. } => model.clone(),
35 LlmBackendSettings::Minimax { model, .. } => model.clone(),
36 };
37
38 Ok(Self { client, model })
39 }
40
41 #[allow(dead_code)]
45 pub async fn chat(
46 &self,
47 model: Option<&str>,
48 messages: Vec<llm_connector::types::Message>,
49 tools: Option<Vec<Tool>>,
50 ) -> Result<Response> {
51 let requested = model.unwrap_or(&self.model);
52 let backend_model = self
53 .client
54 .resolve_model(requested, &self.model);
55 self.client.chat(&backend_model, messages, tools).await
56 }
57
58 #[allow(dead_code)]
62 pub async fn chat_stream_ollama(
63 &self,
64 model: Option<&str>,
65 messages: Vec<llm_connector::types::Message>,
66 format: StreamFormat,
67 ) -> Result<UnboundedReceiverStream<String>> {
68 let requested = model.unwrap_or(&self.model);
69 let backend_model = self
70 .client
71 .resolve_model(requested, &self.model);
72 self.client
73 .chat_stream_with_format(&backend_model, messages, format)
74 .await
75 }
76
77 #[allow(dead_code)]
81 pub async fn chat_stream_ollama_with_tools(
82 &self,
83 model: Option<&str>,
84 messages: Vec<llm_connector::types::Message>,
85 tools: Option<Vec<llm_connector::types::Tool>>,
86 format: StreamFormat,
87 ) -> Result<UnboundedReceiverStream<String>> {
88 let requested = model.unwrap_or(&self.model);
89 let backend_model = self
90 .client
91 .resolve_model(requested, &self.model);
92 self.client
93 .chat_stream_with_format_and_tools(&backend_model, messages, tools, format)
94 .await
95 }
96
97 #[allow(dead_code)]
101 pub async fn chat_stream_openai(
102 &self,
103 model: Option<&str>,
104 messages: Vec<llm_connector::types::Message>,
105 tools: Option<Vec<Tool>>,
106 format: StreamFormat,
107 ) -> Result<UnboundedReceiverStream<String>> {
108 let requested = model.unwrap_or(&self.model);
109 let backend_model = self
110 .client
111 .resolve_model(requested, &self.model);
112 self.client
113 .chat_stream_openai(&backend_model, messages, tools, format)
114 .await
115 }
116
117 pub async fn list_models(&self) -> Result<Vec<Model>> {
119 self.client.list_models().await
120 }
121
122 #[allow(dead_code)]
124 pub async fn validate_model(&self, model: &str) -> Result<bool> {
125 let available_models = self.client.list_models().await?;
126 Ok(available_models.iter().any(|m| m.id == model))
127 }
128}