Skip to content

refactor(gateway): extract validateModelCapabilities - #1571

Merged
steebchen merged 2 commits into
mainfrom
steebchen/validate-model-caps
Feb 2, 2026
Merged

steebchen merged 2 commits into
mainfrom
steebchen/validate-model-caps

Conversation

@steebchen

@steebchen steebchen commented Feb 2, 2026 •

Copy link
Copy Markdown
Member

Summary

Extract model capability validation logic into a dedicated function for improved testability and maintainability. Validates JSON output, JSON schema, reasoning, tools, and web search capabilities.

Changes

  • New validateModelCapabilities() function in tools/validate-model-capabilities.ts
  • Removed ~124 lines of inline validation from chat.ts
  • All validation checks preserved with identical error messages

Test Status

All 390 unit tests pass. Production build verified.

Summary by CodeRabbit

  • Refactor
    • Centralized model capability validation into a single shared validator to improve organization and reduce duplication.
    • Validation now consistently enforces support for requested features (JSON output/schema, reasoning, tools, web search) and returns clear client errors when a model/provider lacks a requested capability.

Extract model capability validation logic into a dedicated
function for JSON output, reasoning, tools, and web search.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 2, 2026 16:17
@coderabbitai

coderabbitai Bot commented Feb 2, 2026 •

Copy link
Copy Markdown
Contributor

Walkthrough

A large block of inline model capability validation checks in the chat endpoint has been extracted into a new utility. The new validateModelCapabilities function centralizes validation for JSON output, JSON schema, reasoning, tools, web search, and tool choice against provider/model capability mappings and now throws HTTP 400 for unsupported requests.

Changes

Cohort / File(s) Summary
Chat endpoint update
apps/gateway/src/chat/chat.ts
Removed ~124 lines of inline capability checks and replaced them with a single validateModelCapabilities(...) call; routing logic preserved but validation delegated to the new utility.
Validation utility
apps/gateway/src/chat/tools/validate-model-capabilities.ts
Added new module exporting ValidateModelCapabilitiesOptions and validateModelCapabilities(...) which validates json output/schema, reasoning, tools, webSearch and tool_choice against provider/model capability mappings and throws HTTP 400 with specific error messages on failure.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant Chat as ChatEndpoint
  participant Validator as ValidateModelCapabilities
  participant Registry as ProviderRegistry

  Client->>Chat: POST /chat (model, provider, options)
  Chat->>Validator: validateModelCapabilities(modelInfo, model, provider, options)
  Validator->>Registry: fetch model/provider capability mappings
  Registry-->>Validator: capability metadata
  alt capabilities supported
    Validator-->>Chat: OK
    Chat->>Chat: proceed with routing/response generation
    Chat-->>Client: 200 OK / streamed response
  else unsupported capability
    Validator-->>Chat: throws HTTP 400 (detailed message)
    Chat-->>Client: 400 Bad Request (capability error)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • #1476: Modifies JSON output/json_schema capability checks in apps/gateway/src/chat/chat.ts, overlapping with the consolidated validator here.
  • #1119: Adds/changes tool-support validation logic in chat handling; strongly related to the new tool/web-search validation.
  • #1010: Introduced json_schema response_format support and provider jsonOutputSchema mappings that this validator now uses.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor(gateway): extract validateModelCapabilities' directly reflects the main change: extracting model capability validation logic into a dedicated function.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch steebchen/validate-model-caps

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.

Copilot AI 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.

Pull request overview

This PR refactors model capability validation logic by extracting it from the main chat handler into a dedicated, well-documented function. This improves code maintainability and testability by separating validation concerns into a focused, reusable module.

Changes:

  • Extracted validation logic into validateModelCapabilities() function with clear interface
  • Reduced complexity in main chat handler by removing ~124 lines of inline validation
  • Preserved all validation checks with identical error messages and logic

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
apps/gateway/src/chat/tools/validate-model-capabilities.ts New module containing extracted validation logic for JSON output, JSON schema, reasoning, tools, and web search capabilities
apps/gateway/src/chat/chat.ts Replaced inline validation code with function call to new validation module

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@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: 1

