Skip to content

Chat Completions allowed_tools tool_choice takes one object, not a list - #43

Merged
lukekim merged 2 commits into
spiceaifrom
fix/chat-allowed-tools-object
Oct 9, 2026
Merged

lukekim merged 2 commits into
spiceaifrom
fix/chat-allowed-tools-object

Conversation

@krinart

@krinart krinart commented Oct 9, 2026

Copy link
Copy Markdown

The Chat Completions API defines tool_choice: {"type": "allowed_tools", "allowed_tools": {"mode", "tools"}} with a single allowed_tools object (ChatCompletionAllowedToolsChoice in openapi.documented.yml). ChatCompletionAllowedToolsChoice.allowed_tools was a Vec, so:

  • a request in OpenAI's shape fails to deserialize;
  • the list shape the type accepts is serialized as a list, which OpenAI rejects with 400 Missing required parameter: 'tool_choice.function'.

allowed_tools is now ChatCompletionAllowedTools. Default is dropped from the choice, since ChatCompletionAllowedTools has no meaningful default mode.

Test: tests/ser_de.rs::allowed_tools_tool_choice_round_trips_in_the_chat_completions_shape deserializes OpenAI's shape, checks it serializes back unchanged, and checks the list shape is rejected. It does not compile against the old field type.

Found in spiceai/spiceai#14910.

The Chat Completions API defines tool_choice {"type": "allowed_tools"} with a
single allowed_tools object ({mode, tools}). The type modelled it as a list, so
a request in OpenAI's shape failed to deserialize, and the list shape this type
did accept was serialized to OpenAI, which rejects it with 400
"Missing required parameter: 'tool_choice.function'".
…ools-object

# Conflicts:
#	async-openai/tests/ser_de.rs
@krinart
krinart requested a balanced review from Copilot October 9, 2026 20:51
@lukekim
lukekim merged commit 216da93 into spiceai Oct 9, 2026
50 checks passed
@lukekim
lukekim deleted the fix/chat-allowed-tools-object branch October 9, 2026 20:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approved

The focused schema correction matches the documented API and is adequately covered by regression tests.

0 open findings

What changed in this PR

Corrects Chat Completions allowed_tools serialization to match the OpenAI API schema.

Changes:

  • Replaces the erroneous list field with one ChatCompletionAllowedTools object.
  • Removes the invalid Default implementation.
  • Adds round-trip and legacy-shape rejection tests.
File Description
async-openai/​src/​types/​chat/​chat_.rs Corrects the allowed_tools field type and documentation.
async-openai/​tests/​ser_de.rs Verifies valid object serialization and rejects the former list shape.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@krinart krinart self-assigned this Oct 9, 2026
krinart added a commit to spiceai/spiceai that referenced this pull request Oct 10, 2026
…istory, MCP tools/call errors (fixes #14910)

- tool_choice `allowed_tools` takes OpenAI's single object; async-openai is
  pinned at the merge of spiceai/async-openai#43.
- /v1/evaluate: every non-200 response marks its `ai_evaluate` task failed;
  every invalid request is a 400 `{"error"}` (415 for a non-JSON body); the
  question rules apply to every model; responses name the Spicepod model;
  error bodies name the model and the kind of failure.
- mistral.rs: a tool_choice naming a tool the request does not list is a 400.
- Provider errors keep their status: `ApiErrorKind` reads `code`, then
  `type`, for /v1/chat/completions and the chat evaluator alike.
- MCP tools/call: an unknown tool is -32602 naming it; a tool that fails
  returns a result with `isError: true`.
- A completed chat stream ends with `data: [DONE]`; the Responses API load
  warning states its fix once.
wangyusheng1985 pushed a commit to wangyusheng1985/spiceai that referenced this pull request Oct 15, 2026
…esponse model (fixes spiceai#14910) (spiceai#14977)

* fix(models, mcp): allowed_tools shape, /v1/evaluate errors and task history, MCP tools/call errors (fixes spiceai#14910)

- tool_choice `allowed_tools` takes OpenAI's single object; async-openai is
  pinned at the merge of spiceai/async-openai#43.
- /v1/evaluate: every non-200 response marks its `ai_evaluate` task failed;
  every invalid request is a 400 `{"error"}` (415 for a non-JSON body); the
  question rules apply to every model; responses name the Spicepod model;
  error bodies name the model and the kind of failure.
- mistral.rs: a tool_choice naming a tool the request does not list is a 400.
- Provider errors keep their status: `ApiErrorKind` reads `code`, then
  `type`, for /v1/chat/completions and the chat evaluator alike.
- MCP tools/call: an unknown tool is -32602 naming it; a tool that fails
  returns a result with `isError: true`.
- A completed chat stream ends with `data: [DONE]`; the Responses API load
  warning states its fix once.

* docs(evaluate): state which /v1/evaluate failures reach task history, and that a model that failed to load is a 404

A body the handler cannot read is refused before the `ai_evaluate` task
starts, so it leaves no row (as on /v1/chat/completions); every other
non-200 response records an error_message. A configured model that failed
to load answers 404 as an undefined one does.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants