@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,479 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BedrockConnector = void 0;
4
+ exports.signAwsRequest = signAwsRequest;
5
+ const node_crypto_1 = require("node:crypto");
6
+ const base_connector_1 = require("../../base/base.connector");
7
+ const types_1 = require("../../types");
8
+ const DEFAULT_MAX_TOKENS = 4096;
9
+ const SERVICE = 'bedrock';
10
+ /**
11
+ * Native adapter for AWS Bedrock's unified **Converse API**
12
+ * (`POST /model/{modelId}/converse`). Requests are signed with hand-rolled AWS
13
+ * SigV4 on `node:crypto` (zero third-party deps — same approach as the
14
+ * notifications SES/SNS connectors). Converse is structurally different from
15
+ * OpenAI (top-level `system[]`, typed content blocks, `inferenceConfig`,
16
+ * `toolConfig`, tool results in a user turn), so this is a full translation
17
+ * layer emitting the identical normalized `ChatResult` / `ChatStreamDelta` /
18
+ * `ConnectorError`.
19
+ *
20
+ * v1 streaming NOTE: Bedrock's `converse-stream` uses AWS's binary
21
+ * event-stream framing (`application/vnd.amazon.eventstream`), not SSE. v1 does
22
+ * NOT parse that binary protocol — `stream()` falls back to a single
23
+ * non-incremental Converse call and yields the full result as deltas. True
24
+ * incremental streaming (a binary event-stream parser) is a documented
25
+ * follow-up.
26
+ *
27
+ * Not normalized here (passthrough/raw, never emulated): reasoning control
28
+ * (model-dependent on Bedrock — pass `additionalModelRequestFields` via
29
+ * `_passthrough.body`), `responseFormat`, prompt caching (`cachePoint` blocks
30
+ * via `_passthrough`), and `top_k`.
31
+ */
32
+ class BedrockConnector extends base_connector_1.BaseConnector {
33
+ constructor(config) {
34
+ super(config.fetch);
35
+ this.id = 'bedrock';
36
+ this.config = config;
37
+ this.origin = (config.baseUrl ?? `https://bedrock-runtime.${config.region}.amazonaws.com`).replace(/\/+$/, '');
38
+ try {
39
+ this.host = new URL(this.origin).host;
40
+ }
41
+ catch {
42
+ throw new types_1.ConnectorError({
43
+ message: `Bedrock: invalid \`baseUrl\`/\`region\` — could not derive a host from '${this.origin}'`,
44
+ statusCode: null,
45
+ providerCode: 'invalid_request',
46
+ });
47
+ }
48
+ }
49
+ async complete(input) {
50
+ const serialized = JSON.stringify(this.buildRequest(input));
51
+ // The wire path is single-encoded, but the SigV4 canonical URI must be
52
+ // DOUBLE-encoded: every non-S3 AWS service URI-encodes the received path a
53
+ // second time before recomputing the signature, so a model id containing `:`
54
+ // (all on-demand IDs like `…-v2:0`, cross-region profiles `us.…-v2:0`, ARNs)
55
+ // must be signed as `%253A` while `%3A` travels on the wire — otherwise the
56
+ // server's signature never matches ours → 403 SignatureDoesNotMatch. For a
57
+ // colon-free id the two are identical, so nothing regresses.
58
+ const wirePath = `/model/${encodeURIComponent(input.model)}/converse`;
59
+ const canonicalPath = `/model/${encodeURIComponent(encodeURIComponent(input.model))}/converse`;
60
+ // `_passthrough.query` must be folded into the SigV4 canonical query BEFORE
61
+ // signing (the query string is part of the signed canonical request); the
62
+ // same canonical form then travels on the wire, so the server's recomputed
63
+ // signature matches. Appending it post-signing would 403 SignatureDoesNotMatch.
64
+ const canonicalQuery = buildCanonicalQuery(input._passthrough?.query);
65
+ const headers = this.buildSignedHeaders('POST', canonicalPath, serialized, input, canonicalQuery);
66
+ const wireUrl = `${this.origin}${wirePath}${canonicalQuery ? `?${canonicalQuery}` : ''}`;
67
+ const response = await this.invokeFetch(wireUrl, {
68
+ method: 'POST',
69
+ headers,
70
+ body: serialized,
71
+ signal: input.signal,
72
+ });
73
+ if (!response.ok) {
74
+ const errBody = await response.json().catch(() => null);
75
+ throw this.mapVendorError(response.status, errBody, response.headers);
76
+ }
77
+ const json = this.requireDecodedBody((await response.json().catch(() => null)), response.status);
78
+ return this.parseResult(json, input.model);
79
+ }
80
+ /**
81
+ * v1 fallback: non-incremental. See the class NOTE — Bedrock streaming is a
82
+ * binary event-stream, not SSE; v1 issues one Converse call and yields the
83
+ * result as a content delta + tool-call deltas + a terminal finish/usage delta.
84
+ */
85
+ async *stream(input) {
86
+ const result = await this.complete(input);
87
+ if (result.message.content)
88
+ yield { contentDelta: result.message.content };
89
+ if (result.message.toolCalls) {
90
+ let index = 0;
91
+ for (const tc of result.message.toolCalls) {
92
+ yield {
93
+ toolCallDelta: {
94
+ index,
95
+ id: tc.id,
96
+ functionName: tc.function.name,
97
+ argumentsDelta: tc.function.arguments,
98
+ },
99
+ };
100
+ index++;
101
+ }
102
+ }
103
+ yield { finishReason: result.finishReason, usage: result.usage, raw: result.raw };
104
+ }
105
+ buildSignedHeaders(method, canonicalPath, serializedBody, input, canonicalQuery) {
106
+ const extra = {
107
+ 'Content-Type': 'application/json',
108
+ ...this.config.headers,
109
+ ...input._passthrough?.headers,
110
+ };
111
+ const signed = signAwsRequest({
112
+ method,
113
+ path: canonicalPath,
114
+ service: SERVICE,
115
+ region: this.config.region,
116
+ host: this.host,
117
+ accessKeyId: this.config.accessKeyId,
118
+ secretAccessKey: this.config.secretAccessKey,
119
+ sessionToken: this.config.sessionToken,
120
+ serializedBody,
121
+ additionalSignedHeaders: extra,
122
+ isoTimestamp: isoBasicTimestamp(),
123
+ canonicalQuery,
124
+ });
125
+ return { ...extra, ...signed };
126
+ }
127
+ buildRequest(input) {
128
+ const { system, messages } = splitMessages(input.messages);
129
+ const body = { messages };
130
+ if (system.length > 0)
131
+ body.system = system;
132
+ const inferenceConfig = {
133
+ maxTokens: input.maxOutputTokens ?? this.config.defaultMaxTokens ?? DEFAULT_MAX_TOKENS,
134
+ };
135
+ if (input.temperature !== undefined)
136
+ inferenceConfig.temperature = input.temperature;
137
+ if (input.topP !== undefined)
138
+ inferenceConfig.topP = input.topP;
139
+ if (input.stop !== undefined) {
140
+ inferenceConfig.stopSequences = Array.isArray(input.stop) ? input.stop : [input.stop];
141
+ }
142
+ body.inferenceConfig = inferenceConfig;
143
+ if (input.tools && input.tools.length > 0) {
144
+ const toolConfig = {
145
+ tools: input.tools.map((t) => ({
146
+ toolSpec: {
147
+ name: t.function.name,
148
+ ...(t.function.description ? { description: t.function.description } : {}),
149
+ inputSchema: { json: t.function.parameters ?? { type: 'object', properties: {} } },
150
+ },
151
+ })),
152
+ };
153
+ const tc = mapToolChoice(input.toolChoice);
154
+ if (tc)
155
+ toolConfig.toolChoice = tc;
156
+ body.toolConfig = toolConfig;
157
+ }
158
+ // reasoning control is model-dependent on Bedrock → not auto-mapped in v1;
159
+ // pass `additionalModelRequestFields` via `_passthrough.body`.
160
+ if (input._passthrough?.body)
161
+ Object.assign(body, input._passthrough.body);
162
+ return body;
163
+ }
164
+ parseResult(json, requestedModel) {
165
+ const blocks = json?.output?.message?.content ?? [];
166
+ let text = '';
167
+ const toolCalls = [];
168
+ for (const b of blocks) {
169
+ if (typeof b.text === 'string') {
170
+ text += b.text;
171
+ }
172
+ else if (b.toolUse) {
173
+ toolCalls.push({
174
+ id: b.toolUse.toolUseId ?? '',
175
+ type: 'function',
176
+ function: { name: b.toolUse.name ?? '', arguments: JSON.stringify(b.toolUse.input ?? {}) },
177
+ });
178
+ }
179
+ }
180
+ const inputTokens = json?.usage?.inputTokens ?? 0;
181
+ const outputTokens = json?.usage?.outputTokens ?? 0;
182
+ return {
183
+ message: {
184
+ role: 'assistant',
185
+ content: text.length > 0 ? text : null,
186
+ ...(toolCalls.length > 0 ? { toolCalls } : {}),
187
+ },
188
+ finishReason: mapStopReason(json?.stopReason),
189
+ usage: {
190
+ inputTokens,
191
+ outputTokens,
192
+ totalTokens: json?.usage?.totalTokens ?? inputTokens + outputTokens,
193
+ },
194
+ model: requestedModel,
195
+ raw: json,
196
+ };
197
+ }
198
+ mapVendorError(status, body, headers) {
199
+ const message = extractBedrockError(body) ?? `HTTP ${status}`;
200
+ const providerCode = mapStatusToProviderCode(status, message);
201
+ const retryAfter = headers.get('retry-after');
202
+ const cause = { raw: body ?? null };
203
+ if (retryAfter != null) {
204
+ cause.retryAfter = retryAfter;
205
+ const secs = parseRetryAfterSeconds(retryAfter);
206
+ if (secs != null)
207
+ cause.retryAfterSeconds = secs;
208
+ }
209
+ return new types_1.ConnectorError({
210
+ message,
211
+ statusCode: status,
212
+ providerCode,
213
+ providerMessage: message,
214
+ cause,
215
+ });
216
+ }
217
+ }
218
+ exports.BedrockConnector = BedrockConnector;
219
+ // ---------------------------------------------------------------------------
220
+ // Hand-rolled AWS SigV4 (node:crypto; adapted from the notifications SES signer)
221
+ // ---------------------------------------------------------------------------
222
+ const UNSIGNABLE_HEADERS = new Set([
223
+ 'authorization',
224
+ 'cache-control',
225
+ 'connection',
226
+ 'content-length',
227
+ 'expect',
228
+ 'from',
229
+ 'keep-alive',
230
+ 'max-forwards',
231
+ 'pragma',
232
+ 'proxy-authenticate',
233
+ 'proxy-authorization',
234
+ 'referer',
235
+ 'te',
236
+ 'trailer',
237
+ 'transfer-encoding',
238
+ 'upgrade',
239
+ 'user-agent',
240
+ 'x-amzn-trace-id',
241
+ ]);
242
+ /**
243
+ * Hand-rolled AWS SigV4 signer (exported for white-box testing of the
244
+ * canonical-path encoding; NOT re-exported from the package barrel).
245
+ */
246
+ function signAwsRequest(opts) {
247
+ const canonicalQuery = opts.canonicalQuery ?? '';
248
+ const dateStamp = opts.isoTimestamp.slice(0, 8);
249
+ const hashedPayload = (0, node_crypto_1.createHash)('sha256').update(opts.serializedBody).digest('hex');
250
+ const canonicalHeaders = {
251
+ ...opts.additionalSignedHeaders,
252
+ Host: opts.host,
253
+ 'X-Amz-Date': opts.isoTimestamp,
254
+ 'X-Amz-Content-Sha256': hashedPayload,
255
+ };
256
+ if (opts.sessionToken)
257
+ canonicalHeaders['X-Amz-Security-Token'] = opts.sessionToken;
258
+ const normalized = {};
259
+ for (const [name, value] of Object.entries(canonicalHeaders)) {
260
+ const lowerName = name.toLowerCase();
261
+ if (UNSIGNABLE_HEADERS.has(lowerName))
262
+ continue;
263
+ normalized[lowerName] = String(value).trim().replace(/\s+/g, ' ');
264
+ }
265
+ const sortedNames = Object.keys(normalized).sort();
266
+ const canonicalHeadersString = sortedNames.map((name) => `${name}:${normalized[name]}\n`).join('');
267
+ const signedHeadersList = sortedNames.join(';');
268
+ const canonicalRequest = `${opts.method}\n${opts.path}\n${canonicalQuery}\n${canonicalHeadersString}\n${signedHeadersList}\n${hashedPayload}`;
269
+ const credentialScope = `${dateStamp}/${opts.region}/${opts.service}/aws4_request`;
270
+ const stringToSign = `AWS4-HMAC-SHA256\n${opts.isoTimestamp}\n${credentialScope}\n` +
271
+ (0, node_crypto_1.createHash)('sha256').update(canonicalRequest).digest('hex');
272
+ const hmac = (key, data) => (0, node_crypto_1.createHmac)('sha256', key).update(data).digest();
273
+ const kDate = hmac('AWS4' + opts.secretAccessKey, dateStamp);
274
+ const kRegion = hmac(kDate, opts.region);
275
+ const kService = hmac(kRegion, opts.service);
276
+ const kSigning = hmac(kService, 'aws4_request');
277
+ const signature = (0, node_crypto_1.createHmac)('sha256', kSigning).update(stringToSign).digest('hex');
278
+ const authorization = `AWS4-HMAC-SHA256 Credential=${opts.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeadersList}, Signature=${signature}`;
279
+ const out = {
280
+ Host: opts.host,
281
+ 'X-Amz-Date': opts.isoTimestamp,
282
+ 'X-Amz-Content-Sha256': hashedPayload,
283
+ Authorization: authorization,
284
+ };
285
+ if (opts.sessionToken)
286
+ out['X-Amz-Security-Token'] = opts.sessionToken;
287
+ return out;
288
+ }
289
+ function isoBasicTimestamp() {
290
+ return new Date().toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, '');
291
+ }
292
+ /**
293
+ * RFC-3986 percent-encode a query key/value for the SigV4 canonical query
294
+ * string. `encodeURIComponent` leaves `!'()*` unescaped, but SigV4's unreserved
295
+ * set is exactly `A-Za-z0-9-_.~`, so those must also be escaped or the
296
+ * signature won't match.
297
+ */
298
+ function awsUriEncodeComponent(s) {
299
+ return encodeURIComponent(s).replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase());
300
+ }
301
+ /**
302
+ * Build the SigV4 canonical query string from `_passthrough.query`: encode each
303
+ * key/value, then sort by encoded key (then value). The same string is both
304
+ * signed and sent on the wire. Returns '' when there are no params.
305
+ */
306
+ function buildCanonicalQuery(query) {
307
+ if (!query)
308
+ return '';
309
+ const pairs = Object.entries(query).map(([k, v]) => [awsUriEncodeComponent(k), awsUriEncodeComponent(v)]);
310
+ if (pairs.length === 0)
311
+ return '';
312
+ pairs.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0));
313
+ return pairs.map(([k, v]) => `${k}=${v}`).join('&');
314
+ }
315
+ // ---------------------------------------------------------------------------
316
+ // Converse mappers
317
+ // ---------------------------------------------------------------------------
318
+ function splitMessages(messages) {
319
+ const system = [];
320
+ const out = [];
321
+ for (const m of messages) {
322
+ if (m.role === 'system') {
323
+ const t = contentToText(m.content);
324
+ if (t)
325
+ system.push({ text: t });
326
+ continue;
327
+ }
328
+ if (m.role === 'tool') {
329
+ const block = {
330
+ toolResult: { toolUseId: m.toolCallId ?? '', content: [{ text: contentToText(m.content) }] },
331
+ };
332
+ const last = out[out.length - 1];
333
+ if (last && last.role === 'user' && isToolResultContent(last.content)) {
334
+ last.content.push(block);
335
+ }
336
+ else {
337
+ out.push({ role: 'user', content: [block] });
338
+ }
339
+ continue;
340
+ }
341
+ out.push({ role: m.role, content: mapContentBlocks(m) });
342
+ }
343
+ return { system, messages: out };
344
+ }
345
+ function contentToText(content) {
346
+ if (content === null)
347
+ return '';
348
+ if (typeof content === 'string')
349
+ return content;
350
+ return content
351
+ .filter((p) => p.type === 'text')
352
+ .map((p) => p.text)
353
+ .join('');
354
+ }
355
+ function mapContentBlocks(m) {
356
+ const blocks = [];
357
+ if (typeof m.content === 'string') {
358
+ if (m.content.length > 0)
359
+ blocks.push({ text: m.content });
360
+ }
361
+ else if (Array.isArray(m.content)) {
362
+ for (const p of m.content) {
363
+ if (p.type === 'text') {
364
+ blocks.push({ text: p.text });
365
+ }
366
+ else {
367
+ blocks.push({ image: { format: imageFormat(p.mediaType), source: { bytes: p.base64 } } });
368
+ }
369
+ }
370
+ }
371
+ if (m.role === 'assistant' && m.toolCalls) {
372
+ for (const tc of m.toolCalls) {
373
+ blocks.push({
374
+ toolUse: { toolUseId: tc.id, name: tc.function.name, input: safeJsonParse(tc.function.arguments) },
375
+ });
376
+ }
377
+ }
378
+ if (blocks.length === 0)
379
+ blocks.push({ text: '' });
380
+ return blocks;
381
+ }
382
+ function isToolResultContent(content) {
383
+ return (content.length > 0 &&
384
+ typeof content[0] === 'object' &&
385
+ content[0] !== null &&
386
+ 'toolResult' in content[0]);
387
+ }
388
+ function mapToolChoice(tc) {
389
+ if (tc === undefined)
390
+ return null;
391
+ if (tc === 'auto')
392
+ return { auto: {} };
393
+ if (tc === 'required')
394
+ return { any: {} };
395
+ if (tc === 'none')
396
+ return null; // Bedrock has no "none"; omit toolChoice
397
+ if (typeof tc === 'object' && tc.type === 'function')
398
+ return { tool: { name: tc.function.name } };
399
+ return null;
400
+ }
401
+ function mapStopReason(sr) {
402
+ switch (sr) {
403
+ case 'end_turn':
404
+ case 'stop_sequence':
405
+ return 'stop';
406
+ case 'max_tokens':
407
+ return 'length';
408
+ case 'tool_use':
409
+ return 'tool_calls';
410
+ case 'content_filtered':
411
+ case 'guardrail_intervened':
412
+ return 'content_filter';
413
+ default:
414
+ return 'unknown';
415
+ }
416
+ }
417
+ function imageFormat(mediaType) {
418
+ const m = mediaType.toLowerCase();
419
+ if (m.includes('png'))
420
+ return 'png';
421
+ if (m.includes('jpeg') || m.includes('jpg'))
422
+ return 'jpeg';
423
+ if (m.includes('gif'))
424
+ return 'gif';
425
+ if (m.includes('webp'))
426
+ return 'webp';
427
+ return 'png';
428
+ }
429
+ function safeJsonParse(s) {
430
+ try {
431
+ return JSON.parse(s);
432
+ }
433
+ catch {
434
+ return {};
435
+ }
436
+ }
437
+ function extractBedrockError(body) {
438
+ if (typeof body === 'string')
439
+ return body;
440
+ if (!body || typeof body !== 'object')
441
+ return null;
442
+ const b = body;
443
+ if (typeof b.message === 'string')
444
+ return b.message;
445
+ if (typeof b.Message === 'string')
446
+ return b.Message;
447
+ if (typeof b.__type === 'string')
448
+ return b.__type;
449
+ return null;
450
+ }
451
+ function mapStatusToProviderCode(status, message) {
452
+ if (status === 401 || status === 403)
453
+ return 'auth_failed';
454
+ if (status === 429)
455
+ return 'rate_limited';
456
+ if (status >= 500)
457
+ return 'provider_unavailable';
458
+ if (status === 400 || status === 404 || status === 422) {
459
+ const msg = message.toLowerCase();
460
+ if (msg.includes('throttl'))
461
+ return 'rate_limited';
462
+ if (msg.includes('too long') || msg.includes('context') || msg.includes('token') || msg.includes('max')) {
463
+ return 'context_length_exceeded';
464
+ }
465
+ return 'invalid_request';
466
+ }
467
+ return 'unknown';
468
+ }
469
+ function parseRetryAfterSeconds(header) {
470
+ const asNum = Number(header);
471
+ if (Number.isFinite(asNum))
472
+ return asNum;
473
+ const asDate = Date.parse(header);
474
+ if (!Number.isNaN(asDate)) {
475
+ const secs = Math.round((asDate - Date.now()) / 1000);
476
+ return secs >= 0 ? secs : 0;
477
+ }
478
+ return null;
479
+ }
@@ -0,0 +1,2 @@
1
+ export { BedrockConnector } from './bedrock.connector';
2
+ export type { BedrockConfig } from './bedrock.config';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BedrockConnector = void 0;
4
+ var bedrock_connector_1 = require("./bedrock.connector");
5
+ Object.defineProperty(exports, "BedrockConnector", { enumerable: true, get: function () { return bedrock_connector_1.BedrockConnector; } });
@@ -0,0 +1,12 @@
1
+ export interface GeminiConfig {
2
+ /** Google AI Studio API key — sent as the `x-goog-api-key` header. */
3
+ apiKey: string;
4
+ /** Override the base URL. Default `https://generativelanguage.googleapis.com/v1beta`. */
5
+ baseUrl?: string;
6
+ /** `generationConfig.maxOutputTokens` used when `ChatInput.maxOutputTokens` is omitted. */
7
+ defaultMaxTokens?: number;
8
+ /** Bring-your-own fetch. Defaults to `globalThis.fetch`. */
9
+ fetch?: typeof fetch;
10
+ /** Extra headers merged onto every request. */
11
+ headers?: Record<string, string>;
12
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,28 @@
1
+ import { BaseConnector } from '../../base/base.connector';
2
+ import type { ChatInput, ChatResult, ChatStreamDelta, IChatConnector } from '../../types';
3
+ import type { GeminiConfig } from './gemini.config';
4
+ /**
5
+ * Native adapter for the Google Gemini (AI Studio) `generateContent` API.
6
+ * Gemini uses `contents[]`/`parts[]` (roles `user`/`model`), a top-level
7
+ * `systemInstruction`, `functionDeclarations`, and keys tool results by
8
+ * function NAME (not id). This connector translates the normalized surface to
9
+ * that shape and back, emitting the identical `ChatResult`/`ChatStreamDelta`/
10
+ * `ConnectorError`. Unlike Bedrock, Gemini streaming IS SSE
11
+ * (`:streamGenerateContent?alt=sse`), so streaming is truly incremental.
12
+ *
13
+ * Passthrough/raw (not emulated): Gemini-3 `thought_signature` round-trip and
14
+ * reasoning CoT output (`raw`), prompt caching (`cachedContent` via
15
+ * `_passthrough`), safety settings / video params (`_passthrough`).
16
+ */
17
+ export declare class GeminiConnector extends BaseConnector implements IChatConnector {
18
+ readonly id = "gemini";
19
+ private readonly config;
20
+ private readonly baseUrl;
21
+ constructor(config: GeminiConfig);
22
+ complete(input: ChatInput): Promise<ChatResult>;
23
+ stream(input: ChatInput): AsyncGenerator<ChatStreamDelta>;
24
+ private authHeaders;
25
+ private buildRequest;
26
+ private parseResult;
27
+ private mapVendorError;
28
+ }