chore: Use primary agent's model as fallback for subagents (#304)
This commit is contained in:
@@ -68,3 +68,29 @@ export function getModelUpdateArgs(
|
||||
const modelInfo = getModelInfo(modelIdentifier);
|
||||
return modelInfo?.updateArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a model ID from the llm_config.model value
|
||||
* The llm_config.model is the model portion without the provider prefix
|
||||
* (e.g., "z-ai/glm-4.6:exacto" for handle "openrouter/z-ai/glm-4.6:exacto")
|
||||
*
|
||||
* Note: This may not distinguish between variants like gpt-5.2-medium vs gpt-5.2-high
|
||||
* since they share the same handle. For provider fallback, this is acceptable.
|
||||
*
|
||||
* @param llmConfigModel - The model value from agent.llm_config.model
|
||||
* @returns The model ID if found, null otherwise
|
||||
*/
|
||||
export function resolveModelByLlmConfig(llmConfigModel: string): string | null {
|
||||
// Try to find a model whose handle ends with the llm_config model value
|
||||
const match = models.find((m) => m.handle.endsWith(`/${llmConfigModel}`));
|
||||
if (match) return match.id;
|
||||
|
||||
// Also try exact match on the model portion (for simple cases like "gpt-5.2")
|
||||
const exactMatch = models.find((m) => {
|
||||
const parts = m.handle.split("/");
|
||||
return parts.slice(1).join("/") === llmConfigModel;
|
||||
});
|
||||
if (exactMatch) return exactMatch.id;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ import { permissionMode } from "../../permissions/mode";
|
||||
import { sessionPermissions } from "../../permissions/session";
|
||||
import { settingsManager } from "../../settings-manager";
|
||||
import { getErrorMessage } from "../../utils/error";
|
||||
import { getClient } from "../client";
|
||||
import { getCurrentAgentId } from "../context";
|
||||
import { resolveModelByLlmConfig } from "../model";
|
||||
import { getAllSubagentConfigs, type SubagentConfig } from ".";
|
||||
|
||||
// ============================================================================
|
||||
@@ -50,6 +53,36 @@ interface ExecutionState {
|
||||
// Helper Functions
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Get the primary agent's model ID
|
||||
* Fetches from API and resolves to a known model ID
|
||||
*/
|
||||
async function getPrimaryAgentModel(): Promise<string | null> {
|
||||
try {
|
||||
const agentId = getCurrentAgentId();
|
||||
const client = await getClient();
|
||||
const agent = await client.agents.retrieve(agentId);
|
||||
const model = agent.llm_config?.model;
|
||||
if (model) {
|
||||
return resolveModelByLlmConfig(model);
|
||||
}
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an error message indicates an unsupported provider
|
||||
*/
|
||||
function isProviderNotSupportedError(errorOutput: string): boolean {
|
||||
return (
|
||||
errorOutput.includes("Provider") &&
|
||||
errorOutput.includes("is not supported") &&
|
||||
errorOutput.includes("supported providers:")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a tool call to the state store
|
||||
*/
|
||||
@@ -328,7 +361,11 @@ async function executeSubagent(
|
||||
userPrompt: string,
|
||||
baseURL: string,
|
||||
subagentId: string,
|
||||
isRetry = false,
|
||||
): Promise<SubagentResult> {
|
||||
// Update the state with the model being used (may differ on retry/fallback)
|
||||
updateSubagent(subagentId, { model });
|
||||
|
||||
try {
|
||||
const cliArgs = buildSubagentArgs(type, config, model, userPrompt);
|
||||
|
||||
@@ -376,6 +413,23 @@ async function executeSubagent(
|
||||
|
||||
// Handle non-zero exit code
|
||||
if (exitCode !== 0) {
|
||||
// Check if this is a provider-not-supported error and we haven't retried yet
|
||||
if (!isRetry && isProviderNotSupportedError(stderr)) {
|
||||
const primaryModel = await getPrimaryAgentModel();
|
||||
if (primaryModel) {
|
||||
// Retry with the primary agent's model
|
||||
return executeSubagent(
|
||||
type,
|
||||
config,
|
||||
primaryModel,
|
||||
userPrompt,
|
||||
baseURL,
|
||||
subagentId,
|
||||
true, // Mark as retry to prevent infinite loops
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
agentId: state.agentId || "",
|
||||
report: "",
|
||||
|
||||
@@ -88,13 +88,14 @@ const AgentRow = memo(({ agent, isLast, expanded }: AgentRowProps) => {
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
{/* Main row: tree char + description + type + stats */}
|
||||
{/* Main row: tree char + description + type + model + stats */}
|
||||
<Box flexDirection="row">
|
||||
<Text color={colors.subagent.treeChar}>{treeChar} </Text>
|
||||
{getDotElement()}
|
||||
<Text> {agent.description}</Text>
|
||||
<Text dimColor> · {agent.type.toLowerCase()}</Text>
|
||||
<Text color={colors.subagent.stats}> · {stats}</Text>
|
||||
{agent.model && <Text dimColor> · {agent.model}</Text>}
|
||||
<Text dimColor> · {stats}</Text>
|
||||
</Box>
|
||||
|
||||
{/* Subagent URL */}
|
||||
|
||||
@@ -31,6 +31,7 @@ export interface StaticSubagent {
|
||||
totalTokens: number;
|
||||
agentURL: string | null;
|
||||
error?: string;
|
||||
model?: string;
|
||||
}
|
||||
|
||||
interface SubagentGroupStaticProps {
|
||||
@@ -58,13 +59,14 @@ const AgentRow = memo(({ agent, isLast }: AgentRowProps) => {
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
{/* Main row: tree char + description + type + stats */}
|
||||
{/* Main row: tree char + description + type + model + stats */}
|
||||
<Box flexDirection="row">
|
||||
<Text color={colors.subagent.treeChar}>{treeChar} </Text>
|
||||
<Text color={dotColor}>●</Text>
|
||||
<Text> {agent.description}</Text>
|
||||
<Text dimColor> · {agent.type.toLowerCase()}</Text>
|
||||
<Text color={colors.subagent.stats}> · {stats}</Text>
|
||||
{agent.model && <Text dimColor> · {agent.model}</Text>}
|
||||
<Text dimColor> · {stats}</Text>
|
||||
</Box>
|
||||
|
||||
{/* Subagent URL */}
|
||||
|
||||
@@ -120,7 +120,6 @@ export const colors = {
|
||||
completed: brandColors.statusSuccess,
|
||||
error: brandColors.statusError,
|
||||
treeChar: brandColors.textDisabled,
|
||||
stats: brandColors.textSecondary,
|
||||
hint: brandColors.textDisabled,
|
||||
},
|
||||
|
||||
|
||||
@@ -108,6 +108,7 @@ export function createSubagentGroupItem(
|
||||
totalTokens: subagent.totalTokens,
|
||||
agentURL: subagent.agentURL,
|
||||
error: subagent.error,
|
||||
model: subagent.model,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ export interface SubagentState {
|
||||
totalTokens: number;
|
||||
durationMs: number;
|
||||
error?: string;
|
||||
model?: string;
|
||||
startTime: number;
|
||||
toolCallId?: string; // Links this subagent to its parent Task tool call
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user