mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-29 20:20:57 +00:00
feat(skills): add model override support via skill frontmatter (#2949)
* feat(skills): add model override support via skill frontmatter Allow skills to specify a `model` field in YAML frontmatter to override which model is used for subsequent turns within the same agentic loop. The override flows through ToolResult → ToolCallResponseInfo → SendMessageOptions and naturally expires when the loop ends. Resolves #2052 * fix(core): only include modelOverride in response when defined Fixes strict equality test failures in nonInteractiveToolExecutor.test.ts where the extra undefined modelOverride field caused object mismatch. * fix(skills): fix model override pipeline issues - Wire up modelOverride in interactive CLI path (useGeminiStream) - Fix inherit/no-model unable to clear a prior override by using 'in' operator instead of truthiness checks in scheduler and CLI - Reject empty/whitespace model strings in parseModelField() - Extract shared parseModelField() to deduplicate skill-load and skill-manager parsing logic - Propagate modelOverride through stop-hook continuation in client * fix(skills): persist model override across turns in interactive and cron paths The interactive path stored the skill model override in a local variable, causing it to be lost when subsequent non-skill tool turns ran. Use a ref to persist the override for the duration of the agentic loop, resetting on new user messages. Also propagate modelOverride in the cron execution loop for consistency with the main non-interactive path. * fix(skills): preserve model override on retry and add unit tests Retry in interactive mode was clearing modelOverrideRef, causing the skill-selected model to silently fall back to session default. Guard the reset so retries preserve the active override. Add unit tests for parseModelField (edge cases, type validation) and modelOverride propagation through the skill tool result path.
This commit is contained in:
parent
189df1b098
commit
9a889dc614
12 changed files with 250 additions and 8 deletions
|
|
@ -237,6 +237,7 @@ export const useGeminiStream = (
|
|||
null,
|
||||
);
|
||||
const processedMemoryToolsRef = useRef<Set<string>>(new Set());
|
||||
const modelOverrideRef = useRef<string | undefined>(undefined);
|
||||
const {
|
||||
startNewPrompt,
|
||||
getPromptCount,
|
||||
|
|
@ -1255,6 +1256,11 @@ export const useGeminiStream = (
|
|||
!allowConcurrentBtwDuringResponse
|
||||
) {
|
||||
setModelSwitchedFromQuotaError(false);
|
||||
// Clear model override for new user turns, but preserve it on retry
|
||||
// so the same skill-selected model is used again.
|
||||
if (submitType !== SendMessageType.Retry) {
|
||||
modelOverrideRef.current = undefined;
|
||||
}
|
||||
// Commit any pending retry error to history (without hint) since the
|
||||
// user is starting a new conversation turn.
|
||||
// Clear both countdown-based errors AND static errors (those without
|
||||
|
|
@ -1354,7 +1360,7 @@ export const useGeminiStream = (
|
|||
finalQueryToSend,
|
||||
abortSignal,
|
||||
prompt_id!,
|
||||
{ type: submitType },
|
||||
{ type: submitType, modelOverride: modelOverrideRef.current },
|
||||
);
|
||||
|
||||
const processingStatus = await processGeminiStreamEvents(
|
||||
|
|
@ -1620,6 +1626,15 @@ export const useGeminiStream = (
|
|||
(toolCall) => toolCall.request.prompt_id,
|
||||
);
|
||||
|
||||
// Persist model override from skill tool results (last one wins).
|
||||
// Uses `in` so that undefined (from inherit/no-model skills) clears a
|
||||
// prior override, while non-skill tools (field absent) leave it intact.
|
||||
for (const toolCall of geminiTools) {
|
||||
if ('modelOverride' in toolCall.response) {
|
||||
modelOverrideRef.current = toolCall.response.modelOverride;
|
||||
}
|
||||
}
|
||||
|
||||
markToolsAsSubmitted(callIdsToMarkAsSubmitted);
|
||||
|
||||
// Don't continue if model was switched due to quota error
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue