Skip to content

fix(providers): route Responses-only models on custom OpenAI providers#2985

Merged
Sayt-0 merged 1 commit into
mainfrom
fix/2303-copilot-custom-provider-responses
Jun 3, 2026
Merged

fix(providers): route Responses-only models on custom OpenAI providers#2985
Sayt-0 merged 1 commit into
mainfrom
fix/2303-copilot-custom-provider-responses

Conversation

@Sayt-0
Copy link
Copy Markdown
Member

@Sayt-0 Sayt-0 commented Jun 3, 2026

Problem

Custom OpenAI-compatible providers declared in the providers: section without
an explicit api_type were hard-defaulted to openai_chatcompletions. Models
that are only served via the Responses API (gpt-4.1, the o-series, gpt-5, Codex)
were therefore sent to /chat/completions and rejected with
400 unsupported_api_for_model — e.g. a github-copilot endpoint declared as a
custom provider running gpt-5.3-codex.

Fixes #2303

Fix

pkg/model/provider/defaults.go

  • Added defaultOpenAIAPIType(model): returns openai_responses when
    modelinfo.SupportsResponsesAPI(model) is true, otherwise
    openai_chatcompletions.
  • mergeFromProviderConfig now uses defaultOpenAIAPIType(dst.Model) for the
    OpenAI-compatible fallback instead of always setting openai_chatcompletions,
    reusing the same predicate as the built-in routing.

Tests

pkg/model/provider/custom_provider_test.go

  • TestApplyProviderDefaults_DefaultsResponsesAPIForNewerModels: a custom
    provider with no api_type routes gpt-5.3-codex, gpt-5, o3-mini to
    openai_responses, while gpt-4o stays on openai_chatcompletions.
  • TestApplyProviderDefaults_ExplicitAPITypeWinsOverModel: an explicit
    provider-level api_type is honored even for a Responses-default model.

Custom OpenAI-compatible providers (providers: section) without an explicit
api_type were hard-defaulted to openai_chatcompletions, disabling the
per-request endpoint auto-detection that the built-in openai/github-copilot
providers rely on. Models served only via the Responses API (gpt-4.1,
o-series, gpt-5, Codex) were therefore sent to /chat/completions and rejected
with 400 unsupported_api_for_model — e.g. a github-copilot endpoint defined as
a custom provider running gpt-5.3-codex.

Default the api_type from the same modelinfo.SupportsResponsesAPI predicate so
both paths stay consistent. An explicit provider- or model-level api_type
still wins.

Fixes #2303
@Sayt-0 Sayt-0 requested a review from a team as a code owner June 3, 2026 09:22
Copy link
Copy Markdown

@docker-agent docker-agent left a comment

Choose a reason for hiding this comment

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

Assessment: 🟡 NEEDS ATTENTION

One medium-severity behavioral regression was confirmed in the changed code.

Summary of findings:

  • 🟡 MEDIUM pkg/model/provider/defaults.go:47 — Model-name-based api_type defaulting silently breaks non-OpenAI custom providers whose model names match OpenAI patterns

// modelinfo.SupportsResponsesAPI predicate keeps both paths consistent so a
// provider pointed at the OpenAI/Copilot API works without a manual
// api_type override. See https://github.com/docker/docker-agent/issues/2303.
func defaultOpenAIAPIType(model string) string {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MEDIUM] Model-name-based api_type defaulting silently breaks non-OpenAI custom providers

defaultOpenAIAPIType now routes any model whose name matches SupportsResponsesAPI prefixes (gpt-4.1*, gpt-5*, codex*, o-series) to openai_responses. This is correct for providers actually pointing at the OpenAI or Copilot API — but the gate that triggers this logic (isOpenAICompatibleProvider(resolveEffectiveProvider(src))) is too broad:

  • A custom provider declared with only a base_url and no explicit Provider field resolves its effective type to "openai" (via resolveEffectiveProvider), which makes isOpenAICompatibleProvider return true.
  • This means any URL-based third-party gateway (a local vLLM instance, LiteLLM proxy, Azure relay, etc.) that names its model gpt-5-local, o3-preview, or codex-custom will silently have requests routed to /responses instead of /chat/completions, causing 404 or 400 errors.

Before this PR: all custom providers defaulted to openai_chatcompletions — a safe universal default.
After this PR: the default is model-name–driven, and any pattern match sends traffic to /responses.

The workaround exists (api_type: openai_chatcompletions in the provider config), but users have no way to know they need it unless they read the source code.

Suggested mitigation: consider narrowing the condition — e.g., only apply defaultOpenAIAPIType when src.BaseURL contains openai.com or githubcopilot.com, or add a first-class boolean flag (responses_api_routing: true) that operators must opt into for custom providers.

@Sayt-0 Sayt-0 merged commit 61020cf into main Jun 3, 2026
12 of 13 checks passed
@Sayt-0 Sayt-0 deleted the fix/2303-copilot-custom-provider-responses branch June 3, 2026 09:44
aheritier added a commit that referenced this pull request Jun 4, 2026
Updates docs/providers/custom/index.md to reflect that `api_type` is
now auto-detected based on the model name for custom OpenAI-compatible
providers. Newer models (gpt-4.1, o-series, gpt-5, Codex) default to
`openai_responses`; others default to `openai_chatcompletions`.

Previously the docs showed a hardcoded default of `openai_chatcompletions`
which was incorrect post-merge.

Source PR: #2985
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.

copilot: unsupported_api_for_model

3 participants