@galdor/provider-anthropic 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Conversion between galdor's shared schema and the Anthropic Messages API
3
+ * wire shape.
4
+ *
5
+ * One direction, {@link buildRequest}, lowers a galdor {@link Request} into the
6
+ * snake_case JSON the Messages API expects: system messages are hoisted into a
7
+ * dedicated `system` array, content parts and tool calls become typed blocks,
8
+ * extended thinking and structured output are expressed in Anthropic's own
9
+ * terms, and prompt-caching markers are attached to the final block of a span.
10
+ * The other direction, {@link responseFromWire} (with
11
+ * {@link extractStructuredOutput} and {@link usageFromWire}), collapses a wire
12
+ * response back into a galdor {@link Response}.
13
+ */
14
+ import { type Request, type Response } from "@galdor/core/provider";
15
+ /**
16
+ * Default `max_tokens` sent when the caller leaves {@link Request.maxTokens}
17
+ * unset. The Messages API requires the field, so a concrete value is always
18
+ * supplied.
19
+ */
20
+ export declare const DEFAULT_MAX_TOKENS = 4096;
21
+ /** A single Anthropic content block in its on-the-wire JSON form. */
22
+ interface WireBlock {
23
+ type: string;
24
+ text?: string;
25
+ source?: {
26
+ type: string;
27
+ media_type?: string;
28
+ data?: string;
29
+ url?: string;
30
+ };
31
+ id?: string;
32
+ name?: string;
33
+ input?: unknown;
34
+ thinking?: string;
35
+ signature?: string;
36
+ data?: string;
37
+ tool_use_id?: string;
38
+ content?: WireBlock[];
39
+ is_error?: boolean;
40
+ cache_control?: {
41
+ type: string;
42
+ };
43
+ }
44
+ /** One turn in the Anthropic conversation array (role plus content blocks). */
45
+ interface WireMessage {
46
+ role: string;
47
+ content: WireBlock[];
48
+ }
49
+ /** Request body of the Anthropic Messages API in its JSON wire shape. */
50
+ export interface MessageRequest {
51
+ model: string;
52
+ messages: WireMessage[];
53
+ system?: Array<{
54
+ type: string;
55
+ text: string;
56
+ cache_control?: {
57
+ type: string;
58
+ };
59
+ }>;
60
+ max_tokens: number;
61
+ temperature?: number;
62
+ top_p?: number;
63
+ stop_sequences?: string[];
64
+ stream?: boolean;
65
+ tools?: Array<{
66
+ name: string;
67
+ description?: string;
68
+ input_schema: unknown;
69
+ }>;
70
+ tool_choice?: {
71
+ type: string;
72
+ name?: string;
73
+ };
74
+ thinking?: {
75
+ type: string;
76
+ budget_tokens: number;
77
+ };
78
+ metadata?: {
79
+ user_id?: string;
80
+ };
81
+ }
82
+ /** Response body of a non-streaming Anthropic Messages API call. */
83
+ export interface MessageResponse {
84
+ id: string;
85
+ type: string;
86
+ role: string;
87
+ model: string;
88
+ content: WireBlock[];
89
+ stop_reason: string;
90
+ stop_sequence?: string;
91
+ usage: WireUsage;
92
+ }
93
+ /** Token-usage block reported by the Messages API, including cache counters. */
94
+ export interface WireUsage {
95
+ input_tokens: number;
96
+ output_tokens: number;
97
+ cache_creation_input_tokens?: number;
98
+ cache_read_input_tokens?: number;
99
+ }
100
+ /**
101
+ * Translate a galdor {@link Request} into an Anthropic {@link MessageRequest}.
102
+ *
103
+ * System messages are hoisted into the `system` array; user, assistant and tool
104
+ * messages become conversation turns, with consecutive tool results folded into
105
+ * the preceding user turn. Enabling reasoning sets a thinking budget (clamped to
106
+ * a minimum), grows `max_tokens` to cover it, and drops sampling controls that
107
+ * are incompatible with extended thinking. A `json_schema` response format is
108
+ * realized as a single forced tool call whose input schema is the requested one.
109
+ *
110
+ * @param req - The galdor request to lower.
111
+ * @param stream - Whether to set the wire `stream` flag.
112
+ * @returns The fully-formed Anthropic request body.
113
+ * @throws {InvalidRequestError} When the model is empty, a role is unknown, or content cannot be converted.
114
+ * @example
115
+ * const wire = buildRequest({ model: "claude-haiku-4-5", messages }, false);
116
+ */
117
+ export declare function buildRequest(req: Request, stream: boolean): MessageRequest;
118
+ /**
119
+ * Convert a wire usage block into galdor's usage shape.
120
+ *
121
+ * @returns Token counts with cache-creation and cache-read figures, each
122
+ * defaulting to zero when the field is absent.
123
+ */
124
+ export declare function usageFromWire(u: WireUsage): {
125
+ inputTokens: number;
126
+ outputTokens: number;
127
+ cacheCreationTokens: number;
128
+ cacheReadTokens: number;
129
+ };
130
+ /**
131
+ * Collapse a non-streaming Anthropic {@link MessageResponse} into a galdor {@link Response}.
132
+ *
133
+ * Text and thinking blocks become assistant content parts; `tool_use` blocks
134
+ * become tool calls; `redacted_thinking` is preserved via its signature. Empty
135
+ * blocks are skipped.
136
+ *
137
+ * @param r - The decoded wire response.
138
+ * @param raw - Optional raw response bytes, attached as `providerRaw` when given.
139
+ * @returns The assembled response with message, stop reason, usage and model.
140
+ */
141
+ export declare function responseFromWire(r: MessageResponse, raw?: Uint8Array): Response;
142
+ /**
143
+ * Rewrite a forced structured-output tool call into plain message text.
144
+ *
145
+ * When the response contains the tool call that backs structured output, its
146
+ * arguments are serialized to JSON and become the assistant message body, so
147
+ * callers receive the structured result as text rather than a tool invocation.
148
+ * If no matching call is found, the response is returned unchanged.
149
+ *
150
+ * @param resp - The response produced by {@link responseFromWire}.
151
+ * @param schemaName - The configured schema name, resolved the same way as in {@link buildRequest}.
152
+ * @returns The (possibly rewritten) response.
153
+ */
154
+ export declare function extractStructuredOutput(resp: Response, schemaName: string | undefined): Response;
155
+ export {};
156
+ //# sourceMappingURL=convert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,303 @@
1
+ /**
2
+ * Conversion between galdor's shared schema and the Anthropic Messages API
3
+ * wire shape.
4
+ *
5
+ * One direction, {@link buildRequest}, lowers a galdor {@link Request} into the
6
+ * snake_case JSON the Messages API expects: system messages are hoisted into a
7
+ * dedicated `system` array, content parts and tool calls become typed blocks,
8
+ * extended thinking and structured output are expressed in Anthropic's own
9
+ * terms, and prompt-caching markers are attached to the final block of a span.
10
+ * The other direction, {@link responseFromWire} (with
11
+ * {@link extractStructuredOutput} and {@link usageFromWire}), collapses a wire
12
+ * response back into a galdor {@link Response}.
13
+ */
14
+ import { InvalidRequestError } from "@galdor/core/provider";
15
+ import { ContentType, messageText, Role, textPart, } from "@galdor/core/schema";
16
+ /**
17
+ * Default `max_tokens` sent when the caller leaves {@link Request.maxTokens}
18
+ * unset. The Messages API requires the field, so a concrete value is always
19
+ * supplied.
20
+ */
21
+ export const DEFAULT_MAX_TOKENS = 4096;
22
+ /**
23
+ * Build an {@link InvalidRequestError} for a request that cannot be lowered to
24
+ * the wire shape (empty model, unknown role, unconvertible content). Typed so
25
+ * callers can discriminate a local build failure from a transport error.
26
+ */
27
+ function invalidRequest(message) {
28
+ return new InvalidRequestError({ kind: "invalid_request", provider: "anthropic", statusCode: 0, message });
29
+ }
30
+ /** Resolve the tool name used for forced structured output, defaulting when unnamed. */
31
+ function structuredToolName(name) {
32
+ return name && name !== "" ? name : "structured_output";
33
+ }
34
+ /** Encode raw image bytes as a base64 string for inline transport. */
35
+ function toBase64(data) {
36
+ return Buffer.from(data).toString("base64");
37
+ }
38
+ /**
39
+ * Build the `source` of an image block, preferring a URL reference and otherwise
40
+ * encoding inline bytes as base64.
41
+ *
42
+ * @throws {InvalidRequestError} When inline data is present but its MIME type is missing, or
43
+ * when the part carries neither a URL nor data.
44
+ */
45
+ function imageToWire(img) {
46
+ if (img.url && img.url !== "")
47
+ return { type: "url", url: img.url };
48
+ if (img.data && img.data.length > 0) {
49
+ if (!img.media)
50
+ throw invalidRequest("anthropic: inline image missing media (MIME type)");
51
+ return { type: "base64", media_type: img.media, data: toBase64(img.data) };
52
+ }
53
+ throw invalidRequest("anthropic: image part with no url or data");
54
+ }
55
+ /**
56
+ * Convert galdor content parts into Anthropic content blocks, attaching the
57
+ * message's cache-control marker to the final emitted block.
58
+ *
59
+ * Unsigned reasoning parts are dropped because they cannot be replayed without a
60
+ * signature.
61
+ *
62
+ * @throws {InvalidRequestError} On an image part missing its image, or an unsupported part type.
63
+ */
64
+ function partsToWire(parts, cc) {
65
+ const out = [];
66
+ for (const p of parts) {
67
+ switch (p.type) {
68
+ case ContentType.Text:
69
+ out.push({ type: "text", text: p.text ?? "" });
70
+ break;
71
+ case ContentType.Image:
72
+ if (!p.image)
73
+ throw invalidRequest("anthropic: image part with nil image");
74
+ out.push({ type: "image", source: imageToWire(p.image) });
75
+ break;
76
+ case ContentType.Thinking:
77
+ if (!p.signature)
78
+ continue; // unsigned reasoning can't be resent
79
+ out.push({ type: "thinking", thinking: p.text ?? "", signature: p.signature });
80
+ break;
81
+ case ContentType.RedactedThinking:
82
+ if (!p.signature)
83
+ continue;
84
+ out.push({ type: "redacted_thinking", data: p.signature });
85
+ break;
86
+ default:
87
+ throw invalidRequest(`anthropic: unsupported content type ${p.type}`);
88
+ }
89
+ }
90
+ applyCacheControl(out, cc);
91
+ return out;
92
+ }
93
+ /** Stamp the cache-control marker, if any, onto the last block of a span. */
94
+ function applyCacheControl(blocks, cc) {
95
+ if (cc && blocks.length > 0)
96
+ blocks[blocks.length - 1].cache_control = { type: cc.type };
97
+ }
98
+ /**
99
+ * Translate a galdor {@link ToolChoice} into the Anthropic `tool_choice` object.
100
+ *
101
+ * @returns The wire choice, or `undefined` to leave the field unset (provider default).
102
+ */
103
+ function toolChoiceToWire(c) {
104
+ switch (c) {
105
+ case "none":
106
+ return { type: "none" };
107
+ case "required":
108
+ return { type: "any" };
109
+ case "auto":
110
+ return { type: "auto" };
111
+ default:
112
+ return undefined;
113
+ }
114
+ }
115
+ /**
116
+ * Translate a galdor {@link Request} into an Anthropic {@link MessageRequest}.
117
+ *
118
+ * System messages are hoisted into the `system` array; user, assistant and tool
119
+ * messages become conversation turns, with consecutive tool results folded into
120
+ * the preceding user turn. Enabling reasoning sets a thinking budget (clamped to
121
+ * a minimum), grows `max_tokens` to cover it, and drops sampling controls that
122
+ * are incompatible with extended thinking. A `json_schema` response format is
123
+ * realized as a single forced tool call whose input schema is the requested one.
124
+ *
125
+ * @param req - The galdor request to lower.
126
+ * @param stream - Whether to set the wire `stream` flag.
127
+ * @returns The fully-formed Anthropic request body.
128
+ * @throws {InvalidRequestError} When the model is empty, a role is unknown, or content cannot be converted.
129
+ * @example
130
+ * const wire = buildRequest({ model: "claude-haiku-4-5", messages }, false);
131
+ */
132
+ export function buildRequest(req, stream) {
133
+ if (req.model === "")
134
+ throw invalidRequest("anthropic: model is required");
135
+ let maxTokens = req.maxTokens ?? DEFAULT_MAX_TOKENS;
136
+ const out = { model: req.model, messages: [], max_tokens: maxTokens, stream };
137
+ if (req.temperature !== undefined)
138
+ out.temperature = req.temperature;
139
+ if (req.topP !== undefined)
140
+ out.top_p = req.topP;
141
+ if (req.stopSequences)
142
+ out.stop_sequences = req.stopSequences;
143
+ if (req.reasoning?.enabled) {
144
+ let budget = req.reasoning.budget ?? 0;
145
+ if (budget < 1024)
146
+ budget = 1024;
147
+ if (out.max_tokens <= budget)
148
+ out.max_tokens = budget + maxTokens;
149
+ out.thinking = { type: "enabled", budget_tokens: budget };
150
+ delete out.temperature; // incompatible with extended thinking
151
+ delete out.top_p;
152
+ }
153
+ for (const m of req.messages) {
154
+ switch (m.role) {
155
+ case Role.System:
156
+ (out.system ??= []).push({ type: "text", text: messageText(m), ...(m.cacheControl ? { cache_control: { type: m.cacheControl.type } } : {}) });
157
+ break;
158
+ case Role.User:
159
+ out.messages.push({ role: "user", content: partsToWire(m.content, m.cacheControl) });
160
+ break;
161
+ case Role.Assistant: {
162
+ const blocks = partsToWire(m.content, undefined);
163
+ for (const tc of m.toolCalls ?? []) {
164
+ const input = tc.arguments === undefined || tc.arguments === null ? {} : tc.arguments;
165
+ blocks.push({ type: "tool_use", id: tc.id, name: tc.name, input });
166
+ }
167
+ applyCacheControl(blocks, m.cacheControl);
168
+ out.messages.push({ role: "assistant", content: blocks });
169
+ break;
170
+ }
171
+ case Role.Tool: {
172
+ const block = {
173
+ type: "tool_result",
174
+ tool_use_id: m.toolCallId ?? "",
175
+ content: [{ type: "text", text: messageText(m) }],
176
+ };
177
+ const last = out.messages.at(-1);
178
+ if (last && last.role === "user")
179
+ last.content.push(block);
180
+ else
181
+ out.messages.push({ role: "user", content: [block] });
182
+ break;
183
+ }
184
+ default:
185
+ throw invalidRequest(`anthropic: unknown role ${m.role}`);
186
+ }
187
+ }
188
+ if (req.tools && req.tools.length > 0) {
189
+ out.tools = req.tools.map((t) => ({ name: t.name, description: t.description, input_schema: t.schema }));
190
+ }
191
+ const tc = toolChoiceToWire(req.toolChoice);
192
+ if (tc)
193
+ out.tool_choice = tc;
194
+ // Structured output → forced single tool whose input_schema is the request's.
195
+ if (req.responseFormat?.type === "json_schema") {
196
+ const name = structuredToolName(req.responseFormat.name);
197
+ out.tools = [{ name, description: "Respond by calling this tool with the structured result.", input_schema: req.responseFormat.schema }];
198
+ out.tool_choice = { type: "tool", name };
199
+ }
200
+ const uid = req.metadata?.user_id;
201
+ if (uid)
202
+ out.metadata = { user_id: uid };
203
+ return out;
204
+ }
205
+ /** Map a wire stop-reason string to a galdor {@link StopReason}, treating empty as `end_turn`. */
206
+ function normalizeStopReason(s) {
207
+ switch (s) {
208
+ case "end_turn":
209
+ return "end_turn";
210
+ case "max_tokens":
211
+ return "max_tokens";
212
+ case "tool_use":
213
+ return "tool_use";
214
+ case "stop_sequence":
215
+ return "stop_sequence";
216
+ case "refusal":
217
+ return "refusal";
218
+ default:
219
+ // Empty and unknown reasons pass through as-is (matching the oracle),
220
+ // rather than being coerced to end_turn.
221
+ return s;
222
+ }
223
+ }
224
+ /**
225
+ * Convert a wire usage block into galdor's usage shape.
226
+ *
227
+ * @returns Token counts with cache-creation and cache-read figures, each
228
+ * defaulting to zero when the field is absent.
229
+ */
230
+ export function usageFromWire(u) {
231
+ return {
232
+ inputTokens: u.input_tokens ?? 0,
233
+ outputTokens: u.output_tokens ?? 0,
234
+ cacheCreationTokens: u.cache_creation_input_tokens ?? 0,
235
+ cacheReadTokens: u.cache_read_input_tokens ?? 0,
236
+ };
237
+ }
238
+ /**
239
+ * Collapse a non-streaming Anthropic {@link MessageResponse} into a galdor {@link Response}.
240
+ *
241
+ * Text and thinking blocks become assistant content parts; `tool_use` blocks
242
+ * become tool calls; `redacted_thinking` is preserved via its signature. Empty
243
+ * blocks are skipped.
244
+ *
245
+ * @param r - The decoded wire response.
246
+ * @param raw - Optional raw response bytes, attached as `providerRaw` when given.
247
+ * @returns The assembled response with message, stop reason, usage and model.
248
+ */
249
+ export function responseFromWire(r, raw) {
250
+ const message = { role: Role.Assistant, content: [] };
251
+ const toolCalls = [];
252
+ for (const b of r.content) {
253
+ switch (b.type) {
254
+ case "text":
255
+ if (b.text)
256
+ message.content.push(textPart(b.text));
257
+ break;
258
+ case "tool_use":
259
+ toolCalls.push({ id: b.id ?? "", name: b.name ?? "", arguments: (b.input ?? {}) });
260
+ break;
261
+ case "thinking":
262
+ if (b.thinking)
263
+ message.content.push({ type: ContentType.Thinking, text: b.thinking, ...(b.signature ? { signature: b.signature } : {}) });
264
+ break;
265
+ case "redacted_thinking":
266
+ if (b.data)
267
+ message.content.push({ type: ContentType.RedactedThinking, signature: b.data });
268
+ break;
269
+ }
270
+ }
271
+ if (toolCalls.length > 0)
272
+ message.toolCalls = toolCalls;
273
+ return {
274
+ message,
275
+ stopReason: normalizeStopReason(r.stop_reason),
276
+ usage: usageFromWire(r.usage),
277
+ model: r.model,
278
+ ...(raw ? { providerRaw: raw } : {}),
279
+ };
280
+ }
281
+ /**
282
+ * Rewrite a forced structured-output tool call into plain message text.
283
+ *
284
+ * When the response contains the tool call that backs structured output, its
285
+ * arguments are serialized to JSON and become the assistant message body, so
286
+ * callers receive the structured result as text rather than a tool invocation.
287
+ * If no matching call is found, the response is returned unchanged.
288
+ *
289
+ * @param resp - The response produced by {@link responseFromWire}.
290
+ * @param schemaName - The configured schema name, resolved the same way as in {@link buildRequest}.
291
+ * @returns The (possibly rewritten) response.
292
+ */
293
+ export function extractStructuredOutput(resp, schemaName) {
294
+ const name = structuredToolName(schemaName);
295
+ for (const tc of resp.message.toolCalls ?? []) {
296
+ if (tc.name === name) {
297
+ resp.message = { role: Role.Assistant, content: [textPart(JSON.stringify(tc.arguments))] };
298
+ return resp;
299
+ }
300
+ }
301
+ return resp;
302
+ }
303
+ //# sourceMappingURL=convert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.js","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Normalization of Anthropic HTTP failures into galdor's typed error surface.
3
+ *
4
+ * The Messages API signals failure both through the HTTP status code and through
5
+ * a JSON `error.type` discriminator in the body. This module folds the two into
6
+ * a single {@link ErrorKind}, preferring the body's classification when present
7
+ * and falling back to the status code otherwise, then wraps the result in an
8
+ * {@link APIError} so callers can branch on `instanceof` of the concrete error
9
+ * subclasses produced by {@link classify}.
10
+ */
11
+ import { APIError } from "@galdor/core/provider";
12
+ /**
13
+ * Minimal structural view of a fetch `Response` needed to classify a failure.
14
+ *
15
+ * Accepting this narrow interface (rather than the full `Response`) keeps
16
+ * {@link normalizeHTTPError} testable with lightweight stubs.
17
+ */
18
+ export interface ResponseLike {
19
+ /** HTTP status code of the failed response. */
20
+ status: number;
21
+ /** Header accessor; used to read `retry-after`. */
22
+ headers: {
23
+ get(name: string): string | null;
24
+ };
25
+ /** Reads the full response body as text. */
26
+ text(): Promise<string>;
27
+ }
28
+ /**
29
+ * Convert a non-2xx Anthropic response into a typed galdor {@link APIError}.
30
+ *
31
+ * The body is read once and parsed as JSON; when it carries an `error.type`,
32
+ * that classification wins over the status-derived kind, and the human-readable
33
+ * `error.message` becomes the error message. A `retry-after` header, if present,
34
+ * is parsed and attached. The result is passed through {@link classify} so the
35
+ * caller receives the concrete error subclass for the kind.
36
+ *
37
+ * @param res - The failed response (or a structural stand-in).
38
+ * @returns A classified {@link APIError} describing the failure.
39
+ * @example
40
+ * if (Math.floor(res.status / 100) !== 2) throw await normalizeHTTPError(res);
41
+ */
42
+ export declare function normalizeHTTPError(res: ResponseLike): Promise<APIError>;
43
+ /**
44
+ * Build a typed galdor {@link APIError} from an Anthropic streaming `error`
45
+ * frame, which carries no HTTP status.
46
+ *
47
+ * The mid-stream `error` SSE event reports its `error.type` discriminator and a
48
+ * human message but, unlike an HTTP failure, has no status code to fall back on.
49
+ * The type is mapped through the same {@link kindForType} table used for HTTP
50
+ * errors and defaults to `server` when unrecognized. The result is passed
51
+ * through {@link classify} so the caller receives the concrete subclass.
52
+ *
53
+ * @param type - The `error.type` discriminator from the frame, if any.
54
+ * @param message - The human-readable error message from the frame.
55
+ * @returns A classified {@link APIError} with `statusCode` 0.
56
+ */
57
+ export declare function classifyStreamError(type: string | undefined, message: string): APIError;
58
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAA6C,MAAM,uBAAuB,CAAC;AA2C5F;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,OAAO,EAAE;QAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC9C,4CAA4C;IAC5C,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkB7E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,CAGvF"}
package/dist/errors.js ADDED
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Normalization of Anthropic HTTP failures into galdor's typed error surface.
3
+ *
4
+ * The Messages API signals failure both through the HTTP status code and through
5
+ * a JSON `error.type` discriminator in the body. This module folds the two into
6
+ * a single {@link ErrorKind}, preferring the body's classification when present
7
+ * and falling back to the status code otherwise, then wraps the result in an
8
+ * {@link APIError} so callers can branch on `instanceof` of the concrete error
9
+ * subclasses produced by {@link classify}.
10
+ */
11
+ import { APIError, classify, parseRetryAfter } from "@galdor/core/provider";
12
+ const PROVIDER_NAME = "anthropic";
13
+ /** Map an HTTP status code to a galdor {@link ErrorKind} as a coarse fallback. */
14
+ function kindForStatus(code) {
15
+ if (code === 401 || code === 403)
16
+ return "auth";
17
+ if (code === 429)
18
+ return "rate_limited";
19
+ if (code >= 500)
20
+ return "server";
21
+ if (code >= 400)
22
+ return "invalid_request";
23
+ return "server";
24
+ }
25
+ /**
26
+ * Map Anthropic's `error.type` discriminator to a galdor {@link ErrorKind}.
27
+ *
28
+ * @returns The matching kind, or `undefined` when the type is unknown so the
29
+ * caller can fall back to {@link kindForStatus}.
30
+ */
31
+ function kindForType(t) {
32
+ switch (t) {
33
+ case "authentication_error":
34
+ case "permission_error":
35
+ return "auth";
36
+ case "rate_limit_error":
37
+ case "overloaded_error":
38
+ return "rate_limited";
39
+ case "invalid_request_error":
40
+ case "not_found_error":
41
+ return "invalid_request";
42
+ case "api_error":
43
+ return "server";
44
+ default:
45
+ return undefined;
46
+ }
47
+ }
48
+ /**
49
+ * Convert a non-2xx Anthropic response into a typed galdor {@link APIError}.
50
+ *
51
+ * The body is read once and parsed as JSON; when it carries an `error.type`,
52
+ * that classification wins over the status-derived kind, and the human-readable
53
+ * `error.message` becomes the error message. A `retry-after` header, if present,
54
+ * is parsed and attached. The result is passed through {@link classify} so the
55
+ * caller receives the concrete error subclass for the kind.
56
+ *
57
+ * @param res - The failed response (or a structural stand-in).
58
+ * @returns A classified {@link APIError} describing the failure.
59
+ * @example
60
+ * if (Math.floor(res.status / 100) !== 2) throw await normalizeHTTPError(res);
61
+ */
62
+ export async function normalizeHTTPError(res) {
63
+ const text = await res.text().catch(() => "");
64
+ let kind = kindForStatus(res.status);
65
+ let message = `anthropic: HTTP ${res.status}`;
66
+ if (text) {
67
+ try {
68
+ const body = JSON.parse(text);
69
+ if (body.error?.message)
70
+ message = body.error.message;
71
+ const k = kindForType(body.error?.type);
72
+ if (k)
73
+ kind = k;
74
+ }
75
+ catch {
76
+ /* non-JSON body: keep the status-derived kind and default message */
77
+ }
78
+ }
79
+ const retryAfter = parseRetryAfter(res.headers.get("retry-after") ?? "", new Date());
80
+ return classify(new APIError({ kind, provider: PROVIDER_NAME, statusCode: res.status, message, ...(retryAfter !== null ? { retryAfter } : {}) }));
81
+ }
82
+ /**
83
+ * Build a typed galdor {@link APIError} from an Anthropic streaming `error`
84
+ * frame, which carries no HTTP status.
85
+ *
86
+ * The mid-stream `error` SSE event reports its `error.type` discriminator and a
87
+ * human message but, unlike an HTTP failure, has no status code to fall back on.
88
+ * The type is mapped through the same {@link kindForType} table used for HTTP
89
+ * errors and defaults to `server` when unrecognized. The result is passed
90
+ * through {@link classify} so the caller receives the concrete subclass.
91
+ *
92
+ * @param type - The `error.type` discriminator from the frame, if any.
93
+ * @param message - The human-readable error message from the frame.
94
+ * @returns A classified {@link APIError} with `statusCode` 0.
95
+ */
96
+ export function classifyStreamError(type, message) {
97
+ const kind = kindForType(type) ?? "server";
98
+ return classify(new APIError({ kind, provider: PROVIDER_NAME, statusCode: 0, message }));
99
+ }
100
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAkB,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE5F,MAAM,aAAa,GAAG,WAAW,CAAC;AAQlC,kFAAkF;AAClF,SAAS,aAAa,CAAC,IAAY;IACjC,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,MAAM,CAAC;IAChD,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,cAAc,CAAC;IACxC,IAAI,IAAI,IAAI,GAAG;QAAE,OAAO,QAAQ,CAAC;IACjC,IAAI,IAAI,IAAI,GAAG;QAAE,OAAO,iBAAiB,CAAC;IAC1C,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;GAKG;AACH,SAAS,WAAW,CAAC,CAAqB;IACxC,QAAQ,CAAC,EAAE,CAAC;QACV,KAAK,sBAAsB,CAAC;QAC5B,KAAK,kBAAkB;YACrB,OAAO,MAAM,CAAC;QAChB,KAAK,kBAAkB,CAAC;QACxB,KAAK,kBAAkB;YACrB,OAAO,cAAc,CAAC;QACxB,KAAK,uBAAuB,CAAC;QAC7B,KAAK,iBAAiB;YACpB,OAAO,iBAAiB,CAAC;QAC3B,KAAK,WAAW;YACd,OAAO,QAAQ,CAAC;QAClB;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAiBD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAiB;IACxD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC9C,IAAI,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,OAAO,GAAG,mBAAmB,GAAG,CAAC,MAAM,EAAE,CAAC;IAC9C,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAuB,CAAC;YACpD,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO;gBAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;YACtD,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YACxC,IAAI,CAAC;gBAAE,IAAI,GAAG,CAAC,CAAC;QAClB,CAAC;QAAC,MAAM,CAAC;YACP,qEAAqE;QACvE,CAAC;IACH,CAAC;IACD,MAAM,UAAU,GAAG,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;IACrF,OAAO,QAAQ,CACb,IAAI,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CACjI,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAwB,EAAE,OAAe;IAC3E,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC;IAC3C,OAAO,QAAQ,CAAC,IAAI,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;AAC3F,CAAC"}