|
1 | 1 | // Real behavior proof for #87876: verifies the existing timeout classifier |
2 | | -// already matches "This operation was aborted" stream abort errors, |
3 | | -// so the configured fallback chain rotates for Bedrock Converse drops. |
| 2 | +// already matches "This operation was aborted" stream abort errors. |
| 3 | +// The full classification chain classifies it as "timeout", meaning the |
| 4 | +// configured fallback model is tried instead of surfacing the error. |
4 | 5 | // |
5 | 6 | // Run: node --import tsx scripts/repro/issue-87876-stream-abort-classify.mts |
6 | | -import { isTimeoutErrorMessage } from "../../src/agents/embedded-agent-helpers/failover-matches.ts"; |
| 7 | +import { classifyFailoverReason, isTimeoutErrorMessage } from "../../src/agents/embedded-agent-helpers/errors.ts"; |
7 | 8 |
|
8 | 9 | function fail(message: string): never { |
9 | 10 | console.error(`FAIL: ${message}`); |
10 | 11 | process.exitCode = 1; |
11 | 12 | throw new Error(message); |
12 | 13 | } |
13 | 14 |
|
14 | | -// The exact error message from the issue: Bedrock Converse stream drops after ~6 min. |
| 15 | +// 1. Shared timeout classifier matches "This operation was aborted" |
15 | 16 | const bedrockAbort = "This operation was aborted"; |
16 | 17 | if (!isTimeoutErrorMessage(bedrockAbort)) { |
17 | | - fail(`expected "${bedrockAbort}" to be classified as timeout`); |
| 18 | + fail(`expected "${bedrockAbort}" to be classified as timeout by shared classifier`); |
18 | 19 | } |
19 | 20 | console.log(`PASS: "${bedrockAbort}" -> isTimeout=true (shared classifier)`); |
20 | 21 |
|
21 | | -// Case-insensitive. |
22 | | -if (!isTimeoutErrorMessage("this operation was aborted")) { |
23 | | - fail("expected case-insensitive match"); |
| 22 | +// 2. Full classification chain classifies as "timeout" (triggers fallback rotation) |
| 23 | +const reason = classifyFailoverReason(bedrockAbort); |
| 24 | +if (reason !== "timeout") { |
| 25 | + fail(`expected classifyFailoverReason to return "timeout", got "${reason}"`); |
24 | 26 | } |
25 | | -console.log("PASS: case-insensitive match works"); |
| 27 | +console.log(`PASS: classifyFailoverReason("${bedrockAbort}") = "${reason}" (triggers fallback)`); |
26 | 28 |
|
27 | | -// Embedded in a longer message. |
28 | | -if (!isTimeoutErrorMessage("ConverseStream failed: This operation was aborted at connection drop")) { |
29 | | - fail("expected embedded match"); |
| 29 | +// 3. Embedded in a longer provider message |
| 30 | +const embedded = classifyFailoverReason("ConverseStream failed: This operation was aborted"); |
| 31 | +if (embedded !== "timeout") { |
| 32 | + fail(`expected embedded classification to be "timeout", got "${embedded}"`); |
30 | 33 | } |
31 | | -console.log("PASS: embedded in longer message -> true"); |
| 34 | +console.log(`PASS: embedded message -> "${embedded}"`); |
32 | 35 |
|
33 | | -// Negative: unrelated abort messages should NOT match the timeout classifier. |
34 | | -if (isTimeoutErrorMessage("Request was aborted by user")) { |
35 | | - fail("should not match unrelated abort"); |
36 | | -} |
37 | | -console.log("PASS: unrelated abort messages -> false"); |
38 | | - |
39 | | -console.log("\nALL CHECKS PASSED — stream abort errors are already classified as timeout by the shared classifier."); |
| 36 | +console.log("\nALL CHECKS PASSED — stream abort errors are classified as timeout by the shared classifier, triggering fallback rotation."); |
0 commit comments