From b0f286042b6e325c3761bb8d72a7f8c4bb8573d8 Mon Sep 17 00:00:00 2001
From: Valery Borovsky <138976239+Vastargazing@users.noreply.github.com>
Date: Tue, 24 Mar 2026 16:22:21 +0300
Subject: [PATCH] copilot_chat: Serialize ToolChoice::Any as "required" instead
of "any" (#52015)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
**Observed behavior:** Inline assistant failed for GPT-4.1,
Gemini, and other non-Anthropic models. Claude worked correctly because
Anthropic's API accepts `"any"` as a valid value.
## Fix
Renamed `ToolChoice::Any` → `ToolChoice::Required` in both
`copilot_chat.rs` and `responses.rs`, matching the convention used by
other OpenAI-compatible providers (`open_ai`, `lmstudio`,
`open_router`).
`copilot_chat::ToolChoice` is a wire type only for the
`/chat/completions`
path — Anthropic models go through `into_anthropic()` and never touch
it,
so no per-model logic is needed.
Also fixes the same serialization bug in `responses::ToolChoice`, which
was not covered by the original approach, and adds regression tests for
both.
## Affected models
- `gpt-4.1` via copilot_chat provider
- `gemini-*` via copilot_chat provider
- Likely affects all OpenAI-compatible models routed through
copilot_chat
## Screenshots
**Bug (only Claude works, Gemini and GPT-4.1 fail):**
**Fix:**
**Result:** After the fix, all models work correctly via inline
assistant.
## Release Notes
- Fix inline assistant 400 errors for GPT-4.1, Gemini, and other
non-Anthropic models via the copilot_chat provider (`tool_choice` was
sending `"any"` instead of `"required"`)
---
crates/copilot_chat/src/copilot_chat.rs | 20 ++++++++++++++-
crates/copilot_chat/src/responses.rs | 25 ++++++++++++++++++-
.../src/provider/copilot_chat.rs | 4 +--
3 files changed, 45 insertions(+), 4 deletions(-)
diff --git a/crates/copilot_chat/src/copilot_chat.rs b/crates/copilot_chat/src/copilot_chat.rs
index d1f339f89a01d1ed0d17e03b8712b42232177db8..850190701e526fe3fad896a17cdc704b89253fea 100644
--- a/crates/copilot_chat/src/copilot_chat.rs
+++ b/crates/copilot_chat/src/copilot_chat.rs
@@ -370,7 +370,7 @@ pub enum Tool {
#[serde(rename_all = "lowercase")]
pub enum ToolChoice {
Auto,
- Any,
+ Required,
None,
}
@@ -1736,4 +1736,22 @@ mod tests {
// Only /v1/messages endpoint -> supports_response = false (doesn't have /responses)
assert!(!model_with_messages.supports_response());
}
+
+ #[test]
+ fn test_tool_choice_required_serializes_as_required() {
+ // Regression test: ToolChoice::Required must serialize as "required" (not "any")
+ // for OpenAI-compatible APIs. Reverting the rename would break this.
+ assert_eq!(
+ serde_json::to_string(&ToolChoice::Required).unwrap(),
+ "\"required\""
+ );
+ assert_eq!(
+ serde_json::to_string(&ToolChoice::Auto).unwrap(),
+ "\"auto\""
+ );
+ assert_eq!(
+ serde_json::to_string(&ToolChoice::None).unwrap(),
+ "\"none\""
+ );
+ }
}
diff --git a/crates/copilot_chat/src/responses.rs b/crates/copilot_chat/src/responses.rs
index 4f30ba1eb083c8a70c9a91853c7df37e65783ce3..1241a76fb1410461cef7337e2339180b18da7a3f 100644
--- a/crates/copilot_chat/src/responses.rs
+++ b/crates/copilot_chat/src/responses.rs
@@ -52,7 +52,7 @@ pub enum ToolDefinition {
#[serde(rename_all = "lowercase")]
pub enum ToolChoice {
Auto,
- Any,
+ Required,
None,
#[serde(untagged)]
Other(ToolDefinition),
@@ -408,3 +408,26 @@ pub async fn stream_response(
}
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_tool_choice_required_serializes_as_required() {
+ // Regression test: ToolChoice::Required must serialize as "required" (not "any")
+ // for OpenAI Responses API. Reverting the rename would break this.
+ assert_eq!(
+ serde_json::to_string(&ToolChoice::Required).unwrap(),
+ "\"required\""
+ );
+ assert_eq!(
+ serde_json::to_string(&ToolChoice::Auto).unwrap(),
+ "\"auto\""
+ );
+ assert_eq!(
+ serde_json::to_string(&ToolChoice::None).unwrap(),
+ "\"none\""
+ );
+ }
+}
diff --git a/crates/language_models/src/provider/copilot_chat.rs b/crates/language_models/src/provider/copilot_chat.rs
index 7063db83bf65b82a4f314ad97e9463b106400c0b..e90d8208b4f37cfc31487768b94a2b943e2320ce 100644
--- a/crates/language_models/src/provider/copilot_chat.rs
+++ b/crates/language_models/src/provider/copilot_chat.rs
@@ -1046,7 +1046,7 @@ fn into_copilot_chat(
tools,
tool_choice: tool_choice.map(|choice| match choice {
LanguageModelToolChoice::Auto => ToolChoice::Auto,
- LanguageModelToolChoice::Any => ToolChoice::Any,
+ LanguageModelToolChoice::Any => ToolChoice::Required,
LanguageModelToolChoice::None => ToolChoice::None,
}),
thinking_budget: None,
@@ -1255,7 +1255,7 @@ fn into_copilot_responses(
let mapped_tool_choice = tool_choice.map(|choice| match choice {
LanguageModelToolChoice::Auto => responses::ToolChoice::Auto,
- LanguageModelToolChoice::Any => responses::ToolChoice::Any,
+ LanguageModelToolChoice::Any => responses::ToolChoice::Required,
LanguageModelToolChoice::None => responses::ToolChoice::None,
});