🪆 fix: Allow Nested addParams in Config Schema (#12526)

* 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
This commit is contained in:
Danny Avila 2026-04-02 20:38:46 -04:00 committed by GitHub
parent 6ecd1b510f
commit ed02fe40e0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 199 additions and 3 deletions

1
.gitignore vendored
View file

@ -137,6 +137,7 @@ helm/**/.values.yaml
# AI Assistants
/.claude/
/.codex/
/.cursor/
/.copilot/
/.aider/

View file

@ -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);
});
});

View file

@ -115,13 +115,39 @@ export const modelConfigSchema = z
export type TAzureModelConfig = z.infer<typeof modelConfigSchema>;
const paramValueSchema: z.ZodType<unknown> = 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<Record<string, unknown>> = 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({