Skip to content

feat: expose max_output in /v1/models - #3379

Merged
steebchen merged 3 commits into
theopenco:mainfrom
RATCHAW:add-llgateway-provider
Aug 2, 2026
Merged

steebchen merged 3 commits into
theopenco:mainfrom
RATCHAW:add-llgateway-provider

Conversation

@RATCHAW

@RATCHAW RATCHAW commented Aug 2, 2026 •

Copy link
Copy Markdown
Contributor

What

Surfaces per-model output-token limits in the public GET /v1/models catalogue:

  • each providers[] entry carries its mapping's exact maxOutput as max_output
  • the model level reports the minimum across still-servable mappings that declare one — the largest max_tokens value guaranteed to be accepted regardless of which provider serves the request (the gateway validates max_tokens against the routed mapping's limit and rejects anything above it with HTTP 400). Deactivated mappings can no longer serve, so their limits are excluded; deprecated mappings remain routable and stay in
  • mappings that declare no limit accept any max_tokens, so they don't constrain the model-level bound; models where no mapping declares one omit the field

Why

Consumers currently have no way to discover the cap — they either guess low and truncate output or guess high and get 400s. The Pi coding-agent integration (earendil-works/pi#7480) reads this field to set per-model output limits (falling back to a conservative 4096 until this ships).

Testing

Extended models.spec.ts with a spot check (claude-haiku-4-5 → 64000 per-provider and model-level) plus an exhaustive assertion that every model's max_output equals the minimum across its still-servable declaring mappings (or is omitted), and a deepseek-v3.2 case pinning the bound to 65536 rather than its retired Nebius mapping's 32768. All 23 models endpoint tests pass; tsc and prettier clean.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Model API responses now include maximum output limits for individual provider mappings.
    • Model-level responses show the lowest available limit across active mappings, while retaining deprecated mappings and excluding deactivated ones.
    • The limit is omitted when no applicable value is defined.
  • Tests

    • Added coverage to verify maximum output limits are reported and calculated correctly.

Surface per-model output-token limits in the public catalogue: each
provider entry carries its mapping's exact maxOutput, and the model
level reports the minimum across mappings that declare one - the
largest max_tokens guaranteed to be accepted regardless of which
provider serves the request (requests are validated against the
routed mapping's limit, so anything above it fails with HTTP 400).

Consumers previously had no way to discover the cap; the Pi
coding-agent integration reads this field to set per-model output
limits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 2, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The models API exposes optional max_output values for provider mappings and models. Model-level values use the minimum declared limit across eligible mappings. Deactivated mappings do not affect the result. Tests cover defined and omitted limits.

Changes

Model output limit metadata

Layer / File(s) Summary
Limit contracts and provider output
apps/gateway/src/models/models.ts
The provider-mapping and model response schemas define optional max_output fields. Provider mappings return provider.maxOutput.
Model limit aggregation
apps/gateway/src/models/models.ts, apps/gateway/src/models/models.spec.ts
The model response computes the minimum declared limit across non-deactivated mappings. Tests cover active and deprecated mappings, deactivated mappings, and omitted limits.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: exposing max_output in the /v1/models endpoint.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
apps/gateway/src/models/models.spec.ts (1)

104-134: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Cover the omitted provider-level field.

This test verifies a mapping with max_output: 64000, but it does not verify the optional provider contract. Select a mapping whose source maxOutput is undefined and assert that the response object omits max_output. Otherwise, a provider-level serialization regression can pass this test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/gateway/src/models/models.spec.ts` around lines 104 - 134, Extend the
GET /v1/models test to select a provider mapping from the response whose source
ProviderModelMapping.maxOutput is undefined, then assert that its serialized
provider object does not contain max_output. Keep the existing positive
max_output assertion and model-level minimum checks unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/gateway/src/models/models.spec.ts`:
- Around line 104-134: Extend the GET /v1/models test to select a provider
mapping from the response whose source ProviderModelMapping.maxOutput is
undefined, then assert that its serialized provider object does not contain
max_output. Keep the existing positive max_output assertion and model-level
minimum checks unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3fcf76f3-0025-4af1-9e72-ae75b6cf78fb

📥 Commits

Reviewing files that changed from the base of the PR and between 119b3c7 and 54f40d1.

📒 Files selected for processing (2)
  • apps/gateway/src/models/models.spec.ts
  • apps/gateway/src/models/models.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 54f40d1db2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +390 to +392
const limits = mappings
.map((p) => p.maxOutput)
.filter((limit): limit is number => limit !== undefined);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exclude deactivated mappings from the model-level limit

When a model retains both active and deactivated mappings, this minimum includes mappings that can no longer serve requests. For example, after 2026-06-22 the default GET /v1/models still includes deepseek-v3.2, but its deactivated Nebius mapping contributes 32768, so consumers such as the Pi integration cap output there even though every active mapping with a declared limit accepts at least 65536. Filter mappings whose deactivatedAt has passed before calculating the advertised serving bound, as pickPricingProvider already does for model-level pricing.

AGENTS.md reference: AGENTS.md:L229-L230

Useful? React with 👍 / 👎.

RATCHAW and others added 2 commits August 2, 2026 12:48
Deactivated mappings can no longer serve requests, so their declared
limits must not constrain the advertised model-level bound (e.g.
deepseek-v3.2's retired Nebius mapping capped it at 32768 while every
servable mapping accepts at least 65536). Deprecated mappings remain
routable and keep enforcing their limit, so they stay in.

Addresses the Codex review finding on theopenco#3379.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@steebchen
steebchen merged commit 3ab66a2 into theopenco:main Aug 2, 2026
8 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.

2 participants