@thinwrap/llm 1.0.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.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -0
  3. package/dist/cjs/base/base.connector.d.ts +44 -0
  4. package/dist/cjs/base/base.connector.js +216 -0
  5. package/dist/cjs/facades/chat.facade.d.ts +17 -0
  6. package/dist/cjs/facades/chat.facade.js +63 -0
  7. package/dist/cjs/facades/embeddings.facade.d.ts +23 -0
  8. package/dist/cjs/facades/embeddings.facade.js +63 -0
  9. package/dist/cjs/index.d.ts +15 -0
  10. package/dist/cjs/index.js +35 -0
  11. package/dist/cjs/package.json +1 -0
  12. package/dist/cjs/providers/_shared/openai-compat.connector.d.ts +23 -0
  13. package/dist/cjs/providers/_shared/openai-compat.connector.js +320 -0
  14. package/dist/cjs/providers/_shared/openai-compat.embeddings.connector.d.ts +18 -0
  15. package/dist/cjs/providers/_shared/openai-compat.embeddings.connector.js +105 -0
  16. package/dist/cjs/providers/_shared/spec.d.ts +99 -0
  17. package/dist/cjs/providers/_shared/spec.js +84 -0
  18. package/dist/cjs/providers/anthropic/anthropic.config.d.ts +17 -0
  19. package/dist/cjs/providers/anthropic/anthropic.config.js +2 -0
  20. package/dist/cjs/providers/anthropic/anthropic.connector.d.ts +30 -0
  21. package/dist/cjs/providers/anthropic/anthropic.connector.js +427 -0
  22. package/dist/cjs/providers/anthropic/index.d.ts +2 -0
  23. package/dist/cjs/providers/anthropic/index.js +5 -0
  24. package/dist/cjs/providers/bedrock/bedrock.config.d.ts +18 -0
  25. package/dist/cjs/providers/bedrock/bedrock.config.js +2 -0
  26. package/dist/cjs/providers/bedrock/bedrock.connector.d.ts +61 -0
  27. package/dist/cjs/providers/bedrock/bedrock.connector.js +479 -0
  28. package/dist/cjs/providers/bedrock/index.d.ts +2 -0
  29. package/dist/cjs/providers/bedrock/index.js +5 -0
  30. package/dist/cjs/providers/gemini/gemini.config.d.ts +12 -0
  31. package/dist/cjs/providers/gemini/gemini.config.js +2 -0
  32. package/dist/cjs/providers/gemini/gemini.connector.d.ts +28 -0
  33. package/dist/cjs/providers/gemini/gemini.connector.js +434 -0
  34. package/dist/cjs/providers/gemini/index.d.ts +2 -0
  35. package/dist/cjs/providers/gemini/index.js +5 -0
  36. package/dist/cjs/types/chat.types.d.ts +115 -0
  37. package/dist/cjs/types/chat.types.js +2 -0
  38. package/dist/cjs/types/config-map.type.d.ts +29 -0
  39. package/dist/cjs/types/config-map.type.js +2 -0
  40. package/dist/cjs/types/embeddings.types.d.ts +29 -0
  41. package/dist/cjs/types/embeddings.types.js +2 -0
  42. package/dist/cjs/types/error.types.d.ts +20 -0
  43. package/dist/cjs/types/error.types.js +22 -0
  44. package/dist/cjs/types/index.d.ts +7 -0
  45. package/dist/cjs/types/index.js +23 -0
  46. package/dist/cjs/types/passthrough.type.d.ts +15 -0
  47. package/dist/cjs/types/passthrough.type.js +2 -0
  48. package/dist/cjs/types/provider-id.enum.d.ts +8 -0
  49. package/dist/cjs/types/provider-id.enum.js +29 -0
  50. package/dist/cjs/types/provider.interface.d.ts +7 -0
  51. package/dist/cjs/types/provider.interface.js +2 -0
  52. package/dist/esm/base/base.connector.d.ts +44 -0
  53. package/dist/esm/base/base.connector.js +209 -0
  54. package/dist/esm/facades/chat.facade.d.ts +17 -0
  55. package/dist/esm/facades/chat.facade.js +59 -0
  56. package/dist/esm/facades/embeddings.facade.d.ts +23 -0
  57. package/dist/esm/facades/embeddings.facade.js +59 -0
  58. package/dist/esm/index.d.ts +15 -0
  59. package/dist/esm/index.js +9 -0
  60. package/dist/esm/package.json +1 -0
  61. package/dist/esm/providers/_shared/openai-compat.connector.d.ts +23 -0
  62. package/dist/esm/providers/_shared/openai-compat.connector.js +316 -0
  63. package/dist/esm/providers/_shared/openai-compat.embeddings.connector.d.ts +18 -0
  64. package/dist/esm/providers/_shared/openai-compat.embeddings.connector.js +101 -0
  65. package/dist/esm/providers/_shared/spec.d.ts +99 -0
  66. package/dist/esm/providers/_shared/spec.js +81 -0
  67. package/dist/esm/providers/anthropic/anthropic.config.d.ts +17 -0
  68. package/dist/esm/providers/anthropic/anthropic.config.js +1 -0
  69. package/dist/esm/providers/anthropic/anthropic.connector.d.ts +30 -0
  70. package/dist/esm/providers/anthropic/anthropic.connector.js +423 -0
  71. package/dist/esm/providers/anthropic/index.d.ts +2 -0
  72. package/dist/esm/providers/anthropic/index.js +1 -0
  73. package/dist/esm/providers/bedrock/bedrock.config.d.ts +18 -0
  74. package/dist/esm/providers/bedrock/bedrock.config.js +1 -0
  75. package/dist/esm/providers/bedrock/bedrock.connector.d.ts +61 -0
  76. package/dist/esm/providers/bedrock/bedrock.connector.js +474 -0
  77. package/dist/esm/providers/bedrock/index.d.ts +2 -0
  78. package/dist/esm/providers/bedrock/index.js +1 -0
  79. package/dist/esm/providers/gemini/gemini.config.d.ts +12 -0
  80. package/dist/esm/providers/gemini/gemini.config.js +1 -0
  81. package/dist/esm/providers/gemini/gemini.connector.d.ts +28 -0
  82. package/dist/esm/providers/gemini/gemini.connector.js +430 -0
  83. package/dist/esm/providers/gemini/index.d.ts +2 -0
  84. package/dist/esm/providers/gemini/index.js +1 -0
  85. package/dist/esm/types/chat.types.d.ts +115 -0
  86. package/dist/esm/types/chat.types.js +1 -0
  87. package/dist/esm/types/config-map.type.d.ts +29 -0
  88. package/dist/esm/types/config-map.type.js +1 -0
  89. package/dist/esm/types/embeddings.types.d.ts +29 -0
  90. package/dist/esm/types/embeddings.types.js +1 -0
  91. package/dist/esm/types/error.types.d.ts +20 -0
  92. package/dist/esm/types/error.types.js +18 -0
  93. package/dist/esm/types/index.d.ts +7 -0
  94. package/dist/esm/types/index.js +7 -0
  95. package/dist/esm/types/passthrough.type.d.ts +15 -0
  96. package/dist/esm/types/passthrough.type.js +1 -0
  97. package/dist/esm/types/provider-id.enum.d.ts +8 -0
  98. package/dist/esm/types/provider-id.enum.js +26 -0
  99. package/dist/esm/types/provider.interface.d.ts +7 -0
  100. package/dist/esm/types/provider.interface.js +1 -0
  101. package/package.json +73 -0
  102. package/src/providers/anthropic/README.md +64 -0
  103. package/src/providers/azure-openai/README.md +62 -0
  104. package/src/providers/bedrock/README.md +71 -0
  105. package/src/providers/cloudflare/README.md +62 -0
  106. package/src/providers/deepinfra/README.md +61 -0
  107. package/src/providers/deepseek/README.md +50 -0
  108. package/src/providers/fireworks/README.md +62 -0
  109. package/src/providers/gemini/README.md +67 -0
  110. package/src/providers/groq/README.md +50 -0
  111. package/src/providers/lmstudio/README.md +62 -0
  112. package/src/providers/mistral/README.md +61 -0
  113. package/src/providers/ollama/README.md +62 -0
  114. package/src/providers/openai/README.md +62 -0
  115. package/src/providers/openrouter/README.md +62 -0
  116. package/src/providers/perplexity/README.md +50 -0
  117. package/src/providers/together/README.md +61 -0
  118. package/src/providers/vllm/README.md +62 -0
  119. package/src/providers/xai/README.md +50 -0
