mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-02 13:40:46 +00:00
feat(core): enhanced loop detection with stagnation + validation-retry checks (#3236)
This commit is contained in:
parent
28d5722955
commit
c175fd3d4a
9 changed files with 817 additions and 17 deletions
|
|
@ -20,6 +20,7 @@ import {
|
|||
promptIdContext,
|
||||
OutputFormat,
|
||||
InputFormat,
|
||||
LoopType,
|
||||
uiTelemetryService,
|
||||
parseAndFormatApiError,
|
||||
createDebugLogger,
|
||||
|
|
@ -51,6 +52,38 @@ import {
|
|||
computeUsageFromMetrics,
|
||||
} from './utils/nonInteractiveHelpers.js';
|
||||
|
||||
// Human-readable labels for the detectors that can fire mid-stream.
|
||||
// Surfaced to stderr in TEXT mode so a headless run that halts on a loop
|
||||
// doesn't exit with empty stdout and no explanation — see PR #3236 review.
|
||||
const LOOP_TYPE_LABELS: Record<LoopType, string> = {
|
||||
[LoopType.CONSECUTIVE_IDENTICAL_TOOL_CALLS]:
|
||||
'the model repeated the same tool call with identical arguments',
|
||||
[LoopType.CHANTING_IDENTICAL_SENTENCES]:
|
||||
'the model repeated the same sentence in its output',
|
||||
[LoopType.REPETITIVE_THOUGHTS]:
|
||||
'the model repeated the same reasoning thought',
|
||||
[LoopType.READ_FILE_LOOP]:
|
||||
'the model spent too many consecutive calls reading files without making progress',
|
||||
[LoopType.ACTION_STAGNATION]:
|
||||
'the model kept calling the same tool without making progress',
|
||||
};
|
||||
|
||||
function emitLoopDetectedMessage(
|
||||
config: Config,
|
||||
loopType: LoopType | undefined,
|
||||
): void {
|
||||
// In TEXT mode the adapter swallows LoopDetected, so we print here. In
|
||||
// JSON modes the adapter emits a structured result, which is enough.
|
||||
if (config.getOutputFormat() !== OutputFormat.TEXT) {
|
||||
return;
|
||||
}
|
||||
const reason = loopType ? LOOP_TYPE_LABELS[loopType] : undefined;
|
||||
const detail = reason ? ` (${loopType}: ${reason})` : '';
|
||||
process.stderr.write(
|
||||
`Loop detection halted the run${detail}. Set the \`model.skipLoopDetection\` setting to true to disable.\n`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a final message for slash command results.
|
||||
* Note: systemMessage should already be emitted before calling this function.
|
||||
|
|
@ -340,6 +373,9 @@ export async function runNonInteractive(
|
|||
if (event.type === GeminiEventType.ToolCallRequest) {
|
||||
toolCallRequests.push(event.value);
|
||||
}
|
||||
if (event.type === GeminiEventType.LoopDetected) {
|
||||
emitLoopDetectedMessage(config, event.value?.loopType);
|
||||
}
|
||||
if (
|
||||
outputFormat === OutputFormat.TEXT &&
|
||||
event.type === GeminiEventType.Error
|
||||
|
|
@ -506,6 +542,9 @@ export async function runNonInteractive(
|
|||
if (event.type === GeminiEventType.ToolCallRequest) {
|
||||
itemToolCallRequests.push(event.value);
|
||||
}
|
||||
if (event.type === GeminiEventType.LoopDetected) {
|
||||
emitLoopDetectedMessage(config, event.value?.loopType);
|
||||
}
|
||||
if (
|
||||
outputFormat === OutputFormat.TEXT &&
|
||||
event.type === GeminiEventType.Error
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue