feat(config): support object-style fallback_models with per-model settings
Add support for object-style entries in fallback_models arrays, enabling per-model configuration of variant, reasoningEffort, temperature, top_p, maxTokens, and thinking settings. - Zod schema for FallbackModelObject with full validation - normalizeFallbackModels() and flattenToFallbackModelStrings() utilities - Provider-agnostic model resolution pipeline with fallback chain - Session prompt params state management - Fallback chain construction with prefix-match lookup - Integration across delegate-task, background-agent, and plugin layers
This commit is contained in:
@@ -7,14 +7,16 @@ import { SISYPHUS_JUNIOR_AGENT } from "./sisyphus-junior-agent"
|
||||
import { resolveCategoryConfig } from "./categories"
|
||||
import { parseModelString } from "./model-string-parser"
|
||||
import { CATEGORY_MODEL_REQUIREMENTS } from "../../shared/model-requirements"
|
||||
import { normalizeFallbackModels } from "../../shared/model-resolver"
|
||||
import { buildFallbackChainFromModels } from "../../shared/fallback-chain-from-models"
|
||||
import { normalizeFallbackModels, flattenToFallbackModelStrings } from "../../shared/model-resolver"
|
||||
import { buildFallbackChainFromModels, findMostSpecificFallbackEntry } from "../../shared/fallback-chain-from-models"
|
||||
import { getAvailableModelsForDelegateTask } from "./available-models"
|
||||
import { resolveModelForDelegateTask } from "./model-selection"
|
||||
|
||||
import type { DelegatedModelConfig } from "./types"
|
||||
|
||||
export interface CategoryResolutionResult {
|
||||
agentToUse: string
|
||||
categoryModel: { providerID: string; modelID: string; variant?: string } | undefined
|
||||
categoryModel: DelegatedModelConfig | undefined
|
||||
categoryPromptAppend: string | undefined
|
||||
maxPromptTokens?: number
|
||||
modelInfo: ModelFallbackInfo | undefined
|
||||
@@ -84,8 +86,9 @@ Available categories: ${allCategoryNames}`,
|
||||
const normalizedConfiguredFallbackModels = normalizeFallbackModels(resolved.config.fallback_models)
|
||||
let actualModel: string | undefined
|
||||
let modelInfo: ModelFallbackInfo | undefined
|
||||
let categoryModel: { providerID: string; modelID: string; variant?: string } | undefined
|
||||
let categoryModel: DelegatedModelConfig | undefined
|
||||
let isModelResolutionSkipped = false
|
||||
let fallbackEntry: FallbackEntry | undefined
|
||||
|
||||
const overrideModel = sisyphusJuniorModel
|
||||
const explicitCategoryModel = userCategories?.[args.category!]?.model
|
||||
@@ -108,7 +111,7 @@ Available categories: ${allCategoryNames}`,
|
||||
} else {
|
||||
const resolution = resolveModelForDelegateTask({
|
||||
userModel: explicitCategoryModel ?? overrideModel,
|
||||
userFallbackModels: normalizedConfiguredFallbackModels,
|
||||
userFallbackModels: flattenToFallbackModelStrings(normalizedConfiguredFallbackModels),
|
||||
categoryDefaultModel: resolved.model,
|
||||
isUserConfiguredCategoryModel: resolved.isUserConfiguredModel,
|
||||
fallbackChain: requirement.fallbackChain,
|
||||
@@ -119,7 +122,8 @@ Available categories: ${allCategoryNames}`,
|
||||
if (resolution && "skipped" in resolution) {
|
||||
isModelResolutionSkipped = true
|
||||
} else if (resolution) {
|
||||
const { model: resolvedModel, variant: resolvedVariant } = resolution
|
||||
const { model: resolvedModel, variant: resolvedVariant, fallbackEntry: resolvedFallbackEntry } = resolution
|
||||
fallbackEntry = resolvedFallbackEntry
|
||||
actualModel = resolvedModel
|
||||
|
||||
if (!parseModelString(actualModel)) {
|
||||
@@ -198,6 +202,26 @@ Available categories: ${categoryNames.join(", ")}`,
|
||||
defaultProviderID,
|
||||
)
|
||||
|
||||
// Apply per-model settings from the source that provided the match:
|
||||
// 1. fallbackEntry from resolver (built-in chain match) — exact, no lookup needed
|
||||
// 2. configuredFallbackChain (user's fallback_models) — prefix match against user config
|
||||
const effectiveEntry = fallbackEntry
|
||||
?? (categoryModel && configuredFallbackChain
|
||||
? findMostSpecificFallbackEntry(categoryModel.providerID, categoryModel.modelID, configuredFallbackChain)
|
||||
: undefined)
|
||||
|
||||
if (categoryModel && effectiveEntry) {
|
||||
categoryModel = {
|
||||
...categoryModel,
|
||||
variant: userCategories?.[args.category!]?.variant ?? effectiveEntry.variant ?? categoryModel.variant,
|
||||
reasoningEffort: effectiveEntry.reasoningEffort,
|
||||
temperature: effectiveEntry.temperature,
|
||||
top_p: effectiveEntry.top_p,
|
||||
maxTokens: effectiveEntry.maxTokens,
|
||||
thinking: effectiveEntry.thinking,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
agentToUse: SISYPHUS_JUNIOR_AGENT,
|
||||
categoryModel,
|
||||
|
||||
Reference in New Issue
Block a user