Skip to content

refactor(model): per-provider jsonOutput flag across mappings - #1068

Merged
steebchen merged 14 commits into
mainfrom
terragon/refactor-jsonoutput-model-mapping-qhb3tl
Oct 25, 2025
Merged

steebchen merged 14 commits into
mainfrom
terragon/refactor-jsonoutput-model-mapping-qhb3tl

Conversation

@steebchen

@steebchen steebchen commented Oct 24, 2025

Copy link
Copy Markdown
Member

Summary

  • jsonOutput is removed at the ModelDefinition level and now defined per provider in ModelProviderMapping.
  • Gate JSON output per provider mappings rather than a model-wide flag.
  • UI and gateway updated to derive jsonOutput capability from provider.jsonOutput.
  • E2E tests adjusted to verify per-mapping jsonOutput support.

Changes

Core types

  • Add jsonOutput?: boolean to ProviderModelMapping in packages/models/src/models.ts.
  • Remove jsonOutput?: boolean from ModelDefinition (and associated model definitions).

Model definitions

  • Update provider mappings across models to include jsonOutput at the provider level (true/false) instead of a global model flag. Examples updated include alibaba, google, openai, llmgateway, mistral, moonshot, routeway, xai, zai, meta, deepseek, and others. This migration ensures each provider explicitly declares jsonOutput support.

UI

  • apps/ui/src/lib/model-utils.ts: determine capabilities using provider?.jsonOutput instead of model.jsonOutput.

Gateway logic

  • apps/gateway/src/chat/chat.ts: when response_format requests JSON outputs (json_object or json_schema), check if any provider in the model mapping has jsonOutput === true. If none do, throw 400 with a clear error.

End-to-end tests

  • apps/gateway/src/chat-json.e2e.ts: adjusted to rely on ProviderModelMapping.jsonOutput for filtering and validation instead of model-wide jsonOutput.

Migration notes

  • Breaking change: jsonOutput is no longer a model-wide property. Providers must declare jsonOutput per mapping. Ensure all provider mappings include jsonOutput where relevant.

Test plan

  • Run end-to-end tests for JSON output paths:
    • Verify that tests only run for models where at least one provider mapping has jsonOutput: true.
    • Verify that attempting JSON output on a model with no jsonOutput providers results in HTTP 400 as expected.
  • Validate UI capabilities listing now reflects JSON Output when provider.jsonOutput is true.

Impact

  • Minor to moderate refactor across model definitions and consumer codepaths.
  • No behavioral change for models that already had per-provider jsonOutput flag set; now enforced via provider mappings.

Notes

  • If any downstream code still references model.jsonOutput, update to use provider.jsonOutput or guard accordingly.
  • This PR includes updates across 28 files to align with the new per-provider jsonOutput contract.

📎 Task: https://www.terragonlabs.com/task/b85a3de2-f3cc-49f1-8f37-6a8db343a1e6

Summary by CodeRabbit

  • Refactor

    • JSON Output capability now resolves per provider (not per model), improving accuracy of which models can produce structured JSON.
  • UI

    • Model lists, filters, badges and comparison views now reflect provider-level JSON support.
    • Provider cards show a new "Capabilities" area with icons and tooltips (including JSON Output).
  • Bug Fixes

    • JSON output and JSON schema gating behave more reliably when selecting specific providers.

Refactor the jsonOutput attribute from the model definition to individual provider model mappings. This enables more precise specification of JSON output support per provider instead of at the global model level. Adjusted model files and relevant code to check jsonOutput from providers.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
@bunnyshell

bunnyshell Bot commented Oct 24, 2025

Copy link
Copy Markdown

❌ Preview Environment deleted from Bunnyshell

Available commands (reply to this comment):

  • 🚀 /bns:deploy to deploy the environment

@coderabbitai

coderabbitai Bot commented Oct 24, 2025

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@steebchen has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 6 minutes and 17 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between cb30cf7 and 3d88ce0.

📒 Files selected for processing (2)
  • apps/ui/src/app/models/[name]/page.tsx (3 hunks)
  • packages/models/src/models/openai.ts (39 hunks)

Walkthrough

Moved JSON output capability from model-level to provider-level across model catalogs and types; removed jsonOutput columns from DB; updated gateway, UI, sync, tests, request-prep, and migrations to consult per-provider jsonOutput/jsonOutputSchema flags.

Changes

