refactor: revamp ultrabrain category with deep work mindset

- Add variant: max to ultrabrain's gemini-3-pro fallback entry
- Rename STRATEGIC_CATEGORY_PROMPT_APPEND to ULTRABRAIN_CATEGORY_PROMPT_APPEND
- Keep original strategic advisor prompt content (no micromanagement instructions)
- Update description: use only for genuinely hard tasks, give clear goals only
- Update tests to match renamed constant
This commit is contained in:
justsisyphus
2026-01-30 14:05:47 +09:00
parent 6e9cb7ecd8
commit c06f38693e
6 changed files with 152 additions and 22 deletions

View File

@@ -90,7 +90,7 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
fallbackChain: [
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "xhigh" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "max" },
],
},
artistry: {

View File

@@ -626,6 +626,103 @@ describe("resolveModelWithFallback", () => {
})
})
describe("categoryDefaultModel (fuzzy matching for category defaults)", () => {
test("applies fuzzy matching to categoryDefaultModel when userModel not provided", () => {
// #given - gemini-3-pro is the category default, but only gemini-3-pro-preview is available
const input: ExtendedModelResolutionInput = {
categoryDefaultModel: "google/gemini-3-pro",
fallbackChain: [
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
],
availableModels: new Set(["google/gemini-3-pro-preview", "anthropic/claude-opus-4-5"]),
systemDefaultModel: "anthropic/claude-sonnet-4-5",
}
// #when
const result = resolveModelWithFallback(input)
// #then - should fuzzy match gemini-3-pro → gemini-3-pro-preview
expect(result!.model).toBe("google/gemini-3-pro-preview")
expect(result!.source).toBe("category-default")
})
test("categoryDefaultModel uses exact match when available", () => {
// #given - exact match exists
const input: ExtendedModelResolutionInput = {
categoryDefaultModel: "google/gemini-3-pro",
fallbackChain: [
{ providers: ["google"], model: "gemini-3-pro" },
],
availableModels: new Set(["google/gemini-3-pro", "google/gemini-3-pro-preview"]),
systemDefaultModel: "anthropic/claude-sonnet-4-5",
}
// #when
const result = resolveModelWithFallback(input)
// #then - should use exact match
expect(result!.model).toBe("google/gemini-3-pro")
expect(result!.source).toBe("category-default")
})
test("categoryDefaultModel falls through to fallbackChain when no match in availableModels", () => {
// #given - categoryDefaultModel has no match, but fallbackChain does
const input: ExtendedModelResolutionInput = {
categoryDefaultModel: "google/gemini-3-pro",
fallbackChain: [
{ providers: ["anthropic"], model: "claude-opus-4-5" },
],
availableModels: new Set(["anthropic/claude-opus-4-5"]),
systemDefaultModel: "system/default",
}
// #when
const result = resolveModelWithFallback(input)
// #then - should fall through to fallbackChain
expect(result!.model).toBe("anthropic/claude-opus-4-5")
expect(result!.source).toBe("provider-fallback")
})
test("userModel takes priority over categoryDefaultModel", () => {
// #given - both userModel and categoryDefaultModel provided
const input: ExtendedModelResolutionInput = {
userModel: "anthropic/claude-opus-4-5",
categoryDefaultModel: "google/gemini-3-pro",
fallbackChain: [
{ providers: ["google"], model: "gemini-3-pro" },
],
availableModels: new Set(["google/gemini-3-pro-preview", "anthropic/claude-opus-4-5"]),
systemDefaultModel: "system/default",
}
// #when
const result = resolveModelWithFallback(input)
// #then - userModel wins
expect(result!.model).toBe("anthropic/claude-opus-4-5")
expect(result!.source).toBe("override")
})
test("categoryDefaultModel works when availableModels is empty but connected provider exists", () => {
// #given - no availableModels but connected provider cache exists
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["google"])
const input: ExtendedModelResolutionInput = {
categoryDefaultModel: "google/gemini-3-pro",
availableModels: new Set(),
systemDefaultModel: "anthropic/claude-sonnet-4-5",
}
// #when
const result = resolveModelWithFallback(input)
// #then - should use categoryDefaultModel since google is connected
expect(result!.model).toBe("google/gemini-3-pro")
expect(result!.source).toBe("category-default")
cacheSpy.mockRestore()
})
})
describe("Optional systemDefaultModel", () => {
test("returns undefined when systemDefaultModel is undefined and no fallback found", () => {
// #given

View File

@@ -11,6 +11,7 @@ export type ModelResolutionInput = {
export type ModelSource =
| "override"
| "category-default"
| "provider-fallback"
| "system-default"
@@ -23,6 +24,7 @@ export type ModelResolutionResult = {
export type ExtendedModelResolutionInput = {
uiSelectedModel?: string
userModel?: string
categoryDefaultModel?: string
fallbackChain?: FallbackEntry[]
availableModels: Set<string>
systemDefaultModel?: string
@@ -44,7 +46,7 @@ export function resolveModel(input: ModelResolutionInput): string | undefined {
export function resolveModelWithFallback(
input: ExtendedModelResolutionInput,
): ModelResolutionResult | undefined {
const { uiSelectedModel, userModel, fallbackChain, availableModels, systemDefaultModel } = input
const { uiSelectedModel, userModel, categoryDefaultModel, fallbackChain, availableModels, systemDefaultModel } = input
// Step 1: UI Selection (highest priority - respects user's model choice in OpenCode UI)
const normalizedUiModel = normalizeModel(uiSelectedModel)
@@ -53,13 +55,42 @@ export function resolveModelWithFallback(
return { model: normalizedUiModel, source: "override" }
}
// Step 2: Config Override (from oh-my-opencode.json)
// Step 2: Config Override (from oh-my-opencode.json user config)
const normalizedUserModel = normalizeModel(userModel)
if (normalizedUserModel) {
log("Model resolved via config override", { model: normalizedUserModel })
return { model: normalizedUserModel, source: "override" }
}
// Step 2.5: Category Default Model (from DEFAULT_CATEGORIES, with fuzzy matching)
const normalizedCategoryDefault = normalizeModel(categoryDefaultModel)
if (normalizedCategoryDefault) {
if (availableModels.size > 0) {
const parts = normalizedCategoryDefault.split("/")
const providerHint = parts.length >= 2 ? [parts[0]] : undefined
const match = fuzzyMatchModel(normalizedCategoryDefault, availableModels, providerHint)
if (match) {
log("Model resolved via category default (fuzzy matched)", { original: normalizedCategoryDefault, matched: match })
return { model: match, source: "category-default" }
}
} else {
const connectedProviders = readConnectedProvidersCache()
if (connectedProviders === null) {
log("Model resolved via category default (no cache, first run)", { model: normalizedCategoryDefault })
return { model: normalizedCategoryDefault, source: "category-default" }
}
const parts = normalizedCategoryDefault.split("/")
if (parts.length >= 2) {
const provider = parts[0]
if (connectedProviders.includes(provider)) {
log("Model resolved via category default (connected provider)", { model: normalizedCategoryDefault })
return { model: normalizedCategoryDefault, source: "category-default" }
}
}
}
log("Category default model not available, falling through to fallback chain", { model: normalizedCategoryDefault })
}
// Step 3: Provider fallback chain (exact match → fuzzy match → next provider)
if (fallbackChain && fallbackChain.length > 0) {
if (availableModels.size === 0) {

View File

@@ -14,8 +14,8 @@ Design-first mindset:
AVOID: Generic fonts, purple gradients on white, predictable layouts, cookie-cutter patterns.
</Category_Context>`
export const STRATEGIC_CATEGORY_PROMPT_APPEND = `<Category_Context>
You are working on BUSINESS LOGIC / ARCHITECTURE tasks.
export const ULTRABRAIN_CATEGORY_PROMPT_APPEND = `<Category_Context>
You are working on DEEP LOGICAL REASONING / COMPLEX ARCHITECTURE tasks.
Strategic advisor mindset:
- Bias toward simplicity: least complex solution that fulfills requirements
@@ -167,7 +167,7 @@ export const DEFAULT_CATEGORIES: Record<string, CategoryConfig> = {
export const CATEGORY_PROMPT_APPENDS: Record<string, string> = {
"visual-engineering": VISUAL_CATEGORY_PROMPT_APPEND,
ultrabrain: STRATEGIC_CATEGORY_PROMPT_APPEND,
ultrabrain: ULTRABRAIN_CATEGORY_PROMPT_APPEND,
artistry: ARTISTRY_CATEGORY_PROMPT_APPEND,
quick: QUICK_CATEGORY_PROMPT_APPEND,
"unspecified-low": UNSPECIFIED_LOW_CATEGORY_PROMPT_APPEND,
@@ -177,7 +177,7 @@ export const CATEGORY_PROMPT_APPENDS: Record<string, string> = {
export const CATEGORY_DESCRIPTIONS: Record<string, string> = {
"visual-engineering": "Frontend, UI/UX, design, styling, animation",
ultrabrain: "Deep logical reasoning, complex architecture decisions requiring extensive analysis",
ultrabrain: "Use ONLY for genuinely hard, logic-heavy tasks. Give clear goals only, not step-by-step instructions.",
artistry: "Highly creative/artistic tasks, novel ideas",
quick: "Trivial tasks - single file changes, typo fixes, simple modifications",
"unspecified-low": "Tasks that don't fit other categories, low effort required",

View File

@@ -63,12 +63,12 @@ describe("sisyphus-task", () => {
expect(promptAppend).toContain("Design-first")
})
test("ultrabrain category has strategic prompt", () => {
test("ultrabrain category has deep logical reasoning prompt", () => {
// #given
const promptAppend = CATEGORY_PROMPT_APPENDS["ultrabrain"]
// #when / #then
expect(promptAppend).toContain("BUSINESS LOGIC")
expect(promptAppend).toContain("DEEP LOGICAL REASONING")
expect(promptAppend).toContain("Strategic advisor")
})
})

View File

@@ -541,7 +541,8 @@ To continue this session: session_id="${args.session_id}"`
}
} else {
const resolution = resolveModelWithFallback({
userModel: userCategories?.[args.category]?.model ?? resolved.model ?? sisyphusJuniorModel,
userModel: userCategories?.[args.category]?.model,
categoryDefaultModel: resolved.model ?? sisyphusJuniorModel,
fallbackChain: requirement.fallbackChain,
availableModels,
systemDefaultModel,
@@ -555,18 +556,19 @@ To continue this session: session_id="${args.session_id}"`
return `Invalid model format "${actualModel}". Expected "provider/model" format (e.g., "anthropic/claude-sonnet-4-5").`
}
let type: "user-defined" | "inherited" | "category-default" | "system-default"
switch (source) {
case "override":
type = "user-defined"
break
case "provider-fallback":
type = "category-default"
break
case "system-default":
type = "system-default"
break
}
let type: "user-defined" | "inherited" | "category-default" | "system-default"
switch (source) {
case "override":
type = "user-defined"
break
case "category-default":
case "provider-fallback":
type = "category-default"
break
case "system-default":
type = "system-default"
break
}
modelInfo = { model: actualModel, type, source }