Skip to content

fix: enforce OpenAI tool-choice contract - #650

Draft
waw2637 wants to merge 4 commits into
ROCm:mainfrom
waw2637:fix/openai-tool-choice-contract
Draft

fix: enforce OpenAI tool-choice contract#650
waw2637 wants to merge 4 commits into
ROCm:mainfrom
waw2637:fix/openai-tool-choice-contract

Conversation

@waw2637

@waw2637 waw2637 commented Aug 4, 2026

Copy link
Copy Markdown

Summary

  • enforce Chat Completions tool_choice modes (none, auto, required, named function, and allowed_tools) and parallel_tool_calls
  • validate function definitions and generated calls before returning them, failing closed and clearing parser/KV/prompt-cache state after rejected generations
  • buffer tool-call streams for validation and honor stream_options.include_usage
  • wire the contract unit suite into root CMake/CTest and Debian PR builds
  • document supported behavior, structured failures, streaming semantics, safe dispatch, and explicit limitations

Closes #649

Why

FastFlowLM 0.9.46 could return HTTP 200 with finish_reason: "tool_calls" even when a truncated model response produced an empty or undeclared function name or malformed arguments. This was reproduced across Gemma 4, Qwen 3, and Qwen 3.5. Clients then attempted to dispatch calls that were not usable.

The REST boundary now treats the model/parser output as untrusted and validates it against the request contract before exposing it to clients.

Supported contract

  • function tools on /v1/chat/completions
  • tool_choice: none, auto, required, named function, and allowed_tools
  • serial or parallel calls according to parallel_tool_calls
  • JSON-encoded object arguments and declared function names
  • buffered SSE tool responses with optional final usage chunk

Explicit limitations

  • strict: true is rejected because constrained JSON Schema decoding is not implemented
  • custom and hosted tools are not supported
  • Responses API tools are not supported
  • applications remain responsible for full JSON Schema validation, authorization, and safe function execution

Verification

Build and unit tests

  • configured the root project with the Ubuntu 26.04 FastFlowLM build environment
  • built the full flm target successfully (96 build steps)
  • ran CTest: openai_tool_policy_contract passed
  • verified the installed test binary matches the built artifact by SHA-256

Live NPU contract matrix

All models were loaded and tested serially with the binary built from 11c4081.

Test Gemma 4 E4B (64K) Gemma 4 E2B (8K) Qwen 3 Instruct 4B (8K) Qwen 3.5 2B (8K)
Invalid request fields return HTTP 400 Pass Pass Pass Pass
tool_choice: none Pass Pass Pass Pass
tool_choice: required Pass Pass Pass Pass
Named function choice Pass Pass Pass Pass
allowed_tools required subset Pass Pass Pass Pass
Truncated call fails closed, then recovers Pass Pass Pass Pass
Multi-turn chained calls Pass Pass Pass Pass
SSE usage absent/opt-in shapes Pass Pass Pass Pass
Autonomous skill/read/write workflow Pass Not run Not run Not run

The recovery test deliberately truncates a required call with max_tokens: 1, expects a structured HTTP 500 model error, and immediately sends a valid required call to verify that rejected state is not reused.

Related reports

These can improve how often a model generates a valid call, while this PR protects the API boundary when it does not.

Review requested

  • general correctness and regression review
  • API/architecture review of Chat Completions compatibility and failure semantics
  • security review of fail-closed behavior and client execution guidance

This PR is intentionally draft until independent review confirms the contract and test coverage.

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.

Bug: tool_choice is not enforced and malformed tool calls are returned as successful responses

1 participant