feat(llm): add openai-responses provider (OpenAI Responses API) - #3121
Merged
Conversation
nicoloboschi
force-pushed
the
feat/openai-responses-provider
branch
2 times, most recently
from
August 3, 2026 09:42
3b48773 to
cc4b14b
Compare
Add a provider that talks exclusively to the OpenAI Responses API (`client.responses.create` → `/v1/responses`) — never chat/completions. Motivation: reasoning models such as gpt-5.6-terra reject `reasoning_effort` combined with function tools on `/v1/chat/completions` (HTTP 400 unless `reasoning_effort="none"`, see #2983). Reflect is a tool-calling search loop, so that constraint forces the whole reflect operation — including the final synthesis — to run with reasoning disabled. The Responses API models the chain-of-thought as a first-class reasoning item, so reasoning and tools coexist; reflect's search loop can now run with a real reasoning effort. `OpenAIResponsesLLM` is a standalone `LLMInterface` implementation (OpenAI-only; it deliberately does NOT subclass the multi-vendor chat/completions provider, so it can never route through `chat.completions` and carries none of the groq/ollama/deepseek special-casing). It reuses only provider-agnostic pure helpers (text cleanup, quota-defer parsing). It translates the engine's chat-shaped inputs: - chat messages → `input` items (assistant `tool_calls` → `function_call`, `role="tool"` → `function_call_output` keyed by `call_id`), - nested `{"type":"function","function":{...}}` tools → flattened `{"type":"function","name":...,"parameters":...}`, - flat `reasoning_effort` → a `reasoning={"effort": ...}` object, - `response_format` → `text={"format": {...}}` (strict json_schema or the soft schema-in-prompt + json_object fallback), - reads `response.output_text` + `function_call` items from `response.output`. Generic LLM config flags are honored: `extra_body`, `timeout`, per-call `temperature`/`max_completion_tokens`/`max_retries`. It also wires two flags the chat/completions path drops — `openai_service_tier` (as the native Responses `service_tier`) and `default_headers` (on the SDK client). The conversation is replayed statelessly each turn (`store=False`, no `previous_response_id`); server-side reasoning reuse across turns is left as a future optimization. Wiring: provider registration + dispatch, `PROVIDER_DEFAULT_MODELS` default (`gpt-5.6`), docs + `.env.example` (+ embed template sync), and an `openai` floor bump to `>=1.66.0` for the Responses API surface. Unit tests mock `responses.create` (incl. the generic-flag wiring) and pin that reasoning + tools are sent together on the tool path — the combination chat/completions rejects. Validated live against real gpt-5.6-terra: plain + reasoning, strict structured output, reasoning+tools together with a stateless function_call replay, and a full run_reflect_agent end-to-end (stubbed retrieval, no DB) — tools drove to a correct synthesized answer.
nicoloboschi
force-pushed
the
feat/openai-responses-provider
branch
from
August 3, 2026 10:17
cc4b14b to
abcac65
Compare
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.
What
Adds a new LLM provider
openai-responsesthat calls the OpenAI Responses API (client.responses.create→/v1/responses) instead of chat/completions.Why
Reasoning models such as
gpt-5.6-terrarejectreasoning_effortcombined with function tools on/v1/chat/completions:Reflect is a tool-calling search loop, so on chat/completions the only non-400 setting (
"none") also strips reasoning from reflect's final synthesis (single effort knob per op). The Responses API models the chain-of-thought as a first-class reasoning item, so reasoning and function tools coexist — reflect's search loop runs with a realreasoning_effortand still calls tools.Design
OpenAIResponsesLLMis a standaloneLLMInterfaceimplementation. It is OpenAI-only and deliberately does not subclass the multi-vendorOpenAICompatibleLLM(chat/completions) class — so it can never route throughchat.completionsand carries none of the groq/ollama/deepseek special-casing. It reuses only a few provider-agnostic pure helpers (text-tag stripping, quota-defer parsing). The shared chat retry loop is untouched (no regression risk to the ~13 existing OpenAI-compatible providers).Translations:
inputitems (assistanttool_calls→function_call;role="tool"→function_call_outputkeyed bycall_id){"type":"function","function":{…}}tools → flattened{"type":"function","name":…,"parameters":…}reasoning_effort→reasoning={"effort": …}response_format→text={"format": {…}}(strictjson_schema, or the soft schema-in-prompt +json_objectfallback)response.output_text+function_callitems fromresponse.output; usage fromusage.input_tokens/output_tokens(reasoning split out of visible output)Conversation is replayed statelessly each turn (
store=False, noprevious_response_id); server-side reasoning reuse across turns is a future optimization.Generic LLM config flags
Honors
extra_body,timeout, and per-calltemperature/max_completion_tokens/max_retries. It additionally wires two generic flags the chat/completions path drops:openai_service_tier→ the native Responsesservice_tierparam (e.g.flex)default_headers→ the OpenAI SDK client (proxy / request-tracing)OpenAI-compatible endpoints
Like the
openai(Chat Completions) provider,openai-responseshonors a customHINDSIGHT_API_LLM_BASE_URL, so any OpenAI-compatible endpoint that exposes/v1/responses(gateways, Azure-style deployments) works the same way as a Chat Completions one.Docs
developer/models.mdx): new provider in the grid, a dedicated "OpenAI Responses API" tip, and the OpenAI-Compatible tip now states explicitly that theopenaiprovider uses Chat Completions whileopenai-responsesuses the Responses API — both usable against compatible custom endpoints.configuration.mdprovider list + Chat-Completions/Responses compatible-endpoint examples..env.example(+ embed template sync); docs skill mirror regenerated.Wiring
llm_wrapper.py).PROVIDER_DEFAULT_MODELS["openai-responses"] = "gpt-5.6".openaifloor bumped to>=1.66.0for the Responses API surface.Tests
tests/test_openai_responses_provider.py(19 tests) mockresponses.createand cover registration/wiring, request shaping (reasoning object, temperature omission for reasoning models,max_output_tokens, strict/soft structured output, truncation →OutputTooLongError), message-history translation, tool-choice mapping, response parsing, and the generic-flag wiring (extra_body,service_tier,default_headers). The load-bearing test pins thatreasoningandtoolsare sent together on the tool path.Live validation (real
gpt-5.6-terra)reasoning_tokens> 0 in the same request as a function call) ✅function_call/function_call_outputreplay → final answer ✅run_reflect_agentend-to-end (stubbed retrieval, no DB): the agent drove the Responses tool loop throughrecallto a correct synthesized answer ✅Usage
🤖 Generated with Claude Code