Skip to content

Commit 5d49733

Browse files
committed
🤖 Fix test flake: limit tool call steps to prevent infinite loops
Reasoning models (especially gpt-5-codex) can get stuck in infinite tool call loops when combined with web_search and high reasoning effort. This was causing the openai-web-search.test.ts integration test to timeout after 120+ seconds with 15+ tool calls and no completion. Root cause: The stream was using `stopWhen: stepCountIs(100000)` which effectively allowed unlimited tool calls. With reasoning models, the model can keep calling tools indefinitely without reaching a final answer. Fix: Replace unlimited steps with `maxSteps: 25` to prevent infinite loops while still allowing reasonable multi-turn tool use. This value is chosen based on observed failure (15 tool calls) with some buffer. The AI SDK will now stop the stream after 25 tool call rounds, ensuring the stream completes and emits stream-end even if the model gets stuck. Fixes: https://github.com/coder/cmux/actions/runs/18766377932
1 parent 07b5d7b commit 5d49733

File tree

1 file changed

+2
-3
lines changed

1 file changed

+2
-3
lines changed

src/services/streamManager.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import * as path from "path";
44
import * as os from "os";
55
import {
66
streamText,
7-
stepCountIs,
87
type ModelMessage,
98
type LanguageModel,
109
type Tool,
@@ -476,8 +475,8 @@ export class StreamManager extends EventEmitter {
476475
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
477476
toolChoice: toolChoice as any, // Force tool use when required by policy
478477
// When toolChoice is set (required tool), limit to 1 step to prevent infinite loops
479-
// Otherwise allow unlimited steps for multi-turn tool use
480-
...(toolChoice ? { maxSteps: 1 } : { stopWhen: stepCountIs(100000) }),
478+
// Otherwise limit to 25 steps to prevent models (especially reasoning models) from getting stuck
479+
...(toolChoice ? { maxSteps: 1 } : { maxSteps: 25 }),
481480
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
482481
providerOptions: providerOptions as any, // Pass provider-specific options (thinking/reasoning config)
483482
// Default to 32000 tokens if not specified (Anthropic defaults to 4096)

0 commit comments

Comments
 (0)