@@ -0,0 +1,427 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AnthropicConnector = void 0;
4
+ const base_connector_1 = require("../../base/base.connector");
5
+ const types_1 = require("../../types");
6
+ const DEFAULT_BASE_URL = 'https://api.anthropic.com/v1';
7
+ const DEFAULT_VERSION = '2023-06-01';
8
+ const DEFAULT_MAX_TOKENS = 4096;
9
+ /**
10
+ * Native adapter for the Anthropic **Messages API** (`POST /v1/messages`).
11
+ * Anthropic is structurally different from OpenAI Chat Completions — different
12
+ * auth, a top-level `system`, typed content blocks, tool results carried inside
13
+ * a user turn, `stop_sequences`, and Anthropic-shaped SSE events — so this is a
14
+ * full translation layer, not an OpenAI passthrough. It emits the identical
15
+ * normalized `ChatResult` / `ChatStreamDelta` / `ConnectorError` as every other
16
+ * connector, so it is drop-in swappable via the `Chat` facade.
17
+ *
18
+ * Not normalized here (per D2/D4, surfaced via `raw` or `_passthrough`, never
19
+ * emulated): `responseFormat` (Anthropic has no `response_format` — use
20
+ * tool-based structured output via `_passthrough`); reasoning CoT output
21
+ * (`thinking` blocks land in `raw`); prompt caching (`cache_control` via
22
+ * `_passthrough`, cached-token counts in `raw`).
23
+ */
24
+ class AnthropicConnector extends base_connector_1.BaseConnector {
25
+ constructor(config) {
26
+ super(config.fetch);
27
+ this.id = 'anthropic';
28
+ this.config = config;
29
+ this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '');
30
+ }
31
+ async complete(input) {
32
+ const body = this.buildRequest(input, false);
33
+ const response = await this.sendPostJson((0, base_connector_1.appendPassthroughQuery)(`${this.baseUrl}/messages`, input._passthrough?.query), body, this.authHeaders(input), input.signal);
34
+ if (!response.ok) {
35
+ const errBody = await response.json().catch(() => null);
36
+ throw this.mapVendorError(response.status, errBody, response.headers);
37
+ }
38
+ const json = this.requireDecodedBody((await response.json().catch(() => null)), response.status);
39
+ return this.parseResult(json, input.model);
40
+ }
41
+ async *stream(input) {
42
+ const body = this.buildRequest(input, true);
43
+ const response = await this.invokeFetch((0, base_connector_1.appendPassthroughQuery)(`${this.baseUrl}/messages`, input._passthrough?.query), {
44
+ method: 'POST',
45
+ headers: {
46
+ 'Content-Type': 'application/json',
47
+ Accept: 'text/event-stream',
48
+ ...this.authHeaders(input),
49
+ },
50
+ body: JSON.stringify(body),
51
+ signal: input.signal,
52
+ });
53
+ if (!response.ok) {
54
+ const errBody = await response.json().catch(() => null);
55
+ throw this.mapVendorError(response.status, errBody, response.headers);
56
+ }
57
+ let inputTokens = 0;
58
+ // Anthropic indexes content blocks (text + tool_use interleaved), so the
59
+ // first tool call can arrive at block index 1. Normalize to a 0-based
60
+ // tool-call-relative index to match OpenAI-compat's emitted surface.
61
+ let nextToolIndex = 0;
62
+ const toolIndexByBlock = new Map();
63
+ for await (const raw of (0, base_connector_1.parseSSEStream)(response)) {
64
+ const evt = raw;
65
+ switch (evt.type) {
66
+ case 'error':
67
+ // A provider error emitted mid-stream (e.g. `overloaded_error`) — throw
68
+ // instead of silently ending the stream with no finishReason.
69
+ throw this.mapVendorError(errorEventStatus(evt.error?.type), evt, new Headers());
70
+ case 'message_start':
71
+ inputTokens = evt.message?.usage?.input_tokens ?? 0;
72
+ break;
73
+ case 'content_block_start': {
74
+ const cb = evt.content_block;
75
+ if (cb?.type === 'tool_use') {
76
+ const toolIndex = nextToolIndex++;
77
+ toolIndexByBlock.set(evt.index ?? 0, toolIndex);
78
+ yield {
79
+ toolCallDelta: {
80
+ index: toolIndex,
81
+ ...(cb.id ? { id: cb.id } : {}),
82
+ ...(cb.name ? { functionName: cb.name } : {}),
83
+ },
84
+ raw: evt,
85
+ };
86
+ }
87
+ break;
88
+ }
89
+ case 'content_block_delta': {
90
+ const d = evt.delta;
91
+ if (d?.type === 'text_delta' && typeof d.text === 'string' && d.text.length > 0) {
92
+ yield { contentDelta: d.text, raw: evt };
93
+ }
94
+ else if (d?.type === 'input_json_delta' && typeof d.partial_json === 'string') {
95
+ const toolIndex = toolIndexByBlock.get(evt.index ?? 0) ?? 0;
96
+ yield { toolCallDelta: { index: toolIndex, argumentsDelta: d.partial_json }, raw: evt };
97
+ }
98
+ // thinking_delta → CoT output not normalized (D2); available only via raw event stream
99
+ break;
100
+ }
101
+ case 'message_delta': {
102
+ const out = { raw: evt };
103
+ if (evt.delta?.stop_reason)
104
+ out.finishReason = mapStopReason(evt.delta.stop_reason);
105
+ const outputTokens = evt.usage?.output_tokens ?? 0;
106
+ out.usage = { inputTokens, outputTokens, totalTokens: inputTokens + outputTokens };
107
+ yield out;
108
+ break;
109
+ }
110
+ // message_stop / content_block_stop / ping → ignored
111
+ }
112
+ }
113
+ }
114
+ authHeaders(input) {
115
+ return {
116
+ 'x-api-key': this.config.apiKey,
117
+ 'anthropic-version': this.config.anthropicVersion ?? DEFAULT_VERSION,
118
+ ...this.config.headers,
119
+ ...input._passthrough?.headers,
120
+ };
121
+ }
122
+ buildRequest(input, stream) {
123
+ const { system, messages } = splitMessages(input.messages);
124
+ const thinking = input.reasoning
125
+ ? { type: 'enabled', budget_tokens: effortBudget(input.reasoning.effort) }
126
+ : undefined;
127
+ const budget = thinking ? thinking.budget_tokens : 0;
128
+ const callerSetMax = input.maxOutputTokens !== undefined;
129
+ let maxTokens = input.maxOutputTokens ?? this.config.defaultMaxTokens ?? DEFAULT_MAX_TOKENS;
130
+ if (thinking && maxTokens <= budget) {
131
+ // Anthropic requires max_tokens > thinking.budget_tokens (max_tokens is the
132
+ // TOTAL, thinking included). If the CALLER set an explicit maxOutputTokens
133
+ // below the budget, silently inflating it would spend past their cap — throw
134
+ // so they lower reasoning.effort or raise the cap. Only a non-explicit default
135
+ // is inflated to keep the request valid.
136
+ if (callerSetMax) {
137
+ throw new types_1.ConnectorError({
138
+ message: `maxOutputTokens (${maxTokens}) must be greater than the reasoning budget (${budget}) for extended thinking — raise maxOutputTokens or lower reasoning.effort`,
139
+ statusCode: null,
140
+ providerCode: 'invalid_request',
141
+ });
142
+ }
143
+ maxTokens = budget + DEFAULT_MAX_TOKENS;
144
+ }
145
+ const body = {
146
+ model: input.model,
147
+ max_tokens: maxTokens,
148
+ messages,
149
+ };
150
+ if (system)
151
+ body.system = system;
152
+ if (input.tools && input.tools.length > 0) {
153
+ body.tools = input.tools.map((t) => ({
154
+ name: t.function.name,
155
+ ...(t.function.description ? { description: t.function.description } : {}),
156
+ input_schema: t.function.parameters ?? { type: 'object', properties: {} },
157
+ }));
158
+ }
159
+ if (input.toolChoice !== undefined) {
160
+ const tc = mapToolChoice(input.toolChoice);
161
+ if (tc)
162
+ body.tool_choice = tc;
163
+ }
164
+ if (thinking) {
165
+ // Extended thinking requires temperature=1 and disallows top_p/top_k — omit sampling.
166
+ body.thinking = thinking;
167
+ }
168
+ else {
169
+ if (input.temperature !== undefined)
170
+ body.temperature = input.temperature;
171
+ if (input.topP !== undefined)
172
+ body.top_p = input.topP;
173
+ }
174
+ if (input.stop !== undefined) {
175
+ body.stop_sequences = Array.isArray(input.stop) ? input.stop : [input.stop];
176
+ }
177
+ // NOTE: `responseFormat` is intentionally NOT mapped — Anthropic has no
178
+ // `response_format`; structured output is via tools / `_passthrough`.
179
+ if (stream)
180
+ body.stream = true;
181
+ if (input._passthrough?.body)
182
+ Object.assign(body, input._passthrough.body);
183
+ return body;
184
+ }
185
+ parseResult(json, requestedModel) {
186
+ const blocks = json?.content ?? [];
187
+ let text = '';
188
+ const toolCalls = [];
189
+ for (const b of blocks) {
190
+ if (b.type === 'text' && typeof b.text === 'string') {
191
+ text += b.text;
192
+ }
193
+ else if (b.type === 'tool_use') {
194
+ toolCalls.push({
195
+ id: b.id ?? '',
196
+ type: 'function',
197
+ function: { name: b.name ?? '', arguments: JSON.stringify(b.input ?? {}) },
198
+ });
199
+ }
200
+ // thinking blocks → raw only (D2)
201
+ }
202
+ const inputTokens = json?.usage?.input_tokens ?? 0;
203
+ const outputTokens = json?.usage?.output_tokens ?? 0;
204
+ return {
205
+ message: {
206
+ role: 'assistant',
207
+ content: text.length > 0 ? text : null,
208
+ ...(toolCalls.length > 0 ? { toolCalls } : {}),
209
+ },
210
+ finishReason: mapStopReason(json?.stop_reason),
211
+ usage: { inputTokens, outputTokens, totalTokens: inputTokens + outputTokens },
212
+ model: json?.model ?? requestedModel,
213
+ raw: json,
214
+ };
215
+ }
216
+ mapVendorError(status, body, headers) {
217
+ const message = extractAnthropicError(body) ?? `HTTP ${status}`;
218
+ const providerCode = mapStatusToProviderCode(status, message);
219
+ const retryAfter = headers.get('retry-after');
220
+ const cause = { raw: body ?? null };
221
+ if (retryAfter != null) {
222
+ cause.retryAfter = retryAfter;
223
+ const secs = parseRetryAfterSeconds(retryAfter);
224
+ if (secs != null)
225
+ cause.retryAfterSeconds = secs;
226
+ }
227
+ return new types_1.ConnectorError({
228
+ message,
229
+ statusCode: status,
230
+ providerCode,
231
+ providerMessage: message,
232
+ cause,
233
+ });
234
+ }
235
+ }
236
+ exports.AnthropicConnector = AnthropicConnector;
237
+ // ---------------------------------------------------------------------------
238
+ // Module-private mappers
239
+ // ---------------------------------------------------------------------------
240
+ function splitMessages(messages) {
241
+ let system;
242
+ const out = [];
243
+ for (const m of messages) {
244
+ if (m.role === 'system') {
245
+ const text = contentToText(m.content);
246
+ system = system ? `${system}\n\n${text}` : text;
247
+ continue;
248
+ }
249
+ if (m.role === 'tool') {
250
+ const block = {
251
+ type: 'tool_result',
252
+ tool_use_id: m.toolCallId ?? '',
253
+ content: contentToText(m.content),
254
+ };
255
+ const last = out[out.length - 1];
256
+ if (last && last.role === 'user' && isToolResultContent(last.content)) {
257
+ last.content.push(block);
258
+ }
259
+ else {
260
+ out.push({ role: 'user', content: [block] });
261
+ }
262
+ continue;
263
+ }
264
+ out.push({ role: m.role, content: mapContentBlocks(m) });
265
+ }
266
+ return { system, messages: out };
267
+ }
268
+ function contentToText(content) {
269
+ if (content === null)
270
+ return '';
271
+ if (typeof content === 'string')
272
+ return content;
273
+ return content
274
+ .filter((p) => p.type === 'text')
275
+ .map((p) => p.text)
276
+ .join('');
277
+ }
278
+ function mapContentBlocks(m) {
279
+ const blocks = [];
280
+ if (typeof m.content === 'string') {
281
+ if (m.content.length > 0)
282
+ blocks.push({ type: 'text', text: m.content });
283
+ }
284
+ else if (Array.isArray(m.content)) {
285
+ for (const p of m.content) {
286
+ if (p.type === 'text') {
287
+ blocks.push({ type: 'text', text: p.text });
288
+ }
289
+ else {
290
+ blocks.push({
291
+ type: 'image',
292
+ source: { type: 'base64', media_type: p.mediaType, data: p.base64 },
293
+ });
294
+ }
295
+ }
296
+ }
297
+ if (m.role === 'assistant' && m.toolCalls) {
298
+ for (const tc of m.toolCalls) {
299
+ blocks.push({
300
+ type: 'tool_use',
301
+ id: tc.id,
302
+ name: tc.function.name,
303
+ input: safeJsonParse(tc.function.arguments),
304
+ });
305
+ }
306
+ }
307
+ if (blocks.length === 0)
308
+ blocks.push({ type: 'text', text: '' });
309
+ return blocks;
310
+ }
311
+ function isToolResultContent(content) {
312
+ return (Array.isArray(content) &&
313
+ content.length > 0 &&
314
+ typeof content[0] === 'object' &&
315
+ content[0] !== null &&
316
+ content[0].type === 'tool_result');
317
+ }
318
+ function mapToolChoice(tc) {
319
+ if (tc === 'auto')
320
+ return { type: 'auto' };
321
+ if (tc === 'required')
322
+ return { type: 'any' };
323
+ if (tc === 'none')
324
+ return { type: 'none' };
325
+ if (typeof tc === 'object' && tc.type === 'function') {
326
+ return { type: 'tool', name: tc.function.name };
327
+ }
328
+ return null;
329
+ }
330
+ function mapStopReason(sr) {
331
+ switch (sr) {
332
+ case 'end_turn':
333
+ case 'stop_sequence':
334
+ return 'stop';
335
+ case 'max_tokens':
336
+ return 'length';
337
+ case 'tool_use':
338
+ return 'tool_calls';
339
+ case 'refusal':
340
+ return 'content_filter';
341
+ default:
342
+ return 'unknown';
343
+ }
344
+ }
345
+ function effortBudget(effort) {
346
+ switch (effort) {
347
+ case 'low':
348
+ return 1024;
349
+ case 'medium':
350
+ return 4096;
351
+ case 'high':
352
+ return 12288;
353
+ }
354
+ }
355
+ function safeJsonParse(s) {
356
+ try {
357
+ return JSON.parse(s);
358
+ }
359
+ catch {
360
+ return {};
361
+ }
362
+ }
363
+ function extractAnthropicError(body) {
364
+ if (typeof body === 'string')
365
+ return body;
366
+ if (!body || typeof body !== 'object')
367
+ return null;
368
+ const b = body;
369
+ if (b.error && typeof b.error === 'object') {
370
+ const em = b.error.message;
371
+ if (typeof em === 'string')
372
+ return em;
373
+ }
374
+ if (typeof b.message === 'string')
375
+ return b.message;
376
+ return null;
377
+ }
378
+ /** Map an Anthropic SSE `error` event's `type` to the HTTP status its non-stream path would carry. */
379
+ function errorEventStatus(type) {
380
+ switch (type) {
381
+ case 'authentication_error':
382
+ return 401;
383
+ case 'permission_error':
384
+ return 403;
385
+ case 'not_found_error':
386
+ return 404;
387
+ case 'rate_limit_error':
388
+ return 429;
389
+ case 'invalid_request_error':
390
+ return 400;
391
+ case 'overloaded_error':
392
+ return 529;
393
+ default:
394
+ return 500;
395
+ }
396
+ }
397
+ function mapStatusToProviderCode(status, message) {
398
+ if (status === 401 || status === 403)
399
+ return 'auth_failed';
400
+ if (status === 429)
401
+ return 'rate_limited';
402
+ // Any 5xx is a provider-side failure. A fixed {500,502,503,529} whitelist
403
+ // dropped 504/520/522/524 (gateway / Cloudflare-origin) to unknown, so a
404
+ // consumer's retry policy wouldn't fire on them.
405
+ if (status >= 500) {
406
+ return 'provider_unavailable';
407
+ }
408
+ if (status === 400 || status === 404 || status === 413 || status === 422) {
409
+ const msg = message.toLowerCase();
410
+ if (msg.includes('too long') || msg.includes('context') || msg.includes('max_tokens')) {
411
+ return 'context_length_exceeded';
412
+ }
413
+ return 'invalid_request';
414
+ }
415
+ return 'unknown';
416
+ }
417
+ function parseRetryAfterSeconds(header) {
418
+ const asNum = Number(header);
419
+ if (Number.isFinite(asNum))
420
+ return asNum;
421
+ const asDate = Date.parse(header);
422
+ if (!Number.isNaN(asDate)) {
423
+ const secs = Math.round((asDate - Date.now()) / 1000);
424
+ return secs >= 0 ? secs : 0;
425
+ }
426
+ return null;
427
+ }
@@ -0,0 +1,2 @@
1
+ export { AnthropicConnector } from './anthropic.connector';
2
+ export type { AnthropicConfig } from './anthropic.config';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AnthropicConnector = void 0;
4
+ var anthropic_connector_1 = require("./anthropic.connector");
5
+ Object.defineProperty(exports, "AnthropicConnector", { enumerable: true, get: function () { return anthropic_connector_1.AnthropicConnector; } });
@@ -0,0 +1,18 @@
1
+ export interface BedrockConfig {
2
+ /** AWS region, e.g. `us-east-1`. No environment inference. */
3
+ region: string;
4
+ /** AWS access key id. */
5
+ accessKeyId: string;
6
+ /** AWS secret access key. */
7
+ secretAccessKey: string;
8
+ /** Optional STS session token (temporary credentials). */
9
+ sessionToken?: string;
10
+ /** Override origin. Default `https://bedrock-runtime.<region>.amazonaws.com`. */
11
+ baseUrl?: string;
12
+ /** Converse `inferenceConfig.maxTokens` used when `ChatInput.maxOutputTokens` is omitted. Default 4096. */
13
+ defaultMaxTokens?: number;
14
+ /** Bring-your-own fetch. Defaults to `globalThis.fetch`. */
15
+ fetch?: typeof fetch;
16
+ /** Extra headers merged onto every request (participate in the SigV4 signature). */
17
+ headers?: Record<string, string>;
18
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,61 @@
1
+ import { BaseConnector } from '../../base/base.connector';
2
+ import type { ChatInput, ChatResult, ChatStreamDelta, IChatConnector } from '../../types';
3
+ import type { BedrockConfig } from './bedrock.config';
4
+ /**
5
+ * Native adapter for AWS Bedrock's unified **Converse API**
6
+ * (`POST /model/{modelId}/converse`). Requests are signed with hand-rolled AWS
7
+ * SigV4 on `node:crypto` (zero third-party deps — same approach as the
8
+ * notifications SES/SNS connectors). Converse is structurally different from
9
+ * OpenAI (top-level `system[]`, typed content blocks, `inferenceConfig`,
10
+ * `toolConfig`, tool results in a user turn), so this is a full translation
11
+ * layer emitting the identical normalized `ChatResult` / `ChatStreamDelta` /
12
+ * `ConnectorError`.
13
+ *
14
+ * v1 streaming NOTE: Bedrock's `converse-stream` uses AWS's binary
15
+ * event-stream framing (`application/vnd.amazon.eventstream`), not SSE. v1 does
16
+ * NOT parse that binary protocol — `stream()` falls back to a single
17
+ * non-incremental Converse call and yields the full result as deltas. True
18
+ * incremental streaming (a binary event-stream parser) is a documented
19
+ * follow-up.
20
+ *
21
+ * Not normalized here (passthrough/raw, never emulated): reasoning control
22
+ * (model-dependent on Bedrock — pass `additionalModelRequestFields` via
23
+ * `_passthrough.body`), `responseFormat`, prompt caching (`cachePoint` blocks
24
+ * via `_passthrough`), and `top_k`.
25
+ */
26
+ export declare class BedrockConnector extends BaseConnector implements IChatConnector {
27
+ readonly id = "bedrock";
28
+ private readonly config;
29
+ private readonly origin;
30
+ private readonly host;
31
+ constructor(config: BedrockConfig);
32
+ complete(input: ChatInput): Promise<ChatResult>;
33
+ /**
34
+ * v1 fallback: non-incremental. See the class NOTE — Bedrock streaming is a
35
+ * binary event-stream, not SSE; v1 issues one Converse call and yields the
36
+ * result as a content delta + tool-call deltas + a terminal finish/usage delta.
37
+ */
38
+ stream(input: ChatInput): AsyncGenerator<ChatStreamDelta>;
39
+ private buildSignedHeaders;
40
+ private buildRequest;
41
+ private parseResult;
42
+ private mapVendorError;
43
+ }
44
+ /**
45
+ * Hand-rolled AWS SigV4 signer (exported for white-box testing of the
46
+ * canonical-path encoding; NOT re-exported from the package barrel).
47
+ */
48
+ export declare function signAwsRequest(opts: {
49
+ method: string;
50
+ path: string;
51
+ service: string;
52
+ region: string;
53
+ host: string;
54
+ accessKeyId: string;
55
+ secretAccessKey: string;
56
+ sessionToken?: string;
57
+ serializedBody: string;
58
+ additionalSignedHeaders: Record<string, string>;
59
+ isoTimestamp: string;
60
+ canonicalQuery?: string;
61
+ }): Record<string, string>;