Skip to content

Gateway model discovery from Claude Desktop 3P inference never advertises the 1M context window #496

Description

@moooyo

Filed by Claude Code (Opus 5), running as an agent on behalf of @moooyo. Everything below is either directly observed on the reporter's machine or read out of main at ac2bbb0; I've marked clearly which parts are hypothesis.

Summary

When Claude Desktop's third-party (custom) inference is pointed at a Floway gateway, gateway model discovery never yields the 1M-context variant of a 1M-capable model. Every discovered model behaves as the default 200K context window; there is no way to select 1M from the Desktop client.

Environment

  • Floway: self-hosted; behavior below read from main @ ac2bbb0
  • Client: Claude Desktop 1.30096.5 on Windows 11, configured for third-party inference against the Floway gateway
  • Bundled CLI in that profile: claude-code 2.1.234
  • Upstream: an Anthropic-native upstream serving a model whose limits.max_context_window_tokens is ≥ 1,000,000

Expected

Discovery through Claude Desktop's 3P inference path advertises the 1M-capable model such that the 1M context window is selectable, the same way it is for the Claude Code CLI.

Actual

Only the default 200K context window is available. No [1m] variant is offered anywhere in the Desktop model picker.

Where it comes from

packages/gateway/src/data-plane/models/http.ts gates the whole Anthropic-native catalog shape — including the [1m] id rewrite — behind a claude-code/ User-Agent prefix:

https://github.com/Menci/Floway/blob/ac2bbb0/packages/gateway/src/data-plane/models/http.ts#L72-L96

const isClaudeCodeUserAgent = (userAgent: string | undefined): boolean =>
  userAgent?.startsWith('claude-code/') ?? false;

// ...
return Response.json(isClaudeCodeUserAgent(userAgent)
  ? toClaudeCodeCatalog(publicCatalog)
  : publicCatalog);

and toClaudeCodeCatalog is the only place the suffix is ever applied:

id: max !== undefined && max >= 1_000_000 ? `${model.id}[1m]` : model.id,

The file's own comment states why this is fatal rather than cosmetic — the suffix has to arrive on the wire, because the client will not synthesize it:

the [1m] suffix convention — append [1m] to a model id and the CLI switches that pick to the 1M-context window — only reaches the picker when the discovered id itself carries the suffix; the CLI does not synthesize the variant on discovered ids in gateway mode.

So any discovery client that does not present a claude-code/<version> User-Agent falls through to the generic PublicModelsResponse branch, whose ids never carry [1m]. limits.max_context_window_tokens is still 1000000 on that payload, but nothing downstream turns that number into a selectable 1M window.

Hypothesis (unverified): Claude Desktop's 3P gateway discovery is issued by the Electron app itself rather than by the bundled CLI, so it presents some other User-Agent and misses the prefix check. I was not able to capture the exact User-Agent string Desktop sends — I only have the client side here, not the gateway's request log. If it's useful, the reporter can capture it from the Floway instance and post it.

Worth noting the codebase already treats "is this Claude Code?" as two different tests: the strict claude-code/ prefix here, versus /^claude-cli\/\d+\.\d+\.\d+/i in answer-claude-code-probe.ts. Neither would match a Desktop-originated request.

Possible directions

Listing these as options, not as a preferred design — the right call depends on how you want discovery clients identified:

  1. Recognize Claude Desktop's discovery User-Agent alongside claude-code/.
  2. Stop keying the Anthropic-native catalog shape on User-Agent, and instead serve it when the request is unambiguously Anthropic-native discovery (e.g. path/anthropic-version based), keeping the superset only as the fallback.
  3. Make it an operator-visible switch (per API key or per gateway) so a client whose UA you don't yet recognize can still be served the [1m]-suffixed catalog.

Happy to supply any further detail from the client side — logs, exact Desktop build, or a reproduction against a clean upstream.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions