fix(ai-formats): strip Claude billing headers from preserved guidance

This commit is contained in:
stabey
2026-06-21 00:22:57 +08:00
parent 6a22488698
commit bb6aa03485
2 changed files with 25 additions and 5 deletions
@@ -841,7 +841,7 @@ mod tests {
{"role": "user", "content": "hello"}, {"role": "user", "content": "hello"},
{ {
"role": "system", "role": "system",
"content": "SessionStart hook additional context: follow the house style." "content": "x-anthropic-billing-header: internal-billing-marker\nSessionStart hook additional context: follow the house style."
}, },
{"role": "assistant", "content": "visible answer"}, {"role": "assistant", "content": "visible answer"},
{"role": "user", "content": "continue"} {"role": "user", "content": "continue"}
@@ -868,6 +868,10 @@ mod tests {
input[2]["content"][0]["text"], input[2]["content"][0]["text"],
"SessionStart hook additional context: follow the house style." "SessionStart hook additional context: follow the house style."
); );
assert!(!input[2]["content"][0]["text"]
.as_str()
.expect("developer guidance text")
.contains("x-anthropic-billing-header:"));
assert_eq!(input[3]["role"], "assistant"); assert_eq!(input[3]["role"], "assistant");
assert_eq!(input[3]["content"][0]["text"], "visible answer"); assert_eq!(input[3]["content"][0]["text"], "visible answer");
assert_eq!(input[4]["role"], "user"); assert_eq!(input[4]["role"], "user");
@@ -16,9 +16,9 @@ use crate::{
openai_response_format_to_canonical, openai_responses_extension, openai_response_format_to_canonical, openai_responses_extension,
openai_responses_generation_config, openai_responses_input_to_canonical_messages, openai_responses_generation_config, openai_responses_input_to_canonical_messages,
openai_responses_tool_choice_to_canonical, openai_responses_tools_to_canonical, openai_responses_tool_choice_to_canonical, openai_responses_tools_to_canonical,
openai_tool_choice_raw_to_responses, CanonicalContentBlock, CanonicalInstruction, openai_tool_choice_raw_to_responses, strip_claude_billing_header, CanonicalContentBlock,
CanonicalRequest, CanonicalRole, CanonicalThinkingConfig, CanonicalToolChoice, CanonicalInstruction, CanonicalRequest, CanonicalRole, CanonicalThinkingConfig,
CanonicalToolDefinition, OPENAI_RESPONSES_EXTENSION_NAMESPACE, CanonicalToolChoice, CanonicalToolDefinition, OPENAI_RESPONSES_EXTENSION_NAMESPACE,
OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE, OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
}, },
}; };
@@ -305,6 +305,12 @@ fn canonical_messages_to_responses_input(canonical: &CanonicalRequest) -> Option
let mut next_generated_tool_call_index = 0usize; let mut next_generated_tool_call_index = 0usize;
let mut pending_tool_call_ids = VecDeque::new(); let mut pending_tool_call_ids = VecDeque::new();
for message in &canonical.messages { for message in &canonical.messages {
let strip_claude_billing_header_from_text =
is_claude_messages_request(&canonical.extensions)
&& matches!(
message.role,
CanonicalRole::System | CanonicalRole::Developer
);
let role = match message.role { let role = match message.role {
CanonicalRole::Assistant => "assistant", CanonicalRole::Assistant => "assistant",
CanonicalRole::Tool | CanonicalRole::User | CanonicalRole::Unknown => "user", CanonicalRole::Tool | CanonicalRole::User | CanonicalRole::Unknown => "user",
@@ -401,7 +407,11 @@ fn canonical_messages_to_responses_input(canonical: &CanonicalRequest) -> Option
} }
} }
other => { other => {
if let Some(part) = canonical_block_to_responses_input_part(other, role) { if let Some(part) = canonical_block_to_responses_input_part(
other,
role,
strip_claude_billing_header_from_text,
) {
content.push(part); content.push(part);
} }
} }
@@ -597,9 +607,15 @@ fn is_openai_responses_reasoning_history_block(extensions: &BTreeMap<String, Val
fn canonical_block_to_responses_input_part( fn canonical_block_to_responses_input_part(
block: &CanonicalContentBlock, block: &CanonicalContentBlock,
role: &str, role: &str,
strip_claude_billing_header_from_text: bool,
) -> Option<Value> { ) -> Option<Value> {
match block { match block {
CanonicalContentBlock::Text { text, .. } => { CanonicalContentBlock::Text { text, .. } => {
let text = if strip_claude_billing_header_from_text {
strip_claude_billing_header(text)
} else {
text.clone()
};
if text.is_empty() { if text.is_empty() {
return None; return None;
} }