Normalize default thinking options (#1028)

This commit is contained in:
Mohamed Boudra
2026-05-15 00:11:56 +08:00
committed by GitHub
parent 5a3fc96b76
commit f27d39fb63
5 changed files with 105 additions and 24 deletions
@@ -71,6 +71,15 @@ export interface AgentSelectOption {
metadata?: AgentMetadata; metadata?: AgentMetadata;
} }
export function normalizeAgentModelDefinition(model: AgentModelDefinition): AgentModelDefinition {
const defaultThinkingOptionId =
model.defaultThinkingOptionId ?? model.thinkingOptions?.find((option) => option.isDefault)?.id;
if (!defaultThinkingOptionId || defaultThinkingOptionId === model.defaultThinkingOptionId) {
return model;
}
return { ...model, defaultThinkingOptionId };
}
export interface ProviderSnapshotEntry { export interface ProviderSnapshotEntry {
provider: AgentProvider; provider: AgentProvider;
status: ProviderStatus; status: ProviderStatus;
@@ -755,6 +755,45 @@ describe("model merging", () => {
]); ]);
}); });
test("profile thinking option default is normalized onto the model", async () => {
const registry = buildProviderRegistry(logger, {
providerOverrides: {
codex: {
models: [
{
id: "profile-default",
label: "Profile Default",
isDefault: true,
thinkingOptions: [
{ id: "off", label: "Off" },
{ id: "max", label: "Max", isDefault: true },
],
},
],
},
},
});
const models = await registry.codex.fetchModels({
cwd: "/tmp/registry-models",
force: false,
});
expect(models).toEqual([
{
provider: "codex",
id: "profile-default",
label: "Profile Default",
isDefault: true,
thinkingOptions: [
{ id: "off", label: "Off" },
{ id: "max", label: "Max", isDefault: true },
],
defaultThinkingOptionId: "max",
},
]);
});
test("additional models append to runtime models", async () => { test("additional models append to runtime models", async () => {
mockState.runtimeModels.set("claude", [ mockState.runtimeModels.set("claude", [
{ {
@@ -14,6 +14,7 @@ import type {
ListPersistedAgentsOptions, ListPersistedAgentsOptions,
PersistedAgentDescriptor, PersistedAgentDescriptor,
} from "./agent-sdk-types.js"; } from "./agent-sdk-types.js";
import { normalizeAgentModelDefinition } from "./agent-sdk-types.js";
import type { WorkspaceGitService } from "../workspace-git-service.js"; import type { WorkspaceGitService } from "../workspace-git-service.js";
import type { import type {
AgentProviderRuntimeSettingsMap, AgentProviderRuntimeSettingsMap,
@@ -263,11 +264,11 @@ function mapPersistedAgentDescriptor(
}; };
} }
function mapModel(provider: AgentProvider, model: AgentModelDefinition): AgentModelDefinition { function mapModel(
return { provider: AgentProvider,
...model, model: AgentModelDefinition | ProviderProfileModel,
provider, ): AgentModelDefinition {
}; return normalizeAgentModelDefinition({ ...model, provider });
} }
function mergeModels( function mergeModels(
@@ -281,10 +282,7 @@ function mergeModels(
if (profileModels.length > 0 && options?.profileModelsAreAdditive !== true) { if (profileModels.length > 0 && options?.profileModelsAreAdditive !== true) {
return mergeModelAdditions( return mergeModelAdditions(
provider, provider,
profileModels.map((model) => ({ profileModels.map((model) => mapModel(provider, model)),
...model,
provider,
})),
additionalModels, additionalModels,
); );
} }
@@ -305,10 +303,7 @@ function mergeModelAdditions(
let hasAdditionalDefault = false; let hasAdditionalDefault = false;
for (const model of modelAdditions) { for (const model of modelAdditions) {
const additionalModel = { const additionalModel = mapModel(provider, model);
...model,
provider,
};
hasAdditionalDefault ||= additionalModel.isDefault === true; hasAdditionalDefault ||= additionalModel.isDefault === true;
const existingIndex = mergedModels.findIndex((candidate) => candidate.id === model.id); const existingIndex = mergedModels.findIndex((candidate) => candidate.id === model.id);
@@ -38,6 +38,39 @@ describe("provider snapshot message schemas", () => {
expect(parsed.enabled).toBe(true); expect(parsed.enabled).toBe(true);
}); });
test("normalizes thinking option defaults on provider snapshot models", () => {
const parsed = ProviderSnapshotEntrySchema.parse({
provider: "claude",
status: "ready",
models: [
{
provider: "claude",
id: "MiniMax-M2.7",
label: "MiniMax-M2.7",
isDefault: true,
thinkingOptions: [
{ id: "off", label: "Off" },
{ id: "max", label: "Max", isDefault: true },
],
},
],
});
expect(parsed.models).toEqual([
{
provider: "claude",
id: "MiniMax-M2.7",
label: "MiniMax-M2.7",
isDefault: true,
thinkingOptions: [
{ id: "off", label: "Off" },
{ id: "max", label: "Max", isDefault: true },
],
defaultThinkingOptionId: "max",
},
]);
});
test("defaults missing enabled state in providers snapshot response entries", () => { test("defaults missing enabled state in providers snapshot response entries", () => {
const parsed = GetProvidersSnapshotResponseMessageSchema.parse({ const parsed = GetProvidersSnapshotResponseMessageSchema.parse({
type: "get_providers_snapshot_response", type: "get_providers_snapshot_response",
+16 -11
View File
@@ -3,7 +3,10 @@ import { CLIENT_CAPS } from "./client-capabilities.js";
import { AGENT_LIFECYCLE_STATUSES } from "./agent-lifecycle.js"; import { AGENT_LIFECYCLE_STATUSES } from "./agent-lifecycle.js";
import { MAX_EXPLICIT_AGENT_TITLE_CHARS } from "../server/agent/agent-title-limits.js"; import { MAX_EXPLICIT_AGENT_TITLE_CHARS } from "../server/agent/agent-title-limits.js";
import { AgentProviderSchema } from "../server/agent/provider-manifest.js"; import { AgentProviderSchema } from "../server/agent/provider-manifest.js";
import { TOOL_CALL_ICON_NAMES } from "../server/agent/agent-sdk-types.js"; import {
normalizeAgentModelDefinition,
TOOL_CALL_ICON_NAMES,
} from "../server/agent/agent-sdk-types.js";
import { import {
ChatCreateRequestSchema, ChatCreateRequestSchema,
ChatListRequestSchema, ChatListRequestSchema,
@@ -194,16 +197,18 @@ export const AgentFeatureSchema = z.discriminatedUnion("type", [
AgentFeatureSelectSchema, AgentFeatureSelectSchema,
]); ]);
const AgentModelDefinitionSchema: z.ZodType<AgentModelDefinition> = z.object({ const AgentModelDefinitionSchema: z.ZodType<AgentModelDefinition> = z
provider: AgentProviderSchema, .object({
id: z.string(), provider: AgentProviderSchema,
label: z.string(), id: z.string(),
description: z.string().optional(), label: z.string(),
isDefault: z.boolean().optional(), description: z.string().optional(),
metadata: z.record(z.unknown()).optional(), isDefault: z.boolean().optional(),
thinkingOptions: z.array(AgentSelectOptionSchema).optional(), metadata: z.record(z.unknown()).optional(),
defaultThinkingOptionId: z.string().optional(), thinkingOptions: z.array(AgentSelectOptionSchema).optional(),
}); defaultThinkingOptionId: z.string().optional(),
})
.transform(normalizeAgentModelDefinition);
export const ProviderSnapshotEntrySchema = z.object({ export const ProviderSnapshotEntrySchema = z.object({
provider: AgentProviderSchema, provider: AgentProviderSchema,