Cohort / File(s) Summary
Type definitions
packages/models/src/models.ts
Add jsonOutput?: boolean to ProviderModelMapping; remove jsonOutput from ModelDefinition.
Model catalogs
packages/models/src/models/*.ts
packages/models/src/models/{alibaba,deepseek,google,llmgateway,meta,mistral,moonshot,nousresearch,openai,perplexity,routeway,xai,zai}.ts
Remove top-level jsonOutput from model entries and add jsonOutput to individual providers[...] entries (per-provider booleans).
Providers declarations
packages/models/src/providers.ts
Remove jsonOutput from ProviderDefinition and provider object literals.
Gateway runtime & mapping
apps/gateway/src/chat/chat.ts, apps/gateway/src/models/models.ts
Replace model-level jsonOutput checks with provider-aware logic (scan providers or respect requested provider) to gate json_object/json_schema.
Tests
apps/gateway/src/chat-json.e2e.ts
Update test filters/assertions to inspect providers for jsonOutput/jsonOutputSchema and use provider-based mappings.
UI / capability utilities
apps/ui/src/lib/model-utils.ts, apps/playground/src/lib/model-utils.ts, apps/ui/src/components/models/*.tsx, apps/ui/src/components/api-keys/multi-model-selector.tsx
Derive/display "JSON Output" capability by checking model providers (e.g., model.providers.some(p => p.jsonOutput)) instead of model.jsonOutput.
DB schema & sync
packages/db/src/schema.ts, apps/worker/src/services/sync-models.ts
Drop json_output columns from model and provider tables in schema and remove jsonOutput from sync insert/update clauses.
Migrations & journal
packages/db/migrations/1761342595_glorious_flatman.sql, packages/db/migrations/1761349187_petite_warbird.sql, packages/db/migrations/meta/_journal.json
Migrations that drop json_output columns and appended journal entries.
Request preparation tweaks
packages/models/src/prepare-request-body.ts
For google-ai-studio provider, set generationConfig.responseMimeType = "application/json" when response_format is used for json_object/json_schema.
UI: Provider card
apps/ui/src/components/models/provider-card.tsx
Add capability badges/tooltips (Streaming, Vision, Tools, Reasoning, JSON Output) and minor layout spacing.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Gateway
    participant ModelCatalog
    participant Provider
    Note over ModelCatalog #D6EAF8: Models include per-provider flags (jsonOutput/jsonOutputSchema)
    Client->>Gateway: Chat request (model id, optional provider)
    Gateway->>ModelCatalog: Load model and providers[]
    ModelCatalog-->>Gateway: Return model + providers
    Note right of Gateway #F7F9F9: Determine JSON support by scanning providers
    alt specific provider requested
        Gateway->>Provider: Inspect requestedProvider.jsonOutput / jsonOutputSchema
    else no specific provider
        Gateway->>Provider: Inspect any provider.jsonOutput / jsonOutputSchema
    end
    alt supports jsonOutput
        Gateway->>Gateway: Apply JSON validation/formatting (schema if required)
        Gateway-->>Client: Return JSON response
    else not supported
        Gateway-->>Client: Return unsupported error or fallback
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Potential attention areas:

  • Type updates in packages/models/src/models.ts and all call sites.
  • DB migration correctness and sync logic in packages/db/src/schema.ts and apps/worker/src/services/sync-models.ts.
  • E2E tests in apps/gateway/src/chat-json.e2e.ts for provider-based filtering and expectations.

Possibly related PRs

Suggested labels

auto-merge

Suggested reviewers

  • smakosh

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "refactor(model): per-provider jsonOutput flag across mappings" directly and accurately reflects the primary architectural change in this changeset. The title specifies that jsonOutput is being refactored to be per-provider and applies across multiple model mappings. This is confirmed by the file-level summaries showing that jsonOutput has been removed from ModelDefinition and added to ProviderModelMapping in the core models file, with corresponding updates across approximately 15+ model definition files (alibaba, google, openai, mistral, etc.) moving jsonOutput from top-level model entries to per-provider blocks. The title is concise, specific, and avoids vague terminology; a developer scanning the repository history would clearly understand that this is a structural refactoring of how JSON output capabilities are declared.

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 and usage tips.

@github-actions github-actions Bot changed the title Refactor jsonOutput to mapping-specific flag across providers refactor(model): per-provider jsonOutput flag across mappings Oct 24, 2025
Previously, the jsonOutput property was stored directly on models and synced
as a top-level attribute. This change removes the jsonOutput field from the
model schema and instead derives jsonOutput support based on the providers
associated with each model. This refactors the handling of the jsonOutput
capability to be provider-specific rather than model-wide, improving accuracy
of capability reporting and data consistency across components.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>

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

Actionable comments posted: 0

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

228-228: Logic is correct; consider removing redundant || false.

The refactor correctly computes json_output by checking if any provider has jsonOutput set to true, which aligns with the PR's goal of moving jsonOutput to per-provider configuration. The logic handles edge cases properly (empty providers array, undefined jsonOutput values).

However, Array.prototype.some() always returns a boolean, so the || false fallback is redundant.

Apply this diff to simplify:

-json_output: model.providers.some((p) => p.jsonOutput) || false,
+json_output: model.providers.some((p) => p.jsonOutput),
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2173c68 and 4b1ac49.

📒 Files selected for processing (4)
  • apps/gateway/src/models/models.ts (1 hunks)
  • apps/playground/src/lib/model-utils.ts (1 hunks)
  • apps/worker/src/services/sync-models.ts (0 hunks)
  • packages/db/src/schema.ts (0 hunks)
💤 Files with no reviewable changes (2)
  • apps/worker/src/services/sync-models.ts
  • packages/db/src/schema.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Always use top-level import; never use require() or dynamic import()

Files:

  • apps/playground/src/lib/model-utils.ts
  • apps/gateway/src/models/models.ts
apps/{ui,playground}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{ui,playground}/**/*.{ts,tsx}: In Next.js apps, use next/link for links and next/navigation’s router for programmatic navigation (avoid tags, window.location, or legacy next/router)
Use cookies for user settings that are not persisted in the database to ensure SSR works

