fix: respect agent model selection in heartbeat and manual runs
Heartbeat read non-existent split modelProvider/modelId fields while the dashboard saves runtimeConfig.model as combined "provider/modelId", so sessions fell through to pi's default model and failed with "No API key for provider: openai-codex". Add extractRuntimeModel helper that prefers the combined string and use it from heartbeat. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
5
.changeset/heartbeat-agent-model.md
Normal file
5
.changeset/heartbeat-agent-model.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@fusion/engine": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix heartbeat and manual agent runs ignoring the agent's configured model. The dashboard saves `runtimeConfig.model` as a combined `"provider/modelId"` string, but heartbeat was reading non-existent split `modelProvider`/`modelId` fields, causing sessions to fall through to pi's default model (often `openai-codex`) and fail with "No API key for provider: openai-codex".
|
||||||
@@ -1344,7 +1344,7 @@ export class HeartbeatMonitor {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const { createResolvedAgentSession, extractRuntimeHint } = await import("./agent-session-helpers.js");
|
const { createResolvedAgentSession, extractRuntimeHint, extractRuntimeModel } = await import("./agent-session-helpers.js");
|
||||||
const { buildSessionSkillContextSync } = await import("./session-skill-context.js");
|
const { buildSessionSkillContextSync } = await import("./session-skill-context.js");
|
||||||
|
|
||||||
// Build tools with task creation tracking and run context for mutation correlation
|
// Build tools with task creation tracking and run context for mutation correlation
|
||||||
@@ -1449,8 +1449,10 @@ export class HeartbeatMonitor {
|
|||||||
systemPrompt,
|
systemPrompt,
|
||||||
tools: "readonly",
|
tools: "readonly",
|
||||||
customTools: heartbeatTools,
|
customTools: heartbeatTools,
|
||||||
defaultProvider: agent.runtimeConfig?.modelProvider as string | undefined,
|
...(() => {
|
||||||
defaultModelId: agent.runtimeConfig?.modelId as string | undefined,
|
const { provider, modelId } = extractRuntimeModel(agent.runtimeConfig);
|
||||||
|
return { defaultProvider: provider, defaultModelId: modelId };
|
||||||
|
})(),
|
||||||
onText: (delta) => {
|
onText: (delta) => {
|
||||||
outputLength += delta.length;
|
outputLength += delta.length;
|
||||||
appendStdoutExcerpt(delta);
|
appendStdoutExcerpt(delta);
|
||||||
|
|||||||
@@ -70,6 +70,37 @@ export function extractRuntimeHint(
|
|||||||
return normalizedHint.length > 0 ? normalizedHint : undefined;
|
return normalizedHint.length > 0 ? normalizedHint : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the model provider and id from an agent's runtimeConfig.
|
||||||
|
*
|
||||||
|
* The dashboard's NewAgentDialog stores the agent's selected model as a
|
||||||
|
* single combined string `runtimeConfig.model = "provider/modelId"` (see
|
||||||
|
* register-chat-routes.ts which parses the same shape). Older code paths
|
||||||
|
* also looked at separate `modelProvider` / `modelId` fields. This helper
|
||||||
|
* accepts either shape, preferring the combined `model` string.
|
||||||
|
*/
|
||||||
|
export function extractRuntimeModel(
|
||||||
|
runtimeConfig: Record<string, unknown> | undefined,
|
||||||
|
): { provider: string | undefined; modelId: string | undefined } {
|
||||||
|
const combined = typeof runtimeConfig?.model === "string" ? runtimeConfig.model.trim() : "";
|
||||||
|
if (combined) {
|
||||||
|
const slashIdx = combined.indexOf("/");
|
||||||
|
if (slashIdx > 0 && slashIdx < combined.length - 1) {
|
||||||
|
return {
|
||||||
|
provider: combined.slice(0, slashIdx).trim() || undefined,
|
||||||
|
modelId: combined.slice(slashIdx + 1).trim() || undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const provider = typeof runtimeConfig?.modelProvider === "string" ? runtimeConfig.modelProvider.trim() : "";
|
||||||
|
const modelId = typeof runtimeConfig?.modelId === "string" ? runtimeConfig.modelId.trim() : "";
|
||||||
|
return {
|
||||||
|
provider: provider || undefined,
|
||||||
|
modelId: modelId || undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an agent session using runtime resolution.
|
* Create an agent session using runtime resolution.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user