feat(delegate-task): use explicit high variant for unspecified-high category

- Update DEFAULT_CATEGORIES to use 'openai/gpt-5.4-high' directly instead of separate model + variant
- Add helper functions (isExplicitHighModel, getExplicitHighBaseModel) to preserve explicit high models during fuzzy matching
- Update category resolver to avoid collapsing explicit high models to base model + variant pair
- Update tests to verify explicit high model handling in both background and sync modes
- Update documentation examples to reflect new configuration

🤖 Generated with OhMyOpenCode assistance
This commit is contained in:
YeonGyu-Kim
2026-03-07 16:35:39 +09:00
parent da663ce494
commit 77670cb46f
5 changed files with 44 additions and 13 deletions
+1 -1
View File
@@ -190,7 +190,7 @@ See the [Orchestration System Guide](./orchestration.md) for how agents dispatch
"categories": { "categories": {
"quick": { "model": "opencode/gpt-5-nano" }, "quick": { "model": "opencode/gpt-5-nano" },
"unspecified-low": { "model": "anthropic/claude-sonnet-4-6" }, "unspecified-low": { "model": "anthropic/claude-sonnet-4-6" },
"unspecified-high": { "model": "openai/gpt-5.4", "variant": "high" }, "unspecified-high": { "model": "openai/gpt-5.4-high" },
"visual-engineering": { "visual-engineering": {
"model": "google/gemini-3.1-pro", "model": "google/gemini-3.1-pro",
"variant": "high", "variant": "high",
+1 -1
View File
@@ -100,7 +100,7 @@ Here's a practical starting configuration:
"unspecified-low": { "model": "anthropic/claude-sonnet-4-6" }, "unspecified-low": { "model": "anthropic/claude-sonnet-4-6" },
// unspecified-high — complex work // unspecified-high — complex work
"unspecified-high": { "model": "openai/gpt-5.4", "variant": "high" }, "unspecified-high": { "model": "openai/gpt-5.4-high" },
// writing — docs/prose // writing — docs/prose
"writing": { "model": "google/gemini-3-flash" }, "writing": { "model": "google/gemini-3-flash" },
+1 -1
View File
@@ -289,7 +289,7 @@ export const DEFAULT_CATEGORIES: Record<string, CategoryConfig> = {
artistry: { model: "google/gemini-3.1-pro", variant: "high" }, artistry: { model: "google/gemini-3.1-pro", variant: "high" },
quick: { model: "anthropic/claude-haiku-4-5" }, quick: { model: "anthropic/claude-haiku-4-5" },
"unspecified-low": { model: "anthropic/claude-sonnet-4-6" }, "unspecified-low": { model: "anthropic/claude-sonnet-4-6" },
"unspecified-high": { model: "openai/gpt-5.4", variant: "high" }, "unspecified-high": { model: "openai/gpt-5.4-high" },
writing: { model: "kimi-for-coding/k2p5" }, writing: { model: "kimi-for-coding/k2p5" },
} }
@@ -3,6 +3,14 @@ import { normalizeModel } from "../../shared/model-normalization"
import { fuzzyMatchModel } from "../../shared/model-availability" import { fuzzyMatchModel } from "../../shared/model-availability"
import { transformModelForProvider } from "../../shared/provider-model-id-transform" import { transformModelForProvider } from "../../shared/provider-model-id-transform"
function isExplicitHighModel(model: string): boolean {
return /(?:^|\/)[^/]+-high$/.test(model)
}
function getExplicitHighBaseModel(model: string): string | null {
return isExplicitHighModel(model) ? model.replace(/-high$/, "") : null
}
export function resolveModelForDelegateTask(input: { export function resolveModelForDelegateTask(input: {
userModel?: string userModel?: string
@@ -17,6 +25,8 @@ export function resolveModelForDelegateTask(input: {
} }
const categoryDefault = normalizeModel(input.categoryDefaultModel) const categoryDefault = normalizeModel(input.categoryDefaultModel)
const explicitHighBaseModel = categoryDefault ? getExplicitHighBaseModel(categoryDefault) : null
const explicitHighModel = explicitHighBaseModel ? categoryDefault : undefined
if (categoryDefault) { if (categoryDefault) {
if (input.availableModels.size === 0) { if (input.availableModels.size === 0) {
return { model: categoryDefault } return { model: categoryDefault }
@@ -26,6 +36,10 @@ export function resolveModelForDelegateTask(input: {
const providerHint = parts.length >= 2 ? [parts[0]] : undefined const providerHint = parts.length >= 2 ? [parts[0]] : undefined
const match = fuzzyMatchModel(categoryDefault, input.availableModels, providerHint) const match = fuzzyMatchModel(categoryDefault, input.availableModels, providerHint)
if (match) { if (match) {
if (isExplicitHighModel(categoryDefault) && match !== categoryDefault) {
return { model: categoryDefault }
}
return { model: match } return { model: match }
} }
} }
@@ -45,12 +59,20 @@ export function resolveModelForDelegateTask(input: {
const fullModel = `${provider}/${entry.model}` const fullModel = `${provider}/${entry.model}`
const match = fuzzyMatchModel(fullModel, input.availableModels, [provider]) const match = fuzzyMatchModel(fullModel, input.availableModels, [provider])
if (match) { if (match) {
if (explicitHighModel && entry.variant === "high" && match === explicitHighBaseModel) {
return { model: explicitHighModel }
}
return { model: match, variant: entry.variant } return { model: match, variant: entry.variant }
} }
} }
const crossProviderMatch = fuzzyMatchModel(entry.model, input.availableModels) const crossProviderMatch = fuzzyMatchModel(entry.model, input.availableModels)
if (crossProviderMatch) { if (crossProviderMatch) {
if (explicitHighModel && entry.variant === "high" && crossProviderMatch === explicitHighBaseModel) {
return { model: explicitHighModel }
}
return { model: crossProviderMatch, variant: entry.variant } return { model: crossProviderMatch, variant: entry.variant }
} }
} }
+19 -10
View File
@@ -96,6 +96,16 @@ describe("sisyphus-task", () => {
expect(category.model).toBe("openai/gpt-5.3-codex") expect(category.model).toBe("openai/gpt-5.3-codex")
expect(category.variant).toBe("medium") expect(category.variant).toBe("medium")
}) })
test("unspecified-high category uses explicit high model", () => {
// given
const category = DEFAULT_CATEGORIES["unspecified-high"]
// when / #then
expect(category).toBeDefined()
expect(category.model).toBe("openai/gpt-5.4-high")
expect(category.variant).toBeUndefined()
})
}) })
describe("CATEGORY_PROMPT_APPENDS", () => { describe("CATEGORY_PROMPT_APPENDS", () => {
@@ -981,7 +991,7 @@ describe("sisyphus-task", () => {
}) })
}) })
test("DEFAULT_CATEGORIES variant passes to background WITHOUT userCategories", async () => { test("DEFAULT_CATEGORIES explicit high model passes to background WITHOUT userCategories", async () => {
// given - NO userCategories, testing DEFAULT_CATEGORIES only // given - NO userCategories, testing DEFAULT_CATEGORIES only
const { createDelegateTask } = require("./tools") const { createDelegateTask } = require("./tools")
let launchInput: any let launchInput: any
@@ -1026,7 +1036,7 @@ describe("sisyphus-task", () => {
abort: new AbortController().signal, abort: new AbortController().signal,
} }
// when - unspecified-high has variant: "max" in DEFAULT_CATEGORIES // when - unspecified-high uses the explicit high model in DEFAULT_CATEGORIES
await tool.execute( await tool.execute(
{ {
description: "Test unspecified-high default variant", description: "Test unspecified-high default variant",
@@ -1038,15 +1048,14 @@ describe("sisyphus-task", () => {
toolContext toolContext
) )
// then - variant MUST be "max" from DEFAULT_CATEGORIES // then - the explicit high model should be passed without a separate variant
expect(launchInput.model).toEqual({ expect(launchInput.model).toEqual({
providerID: "openai", providerID: "openai",
modelID: "gpt-5.4", modelID: "gpt-5.4-high",
variant: "high",
}) })
}, { timeout: 20000 }) }, { timeout: 20000 })
test("DEFAULT_CATEGORIES variant passes to sync session.prompt WITHOUT userCategories", async () => { test("DEFAULT_CATEGORIES explicit high model passes to sync session.prompt WITHOUT userCategories", async () => {
// given - NO userCategories, testing DEFAULT_CATEGORIES for sync mode // given - NO userCategories, testing DEFAULT_CATEGORIES for sync mode
const { createDelegateTask } = require("./tools") const { createDelegateTask } = require("./tools")
let promptBody: any let promptBody: any
@@ -1087,7 +1096,7 @@ describe("sisyphus-task", () => {
abort: new AbortController().signal, abort: new AbortController().signal,
} }
// when - unspecified-high has variant: "max" in DEFAULT_CATEGORIES // when - unspecified-high uses the explicit high model in DEFAULT_CATEGORIES
await tool.execute( await tool.execute(
{ {
description: "Test unspecified-high sync variant", description: "Test unspecified-high sync variant",
@@ -1099,12 +1108,12 @@ describe("sisyphus-task", () => {
toolContext toolContext
) )
// then - variant MUST be "max" from DEFAULT_CATEGORIES (passed as separate field) // then - the explicit high model should be passed without a separate variant
expect(promptBody.model).toEqual({ expect(promptBody.model).toEqual({
providerID: "openai", providerID: "openai",
modelID: "gpt-5.4", modelID: "gpt-5.4-high",
}) })
expect(promptBody.variant).toBe("high") expect(promptBody.variant).toBeUndefined()
}, { timeout: 20000 }) }, { timeout: 20000 })
}) })