Files
OmniRoute/tests/unit/stream-readiness-policy.test.ts
Abhishek Sharma 4c8074ba7a fix(sse): give extended-thinking targets the reasoning readiness budget (#11959)
Dá aos alvos de extended-thinking o orçamento de prontidão de reasoning, com cobertura de teste ampliada em `stream-readiness-policy.test.ts`. Validado no worktree combinado. Obrigado!
2026-08-30 09:09:54 -03:00

340 lines
12 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import { resolveStreamReadinessTimeout } from "../../open-sse/utils/streamReadinessPolicy.ts";
function items(count: number): Array<{ role: string; content: string }> {
return Array.from({ length: count }, (_, index) => ({
role: "user",
content: `message ${index}`,
}));
}
function tools(count: number): Array<{ type: string; name: string }> {
return Array.from({ length: count }, (_, index) => ({ type: "function", name: `tool_${index}` }));
}
test("keeps the base timeout for small requests", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 30_000,
provider: "codex",
model: "gpt-5.5",
body: { input: items(3), tools: tools(2) },
});
assert.equal(result.timeoutMs, 30_000);
assert.deepEqual(result.reasons, ["base"]);
});
test("increases timeout for large conversation history", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 30_000,
provider: "openai",
model: "gpt-4.1",
body: { input: items(181) },
});
assert.equal(result.timeoutMs, 50_000);
assert.ok(result.reasons.includes("large_history"));
});
test("increases timeout for tool-heavy requests", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 30_000,
provider: "openai",
model: "gpt-4.1",
body: { input: items(10), tools: tools(20) },
});
assert.equal(result.timeoutMs, 45_000);
assert.ok(result.reasons.includes("tool_heavy"));
});
test("gives Codex GPT-5.5 large Responses requests extra readiness budget", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 30_000,
provider: "codex",
model: "gpt-5.5",
body: { input: items(181), tools: tools(20) },
});
assert.equal(result.timeoutMs, 95_000);
assert.ok(result.reasons.includes("large_history"));
assert.ok(result.reasons.includes("tool_heavy"));
assert.ok(result.reasons.includes("codex_gpt_5_5_large_responses"));
});
test("gives high-reasoning Codex GPT-5.x extra readiness budget even for SMALL requests (#3825)", () => {
// Regression for #3825: a small-prompt high-reasoning codex target has ~78s TTFB
// (cold high-reasoning start). Before the fix it only received the 80s base and 504'd
// at the readiness window. The reasoning-aware bump must fire UNCONDITIONALLY for
// high-effort codex, regardless of request size.
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "codex",
model: "gpt-5.5-high",
body: { messages: items(3), tools: tools(2) },
});
assert.ok(
result.timeoutMs >= 110_000,
`expected >= 110000ms for small high-reasoning codex, got ${result.timeoutMs}`
);
assert.ok(result.reasons.includes("codex_gpt_5_5_high_reasoning"));
});
test("does NOT bump small NON-high codex requests (#3825 scope guard)", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "codex",
model: "gpt-5.5",
body: { messages: items(3), tools: tools(2) },
});
assert.equal(result.timeoutMs, 80_000);
assert.deepEqual(result.reasons, ["base"]);
});
test("does NOT bump small high-reasoning NON-codex requests (#3825 scope guard)", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "openai",
model: "gpt-5.5-high",
body: { messages: items(3), tools: tools(2) },
});
assert.equal(result.timeoutMs, 80_000);
assert.deepEqual(result.reasons, ["base"]);
});
test("caps adaptive timeout at maxTimeoutMs", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 30_000,
maxTimeoutMs: 120_000,
provider: "codex",
model: "gpt-5.5",
body: { input: items(500), tools: tools(20), instructions: "x".repeat(800_000) },
});
assert.equal(result.timeoutMs, 120_000);
assert.ok(result.reasons.includes("very_large_history"));
assert.ok(result.reasons.includes("very_large_payload"));
});
test("uses a 180s adaptive cap by default for very large agent requests", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "codex",
model: "gpt-5.5",
body: { input: items(500), tools: tools(20), instructions: "x".repeat(800_000) },
});
assert.equal(result.timeoutMs, 180_000);
assert.ok(result.reasons.includes("very_large_history"));
assert.ok(result.reasons.includes("very_large_payload"));
});
test("preserves zero timeout so readiness checks can be disabled", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 0,
provider: "codex",
model: "gpt-5.5",
body: { input: items(500), tools: tools(20) },
});
assert.equal(result.timeoutMs, 0);
assert.deepEqual(result.reasons, ["disabled"]);
});
test("bumps small requests to third-party Claude-format replicas (agentrouter, ZAI, bailian) — guards against #3825-class false 504s on long reasoning warm-ups", () => {
// Provider registry lists agentrouter with `format: "claude"` — the readiness budget
// must fire UNCONDITIONALLY for those replicas, like the codex_gpt_5_5_high
// bump, because their reasoning warm-ups routinely exceed the default 80s window.
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "agentrouter",
model: "claude-opus-4-8",
body: { messages: items(3), tools: tools(2) },
});
assert.equal(result.timeoutMs, 110_000);
assert.ok(
result.reasons.includes("claude_format_heavy_reasoning"),
`expected claude_format_heavy_reasoning in reasons, got ${JSON.stringify(result.reasons)}`
);
});
test('does NOT bump Minimax (M3) — #3110 moved it from claude to openai format so images work, and the readiness bump is keyed off the registry\'s `format: "claude"` field', () => {
// Minimax's replica quirk (long reasoning warm-up) hasn't changed, but this
// policy intentionally keys off the translator format, not the provider
// name — the registry is the single source of truth (see isClaudeFormatReasoningProvider
// doc comment). Now that minimax routes through the OpenAI translator, it no
// longer matches, mirroring the OpenAI/non-Claude exclusion below.
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "minimax",
model: "MiniMax-M3",
body: { messages: items(3), tools: tools(2) },
});
assert.equal(result.timeoutMs, 80_000);
assert.ok(!result.reasons.includes("claude_format_heavy_reasoning"));
});
test("bumps ZAI (claude-format replica) readiness budget the same way", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "zai",
model: "GLM-5",
body: { messages: items(3), tools: tools(2) },
});
assert.equal(result.timeoutMs, 110_000);
assert.ok(result.reasons.includes("claude_format_heavy_reasoning"));
});
test("does NOT bump official Anthropic first-party providers (claude/anthropic) — they have stable cold starts", () => {
const claudeResult = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "claude",
model: "claude-opus-4.5",
body: { messages: items(3), tools: tools(2) },
});
const anthropicResult = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "anthropic",
model: "claude-sonnet-4.5",
body: { messages: items(3), tools: tools(2) },
});
assert.equal(claudeResult.timeoutMs, 80_000);
assert.equal(anthropicResult.timeoutMs, 80_000);
assert.ok(!claudeResult.reasons.includes("claude_format_heavy_reasoning"));
assert.ok(!anthropicResult.reasons.includes("claude_format_heavy_reasoning"));
});
test("does NOT bump OpenAI / non-Claude providers", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "openai",
model: "gpt-5",
body: { messages: items(3), tools: tools(2) },
});
assert.equal(result.timeoutMs, 80_000);
assert.ok(!result.reasons.includes("claude_format_heavy_reasoning"));
});
test("does NOT double-bump when codex-high reasoning and Claude-format replica both match", () => {
// Belt-and-braces guard: even if someone extends the codex detection to
// Claude-format providers later, the readiness bump must not stack.
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "agentrouter",
model: "claude-opus-4-8-high",
body: { messages: items(3), tools: tools(2), reasoning_effort: "high" },
});
// Should be bumped by exactly one reason — claude_format_heavy_reasoning —
// because agentrouter is not a codex provider, the codex_* path never fires.
assert.equal(result.timeoutMs, 110_000);
assert.ok(result.reasons.includes("claude_format_heavy_reasoning"));
assert.ok(!result.reasons.includes("codex_gpt_5_5_high_reasoning"));
});
test("caps Claude-format replica bump at the configured maxTimeoutMs", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
maxTimeoutMs: 100_000,
provider: "agentrouter",
model: "claude-opus-4-8",
body: { messages: items(500), tools: tools(20), instructions: "x".repeat(800_000) },
});
assert.equal(result.timeoutMs, 100_000);
assert.ok(result.reasons.includes("claude_format_heavy_reasoning"));
});
test("treats unknown provider names as non-Claude-format (no false positives)", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "not-a-real-provider",
model: "anything",
body: { messages: items(3) },
});
assert.equal(result.timeoutMs, 80_000);
assert.ok(!result.reasons.includes("claude_format_heavy_reasoning"));
});
test("gives extended-thinking model aliases the reasoning readiness bump (#11922)", () => {
// #11922: kiro/claude-sonnet-5-thinking 504'd with
// "Stream produced no non-ping SSE event within 125000ms" — the 80s base plus
// the 45s very-large-history bump, capped there because nothing recognised the
// request as a reasoning target. Kiro serves Anthropic thinking models through
// its own CodeWhisperer translator, so `format: "kiro"` (not "claude") kept it
// out of the claude_format_heavy_reasoning bump, and the `-thinking` alias was
// never a reasoning signal the way `-high` is.
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "kiro",
model: "claude-sonnet-5-thinking",
body: { messages: items(401) },
});
assert.equal(result.timeoutMs, 155_000);
assert.ok(result.reasons.includes("extended_thinking"));
});
test("extended-thinking bump is provider-agnostic and fires for small requests", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "devin",
model: "claude-opus-4-6-thinking",
body: { messages: items(2) },
});
assert.equal(result.timeoutMs, 110_000);
assert.ok(result.reasons.includes("extended_thinking"));
});
test("does NOT stack extended-thinking with the Claude-format replica bump", () => {
// Both bumps model the same one-off reasoning warm-up. A claude-format replica
// serving a `-thinking` alias must get 30s once, not 60s twice.
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "agentrouter",
model: "claude-sonnet-4-6-thinking",
body: { messages: items(2) },
});
assert.equal(result.timeoutMs, 110_000);
assert.ok(result.reasons.includes("extended_thinking"));
assert.ok(!result.reasons.includes("claude_format_heavy_reasoning"));
});
test("does NOT stack extended-thinking with the codex high-reasoning bump", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "codex",
model: "gpt-5.5-thinking",
body: { messages: items(2), reasoning_effort: "high" },
});
assert.equal(result.timeoutMs, 110_000);
assert.ok(result.reasons.includes("codex_gpt_5_5_high_reasoning"));
assert.ok(!result.reasons.includes("extended_thinking"));
});
test("does not treat an unrelated id containing 'thinking' as an alias suffix", () => {
const result = resolveStreamReadinessTimeout({
baseTimeoutMs: 80_000,
provider: "openai",
model: "thinking-machines-lab-model",
body: { messages: items(2) },
});
assert.equal(result.timeoutMs, 80_000);
assert.ok(!result.reasons.includes("extended_thinking"));
});