feat: anthropic SDK header matching + request profile

This commit is contained in:
Yeachan-Heo
2026-04-01 05:55:25 +00:00
parent 828597024e
commit 1b42c6096c
3 changed files with 36 additions and 7 deletions

View File

@@ -439,6 +439,19 @@ impl AnthropicClient {
"beta_count".to_string(), "beta_count".to_string(),
Value::from(u64::try_from(self.request_profile.betas.len()).unwrap_or(u64::MAX)), Value::from(u64::try_from(self.request_profile.betas.len()).unwrap_or(u64::MAX)),
); );
if !self.request_profile.betas.is_empty() {
attributes.insert(
"betas".to_string(),
Value::Array(
self.request_profile
.betas
.iter()
.cloned()
.map(Value::String)
.collect(),
),
);
}
if !self.request_profile.extra_body.is_empty() { if !self.request_profile.extra_body.is_empty() {
attributes.insert( attributes.insert(
"extra_body_keys".to_string(), "extra_body_keys".to_string(),

View File

@@ -75,7 +75,7 @@ async fn send_message_posts_json_and_parses_response() {
); );
assert_eq!( assert_eq!(
request.headers.get("anthropic-beta").map(String::as_str), request.headers.get("anthropic-beta").map(String::as_str),
Some("claude-code-20250219") Some("claude-code-20250219,prompt-caching-scope-2026-01-05")
); );
let body: serde_json::Value = let body: serde_json::Value =
serde_json::from_str(&request.body).expect("request body should be json"); serde_json::from_str(&request.body).expect("request body should be json");
@@ -86,7 +86,10 @@ async fn send_message_posts_json_and_parses_response() {
assert!(body.get("stream").is_none()); assert!(body.get("stream").is_none());
assert_eq!(body["tools"][0]["name"], json!("get_weather")); assert_eq!(body["tools"][0]["name"], json!("get_weather"));
assert_eq!(body["tool_choice"]["type"], json!("auto")); assert_eq!(body["tool_choice"]["type"], json!("auto"));
assert_eq!(body["betas"], json!(["claude-code-20250219"])); assert_eq!(
body["betas"],
json!(["claude-code-20250219", "prompt-caching-scope-2026-01-05"])
);
} }
#[tokio::test] #[tokio::test]
@@ -133,7 +136,7 @@ async fn send_message_applies_request_profile_and_records_telemetry() {
let request = captured.first().expect("server should capture request"); let request = captured.first().expect("server should capture request");
assert_eq!( assert_eq!(
request.headers.get("anthropic-beta").map(String::as_str), request.headers.get("anthropic-beta").map(String::as_str),
Some("claude-code-20250219,tools-2026-04-01") Some("claude-code-20250219,prompt-caching-scope-2026-01-05,tools-2026-04-01")
); );
assert_eq!( assert_eq!(
request.headers.get("user-agent").map(String::as_str), request.headers.get("user-agent").map(String::as_str),
@@ -144,7 +147,11 @@ async fn send_message_applies_request_profile_and_records_telemetry() {
assert_eq!(body["metadata"]["source"], json!("clawd-code")); assert_eq!(body["metadata"]["source"], json!("clawd-code"));
assert_eq!( assert_eq!(
body["betas"], body["betas"],
json!(["claude-code-20250219", "tools-2026-04-01"]) json!([
"claude-code-20250219",
"prompt-caching-scope-2026-01-05",
"tools-2026-04-01"
])
); );
let events = sink.events(); let events = sink.events();

View File

@@ -13,6 +13,7 @@ pub const DEFAULT_ANTHROPIC_VERSION: &str = "2023-06-01";
pub const DEFAULT_APP_NAME: &str = "claude-code"; pub const DEFAULT_APP_NAME: &str = "claude-code";
pub const DEFAULT_RUNTIME: &str = "rust"; pub const DEFAULT_RUNTIME: &str = "rust";
pub const DEFAULT_AGENTIC_BETA: &str = "claude-code-20250219"; pub const DEFAULT_AGENTIC_BETA: &str = "claude-code-20250219";
pub const DEFAULT_PROMPT_CACHING_SCOPE_BETA: &str = "prompt-caching-scope-2026-01-05";
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ClientIdentity { pub struct ClientIdentity {
@@ -65,7 +66,10 @@ impl AnthropicRequestProfile {
Self { Self {
anthropic_version: DEFAULT_ANTHROPIC_VERSION.to_string(), anthropic_version: DEFAULT_ANTHROPIC_VERSION.to_string(),
client_identity, client_identity,
betas: vec![DEFAULT_AGENTIC_BETA.to_string()], betas: vec![
DEFAULT_AGENTIC_BETA.to_string(),
DEFAULT_PROMPT_CACHING_SCOPE_BETA.to_string(),
],
extra_body: Map::new(), extra_body: Map::new(),
} }
} }
@@ -445,7 +449,8 @@ mod tests {
("user-agent".to_string(), "claude-code/1.2.3".to_string()), ("user-agent".to_string(), "claude-code/1.2.3".to_string()),
( (
"anthropic-beta".to_string(), "anthropic-beta".to_string(),
"claude-code-20250219,tools-2026-04-01".to_string(), "claude-code-20250219,prompt-caching-scope-2026-01-05,tools-2026-04-01"
.to_string(),
), ),
] ]
); );
@@ -459,7 +464,11 @@ mod tests {
); );
assert_eq!( assert_eq!(
body["betas"], body["betas"],
serde_json::json!(["claude-code-20250219", "tools-2026-04-01"]) serde_json::json!([
"claude-code-20250219",
"prompt-caching-scope-2026-01-05",
"tools-2026-04-01"
])
); );
} }