fix(delegate-task): trust user-configured category models without fuzzy validation (fixes #2740)
This commit is contained in:
@@ -2,6 +2,7 @@ import type { CategoryConfig, CategoriesConfig } from "../../config/schema"
|
|||||||
import { DEFAULT_CATEGORIES, CATEGORY_PROMPT_APPENDS } from "./constants"
|
import { DEFAULT_CATEGORIES, CATEGORY_PROMPT_APPENDS } from "./constants"
|
||||||
import { resolveModel } from "../../shared/model-resolver"
|
import { resolveModel } from "../../shared/model-resolver"
|
||||||
import { isModelAvailable } from "../../shared/model-availability"
|
import { isModelAvailable } from "../../shared/model-availability"
|
||||||
|
import { normalizeModel } from "../../shared/model-normalization"
|
||||||
import { CATEGORY_MODEL_REQUIREMENTS } from "../../shared/model-requirements"
|
import { CATEGORY_MODEL_REQUIREMENTS } from "../../shared/model-requirements"
|
||||||
import { log } from "../../shared/logger"
|
import { log } from "../../shared/logger"
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ export interface ResolveCategoryConfigResult {
|
|||||||
config: CategoryConfig
|
config: CategoryConfig
|
||||||
promptAppend: string
|
promptAppend: string
|
||||||
model: string | undefined
|
model: string | undefined
|
||||||
|
isUserConfiguredModel: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,6 +58,7 @@ export function resolveCategoryConfig(
|
|||||||
inheritedModel: defaultConfig?.model, // Category's built-in model takes precedence over system default
|
inheritedModel: defaultConfig?.model, // Category's built-in model takes precedence over system default
|
||||||
systemDefault: systemDefaultModel,
|
systemDefault: systemDefaultModel,
|
||||||
})
|
})
|
||||||
|
const isUserConfiguredModel = normalizeModel(userConfig?.model) !== undefined
|
||||||
const config: CategoryConfig = {
|
const config: CategoryConfig = {
|
||||||
...defaultConfig,
|
...defaultConfig,
|
||||||
...userConfig,
|
...userConfig,
|
||||||
@@ -70,5 +73,5 @@ export function resolveCategoryConfig(
|
|||||||
: userConfig.prompt_append
|
: userConfig.prompt_append
|
||||||
}
|
}
|
||||||
|
|
||||||
return { config, promptAppend, model }
|
return { config, promptAppend, model, isUserConfiguredModel }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ Available categories: ${allCategoryNames}`,
|
|||||||
userModel: explicitCategoryModel ?? overrideModel,
|
userModel: explicitCategoryModel ?? overrideModel,
|
||||||
userFallbackModels: normalizedConfiguredFallbackModels,
|
userFallbackModels: normalizedConfiguredFallbackModels,
|
||||||
categoryDefaultModel: resolved.model,
|
categoryDefaultModel: resolved.model,
|
||||||
|
isUserConfiguredCategoryModel: resolved.isUserConfiguredModel,
|
||||||
fallbackChain: requirement.fallbackChain,
|
fallbackChain: requirement.fallbackChain,
|
||||||
availableModels,
|
availableModels,
|
||||||
systemDefaultModel,
|
systemDefaultModel,
|
||||||
|
|||||||
@@ -102,6 +102,16 @@ describe("resolveModelForDelegateTask", () => {
|
|||||||
|
|
||||||
expect(result).toEqual({ model: "anthropic/claude-sonnet-4-6" })
|
expect(result).toEqual({ model: "anthropic/claude-sonnet-4-6" })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("#then trusts user-configured category model without fuzzy validation", () => {
|
||||||
|
const result = resolveModelForDelegateTask({
|
||||||
|
categoryDefaultModel: "new-api-openai/gpt-5.4-high",
|
||||||
|
isUserConfiguredCategoryModel: true,
|
||||||
|
availableModels: new Set(["openai/gpt-5.4"]),
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual({ model: "new-api-openai/gpt-5.4-high" })
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("#when user fallback models include variant syntax", () => {
|
describe("#when user fallback models include variant syntax", () => {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ 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"
|
||||||
import { hasConnectedProvidersCache, hasProviderModelsCache } from "../../shared/connected-providers-cache"
|
import { hasConnectedProvidersCache, hasProviderModelsCache } from "../../shared/connected-providers-cache"
|
||||||
|
import { log } from "../../shared/logger"
|
||||||
import { parseModelString, parseVariantFromModelID } from "./model-string-parser"
|
import { parseModelString, parseVariantFromModelID } from "./model-string-parser"
|
||||||
|
|
||||||
function isExplicitHighModel(model: string): boolean {
|
function isExplicitHighModel(model: string): boolean {
|
||||||
@@ -48,6 +49,7 @@ export function resolveModelForDelegateTask(input: {
|
|||||||
userModel?: string
|
userModel?: string
|
||||||
userFallbackModels?: string[]
|
userFallbackModels?: string[]
|
||||||
categoryDefaultModel?: string
|
categoryDefaultModel?: string
|
||||||
|
isUserConfiguredCategoryModel?: boolean
|
||||||
fallbackChain?: FallbackEntry[]
|
fallbackChain?: FallbackEntry[]
|
||||||
availableModels: Set<string>
|
availableModels: Set<string>
|
||||||
systemDefaultModel?: string
|
systemDefaultModel?: string
|
||||||
@@ -67,6 +69,13 @@ export function resolveModelForDelegateTask(input: {
|
|||||||
const explicitHighBaseModel = categoryDefault ? getExplicitHighBaseModel(categoryDefault) : null
|
const explicitHighBaseModel = categoryDefault ? getExplicitHighBaseModel(categoryDefault) : null
|
||||||
const explicitHighModel = explicitHighBaseModel ? categoryDefault : undefined
|
const explicitHighModel = explicitHighBaseModel ? categoryDefault : undefined
|
||||||
if (categoryDefault) {
|
if (categoryDefault) {
|
||||||
|
if (input.isUserConfiguredCategoryModel) {
|
||||||
|
log("[resolveModelForDelegateTask] using user-configured category model (bypass validation)", {
|
||||||
|
categoryDefaultModel: categoryDefault,
|
||||||
|
})
|
||||||
|
return { model: categoryDefault }
|
||||||
|
}
|
||||||
|
|
||||||
if (input.availableModels.size === 0) {
|
if (input.availableModels.size === 0) {
|
||||||
return { model: categoryDefault }
|
return { model: categoryDefault }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user