Route gpt-5.6 models through Responses API when function tools are bound#135
Merged
Merged
Conversation
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
Contributor
There was a problem hiding this comment.
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The chat-app agent page fails for the gpt-5.6 models (
sol/terra/luna) with an OpenAI 400:The gpt-5.6 family applies a default
reasoning_effortserver-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_effortitself, andlangchain-openaionly auto-prefers the Responses API for-pro/codexvariants — 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 (asreasoning_effort='none'would).model_registry.py: newresponses_api_for_toolsflag onModelConfig, set ongpt-5.6-sol/terra/luna.llm_backend.py:get_chat_model_cachedgains aforce_responses_apiparam; the OpenAI branch switches to the Responses API when it (orweb_search) is set. Model cache key extended accordingly.chat_controller.py: both streaming and non-streaming paths build the tools list first, then setforce_responses_apivia_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:force_responses_api=Trueforce_responses_api=Falseforce_responses_api=Falseruff format,ruff check, andmypyall 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