@tanstack/ai-bedrock 0.2.0 → 0.2.2
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.
|
@@ -107,7 +107,8 @@ var BedrockConverseTextAdapter = class extends BaseTextAdapter {
|
|
|
107
107
|
model: this.model
|
|
108
108
|
});
|
|
109
109
|
const input = this.buildInput(options);
|
|
110
|
-
|
|
110
|
+
const stream = await this.sendStream(input);
|
|
111
|
+
yield* processConverseStream(stream, () => this.generateId(), {
|
|
111
112
|
threadId: options.threadId,
|
|
112
113
|
parentRunId: options.parentRunId,
|
|
113
114
|
model: options.model
|
|
@@ -148,11 +149,18 @@ var BedrockConverseTextAdapter = class extends BaseTextAdapter {
|
|
|
148
149
|
...this.buildInput(chatOptions),
|
|
149
150
|
toolConfig: buildStructuredToolConfig(outputSchema)
|
|
150
151
|
};
|
|
151
|
-
const
|
|
152
|
+
const res = await this.send(input);
|
|
153
|
+
const structured = extractStructuredToolInput(res);
|
|
152
154
|
if (structured === void 0) throw new Error(`${this.name}.structuredOutput: response contained no forced-tool output`);
|
|
155
|
+
const usage = res.usage;
|
|
153
156
|
return {
|
|
154
157
|
data: structured,
|
|
155
|
-
rawText: JSON.stringify(structured)
|
|
158
|
+
rawText: JSON.stringify(structured),
|
|
159
|
+
...usage && { usage: {
|
|
160
|
+
promptTokens: usage.inputTokens ?? 0,
|
|
161
|
+
completionTokens: usage.outputTokens ?? 0,
|
|
162
|
+
totalTokens: usage.totalTokens ?? 0
|
|
163
|
+
} }
|
|
156
164
|
};
|
|
157
165
|
} catch (error) {
|
|
158
166
|
chatOptions.logger.errors(`${this.name}.structuredOutput fatal`, {
|
|
@@ -171,7 +179,6 @@ var BedrockConverseTextAdapter = class extends BaseTextAdapter {
|
|
|
171
179
|
*/
|
|
172
180
|
async *structuredOutputStream(options) {
|
|
173
181
|
const { chatOptions, outputSchema } = options;
|
|
174
|
-
const timestamp = Date.now();
|
|
175
182
|
const runId = this.generateId();
|
|
176
183
|
const threadId = chatOptions.threadId ?? this.generateId();
|
|
177
184
|
const messageId = this.generateId();
|
|
@@ -197,7 +204,7 @@ var BedrockConverseTextAdapter = class extends BaseTextAdapter {
|
|
|
197
204
|
runId,
|
|
198
205
|
threadId,
|
|
199
206
|
model: chatOptions.model,
|
|
200
|
-
timestamp,
|
|
207
|
+
timestamp: Date.now(),
|
|
201
208
|
parentRunId: chatOptions.parentRunId
|
|
202
209
|
};
|
|
203
210
|
}
|
|
@@ -213,7 +220,7 @@ var BedrockConverseTextAdapter = class extends BaseTextAdapter {
|
|
|
213
220
|
messageId,
|
|
214
221
|
role: "assistant",
|
|
215
222
|
model: chatOptions.model,
|
|
216
|
-
timestamp
|
|
223
|
+
timestamp: Date.now()
|
|
217
224
|
};
|
|
218
225
|
}
|
|
219
226
|
accumulatedRaw += fragment;
|
|
@@ -223,7 +230,7 @@ var BedrockConverseTextAdapter = class extends BaseTextAdapter {
|
|
|
223
230
|
delta: fragment,
|
|
224
231
|
content: accumulatedRaw,
|
|
225
232
|
model: chatOptions.model,
|
|
226
|
-
timestamp
|
|
233
|
+
timestamp: Date.now()
|
|
227
234
|
};
|
|
228
235
|
}
|
|
229
236
|
continue;
|
|
@@ -241,7 +248,7 @@ var BedrockConverseTextAdapter = class extends BaseTextAdapter {
|
|
|
241
248
|
runId,
|
|
242
249
|
threadId,
|
|
243
250
|
model: chatOptions.model,
|
|
244
|
-
timestamp,
|
|
251
|
+
timestamp: Date.now(),
|
|
245
252
|
parentRunId: chatOptions.parentRunId
|
|
246
253
|
};
|
|
247
254
|
}
|
|
@@ -249,14 +256,14 @@ var BedrockConverseTextAdapter = class extends BaseTextAdapter {
|
|
|
249
256
|
type: EventType.TEXT_MESSAGE_END,
|
|
250
257
|
messageId,
|
|
251
258
|
model: chatOptions.model,
|
|
252
|
-
timestamp
|
|
259
|
+
timestamp: Date.now()
|
|
253
260
|
};
|
|
254
261
|
if (accumulatedRaw.length === 0) {
|
|
255
262
|
yield {
|
|
256
263
|
type: EventType.RUN_ERROR,
|
|
257
264
|
runId,
|
|
258
265
|
model: chatOptions.model,
|
|
259
|
-
timestamp,
|
|
266
|
+
timestamp: Date.now(),
|
|
260
267
|
message: `${this.name}.structuredOutputStream: response contained no content`,
|
|
261
268
|
code: "empty-response",
|
|
262
269
|
error: {
|
|
@@ -274,7 +281,7 @@ var BedrockConverseTextAdapter = class extends BaseTextAdapter {
|
|
|
274
281
|
type: EventType.RUN_ERROR,
|
|
275
282
|
runId,
|
|
276
283
|
model: chatOptions.model,
|
|
277
|
-
timestamp,
|
|
284
|
+
timestamp: Date.now(),
|
|
278
285
|
message: `Failed to parse structured output as JSON. Content: ${accumulatedRaw.slice(0, 200)}${accumulatedRaw.length > 200 ? "..." : ""}`,
|
|
279
286
|
code: "parse-error",
|
|
280
287
|
error: {
|
|
@@ -292,14 +299,14 @@ var BedrockConverseTextAdapter = class extends BaseTextAdapter {
|
|
|
292
299
|
raw: accumulatedRaw
|
|
293
300
|
},
|
|
294
301
|
model: chatOptions.model,
|
|
295
|
-
timestamp
|
|
302
|
+
timestamp: Date.now()
|
|
296
303
|
};
|
|
297
304
|
yield {
|
|
298
305
|
type: EventType.RUN_FINISHED,
|
|
299
306
|
runId,
|
|
300
307
|
threadId,
|
|
301
308
|
model: chatOptions.model,
|
|
302
|
-
timestamp,
|
|
309
|
+
timestamp: Date.now(),
|
|
303
310
|
finishReason
|
|
304
311
|
};
|
|
305
312
|
} catch (error) {
|
|
@@ -310,7 +317,7 @@ var BedrockConverseTextAdapter = class extends BaseTextAdapter {
|
|
|
310
317
|
runId,
|
|
311
318
|
threadId,
|
|
312
319
|
model: chatOptions.model,
|
|
313
|
-
timestamp,
|
|
320
|
+
timestamp: Date.now(),
|
|
314
321
|
parentRunId: chatOptions.parentRunId
|
|
315
322
|
};
|
|
316
323
|
}
|
|
@@ -323,7 +330,7 @@ var BedrockConverseTextAdapter = class extends BaseTextAdapter {
|
|
|
323
330
|
type: EventType.RUN_ERROR,
|
|
324
331
|
runId,
|
|
325
332
|
model: chatOptions.model,
|
|
326
|
-
timestamp,
|
|
333
|
+
timestamp: Date.now(),
|
|
327
334
|
message: errorPayload.message,
|
|
328
335
|
...errorPayload.code !== void 0 && { code: errorPayload.code },
|
|
329
336
|
error: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"converse-text.js","names":[],"sources":["../../../src/adapters/converse-text.ts"],"sourcesContent":["import { EventType, convertSchemaToJsonSchema } from '@tanstack/ai'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { resolveBedrockAuth } from '../utils/auth'\nimport { toConverseMessages } from '../converse/message-converter'\nimport { toToolConfig } from '../converse/tool-converter'\nimport {\n processConverseStream,\n throwIfConverseStreamError,\n} from '../converse/stream-processor'\nimport {\n STRUCTURED_TOOL_NAME,\n buildStructuredToolConfig,\n} from '../converse/structured-output'\nimport type { ResolvedBedrockAuth } from '../utils/auth'\nimport type { ConverseToolInput } from '../converse/tool-converter'\nimport type * as BedrockRuntime from '@aws-sdk/client-bedrock-runtime'\nimport type {\n BedrockRuntimeClient,\n BedrockRuntimeClientConfig,\n ContentBlock,\n ConverseCommandInput,\n ConverseCommandOutput,\n ConverseStreamCommandInput,\n ConverseStreamOutput,\n} from '@aws-sdk/client-bedrock-runtime'\nimport type {\n JSONSchema,\n Modality,\n StreamChunk,\n TextOptions,\n Tool,\n} from '@tanstack/ai'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type { BedrockClientConfig } from '../utils/client'\nimport type { BedrockMessageMetadataByModality } from '../message-types'\nimport type {\n BedrockConverseModels,\n ResolveConverseProviderOptions,\n ResolveInputModalities,\n} from '../model-meta'\n\n/** Config for the Converse adapter — same client config as the chat adapter. */\nexport interface BedrockConverseConfig extends BedrockClientConfig {}\n\n/**\n * Bedrock Converse text adapter. Wires the Converse translation modules (message\n * converter, tool converter, stream processor, structured-output forced-tool\n * builder) onto `@tanstack/ai`'s `BaseTextAdapter` and the\n * `@aws-sdk/client-bedrock-runtime` `BedrockRuntimeClient`.\n *\n * The success-path AG-UI lifecycle (`RUN_STARTED`..`RUN_FINISHED`) is owned by\n * `processConverseStream`; this adapter only owns the catch/`RUN_ERROR` path,\n * mirroring openai-base's `chatStream`.\n *\n * The actual SDK calls live behind two protected seams (`sendStream` / `send`)\n * so tests can subclass and inject canned Converse SDK shapes without a real\n * AWS request.\n */\nexport class BedrockConverseTextAdapter<\n TModel extends BedrockConverseModels,\n // Constraint mirrors the chat adapter (text.ts): the base parameterises\n // `TProviderOptions extends Record<string, any>`, and our default\n // `ResolveConverseProviderOptions<TModel>` resolves to an interface lacking an\n // implicit index signature — which `Record<string, unknown>` would reject but\n // `Record<string, any>` accepts. Confined to the generic constraint (the\n // established adapter pattern) — no value `as` cast is introduced.\n TProviderOptions extends Record<string, any> =\n ResolveConverseProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n BedrockMessageMetadataByModality\n> {\n override readonly kind = 'text' as const\n override readonly name = 'bedrock-converse' as const\n private clientPromise?: Promise<BedrockRuntimeClient>\n private readonly clientConfig: BedrockConverseConfig\n\n constructor(config: BedrockConverseConfig, model: TModel) {\n super({}, model)\n // Defer client construction and auth resolution: the AWS SDK is Node/\n // server-only, so we must not pull it into the static graph here. The\n // client (and its dynamic import) is built lazily on first SDK call.\n this.clientConfig = config\n }\n\n /**\n * Dynamically import `@aws-sdk/client-bedrock-runtime`. The specifier is held\n * in a variable (not a string literal) so bundler dep scanners (e.g. Vite/\n * esbuild optimizeDeps) cannot statically discover the AWS SDK and try to\n * pre-bundle it for the browser — it would fail on the SDK's Node-only\n * `fromTokenFile` export chain. The SDK is Node/server-only and is only\n * reached on a real request. `typeof import(...)` is a type-only reference\n * (erased at emit) so the imported members keep full typing.\n */\n protected importBedrockRuntime(): Promise<typeof BedrockRuntime> {\n const mod = '@aws-sdk/client-bedrock-runtime'\n return import(/* @vite-ignore */ mod) as Promise<typeof BedrockRuntime>\n }\n\n /**\n * Lazily construct the `BedrockRuntimeClient`. The dynamic import keeps\n * `@aws-sdk/client-bedrock-runtime` out of the static/browser graph and\n * defers `resolveBedrockAuth` until a real request is made.\n */\n protected async getClient(): Promise<BedrockRuntimeClient> {\n if (!this.clientPromise) {\n this.clientPromise = (async () => {\n const { BedrockRuntimeClient } = await this.importBedrockRuntime()\n const region = this.clientConfig.region ?? 'us-east-1'\n const resolved = resolveBedrockAuth(\n {\n apiKey: this.clientConfig.apiKey,\n region,\n auth: this.clientConfig.auth,\n },\n 'runtime',\n )\n return new BedrockRuntimeClient(\n this.buildClientConfig(resolved, region, this.clientConfig.baseURL),\n )\n })().catch((error: unknown) => {\n // Don't cache a rejected promise — clear it so a later call can retry\n // (e.g. after a transient import failure or fixed auth config).\n this.clientPromise = undefined\n throw error\n })\n }\n return this.clientPromise\n }\n\n /**\n * Map resolved auth + endpoint to a `BedrockRuntimeClientConfig`.\n *\n * Recent `@aws-sdk/client-bedrock-runtime` exposes a first-class `token`\n * config field for Bedrock API-key bearer auth. But the client's default\n * auth-scheme order is SigV4 first, then bearer — so passing `token` alone is\n * not enough: the SDK still resolves SigV4 and throws \"Could not load\n * credentials from any providers\". Pinning `authSchemePreference` to the\n * bearer scheme makes the API key actually get used. SigV4 uses the AWS\n * credential provider chain and the default scheme order.\n */\n protected buildClientConfig(\n resolved: ResolvedBedrockAuth,\n region: string,\n endpoint: string | undefined,\n ): BedrockRuntimeClientConfig {\n if (resolved.kind === 'bearer') {\n return {\n region,\n token: { token: resolved.token },\n authSchemePreference: ['httpBearerAuth'],\n ...(endpoint ? { endpoint } : {}),\n }\n }\n return {\n region: resolved.region,\n credentials: resolved.credentials,\n ...(endpoint ? { endpoint } : {}),\n }\n }\n\n // ---------------------------------------------------------------------------\n // SDK seams (overridden in tests so no real AWS call happens)\n // ---------------------------------------------------------------------------\n\n protected async sendStream(\n input: ConverseStreamCommandInput,\n ): Promise<AsyncIterable<ConverseStreamOutput>> {\n const { ConverseStreamCommand } = await this.importBedrockRuntime()\n const client = await this.getClient()\n const res = await client.send(new ConverseStreamCommand(input))\n if (!res.stream) {\n throw new Error('Bedrock Converse: empty stream response')\n }\n return res.stream\n }\n\n protected async send(\n input: ConverseCommandInput,\n ): Promise<ConverseCommandOutput> {\n const { ConverseCommand } = await this.importBedrockRuntime()\n const client = await this.getClient()\n return client.send(new ConverseCommand(input))\n }\n\n // ---------------------------------------------------------------------------\n // Public adapter surface\n // ---------------------------------------------------------------------------\n\n async *chatStream(\n options: TextOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n try {\n options.logger.request(\n `activity=chat provider=${this.name} model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: this.name, model: this.model },\n )\n const input = this.buildInput(options)\n const stream = await this.sendStream(input)\n yield* processConverseStream(stream, () => this.generateId(), {\n threadId: options.threadId,\n parentRunId: options.parentRunId,\n model: options.model,\n })\n } catch (error: unknown) {\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.chatStream failed`,\n )\n options.logger.errors(`${this.name}.chatStream fatal`, {\n error: errorPayload,\n source: `${this.name}.chatStream`,\n })\n // Conditional `code` spread keeps the wire shape spec-compliant under\n // `exactOptionalPropertyTypes` (AG-UI's `RunErrorEvent.code` is optional).\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n error: {\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n },\n }\n }\n }\n\n /**\n * Structured output via the forced-tool strategy. Converse has no native\n * json_schema response_format, so we force a single tool whose input schema\n * is the requested output schema and read the model's `toolUse.input` back as\n * the structured result.\n */\n async structuredOutput(\n options: StructuredOutputOptions<TProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n try {\n chatOptions.logger.request(\n `activity=structuredOutput provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n const input: ConverseCommandInput = {\n ...this.buildInput(chatOptions),\n toolConfig: buildStructuredToolConfig(outputSchema),\n }\n const res = await this.send(input)\n const structured = extractStructuredToolInput(res)\n if (structured === undefined) {\n throw new Error(\n `${this.name}.structuredOutput: response contained no forced-tool output`,\n )\n }\n return {\n data: structured,\n rawText: JSON.stringify(structured),\n }\n } catch (error: unknown) {\n chatOptions.logger.errors(`${this.name}.structuredOutput fatal`, {\n error: toRunErrorPayload(error, `${this.name}.structuredOutput failed`),\n source: `${this.name}.structuredOutput`,\n })\n throw error\n }\n }\n\n /**\n * Streaming structured output. Same forced-tool strategy as\n * `structuredOutput`, but streamed: the forced tool's `toolUse.input` JSON\n * fragments are accumulated from the Converse stream and a terminal\n * `CUSTOM 'structured-output.complete'` event carries `{ object, raw }`,\n * mirroring openai-base's `structuredOutputStream` contract exactly.\n */\n async *structuredOutputStream(\n options: StructuredOutputOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const { chatOptions, outputSchema } = options\n const timestamp = Date.now()\n const runId = this.generateId()\n const threadId = chatOptions.threadId ?? this.generateId()\n const messageId = this.generateId()\n\n let hasEmittedRunStarted = false\n let hasEmittedTextMessageStart = false\n let accumulatedRaw = ''\n let finishReason: 'stop' | 'length' | 'content_filter' = 'stop'\n\n try {\n chatOptions.logger.request(\n `activity=structuredOutputStream provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n const input: ConverseStreamCommandInput = {\n ...this.buildInput(chatOptions),\n toolConfig: buildStructuredToolConfig(outputSchema),\n }\n const stream = await this.sendStream(input)\n\n // The forced tool streams its `input` as partial-JSON fragments inside\n // `contentBlockDelta.delta.toolUse.input`. We surface them as\n // TEXT_MESSAGE_CONTENT deltas (raw JSON text), matching openai-base which\n // carries the structured JSON as text deltas.\n for await (const ev of stream) {\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp,\n parentRunId: chatOptions.parentRunId,\n }\n }\n\n // Surface in-band server/throttle/validation errors instead of\n // letting them fall through and masquerade as an empty response.\n throwIfConverseStreamError(ev)\n\n if ('contentBlockDelta' in ev) {\n const delta = ev.contentBlockDelta?.delta\n const fragment =\n delta && 'toolUse' in delta ? delta.toolUse?.input : undefined\n if (fragment !== undefined) {\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n role: 'assistant',\n model: chatOptions.model,\n timestamp,\n }\n }\n accumulatedRaw += fragment\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n delta: fragment,\n content: accumulatedRaw,\n model: chatOptions.model,\n timestamp,\n }\n }\n continue\n }\n\n if ('messageStop' in ev) {\n const stopReason = ev.messageStop?.stopReason\n // The forced structured-output tool produces stopReason 'tool_use' on\n // success, but that's an implementation detail — a cleanly-completed\n // structured run reports 'stop', matching openai-base's contract.\n finishReason =\n stopReason === 'max_tokens'\n ? 'length'\n : stopReason === 'content_filtered'\n ? 'content_filter'\n : 'stop'\n continue\n }\n }\n\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp,\n parentRunId: chatOptions.parentRunId,\n }\n }\n\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model: chatOptions.model,\n timestamp,\n }\n }\n\n if (accumulatedRaw.length === 0) {\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: chatOptions.model,\n timestamp,\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n error: {\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n },\n }\n return\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(accumulatedRaw)\n } catch {\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: chatOptions.model,\n timestamp,\n message: `Failed to parse structured output as JSON. Content: ${accumulatedRaw.slice(0, 200)}${accumulatedRaw.length > 200 ? '...' : ''}`,\n code: 'parse-error',\n error: {\n message: 'Failed to parse structured output as JSON',\n code: 'parse-error',\n },\n }\n return\n }\n\n yield {\n type: EventType.CUSTOM,\n name: 'structured-output.complete',\n value: {\n object: parsed,\n raw: accumulatedRaw,\n },\n model: chatOptions.model,\n timestamp,\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp,\n finishReason,\n }\n } catch (error: unknown) {\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp,\n parentRunId: chatOptions.parentRunId,\n }\n }\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.structuredOutputStream failed`,\n )\n chatOptions.logger.errors(`${this.name}.structuredOutputStream fatal`, {\n error: errorPayload,\n source: `${this.name}.structuredOutputStream`,\n })\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: chatOptions.model,\n timestamp,\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n error: {\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n },\n }\n }\n }\n\n /**\n * Converse sends `tools` and a forced structured-output tool via two separate\n * mechanisms, never together. Declaring `false` makes the engine run the\n * agent loop without `outputSchema` and finalize via `structuredOutput` /\n * `structuredOutputStream`.\n */\n supportsCombinedToolsAndSchema(): boolean {\n return false\n }\n\n // ---------------------------------------------------------------------------\n // Request construction\n // ---------------------------------------------------------------------------\n\n /**\n * Translate `TextOptions` into a `ConverseCommandInput`. Shared by chatStream,\n * structuredOutput, and structuredOutputStream (the latter two override\n * `toolConfig` with the forced structured tool afterwards).\n */\n protected buildInput(\n options: TextOptions<TProviderOptions>,\n ): ConverseCommandInput {\n const { system, messages } = toConverseMessages(\n options.messages,\n options.systemPrompts,\n )\n\n const toolConfig = options.tools\n ? toToolConfig(convertTools(options.tools), 'auto')\n : undefined\n\n // Sampling options live on `modelOptions` (typed as the narrowed\n // `BedrockConverseProviderOptions`, which surfaces the OpenAI Chat\n // Completions field names); translate them into Converse's `inferenceConfig`,\n // which uses AWS-native camelCase keys.\n const modelOptions = options.modelOptions\n const temperature = modelOptions?.temperature\n const topP = modelOptions?.top_p\n const maxTokens = modelOptions?.max_completion_tokens\n const stop = modelOptions?.stop\n const stopSequences =\n stop == null ? undefined : Array.isArray(stop) ? stop : [stop]\n\n const inferenceConfig =\n temperature != null ||\n topP != null ||\n maxTokens != null ||\n stopSequences != null\n ? {\n ...(temperature != null && { temperature }),\n ...(topP != null && { topP }),\n ...(maxTokens != null && { maxTokens }),\n ...(stopSequences != null && { stopSequences }),\n }\n : undefined\n\n return {\n modelId: this.model,\n messages,\n ...(system.length > 0 && { system }),\n ...(toolConfig && { toolConfig }),\n ...(inferenceConfig && { inferenceConfig }),\n }\n }\n}\n\n/**\n * Convert TanStack `Tool[]` to the Converse tool-converter input shape. Reuses\n * the SAME `convertSchemaToJsonSchema` the other adapters use so the Converse\n * tool input schemas match what every other provider sends.\n */\nfunction convertTools(tools: Array<Tool>): Array<ConverseToolInput> {\n return tools.map((tool) => {\n const inputSchema: JSONSchema = convertSchemaToJsonSchema(\n tool.inputSchema,\n ) ?? { type: 'object', properties: {}, required: [] }\n return {\n name: tool.name,\n description: tool.description,\n inputSchema,\n }\n })\n}\n\n/**\n * Find the forced structured-output tool's `input` in a non-streaming Converse\n * response. SDK-boundary narrowing only — `ConverseOutput` is a tagged union\n * (`{ message }`) and a tool-use block is `{ toolUse: { input } }`.\n */\nfunction extractStructuredToolInput(\n res: ConverseCommandOutput,\n): unknown | undefined {\n const message =\n res.output && 'message' in res.output ? res.output.message : undefined\n const content: Array<ContentBlock> = message?.content ?? []\n for (const block of content) {\n if ('toolUse' in block && block.toolUse) {\n // Only accept the forced structured tool (an unnamed block is allowed,\n // since the forced tool is the only one configured). A differently-named\n // tool-use block is a hallucinated/leftover call whose arbitrary input\n // must not be returned as the validated result — leave it to the caller's\n // `throw` so the failure is accurate instead of silently wrong.\n if (\n block.toolUse.name === STRUCTURED_TOOL_NAME ||\n block.toolUse.name === undefined\n ) {\n return block.toolUse.input\n }\n }\n }\n return undefined\n}\n\n/** Converse adapter with an explicit API key (low-level; mirrors createBedrockChat). */\nexport function createBedrockConverse<TModel extends BedrockConverseModels>(\n model: TModel,\n apiKey: string,\n config?: Omit<BedrockConverseConfig, 'apiKey'>,\n): BedrockConverseTextAdapter<TModel> {\n return new BedrockConverseTextAdapter({ ...config, apiKey }, model)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"converse-text.js","names":[],"sources":["../../../src/adapters/converse-text.ts"],"sourcesContent":["import { EventType, convertSchemaToJsonSchema } from '@tanstack/ai'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { resolveBedrockAuth } from '../utils/auth'\nimport { toConverseMessages } from '../converse/message-converter'\nimport { toToolConfig } from '../converse/tool-converter'\nimport {\n processConverseStream,\n throwIfConverseStreamError,\n} from '../converse/stream-processor'\nimport {\n STRUCTURED_TOOL_NAME,\n buildStructuredToolConfig,\n} from '../converse/structured-output'\nimport type { ResolvedBedrockAuth } from '../utils/auth'\nimport type { ConverseToolInput } from '../converse/tool-converter'\nimport type * as BedrockRuntime from '@aws-sdk/client-bedrock-runtime'\nimport type {\n BedrockRuntimeClient,\n BedrockRuntimeClientConfig,\n ContentBlock,\n ConverseCommandInput,\n ConverseCommandOutput,\n ConverseStreamCommandInput,\n ConverseStreamOutput,\n} from '@aws-sdk/client-bedrock-runtime'\nimport type {\n JSONSchema,\n Modality,\n StreamChunk,\n TextOptions,\n Tool,\n} from '@tanstack/ai'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type { BedrockClientConfig } from '../utils/client'\nimport type { BedrockMessageMetadataByModality } from '../message-types'\nimport type {\n BedrockConverseModels,\n ResolveConverseProviderOptions,\n ResolveInputModalities,\n} from '../model-meta'\n\n/** Config for the Converse adapter — same client config as the chat adapter. */\nexport interface BedrockConverseConfig extends BedrockClientConfig {}\n\n/**\n * Bedrock Converse text adapter. Wires the Converse translation modules (message\n * converter, tool converter, stream processor, structured-output forced-tool\n * builder) onto `@tanstack/ai`'s `BaseTextAdapter` and the\n * `@aws-sdk/client-bedrock-runtime` `BedrockRuntimeClient`.\n *\n * The success-path AG-UI lifecycle (`RUN_STARTED`..`RUN_FINISHED`) is owned by\n * `processConverseStream`; this adapter only owns the catch/`RUN_ERROR` path,\n * mirroring openai-base's `chatStream`.\n *\n * The actual SDK calls live behind two protected seams (`sendStream` / `send`)\n * so tests can subclass and inject canned Converse SDK shapes without a real\n * AWS request.\n */\nexport class BedrockConverseTextAdapter<\n TModel extends BedrockConverseModels,\n // Constraint mirrors the chat adapter (text.ts): the base parameterises\n // `TProviderOptions extends Record<string, any>`, and our default\n // `ResolveConverseProviderOptions<TModel>` resolves to an interface lacking an\n // implicit index signature — which `Record<string, unknown>` would reject but\n // `Record<string, any>` accepts. Confined to the generic constraint (the\n // established adapter pattern) — no value `as` cast is introduced.\n TProviderOptions extends Record<string, any> =\n ResolveConverseProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n BedrockMessageMetadataByModality\n> {\n override readonly kind = 'text' as const\n override readonly name = 'bedrock-converse' as const\n private clientPromise?: Promise<BedrockRuntimeClient>\n private readonly clientConfig: BedrockConverseConfig\n\n constructor(config: BedrockConverseConfig, model: TModel) {\n super({}, model)\n // Defer client construction and auth resolution: the AWS SDK is Node/\n // server-only, so we must not pull it into the static graph here. The\n // client (and its dynamic import) is built lazily on first SDK call.\n this.clientConfig = config\n }\n\n /**\n * Dynamically import `@aws-sdk/client-bedrock-runtime`. The specifier is held\n * in a variable (not a string literal) so bundler dep scanners (e.g. Vite/\n * esbuild optimizeDeps) cannot statically discover the AWS SDK and try to\n * pre-bundle it for the browser — it would fail on the SDK's Node-only\n * `fromTokenFile` export chain. The SDK is Node/server-only and is only\n * reached on a real request. `typeof import(...)` is a type-only reference\n * (erased at emit) so the imported members keep full typing.\n */\n protected importBedrockRuntime(): Promise<typeof BedrockRuntime> {\n const mod = '@aws-sdk/client-bedrock-runtime'\n return import(/* @vite-ignore */ mod) as Promise<typeof BedrockRuntime>\n }\n\n /**\n * Lazily construct the `BedrockRuntimeClient`. The dynamic import keeps\n * `@aws-sdk/client-bedrock-runtime` out of the static/browser graph and\n * defers `resolveBedrockAuth` until a real request is made.\n */\n protected async getClient(): Promise<BedrockRuntimeClient> {\n if (!this.clientPromise) {\n this.clientPromise = (async () => {\n const { BedrockRuntimeClient } = await this.importBedrockRuntime()\n const region = this.clientConfig.region ?? 'us-east-1'\n const resolved = resolveBedrockAuth(\n {\n apiKey: this.clientConfig.apiKey,\n region,\n auth: this.clientConfig.auth,\n },\n 'runtime',\n )\n return new BedrockRuntimeClient(\n this.buildClientConfig(resolved, region, this.clientConfig.baseURL),\n )\n })().catch((error: unknown) => {\n // Don't cache a rejected promise — clear it so a later call can retry\n // (e.g. after a transient import failure or fixed auth config).\n this.clientPromise = undefined\n throw error\n })\n }\n return this.clientPromise\n }\n\n /**\n * Map resolved auth + endpoint to a `BedrockRuntimeClientConfig`.\n *\n * Recent `@aws-sdk/client-bedrock-runtime` exposes a first-class `token`\n * config field for Bedrock API-key bearer auth. But the client's default\n * auth-scheme order is SigV4 first, then bearer — so passing `token` alone is\n * not enough: the SDK still resolves SigV4 and throws \"Could not load\n * credentials from any providers\". Pinning `authSchemePreference` to the\n * bearer scheme makes the API key actually get used. SigV4 uses the AWS\n * credential provider chain and the default scheme order.\n */\n protected buildClientConfig(\n resolved: ResolvedBedrockAuth,\n region: string,\n endpoint: string | undefined,\n ): BedrockRuntimeClientConfig {\n if (resolved.kind === 'bearer') {\n return {\n region,\n token: { token: resolved.token },\n authSchemePreference: ['httpBearerAuth'],\n ...(endpoint ? { endpoint } : {}),\n }\n }\n return {\n region: resolved.region,\n credentials: resolved.credentials,\n ...(endpoint ? { endpoint } : {}),\n }\n }\n\n // ---------------------------------------------------------------------------\n // SDK seams (overridden in tests so no real AWS call happens)\n // ---------------------------------------------------------------------------\n\n protected async sendStream(\n input: ConverseStreamCommandInput,\n ): Promise<AsyncIterable<ConverseStreamOutput>> {\n const { ConverseStreamCommand } = await this.importBedrockRuntime()\n const client = await this.getClient()\n const res = await client.send(new ConverseStreamCommand(input))\n if (!res.stream) {\n throw new Error('Bedrock Converse: empty stream response')\n }\n return res.stream\n }\n\n protected async send(\n input: ConverseCommandInput,\n ): Promise<ConverseCommandOutput> {\n const { ConverseCommand } = await this.importBedrockRuntime()\n const client = await this.getClient()\n return client.send(new ConverseCommand(input))\n }\n\n // ---------------------------------------------------------------------------\n // Public adapter surface\n // ---------------------------------------------------------------------------\n\n async *chatStream(\n options: TextOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n try {\n options.logger.request(\n `activity=chat provider=${this.name} model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: this.name, model: this.model },\n )\n const input = this.buildInput(options)\n const stream = await this.sendStream(input)\n yield* processConverseStream(stream, () => this.generateId(), {\n threadId: options.threadId,\n parentRunId: options.parentRunId,\n model: options.model,\n })\n } catch (error: unknown) {\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.chatStream failed`,\n )\n options.logger.errors(`${this.name}.chatStream fatal`, {\n error: errorPayload,\n source: `${this.name}.chatStream`,\n })\n // Conditional `code` spread keeps the wire shape spec-compliant under\n // `exactOptionalPropertyTypes` (AG-UI's `RunErrorEvent.code` is optional).\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n error: {\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n },\n }\n }\n }\n\n /**\n * Structured output via the forced-tool strategy. Converse has no native\n * json_schema response_format, so we force a single tool whose input schema\n * is the requested output schema and read the model's `toolUse.input` back as\n * the structured result.\n */\n async structuredOutput(\n options: StructuredOutputOptions<TProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n try {\n chatOptions.logger.request(\n `activity=structuredOutput provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n const input: ConverseCommandInput = {\n ...this.buildInput(chatOptions),\n toolConfig: buildStructuredToolConfig(outputSchema),\n }\n const res = await this.send(input)\n const structured = extractStructuredToolInput(res)\n if (structured === undefined) {\n throw new Error(\n `${this.name}.structuredOutput: response contained no forced-tool output`,\n )\n }\n const usage = res.usage\n return {\n data: structured,\n rawText: JSON.stringify(structured),\n ...(usage && {\n usage: {\n promptTokens: usage.inputTokens ?? 0,\n completionTokens: usage.outputTokens ?? 0,\n totalTokens: usage.totalTokens ?? 0,\n },\n }),\n }\n } catch (error: unknown) {\n chatOptions.logger.errors(`${this.name}.structuredOutput fatal`, {\n error: toRunErrorPayload(error, `${this.name}.structuredOutput failed`),\n source: `${this.name}.structuredOutput`,\n })\n throw error\n }\n }\n\n /**\n * Streaming structured output. Same forced-tool strategy as\n * `structuredOutput`, but streamed: the forced tool's `toolUse.input` JSON\n * fragments are accumulated from the Converse stream and a terminal\n * `CUSTOM 'structured-output.complete'` event carries `{ object, raw }`,\n * mirroring openai-base's `structuredOutputStream` contract exactly.\n */\n async *structuredOutputStream(\n options: StructuredOutputOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const { chatOptions, outputSchema } = options\n const runId = this.generateId()\n const threadId = chatOptions.threadId ?? this.generateId()\n const messageId = this.generateId()\n\n let hasEmittedRunStarted = false\n let hasEmittedTextMessageStart = false\n let accumulatedRaw = ''\n let finishReason: 'stop' | 'length' | 'content_filter' = 'stop'\n\n try {\n chatOptions.logger.request(\n `activity=structuredOutputStream provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n const input: ConverseStreamCommandInput = {\n ...this.buildInput(chatOptions),\n toolConfig: buildStructuredToolConfig(outputSchema),\n }\n const stream = await this.sendStream(input)\n\n // The forced tool streams its `input` as partial-JSON fragments inside\n // `contentBlockDelta.delta.toolUse.input`. We surface them as\n // TEXT_MESSAGE_CONTENT deltas (raw JSON text), matching openai-base which\n // carries the structured JSON as text deltas.\n for await (const ev of stream) {\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp: Date.now(),\n parentRunId: chatOptions.parentRunId,\n }\n }\n\n // Surface in-band server/throttle/validation errors instead of\n // letting them fall through and masquerade as an empty response.\n throwIfConverseStreamError(ev)\n\n if ('contentBlockDelta' in ev) {\n const delta = ev.contentBlockDelta?.delta\n const fragment =\n delta && 'toolUse' in delta ? delta.toolUse?.input : undefined\n if (fragment !== undefined) {\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n role: 'assistant',\n model: chatOptions.model,\n timestamp: Date.now(),\n }\n }\n accumulatedRaw += fragment\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n delta: fragment,\n content: accumulatedRaw,\n model: chatOptions.model,\n timestamp: Date.now(),\n }\n }\n continue\n }\n\n if ('messageStop' in ev) {\n const stopReason = ev.messageStop?.stopReason\n // The forced structured-output tool produces stopReason 'tool_use' on\n // success, but that's an implementation detail — a cleanly-completed\n // structured run reports 'stop', matching openai-base's contract.\n finishReason =\n stopReason === 'max_tokens'\n ? 'length'\n : stopReason === 'content_filtered'\n ? 'content_filter'\n : 'stop'\n continue\n }\n }\n\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp: Date.now(),\n parentRunId: chatOptions.parentRunId,\n }\n }\n\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model: chatOptions.model,\n timestamp: Date.now(),\n }\n }\n\n if (accumulatedRaw.length === 0) {\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: chatOptions.model,\n timestamp: Date.now(),\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n error: {\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n },\n }\n return\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(accumulatedRaw)\n } catch {\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: chatOptions.model,\n timestamp: Date.now(),\n message: `Failed to parse structured output as JSON. Content: ${accumulatedRaw.slice(0, 200)}${accumulatedRaw.length > 200 ? '...' : ''}`,\n code: 'parse-error',\n error: {\n message: 'Failed to parse structured output as JSON',\n code: 'parse-error',\n },\n }\n return\n }\n\n yield {\n type: EventType.CUSTOM,\n name: 'structured-output.complete',\n value: {\n object: parsed,\n raw: accumulatedRaw,\n },\n model: chatOptions.model,\n timestamp: Date.now(),\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp: Date.now(),\n finishReason,\n }\n } catch (error: unknown) {\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp: Date.now(),\n parentRunId: chatOptions.parentRunId,\n }\n }\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.structuredOutputStream failed`,\n )\n chatOptions.logger.errors(`${this.name}.structuredOutputStream fatal`, {\n error: errorPayload,\n source: `${this.name}.structuredOutputStream`,\n })\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: chatOptions.model,\n timestamp: Date.now(),\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n error: {\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n },\n }\n }\n }\n\n /**\n * Converse sends `tools` and a forced structured-output tool via two separate\n * mechanisms, never together. Declaring `false` makes the engine run the\n * agent loop without `outputSchema` and finalize via `structuredOutput` /\n * `structuredOutputStream`.\n */\n supportsCombinedToolsAndSchema(): boolean {\n return false\n }\n\n // ---------------------------------------------------------------------------\n // Request construction\n // ---------------------------------------------------------------------------\n\n /**\n * Translate `TextOptions` into a `ConverseCommandInput`. Shared by chatStream,\n * structuredOutput, and structuredOutputStream (the latter two override\n * `toolConfig` with the forced structured tool afterwards).\n */\n protected buildInput(\n options: TextOptions<TProviderOptions>,\n ): ConverseCommandInput {\n const { system, messages } = toConverseMessages(\n options.messages,\n options.systemPrompts,\n )\n\n const toolConfig = options.tools\n ? toToolConfig(convertTools(options.tools), 'auto')\n : undefined\n\n // Sampling options live on `modelOptions` (typed as the narrowed\n // `BedrockConverseProviderOptions`, which surfaces the OpenAI Chat\n // Completions field names); translate them into Converse's `inferenceConfig`,\n // which uses AWS-native camelCase keys.\n const modelOptions = options.modelOptions\n const temperature = modelOptions?.temperature\n const topP = modelOptions?.top_p\n const maxTokens = modelOptions?.max_completion_tokens\n const stop = modelOptions?.stop\n const stopSequences =\n stop == null ? undefined : Array.isArray(stop) ? stop : [stop]\n\n const inferenceConfig =\n temperature != null ||\n topP != null ||\n maxTokens != null ||\n stopSequences != null\n ? {\n ...(temperature != null && { temperature }),\n ...(topP != null && { topP }),\n ...(maxTokens != null && { maxTokens }),\n ...(stopSequences != null && { stopSequences }),\n }\n : undefined\n\n return {\n modelId: this.model,\n messages,\n ...(system.length > 0 && { system }),\n ...(toolConfig && { toolConfig }),\n ...(inferenceConfig && { inferenceConfig }),\n }\n }\n}\n\n/**\n * Convert TanStack `Tool[]` to the Converse tool-converter input shape. Reuses\n * the SAME `convertSchemaToJsonSchema` the other adapters use so the Converse\n * tool input schemas match what every other provider sends.\n */\nfunction convertTools(tools: Array<Tool>): Array<ConverseToolInput> {\n return tools.map((tool) => {\n const inputSchema: JSONSchema = convertSchemaToJsonSchema(\n tool.inputSchema,\n ) ?? { type: 'object', properties: {}, required: [] }\n return {\n name: tool.name,\n description: tool.description,\n inputSchema,\n }\n })\n}\n\n/**\n * Find the forced structured-output tool's `input` in a non-streaming Converse\n * response. SDK-boundary narrowing only — `ConverseOutput` is a tagged union\n * (`{ message }`) and a tool-use block is `{ toolUse: { input } }`.\n */\nfunction extractStructuredToolInput(\n res: ConverseCommandOutput,\n): unknown | undefined {\n const message =\n res.output && 'message' in res.output ? res.output.message : undefined\n const content: Array<ContentBlock> = message?.content ?? []\n for (const block of content) {\n if ('toolUse' in block && block.toolUse) {\n // Only accept the forced structured tool (an unnamed block is allowed,\n // since the forced tool is the only one configured). A differently-named\n // tool-use block is a hallucinated/leftover call whose arbitrary input\n // must not be returned as the validated result — leave it to the caller's\n // `throw` so the failure is accurate instead of silently wrong.\n if (\n block.toolUse.name === STRUCTURED_TOOL_NAME ||\n block.toolUse.name === undefined\n ) {\n return block.toolUse.input\n }\n }\n }\n return undefined\n}\n\n/** Converse adapter with an explicit API key (low-level; mirrors createBedrockChat). */\nexport function createBedrockConverse<TModel extends BedrockConverseModels>(\n model: TModel,\n apiKey: string,\n config?: Omit<BedrockConverseConfig, 'apiKey'>,\n): BedrockConverseTextAdapter<TModel> {\n return new BedrockConverseTextAdapter({ ...config, apiKey }, model)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA8DA,IAAa,6BAAb,cAYU,gBAKR;CACA,OAAyB;CACzB,OAAyB;CACzB;CACA;CAEA,YAAY,QAA+B,OAAe;EACxD,MAAM,CAAC,GAAG,KAAK;EAIf,KAAK,eAAe;CACtB;;;;;;;;;;CAWA,uBAAiE;EAE/D,OAAO;;GAA0B;;CACnC;;;;;;CAOA,MAAgB,YAA2C;EACzD,IAAI,CAAC,KAAK,eACR,KAAK,iBAAiB,YAAY;GAChC,MAAM,EAAE,yBAAyB,MAAM,KAAK,qBAAqB;GACjE,MAAM,SAAS,KAAK,aAAa,UAAU;GAC3C,MAAM,WAAW,mBACf;IACE,QAAQ,KAAK,aAAa;IAC1B;IACA,MAAM,KAAK,aAAa;GAC1B,GACA,SACF;GACA,OAAO,IAAI,qBACT,KAAK,kBAAkB,UAAU,QAAQ,KAAK,aAAa,OAAO,CACpE;EACF,EAAA,CAAG,CAAC,CAAC,OAAO,UAAmB;GAG7B,KAAK,gBAAgB,KAAA;GACrB,MAAM;EACR,CAAC;EAEH,OAAO,KAAK;CACd;;;;;;;;;;;;CAaA,kBACE,UACA,QACA,UAC4B;EAC5B,IAAI,SAAS,SAAS,UACpB,OAAO;GACL;GACA,OAAO,EAAE,OAAO,SAAS,MAAM;GAC/B,sBAAsB,CAAC,gBAAgB;GACvC,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;EACjC;EAEF,OAAO;GACL,QAAQ,SAAS;GACjB,aAAa,SAAS;GACtB,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;EACjC;CACF;CAMA,MAAgB,WACd,OAC8C;EAC9C,MAAM,EAAE,0BAA0B,MAAM,KAAK,qBAAqB;EAElE,MAAM,MAAM,OAAM,MADG,KAAK,UAAU,EAAA,CACX,KAAK,IAAI,sBAAsB,KAAK,CAAC;EAC9D,IAAI,CAAC,IAAI,QACP,MAAM,IAAI,MAAM,yCAAyC;EAE3D,OAAO,IAAI;CACb;CAEA,MAAgB,KACd,OACgC;EAChC,MAAM,EAAE,oBAAoB,MAAM,KAAK,qBAAqB;EAE5D,QAAO,MADc,KAAK,UAAU,EAAA,CACtB,KAAK,IAAI,gBAAgB,KAAK,CAAC;CAC/C;CAMA,OAAO,WACL,SAC4B;EAC5B,IAAI;GACF,QAAQ,OAAO,QACb,0BAA0B,KAAK,KAAK,SAAS,KAAK,MAAM,YAAY,QAAQ,SAAS,OAAO,SAAS,QAAQ,OAAO,UAAU,EAAE,eAChI;IAAE,UAAU,KAAK;IAAM,OAAO,KAAK;GAAM,CAC3C;GACA,MAAM,QAAQ,KAAK,WAAW,OAAO;GACrC,MAAM,SAAS,MAAM,KAAK,WAAW,KAAK;GAC1C,OAAO,sBAAsB,cAAc,KAAK,WAAW,GAAG;IAC5D,UAAU,QAAQ;IAClB,aAAa,QAAQ;IACrB,OAAO,QAAQ;GACjB,CAAC;EACH,SAAS,OAAgB;GACvB,MAAM,eAAe,kBACnB,OACA,GAAG,KAAK,KAAK,mBACf;GACA,QAAQ,OAAO,OAAO,GAAG,KAAK,KAAK,oBAAoB;IACrD,OAAO;IACP,QAAQ,GAAG,KAAK,KAAK;GACvB,CAAC;GAGD,MAAM;IACJ,MAAM,UAAU;IAChB,OAAO,QAAQ;IACf,WAAW,KAAK,IAAI;IACpB,SAAS,aAAa;IACtB,GAAI,aAAa,SAAS,KAAA,KAAa,EAAE,MAAM,aAAa,KAAK;IACjE,OAAO;KACL,SAAS,aAAa;KACtB,GAAI,aAAa,SAAS,KAAA,KAAa,EAAE,MAAM,aAAa,KAAK;IACnE;GACF;EACF;CACF;;;;;;;CAQA,MAAM,iBACJ,SAC0C;EAC1C,MAAM,EAAE,aAAa,iBAAiB;EACtC,IAAI;GACF,YAAY,OAAO,QACjB,sCAAsC,KAAK,KAAK,SAAS,KAAK,MAAM,YAAY,YAAY,SAAS,UACrG;IAAE,UAAU,KAAK;IAAM,OAAO,KAAK;GAAM,CAC3C;GACA,MAAM,QAA8B;IAClC,GAAG,KAAK,WAAW,WAAW;IAC9B,YAAY,0BAA0B,YAAY;GACpD;GACA,MAAM,MAAM,MAAM,KAAK,KAAK,KAAK;GACjC,MAAM,aAAa,2BAA2B,GAAG;GACjD,IAAI,eAAe,KAAA,GACjB,MAAM,IAAI,MACR,GAAG,KAAK,KAAK,4DACf;GAEF,MAAM,QAAQ,IAAI;GAClB,OAAO;IACL,MAAM;IACN,SAAS,KAAK,UAAU,UAAU;IAClC,GAAI,SAAS,EACX,OAAO;KACL,cAAc,MAAM,eAAe;KACnC,kBAAkB,MAAM,gBAAgB;KACxC,aAAa,MAAM,eAAe;IACpC,EACF;GACF;EACF,SAAS,OAAgB;GACvB,YAAY,OAAO,OAAO,GAAG,KAAK,KAAK,0BAA0B;IAC/D,OAAO,kBAAkB,OAAO,GAAG,KAAK,KAAK,yBAAyB;IACtE,QAAQ,GAAG,KAAK,KAAK;GACvB,CAAC;GACD,MAAM;EACR;CACF;;;;;;;;CASA,OAAO,uBACL,SAC4B;EAC5B,MAAM,EAAE,aAAa,iBAAiB;EACtC,MAAM,QAAQ,KAAK,WAAW;EAC9B,MAAM,WAAW,YAAY,YAAY,KAAK,WAAW;EACzD,MAAM,YAAY,KAAK,WAAW;EAElC,IAAI,uBAAuB;EAC3B,IAAI,6BAA6B;EACjC,IAAI,iBAAiB;EACrB,IAAI,eAAqD;EAEzD,IAAI;GACF,YAAY,OAAO,QACjB,4CAA4C,KAAK,KAAK,SAAS,KAAK,MAAM,YAAY,YAAY,SAAS,UAC3G;IAAE,UAAU,KAAK;IAAM,OAAO,KAAK;GAAM,CAC3C;GACA,MAAM,QAAoC;IACxC,GAAG,KAAK,WAAW,WAAW;IAC9B,YAAY,0BAA0B,YAAY;GACpD;GACA,MAAM,SAAS,MAAM,KAAK,WAAW,KAAK;GAM1C,WAAW,MAAM,MAAM,QAAQ;IAC7B,IAAI,CAAC,sBAAsB;KACzB,uBAAuB;KACvB,MAAM;MACJ,MAAM,UAAU;MAChB;MACA;MACA,OAAO,YAAY;MACnB,WAAW,KAAK,IAAI;MACpB,aAAa,YAAY;KAC3B;IACF;IAIA,2BAA2B,EAAE;IAE7B,IAAI,uBAAuB,IAAI;KAC7B,MAAM,QAAQ,GAAG,mBAAmB;KACpC,MAAM,WACJ,SAAS,aAAa,QAAQ,MAAM,SAAS,QAAQ,KAAA;KACvD,IAAI,aAAa,KAAA,GAAW;MAC1B,IAAI,CAAC,4BAA4B;OAC/B,6BAA6B;OAC7B,MAAM;QACJ,MAAM,UAAU;QAChB;QACA,MAAM;QACN,OAAO,YAAY;QACnB,WAAW,KAAK,IAAI;OACtB;MACF;MACA,kBAAkB;MAClB,MAAM;OACJ,MAAM,UAAU;OAChB;OACA,OAAO;OACP,SAAS;OACT,OAAO,YAAY;OACnB,WAAW,KAAK,IAAI;MACtB;KACF;KACA;IACF;IAEA,IAAI,iBAAiB,IAAI;KACvB,MAAM,aAAa,GAAG,aAAa;KAInC,eACE,eAAe,eACX,WACA,eAAe,qBACb,mBACA;KACR;IACF;GACF;GAEA,IAAI,CAAC,sBAAsB;IACzB,uBAAuB;IACvB,MAAM;KACJ,MAAM,UAAU;KAChB;KACA;KACA,OAAO,YAAY;KACnB,WAAW,KAAK,IAAI;KACpB,aAAa,YAAY;IAC3B;GACF;GAEA,IAAI,4BACF,MAAM;IACJ,MAAM,UAAU;IAChB;IACA,OAAO,YAAY;IACnB,WAAW,KAAK,IAAI;GACtB;GAGF,IAAI,eAAe,WAAW,GAAG;IAC/B,MAAM;KACJ,MAAM,UAAU;KAChB;KACA,OAAO,YAAY;KACnB,WAAW,KAAK,IAAI;KACpB,SAAS,GAAG,KAAK,KAAK;KACtB,MAAM;KACN,OAAO;MACL,SAAS,GAAG,KAAK,KAAK;MACtB,MAAM;KACR;IACF;IACA;GACF;GAEA,IAAI;GACJ,IAAI;IACF,SAAS,KAAK,MAAM,cAAc;GACpC,QAAQ;IACN,MAAM;KACJ,MAAM,UAAU;KAChB;KACA,OAAO,YAAY;KACnB,WAAW,KAAK,IAAI;KACpB,SAAS,uDAAuD,eAAe,MAAM,GAAG,GAAG,IAAI,eAAe,SAAS,MAAM,QAAQ;KACrI,MAAM;KACN,OAAO;MACL,SAAS;MACT,MAAM;KACR;IACF;IACA;GACF;GAEA,MAAM;IACJ,MAAM,UAAU;IAChB,MAAM;IACN,OAAO;KACL,QAAQ;KACR,KAAK;IACP;IACA,OAAO,YAAY;IACnB,WAAW,KAAK,IAAI;GACtB;GAEA,MAAM;IACJ,MAAM,UAAU;IAChB;IACA;IACA,OAAO,YAAY;IACnB,WAAW,KAAK,IAAI;IACpB;GACF;EACF,SAAS,OAAgB;GACvB,IAAI,CAAC,sBAAsB;IACzB,uBAAuB;IACvB,MAAM;KACJ,MAAM,UAAU;KAChB;KACA;KACA,OAAO,YAAY;KACnB,WAAW,KAAK,IAAI;KACpB,aAAa,YAAY;IAC3B;GACF;GACA,MAAM,eAAe,kBACnB,OACA,GAAG,KAAK,KAAK,+BACf;GACA,YAAY,OAAO,OAAO,GAAG,KAAK,KAAK,gCAAgC;IACrE,OAAO;IACP,QAAQ,GAAG,KAAK,KAAK;GACvB,CAAC;GACD,MAAM;IACJ,MAAM,UAAU;IAChB;IACA,OAAO,YAAY;IACnB,WAAW,KAAK,IAAI;IACpB,SAAS,aAAa;IACtB,GAAI,aAAa,SAAS,KAAA,KAAa,EAAE,MAAM,aAAa,KAAK;IACjE,OAAO;KACL,SAAS,aAAa;KACtB,GAAI,aAAa,SAAS,KAAA,KAAa,EAAE,MAAM,aAAa,KAAK;IACnE;GACF;EACF;CACF;;;;;;;CAQA,iCAA0C;EACxC,OAAO;CACT;;;;;;CAWA,WACE,SACsB;EACtB,MAAM,EAAE,QAAQ,aAAa,mBAC3B,QAAQ,UACR,QAAQ,aACV;EAEA,MAAM,aAAa,QAAQ,QACvB,aAAa,aAAa,QAAQ,KAAK,GAAG,MAAM,IAChD,KAAA;EAMJ,MAAM,eAAe,QAAQ;EAC7B,MAAM,cAAc,cAAc;EAClC,MAAM,OAAO,cAAc;EAC3B,MAAM,YAAY,cAAc;EAChC,MAAM,OAAO,cAAc;EAC3B,MAAM,gBACJ,QAAQ,OAAO,KAAA,IAAY,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;EAE/D,MAAM,kBACJ,eAAe,QACf,QAAQ,QACR,aAAa,QACb,iBAAiB,OACb;GACE,GAAI,eAAe,QAAQ,EAAE,YAAY;GACzC,GAAI,QAAQ,QAAQ,EAAE,KAAK;GAC3B,GAAI,aAAa,QAAQ,EAAE,UAAU;GACrC,GAAI,iBAAiB,QAAQ,EAAE,cAAc;EAC/C,IACA,KAAA;EAEN,OAAO;GACL,SAAS,KAAK;GACd;GACA,GAAI,OAAO,SAAS,KAAK,EAAE,OAAO;GAClC,GAAI,cAAc,EAAE,WAAW;GAC/B,GAAI,mBAAmB,EAAE,gBAAgB;EAC3C;CACF;AACF;;;;;;AAOA,SAAS,aAAa,OAA8C;CAClE,OAAO,MAAM,KAAK,SAAS;EACzB,MAAM,cAA0B,0BAC9B,KAAK,WACP,KAAK;GAAE,MAAM;GAAU,YAAY,CAAC;GAAG,UAAU,CAAC;EAAE;EACpD,OAAO;GACL,MAAM,KAAK;GACX,aAAa,KAAK;GAClB;EACF;CACF,CAAC;AACH;;;;;;AAOA,SAAS,2BACP,KACqB;CAGrB,MAAM,WADJ,IAAI,UAAU,aAAa,IAAI,SAAS,IAAI,OAAO,UAAU,KAAA,EAAA,EACjB,WAAW,CAAC;CAC1D,KAAK,MAAM,SAAS,SAClB,IAAI,aAAa,SAAS,MAAM,SAO5B;MAAA,MAAM,QAAQ,SAAA,uBACd,MAAM,QAAQ,SAAS,KAAA,GAEvB,OAAO,MAAM,QAAQ;CAAA;AAK7B;;AAGA,SAAgB,sBACd,OACA,QACA,QACoC;CACpC,OAAO,IAAI,2BAA2B;EAAE,GAAG;EAAQ;CAAO,GAAG,KAAK;AACpE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-converter.js","names":[],"sources":["../../../src/converse/message-converter.ts"],"sourcesContent":["import { normalizeSystemPrompts } from '@tanstack/ai'\nimport type {\n ContentPart,\n ContentPartDataSource,\n DocumentPart,\n ImagePart,\n ModelMessage,\n SystemPrompt,\n TextPart,\n} from '@tanstack/ai'\nimport type {\n ContentBlock,\n Message,\n SystemContentBlock,\n ToolResultContentBlock,\n} from '@aws-sdk/client-bedrock-runtime'\nimport type { DocumentType } from '@smithy/types'\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction base64ToBytes(b64: string): Uint8Array {\n return new Uint8Array(Buffer.from(b64, 'base64'))\n}\n\nfunction imageFormat(mime: string): 'png' | 'jpeg' | 'gif' | 'webp' {\n switch (mime) {\n case 'image/png':\n return 'png'\n case 'image/jpeg':\n case 'image/jpg':\n return 'jpeg'\n case 'image/gif':\n return 'gif'\n case 'image/webp':\n return 'webp'\n default:\n throw new Error(\n `Bedrock Converse: unsupported image MIME type \"${mime}\". Supported types: image/png, image/jpeg, image/gif, image/webp.`,\n )\n }\n}\n\nfunction documentFormat(\n mime: string,\n): 'pdf' | 'csv' | 'doc' | 'docx' | 'xls' | 'xlsx' | 'html' | 'txt' | 'md' {\n switch (mime) {\n case 'application/pdf':\n return 'pdf'\n case 'text/csv':\n return 'csv'\n case 'application/msword':\n return 'doc'\n case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':\n return 'docx'\n case 'application/vnd.ms-excel':\n return 'xls'\n case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':\n return 'xlsx'\n case 'text/html':\n return 'html'\n case 'text/plain':\n return 'txt'\n case 'text/markdown':\n case 'text/x-markdown':\n return 'md'\n default:\n throw new Error(\n `Bedrock Converse: unsupported document MIME type \"${mime}\". Supported types: pdf, csv, doc, docx, xls, xlsx, html, txt, md.`,\n )\n }\n}\n\nfunction stringContent(content: string | null | Array<ContentPart>): string {\n if (content === null) return ''\n if (typeof content === 'string') return content\n return content\n .filter((p): p is TextPart => p.type === 'text')\n .map((p) => p.content)\n .join('')\n}\n\nfunction isTextPart(p: ContentPart): p is TextPart {\n return p.type === 'text'\n}\n\nfunction isImagePart(p: ContentPart): p is ImagePart {\n return p.type === 'image'\n}\n\nfunction isDocumentPart(p: ContentPart): p is DocumentPart {\n return p.type === 'document'\n}\n\nfunction isDataSource(\n source: ImagePart['source'] | DocumentPart['source'],\n): source is ContentPartDataSource {\n return source.type === 'data'\n}\n\nfunction contentPartToBlock(part: ContentPart, docIndex: number): ContentBlock {\n if (isTextPart(part)) {\n return { text: part.content }\n }\n\n if (isImagePart(part)) {\n const { source } = part\n if (!isDataSource(source)) {\n throw new Error(\n 'Bedrock Converse requires inline image bytes; URL image sources are not supported.',\n )\n }\n return {\n image: {\n format: imageFormat(source.mimeType),\n source: { bytes: base64ToBytes(source.value) },\n },\n }\n }\n\n if (isDocumentPart(part)) {\n const { source } = part\n if (!isDataSource(source)) {\n throw new Error(\n 'Bedrock Converse requires inline document bytes; URL document sources are not supported.',\n )\n }\n return {\n document: {\n format: documentFormat(source.mimeType),\n name: `document-${docIndex}`,\n source: { bytes: base64ToBytes(source.value) },\n },\n }\n }\n\n // Fail loud for unsupported part types (audio, video, etc.)\n throw new Error(\n `Bedrock Converse does not support content part type \"${String(part.type)}\".`,\n )\n}\n\nfunction messageToBlocks(\n msg: ModelMessage,\n docCounter: { value: number },\n): Array<ContentBlock> {\n const blocks: Array<ContentBlock> = []\n\n if (msg.role === 'tool') {\n if (!msg.toolCallId) {\n throw new Error(\n 'Bedrock Converse: tool message is missing toolCallId. Every tool result must reference the tool use ID it is responding to.',\n )\n }\n const textContent = stringContent(msg.content)\n const toolResult: ToolResultContentBlock = { text: textContent }\n blocks.push({\n toolResult: {\n toolUseId: msg.toolCallId,\n content: [toolResult],\n status: 'success',\n },\n })\n return blocks\n }\n\n // Map content field to text/image/document blocks\n if (typeof msg.content === 'string') {\n if (msg.content !== '') {\n blocks.push({ text: msg.content })\n }\n } else if (Array.isArray(msg.content)) {\n for (const part of msg.content) {\n const docIndex = isDocumentPart(part) ? ++docCounter.value : 0\n blocks.push(contentPartToBlock(part, docIndex))\n }\n }\n // null → no text blocks\n\n // Append toolUse blocks for assistant tool calls. Malformed or non-object\n // arguments come from a prior assistant turn the engine already accepted, so\n // they signal a real upstream problem — throw rather than silently coercing to\n // `{}` and forwarding a corrupted tool call (the adapter's catch surfaces it\n // as a RUN_ERROR).\n if (msg.role === 'assistant' && msg.toolCalls) {\n for (const call of msg.toolCalls) {\n const rawArguments = call.function.arguments || '{}'\n let parsed: unknown\n try {\n parsed = JSON.parse(rawArguments)\n } catch (error: unknown) {\n throw new Error(\n `Bedrock Converse: tool call \"${call.function.name}\" has malformed JSON arguments (${String(error)}). Raw: ${rawArguments}`,\n )\n }\n if (\n parsed === null ||\n typeof parsed !== 'object' ||\n Array.isArray(parsed)\n ) {\n throw new Error(\n `Bedrock Converse: tool call \"${call.function.name}\" arguments must be a JSON object, got ${Array.isArray(parsed) ? 'array' : typeof parsed}.`,\n )\n }\n blocks.push({\n toolUse: {\n toolUseId: call.id,\n name: call.function.name,\n input: parsed as DocumentType,\n },\n })\n }\n }\n\n return blocks\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Convert TanStack AI messages + system prompts into the Converse API format.\n *\n * - System prompts are lifted into `SystemContentBlock[]`.\n * - `tool` role messages are remapped to `user` role `toolResult` blocks.\n * - Consecutive messages with the same Converse role are merged (Converse\n * requires strict user/assistant alternation).\n */\nexport function toConverseMessages(\n messages: Array<ModelMessage>,\n systemPrompts?: Array<SystemPrompt>,\n): { system: Array<SystemContentBlock>; messages: Array<Message> } {\n // Build system blocks (uses normalizeSystemPrompts for runtime validation)\n const system: Array<SystemContentBlock> = normalizeSystemPrompts(\n systemPrompts,\n ).map((p) => ({ text: p.content }))\n\n // Convert each ModelMessage to a Converse Message, merging same-role pairs\n const converseMessages: Array<Message> = []\n // Global document counter: ensures every document block across all messages\n // gets a unique name, preventing Bedrock ValidationException for duplicate names.\n const docCounter = { value: 0 }\n\n for (const msg of messages) {\n // Map TanStack roles to Converse roles\n const converseRole: 'user' | 'assistant' =\n msg.role === 'assistant' ? 'assistant' : 'user'\n\n const blocks = messageToBlocks(msg, docCounter)\n\n // Skip messages that produce no content blocks (e.g. assistant with\n // null content and no toolCalls). Pushing an empty-content message to\n // Converse triggers a ValidationException.\n if (blocks.length === 0) continue\n\n const last = converseMessages[converseMessages.length - 1]\n if (last && last.role === converseRole) {\n // Merge into the previous message's content array\n last.content = [...(last.content ?? []), ...blocks]\n } else {\n converseMessages.push({ role: converseRole, content: blocks })\n }\n }\n\n return { system, messages: converseMessages }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"message-converter.js","names":[],"sources":["../../../src/converse/message-converter.ts"],"sourcesContent":["import { normalizeSystemPrompts } from '@tanstack/ai'\nimport type {\n ContentPart,\n ContentPartDataSource,\n DocumentPart,\n ImagePart,\n ModelMessage,\n SystemPrompt,\n TextPart,\n} from '@tanstack/ai'\nimport type {\n ContentBlock,\n Message,\n SystemContentBlock,\n ToolResultContentBlock,\n} from '@aws-sdk/client-bedrock-runtime'\nimport type { DocumentType } from '@smithy/types'\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction base64ToBytes(b64: string): Uint8Array {\n return new Uint8Array(Buffer.from(b64, 'base64'))\n}\n\nfunction imageFormat(mime: string): 'png' | 'jpeg' | 'gif' | 'webp' {\n switch (mime) {\n case 'image/png':\n return 'png'\n case 'image/jpeg':\n case 'image/jpg':\n return 'jpeg'\n case 'image/gif':\n return 'gif'\n case 'image/webp':\n return 'webp'\n default:\n throw new Error(\n `Bedrock Converse: unsupported image MIME type \"${mime}\". Supported types: image/png, image/jpeg, image/gif, image/webp.`,\n )\n }\n}\n\nfunction documentFormat(\n mime: string,\n): 'pdf' | 'csv' | 'doc' | 'docx' | 'xls' | 'xlsx' | 'html' | 'txt' | 'md' {\n switch (mime) {\n case 'application/pdf':\n return 'pdf'\n case 'text/csv':\n return 'csv'\n case 'application/msword':\n return 'doc'\n case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':\n return 'docx'\n case 'application/vnd.ms-excel':\n return 'xls'\n case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':\n return 'xlsx'\n case 'text/html':\n return 'html'\n case 'text/plain':\n return 'txt'\n case 'text/markdown':\n case 'text/x-markdown':\n return 'md'\n default:\n throw new Error(\n `Bedrock Converse: unsupported document MIME type \"${mime}\". Supported types: pdf, csv, doc, docx, xls, xlsx, html, txt, md.`,\n )\n }\n}\n\nfunction stringContent(content: string | null | Array<ContentPart>): string {\n if (content === null) return ''\n if (typeof content === 'string') return content\n return content\n .filter((p): p is TextPart => p.type === 'text')\n .map((p) => p.content)\n .join('')\n}\n\nfunction isTextPart(p: ContentPart): p is TextPart {\n return p.type === 'text'\n}\n\nfunction isImagePart(p: ContentPart): p is ImagePart {\n return p.type === 'image'\n}\n\nfunction isDocumentPart(p: ContentPart): p is DocumentPart {\n return p.type === 'document'\n}\n\nfunction isDataSource(\n source: ImagePart['source'] | DocumentPart['source'],\n): source is ContentPartDataSource {\n return source.type === 'data'\n}\n\nfunction contentPartToBlock(part: ContentPart, docIndex: number): ContentBlock {\n if (isTextPart(part)) {\n return { text: part.content }\n }\n\n if (isImagePart(part)) {\n const { source } = part\n if (!isDataSource(source)) {\n throw new Error(\n 'Bedrock Converse requires inline image bytes; URL image sources are not supported.',\n )\n }\n return {\n image: {\n format: imageFormat(source.mimeType),\n source: { bytes: base64ToBytes(source.value) },\n },\n }\n }\n\n if (isDocumentPart(part)) {\n const { source } = part\n if (!isDataSource(source)) {\n throw new Error(\n 'Bedrock Converse requires inline document bytes; URL document sources are not supported.',\n )\n }\n return {\n document: {\n format: documentFormat(source.mimeType),\n name: `document-${docIndex}`,\n source: { bytes: base64ToBytes(source.value) },\n },\n }\n }\n\n // Fail loud for unsupported part types (audio, video, etc.)\n throw new Error(\n `Bedrock Converse does not support content part type \"${String(part.type)}\".`,\n )\n}\n\nfunction messageToBlocks(\n msg: ModelMessage,\n docCounter: { value: number },\n): Array<ContentBlock> {\n const blocks: Array<ContentBlock> = []\n\n if (msg.role === 'tool') {\n if (!msg.toolCallId) {\n throw new Error(\n 'Bedrock Converse: tool message is missing toolCallId. Every tool result must reference the tool use ID it is responding to.',\n )\n }\n const textContent = stringContent(msg.content)\n const toolResult: ToolResultContentBlock = { text: textContent }\n blocks.push({\n toolResult: {\n toolUseId: msg.toolCallId,\n content: [toolResult],\n status: 'success',\n },\n })\n return blocks\n }\n\n // Map content field to text/image/document blocks\n if (typeof msg.content === 'string') {\n if (msg.content !== '') {\n blocks.push({ text: msg.content })\n }\n } else if (Array.isArray(msg.content)) {\n for (const part of msg.content) {\n const docIndex = isDocumentPart(part) ? ++docCounter.value : 0\n blocks.push(contentPartToBlock(part, docIndex))\n }\n }\n // null → no text blocks\n\n // Append toolUse blocks for assistant tool calls. Malformed or non-object\n // arguments come from a prior assistant turn the engine already accepted, so\n // they signal a real upstream problem — throw rather than silently coercing to\n // `{}` and forwarding a corrupted tool call (the adapter's catch surfaces it\n // as a RUN_ERROR).\n if (msg.role === 'assistant' && msg.toolCalls) {\n for (const call of msg.toolCalls) {\n const rawArguments = call.function.arguments || '{}'\n let parsed: unknown\n try {\n parsed = JSON.parse(rawArguments)\n } catch (error: unknown) {\n throw new Error(\n `Bedrock Converse: tool call \"${call.function.name}\" has malformed JSON arguments (${String(error)}). Raw: ${rawArguments}`,\n )\n }\n if (\n parsed === null ||\n typeof parsed !== 'object' ||\n Array.isArray(parsed)\n ) {\n throw new Error(\n `Bedrock Converse: tool call \"${call.function.name}\" arguments must be a JSON object, got ${Array.isArray(parsed) ? 'array' : typeof parsed}.`,\n )\n }\n blocks.push({\n toolUse: {\n toolUseId: call.id,\n name: call.function.name,\n input: parsed as DocumentType,\n },\n })\n }\n }\n\n return blocks\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Convert TanStack AI messages + system prompts into the Converse API format.\n *\n * - System prompts are lifted into `SystemContentBlock[]`.\n * - `tool` role messages are remapped to `user` role `toolResult` blocks.\n * - Consecutive messages with the same Converse role are merged (Converse\n * requires strict user/assistant alternation).\n */\nexport function toConverseMessages(\n messages: Array<ModelMessage>,\n systemPrompts?: Array<SystemPrompt>,\n): { system: Array<SystemContentBlock>; messages: Array<Message> } {\n // Build system blocks (uses normalizeSystemPrompts for runtime validation)\n const system: Array<SystemContentBlock> = normalizeSystemPrompts(\n systemPrompts,\n ).map((p) => ({ text: p.content }))\n\n // Convert each ModelMessage to a Converse Message, merging same-role pairs\n const converseMessages: Array<Message> = []\n // Global document counter: ensures every document block across all messages\n // gets a unique name, preventing Bedrock ValidationException for duplicate names.\n const docCounter = { value: 0 }\n\n for (const msg of messages) {\n // Map TanStack roles to Converse roles\n const converseRole: 'user' | 'assistant' =\n msg.role === 'assistant' ? 'assistant' : 'user'\n\n const blocks = messageToBlocks(msg, docCounter)\n\n // Skip messages that produce no content blocks (e.g. assistant with\n // null content and no toolCalls). Pushing an empty-content message to\n // Converse triggers a ValidationException.\n if (blocks.length === 0) continue\n\n const last = converseMessages[converseMessages.length - 1]\n if (last && last.role === converseRole) {\n // Merge into the previous message's content array\n last.content = [...(last.content ?? []), ...blocks]\n } else {\n converseMessages.push({ role: converseRole, content: blocks })\n }\n }\n\n return { system, messages: converseMessages }\n}\n"],"mappings":";;AAsBA,SAAS,cAAc,KAAyB;CAC9C,OAAO,IAAI,WAAW,OAAO,KAAK,KAAK,QAAQ,CAAC;AAClD;AAEA,SAAS,YAAY,MAA+C;CAClE,QAAQ,MAAR;EACE,KAAK,aACH,OAAO;EACT,KAAK;EACL,KAAK,aACH,OAAO;EACT,KAAK,aACH,OAAO;EACT,KAAK,cACH,OAAO;EACT,SACE,MAAM,IAAI,MACR,kDAAkD,KAAK,kEACzD;CACJ;AACF;AAEA,SAAS,eACP,MACyE;CACzE,QAAQ,MAAR;EACE,KAAK,mBACH,OAAO;EACT,KAAK,YACH,OAAO;EACT,KAAK,sBACH,OAAO;EACT,KAAK,2EACH,OAAO;EACT,KAAK,4BACH,OAAO;EACT,KAAK,qEACH,OAAO;EACT,KAAK,aACH,OAAO;EACT,KAAK,cACH,OAAO;EACT,KAAK;EACL,KAAK,mBACH,OAAO;EACT,SACE,MAAM,IAAI,MACR,qDAAqD,KAAK,mEAC5D;CACJ;AACF;AAEA,SAAS,cAAc,SAAqD;CAC1E,IAAI,YAAY,MAAM,OAAO;CAC7B,IAAI,OAAO,YAAY,UAAU,OAAO;CACxC,OAAO,QACJ,QAAQ,MAAqB,EAAE,SAAS,MAAM,CAAC,CAC/C,KAAK,MAAM,EAAE,OAAO,CAAC,CACrB,KAAK,EAAE;AACZ;AAEA,SAAS,WAAW,GAA+B;CACjD,OAAO,EAAE,SAAS;AACpB;AAEA,SAAS,YAAY,GAAgC;CACnD,OAAO,EAAE,SAAS;AACpB;AAEA,SAAS,eAAe,GAAmC;CACzD,OAAO,EAAE,SAAS;AACpB;AAEA,SAAS,aACP,QACiC;CACjC,OAAO,OAAO,SAAS;AACzB;AAEA,SAAS,mBAAmB,MAAmB,UAAgC;CAC7E,IAAI,WAAW,IAAI,GACjB,OAAO,EAAE,MAAM,KAAK,QAAQ;CAG9B,IAAI,YAAY,IAAI,GAAG;EACrB,MAAM,EAAE,WAAW;EACnB,IAAI,CAAC,aAAa,MAAM,GACtB,MAAM,IAAI,MACR,oFACF;EAEF,OAAO,EACL,OAAO;GACL,QAAQ,YAAY,OAAO,QAAQ;GACnC,QAAQ,EAAE,OAAO,cAAc,OAAO,KAAK,EAAE;EAC/C,EACF;CACF;CAEA,IAAI,eAAe,IAAI,GAAG;EACxB,MAAM,EAAE,WAAW;EACnB,IAAI,CAAC,aAAa,MAAM,GACtB,MAAM,IAAI,MACR,0FACF;EAEF,OAAO,EACL,UAAU;GACR,QAAQ,eAAe,OAAO,QAAQ;GACtC,MAAM,YAAY;GAClB,QAAQ,EAAE,OAAO,cAAc,OAAO,KAAK,EAAE;EAC/C,EACF;CACF;CAGA,MAAM,IAAI,MACR,wDAAwD,OAAO,KAAK,IAAI,EAAE,GAC5E;AACF;AAEA,SAAS,gBACP,KACA,YACqB;CACrB,MAAM,SAA8B,CAAC;CAErC,IAAI,IAAI,SAAS,QAAQ;EACvB,IAAI,CAAC,IAAI,YACP,MAAM,IAAI,MACR,6HACF;EAGF,MAAM,aAAqC,EAAE,MADzB,cAAc,IAAI,OACa,EAAY;EAC/D,OAAO,KAAK,EACV,YAAY;GACV,WAAW,IAAI;GACf,SAAS,CAAC,UAAU;GACpB,QAAQ;EACV,EACF,CAAC;EACD,OAAO;CACT;CAGA,IAAI,OAAO,IAAI,YAAY,UACrB;MAAA,IAAI,YAAY,IAClB,OAAO,KAAK,EAAE,MAAM,IAAI,QAAQ,CAAC;CAAA,OAE9B,IAAI,MAAM,QAAQ,IAAI,OAAO,GAClC,KAAK,MAAM,QAAQ,IAAI,SAAS;EAC9B,MAAM,WAAW,eAAe,IAAI,IAAI,EAAE,WAAW,QAAQ;EAC7D,OAAO,KAAK,mBAAmB,MAAM,QAAQ,CAAC;CAChD;CASF,IAAI,IAAI,SAAS,eAAe,IAAI,WAClC,KAAK,MAAM,QAAQ,IAAI,WAAW;EAChC,MAAM,eAAe,KAAK,SAAS,aAAa;EAChD,IAAI;EACJ,IAAI;GACF,SAAS,KAAK,MAAM,YAAY;EAClC,SAAS,OAAgB;GACvB,MAAM,IAAI,MACR,gCAAgC,KAAK,SAAS,KAAK,kCAAkC,OAAO,KAAK,EAAE,UAAU,cAC/G;EACF;EACA,IACE,WAAW,QACX,OAAO,WAAW,YAClB,MAAM,QAAQ,MAAM,GAEpB,MAAM,IAAI,MACR,gCAAgC,KAAK,SAAS,KAAK,yCAAyC,MAAM,QAAQ,MAAM,IAAI,UAAU,OAAO,OAAO,EAC9I;EAEF,OAAO,KAAK,EACV,SAAS;GACP,WAAW,KAAK;GAChB,MAAM,KAAK,SAAS;GACpB,OAAO;EACT,EACF,CAAC;CACH;CAGF,OAAO;AACT;;;;;;;;;AAcA,SAAgB,mBACd,UACA,eACiE;CAEjE,MAAM,SAAoC,uBACxC,aACF,CAAC,CAAC,KAAK,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE;CAGlC,MAAM,mBAAmC,CAAC;CAG1C,MAAM,aAAa,EAAE,OAAO,EAAE;CAE9B,KAAK,MAAM,OAAO,UAAU;EAE1B,MAAM,eACJ,IAAI,SAAS,cAAc,cAAc;EAE3C,MAAM,SAAS,gBAAgB,KAAK,UAAU;EAK9C,IAAI,OAAO,WAAW,GAAG;EAEzB,MAAM,OAAO,iBAAiB,iBAAiB,SAAS;EACxD,IAAI,QAAQ,KAAK,SAAS,cAExB,KAAK,UAAU,CAAC,GAAI,KAAK,WAAW,CAAC,GAAI,GAAG,MAAM;OAElD,iBAAiB,KAAK;GAAE,MAAM;GAAc,SAAS;EAAO,CAAC;CAEjE;CAEA,OAAO;EAAE;EAAQ,UAAU;CAAiB;AAC9C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-bedrock",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Amazon Bedrock adapter for TanStack AI — OpenAI-compatible chat, responses, tools, and reasoning.",
|
|
6
6
|
"author": "",
|
|
@@ -35,12 +35,12 @@
|
|
|
35
35
|
"tool-calling"
|
|
36
36
|
],
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@vitest/coverage-v8": "4.
|
|
39
|
-
"vite": "^8.1
|
|
38
|
+
"@vitest/coverage-v8": "4.1.10",
|
|
39
|
+
"vite": "^8.2.1"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"zod": "^4.0.0",
|
|
43
|
-
"@tanstack/ai": "^0.
|
|
43
|
+
"@tanstack/ai": "^0.45.0"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@aws-crypto/sha256-js": "^5.2.0",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"@smithy/signature-v4": "^5.4.5",
|
|
50
50
|
"@smithy/types": "^4.14.2",
|
|
51
51
|
"openai": "^6.41.0",
|
|
52
|
-
"@tanstack/openai-base": "^0.9.
|
|
52
|
+
"@tanstack/openai-base": "^0.9.13"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build": "vite build",
|
|
@@ -261,9 +261,17 @@ export class BedrockConverseTextAdapter<
|
|
|
261
261
|
`${this.name}.structuredOutput: response contained no forced-tool output`,
|
|
262
262
|
)
|
|
263
263
|
}
|
|
264
|
+
const usage = res.usage
|
|
264
265
|
return {
|
|
265
266
|
data: structured,
|
|
266
267
|
rawText: JSON.stringify(structured),
|
|
268
|
+
...(usage && {
|
|
269
|
+
usage: {
|
|
270
|
+
promptTokens: usage.inputTokens ?? 0,
|
|
271
|
+
completionTokens: usage.outputTokens ?? 0,
|
|
272
|
+
totalTokens: usage.totalTokens ?? 0,
|
|
273
|
+
},
|
|
274
|
+
}),
|
|
267
275
|
}
|
|
268
276
|
} catch (error: unknown) {
|
|
269
277
|
chatOptions.logger.errors(`${this.name}.structuredOutput fatal`, {
|
|
@@ -285,7 +293,6 @@ export class BedrockConverseTextAdapter<
|
|
|
285
293
|
options: StructuredOutputOptions<TProviderOptions>,
|
|
286
294
|
): AsyncIterable<StreamChunk> {
|
|
287
295
|
const { chatOptions, outputSchema } = options
|
|
288
|
-
const timestamp = Date.now()
|
|
289
296
|
const runId = this.generateId()
|
|
290
297
|
const threadId = chatOptions.threadId ?? this.generateId()
|
|
291
298
|
const messageId = this.generateId()
|
|
@@ -318,7 +325,7 @@ export class BedrockConverseTextAdapter<
|
|
|
318
325
|
runId,
|
|
319
326
|
threadId,
|
|
320
327
|
model: chatOptions.model,
|
|
321
|
-
timestamp,
|
|
328
|
+
timestamp: Date.now(),
|
|
322
329
|
parentRunId: chatOptions.parentRunId,
|
|
323
330
|
}
|
|
324
331
|
}
|
|
@@ -339,7 +346,7 @@ export class BedrockConverseTextAdapter<
|
|
|
339
346
|
messageId,
|
|
340
347
|
role: 'assistant',
|
|
341
348
|
model: chatOptions.model,
|
|
342
|
-
timestamp,
|
|
349
|
+
timestamp: Date.now(),
|
|
343
350
|
}
|
|
344
351
|
}
|
|
345
352
|
accumulatedRaw += fragment
|
|
@@ -349,7 +356,7 @@ export class BedrockConverseTextAdapter<
|
|
|
349
356
|
delta: fragment,
|
|
350
357
|
content: accumulatedRaw,
|
|
351
358
|
model: chatOptions.model,
|
|
352
|
-
timestamp,
|
|
359
|
+
timestamp: Date.now(),
|
|
353
360
|
}
|
|
354
361
|
}
|
|
355
362
|
continue
|
|
@@ -377,7 +384,7 @@ export class BedrockConverseTextAdapter<
|
|
|
377
384
|
runId,
|
|
378
385
|
threadId,
|
|
379
386
|
model: chatOptions.model,
|
|
380
|
-
timestamp,
|
|
387
|
+
timestamp: Date.now(),
|
|
381
388
|
parentRunId: chatOptions.parentRunId,
|
|
382
389
|
}
|
|
383
390
|
}
|
|
@@ -387,7 +394,7 @@ export class BedrockConverseTextAdapter<
|
|
|
387
394
|
type: EventType.TEXT_MESSAGE_END,
|
|
388
395
|
messageId,
|
|
389
396
|
model: chatOptions.model,
|
|
390
|
-
timestamp,
|
|
397
|
+
timestamp: Date.now(),
|
|
391
398
|
}
|
|
392
399
|
}
|
|
393
400
|
|
|
@@ -396,7 +403,7 @@ export class BedrockConverseTextAdapter<
|
|
|
396
403
|
type: EventType.RUN_ERROR,
|
|
397
404
|
runId,
|
|
398
405
|
model: chatOptions.model,
|
|
399
|
-
timestamp,
|
|
406
|
+
timestamp: Date.now(),
|
|
400
407
|
message: `${this.name}.structuredOutputStream: response contained no content`,
|
|
401
408
|
code: 'empty-response',
|
|
402
409
|
error: {
|
|
@@ -415,7 +422,7 @@ export class BedrockConverseTextAdapter<
|
|
|
415
422
|
type: EventType.RUN_ERROR,
|
|
416
423
|
runId,
|
|
417
424
|
model: chatOptions.model,
|
|
418
|
-
timestamp,
|
|
425
|
+
timestamp: Date.now(),
|
|
419
426
|
message: `Failed to parse structured output as JSON. Content: ${accumulatedRaw.slice(0, 200)}${accumulatedRaw.length > 200 ? '...' : ''}`,
|
|
420
427
|
code: 'parse-error',
|
|
421
428
|
error: {
|
|
@@ -434,7 +441,7 @@ export class BedrockConverseTextAdapter<
|
|
|
434
441
|
raw: accumulatedRaw,
|
|
435
442
|
},
|
|
436
443
|
model: chatOptions.model,
|
|
437
|
-
timestamp,
|
|
444
|
+
timestamp: Date.now(),
|
|
438
445
|
}
|
|
439
446
|
|
|
440
447
|
yield {
|
|
@@ -442,7 +449,7 @@ export class BedrockConverseTextAdapter<
|
|
|
442
449
|
runId,
|
|
443
450
|
threadId,
|
|
444
451
|
model: chatOptions.model,
|
|
445
|
-
timestamp,
|
|
452
|
+
timestamp: Date.now(),
|
|
446
453
|
finishReason,
|
|
447
454
|
}
|
|
448
455
|
} catch (error: unknown) {
|
|
@@ -453,7 +460,7 @@ export class BedrockConverseTextAdapter<
|
|
|
453
460
|
runId,
|
|
454
461
|
threadId,
|
|
455
462
|
model: chatOptions.model,
|
|
456
|
-
timestamp,
|
|
463
|
+
timestamp: Date.now(),
|
|
457
464
|
parentRunId: chatOptions.parentRunId,
|
|
458
465
|
}
|
|
459
466
|
}
|
|
@@ -469,7 +476,7 @@ export class BedrockConverseTextAdapter<
|
|
|
469
476
|
type: EventType.RUN_ERROR,
|
|
470
477
|
runId,
|
|
471
478
|
model: chatOptions.model,
|
|
472
|
-
timestamp,
|
|
479
|
+
timestamp: Date.now(),
|
|
473
480
|
message: errorPayload.message,
|
|
474
481
|
...(errorPayload.code !== undefined && { code: errorPayload.code }),
|
|
475
482
|
error: {
|