From ed02fe40e07b5cbe86b935537bc7050222d93936 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Thu, 2 Apr 2026 20:38:46 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=AA=86=20fix:=20Allow=20Nested=20`addPara?= =?UTF-8?q?ms`=20in=20Config=20Schema=20(#12526)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: allow nested addParams in config schema * Respect no-op task constraint Constraint: Task 2 explicitly forbids code changes Directive: Keep this worker branch code-identical to the assigned base for this task Confidence: high Scope-risk: narrow Tested: git status --short (clean) * fix: align addParams web_search validation with runtime * test: cover addParams edge cases * chore: ignore .codex directory --- .gitignore | 1 + .../specs/config-schemas.spec.ts | 171 +++++++++++++++++- packages/data-provider/src/config.ts | 30 ++- 3 files changed, 199 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index e302d15a46..d775e70a26 100644 --- a/.gitignore +++ b/.gitignore @@ -137,6 +137,7 @@ helm/**/.values.yaml # AI Assistants /.claude/ +/.codex/ /.cursor/ /.copilot/ /.aider/ diff --git a/packages/data-provider/specs/config-schemas.spec.ts b/packages/data-provider/specs/config-schemas.spec.ts index fabd35cec9..66013baadd 100644 --- a/packages/data-provider/specs/config-schemas.spec.ts +++ b/packages/data-provider/specs/config-schemas.spec.ts @@ -1,8 +1,9 @@ import { - endpointSchema, paramDefinitionSchema, agentsEndpointSchema, azureEndpointSchema, + endpointSchema, + configSchema, } from '../src/config'; import { tModelSpecPresetSchema, EModelEndpoint } from '../src/schemas'; @@ -222,6 +223,109 @@ describe('endpointSchema deprecated fields', () => { }); }); +describe('endpointSchema addParams validation', () => { + const validEndpoint = { + name: 'CustomEndpoint', + apiKey: 'test-key', + baseURL: 'https://api.example.com', + models: { default: ['model-1'] }, + }; + const nestedAddParams = { + provider: { + only: ['z-ai'], + quantizations: ['int4'], + }, + }; + + it('accepts nested addParams objects and arrays', () => { + const result = endpointSchema.safeParse({ + ...validEndpoint, + addParams: nestedAddParams, + }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.addParams).toEqual(nestedAddParams); + } + }); + + it('keeps configSchema validation intact with nested custom addParams', () => { + const result = configSchema.safeParse({ + version: '1.0.0', + endpoints: { + custom: [ + { + ...validEndpoint, + addParams: nestedAddParams, + }, + ], + }, + }); + expect(result.success).toBe(true); + }); + + it('accepts boolean web_search in addParams', () => { + const result = endpointSchema.safeParse({ + ...validEndpoint, + addParams: { + provider: { + only: ['z-ai'], + }, + web_search: true, + }, + }); + expect(result.success).toBe(true); + }); + + it('accepts scalar addParams values', () => { + const result = endpointSchema.safeParse({ + ...validEndpoint, + addParams: { + model: 'custom-model', + retries: 2, + metadata: null, + }, + }); + expect(result.success).toBe(true); + }); + + it('rejects non-boolean web_search objects in addParams', () => { + const result = endpointSchema.safeParse({ + ...validEndpoint, + addParams: { + provider: { + only: ['z-ai'], + }, + web_search: { + enabled: true, + }, + }, + }); + expect(result.success).toBe(false); + }); + + it('rejects configSchema entries with non-boolean web_search objects in custom addParams', () => { + const result = configSchema.safeParse({ + version: '1.0.0', + endpoints: { + custom: [ + { + ...validEndpoint, + addParams: { + provider: { + only: ['z-ai'], + }, + web_search: { + enabled: true, + }, + }, + }, + ], + }, + }); + expect(result.success).toBe(false); + }); +}); + describe('agentsEndpointSchema', () => { it('does not accept baseURL', () => { const result = agentsEndpointSchema.safeParse({ @@ -251,4 +355,69 @@ describe('azureEndpointSchema', () => { expect(result.data).not.toHaveProperty('plugins'); } }); + + it('accepts nested addParams in azure groups', () => { + const result = azureEndpointSchema.safeParse({ + groups: [ + { + group: 'test-group', + apiKey: 'test-key', + models: { 'gpt-4': true }, + addParams: { + provider: { + only: ['z-ai'], + }, + }, + }, + ], + }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.groups[0].addParams).toEqual({ + provider: { + only: ['z-ai'], + }, + }); + } + }); + + it('accepts boolean web_search in azure addParams', () => { + const result = azureEndpointSchema.safeParse({ + groups: [ + { + group: 'test-group', + apiKey: 'test-key', + models: { 'gpt-4': true }, + addParams: { + provider: { + only: ['z-ai'], + }, + web_search: false, + }, + }, + ], + }); + expect(result.success).toBe(true); + }); + + it('rejects non-boolean web_search objects in azure addParams', () => { + const result = azureEndpointSchema.safeParse({ + groups: [ + { + group: 'test-group', + apiKey: 'test-key', + models: { 'gpt-4': true }, + addParams: { + provider: { + only: ['z-ai'], + }, + web_search: { + enabled: true, + }, + }, + }, + ], + }); + expect(result.success).toBe(false); + }); }); diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index ae3f5b9560..2b512a84d7 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -115,13 +115,39 @@ export const modelConfigSchema = z export type TAzureModelConfig = z.infer; +const paramValueSchema: z.ZodType = z.lazy(() => + z.union([ + z.string(), + z.number(), + z.boolean(), + z.null(), + z.array(paramValueSchema), + z.record(z.string(), paramValueSchema), + ]), +); + +/** Validates addParams while keeping web_search aligned with current runtime boolean handling. */ +const addParamsSchema: z.ZodType> = z + .record(z.string(), paramValueSchema) + .superRefine((params, ctx) => { + if (params.web_search === undefined || typeof params.web_search === 'boolean') { + return; + } + + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ['web_search'], + message: '`web_search` must be a boolean in addParams', + }); + }); + export const azureBaseSchema = z.object({ apiKey: z.string(), serverless: z.boolean().optional(), instanceName: z.string().optional(), deploymentName: z.string().optional(), assistants: z.boolean().optional(), - addParams: z.record(z.union([z.string(), z.number(), z.boolean(), z.null()])).optional(), + addParams: addParamsSchema.optional(), dropParams: z.array(z.string()).optional(), version: z.string().optional(), baseURL: z.string().optional(), @@ -361,7 +387,7 @@ export const endpointSchema = baseEndpointSchema.merge( iconURL: z.string().optional(), modelDisplayLabel: z.string().optional(), headers: z.record(z.string()).optional(), - addParams: z.record(z.union([z.string(), z.number(), z.boolean(), z.null()])).optional(), + addParams: addParamsSchema.optional(), dropParams: z.array(z.string()).optional(), customParams: z .object({