fix(cli): preserve installer fallback chains in generated config (#3144)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -98,6 +98,41 @@ describe("generateOmoConfig - model fallback system", () => {
|
||||
expect((result.agents as Record<string, { model: string }>)['multimodal-looker'].model).toBe("openai/gpt-5.4")
|
||||
})
|
||||
|
||||
test("adds fallback_models when multiple providers are available", () => {
|
||||
//#given
|
||||
const config: InstallConfig = {
|
||||
hasClaude: true,
|
||||
isMax20: false,
|
||||
hasOpenAI: true,
|
||||
hasGemini: false,
|
||||
hasCopilot: false,
|
||||
hasOpencodeZen: false,
|
||||
hasZaiCodingPlan: false,
|
||||
hasKimiForCoding: false,
|
||||
hasOpencodeGo: false,
|
||||
}
|
||||
|
||||
//#when
|
||||
const result = generateOmoConfig(config)
|
||||
const agents = result.agents as Record<string, {
|
||||
model: string
|
||||
variant?: string
|
||||
fallback_models?: Array<{ model: string; variant?: string }>
|
||||
}>
|
||||
const categories = result.categories as Record<string, {
|
||||
model: string
|
||||
variant?: string
|
||||
fallback_models?: Array<{ model: string; variant?: string }>
|
||||
}>
|
||||
|
||||
//#then
|
||||
expect(agents.sisyphus.model).toBe("anthropic/claude-opus-4-6")
|
||||
expect(agents.sisyphus.fallback_models?.map((entry) => entry.model)).toContain("openai/gpt-5.4")
|
||||
expect(agents.sisyphus.fallback_models?.find((entry) => entry.model === "openai/gpt-5.4")?.variant).toBe("medium")
|
||||
expect(categories.deep.model).toBe("openai/gpt-5.4")
|
||||
expect(categories.deep.fallback_models?.map((entry) => entry.model)).toContain("anthropic/claude-opus-4-6")
|
||||
})
|
||||
|
||||
test("uses haiku for explore when Claude max20", () => {
|
||||
//#given
|
||||
const config: InstallConfig = {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { FallbackModelObject } from "../config/schema/fallback-models"
|
||||
|
||||
export interface ProviderAvailability {
|
||||
native: {
|
||||
claude: boolean
|
||||
@@ -15,11 +17,13 @@ kimiForCoding: boolean
|
||||
export interface AgentConfig {
|
||||
model: string
|
||||
variant?: string
|
||||
fallback_models?: FallbackModelObject[]
|
||||
}
|
||||
|
||||
export interface CategoryConfig {
|
||||
model: string
|
||||
variant?: string
|
||||
fallback_models?: FallbackModelObject[]
|
||||
}
|
||||
|
||||
export interface GeneratedOmoConfig {
|
||||
|
||||
@@ -2,11 +2,13 @@ import {
|
||||
CLI_AGENT_MODEL_REQUIREMENTS,
|
||||
CLI_CATEGORY_MODEL_REQUIREMENTS,
|
||||
} from "./model-fallback-requirements"
|
||||
import type { FallbackModelObject } from "../config/schema/fallback-models"
|
||||
import type { FallbackEntry } from "../shared/model-requirements"
|
||||
import type { InstallConfig } from "./types"
|
||||
|
||||
import type { AgentConfig, CategoryConfig, GeneratedOmoConfig } from "./model-fallback-types"
|
||||
import { applyOpenAiOnlyModelCatalog, isOpenAiOnlyAvailability } from "./openai-only-model-catalog"
|
||||
import { toProviderAvailability } from "./provider-availability"
|
||||
import { isProviderAvailable, toProviderAvailability } from "./provider-availability"
|
||||
import {
|
||||
getSisyphusFallbackChain,
|
||||
isAnyFallbackEntryAvailable,
|
||||
@@ -14,6 +16,7 @@ import {
|
||||
isRequiredProviderAvailable,
|
||||
resolveModelFromChain,
|
||||
} from "./fallback-chain-resolution"
|
||||
import { transformModelForProvider } from "./provider-model-id-transform"
|
||||
|
||||
export type { GeneratedOmoConfig } from "./model-fallback-types"
|
||||
|
||||
@@ -22,6 +25,44 @@ const ZAI_MODEL = "zai-coding-plan/glm-4.7"
|
||||
const ULTIMATE_FALLBACK = "opencode/gpt-5-nano"
|
||||
const SCHEMA_URL = "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json"
|
||||
|
||||
function toFallbackModelObject(entry: FallbackEntry, provider: string): FallbackModelObject {
|
||||
return {
|
||||
model: `${provider}/${transformModelForProvider(provider, entry.model)}`,
|
||||
...(entry.variant ? { variant: entry.variant } : {}),
|
||||
...(entry.reasoningEffort ? { reasoningEffort: entry.reasoningEffort as FallbackModelObject["reasoningEffort"] } : {}),
|
||||
...(entry.temperature !== undefined ? { temperature: entry.temperature } : {}),
|
||||
...(entry.top_p !== undefined ? { top_p: entry.top_p } : {}),
|
||||
...(entry.maxTokens !== undefined ? { maxTokens: entry.maxTokens } : {}),
|
||||
...(entry.thinking ? { thinking: entry.thinking } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
function attachFallbackModels<T extends AgentConfig | CategoryConfig>(
|
||||
config: T,
|
||||
fallbackChain: FallbackEntry[],
|
||||
availability: ReturnType<typeof toProviderAvailability>,
|
||||
): T {
|
||||
const expandedFallbacks = fallbackChain.flatMap((entry) =>
|
||||
entry.providers
|
||||
.filter((provider) => isProviderAvailable(provider, availability))
|
||||
.map((provider) => toFallbackModelObject(entry, provider))
|
||||
)
|
||||
const primaryIndex = expandedFallbacks.findIndex((entry) => entry.model === config.model)
|
||||
if (primaryIndex === -1) {
|
||||
return config
|
||||
}
|
||||
|
||||
const fallbackModels = expandedFallbacks.slice(primaryIndex + 1)
|
||||
if (fallbackModels.length === 0) {
|
||||
return config
|
||||
}
|
||||
|
||||
return {
|
||||
...config,
|
||||
fallback_models: fallbackModels,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function generateModelConfig(config: InstallConfig): GeneratedOmoConfig {
|
||||
@@ -85,7 +126,8 @@ export function generateModelConfig(config: InstallConfig): GeneratedOmoConfig {
|
||||
const resolved = resolveModelFromChain(fallbackChain, avail)
|
||||
if (resolved) {
|
||||
const variant = resolved.variant ?? req.variant
|
||||
agents[role] = variant ? { model: resolved.model, variant } : { model: resolved.model }
|
||||
const agentConfig = variant ? { model: resolved.model, variant } : { model: resolved.model }
|
||||
agents[role] = attachFallbackModels(agentConfig, fallbackChain, avail)
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -100,7 +142,8 @@ export function generateModelConfig(config: InstallConfig): GeneratedOmoConfig {
|
||||
const resolved = resolveModelFromChain(req.fallbackChain, avail)
|
||||
if (resolved) {
|
||||
const variant = resolved.variant ?? req.variant
|
||||
agents[role] = variant ? { model: resolved.model, variant } : { model: resolved.model }
|
||||
const agentConfig = variant ? { model: resolved.model, variant } : { model: resolved.model }
|
||||
agents[role] = attachFallbackModels(agentConfig, req.fallbackChain, avail)
|
||||
} else {
|
||||
agents[role] = { model: ULTIMATE_FALLBACK }
|
||||
}
|
||||
@@ -123,7 +166,8 @@ export function generateModelConfig(config: InstallConfig): GeneratedOmoConfig {
|
||||
const resolved = resolveModelFromChain(fallbackChain, avail)
|
||||
if (resolved) {
|
||||
const variant = resolved.variant ?? req.variant
|
||||
categories[cat] = variant ? { model: resolved.model, variant } : { model: resolved.model }
|
||||
const categoryConfig = variant ? { model: resolved.model, variant } : { model: resolved.model }
|
||||
categories[cat] = attachFallbackModels(categoryConfig, fallbackChain, avail)
|
||||
} else {
|
||||
categories[cat] = { model: ULTIMATE_FALLBACK }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user