Claude 3.7 is out. we had some harcoded stuff (#6197)

Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Douwe Osinga
2026-01-06 15:50:39 -05:00
committed by GitHub
parent 635131d7bf
commit 463eee3f76
6 changed files with 24 additions and 98 deletions
-4
View File
@@ -37,10 +37,6 @@ const ANTHROPIC_KNOWN_MODELS: &[&str] = &[
"claude-sonnet-4-20250514", "claude-sonnet-4-20250514",
"claude-opus-4-0", "claude-opus-4-0",
"claude-opus-4-20250514", "claude-opus-4-20250514",
// Legacy Claude 3.x models
"claude-3-7-sonnet-latest",
"claude-3-7-sonnet-20250219",
"claude-3-opus-latest",
]; ];
const ANTHROPIC_DOC_URL: &str = "https://docs.anthropic.com/en/docs/about-claude/models"; const ANTHROPIC_DOC_URL: &str = "https://docs.anthropic.com/en/docs/about-claude/models";
@@ -333,55 +333,23 @@ mod tests {
Some("anthropic/claude-3.5-sonnet".to_string()) Some("anthropic/claude-3.5-sonnet".to_string())
); );
// === Claude word-order swapping (3.x series) === // 3.x: {model}-{version} → {version}-{model}
assert_eq!( assert_eq!(
map_to_canonical_model("databricks", "claude-haiku-3-5", r), map_to_canonical_model("databricks", "claude-haiku-3-5", r),
Some("anthropic/claude-3.5-haiku".to_string()) Some("anthropic/claude-3.5-haiku".to_string())
); );
assert_eq!(
map_to_canonical_model("databricks", "claude-sonnet-3-7", r),
Some("anthropic/claude-3.7-sonnet".to_string())
);
assert_eq!(
map_to_canonical_model("databricks", "ng-tools-claude-haiku-3-5", r),
Some("anthropic/claude-3.5-haiku".to_string())
);
// === Claude word-order swapping (4.x series) === // 4.x: {version}-{model} → {model}-{version}
assert_eq!(
map_to_canonical_model("databricks", "claude-4-opus", r),
Some("anthropic/claude-opus-4".to_string())
);
assert_eq!( assert_eq!(
map_to_canonical_model("databricks", "claude-4-sonnet", r), map_to_canonical_model("databricks", "claude-4-sonnet", r),
Some("anthropic/claude-sonnet-4".to_string()) Some("anthropic/claude-sonnet-4".to_string())
); );
// 4.x with minor version + prefix stripping
assert_eq!( assert_eq!(
map_to_canonical_model("databricks", "raml-claude-opus-4-5", r), map_to_canonical_model("databricks", "raml-claude-opus-4-5", r),
Some("anthropic/claude-opus-4.5".to_string()) Some("anthropic/claude-opus-4.5".to_string())
); );
assert_eq!(
map_to_canonical_model("databricks", "databricks-claude-sonnet-4-5", r),
Some("anthropic/claude-sonnet-4.5".to_string())
);
// === Claude with custom prefixes ===
assert_eq!(
map_to_canonical_model("databricks", "goose-claude-4-opus", r),
Some("anthropic/claude-opus-4".to_string())
);
assert_eq!(
map_to_canonical_model("databricks", "kgoose-claude-4-sonnet", r),
Some("anthropic/claude-sonnet-4".to_string())
);
assert_eq!(
map_to_canonical_model("databricks", "headless-goose-claude-4-sonnet", r),
Some("anthropic/claude-sonnet-4".to_string())
);
assert_eq!(
map_to_canonical_model("databricks", "kgoose-cashapp-claude-4-sonnet", r),
Some("anthropic/claude-sonnet-4".to_string())
);
// === Claude with platform suffixes === // === Claude with platform suffixes ===
assert_eq!( assert_eq!(
+17 -51
View File
@@ -394,9 +394,18 @@ pub fn create_request(
return Err(anyhow!("No valid messages to send to Anthropic API")); return Err(anyhow!("No valid messages to send to Anthropic API"));
} }
// https://docs.anthropic.com/en/docs/about-claude/models/all-models#model-comparison-table // https://platform.claude.com/docs/en/about-claude/models/overview
// Claude 3.7 supports max output tokens up to 8192 // 64k output tokens works for most claude models, but not old opus:
let max_tokens = model_config.max_tokens.unwrap_or(8192); let max_tokens = model_config.max_tokens.unwrap_or_else(|| {
let name = &model_config.model_name;
if name.contains("claude-3-haiku") {
4096
} else if name.contains("claude-opus-4-0") || name.contains("claude-opus-4-1") {
32000
} else {
64000
}
});
let mut payload = json!({ let mut payload = json!({
"model": model_config.model_name, "model": model_config.model_name,
"messages": anthropic_messages, "messages": anthropic_messages,
@@ -421,18 +430,15 @@ pub fn create_request(
// Add temperature if specified and not using extended thinking model // Add temperature if specified and not using extended thinking model
if let Some(temp) = model_config.temperature { if let Some(temp) = model_config.temperature {
// Claude 3.7 models with thinking enabled don't support temperature payload
if !model_config.model_name.starts_with("claude-3-7-sonnet-") { .as_object_mut()
payload .unwrap()
.as_object_mut() .insert("temperature".to_string(), json!(temp));
.unwrap()
.insert("temperature".to_string(), json!(temp));
}
} }
// Add thinking parameters for claude-3-7-sonnet model // Add thinking parameters for claude-3-7-sonnet model
let is_thinking_enabled = std::env::var("CLAUDE_THINKING_ENABLED").is_ok(); let is_thinking_enabled = std::env::var("CLAUDE_THINKING_ENABLED").is_ok();
if model_config.model_name.starts_with("claude-3-7-sonnet-") && is_thinking_enabled { if is_thinking_enabled {
// Minimum budget_tokens is 1024 // Minimum budget_tokens is 1024
let budget_tokens = std::env::var("CLAUDE_THINKING_BUDGET") let budget_tokens = std::env::var("CLAUDE_THINKING_BUDGET")
.unwrap_or_else(|_| "16000".to_string()) .unwrap_or_else(|_| "16000".to_string())
@@ -452,7 +458,6 @@ pub fn create_request(
}), }),
); );
} }
Ok(payload) Ok(payload)
} }
@@ -931,45 +936,6 @@ mod tests {
assert!(spec_array[0].get("cache_control").is_some()); assert!(spec_array[0].get("cache_control").is_some());
} }
#[test]
fn test_create_request_with_thinking() -> Result<()> {
let original_value = std::env::var("CLAUDE_THINKING_ENABLED").ok();
std::env::set_var("CLAUDE_THINKING_ENABLED", "true");
let result = (|| {
let model_config = ModelConfig::new_or_fail("claude-3-7-sonnet-20250219");
let system = "You are a helpful assistant.";
let messages = vec![Message::user().with_text("Hello")];
let tools = vec![];
let payload = create_request(&model_config, system, &messages, &tools)?;
// Verify basic structure
assert_eq!(payload["model"], "claude-3-7-sonnet-20250219");
assert_eq!(payload["messages"][0]["role"], "user");
assert_eq!(payload["messages"][0]["content"][0]["text"], "Hello");
// Verify thinking parameters
assert!(payload.get("thinking").is_some());
assert_eq!(payload["thinking"]["type"], "enabled");
assert!(payload["thinking"]["budget_tokens"].as_i64().unwrap() >= 1024);
// Temperature should not be present for 3.7 models with thinking
assert!(payload.get("temperature").is_none());
Ok(())
})();
// Restore the original env var state
match original_value {
Some(val) => std::env::set_var("CLAUDE_THINKING_ENABLED", val),
None => std::env::remove_var("CLAUDE_THINKING_ENABLED"),
}
// Return the test result
result
}
#[test] #[test]
fn test_cache_pricing_calculation() -> Result<()> { fn test_cache_pricing_calculation() -> Result<()> {
// Test realistic cache scenario: small fresh input, large cached content // Test realistic cache scenario: small fresh input, large cached content
@@ -583,7 +583,6 @@ pub fn create_request(
.insert("tools".to_string(), json!(tools_spec)); .insert("tools".to_string(), json!(tools_spec));
} }
// Add thinking parameters for Claude 3.7 Sonnet model when requested
let is_thinking_enabled = std::env::var("CLAUDE_THINKING_ENABLED").is_ok(); let is_thinking_enabled = std::env::var("CLAUDE_THINKING_ENABLED").is_ok();
if is_claude_sonnet && is_thinking_enabled { if is_claude_sonnet && is_thinking_enabled {
// Minimum budget_tokens is 1024 // Minimum budget_tokens is 1024
@@ -608,7 +607,6 @@ pub fn create_request(
}), }),
); );
// Temperature is fixed to 2 when using claude 3.7 thinking with Databricks
payload payload
.as_object_mut() .as_object_mut()
.unwrap() .unwrap()
+1 -3
View File
@@ -462,9 +462,7 @@ impl Provider for GcpVertexAIProvider {
"gcp_vertex_ai", "gcp_vertex_ai",
"GCP Vertex AI", "GCP Vertex AI",
"Access variety of AI models such as Claude, Gemini through Vertex AI", "Access variety of AI models such as Claude, Gemini through Vertex AI",
GcpVertexAIModel::Gemini(GeminiVersion::Flash25) "gemini-2.5-flash",
.to_string()
.as_str(),
known_models, known_models,
GCP_VERTEX_AI_DOC_URL, GCP_VERTEX_AI_DOC_URL,
vec![ vec![
+2 -2
View File
@@ -17,16 +17,16 @@ use crate::providers::formats::openai::{create_request, get_usage, response_to_m
use rmcp::model::Tool; use rmcp::model::Tool;
pub const OPENROUTER_DEFAULT_MODEL: &str = "anthropic/claude-sonnet-4"; pub const OPENROUTER_DEFAULT_MODEL: &str = "anthropic/claude-sonnet-4";
pub const OPENROUTER_DEFAULT_FAST_MODEL: &str = "google/gemini-flash-2.5"; pub const OPENROUTER_DEFAULT_FAST_MODEL: &str = "google/gemini-2.5-flash";
pub const OPENROUTER_MODEL_PREFIX_ANTHROPIC: &str = "anthropic"; pub const OPENROUTER_MODEL_PREFIX_ANTHROPIC: &str = "anthropic";
// OpenRouter can run many models, we suggest the default // OpenRouter can run many models, we suggest the default
pub const OPENROUTER_KNOWN_MODELS: &[&str] = &[ pub const OPENROUTER_KNOWN_MODELS: &[&str] = &[
"x-ai/grok-code-fast-1",
"anthropic/claude-sonnet-4.5", "anthropic/claude-sonnet-4.5",
"anthropic/claude-sonnet-4", "anthropic/claude-sonnet-4",
"anthropic/claude-opus-4.1", "anthropic/claude-opus-4.1",
"anthropic/claude-opus-4", "anthropic/claude-opus-4",
"anthropic/claude-3.7-sonnet",
"google/gemini-2.5-pro", "google/gemini-2.5-pro",
"google/gemini-2.5-flash", "google/gemini-2.5-flash",
"deepseek/deepseek-r1-0528", "deepseek/deepseek-r1-0528",