Fix model fallback across main/background/sync agents
This commit is contained in:
@@ -217,6 +217,27 @@ describe("TaskToastManager", () => {
|
||||
expect(call.body.message).toContain("(inherited from parent)")
|
||||
})
|
||||
|
||||
test("should display warning when model is runtime fallback", () => {
|
||||
// given - runtime-fallback indicates a model swap mid-run
|
||||
const task = {
|
||||
id: "task_runtime",
|
||||
description: "Task with runtime fallback model",
|
||||
agent: "explore",
|
||||
isBackground: false,
|
||||
modelInfo: { model: "quotio/oswe-vscode-prime", type: "runtime-fallback" as const },
|
||||
}
|
||||
|
||||
// when - addTask is called
|
||||
toastManager.addTask(task)
|
||||
|
||||
// then - toast should show fallback warning
|
||||
expect(mockClient.tui.showToast).toHaveBeenCalled()
|
||||
const call = mockClient.tui.showToast.mock.calls[0][0]
|
||||
expect(call.body.message).toContain("[FALLBACK]")
|
||||
expect(call.body.message).toContain("quotio/oswe-vscode-prime")
|
||||
expect(call.body.message).toContain("(runtime fallback)")
|
||||
})
|
||||
|
||||
test("should not display model info when user-defined", () => {
|
||||
// given - a task with user-defined model
|
||||
const task = {
|
||||
@@ -257,4 +278,32 @@ describe("TaskToastManager", () => {
|
||||
expect(call.body.message).not.toContain("[FALLBACK] Model:")
|
||||
})
|
||||
})
|
||||
|
||||
describe("updateTaskModelBySession", () => {
|
||||
test("updates task model info and shows fallback toast", () => {
|
||||
// given - task without model info
|
||||
const task = {
|
||||
id: "task_update",
|
||||
sessionID: "ses_update_1",
|
||||
description: "Task that will fallback",
|
||||
agent: "explore",
|
||||
isBackground: false,
|
||||
}
|
||||
toastManager.addTask(task)
|
||||
mockClient.tui.showToast.mockClear()
|
||||
|
||||
// when - runtime fallback applied by session
|
||||
toastManager.updateTaskModelBySession("ses_update_1", {
|
||||
model: "nvidia/stepfun-ai/step-3.5-flash",
|
||||
type: "runtime-fallback",
|
||||
})
|
||||
|
||||
// then - new toast shows fallback model
|
||||
expect(mockClient.tui.showToast).toHaveBeenCalled()
|
||||
const call = mockClient.tui.showToast.mock.calls[0][0]
|
||||
expect(call.body.message).toContain("[FALLBACK]")
|
||||
expect(call.body.message).toContain("nvidia/stepfun-ai/step-3.5-flash")
|
||||
expect(call.body.message).toContain("(runtime fallback)")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -20,6 +20,7 @@ export class TaskToastManager {
|
||||
|
||||
addTask(task: {
|
||||
id: string
|
||||
sessionID?: string
|
||||
description: string
|
||||
agent: string
|
||||
isBackground: boolean
|
||||
@@ -30,6 +31,7 @@ export class TaskToastManager {
|
||||
}): void {
|
||||
const trackedTask: TrackedTask = {
|
||||
id: task.id,
|
||||
sessionID: task.sessionID,
|
||||
description: task.description,
|
||||
agent: task.agent,
|
||||
status: task.status ?? "running",
|
||||
@@ -54,6 +56,18 @@ export class TaskToastManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update model info for a task by session ID
|
||||
*/
|
||||
updateTaskModelBySession(sessionID: string, modelInfo: ModelFallbackInfo): void {
|
||||
if (!sessionID) return
|
||||
const task = Array.from(this.tasks.values()).find((t) => t.sessionID === sessionID)
|
||||
if (!task) return
|
||||
if (task.modelInfo?.model === modelInfo.model && task.modelInfo?.type === modelInfo.type) return
|
||||
task.modelInfo = modelInfo
|
||||
this.showTaskListToast(task)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove completed/error task
|
||||
*/
|
||||
@@ -110,14 +124,17 @@ export class TaskToastManager {
|
||||
const lines: string[] = []
|
||||
|
||||
const isFallback = newTask.modelInfo && (
|
||||
newTask.modelInfo.type === "inherited" || newTask.modelInfo.type === "system-default"
|
||||
newTask.modelInfo.type === "inherited" ||
|
||||
newTask.modelInfo.type === "system-default" ||
|
||||
newTask.modelInfo.type === "runtime-fallback"
|
||||
)
|
||||
if (isFallback) {
|
||||
const suffixMap: Record<"inherited" | "system-default", string> = {
|
||||
const suffixMap: Record<"inherited" | "system-default" | "runtime-fallback", string> = {
|
||||
inherited: " (inherited from parent)",
|
||||
"system-default": " (system default fallback)",
|
||||
"runtime-fallback": " (runtime fallback)",
|
||||
}
|
||||
const suffix = suffixMap[newTask.modelInfo!.type as "inherited" | "system-default"]
|
||||
const suffix = suffixMap[newTask.modelInfo!.type as "inherited" | "system-default" | "runtime-fallback"]
|
||||
lines.push(`[FALLBACK] Model: ${newTask.modelInfo!.model}${suffix}`)
|
||||
lines.push("")
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@ export type TaskStatus = "running" | "queued" | "completed" | "error"
|
||||
|
||||
export interface ModelFallbackInfo {
|
||||
model: string
|
||||
type: "user-defined" | "inherited" | "category-default" | "system-default"
|
||||
type: "user-defined" | "inherited" | "category-default" | "system-default" | "runtime-fallback"
|
||||
source?: ModelSource
|
||||
}
|
||||
|
||||
export interface TrackedTask {
|
||||
id: string
|
||||
sessionID?: string
|
||||
description: string
|
||||
agent: string
|
||||
status: TaskStatus
|
||||
|
||||
Reference in New Issue
Block a user