forked from NoeFabris/opencode-antigravity-auth
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrecovery.test.ts
More file actions
157 lines (133 loc) · 5.78 KB
/
Copy pathrecovery.test.ts
File metadata and controls
157 lines (133 loc) · 5.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import { describe, it, expect } from "vitest";
import { detectErrorType, isRecoverableError } from "./recovery";
describe("detectErrorType", () => {
describe("tool_result_missing detection", () => {
it("detects tool_use without tool_result error", () => {
const error = {
type: "invalid_request_error",
message: "messages.105: `tool_use` ids were found without `tool_result` blocks immediately after: tool-call-59"
};
expect(detectErrorType(error)).toBe("tool_result_missing");
});
it("detects tool_use/tool_result mismatch error", () => {
const error = "Each `tool_use` block must have a corresponding `tool_result` block in the next message.";
expect(detectErrorType(error)).toBe("tool_result_missing");
});
it("detects error from string message", () => {
const error = "tool_use without matching tool_result";
expect(detectErrorType(error)).toBe("tool_result_missing");
});
});
describe("thinking_block_order detection", () => {
it("detects thinking first block error", () => {
const error = "thinking must be the first block in the message";
expect(detectErrorType(error)).toBe("thinking_block_order");
});
it("detects thinking must start with error", () => {
const error = "Response must start with thinking block";
expect(detectErrorType(error)).toBe("thinking_block_order");
});
it("detects thinking preceeding error", () => {
const error = "thinking block preceeding tool use is required";
expect(detectErrorType(error)).toBe("thinking_block_order");
});
it("detects thinking expected/found error", () => {
const error = "Expected thinking block but found text";
expect(detectErrorType(error)).toBe("thinking_block_order");
});
});
describe("thinking_disabled_violation detection", () => {
it("detects thinking disabled error", () => {
const error = "thinking is disabled for this model and cannot contain thinking blocks";
expect(detectErrorType(error)).toBe("thinking_disabled_violation");
});
});
describe("non-recoverable errors", () => {
it("returns null for prompt too long error", () => {
// This is handled separately, not as a recoverable error
const error = { message: "Prompt is too long" };
expect(detectErrorType(error)).toBeNull();
});
it("returns null for context length exceeded error", () => {
const error = "context length exceeded";
expect(detectErrorType(error)).toBeNull();
});
it("returns null for generic errors", () => {
expect(detectErrorType("Something went wrong")).toBeNull();
expect(detectErrorType({ message: "Unknown error" })).toBeNull();
expect(detectErrorType(null)).toBeNull();
expect(detectErrorType(undefined)).toBeNull();
});
it("returns null for rate limit errors", () => {
const error = { message: "Rate limit exceeded. Retry after 5s" };
expect(detectErrorType(error)).toBeNull();
});
it("returns null for generic INVALID_ARGUMENT with debug expected/found metadata", () => {
const error = {
message:
"Request contains an invalid argument. [Debug Info] Requested Model: antigravity-claude-opus-4-6-thinking Tool Debug Summary: expected=1 found=0",
};
expect(detectErrorType(error)).toBeNull();
});
});
});
describe("isRecoverableError", () => {
it("returns true for tool_result_missing", () => {
const error = "tool_use without tool_result";
expect(isRecoverableError(error)).toBe(true);
});
it("returns true for thinking_block_order", () => {
const error = "thinking must be the first block";
expect(isRecoverableError(error)).toBe(true);
});
it("returns true for thinking_disabled_violation", () => {
const error = "thinking is disabled and cannot contain thinking";
expect(isRecoverableError(error)).toBe(true);
});
it("returns false for non-recoverable errors", () => {
expect(isRecoverableError("Prompt is too long")).toBe(false);
expect(isRecoverableError("context length exceeded")).toBe(false);
expect(isRecoverableError("Generic error")).toBe(false);
expect(isRecoverableError(null)).toBe(false);
});
});
// =============================================================================
// CONTEXT ERROR MESSAGES
// These test that error messages from the API can be properly categorized
// =============================================================================
describe("context error message patterns", () => {
describe("prompt too long patterns", () => {
const promptTooLongPatterns = [
"Prompt is too long",
"prompt is too long for this model",
"The prompt is too long",
];
it.each(promptTooLongPatterns)("'%s' is not a recoverable error", (msg) => {
expect(isRecoverableError(msg)).toBe(false);
expect(detectErrorType(msg)).toBeNull();
});
});
describe("context length exceeded patterns", () => {
const contextLengthPatterns = [
"context length exceeded",
"context_length_exceeded",
"maximum context length",
"exceeds the maximum context window",
];
it.each(contextLengthPatterns)("'%s' is not a recoverable error", (msg) => {
expect(isRecoverableError(msg)).toBe(false);
expect(detectErrorType(msg)).toBeNull();
});
});
describe("tool pairing error patterns", () => {
const toolPairingPatterns = [
"tool_use ids were found without tool_result blocks immediately after",
"Each tool_use block must have a corresponding tool_result",
"tool_use without matching tool_result",
];
it.each(toolPairingPatterns)("'%s' is detected as tool_result_missing", (msg) => {
expect(detectErrorType(msg)).toBe("tool_result_missing");
expect(isRecoverableError(msg)).toBe(true);
});
});
});