Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
18 changes: 12 additions & 6 deletions src/clients/anthropic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -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)]
Expand Down Expand Up @@ -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,
}
}

Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -345,7 +351,7 @@ impl AnthropicClient {

let response = self
.client
.post(ANTHROPIC_API_URL)
.post(&self.base_url)
.headers(headers)
.json(&request)
.send()
Expand Down Expand Up @@ -406,7 +412,7 @@ impl AnthropicClient {
messages: Vec<Message>,
system: Option<String>,
config: &ApiConfig,
) -> Pin<Box<dyn Stream<Item = Result<StreamEvent>> + Send>> {
) -> Pin<Box<dyn Stream<Item = Result<StreamEvent>> + 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) })),
Expand All @@ -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()
Expand Down
18 changes: 12 additions & 6 deletions src/clients/deepseek.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -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)]
Expand Down Expand Up @@ -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,
}
}

Expand Down Expand Up @@ -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": {
Expand Down Expand Up @@ -300,7 +306,7 @@ impl DeepSeekClient {

let response = self
.client
.post(DEEPSEEK_API_URL)
.post(&self.base_url)
.headers(headers)
.json(&request)
.send()
Expand Down Expand Up @@ -359,7 +365,7 @@ impl DeepSeekClient {
&self,
messages: Vec<Message>,
config: &ApiConfig,
) -> Pin<Box<dyn Stream<Item = Result<StreamResponse>> + Send>> {
) -> Pin<Box<dyn Stream<Item = Result<StreamResponse>> + 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) })),
Expand All @@ -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()
Expand Down
8 changes: 8 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down