apps/{ui,playground}/**/*.{ts,tsx}: Use next/link for links and next/navigation's router for programmatic navigation
Use cookies for user settings not saved in the database to ensure SSR works

Files:

  • apps/playground/src/lib/model-utils.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Never use any or as any in this TypeScript project unless absolutely necessary
Always use top-level import; do not use require or dynamic import()

Files:

  • apps/playground/src/lib/model-utils.ts
  • apps/gateway/src/models/models.ts
apps/{gateway,api}/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

apps/{gateway,api}/**/*.ts: Use Hono for HTTP routing in Gateway and API services
Use Zod schemas for request/response validation in server routes

Files:

  • apps/gateway/src/models/models.ts
{apps/{api,gateway}/**/*.ts,packages/db/**/*.ts}

📄 CodeRabbit inference engine (CLAUDE.md)

For read operations, use db().query.<table>.findMany() or db().query.<table>.findFirst()

Files:

  • apps/gateway/src/models/models.ts
🧬 Code graph analysis (1)
apps/playground/src/lib/model-utils.ts (1)
packages/db/src/schema.ts (1)
  • provider (554-587)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: build / run
  • GitHub Check: test / run
  • GitHub Check: generate / run
  • GitHub Check: lint / run
  • GitHub Check: e2e-shards (5)
  • GitHub Check: e2e-shards (2)
  • GitHub Check: e2e-shards (3)
  • GitHub Check: e2e-shards (4)
  • GitHub Check: e2e-shards (1)
  • GitHub Check: autofix
🔇 Additional comments (1)
apps/playground/src/lib/model-utils.ts (1)

53-53: LGTM! Consistent with per-provider capability pattern.

The change to check provider?.jsonOutput aligns with the PR's refactoring objective and is consistent with how other capabilities (streaming, vision, tools, reasoning) are already checked at the provider level.

steebchen and others added 10 commits October 24, 2025 21:48
…bilities

The jsonOutput property is now determined by checking if any providers have jsonOutput enabled, rather than relying on a top-level property. This fix adjusts MultiModelSelector, AllModels filtering logic, and ModelComparison to accurately reflect provider-level jsonOutput support.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
…port checks

Previously, JSON output support was checked across all model providers regardless of the requested provider.

This change filters the providers list by the requestedProvider when specified, ensuring JSON output and schema support checks are performed only on relevant providers.

This improves accuracy in determining supported response formats based on the user's requested provider.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
…ders

Removed the jsonOutput boolean property from the ProviderDefinition interface, all provider definitions, and related database schema and sync code. This cleans up unused legacy code related to jsonOutput support for providers.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
- Added handling for response_format types 'json_object' and 'json_schema'
- Set 'responseMimeType' to 'application/json' accordingly
- Prepared groundwork for future JSON Schema conversion for Google format

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
Use explicit type assertion for provider in json_output check to ensure correct boolean evaluation instead of relying on loose truthiness check.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
- Introduced a new capabilities section in ProviderCard component displaying various provider features like streaming, vision, tools, reasoning, and JSON output.
- Each capability is represented with an icon and tooltip explaining the feature.
- Fixed a variable reference in AllModels component to correctly check jsonOutput capability.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/ui/src/components/models/provider-card.tsx (1)

