chore: Use primary agent's model as fallback for subagents (#304)

This commit is contained in:
Devansh Jain
2025-12-18 15:32:54 -08:00
committed by GitHub
parent 26f2e5d305
commit 2a1f117502
7 changed files with 89 additions and 5 deletions

View File

@@ -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;
}

View File

@@ -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: "",

View File

@@ -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 */}

View File

@@ -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 */}

View File

@@ -120,7 +120,6 @@ export const colors = {
completed: brandColors.statusSuccess,
error: brandColors.statusError,
treeChar: brandColors.textDisabled,
stats: brandColors.textSecondary,
hint: brandColors.textDisabled,
},

View File

@@ -108,6 +108,7 @@ export function createSubagentGroupItem(
totalTokens: subagent.totalTokens,
agentURL: subagent.agentURL,
error: subagent.error,
model: subagent.model,
});
}
}

View File

@@ -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
}