Skip to content

Route gpt-5.6 models through Responses API when function tools are bound#135

Merged
adambalogh merged 2 commits into
mainfrom
claude/gpt-5.6-function-tools-error-o3l8h4
Jul 24, 2026
Merged

Route gpt-5.6 models through Responses API when function tools are bound#135
adambalogh merged 2 commits into
mainfrom
claude/gpt-5.6-function-tools-error-o3l8h4

Conversation

@adambalogh

Copy link
Copy Markdown
Contributor

Problem

The chat-app agent page fails for the gpt-5.6 models (sol/terra/luna) with an OpenAI 400:

Error code: 400 - Function tools with reasoning_effort are not supported for
gpt-5.6-luna in /v1/chat/completions. To use function tools, use /v1/responses
or set reasoning_effort to 'none'.

The gpt-5.6 family applies a default reasoning_effort server-side, and OpenAI's Chat Completions endpoint rejects that default when function tools are also present. The agent page always binds tools, so every gpt-5.6 agent request 400s.

The gateway never sends reasoning_effort itself, and langchain-openai only auto-prefers the Responses API for -pro/codex variants — so these models default to Chat Completions and hit the wall.

Fix

Route the gpt-5.6 family through OpenAI's Responses API whenever function tools are bound — the path already used for native web search (use_responses_api=True, output_version="responses/v1"). The Responses API supports reasoning and function tools together, so reasoning is preserved rather than disabled (as reasoning_effort='none' would).

  • model_registry.py: new responses_api_for_tools flag on ModelConfig, set on gpt-5.6-sol/terra/luna.
  • llm_backend.py: get_chat_model_cached gains a force_responses_api param; the OpenAI branch switches to the Responses API when it (or web_search) is set. Model cache key extended accordingly.
  • chat_controller.py: both streaming and non-streaming paths build the tools list first, then set force_responses_api via _needs_responses_api_for_tools(provider, cfg, tools_list) before constructing the model.

Scoped precisely: only OpenAI gpt-5.6 models with tools bound switch APIs. Non-gpt-5.6 models, and tool-less gpt-5.6 requests, keep using Chat Completions unchanged — so structured-output / plain-chat paths are untouched.

Tests

Added three cases to test_tool_forwarding.py:

  • gpt-5.6 + tools → force_responses_api=True
  • gpt-5.6 without tools → force_responses_api=False
  • gpt-4.1 (non-reasoning) + tools → force_responses_api=False

ruff format, ruff check, and mypy all pass on the changed files. Full existing suite shows no new failures (pre-existing unrelated loader/import failures remain).

🤖 Generated with Claude Code


Generated by Claude Code

claude added 2 commits July 24, 2026 10:56
OpenAI's gpt-5.6 family (sol/terra/luna) applies a default reasoning_effort
that the Chat Completions endpoint rejects when function tools are also
present:

  Function tools with reasoning_effort are not supported for gpt-5.6-luna in
  /v1/chat/completions. To use function tools, use /v1/responses or set
  reasoning_effort to 'none'.

This broke the chat-app agent page, where tools are always bound.

Fix: add a `responses_api_for_tools` flag to ModelConfig (set on the gpt-5.6
family) and, when function tools are bound for such a model, construct the
OpenAI chat model against the Responses API (`use_responses_api=True`,
`output_version="responses/v1"`) — the same path already used for native web
search. The Responses API supports reasoning and function tools together, so
reasoning is preserved rather than disabled.

Non-gpt-5.6 models and tool-less gpt-5.6 requests keep using Chat Completions,
so behavior is unchanged outside the failing case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KBaBPmkTvezKBQnPoHqNFn
The ruff lint job used astral-sh/ruff-action@v3, which pulls its own latest
ruff release (currently 0.16.0) instead of the version pinned in uv.lock
(0.15.20). The newer ruff enabled rules (RUF013, UP006, RUF059, I001) that
flag pre-existing violations, and the action's default `src` of "." caused it
to lint the whole repo (including tests/) rather than the `tee_gateway`
target the job's args declared — so lint failed non-deterministically on code
unrelated to any given change.

Run ruff via `uv run` like the mypy job already does, so both linters use the
locked toolchain (uv.lock as the single source of truth) and lint is
reproducible and matches what developers run locally.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KBaBPmkTvezKBQnPoHqNFn
@adambalogh
adambalogh marked this pull request as ready for review July 24, 2026 11:02
@adambalogh
adambalogh requested a review from Copilot July 24, 2026 11:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes OpenAI gpt-5.6 (sol/terra/luna) requests failing on the agent/chat path when function tools are bound by routing those requests through OpenAI’s Responses API (which supports reasoning + tools), while keeping non-gpt-5.6 and tool-less requests on Chat Completions.

Changes:

  • Added a per-model registry flag to mark models that must use the Responses API when tools are present (gpt-5.6 family).
  • Extended cached model construction to optionally force the Responses API for OpenAI, and updated chat controller logic to compute this before instantiation (streaming + non-streaming).
  • Updated CI lint workflow to run ruff via uv run (locked toolchain), and added targeted unit tests for the gpt-5.6 tool-routing behavior.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tee_gateway/model_registry.py Adds responses_api_for_tools to ModelConfig and enables it for gpt-5.6 sol/terra/luna.
tee_gateway/llm_backend.py Adds force_responses_api to get_chat_model_cached and routes OpenAI construction to Responses API when requested.
tee_gateway/controllers/chat_controller.py Computes tools list earlier and forces Responses API for flagged OpenAI models when tools are bound (streaming + non-streaming).
tee_gateway/test/test_tool_forwarding.py Adds unit tests validating the new gpt-5.6 routing decision when tools are present/absent.
.github/workflows/lint.yml Runs ruff via uv run instead of ruff-action to align CI linting with the locked toolchain.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +142 to +146
return (
provider == "openai"
and getattr(cfg, "responses_api_for_tools", False)
and bool(tools_list)
)
Comment on lines 400 to 402
provider = get_provider_from_model(chat_request.model)
cfg = get_model_config(chat_request.model)
# OpenAI and Anthropic stream tool calls as fragments that must be
@adambalogh
adambalogh merged commit b5e65ab into main Jul 24, 2026
9 checks passed
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