fix(runtime-fallback): address cubic AI review issues
- Add normalizeFallbackModels helper to centralize string/array normalization (P3) - Export RuntimeFallbackConfig and FallbackModels types from config/index.ts - Fix agent detection regex to use word boundaries for sessionID matching - Improve tests to verify actual fallback switching logic (not just log paths) - Add SessionCategoryRegistry cleanup in executeSyncTask on completion/error (P2) - All 24 runtime-fallback tests pass, 115 delegate-task tests pass
This commit is contained in:
@@ -32,4 +32,5 @@ export type {
|
|||||||
SisyphusConfig,
|
SisyphusConfig,
|
||||||
SisyphusTasksConfig,
|
SisyphusTasksConfig,
|
||||||
RuntimeFallbackConfig,
|
RuntimeFallbackConfig,
|
||||||
|
FallbackModels,
|
||||||
} from "./schema"
|
} from "./schema"
|
||||||
|
|||||||
@@ -522,9 +522,22 @@ describe("runtime-fallback", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("fallback models configuration", () => {
|
describe("fallback models configuration", () => {
|
||||||
|
function createMockPluginConfigWithAgentFallback(agentName: string, fallbackModels: string[]): OhMyOpenCodeConfig {
|
||||||
|
return {
|
||||||
|
agents: {
|
||||||
|
[agentName]: {
|
||||||
|
fallback_models: fallbackModels,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test("should use agent-level fallback_models", async () => {
|
test("should use agent-level fallback_models", async () => {
|
||||||
const input = createMockPluginInput()
|
const input = createMockPluginInput()
|
||||||
const hook = createRuntimeFallbackHook(input, { config: createMockConfig() })
|
const hook = createRuntimeFallbackHook(input, {
|
||||||
|
config: createMockConfig({ notify_on_fallback: false }),
|
||||||
|
pluginConfig: createMockPluginConfigWithAgentFallback("oracle", ["openai/gpt-5.2", "google/gemini-3-pro"]),
|
||||||
|
})
|
||||||
const sessionID = "test-agent-fallback"
|
const sessionID = "test-agent-fallback"
|
||||||
|
|
||||||
//#given - agent with custom fallback models
|
//#given - agent with custom fallback models
|
||||||
@@ -543,13 +556,17 @@ describe("runtime-fallback", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
//#then - should use oracle's fallback models
|
//#then - should prepare fallback to openai/gpt-5.2
|
||||||
const fallbackLog = logCalls.find((c) => c.msg.includes("No fallback models configured") || c.msg.includes("Fallback triggered"))
|
const fallbackLog = logCalls.find((c) => c.msg.includes("Preparing fallback"))
|
||||||
expect(fallbackLog).toBeDefined()
|
expect(fallbackLog).toBeDefined()
|
||||||
|
expect(fallbackLog?.data).toMatchObject({ from: "anthropic/claude-opus-4-5", to: "openai/gpt-5.2" })
|
||||||
})
|
})
|
||||||
|
|
||||||
test("should detect agent from sessionID pattern", async () => {
|
test("should detect agent from sessionID pattern", async () => {
|
||||||
const hook = createRuntimeFallbackHook(createMockPluginInput(), { config: createMockConfig() })
|
const hook = createRuntimeFallbackHook(createMockPluginInput(), {
|
||||||
|
config: createMockConfig({ notify_on_fallback: false }),
|
||||||
|
pluginConfig: createMockPluginConfigWithAgentFallback("sisyphus", ["openai/gpt-5.2"]),
|
||||||
|
})
|
||||||
const sessionID = "sisyphus-session-123"
|
const sessionID = "sisyphus-session-123"
|
||||||
|
|
||||||
await hook.event({
|
await hook.event({
|
||||||
@@ -566,8 +583,10 @@ describe("runtime-fallback", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const errorLog = logCalls.find((c) => c.msg.includes("session.error received"))
|
//#then - should detect sisyphus from sessionID and use its fallback
|
||||||
expect(errorLog?.data).toMatchObject({ sessionID })
|
const fallbackLog = logCalls.find((c) => c.msg.includes("Preparing fallback"))
|
||||||
|
expect(fallbackLog).toBeDefined()
|
||||||
|
expect(fallbackLog?.data).toMatchObject({ to: "openai/gpt-5.2" })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -115,7 +115,26 @@ function getFallbackModelsForSession(
|
|||||||
if (result) return result
|
if (result) return result
|
||||||
}
|
}
|
||||||
|
|
||||||
const sessionAgentMatch = sessionID.match(/\b(sisyphus|oracle|librarian|explore|prometheus|atlas|metis|momus|hephaestus|sisyphus-junior|build|plan|multimodal-looker)\b/i)
|
const AGENT_NAMES = [
|
||||||
|
"sisyphus",
|
||||||
|
"oracle",
|
||||||
|
"librarian",
|
||||||
|
"explore",
|
||||||
|
"prometheus",
|
||||||
|
"atlas",
|
||||||
|
"metis",
|
||||||
|
"momus",
|
||||||
|
"hephaestus",
|
||||||
|
"sisyphus-junior",
|
||||||
|
"build",
|
||||||
|
"plan",
|
||||||
|
"multimodal-looker",
|
||||||
|
]
|
||||||
|
const agentPattern = new RegExp(
|
||||||
|
`(?:^|[^a-zA-Z0-9_-])(${AGENT_NAMES.map((a) => a.replace(/-/g, "\\-")).join("|")})(?:$|[^a-zA-Z0-9_-])`,
|
||||||
|
"i",
|
||||||
|
)
|
||||||
|
const sessionAgentMatch = sessionID.match(agentPattern)
|
||||||
if (sessionAgentMatch) {
|
if (sessionAgentMatch) {
|
||||||
const detectedAgent = sessionAgentMatch[1].toLowerCase()
|
const detectedAgent = sessionAgentMatch[1].toLowerCase()
|
||||||
const result = tryGetFallbackFromAgent(detectedAgent)
|
const result = tryGetFallbackFromAgent(detectedAgent)
|
||||||
|
|||||||
@@ -73,3 +73,13 @@ export function resolveModelWithFallback(
|
|||||||
variant: resolved.variant,
|
variant: resolved.variant,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes fallback_models config (which can be string or string[]) to string[]
|
||||||
|
* Centralized helper to avoid duplicated normalization logic
|
||||||
|
*/
|
||||||
|
export function normalizeFallbackModels(models: string | string[] | undefined): string[] | undefined {
|
||||||
|
if (!models) return undefined
|
||||||
|
if (typeof models === "string") return [models]
|
||||||
|
return models
|
||||||
|
}
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ session_id: ${sessionID}
|
|||||||
} finally {
|
} finally {
|
||||||
if (syncSessionID) {
|
if (syncSessionID) {
|
||||||
subagentSessions.delete(syncSessionID)
|
subagentSessions.delete(syncSessionID)
|
||||||
|
SessionCategoryRegistry.remove(syncSessionID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user