🤖 Fix all issues with AI agents
In `@apps/gateway/src/chat/tools/validate-model-capabilities.ts`:
- Around line 86-115: The reasoning check currently inspects all
modelInfo.providers regardless of requestedProvider, so if a specific provider
was requested you must filter providers the same way as other checks: when
requestedProvider is defined, filter modelInfo.providers to only those with
providerId === requestedProvider before computing supportsReasoning; then
compute supportsReasoning from that filtered list (use reasoning_effort,
requestedModel, requestedProvider, modelInfo.providers, and
ProviderModelMapping.reasoning as referenced) and keep the existing logging and
HTTPException behavior if none of the filtered providers support reasoning.
🧹 Nitpick comments (1)
apps/gateway/src/chat/tools/validate-model-capabilities.ts (1)

46-54: Consider removing redundant type casts.

The providers property on ModelDefinition is already typed as ProviderModelMapping[], so the (p as ProviderModelMapping) casts are redundant. While harmless, removing them would reduce noise.

♻️ Example simplification
 		const providersToCheck = requestedProvider
-			? modelInfo.providers.filter(
-					(p) => (p as ProviderModelMapping).providerId === requestedProvider,
-				)
+			? modelInfo.providers.filter((p) => p.providerId === requestedProvider)
 			: modelInfo.providers;
 
-		const supportsJsonOutput = providersToCheck.some(
-			(provider) => (provider as ProviderModelMapping).jsonOutput === true,
-		);
+		const supportsJsonOutput = providersToCheck.some((p) => p.jsonOutput === true);

Also applies to: 65-76, 124-132

Comment on lines +86 to +115
// Check if reasoning_effort is specified but model doesn't support reasoning
// Skip this check for "auto" and "custom" models as they will be resolved dynamically
if (
reasoning_effort !== undefined &&
requestedModel !== "auto" &&
requestedModel !== "custom"
) {
const supportsReasoning = modelInfo.providers.some(
(provider) => (provider as ProviderModelMapping).reasoning === true,
);

if (!supportsReasoning) {
logger.error(
`Reasoning effort specified for non-reasoning model: ${requestedModel}`,
{
requestedModel,
requestedProvider,
reasoning_effort,
modelProviders: modelInfo.providers.map((p) => ({
providerId: p.providerId,
reasoning: (p as ProviderModelMapping).reasoning,
})),
},
);

throw new HTTPException(400, {
message: `Model ${requestedModel} does not support reasoning. Remove the reasoning_effort parameter or use a reasoning-capable model.`,
});
}
}

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.

⚠️ Potential issue | 🟡 Minor

Inconsistent provider filtering in reasoning check.

The JSON object check (lines 46-50), JSON schema check (lines 65-69), and tools check (lines 124-128) all filter providers by requestedProvider when specified. However, the reasoning check queries all providers in modelInfo.providers without respecting requestedProvider.

If a user requests a specific provider that doesn't support reasoning while other providers for the same model do, this check would incorrectly pass.

🔧 Proposed fix to add provider filtering for reasoning check
 	if (
 		reasoning_effort !== undefined &&
 		requestedModel !== "auto" &&
 		requestedModel !== "custom"
 	) {
-		const supportsReasoning = modelInfo.providers.some(
+		const providersToCheck = requestedProvider
+			? modelInfo.providers.filter(
+					(p) => (p as ProviderModelMapping).providerId === requestedProvider,
+				)
+			: modelInfo.providers;
+
+		const supportsReasoning = providersToCheck.some(
 			(provider) => (provider as ProviderModelMapping).reasoning === true,
 		);
🤖 Prompt for AI Agents
In `@apps/gateway/src/chat/tools/validate-model-capabilities.ts` around lines 86 -
115, The reasoning check currently inspects all modelInfo.providers regardless
of requestedProvider, so if a specific provider was requested you must filter
providers the same way as other checks: when requestedProvider is defined,
filter modelInfo.providers to only those with providerId === requestedProvider
before computing supportsReasoning; then compute supportsReasoning from that
filtered list (use reasoning_effort, requestedModel, requestedProvider,
modelInfo.providers, and ProviderModelMapping.reasoning as referenced) and keep
the existing logging and HTTPException behavior if none of the filtered
providers support reasoning.

@steebchen
steebchen merged commit 9ecb01e into main Feb 2, 2026
13 of 14 checks passed
@steebchen
steebchen deleted the steebchen/validate-model-caps branch February 2, 2026 18:38
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