Skip to content
Merged
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
14 changes: 14 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2530,6 +2530,7 @@ impl Config {

// Env-only routing: check for env overrides on channel/worker models.
// SPACEBOT_MODEL overrides all process types at once; specific vars take precedence.
// ANTHROPIC_MODEL sets all anthropic/* models to the specified value.
let mut routing = RoutingConfig::default();
if let Ok(model) = std::env::var("SPACEBOT_MODEL") {
routing.channel = model.clone();
Expand All @@ -2538,6 +2539,19 @@ impl Config {
routing.compactor = model.clone();
routing.cortex = model;
}
if let Ok(anthropic_model) = std::env::var("ANTHROPIC_MODEL") {
// ANTHROPIC_MODEL sets all anthropic/* routes to the specified model
let channel = format!("anthropic/{}", anthropic_model);
let branch = format!("anthropic/{}", anthropic_model);
let worker = format!("anthropic/{}", anthropic_model);
let compactor = format!("anthropic/{}", anthropic_model);
let cortex = format!("anthropic/{}", anthropic_model);
routing.channel = channel;
routing.branch = branch;
routing.worker = worker;
routing.compactor = compactor;
routing.cortex = cortex;
}
Comment on lines +2542 to +2554
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Normalize and validate ANTHROPIC_MODEL before composing routes.

This block currently accepts empty/whitespace values and double-prefixes values like anthropic/claude-*, which can produce invalid route strings and runtime routing failures.

🔧 Proposed fix
-        if let Ok(anthropic_model) = std::env::var("ANTHROPIC_MODEL") {
-            // ANTHROPIC_MODEL sets all anthropic/* routes to the specified model
-            let channel = format!("anthropic/{}", anthropic_model);
-            let branch = format!("anthropic/{}", anthropic_model);
-            let worker = format!("anthropic/{}", anthropic_model);
-            let compactor = format!("anthropic/{}", anthropic_model);
-            let cortex = format!("anthropic/{}", anthropic_model);
-            routing.channel = channel;
-            routing.branch = branch;
-            routing.worker = worker;
-            routing.compactor = compactor;
-            routing.cortex = cortex;
-        }
+        if let Ok(anthropic_model_raw) = std::env::var("ANTHROPIC_MODEL") {
+            // ANTHROPIC_MODEL sets all anthropic/* routes to the specified model.
+            let model = anthropic_model_raw
+                .trim()
+                .trim_start_matches("anthropic/");
+            if !model.is_empty() {
+                let route = format!("anthropic/{model}");
+                routing.channel = route.clone();
+                routing.branch = route.clone();
+                routing.worker = route.clone();
+                routing.compactor = route.clone();
+                routing.cortex = route;
+            }
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if let Ok(anthropic_model) = std::env::var("ANTHROPIC_MODEL") {
// ANTHROPIC_MODEL sets all anthropic/* routes to the specified model
let channel = format!("anthropic/{}", anthropic_model);
let branch = format!("anthropic/{}", anthropic_model);
let worker = format!("anthropic/{}", anthropic_model);
let compactor = format!("anthropic/{}", anthropic_model);
let cortex = format!("anthropic/{}", anthropic_model);
routing.channel = channel;
routing.branch = branch;
routing.worker = worker;
routing.compactor = compactor;
routing.cortex = cortex;
}
if let Ok(anthropic_model_raw) = std::env::var("ANTHROPIC_MODEL") {
// ANTHROPIC_MODEL sets all anthropic/* routes to the specified model.
let model = anthropic_model_raw
.trim()
.trim_start_matches("anthropic/");
if !model.is_empty() {
let route = format!("anthropic/{model}");
routing.channel = route.clone();
routing.branch = route.clone();
routing.worker = route.clone();
routing.compactor = route.clone();
routing.cortex = route;
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config.rs` around lines 2542 - 2554, Trim and validate the
ANTHROPIC_MODEL env var before using it to build routes: read
std::env::var("ANTHROPIC_MODEL"), call .trim() and if the result is empty do not
modify routing; then strip any leading "anthropic/" prefix (e.g. remove a
leading case-sensitive "anthropic/" if present) so you don't double-prefix,
optionally reject values containing path separators beyond a simple model token,
and finally compose the routes using the sanitized model (e.g.
format!("anthropic/{}", sanitized_model)) and assign to routing.channel,
routing.branch, routing.worker, routing.compactor, and routing.cortex.

if let Ok(channel_model) = std::env::var("SPACEBOT_CHANNEL_MODEL") {
routing.channel = channel_model;
}
Expand Down
8 changes: 5 additions & 3 deletions src/llm/anthropic/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,7 @@ mod tests {

#[test]
fn proxy_bearer_uses_bearer_header() {
let (request, auth_path) =
build_request_with_bearer("my-proxy-token", false, true);
let (request, auth_path) = build_request_with_bearer("my-proxy-token", false, true);
assert_eq!(auth_path, AnthropicAuthPath::ProxyBearer);
assert_eq!(
request.headers().get("Authorization").unwrap(),
Expand All @@ -298,7 +297,10 @@ mod tests {
let (request, _) = build_request_with_bearer("my-proxy-token", false, true);
assert!(request.headers().get("x-app").is_none());
// Should not have Claude Code user-agent
let ua = request.headers().get("user-agent").map(|v| v.to_str().unwrap().to_string());
let ua = request
.headers()
.get("user-agent")
.map(|v| v.to_str().unwrap().to_string());
assert!(ua.is_none() || !ua.unwrap().contains("claude-code"));
}

Expand Down
4 changes: 2 additions & 2 deletions src/llm/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ impl LlmManager {
base_url: "https://api.anthropic.com".to_string(),
api_key: token,
name: None,
use_bearer_auth: false,
use_bearer_auth: false,
}),
(None, None) => Err(LlmError::UnknownProvider("anthropic".to_string()).into()),
}
Expand Down Expand Up @@ -254,7 +254,7 @@ impl LlmManager {
base_url: "https://chatgpt.com/backend-api/codex".to_string(),
api_key: token,
name: None,
use_bearer_auth: false,
use_bearer_auth: false,
}),
None => Err(LlmError::UnknownProvider("openai-chatgpt".to_string()).into()),
}
Expand Down
5 changes: 4 additions & 1 deletion src/llm/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,10 @@ impl SpacebotModel {
fn remap_model_name_for_api(&self) -> String {
if self.provider == "zai-coding-plan" {
// Z.AI Coding Plan API expects "zai/glm-5" not "glm-5"
let model_name = self.model_name.strip_prefix("zai/").unwrap_or(&self.model_name);
let model_name = self
.model_name
.strip_prefix("zai/")
.unwrap_or(&self.model_name);
format!("zai/{model_name}")
} else {
self.model_name.clone()
Expand Down