diff --git a/README.md b/README.md index 8e8005e..b08ab69 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,10 @@ Create a `config.toml` file in the project root: [server] host = "127.0.0.1" port = 3000 +anthropic_base_url = "https://api.anthropic.com" +deepseek_base_url = "https://api.deepseek.com" +anthropic_model_id = "claude-3-7-sonnet-20250219" +deepseek_model_id = "deepseek-reasoner" [pricing] # Configure pricing settings for usage tracking diff --git a/config.toml b/config.toml index eff690d..3d2233f 100644 --- a/config.toml +++ b/config.toml @@ -2,6 +2,10 @@ [server] host = "127.0.0.1" port = 1337 +anthropic_base_url = "https://api.anthropic.com" +deepseek_base_url = "https://api.deepseek.com" +anthropic_model_id = "claude-3-7-sonnet-20250219" +deepseek_model_id = "deepseek-reasoner" # Pricing Configuration (per million tokens) [pricing] diff --git a/frontend/components/chat.tsx b/frontend/components/chat.tsx index 6f5b413..03dc475 100644 --- a/frontend/components/chat.tsx +++ b/frontend/components/chat.tsx @@ -468,7 +468,7 @@ export function Chat({ selectedModel, onModelChange, apiTokens }: ChatProps) { } } - const response = await fetch("https://api.deepclaude.com", { + const response = await fetch("http://127.0.0.1:1337", { method: "POST", signal: controller.signal, headers: { diff --git a/src/clients/anthropic.rs b/src/clients/anthropic.rs index ff47398..aeef16e 100644 --- a/src/clients/anthropic.rs +++ b/src/clients/anthropic.rs @@ -47,8 +47,7 @@ use std::{collections::HashMap, pin::Pin}; use futures::StreamExt; use serde_json; -pub(crate) const ANTHROPIC_API_URL: &str = "https://api.anthropic.com/v1/messages"; -const DEFAULT_MODEL: &str = "claude-3-5-sonnet-20241022"; +use crate::config::Config; /// Client for interacting with Anthropic's Claude models. /// @@ -66,6 +65,8 @@ const DEFAULT_MODEL: &str = "claude-3-5-sonnet-20241022"; pub struct AnthropicClient { pub(crate) client: Client, api_token: String, + base_url: String, + model_id: String, } #[derive(Debug, Deserialize, Serialize, Clone)] @@ -174,9 +175,14 @@ impl AnthropicClient { /// /// A new `AnthropicClient` instance configured with the provided API token pub fn new(api_token: String) -> Self { + let config = Config::load().unwrap_or_default(); + let base_url = format!("{}/v1/messages", config.server.anthropic_base_url.trim_end_matches('/')); + Self { client: Client::new(), api_token, + base_url, + model_id: config.server.anthropic_model_id, } } @@ -262,7 +268,7 @@ impl AnthropicClient { .collect(); // Create base request with required fields - let default_model = serde_json::json!(DEFAULT_MODEL); + let default_model = serde_json::json!(&self.model_id); let model_value = config.body.get("model").unwrap_or(&default_model); let default_max_tokens = if let Some(model_str) = model_value.as_str() { @@ -345,7 +351,7 @@ impl AnthropicClient { let response = self .client - .post(ANTHROPIC_API_URL) + .post(&self.base_url) .headers(headers) .json(&request) .send() @@ -406,7 +412,7 @@ impl AnthropicClient { messages: Vec, system: Option, config: &ApiConfig, - ) -> Pin> + Send>> { + ) -> Pin> + Send + '_>> { let headers = match self.build_headers(Some(&config.headers)) { Ok(h) => h, Err(e) => return Box::pin(futures::stream::once(async move { Err(e) })), @@ -417,7 +423,7 @@ impl AnthropicClient { Box::pin(async_stream::try_stream! { let mut stream = client - .post(ANTHROPIC_API_URL) + .post(&self.base_url) .headers(headers) .json(&request) .send() diff --git a/src/clients/deepseek.rs b/src/clients/deepseek.rs index 1fbd67a..62369a6 100644 --- a/src/clients/deepseek.rs +++ b/src/clients/deepseek.rs @@ -65,8 +65,7 @@ use std::{collections::HashMap, pin::Pin}; use futures::StreamExt; use serde_json; -pub(crate) const DEEPSEEK_API_URL: &str = "https://api.deepseek.com/chat/completions"; -const DEFAULT_MODEL: &str = "deepseek-reasoner"; +use crate::config::Config; /// Client for interacting with DeepSeek's AI models. /// @@ -84,6 +83,8 @@ const DEFAULT_MODEL: &str = "deepseek-reasoner"; pub struct DeepSeekClient { pub(crate) client: Client, api_token: String, + base_url: String, + model_id: String, } #[derive(Debug, Deserialize, Serialize, Clone)] @@ -170,9 +171,14 @@ pub(crate) struct DeepSeekRequest { impl DeepSeekClient { pub fn new(api_token: String) -> Self { + let config = Config::load().unwrap_or_default(); + let base_url = format!("{}/chat/completions", config.server.deepseek_base_url.trim_end_matches('/')); + Self { client: Client::new(), api_token, + base_url, + model_id: config.server.deepseek_model_id, } } @@ -242,7 +248,7 @@ impl DeepSeekClient { "messages": messages, "stream": stream, // Set defaults only if not provided in config - "model": config.body.get("model").unwrap_or(&serde_json::json!(DEFAULT_MODEL)), + "model": config.body.get("model").unwrap_or(&serde_json::json!(&self.model_id)), "max_tokens": config.body.get("max_tokens").unwrap_or(&serde_json::json!(8192)), "temperature": config.body.get("temperature").unwrap_or(&serde_json::json!(1.0)), "response_format": { @@ -300,7 +306,7 @@ impl DeepSeekClient { let response = self .client - .post(DEEPSEEK_API_URL) + .post(&self.base_url) .headers(headers) .json(&request) .send() @@ -359,7 +365,7 @@ impl DeepSeekClient { &self, messages: Vec, config: &ApiConfig, - ) -> Pin> + Send>> { + ) -> Pin> + Send + '_>> { let headers = match self.build_headers(Some(&config.headers)) { Ok(h) => h, Err(e) => return Box::pin(futures::stream::once(async move { Err(e) })), @@ -370,7 +376,7 @@ impl DeepSeekClient { Box::pin(async_stream::try_stream! { let mut stream = client - .post(DEEPSEEK_API_URL) + .post(&self.base_url) .headers(headers) .json(&request) .send() diff --git a/src/config.rs b/src/config.rs index 5ee8fff..88dc057 100644 --- a/src/config.rs +++ b/src/config.rs @@ -25,6 +25,10 @@ pub struct Config { pub struct ServerConfig { pub host: String, pub port: u16, + pub anthropic_base_url: String, + pub deepseek_base_url: String, + pub anthropic_model_id: String, + pub deepseek_model_id: String, } /// Pricing configuration for all supported AI models. @@ -107,6 +111,10 @@ impl Default for Config { server: ServerConfig { host: "127.0.0.1".to_string(), port: 3000, + anthropic_base_url: "https://api.anthropic.com".to_string(), + deepseek_base_url: "https://api.deepseek.com".to_string(), + anthropic_model_id: "claude-3-5-sonnet-20241022".to_string(), + deepseek_model_id: "deepseek-reasoner".to_string(), }, pricing: PricingConfig { deepseek: DeepSeekPricing {