Skip to content

Commit f8e7573

Browse files
qinlinwangHmbown
authored andcommitted
fix: sanitize tool input_schema for Anthropic adapter
When configuring Anthropic as the provider, any tool whose input_schema contains a top-level oneOf/anyOf/allOf causes the API to reject the entire request with HTTP 400: Anthropic API error (HTTP 400 Bad Request invalid_request_error): tools.1.custom.input_schema: input_schema does not support oneOf, allOf, or anyOf at the top level The OpenAI Responses adapter already handles this via schema_sanitize::sanitize_for_responses, which strips root-level composition keywords, merges alternative properties into the root object, and surfaces dropped constraints as a description note so the model still knows which parameters are expected. Apply the same sanitization in the Anthropic adapter before sending tool definitions, aligning both adapters for consistent schema handling across providers. Harvested from PR #4346 by @qinlinwang
1 parent 17bde5e commit f8e7573

1 file changed

Lines changed: 16 additions & 2 deletions

File tree

crates/tui/src/client/anthropic.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use serde_json::{Value, json};
2929
use crate::llm_client::StreamEventBox;
3030
use crate::logging;
3131
use crate::models::{ContentBlock, MessageRequest, MessageResponse, StreamEvent, Usage};
32+
use crate::tools::schema_sanitize;
3233

3334
use super::{DeepSeekClient, ERROR_BODY_MAX_BYTES, bounded_error_text};
3435

@@ -80,10 +81,23 @@ impl DeepSeekClient {
8081
tools
8182
.iter()
8283
.map(|tool| {
84+
// Sanitize the tool's input_schema the same way the
85+
// OpenAI Responses adapter does: strip top-level
86+
// oneOf/anyOf/allOf (which Anthropic rejects), merge
87+
// alternative properties into the root, and surface
88+
// the dropped constraint as a description note so the
89+
// model still knows which parameters are expected.
90+
let mut schema = tool.input_schema.clone();
91+
let constraint_note = schema_sanitize::sanitize_for_responses(&mut schema);
92+
let description = match constraint_note {
93+
Some(note) if tool.description.trim().is_empty() => note,
94+
Some(note) => format!("{}\n\n{}", tool.description.trim(), note),
95+
None => tool.description.clone(),
96+
};
8397
let mut value = json!({
8498
"name": tool.name,
85-
"description": tool.description,
86-
"input_schema": tool.input_schema,
99+
"description": description,
100+
"input_schema": schema,
87101
});
88102
if let Some(strict) = tool.strict {
89103
value["strict"] = json!(strict);

0 commit comments

Comments
 (0)