* fix: expand override.category and explicit reasoningEffort priority (#1219) Two bugs fixed: 1. createBuiltinAgents(): override.category was never expanded into concrete config properties (model, variant, reasoningEffort, etc.). Added applyCategoryOverride() helper and applied it in the standard agent loop, Sisyphus path, and Atlas path. 2. Prometheus config-handler: reasoningEffort/textVerbosity/thinking from direct override now use explicit priority chains (direct > category) matching the existing variant pattern, instead of relying on spread ordering. Priority order (highest to lowest): 1. Direct override properties 2. Override category properties 3. Resolved variant from model fallback chain 4. Factory base defaults Closes #1219 * fix: use undefined check for thinking to allow explicit false
This commit is contained in:
@@ -280,3 +280,127 @@ describe("Prometheus category config resolution", () => {
|
||||
expect(config?.tools).toEqual({ tool1: true, tool2: false })
|
||||
})
|
||||
})
|
||||
|
||||
describe("Prometheus direct override priority over category", () => {
|
||||
test("direct reasoningEffort takes priority over category reasoningEffort", async () => {
|
||||
// #given - category has reasoningEffort=xhigh, direct override says "low"
|
||||
const pluginConfig: OhMyOpenCodeConfig = {
|
||||
sisyphus_agent: {
|
||||
planner_enabled: true,
|
||||
},
|
||||
categories: {
|
||||
"test-planning": {
|
||||
model: "openai/gpt-5.2",
|
||||
reasoningEffort: "xhigh",
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
prometheus: {
|
||||
category: "test-planning",
|
||||
reasoningEffort: "low",
|
||||
},
|
||||
},
|
||||
}
|
||||
const config: Record<string, unknown> = {
|
||||
model: "anthropic/claude-opus-4-5",
|
||||
agent: {},
|
||||
}
|
||||
const handler = createConfigHandler({
|
||||
ctx: { directory: "/tmp" },
|
||||
pluginConfig,
|
||||
modelCacheState: {
|
||||
anthropicContext1MEnabled: false,
|
||||
modelContextLimitsCache: new Map(),
|
||||
},
|
||||
})
|
||||
|
||||
// #when
|
||||
await handler(config)
|
||||
|
||||
// #then - direct override's reasoningEffort wins
|
||||
const agents = config.agent as Record<string, { reasoningEffort?: string }>
|
||||
expect(agents.prometheus).toBeDefined()
|
||||
expect(agents.prometheus.reasoningEffort).toBe("low")
|
||||
})
|
||||
|
||||
test("category reasoningEffort applied when no direct override", async () => {
|
||||
// #given - category has reasoningEffort but no direct override
|
||||
const pluginConfig: OhMyOpenCodeConfig = {
|
||||
sisyphus_agent: {
|
||||
planner_enabled: true,
|
||||
},
|
||||
categories: {
|
||||
"reasoning-cat": {
|
||||
model: "openai/gpt-5.2",
|
||||
reasoningEffort: "high",
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
prometheus: {
|
||||
category: "reasoning-cat",
|
||||
},
|
||||
},
|
||||
}
|
||||
const config: Record<string, unknown> = {
|
||||
model: "anthropic/claude-opus-4-5",
|
||||
agent: {},
|
||||
}
|
||||
const handler = createConfigHandler({
|
||||
ctx: { directory: "/tmp" },
|
||||
pluginConfig,
|
||||
modelCacheState: {
|
||||
anthropicContext1MEnabled: false,
|
||||
modelContextLimitsCache: new Map(),
|
||||
},
|
||||
})
|
||||
|
||||
// #when
|
||||
await handler(config)
|
||||
|
||||
// #then - category's reasoningEffort is applied
|
||||
const agents = config.agent as Record<string, { reasoningEffort?: string }>
|
||||
expect(agents.prometheus).toBeDefined()
|
||||
expect(agents.prometheus.reasoningEffort).toBe("high")
|
||||
})
|
||||
|
||||
test("direct temperature takes priority over category temperature", async () => {
|
||||
// #given
|
||||
const pluginConfig: OhMyOpenCodeConfig = {
|
||||
sisyphus_agent: {
|
||||
planner_enabled: true,
|
||||
},
|
||||
categories: {
|
||||
"temp-cat": {
|
||||
model: "openai/gpt-5.2",
|
||||
temperature: 0.8,
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
prometheus: {
|
||||
category: "temp-cat",
|
||||
temperature: 0.1,
|
||||
},
|
||||
},
|
||||
}
|
||||
const config: Record<string, unknown> = {
|
||||
model: "anthropic/claude-opus-4-5",
|
||||
agent: {},
|
||||
}
|
||||
const handler = createConfigHandler({
|
||||
ctx: { directory: "/tmp" },
|
||||
pluginConfig,
|
||||
modelCacheState: {
|
||||
anthropicContext1MEnabled: false,
|
||||
modelContextLimitsCache: new Map(),
|
||||
},
|
||||
})
|
||||
|
||||
// #when
|
||||
await handler(config)
|
||||
|
||||
// #then - direct temperature wins over category
|
||||
const agents = config.agent as Record<string, { temperature?: number }>
|
||||
expect(agents.prometheus).toBeDefined()
|
||||
expect(agents.prometheus.temperature).toBe(0.1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -227,7 +227,17 @@ export function createConfigHandler(deps: ConfigHandlerDeps) {
|
||||
);
|
||||
const prometheusOverride =
|
||||
pluginConfig.agents?.["prometheus"] as
|
||||
| (Record<string, unknown> & { category?: string; model?: string; variant?: string })
|
||||
| (Record<string, unknown> & {
|
||||
category?: string
|
||||
model?: string
|
||||
variant?: string
|
||||
reasoningEffort?: string
|
||||
textVerbosity?: string
|
||||
thinking?: { type: string; budgetTokens?: number }
|
||||
temperature?: number
|
||||
top_p?: number
|
||||
maxTokens?: number
|
||||
})
|
||||
| undefined;
|
||||
|
||||
const categoryConfig = prometheusOverride?.category
|
||||
@@ -248,12 +258,18 @@ export function createConfigHandler(deps: ConfigHandlerDeps) {
|
||||
userModel: prometheusOverride?.model ?? categoryConfig?.model,
|
||||
fallbackChain: prometheusRequirement?.fallbackChain,
|
||||
availableModels,
|
||||
systemDefaultModel: undefined, // let fallback chain handle this
|
||||
systemDefaultModel: undefined,
|
||||
});
|
||||
const resolvedModel = modelResolution?.model;
|
||||
const resolvedVariant = modelResolution?.variant;
|
||||
|
||||
const variantToUse = prometheusOverride?.variant ?? resolvedVariant;
|
||||
const reasoningEffortToUse = prometheusOverride?.reasoningEffort ?? categoryConfig?.reasoningEffort;
|
||||
const textVerbosityToUse = prometheusOverride?.textVerbosity ?? categoryConfig?.textVerbosity;
|
||||
const thinkingToUse = prometheusOverride?.thinking ?? categoryConfig?.thinking;
|
||||
const temperatureToUse = prometheusOverride?.temperature ?? categoryConfig?.temperature;
|
||||
const topPToUse = prometheusOverride?.top_p ?? categoryConfig?.top_p;
|
||||
const maxTokensToUse = prometheusOverride?.maxTokens ?? categoryConfig?.maxTokens;
|
||||
const prometheusBase = {
|
||||
name: "prometheus",
|
||||
...(resolvedModel ? { model: resolvedModel } : {}),
|
||||
@@ -263,22 +279,16 @@ export function createConfigHandler(deps: ConfigHandlerDeps) {
|
||||
permission: PROMETHEUS_PERMISSION,
|
||||
description: `${configAgent?.plan?.description ?? "Plan agent"} (Prometheus - OhMyOpenCode)`,
|
||||
color: (configAgent?.plan?.color as string) ?? "#FF6347",
|
||||
...(categoryConfig?.temperature !== undefined
|
||||
? { temperature: categoryConfig.temperature }
|
||||
: {}),
|
||||
...(categoryConfig?.top_p !== undefined
|
||||
? { top_p: categoryConfig.top_p }
|
||||
: {}),
|
||||
...(categoryConfig?.maxTokens !== undefined
|
||||
? { maxTokens: categoryConfig.maxTokens }
|
||||
: {}),
|
||||
...(temperatureToUse !== undefined ? { temperature: temperatureToUse } : {}),
|
||||
...(topPToUse !== undefined ? { top_p: topPToUse } : {}),
|
||||
...(maxTokensToUse !== undefined ? { maxTokens: maxTokensToUse } : {}),
|
||||
...(categoryConfig?.tools ? { tools: categoryConfig.tools } : {}),
|
||||
...(categoryConfig?.thinking ? { thinking: categoryConfig.thinking } : {}),
|
||||
...(categoryConfig?.reasoningEffort !== undefined
|
||||
? { reasoningEffort: categoryConfig.reasoningEffort }
|
||||
...(thinkingToUse ? { thinking: thinkingToUse } : {}),
|
||||
...(reasoningEffortToUse !== undefined
|
||||
? { reasoningEffort: reasoningEffortToUse }
|
||||
: {}),
|
||||
...(categoryConfig?.textVerbosity !== undefined
|
||||
? { textVerbosity: categoryConfig.textVerbosity }
|
||||
...(textVerbosityToUse !== undefined
|
||||
? { textVerbosity: textVerbosityToUse }
|
||||
: {}),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user