Remove deprecated Claude 3.5 models (#4590)

This commit is contained in:
Angie Jones
2025-09-10 14:41:02 -05:00
committed by GitHub
parent 5e478e56c1
commit 63f3669cf7
49 changed files with 128 additions and 154 deletions
-2
View File
@@ -31,8 +31,6 @@ const ANTHROPIC_KNOWN_MODELS: &[&str] = &[
"claude-opus-4-20250514",
"claude-3-7-sonnet-latest",
"claude-3-7-sonnet-20250219",
"claude-3-5-sonnet-latest",
"claude-3-5-haiku-latest",
"claude-3-opus-latest",
];
+5 -5
View File
@@ -552,17 +552,17 @@ mod tests {
assert_eq!(model, Some("gpt-4o".to_string()));
// Change the model
set_current_model("claude-3.5-sonnet");
set_current_model("claude-sonnet-4-20250514");
// Get the updated model and verify
let model = get_current_model();
assert_eq!(model, Some("claude-3.5-sonnet".to_string()));
assert_eq!(model, Some("claude-sonnet-4-20250514".to_string()));
}
#[test]
fn test_provider_metadata_context_limits() {
// Test that ProviderMetadata::new correctly sets context limits
let test_models = vec!["gpt-4o", "claude-3-5-sonnet-latest", "unknown-model"];
let test_models = vec!["gpt-4o", "claude-sonnet-4-20250514", "unknown-model"];
let metadata = ProviderMetadata::new(
"test",
"Test Provider",
@@ -582,9 +582,9 @@ mod tests {
// gpt-4o should have 128k limit
assert_eq!(*model_info.get("gpt-4o").unwrap(), 128_000);
// claude-3-5-sonnet-latest should have 200k limit
// claude-sonnet-4-20250514 should have 200k limit
assert_eq!(
*model_info.get("claude-3-5-sonnet-latest").unwrap(),
*model_info.get("claude-sonnet-4-20250514").unwrap(),
200_000
);
+1 -3
View File
@@ -25,10 +25,8 @@ pub const BEDROCK_DOC_LINK: &str =
pub const BEDROCK_DEFAULT_MODEL: &str = "anthropic.claude-sonnet-4-20250514-v1:0";
pub const BEDROCK_KNOWN_MODELS: &[&str] = &[
"anthropic.claude-3-5-sonnet-20240620-v1:0",
"anthropic.claude-3-5-sonnet-20241022-v2:0",
"anthropic.claude-3-7-sonnet-20250219-v1:0",
"anthropic.claude-sonnet-4-20250514-v1:0",
"anthropic.claude-3-7-sonnet-20250219-v1:0",
"anthropic.claude-opus-4-20250514-v1:0",
"anthropic.claude-opus-4-1-20250805-v1:0",
];
+3 -3
View File
@@ -16,8 +16,8 @@ use crate::impl_provider_default;
use crate::model::ModelConfig;
use rmcp::model::Tool;
pub const CLAUDE_CODE_DEFAULT_MODEL: &str = "claude-3-5-sonnet-latest";
pub const CLAUDE_CODE_KNOWN_MODELS: &[&str] = &["sonnet", "opus", "claude-3-5-sonnet-latest"];
pub const CLAUDE_CODE_DEFAULT_MODEL: &str = "claude-sonnet-4-20250514";
pub const CLAUDE_CODE_KNOWN_MODELS: &[&str] = &["sonnet", "opus", "claude-sonnet-4-20250514"];
pub const CLAUDE_CODE_DOC_URL: &str = "https://claude.ai/cli";
@@ -525,7 +525,7 @@ mod tests {
let provider = ClaudeCodeProvider::default();
let config = provider.get_model_config();
assert_eq!(config.model_name, "claude-3-5-sonnet-latest");
assert_eq!(config.model_name, "claude-sonnet-4-20250514");
// Context limit should be set by the ModelConfig
assert!(config.context_limit() > 0);
}
@@ -692,7 +692,7 @@ mod tests {
"type": "text",
"text": "Hello! How can I assist you today?"
}],
"model": "claude-3-5-sonnet-latest",
"model": "claude-sonnet-4-20250514",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
@@ -962,7 +962,7 @@ mod tests {
"type": "text",
"text": "Based on the cached context, here's my response."
}],
"model": "claude-3-5-sonnet-latest",
"model": "claude-sonnet-4-20250514",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
@@ -73,14 +73,8 @@ pub enum GcpVertexAIModel {
/// Represents available versions of the Claude model for Goose.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ClaudeVersion {
/// Claude 3.5 Sonnet initial version
Sonnet35,
/// Claude 3.5 Sonnet version 2
Sonnet35V2,
/// Claude 3.7 Sonnet
Sonnet37,
/// Claude 3.5 Haiku
Haiku35,
/// Claude Sonnet 4
Sonnet4,
/// Claude Opus 4
@@ -116,10 +110,7 @@ impl fmt::Display for GcpVertexAIModel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let model_id = match self {
Self::Claude(version) => match version {
ClaudeVersion::Sonnet35 => "claude-3-5-sonnet@20240620",
ClaudeVersion::Sonnet35V2 => "claude-3-5-sonnet-v2@20241022",
ClaudeVersion::Sonnet37 => "claude-3-7-sonnet@20250219",
ClaudeVersion::Haiku35 => "claude-3-5-haiku@20241022",
ClaudeVersion::Sonnet4 => "claude-sonnet-4@20250514",
ClaudeVersion::Opus4 => "claude-opus-4@20250514",
ClaudeVersion::Generic(name) => name,
@@ -160,10 +151,7 @@ impl TryFrom<&str> for GcpVertexAIModel {
fn try_from(s: &str) -> Result<Self, Self::Error> {
// Known models
match s {
"claude-3-5-sonnet@20240620" => Ok(Self::Claude(ClaudeVersion::Sonnet35)),
"claude-3-5-sonnet-v2@20241022" => Ok(Self::Claude(ClaudeVersion::Sonnet35V2)),
"claude-3-7-sonnet@20250219" => Ok(Self::Claude(ClaudeVersion::Sonnet37)),
"claude-3-5-haiku@20241022" => Ok(Self::Claude(ClaudeVersion::Haiku35)),
"claude-sonnet-4@20250514" => Ok(Self::Claude(ClaudeVersion::Sonnet4)),
"claude-opus-4@20250514" => Ok(Self::Claude(ClaudeVersion::Opus4)),
"gemini-1.5-pro-002" => Ok(Self::Gemini(GeminiVersion::Pro15)),
@@ -360,10 +348,8 @@ mod tests {
#[test]
fn test_model_parsing() -> Result<()> {
let valid_models = [
"claude-3-5-sonnet@20240620",
"claude-3-5-sonnet-v2@20241022",
"claude-sonnet-4-20250514",
"claude-3-7-sonnet@20250219",
"claude-3-5-haiku@20241022",
"claude-sonnet-4@20250514",
"gemini-1.5-pro-002",
"gemini-2.0-flash-001",
@@ -385,10 +371,8 @@ mod tests {
#[test]
fn test_default_locations() -> Result<()> {
let test_cases = [
("claude-3-5-sonnet@20240620", GcpLocation::Ohio),
("claude-3-5-sonnet-v2@20241022", GcpLocation::Ohio),
("claude-sonnet-4-20250514", GcpLocation::Ohio),
("claude-3-7-sonnet@20250219", GcpLocation::Ohio),
("claude-3-5-haiku@20241022", GcpLocation::Ohio),
("claude-sonnet-4@20250514", GcpLocation::Ohio),
("gemini-1.5-pro-002", GcpLocation::Iowa),
("gemini-2.0-flash-001", GcpLocation::Iowa),
+10 -10
View File
@@ -373,7 +373,7 @@ mod tests {
"type": "text",
"text": "Hello! How can I assist you today?"
}],
"model": "claude-3-5-sonnet",
"model": "claude-4-sonnet",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
@@ -410,7 +410,7 @@ mod tests {
"name": "calculator",
"input": {"expression": "2 + 2"}
}],
"model": "claude-3-5-sonnet",
"model": "claude-4-sonnet",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
@@ -513,13 +513,13 @@ mod tests {
#[test]
fn test_parse_streaming_response() -> Result<()> {
let sse_data = r#"data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-3-5-sonnet","choices":[{"delta":{"type":"text","content":"I","content_list":[{"type":"text","text":"I"}],"text":"I"}}],"usage":{}}
let sse_data = r#"data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-sonnet-4-20250514","choices":[{"delta":{"type":"text","content":"I","content_list":[{"type":"text","text":"I"}],"text":"I"}}],"usage":{}}
data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-3-5-sonnet","choices":[{"delta":{"type":"text","content":"'ll help you check Nvidia's current","content_list":[{"type":"text","text":"'ll help you check Nvidia's current"}],"text":"'ll help you check Nvidia's current"}}],"usage":{}}
data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-sonnet-4-20250514","choices":[{"delta":{"type":"text","content":"'ll help you check Nvidia's current","content_list":[{"type":"text","text":"'ll help you check Nvidia's current"}],"text":"'ll help you check Nvidia's current"}}],"usage":{}}
data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-3-5-sonnet","choices":[{"delta":{"type":"tool_use","tool_use_id":"tooluse_FB_nOElDTAOKa-YnVWI5Uw","name":"get_stock_price","content_list":[{"tool_use_id":"tooluse_FB_nOElDTAOKa-YnVWI5Uw","name":"get_stock_price"}],"text":""}}],"usage":{}}
data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-sonnet-4-20250514","choices":[{"delta":{"type":"tool_use","tool_use_id":"tooluse_FB_nOElDTAOKa-YnVWI5Uw","name":"get_stock_price","content_list":[{"tool_use_id":"tooluse_FB_nOElDTAOKa-YnVWI5Uw","name":"get_stock_price"}],"text":""}}],"usage":{}}
data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-3-5-sonnet","choices":[{"delta":{"type":"tool_use","input":"{\"symbol\":\"NVDA\"}","content_list":[{"input":"{\"symbol\":\"NVDA\"}"}],"text":""}}],"usage":{"prompt_tokens":397,"completion_tokens":65,"total_tokens":462}}
data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-sonnet-4-20250514","choices":[{"delta":{"type":"tool_use","input":"{\"symbol\":\"NVDA\"}","content_list":[{"input":"{\"symbol\":\"NVDA\"}"}],"text":""}}],"usage":{"prompt_tokens":397,"completion_tokens":65,"total_tokens":462}}
"#;
let message = parse_streaming_response(sse_data)?;
@@ -550,7 +550,7 @@ data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-3-5-sonnet","
use crate::conversation::message::Message;
use crate::model::ModelConfig;
let model_config = ModelConfig::new_or_fail("claude-3-5-sonnet");
let model_config = ModelConfig::new_or_fail("claude-4-sonnet");
let system = "You are a helpful assistant that can use tools to get information.";
let messages = vec![Message::user().with_text("What is the stock price of Nvidia?")];
@@ -573,7 +573,7 @@ data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-3-5-sonnet","
let request = create_request(&model_config, system, &messages, &tools)?;
// Check basic structure
assert_eq!(request["model"], "claude-3-5-sonnet");
assert_eq!(request["model"], "claude-4-sonnet");
let messages_array = request["messages"].as_array().unwrap();
assert_eq!(messages_array.len(), 2); // system + user message
@@ -618,7 +618,7 @@ data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-3-5-sonnet","
"input": {"expression": "2 + 2"}
}
],
"model": "claude-3-5-sonnet",
"model": "claude-4-sonnet",
"usage": {
"input_tokens": 10,
"output_tokens": 15
@@ -659,7 +659,7 @@ data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-3-5-sonnet","
use crate::conversation::message::Message;
use crate::model::ModelConfig;
let model_config = ModelConfig::new_or_fail("claude-3-5-sonnet");
let model_config = ModelConfig::new_or_fail("claude-4-sonnet");
let system = "Reply with only a description in four words or less";
let messages = vec![Message::user().with_text("Test message")];
let tools = vec![Tool::new(
+3 -5
View File
@@ -444,10 +444,7 @@ impl Provider for GcpVertexAIProvider {
Self: Sized,
{
let model_strings: Vec<String> = vec![
GcpVertexAIModel::Claude(ClaudeVersion::Sonnet35),
GcpVertexAIModel::Claude(ClaudeVersion::Sonnet35V2),
GcpVertexAIModel::Claude(ClaudeVersion::Sonnet37),
GcpVertexAIModel::Claude(ClaudeVersion::Haiku35),
GcpVertexAIModel::Claude(ClaudeVersion::Sonnet4),
GcpVertexAIModel::Claude(ClaudeVersion::Opus4),
GcpVertexAIModel::Gemini(GeminiVersion::Pro15),
@@ -597,7 +594,7 @@ mod tests {
fn test_url_construction() {
use url::Url;
let model_config = ModelConfig::new_or_fail("claude-3-5-sonnet-v2@20241022");
let model_config = ModelConfig::new_or_fail("claude-sonnet-4-20250514");
let context = RequestContext::new(&model_config.model_name).unwrap();
let api_model_id = context.model.to_string();
@@ -629,7 +626,8 @@ mod tests {
.iter()
.map(|m| m.name.clone())
.collect();
assert!(model_names.contains(&"claude-3-5-sonnet-v2@20241022".to_string()));
assert!(model_names.contains(&"claude-3-7-sonnet@20250219".to_string()));
assert!(model_names.contains(&"claude-sonnet-4@20250514".to_string()));
assert!(model_names.contains(&"gemini-1.5-pro-002".to_string()));
assert!(model_names.contains(&"gemini-2.5-pro".to_string()));
// Should contain the original 2 config keys plus 4 new retry-related ones
+2 -2
View File
@@ -30,11 +30,11 @@ pub const GITHUB_COPILOT_KNOWN_MODELS: &[&str] = &[
"o1",
"o3-mini",
"claude-3.7-sonnet",
"claude-3.5-sonnet",
"claude-sonnet-4-20250514",
];
pub const GITHUB_COPILOT_STREAM_MODELS: &[&str] =
&["gpt-4.1", "claude-3.7-sonnet", "claude-3.5-sonnet"];
&["gpt-4.1", "claude-3.7-sonnet", "claude-sonnet-4-20250514"];
const GITHUB_COPILOT_DOC_URL: &str =
"https://docs.github.com/en/copilot/using-github-copilot/ai-models";
+4 -3
View File
@@ -16,14 +16,15 @@ use crate::model::ModelConfig;
use crate::providers::formats::openai::{create_request, get_usage, response_to_message};
use rmcp::model::Tool;
pub const OPENROUTER_DEFAULT_MODEL: &str = "anthropic/claude-3.5-sonnet";
pub const OPENROUTER_DEFAULT_MODEL: &str = "anthropic/claude-sonnet-4";
pub const OPENROUTER_MODEL_PREFIX_ANTHROPIC: &str = "anthropic";
// OpenRouter can run many models, we suggest the default
pub const OPENROUTER_KNOWN_MODELS: &[&str] = &[
"anthropic/claude-3.5-sonnet",
"anthropic/claude-3.7-sonnet",
"anthropic/claude-sonnet-4",
"anthropic/claude-opus-4.1",
"anthropic/claude-opus-4",
"anthropic/claude-3.7-sonnet",
"google/gemini-2.5-pro",
"deepseek/deepseek-r1-0528",
"qwen/qwen3-coder",
+12 -6
View File
@@ -333,7 +333,7 @@ pub async fn get_all_pricing() -> HashMap<String, HashMap<String, PricingInfo>>
}
/// Convert OpenRouter model ID to provider/model format
/// e.g., "anthropic/claude-3.5-sonnet" -> ("anthropic", "claude-3.5-sonnet")
/// e.g., "anthropic/claude-sonnet-4-20250514" -> ("anthropic", "claude-sonnet-4-20250514")
pub fn parse_model_id(model_id: &str) -> Option<(String, String)> {
let parts: Vec<&str> = model_id.splitn(2, '/').collect();
if parts.len() == 2 {
@@ -373,8 +373,11 @@ mod tests {
#[test]
fn test_parse_model_id() {
assert_eq!(
parse_model_id("anthropic/claude-3.5-sonnet"),
Some(("anthropic".to_string(), "claude-3.5-sonnet".to_string()))
parse_model_id("anthropic/claude-sonnet-4-20250514"),
Some((
"anthropic".to_string(),
"claude-sonnet-4-20250514".to_string()
))
);
assert_eq!(
parse_model_id("openai/gpt-4"),
@@ -384,8 +387,11 @@ mod tests {
// Test the specific model causing issues
assert_eq!(
parse_model_id("anthropic/claude-sonnet-4"),
Some(("anthropic".to_string(), "claude-sonnet-4".to_string()))
parse_model_id("anthropic/claude-sonnet-4-20250514"),
Some((
"anthropic".to_string(),
"claude-sonnet-4-20250514".to_string()
))
);
}
@@ -404,7 +410,7 @@ mod tests {
return;
}
// Test lookup for the specific model
// Test lookup for the specific model (use the name that actually exists in cache)
let pricing = get_model_pricing("anthropic", "claude-sonnet-4").await;
println!(
+2 -2
View File
@@ -15,8 +15,8 @@ use crate::impl_provider_default;
use crate::model::ModelConfig;
use rmcp::model::Tool;
pub const SNOWFLAKE_DEFAULT_MODEL: &str = "claude-3-7-sonnet";
pub const SNOWFLAKE_KNOWN_MODELS: &[&str] = &["claude-3-7-sonnet", "claude-3-5-sonnet"];
pub const SNOWFLAKE_DEFAULT_MODEL: &str = "claude-4-sonnet";
pub const SNOWFLAKE_KNOWN_MODELS: &[&str] = &["claude-4-sonnet", "claude-3-7-sonnet"];
pub const SNOWFLAKE_DOC_URL: &str =
"https://docs.snowflake.com/user-guide/snowflake-cortex/aisql#choosing-a-model";
+1 -2
View File
@@ -29,8 +29,7 @@ use rmcp::model::Tool;
pub const TETRATE_KNOWN_MODELS: &[&str] = &[
"claude-opus-4-1",
"claude-3-7-sonnet-latest",
"claude-3-5-sonnet-latest",
"claude-3-5-haiku-latest",
"claude-sonnet-4-20250514",
"gemini-2.5-pro",
"gemini-2.0-flash",
"gemini-2.0-flash-lite",
@@ -349,24 +349,24 @@ data: [DONE]
assert_eq!(choice.finish_reason, "stop");
}
const CLAUDE_STREAM: &str = r#"
data: {"choices":[{"index":0,"delta":{"content":"I","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":"'ll","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":" help","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":" you examine","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":" the most","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":" recent commit using","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":" the shell","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":" comman","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":"d `git show HEAD","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":"`.","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"name":"developer__shell"},"id":"tooluse_9eC8o8MvTN-KOWuDGXgq1Q","index":0,"type":"function"}]}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":""},"index":0,"type":"function"}]}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":"{\"command"},"index":0,"type":"function"}]}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":"\": "},"index":0,"type":"function"}]}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":"\"git show H"},"index":0,"type":"function"}]}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":"EAD"},"index":0,"type":"function"}]}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":"\"}"},"index":0,"type":"function"}]}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-3.5-sonnet"}
data: {"choices":[{"finish_reason":"tool_calls","index":0,"delta":{"content":null}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","usage":{"completion_tokens":56,"prompt_tokens":2594,"prompt_tokens_details":{"cached_tokens":0},"total_tokens":2650},"model":"claude-3.5-sonnet"}
data: {"choices":[{"index":0,"delta":{"content":"I","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":"'ll","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":" help","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":" you examine","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":" the most","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":" recent commit using","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":" the shell","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":" comman","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":"d `git show HEAD","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":"`.","role":"assistant"}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"name":"developer__shell"},"id":"tooluse_9eC8o8MvTN-KOWuDGXgq1Q","index":0,"type":"function"}]}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":""},"index":0,"type":"function"}]}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":"{\"command"},"index":0,"type":"function"}]}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":"\": "},"index":0,"type":"function"}]}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":"\"git show H"},"index":0,"type":"function"}]}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":"EAD"},"index":0,"type":"function"}]}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":"\"}"},"index":0,"type":"function"}]}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","model":"claude-sonnet-4-20250514"}
data: {"choices":[{"finish_reason":"tool_calls","index":0,"delta":{"content":null}}],"created":1747613682,"id":"938bb8e2-6276-4a58-bca3-c675cfe7f2f5","usage":{"completion_tokens":56,"prompt_tokens":2594,"prompt_tokens_details":{"cached_tokens":0},"total_tokens":2650},"model":"claude-sonnet-4-20250514"}
data: [DONE]
"#;
#[test]