233-304: Consider refactoring to reduce code duplication.

The Capabilities section contains significant duplication across the five capability badges. Each follows an identical pattern with only the property name, icon, label, color scheme, and tooltip text varying.

Consider extracting this into a reusable pattern:

// Define capabilities configuration
const capabilities = [
  {
    key: 'streaming' as const,
    icon: Zap,
    label: 'Streaming',
    tooltip: 'Supports streaming responses',
    colorClasses: 'bg-blue-50 dark:bg-blue-950/30 text-blue-700 dark:text-blue-300'
  },
  {
    key: 'vision' as const,
    icon: Eye,
    label: 'Vision',
    tooltip: 'Supports vision and image inputs',
    colorClasses: 'bg-green-50 dark:bg-green-950/30 text-green-700 dark:text-green-300'
  },
  // ... other capabilities
] as const;

// In the render:
<div className="flex flex-wrap gap-2">
  {capabilities.map(({ key, icon: Icon, label, tooltip, colorClasses }) => 
    provider[key] && (
      <Tooltip key={key}>
        <TooltipTrigger asChild>
          <div className={`flex items-center gap-1.5 px-2 py-1 rounded-md ${colorClasses} text-xs`}>
            <Icon className="h-3.5 w-3.5" />
            <span>{label}</span>
          </div>
        </TooltipTrigger>
        <TooltipContent>
          <p>{tooltip}</p>
        </TooltipContent>
      </Tooltip>
    )
  )}
</div>

This would reduce ~60 lines to ~20 and make it easier to add new capabilities in the future.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1df4531 and cb30cf7.

📒 Files selected for processing (2)
  • apps/ui/src/components/models/all-models.tsx (2 hunks)
  • apps/ui/src/components/models/provider-card.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/ui/src/components/models/all-models.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Always use top-level import; never use require() or dynamic import()

Files:

  • apps/ui/src/components/models/provider-card.tsx
apps/{ui,playground}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{ui,playground}/**/*.{ts,tsx}: In Next.js apps, use next/link for links and next/navigation’s router for programmatic navigation (avoid tags, window.location, or legacy next/router)
Use cookies for user settings that are not persisted in the database to ensure SSR works

apps/{ui,playground}/**/*.{ts,tsx}: Use next/link for links and next/navigation's router for programmatic navigation
Use cookies for user settings not saved in the database to ensure SSR works

Files:

  • apps/ui/src/components/models/provider-card.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Never use any or as any in this TypeScript project unless absolutely necessary
Always use top-level import; do not use require or dynamic import()

Files:

  • apps/ui/src/components/models/provider-card.tsx
🧬 Code graph analysis (1)
apps/ui/src/components/models/provider-card.tsx (1)
packages/db/src/schema.ts (1)
  • provider (554-586)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: test / run
  • GitHub Check: lint / run
  • GitHub Check: build / run
  • GitHub Check: generate / run
  • GitHub Check: e2e-shards (5)
  • GitHub Check: e2e-shards (4)
  • GitHub Check: e2e-shards (1)
  • GitHub Check: e2e-shards (2)
  • GitHub Check: e2e-shards (3)
  • GitHub Check: autofix
🔇 Additional comments (3)
apps/ui/src/components/models/provider-card.tsx (3)

3-24: LGTM! Import additions support the new Capabilities section.

The new icon imports and Tooltip components are correctly added to support the capability badges feature.


146-146: LGTM! Spacing adjustment improves layout.

The mb-4 addition provides appropriate visual separation before the new Capabilities section.


289-301: Correct implementation of per-provider JSON Output capability.

The JSON Output badge correctly reads from provider.jsonOutput, aligning with this PR's refactoring goal to move JSON output support from model-level to provider-level configuration.

steebchen and others added 2 commits October 25, 2025 12:56
- Import Braces icon from lucide-react
- Add hasJsonOutput check for model providers
- Display JSON Output item with Braces icon and cyan color in UI

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
@steebchen
steebchen merged commit 12cce5e into main Oct 25, 2025
12 of 13 checks passed
@steebchen
steebchen deleted the terragon/refactor-jsonoutput-model-mapping-qhb3tl branch October 25, 2025 13:06
@coderabbitai coderabbitai Bot mentioned this pull request Jan 11, 2026
6 tasks
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.

1 participant