From 95931f5b665a997ebadb931afd4dd58d8e218334 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Wed, 5 Aug 2026 11:19:30 +0200 Subject: [PATCH 1/3] feat(ai-gateway): store model reasoning metadata --- packages/db/src/schema-types.test.ts | 44 +++++++++++++++++++++++++++- packages/db/src/schema-types.ts | 6 ++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/packages/db/src/schema-types.test.ts b/packages/db/src/schema-types.test.ts index 6ed85c58b0..2e74233548 100644 --- a/packages/db/src/schema-types.test.ts +++ b/packages/db/src/schema-types.test.ts @@ -1,5 +1,9 @@ import { describe, expect, it } from '@jest/globals'; -import { CodeReviewCouncilConfigSchema, OrganizationSettingsSchema } from './schema-types'; +import { + CodeReviewCouncilConfigSchema, + ModelsSchema, + OrganizationSettingsSchema, +} from './schema-types'; describe('CodeReviewCouncilConfigSchema', () => { const specialist = (id: string) => ({ @@ -66,3 +70,41 @@ describe('OrganizationSettingsSchema org_auto_model', () => { expect(result.success).toBe(false); }); }); + +describe('ModelsSchema', () => { + it('preserves only supported reasoning metadata', () => { + const result = ModelsSchema.parse({ + data: [ + { + id: 'openai/gpt-5.2', + name: 'OpenAI: GPT-5.2', + reasoning: { + mandatory: false, + supported_efforts: ['high', 'medium', 'low', 'none'], + default_enabled: true, + default_effort: 'medium', + }, + }, + ], + }); + + expect(result.data[0].reasoning).toEqual({ + mandatory: false, + supported_efforts: ['high', 'medium', 'low', 'none'], + }); + }); + + it('allows reasoning metadata without supported efforts', () => { + const result = ModelsSchema.parse({ + data: [ + { + id: 'google/gemini-2.5-pro', + name: 'Google: Gemini 2.5 Pro', + reasoning: { mandatory: true }, + }, + ], + }); + + expect(result.data[0].reasoning).toEqual({ mandatory: true }); + }); +}); diff --git a/packages/db/src/schema-types.ts b/packages/db/src/schema-types.ts index 3de62fd723..371ac85788 100644 --- a/packages/db/src/schema-types.ts +++ b/packages/db/src/schema-types.ts @@ -2025,6 +2025,12 @@ export const ModelSchema = z.object({ id: z.string(), name: z.string(), type: z.enum(['language', 'embedding', 'image']).optional().catch(undefined), + reasoning: z + .object({ + mandatory: z.boolean(), + supported_efforts: z.array(z.string()).optional(), + }) + .optional(), }); export const ModelsSchema = z.object({ data: z.array(ModelSchema) }); From 508a4aead540230e866d8e877e10e4378454654a Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Wed, 5 Aug 2026 11:22:28 +0200 Subject: [PATCH 2/3] feat(ai-gateway): sync model reasoning capabilities --- .../ai-gateway/providers/openrouter/sync-providers.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/web/src/lib/ai-gateway/providers/openrouter/sync-providers.ts b/apps/web/src/lib/ai-gateway/providers/openrouter/sync-providers.ts index f05f3bcddf..8a060b7faa 100644 --- a/apps/web/src/lib/ai-gateway/providers/openrouter/sync-providers.ts +++ b/apps/web/src/lib/ai-gateway/providers/openrouter/sync-providers.ts @@ -98,8 +98,17 @@ async function fetchGatewayModels(gateway: Provider) { ); } const endpoints = EndpointsSchema.parse(await endpointsResponse.json()); + const { reasoning, ...modelMetadata } = model; result[model.id] = { - ...model, + ...modelMetadata, + ...(reasoning && { + reasoning: { + mandatory: reasoning.mandatory, + ...(reasoning.supported_efforts && { + supported_efforts: reasoning.supported_efforts, + }), + }, + }), endpoints: endpoints.data.endpoints, }; }) From 92e2254ffd1ac4978d43e1fbc0486dbc5ee475a0 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Wed, 5 Aug 2026 11:26:22 +0200 Subject: [PATCH 3/3] fix(ai-gateway): validate synced reasoning efforts --- packages/db/src/schema-types.test.ts | 35 ++++++++++++++++++++++++++++ packages/db/src/schema-types.ts | 5 ++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/packages/db/src/schema-types.test.ts b/packages/db/src/schema-types.test.ts index 2e74233548..ab4dc4b010 100644 --- a/packages/db/src/schema-types.test.ts +++ b/packages/db/src/schema-types.test.ts @@ -3,7 +3,9 @@ import { CodeReviewCouncilConfigSchema, ModelsSchema, OrganizationSettingsSchema, + StoredModelSchema, } from './schema-types'; +import type { StoredModel } from './schema-types'; describe('CodeReviewCouncilConfigSchema', () => { const specialist = (id: string) => ({ @@ -107,4 +109,37 @@ describe('ModelsSchema', () => { expect(result.data[0].reasoning).toEqual({ mandatory: true }); }); + + it('drops invalid reasoning metadata', () => { + const result = ModelsSchema.parse({ + data: [ + { + id: 'example/future-effort', + name: 'Future Effort', + reasoning: { + mandatory: false, + supported_efforts: ['future'], + }, + }, + ], + }); + + expect(result.data[0].reasoning).toBeUndefined(); + }); +}); + +describe('StoredModelSchema', () => { + it('includes model reasoning metadata', () => { + const storedModel: StoredModel = { + id: 'openai/gpt-5.2', + name: 'OpenAI: GPT-5.2', + reasoning: { + mandatory: false, + supported_efforts: ['high', 'medium', 'low', 'none'], + }, + endpoints: [], + }; + + expect(StoredModelSchema.parse(storedModel).reasoning).toEqual(storedModel.reasoning); + }); }); diff --git a/packages/db/src/schema-types.ts b/packages/db/src/schema-types.ts index 371ac85788..af7ebfdc82 100644 --- a/packages/db/src/schema-types.ts +++ b/packages/db/src/schema-types.ts @@ -2028,9 +2028,10 @@ export const ModelSchema = z.object({ reasoning: z .object({ mandatory: z.boolean(), - supported_efforts: z.array(z.string()).optional(), + supported_efforts: z.array(ReasoningEffortSchema).optional(), }) - .optional(), + .optional() + .catch(undefined), }); export const ModelsSchema = z.object({ data: z.array(ModelSchema) });