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;
}
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 {
provider: AgentProvider;
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 () => {
mockState.runtimeModels.set("claude", [
{
@@ -14,6 +14,7 @@ import type {
ListPersistedAgentsOptions,
PersistedAgentDescriptor,
} from "./agent-sdk-types.js";
import { normalizeAgentModelDefinition } from "./agent-sdk-types.js";
import type { WorkspaceGitService } from "../workspace-git-service.js";
import type {
AgentProviderRuntimeSettingsMap,
@@ -263,11 +264,11 @@ function mapPersistedAgentDescriptor(
};
}
function mapModel(provider: AgentProvider, model: AgentModelDefinition): AgentModelDefinition {
return {
...model,
provider,
};
function mapModel(
provider: AgentProvider,
model: AgentModelDefinition | ProviderProfileModel,
): AgentModelDefinition {
return normalizeAgentModelDefinition({ ...model, provider });
}
function mergeModels(
@@ -281,10 +282,7 @@ function mergeModels(
if (profileModels.length > 0 && options?.profileModelsAreAdditive !== true) {
return mergeModelAdditions(
provider,
profileModels.map((model) => ({
...model,
provider,
})),
profileModels.map((model) => mapModel(provider, model)),
additionalModels,
);
}
@@ -305,10 +303,7 @@ function mergeModelAdditions(
let hasAdditionalDefault = false;
for (const model of modelAdditions) {
const additionalModel = {
...model,
provider,
};
const additionalModel = mapModel(provider, model);
hasAdditionalDefault ||= additionalModel.isDefault === true;
const existingIndex = mergedModels.findIndex((candidate) => candidate.id === model.id);
@@ -38,6 +38,39 @@ describe("provider snapshot message schemas", () => {
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", () => {
const parsed = GetProvidersSnapshotResponseMessageSchema.parse({
type: "get_providers_snapshot_response",
+8 -3
View File
@@ -3,7 +3,10 @@ import { CLIENT_CAPS } from "./client-capabilities.js";
import { AGENT_LIFECYCLE_STATUSES } from "./agent-lifecycle.js";
import { MAX_EXPLICIT_AGENT_TITLE_CHARS } from "../server/agent/agent-title-limits.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 {
ChatCreateRequestSchema,
ChatListRequestSchema,
@@ -194,7 +197,8 @@ export const AgentFeatureSchema = z.discriminatedUnion("type", [
AgentFeatureSelectSchema,
]);
const AgentModelDefinitionSchema: z.ZodType<AgentModelDefinition> = z.object({
const AgentModelDefinitionSchema: z.ZodType<AgentModelDefinition> = z
.object({
provider: AgentProviderSchema,
id: z.string(),
label: z.string(),
@@ -203,7 +207,8 @@ const AgentModelDefinitionSchema: z.ZodType<AgentModelDefinition> = z.object({
metadata: z.record(z.unknown()).optional(),
thinkingOptions: z.array(AgentSelectOptionSchema).optional(),
defaultThinkingOptionId: z.string().optional(),
});
})
.transform(normalizeAgentModelDefinition);
export const ProviderSnapshotEntrySchema = z.object({
provider: AgentProviderSchema,