From bfd544f416622fea0a5ffcc092d7d173bb83f0cf Mon Sep 17 00:00:00 2001 From: Cavit Erginsoy Date: Tue, 28 Jan 2025 14:10:19 +0000 Subject: [PATCH 1/3] Fix: Change default fetch URL to local backend in chat component This commit updates the default `fetch` URL in `frontend/components/chat.tsx` from `https://api.deepclaude.com` to `http://127.0.0.1:1337`. **Reasoning:** The previous default configuration unintentionally directed API requests to the hosted `api.deepclaude.com` endpoint, even when users were running the frontend locally. This could lead to users inadvertently sending their API keys to the external server if they used the chat interface without realizing this default setting. This change ensures that by default, the frontend communicates with the local backend server running on `http://127.0.0.1:1337`, aligning with the expected behavior for users who are self-hosting the application. **Security Improvement:** By pointing to the local backend by default, this commit reduces the risk of users unintentionally exposing their API keys to an external server. Users who intend to use the managed `api.deepclaude.com` API can still manually change the fetch URL, but the default is now correctly configured for local development and self-hosting scenarios. --- frontend/components/chat.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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: { From 4d9deb653d3656c17557b91dbb12760775ef0580 Mon Sep 17 00:00:00 2001 From: David Manouchehri Date: Tue, 25 Feb 2025 18:19:08 +0000 Subject: [PATCH 2/3] Add support for setting custom models and endpoints. --- README.md | 4 ++++ config.toml | 4 ++++ src/clients/anthropic.rs | 18 ++++++++++++------ src/clients/deepseek.rs | 18 ++++++++++++------ src/config.rs | 8 ++++++++ 5 files changed, 40 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8e8005e..ccd0d7e 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-5-sonnet-20241022" +deepseek_model_id = "deepseek-reasoner" [pricing] # Configure pricing settings for usage tracking diff --git a/config.toml b/config.toml index eff690d..31d4053 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-5-sonnet-20241022" +deepseek_model_id = "deepseek-reasoner" # Pricing Configuration (per million tokens) [pricing] 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 { From 2c419a445a01e89acfedf5f23d8c81c2f444318e Mon Sep 17 00:00:00 2001 From: David Manouchehri Date: Tue, 25 Feb 2025 18:24:59 +0000 Subject: [PATCH 3/3] Use claude-3-7-sonnet-20250219 by default. --- README.md | 2 +- config.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ccd0d7e..b08ab69 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ 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-5-sonnet-20241022" +anthropic_model_id = "claude-3-7-sonnet-20250219" deepseek_model_id = "deepseek-reasoner" [pricing] diff --git a/config.toml b/config.toml index 31d4053..3d2233f 100644 --- a/config.toml +++ b/config.toml @@ -4,7 +4,7 @@ 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-5-sonnet-20241022" +anthropic_model_id = "claude-3-7-sonnet-20250219" deepseek_model_id = "deepseek-reasoner" # Pricing Configuration (per million tokens)