@tanstack/ai-bedrock 0.3.13 → 0.4.1

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.
@@ -3,7 +3,7 @@ import { ResolvedBedrockAuth } from '../utils/auth.js';
3
3
  import { BedrockRuntimeClient, BedrockRuntimeClientConfig, ConverseCommandInput, ConverseCommandOutput, ConverseStreamCommandInput, ConverseStreamOutput } from '@aws-sdk/client-bedrock-runtime';
4
4
  import { Modality, AdapterYieldChunk, TextOptions } from '@tanstack/ai';
5
5
  import { BedrockClientConfig } from '../utils/client.js';
6
- import { BedrockMessageMetadataByModality } from '../message-types.js';
6
+ import { BedrockMessageMetadataByModality, BedrockSystemPromptMetadata } from '../message-types.js';
7
7
  import { BedrockConverseModels, ResolveConverseProviderOptions, ResolveInputModalities } from '../model-meta.js';
8
8
  import type * as BedrockRuntime from '@aws-sdk/client-bedrock-runtime';
9
9
  /** Config for the Converse adapter — same client config as the chat adapter. */
@@ -23,7 +23,7 @@ export interface BedrockConverseConfig extends BedrockClientConfig {
23
23
  * so tests can subclass and inject canned Converse SDK shapes without a real
24
24
  * AWS request.
25
25
  */
26
- export declare class BedrockConverseTextAdapter<TModel extends BedrockConverseModels, TProviderOptions extends Record<string, any> = ResolveConverseProviderOptions<TModel>, TInputModalities extends ReadonlyArray<Modality> = ResolveInputModalities<TModel>> extends BaseTextAdapter<TModel, TProviderOptions, TInputModalities, BedrockMessageMetadataByModality> {
26
+ export declare class BedrockConverseTextAdapter<TModel extends BedrockConverseModels, TProviderOptions extends Record<string, any> = ResolveConverseProviderOptions<TModel>, TInputModalities extends ReadonlyArray<Modality> = ResolveInputModalities<TModel>> extends BaseTextAdapter<TModel, TProviderOptions, TInputModalities, BedrockMessageMetadataByModality, ReadonlyArray<string>, unknown, BedrockSystemPromptMetadata> {
27
27
  readonly kind: "text";
28
28
  readonly name: "bedrock-converse";
29
29
  private clientPromise?;
@@ -404,10 +404,12 @@ function convertTools(tools) {
404
404
  properties: {},
405
405
  required: []
406
406
  };
407
+ const { cachePoint } = tool.metadata ?? {};
407
408
  return {
408
409
  name: tool.name,
409
410
  description: tool.description,
410
- inputSchema
411
+ inputSchema,
412
+ ...cachePoint && { cachePoint }
411
413
  };
412
414
  });
413
415
  }
@@ -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 { buildConverseUsage } from '../converse/usage'\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 AdapterYieldChunk,\n TextOptions,\n TokenUsage,\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 const client = new BedrockRuntimeClient(\n this.buildClientConfig(resolved, region, this.clientConfig.baseURL),\n )\n const defaultHeaders = this.clientConfig.defaultHeaders\n if (defaultHeaders) {\n // `build` runs before SigV4 signing, so gateway headers are signed\n // along with the rest of the request.\n client.middlewareStack.add(\n (next) => (args) => {\n const request = args.request\n if (\n typeof request === 'object' &&\n request !== null &&\n 'headers' in request &&\n typeof request.headers === 'object' &&\n request.headers !== null\n ) {\n Object.assign(request.headers, defaultHeaders)\n }\n return next(args)\n },\n { step: 'build', name: 'tanstackDefaultHeaders' },\n )\n }\n return client\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<AdapterYieldChunk> {\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 && { usage: buildConverseUsage(usage) }),\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<AdapterYieldChunk> {\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 // Usage arrives on the trailing `metadata` event, after the finish signal,\n // so it is captured during iteration and folded into RUN_FINISHED below.\n let usage: TokenUsage | undefined\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 if ('metadata' in ev) {\n const u = ev.metadata?.usage\n if (u) {\n usage = buildConverseUsage(u)\n }\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 ...(usage && { usage }),\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":"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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 { buildConverseUsage } from '../converse/usage'\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 AdapterYieldChunk,\n TextOptions,\n TokenUsage,\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 {\n BedrockMessageMetadataByModality,\n BedrockSystemPromptMetadata,\n BedrockToolMetadata,\n} 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 // TToolCapabilities — Converse has no per-model tool-capability table; the\n // base default.\n ReadonlyArray<string>,\n // TToolCallMetadata — Converse has no tool-call metadata round-tripping.\n unknown,\n // TSystemPromptMetadata — narrows `systemPrompts[i].metadata` at the chat()\n // call site so users get `cachePoint` autocomplete.\n BedrockSystemPromptMetadata\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 const client = new BedrockRuntimeClient(\n this.buildClientConfig(resolved, region, this.clientConfig.baseURL),\n )\n const defaultHeaders = this.clientConfig.defaultHeaders\n if (defaultHeaders) {\n // `build` runs before SigV4 signing, so gateway headers are signed\n // along with the rest of the request.\n client.middlewareStack.add(\n (next) => (args) => {\n const request = args.request\n if (\n typeof request === 'object' &&\n request !== null &&\n 'headers' in request &&\n typeof request.headers === 'object' &&\n request.headers !== null\n ) {\n Object.assign(request.headers, defaultHeaders)\n }\n return next(args)\n },\n { step: 'build', name: 'tanstackDefaultHeaders' },\n )\n }\n return client\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<AdapterYieldChunk> {\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 && { usage: buildConverseUsage(usage) }),\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<AdapterYieldChunk> {\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 // Usage arrives on the trailing `metadata` event, after the finish signal,\n // so it is captured during iteration and folded into RUN_FINISHED below.\n let usage: TokenUsage | undefined\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 if ('metadata' in ev) {\n const u = ev.metadata?.usage\n if (u) {\n usage = buildConverseUsage(u)\n }\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 ...(usage && { usage }),\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 const { cachePoint }: BedrockToolMetadata = tool.metadata ?? {}\n return {\n name: tool.name,\n description: tool.description,\n inputSchema,\n ...(cachePoint && { cachePoint }),\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,6 +1,6 @@
1
1
  import { resolveBedrockAuth } from "../utils/auth.js";
2
2
  import { BEDROCK_EMBEDDING_MODELS } from "../model-meta.js";
3
- import { requireTextOnlyEmbeddingInput, resolveEmbeddingInput } from "@tanstack/ai";
3
+ import { isFileSource, requireTextOnlyEmbeddingInput, resolveEmbeddingInput, unsupportedFileSourceError } from "@tanstack/ai";
4
4
  import { BaseEmbeddingAdapter } from "@tanstack/ai/adapters";
5
5
  import { toRunErrorPayload } from "@tanstack/ai/adapter-internals";
6
6
  //#region src/adapters/embedding.ts
@@ -244,6 +244,7 @@ function readCohereEmbeddingBody(raw, context) {
244
244
  */
245
245
  function toTitanInputImage(image, model) {
246
246
  const source = image.source;
247
+ if (isFileSource(source)) throw unsupportedFileSourceError("bedrock");
247
248
  if (source.type === "data") return source.value;
248
249
  if (source.value.startsWith("data:")) {
249
250
  const comma = source.value.indexOf(",");
@@ -1 +1 @@
1
- {"version":3,"file":"embedding.js","names":[],"sources":["../../../src/adapters/embedding.ts"],"sourcesContent":["import { BaseEmbeddingAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport {\n requireTextOnlyEmbeddingInput,\n resolveEmbeddingInput,\n} from '@tanstack/ai'\nimport { resolveBedrockAuth } from '../utils/auth'\nimport { BEDROCK_EMBEDDING_MODELS } from '../model-meta'\nimport type * as BedrockRuntime from '@aws-sdk/client-bedrock-runtime'\nimport type {\n BedrockRuntimeClient,\n BedrockRuntimeClientConfig,\n} from '@aws-sdk/client-bedrock-runtime'\nimport type {\n EmbeddingOptions,\n EmbeddingResult,\n ImagePart,\n TokenUsage,\n} from '@tanstack/ai'\nimport type { ResolvedBedrockAuth } from '../utils/auth'\nimport type { BedrockClientConfig } from '../utils/client'\nimport type {\n BedrockEmbeddingModel,\n BedrockEmbeddingModelInputModalitiesByName,\n BedrockEmbeddingModelProviderOptionsByName,\n ResolveEmbeddingProviderOptions,\n} from '../model-meta'\n\n/**\n * Config for the Bedrock embedding adapter — the same auth surface as the\n * other Bedrock adapters (apiKey → env → SigV4 via `resolveBedrockAuth`),\n * minus the OpenAI-compat client options that don't apply to `InvokeModel`.\n */\nexport interface BedrockEmbeddingConfig extends Pick<\n BedrockClientConfig,\n 'apiKey' | 'region' | 'auth' | 'baseURL'\n> {}\n\n/** InvokeModel calls issued concurrently during a per-item fan-out. */\nconst MAX_CONCURRENT_INVOCATIONS = 5\n\n/** Valid `dimensions` for `amazon.titan-embed-text-v2:0`. */\nconst TITAN_TEXT_DIMENSIONS: ReadonlyArray<number> = [256, 512, 1024]\n\n/** Valid `dimensions` (outputEmbeddingLength) for `amazon.titan-embed-image-v1`. */\nconst TITAN_IMAGE_DIMENSIONS: ReadonlyArray<number> = [256, 384, 1024]\nconst TITAN_IMAGE_DEFAULT_DIMENSIONS = 1024\n\n/** Cohere embed accepts at most 96 texts per InvokeModel call. */\nconst COHERE_MAX_BATCH_SIZE = 96\n\n/**\n * Bedrock Embedding Adapter\n *\n * Tree-shakeable adapter for embeddings served through Bedrock's native\n * `InvokeModel` API (embedding models have no Converse surface). Each model\n * family has its own JSON body dialect:\n *\n * - `amazon.titan-embed-text-v2:0` — text-only, ONE text per call; the batch\n * is fanned out with a small concurrency cap and per-call\n * `inputTextTokenCount`s are summed into usage.\n * - `amazon.titan-embed-image-v1` — MULTIMODAL: text, image, or a fused\n * text+image item embedded into a single vector; one item per call.\n * - `cohere.embed-english-v3` / `cohere.embed-multilingual-v3` — text-only,\n * batched natively (chunked at 96 texts per call). `inputType` is required.\n *\n * The SDK call lives behind a protected `invokeModel` seam so tests can\n * subclass and inject canned response bodies without a real AWS request, and\n * the AWS SDK itself is imported lazily (it's Node/server-only).\n */\nexport class BedrockEmbeddingAdapter<\n TModel extends BedrockEmbeddingModel,\n // Same rationale as the text adapters: the base parameterises\n // `TProviderOptions extends object`, and the per-model options interfaces\n // lack implicit index signatures — `Record<string, any>` (not `unknown`)\n // accepts them. Confined to the generic constraint; no value cast.\n TProviderOptions extends Record<string, any> =\n ResolveEmbeddingProviderOptions<TModel>,\n> extends BaseEmbeddingAdapter<\n TModel,\n TProviderOptions,\n BedrockEmbeddingModelProviderOptionsByName,\n BedrockEmbeddingModelInputModalitiesByName\n> {\n readonly name = 'bedrock' as const\n private clientPromise?: Promise<BedrockRuntimeClient>\n private readonly clientConfig: BedrockEmbeddingConfig\n\n constructor(config: BedrockEmbeddingConfig, 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\n * held in a variable (not a string literal) so bundler dep scanners cannot\n * statically discover the AWS SDK and try to pre-bundle it for the browser.\n * Same pattern as the Converse text adapter.\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`, deferring\n * `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`. Bearer\n * auth needs `authSchemePreference` pinned or the SDK still tries SigV4\n * first — same reasoning as the Converse text adapter.\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 seam (overridden in tests so no real AWS call happens)\n // ---------------------------------------------------------------------------\n\n /** Send one InvokeModel call and parse its JSON response body. */\n protected async invokeModel(\n modelId: string,\n body: Record<string, unknown>,\n ): Promise<unknown> {\n const { InvokeModelCommand } = await this.importBedrockRuntime()\n const client = await this.getClient()\n const response = await client.send(\n new InvokeModelCommand({\n modelId,\n contentType: 'application/json',\n accept: 'application/json',\n body: JSON.stringify(body),\n }),\n )\n return JSON.parse(new TextDecoder().decode(response.body))\n }\n\n // ---------------------------------------------------------------------------\n // Public adapter surface\n // ---------------------------------------------------------------------------\n\n async createEmbeddings(\n options: EmbeddingOptions<TProviderOptions>,\n ): Promise<EmbeddingResult> {\n const { model, logger } = options\n try {\n logger.request(\n `activity=embed provider=${this.name} model=${model} inputs=${options.input.length}`,\n { provider: this.name, model },\n )\n switch (model) {\n case 'amazon.titan-embed-text-v2:0':\n return await this.embedTitanText(options)\n case 'amazon.titan-embed-image-v1':\n return await this.embedTitanImage(options)\n case 'cohere.embed-english-v3':\n case 'cohere.embed-multilingual-v3':\n return await this.embedCohere(options)\n default:\n throw new Error(\n `Unknown Bedrock embedding model \"${model}\". Supported models: ` +\n `${BEDROCK_EMBEDDING_MODELS.join(', ')}.`,\n )\n }\n } catch (error: unknown) {\n logger.errors(`${this.name}.createEmbeddings fatal`, {\n error: toRunErrorPayload(error, `${this.name}.createEmbeddings failed`),\n source: `${this.name}.createEmbeddings`,\n })\n throw error\n }\n }\n\n // ---------------------------------------------------------------------------\n // Per-model request mapping\n // ---------------------------------------------------------------------------\n\n /**\n * `amazon.titan-embed-text-v2:0` — one text per InvokeModel call, fanned\n * out with a concurrency cap; result order matches input order and per-call\n * `inputTextTokenCount`s are summed into usage.\n */\n private async embedTitanText(\n options: EmbeddingOptions<TProviderOptions>,\n ): Promise<EmbeddingResult> {\n const { model, dimensions } = options\n if (\n dimensions !== undefined &&\n !TITAN_TEXT_DIMENSIONS.includes(dimensions)\n ) {\n throw new Error(\n `${model} supports dimensions 256, 512, or 1024; got ${dimensions}`,\n )\n }\n const normalize: boolean | undefined = options.modelOptions?.normalize\n const texts = requireTextOnlyEmbeddingInput(options.input, this.name, model)\n\n const responses = await mapWithConcurrency(\n texts,\n MAX_CONCURRENT_INVOCATIONS,\n async (text) => {\n // Built incrementally: exactOptionalPropertyTypes is on, and Titan\n // rejects explicit nulls/undefined for absent optional fields.\n const body: Record<string, unknown> = { inputText: text }\n if (dimensions !== undefined) body.dimensions = dimensions\n if (normalize !== undefined) body.normalize = normalize\n return readTitanEmbeddingBody(\n await this.invokeModel(model, body),\n `${this.name} ${model}`,\n )\n },\n )\n\n return this.toTitanResult(model, responses)\n }\n\n /**\n * `amazon.titan-embed-image-v1` (Titan Multimodal) — one item per\n * InvokeModel call. An item may carry text, an image, or both (a fused\n * item embedded into a single vector). Titan accepts at most one image per\n * request and never fetches remote URLs.\n */\n private async embedTitanImage(\n options: EmbeddingOptions<TProviderOptions>,\n ): Promise<EmbeddingResult> {\n const { model, dimensions } = options\n const outputEmbeddingLength = dimensions ?? TITAN_IMAGE_DEFAULT_DIMENSIONS\n if (!TITAN_IMAGE_DIMENSIONS.includes(outputEmbeddingLength)) {\n throw new Error(\n `${model} supports dimensions 256, 384, or 1024; got ${outputEmbeddingLength}`,\n )\n }\n const items = resolveEmbeddingInput(options.input)\n\n const responses = await mapWithConcurrency(\n items,\n MAX_CONCURRENT_INVOCATIONS,\n async (item, index) => {\n if (item.images.length > 1) {\n throw new Error(\n `${model} accepts at most one image per input item; input item ` +\n `at index ${index} contains ${item.images.length} images. ` +\n `Pass them as separate input items (one vector each).`,\n )\n }\n const body: Record<string, unknown> = {\n embeddingConfig: { outputEmbeddingLength },\n }\n if (item.texts.length > 0) body.inputText = item.texts.join('\\n')\n const image = item.images[0]\n if (image) body.inputImage = toTitanInputImage(image, model)\n return readTitanEmbeddingBody(\n await this.invokeModel(model, body),\n `${this.name} ${model}`,\n )\n },\n )\n\n return this.toTitanResult(model, responses)\n }\n\n /**\n * `cohere.embed-*-v3` — natively batched (chunked at 96 texts per call,\n * order preserved across chunks). `inputType` is required by the Cohere\n * API; output dimensionality is fixed, so `dimensions` is rejected.\n */\n private async embedCohere(\n options: EmbeddingOptions<TProviderOptions>,\n ): Promise<EmbeddingResult> {\n const { model, dimensions } = options\n if (dimensions !== undefined) {\n throw new Error(\n `${model} does not support the dimensions option; its output size is fixed`,\n )\n }\n const inputType: string | undefined = options.modelOptions?.inputType\n if (inputType === undefined) {\n throw new Error(\n `${model} requires modelOptions.inputType ('search_document' | ` +\n `'search_query' | 'classification' | 'clustering')`,\n )\n }\n const truncate: string | undefined = options.modelOptions?.truncate\n const texts = requireTextOnlyEmbeddingInput(options.input, this.name, model)\n const batches = chunk(texts, COHERE_MAX_BATCH_SIZE)\n\n const responses = await mapWithConcurrency(\n batches,\n MAX_CONCURRENT_INVOCATIONS,\n async (batch) => {\n const body: Record<string, unknown> = {\n texts: batch,\n input_type: inputType,\n }\n if (truncate !== undefined) body.truncate = truncate\n return readCohereEmbeddingBody(\n await this.invokeModel(model, body),\n `${this.name} ${model}`,\n )\n },\n )\n\n return {\n id: this.generateId(),\n model,\n embeddings: responses.flat().map((vector, index) => ({ vector, index })),\n }\n }\n\n /** Assemble an EmbeddingResult from per-item Titan responses. */\n private toTitanResult(\n model: string,\n responses: Array<TitanEmbeddingBody>,\n ): EmbeddingResult {\n let promptTokens = 0\n const embeddings = responses.map((response, index) => {\n promptTokens += response.inputTextTokenCount\n return { vector: response.embedding, index }\n })\n const usage: TokenUsage = {\n promptTokens,\n completionTokens: 0,\n totalTokens: promptTokens,\n }\n return { id: this.generateId(), model, embeddings, usage }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Response-body narrowing (SDK JSON boundary)\n// ---------------------------------------------------------------------------\n\ninterface TitanEmbeddingBody {\n embedding: Array<number>\n /** 0 when the response omits it (e.g. image-only Titan Multimodal calls). */\n inputTextTokenCount: number\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\n/** Narrow a Titan InvokeModel JSON body: `{ embedding, inputTextTokenCount? }`. */\nfunction readTitanEmbeddingBody(\n raw: unknown,\n context: string,\n): TitanEmbeddingBody {\n const embedding =\n isRecord(raw) && Array.isArray(raw.embedding) ? raw.embedding : undefined\n if (!embedding) {\n throw new Error(\n `${context}: response body is missing the \"embedding\" array`,\n )\n }\n const inputTextTokenCount =\n isRecord(raw) && typeof raw.inputTextTokenCount === 'number'\n ? raw.inputTextTokenCount\n : 0\n return { embedding, inputTextTokenCount }\n}\n\n/** Narrow a Cohere InvokeModel JSON body: `{ embeddings: number[][] }` (float). */\nfunction readCohereEmbeddingBody(\n raw: unknown,\n context: string,\n): Array<Array<number>> {\n const embeddings =\n isRecord(raw) && Array.isArray(raw.embeddings) ? raw.embeddings : undefined\n if (!embeddings) {\n throw new Error(\n `${context}: response body is missing the \"embeddings\" array`,\n )\n }\n return embeddings\n}\n\n// ---------------------------------------------------------------------------\n// Input mapping helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Map an ImagePart to Titan's `inputImage` (RAW base64, no data: prefix).\n * Accepts `data` sources as-is and `url` sources ONLY when the value is a\n * `data:` URI; Titan cannot fetch remote http(s) URLs.\n */\nfunction toTitanInputImage(image: ImagePart, model: string): string {\n const source = image.source\n if (source.type === 'data') {\n return source.value\n }\n if (source.value.startsWith('data:')) {\n const comma = source.value.indexOf(',')\n if (comma !== -1) {\n return source.value.slice(comma + 1)\n }\n }\n throw new Error(\n `Bedrock Titan does not fetch remote image URLs; pass base64 data ` +\n `(a { type: 'data' } source or a data: URI) for ${model}.`,\n )\n}\n\n/** Split into runs of at most `size`, preserving order. */\nfunction chunk<T>(items: Array<T>, size: number): Array<Array<T>> {\n const chunks: Array<Array<T>> = []\n for (let i = 0; i < items.length; i += size) {\n chunks.push(items.slice(i, i + size))\n }\n return chunks\n}\n\n/**\n * Map `fn` over `items` with at most `limit` calls in flight, returning\n * results in input order (result[i] corresponds to items[i] regardless of\n * completion order). Rejects with the first error.\n */\nasync function mapWithConcurrency<T, TResult>(\n items: ReadonlyArray<T>,\n limit: number,\n fn: (item: T, index: number) => Promise<TResult>,\n): Promise<Array<TResult>> {\n const results = new Array<TResult>(items.length)\n let next = 0\n const worker = async (): Promise<void> => {\n while (next < items.length) {\n const index = next++\n const item = items[index]\n if (item === undefined) continue // unreachable: index < length\n results[index] = await fn(item, index)\n }\n }\n const workerCount = Math.max(1, Math.min(limit, items.length))\n await Promise.all(Array.from({ length: workerCount }, () => worker()))\n return results\n}\n\n// ---------------------------------------------------------------------------\n// Factories\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a Bedrock embedding adapter with an explicit API key (bearer).\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'amazon.titan-embed-text-v2:0')\n * @param apiKey - Your Bedrock API key\n * @param config - Optional additional configuration (region, baseURL, ...)\n * @returns Configured Bedrock embedding adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createBedrockEmbedding(\n * 'amazon.titan-embed-text-v2:0',\n * 'bedrock-api-key',\n * );\n *\n * const result = await embed({\n * adapter,\n * input: 'a red guitar',\n * });\n * ```\n */\nexport function createBedrockEmbedding<TModel extends BedrockEmbeddingModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<BedrockEmbeddingConfig, 'apiKey'>,\n): BedrockEmbeddingAdapter<TModel> {\n // Explicit apiKey is authoritative — spread config first so it can't override.\n return new BedrockEmbeddingAdapter({ ...config, apiKey }, model)\n}\n\n/**\n * Creates a Bedrock embedding adapter using the ambient auth cascade:\n * `config.apiKey` → `BEDROCK_API_KEY` → `AWS_BEARER_TOKEN_BEDROCK` → SigV4\n * (AWS credential provider chain). Auth resolves lazily on the first\n * request, so `auth: 'sigv4'` never requires an API key.\n *\n * @param model - The model name (e.g., 'cohere.embed-english-v3')\n * @param config - Optional configuration (region, auth, baseURL, ...)\n * @returns Configured Bedrock embedding adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = bedrockEmbedding('cohere.embed-english-v3');\n *\n * const result = await embed({\n * adapter,\n * input: ['a red guitar', 'a blue drum kit'],\n * modelOptions: { inputType: 'search_document' },\n * });\n *\n * console.log(result.embeddings[0].vector)\n * ```\n */\nexport function bedrockEmbedding<TModel extends BedrockEmbeddingModel>(\n model: TModel,\n config?: BedrockEmbeddingConfig,\n): BedrockEmbeddingAdapter<TModel> {\n return new BedrockEmbeddingAdapter(config ?? {}, model)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"embedding.js","names":[],"sources":["../../../src/adapters/embedding.ts"],"sourcesContent":["import { BaseEmbeddingAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport {\n isFileSource,\n requireTextOnlyEmbeddingInput,\n resolveEmbeddingInput,\n unsupportedFileSourceError,\n} from '@tanstack/ai'\nimport { resolveBedrockAuth } from '../utils/auth'\nimport { BEDROCK_EMBEDDING_MODELS } from '../model-meta'\nimport type * as BedrockRuntime from '@aws-sdk/client-bedrock-runtime'\nimport type {\n BedrockRuntimeClient,\n BedrockRuntimeClientConfig,\n} from '@aws-sdk/client-bedrock-runtime'\nimport type {\n EmbeddingOptions,\n EmbeddingResult,\n ImagePart,\n TokenUsage,\n} from '@tanstack/ai'\nimport type { ResolvedBedrockAuth } from '../utils/auth'\nimport type { BedrockClientConfig } from '../utils/client'\nimport type {\n BedrockEmbeddingModel,\n BedrockEmbeddingModelInputModalitiesByName,\n BedrockEmbeddingModelProviderOptionsByName,\n ResolveEmbeddingProviderOptions,\n} from '../model-meta'\n\n/**\n * Config for the Bedrock embedding adapter — the same auth surface as the\n * other Bedrock adapters (apiKey → env → SigV4 via `resolveBedrockAuth`),\n * minus the OpenAI-compat client options that don't apply to `InvokeModel`.\n */\nexport interface BedrockEmbeddingConfig extends Pick<\n BedrockClientConfig,\n 'apiKey' | 'region' | 'auth' | 'baseURL'\n> {}\n\n/** InvokeModel calls issued concurrently during a per-item fan-out. */\nconst MAX_CONCURRENT_INVOCATIONS = 5\n\n/** Valid `dimensions` for `amazon.titan-embed-text-v2:0`. */\nconst TITAN_TEXT_DIMENSIONS: ReadonlyArray<number> = [256, 512, 1024]\n\n/** Valid `dimensions` (outputEmbeddingLength) for `amazon.titan-embed-image-v1`. */\nconst TITAN_IMAGE_DIMENSIONS: ReadonlyArray<number> = [256, 384, 1024]\nconst TITAN_IMAGE_DEFAULT_DIMENSIONS = 1024\n\n/** Cohere embed accepts at most 96 texts per InvokeModel call. */\nconst COHERE_MAX_BATCH_SIZE = 96\n\n/**\n * Bedrock Embedding Adapter\n *\n * Tree-shakeable adapter for embeddings served through Bedrock's native\n * `InvokeModel` API (embedding models have no Converse surface). Each model\n * family has its own JSON body dialect:\n *\n * - `amazon.titan-embed-text-v2:0` — text-only, ONE text per call; the batch\n * is fanned out with a small concurrency cap and per-call\n * `inputTextTokenCount`s are summed into usage.\n * - `amazon.titan-embed-image-v1` — MULTIMODAL: text, image, or a fused\n * text+image item embedded into a single vector; one item per call.\n * - `cohere.embed-english-v3` / `cohere.embed-multilingual-v3` — text-only,\n * batched natively (chunked at 96 texts per call). `inputType` is required.\n *\n * The SDK call lives behind a protected `invokeModel` seam so tests can\n * subclass and inject canned response bodies without a real AWS request, and\n * the AWS SDK itself is imported lazily (it's Node/server-only).\n */\nexport class BedrockEmbeddingAdapter<\n TModel extends BedrockEmbeddingModel,\n // Same rationale as the text adapters: the base parameterises\n // `TProviderOptions extends object`, and the per-model options interfaces\n // lack implicit index signatures — `Record<string, any>` (not `unknown`)\n // accepts them. Confined to the generic constraint; no value cast.\n TProviderOptions extends Record<string, any> =\n ResolveEmbeddingProviderOptions<TModel>,\n> extends BaseEmbeddingAdapter<\n TModel,\n TProviderOptions,\n BedrockEmbeddingModelProviderOptionsByName,\n BedrockEmbeddingModelInputModalitiesByName\n> {\n readonly name = 'bedrock' as const\n private clientPromise?: Promise<BedrockRuntimeClient>\n private readonly clientConfig: BedrockEmbeddingConfig\n\n constructor(config: BedrockEmbeddingConfig, 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\n * held in a variable (not a string literal) so bundler dep scanners cannot\n * statically discover the AWS SDK and try to pre-bundle it for the browser.\n * Same pattern as the Converse text adapter.\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`, deferring\n * `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`. Bearer\n * auth needs `authSchemePreference` pinned or the SDK still tries SigV4\n * first — same reasoning as the Converse text adapter.\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 seam (overridden in tests so no real AWS call happens)\n // ---------------------------------------------------------------------------\n\n /** Send one InvokeModel call and parse its JSON response body. */\n protected async invokeModel(\n modelId: string,\n body: Record<string, unknown>,\n ): Promise<unknown> {\n const { InvokeModelCommand } = await this.importBedrockRuntime()\n const client = await this.getClient()\n const response = await client.send(\n new InvokeModelCommand({\n modelId,\n contentType: 'application/json',\n accept: 'application/json',\n body: JSON.stringify(body),\n }),\n )\n return JSON.parse(new TextDecoder().decode(response.body))\n }\n\n // ---------------------------------------------------------------------------\n // Public adapter surface\n // ---------------------------------------------------------------------------\n\n async createEmbeddings(\n options: EmbeddingOptions<TProviderOptions>,\n ): Promise<EmbeddingResult> {\n const { model, logger } = options\n try {\n logger.request(\n `activity=embed provider=${this.name} model=${model} inputs=${options.input.length}`,\n { provider: this.name, model },\n )\n switch (model) {\n case 'amazon.titan-embed-text-v2:0':\n return await this.embedTitanText(options)\n case 'amazon.titan-embed-image-v1':\n return await this.embedTitanImage(options)\n case 'cohere.embed-english-v3':\n case 'cohere.embed-multilingual-v3':\n return await this.embedCohere(options)\n default:\n throw new Error(\n `Unknown Bedrock embedding model \"${model}\". Supported models: ` +\n `${BEDROCK_EMBEDDING_MODELS.join(', ')}.`,\n )\n }\n } catch (error: unknown) {\n logger.errors(`${this.name}.createEmbeddings fatal`, {\n error: toRunErrorPayload(error, `${this.name}.createEmbeddings failed`),\n source: `${this.name}.createEmbeddings`,\n })\n throw error\n }\n }\n\n // ---------------------------------------------------------------------------\n // Per-model request mapping\n // ---------------------------------------------------------------------------\n\n /**\n * `amazon.titan-embed-text-v2:0` — one text per InvokeModel call, fanned\n * out with a concurrency cap; result order matches input order and per-call\n * `inputTextTokenCount`s are summed into usage.\n */\n private async embedTitanText(\n options: EmbeddingOptions<TProviderOptions>,\n ): Promise<EmbeddingResult> {\n const { model, dimensions } = options\n if (\n dimensions !== undefined &&\n !TITAN_TEXT_DIMENSIONS.includes(dimensions)\n ) {\n throw new Error(\n `${model} supports dimensions 256, 512, or 1024; got ${dimensions}`,\n )\n }\n const normalize: boolean | undefined = options.modelOptions?.normalize\n const texts = requireTextOnlyEmbeddingInput(options.input, this.name, model)\n\n const responses = await mapWithConcurrency(\n texts,\n MAX_CONCURRENT_INVOCATIONS,\n async (text) => {\n // Built incrementally: exactOptionalPropertyTypes is on, and Titan\n // rejects explicit nulls/undefined for absent optional fields.\n const body: Record<string, unknown> = { inputText: text }\n if (dimensions !== undefined) body.dimensions = dimensions\n if (normalize !== undefined) body.normalize = normalize\n return readTitanEmbeddingBody(\n await this.invokeModel(model, body),\n `${this.name} ${model}`,\n )\n },\n )\n\n return this.toTitanResult(model, responses)\n }\n\n /**\n * `amazon.titan-embed-image-v1` (Titan Multimodal) — one item per\n * InvokeModel call. An item may carry text, an image, or both (a fused\n * item embedded into a single vector). Titan accepts at most one image per\n * request and never fetches remote URLs.\n */\n private async embedTitanImage(\n options: EmbeddingOptions<TProviderOptions>,\n ): Promise<EmbeddingResult> {\n const { model, dimensions } = options\n const outputEmbeddingLength = dimensions ?? TITAN_IMAGE_DEFAULT_DIMENSIONS\n if (!TITAN_IMAGE_DIMENSIONS.includes(outputEmbeddingLength)) {\n throw new Error(\n `${model} supports dimensions 256, 384, or 1024; got ${outputEmbeddingLength}`,\n )\n }\n const items = resolveEmbeddingInput(options.input)\n\n const responses = await mapWithConcurrency(\n items,\n MAX_CONCURRENT_INVOCATIONS,\n async (item, index) => {\n if (item.images.length > 1) {\n throw new Error(\n `${model} accepts at most one image per input item; input item ` +\n `at index ${index} contains ${item.images.length} images. ` +\n `Pass them as separate input items (one vector each).`,\n )\n }\n const body: Record<string, unknown> = {\n embeddingConfig: { outputEmbeddingLength },\n }\n if (item.texts.length > 0) body.inputText = item.texts.join('\\n')\n const image = item.images[0]\n if (image) body.inputImage = toTitanInputImage(image, model)\n return readTitanEmbeddingBody(\n await this.invokeModel(model, body),\n `${this.name} ${model}`,\n )\n },\n )\n\n return this.toTitanResult(model, responses)\n }\n\n /**\n * `cohere.embed-*-v3` — natively batched (chunked at 96 texts per call,\n * order preserved across chunks). `inputType` is required by the Cohere\n * API; output dimensionality is fixed, so `dimensions` is rejected.\n */\n private async embedCohere(\n options: EmbeddingOptions<TProviderOptions>,\n ): Promise<EmbeddingResult> {\n const { model, dimensions } = options\n if (dimensions !== undefined) {\n throw new Error(\n `${model} does not support the dimensions option; its output size is fixed`,\n )\n }\n const inputType: string | undefined = options.modelOptions?.inputType\n if (inputType === undefined) {\n throw new Error(\n `${model} requires modelOptions.inputType ('search_document' | ` +\n `'search_query' | 'classification' | 'clustering')`,\n )\n }\n const truncate: string | undefined = options.modelOptions?.truncate\n const texts = requireTextOnlyEmbeddingInput(options.input, this.name, model)\n const batches = chunk(texts, COHERE_MAX_BATCH_SIZE)\n\n const responses = await mapWithConcurrency(\n batches,\n MAX_CONCURRENT_INVOCATIONS,\n async (batch) => {\n const body: Record<string, unknown> = {\n texts: batch,\n input_type: inputType,\n }\n if (truncate !== undefined) body.truncate = truncate\n return readCohereEmbeddingBody(\n await this.invokeModel(model, body),\n `${this.name} ${model}`,\n )\n },\n )\n\n return {\n id: this.generateId(),\n model,\n embeddings: responses.flat().map((vector, index) => ({ vector, index })),\n }\n }\n\n /** Assemble an EmbeddingResult from per-item Titan responses. */\n private toTitanResult(\n model: string,\n responses: Array<TitanEmbeddingBody>,\n ): EmbeddingResult {\n let promptTokens = 0\n const embeddings = responses.map((response, index) => {\n promptTokens += response.inputTextTokenCount\n return { vector: response.embedding, index }\n })\n const usage: TokenUsage = {\n promptTokens,\n completionTokens: 0,\n totalTokens: promptTokens,\n }\n return { id: this.generateId(), model, embeddings, usage }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Response-body narrowing (SDK JSON boundary)\n// ---------------------------------------------------------------------------\n\ninterface TitanEmbeddingBody {\n embedding: Array<number>\n /** 0 when the response omits it (e.g. image-only Titan Multimodal calls). */\n inputTextTokenCount: number\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\n/** Narrow a Titan InvokeModel JSON body: `{ embedding, inputTextTokenCount? }`. */\nfunction readTitanEmbeddingBody(\n raw: unknown,\n context: string,\n): TitanEmbeddingBody {\n const embedding =\n isRecord(raw) && Array.isArray(raw.embedding) ? raw.embedding : undefined\n if (!embedding) {\n throw new Error(\n `${context}: response body is missing the \"embedding\" array`,\n )\n }\n const inputTextTokenCount =\n isRecord(raw) && typeof raw.inputTextTokenCount === 'number'\n ? raw.inputTextTokenCount\n : 0\n return { embedding, inputTextTokenCount }\n}\n\n/** Narrow a Cohere InvokeModel JSON body: `{ embeddings: number[][] }` (float). */\nfunction readCohereEmbeddingBody(\n raw: unknown,\n context: string,\n): Array<Array<number>> {\n const embeddings =\n isRecord(raw) && Array.isArray(raw.embeddings) ? raw.embeddings : undefined\n if (!embeddings) {\n throw new Error(\n `${context}: response body is missing the \"embeddings\" array`,\n )\n }\n return embeddings\n}\n\n// ---------------------------------------------------------------------------\n// Input mapping helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Map an ImagePart to Titan's `inputImage` (RAW base64, no data: prefix).\n * Accepts `data` sources as-is and `url` sources ONLY when the value is a\n * `data:` URI; Titan cannot fetch remote http(s) URLs.\n */\nfunction toTitanInputImage(image: ImagePart, model: string): string {\n const source = image.source\n if (isFileSource(source)) throw unsupportedFileSourceError('bedrock')\n if (source.type === 'data') {\n return source.value\n }\n if (source.value.startsWith('data:')) {\n const comma = source.value.indexOf(',')\n if (comma !== -1) {\n return source.value.slice(comma + 1)\n }\n }\n throw new Error(\n `Bedrock Titan does not fetch remote image URLs; pass base64 data ` +\n `(a { type: 'data' } source or a data: URI) for ${model}.`,\n )\n}\n\n/** Split into runs of at most `size`, preserving order. */\nfunction chunk<T>(items: Array<T>, size: number): Array<Array<T>> {\n const chunks: Array<Array<T>> = []\n for (let i = 0; i < items.length; i += size) {\n chunks.push(items.slice(i, i + size))\n }\n return chunks\n}\n\n/**\n * Map `fn` over `items` with at most `limit` calls in flight, returning\n * results in input order (result[i] corresponds to items[i] regardless of\n * completion order). Rejects with the first error.\n */\nasync function mapWithConcurrency<T, TResult>(\n items: ReadonlyArray<T>,\n limit: number,\n fn: (item: T, index: number) => Promise<TResult>,\n): Promise<Array<TResult>> {\n const results = new Array<TResult>(items.length)\n let next = 0\n const worker = async (): Promise<void> => {\n while (next < items.length) {\n const index = next++\n const item = items[index]\n if (item === undefined) continue // unreachable: index < length\n results[index] = await fn(item, index)\n }\n }\n const workerCount = Math.max(1, Math.min(limit, items.length))\n await Promise.all(Array.from({ length: workerCount }, () => worker()))\n return results\n}\n\n// ---------------------------------------------------------------------------\n// Factories\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a Bedrock embedding adapter with an explicit API key (bearer).\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'amazon.titan-embed-text-v2:0')\n * @param apiKey - Your Bedrock API key\n * @param config - Optional additional configuration (region, baseURL, ...)\n * @returns Configured Bedrock embedding adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createBedrockEmbedding(\n * 'amazon.titan-embed-text-v2:0',\n * 'bedrock-api-key',\n * );\n *\n * const result = await embed({\n * adapter,\n * input: 'a red guitar',\n * });\n * ```\n */\nexport function createBedrockEmbedding<TModel extends BedrockEmbeddingModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<BedrockEmbeddingConfig, 'apiKey'>,\n): BedrockEmbeddingAdapter<TModel> {\n // Explicit apiKey is authoritative — spread config first so it can't override.\n return new BedrockEmbeddingAdapter({ ...config, apiKey }, model)\n}\n\n/**\n * Creates a Bedrock embedding adapter using the ambient auth cascade:\n * `config.apiKey` → `BEDROCK_API_KEY` → `AWS_BEARER_TOKEN_BEDROCK` → SigV4\n * (AWS credential provider chain). Auth resolves lazily on the first\n * request, so `auth: 'sigv4'` never requires an API key.\n *\n * @param model - The model name (e.g., 'cohere.embed-english-v3')\n * @param config - Optional configuration (region, auth, baseURL, ...)\n * @returns Configured Bedrock embedding adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = bedrockEmbedding('cohere.embed-english-v3');\n *\n * const result = await embed({\n * adapter,\n * input: ['a red guitar', 'a blue drum kit'],\n * modelOptions: { inputType: 'search_document' },\n * });\n *\n * console.log(result.embeddings[0].vector)\n * ```\n */\nexport function bedrockEmbedding<TModel extends BedrockEmbeddingModel>(\n model: TModel,\n config?: BedrockEmbeddingConfig,\n): BedrockEmbeddingAdapter<TModel> {\n return new BedrockEmbeddingAdapter(config ?? {}, model)\n}\n"],"mappings":";;;;;;;AAyCA,IAAM,6BAA6B;;AAGnC,IAAM,wBAA+C;CAAC;CAAK;CAAK;AAAI;;AAGpE,IAAM,yBAAgD;CAAC;CAAK;CAAK;AAAI;AACrE,IAAM,iCAAiC;;AAGvC,IAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;AAqB9B,IAAa,0BAAb,cAQU,qBAKR;CACA,OAAgB;CAChB;CACA;CAEA,YAAY,QAAgC,OAAe;EACzD,MAAM,OAAO,CAAC,CAAC;EAIf,KAAK,eAAe;CACtB;;;;;;;CAQA,uBAAiE;EAE/D,OAAO;;GAA0B;;CACnC;;;;;CAMA,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;;;;;;CAOA,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;;CAOA,MAAgB,YACd,SACA,MACkB;EAClB,MAAM,EAAE,uBAAuB,MAAM,KAAK,qBAAqB;EAE/D,MAAM,WAAW,OAAM,MADF,KAAK,UAAU,EAAA,CACN,KAC5B,IAAI,mBAAmB;GACrB;GACA,aAAa;GACb,QAAQ;GACR,MAAM,KAAK,UAAU,IAAI;EAC3B,CAAC,CACH;EACA,OAAO,KAAK,MAAM,IAAI,YAAY,CAAC,CAAC,OAAO,SAAS,IAAI,CAAC;CAC3D;CAMA,MAAM,iBACJ,SAC0B;EAC1B,MAAM,EAAE,OAAO,WAAW;EAC1B,IAAI;GACF,OAAO,QACL,2BAA2B,KAAK,KAAK,SAAS,MAAM,UAAU,QAAQ,MAAM,UAC5E;IAAE,UAAU,KAAK;IAAM;GAAM,CAC/B;GACA,QAAQ,OAAR;IACE,KAAK,gCACH,OAAO,MAAM,KAAK,eAAe,OAAO;IAC1C,KAAK,+BACH,OAAO,MAAM,KAAK,gBAAgB,OAAO;IAC3C,KAAK;IACL,KAAK,gCACH,OAAO,MAAM,KAAK,YAAY,OAAO;IACvC,SACE,MAAM,IAAI,MACR,oCAAoC,MAAM,uBACrC,yBAAyB,KAAK,IAAI,EAAE,EAC3C;GACJ;EACF,SAAS,OAAgB;GACvB,OAAO,OAAO,GAAG,KAAK,KAAK,0BAA0B;IACnD,OAAO,kBAAkB,OAAO,GAAG,KAAK,KAAK,yBAAyB;IACtE,QAAQ,GAAG,KAAK,KAAK;GACvB,CAAC;GACD,MAAM;EACR;CACF;;;;;;CAWA,MAAc,eACZ,SAC0B;EAC1B,MAAM,EAAE,OAAO,eAAe;EAC9B,IACE,eAAe,KAAA,KACf,CAAC,sBAAsB,SAAS,UAAU,GAE1C,MAAM,IAAI,MACR,GAAG,MAAM,8CAA8C,YACzD;EAEF,MAAM,YAAiC,QAAQ,cAAc;EAG7D,MAAM,YAAY,MAAM,mBAFV,8BAA8B,QAAQ,OAAO,KAAK,MAAM,KAGpE,GACA,4BACA,OAAO,SAAS;GAGd,MAAM,OAAgC,EAAE,WAAW,KAAK;GACxD,IAAI,eAAe,KAAA,GAAW,KAAK,aAAa;GAChD,IAAI,cAAc,KAAA,GAAW,KAAK,YAAY;GAC9C,OAAO,uBACL,MAAM,KAAK,YAAY,OAAO,IAAI,GAClC,GAAG,KAAK,KAAK,GAAG,OAClB;EACF,CACF;EAEA,OAAO,KAAK,cAAc,OAAO,SAAS;CAC5C;;;;;;;CAQA,MAAc,gBACZ,SAC0B;EAC1B,MAAM,EAAE,OAAO,eAAe;EAC9B,MAAM,wBAAwB,cAAc;EAC5C,IAAI,CAAC,uBAAuB,SAAS,qBAAqB,GACxD,MAAM,IAAI,MACR,GAAG,MAAM,8CAA8C,uBACzD;EAIF,MAAM,YAAY,MAAM,mBAFV,sBAAsB,QAAQ,KAG1C,GACA,4BACA,OAAO,MAAM,UAAU;GACrB,IAAI,KAAK,OAAO,SAAS,GACvB,MAAM,IAAI,MACR,GAAG,MAAM,iEACK,MAAM,YAAY,KAAK,OAAO,OAAO,8DAErD;GAEF,MAAM,OAAgC,EACpC,iBAAiB,EAAE,sBAAsB,EAC3C;GACA,IAAI,KAAK,MAAM,SAAS,GAAG,KAAK,YAAY,KAAK,MAAM,KAAK,IAAI;GAChE,MAAM,QAAQ,KAAK,OAAO;GAC1B,IAAI,OAAO,KAAK,aAAa,kBAAkB,OAAO,KAAK;GAC3D,OAAO,uBACL,MAAM,KAAK,YAAY,OAAO,IAAI,GAClC,GAAG,KAAK,KAAK,GAAG,OAClB;EACF,CACF;EAEA,OAAO,KAAK,cAAc,OAAO,SAAS;CAC5C;;;;;;CAOA,MAAc,YACZ,SAC0B;EAC1B,MAAM,EAAE,OAAO,eAAe;EAC9B,IAAI,eAAe,KAAA,GACjB,MAAM,IAAI,MACR,GAAG,MAAM,kEACX;EAEF,MAAM,YAAgC,QAAQ,cAAc;EAC5D,IAAI,cAAc,KAAA,GAChB,MAAM,IAAI,MACR,GAAG,MAAM,wGAEX;EAEF,MAAM,WAA+B,QAAQ,cAAc;EAI3D,MAAM,YAAY,MAAM,mBAFR,MADF,8BAA8B,QAAQ,OAAO,KAAK,MAAM,KAChD,GAAO,qBAG3B,GACA,4BACA,OAAO,UAAU;GACf,MAAM,OAAgC;IACpC,OAAO;IACP,YAAY;GACd;GACA,IAAI,aAAa,KAAA,GAAW,KAAK,WAAW;GAC5C,OAAO,wBACL,MAAM,KAAK,YAAY,OAAO,IAAI,GAClC,GAAG,KAAK,KAAK,GAAG,OAClB;EACF,CACF;EAEA,OAAO;GACL,IAAI,KAAK,WAAW;GACpB;GACA,YAAY,UAAU,KAAK,CAAC,CAAC,KAAK,QAAQ,WAAW;IAAE;IAAQ;GAAM,EAAE;EACzE;CACF;;CAGA,cACE,OACA,WACiB;EACjB,IAAI,eAAe;EACnB,MAAM,aAAa,UAAU,KAAK,UAAU,UAAU;GACpD,gBAAgB,SAAS;GACzB,OAAO;IAAE,QAAQ,SAAS;IAAW;GAAM;EAC7C,CAAC;EACD,MAAM,QAAoB;GACxB;GACA,kBAAkB;GAClB,aAAa;EACf;EACA,OAAO;GAAE,IAAI,KAAK,WAAW;GAAG;GAAO;GAAY;EAAM;CAC3D;AACF;AAYA,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU;AAChD;;AAGA,SAAS,uBACP,KACA,SACoB;CACpB,MAAM,YACJ,SAAS,GAAG,KAAK,MAAM,QAAQ,IAAI,SAAS,IAAI,IAAI,YAAY,KAAA;CAClE,IAAI,CAAC,WACH,MAAM,IAAI,MACR,GAAG,QAAQ,iDACb;CAMF,OAAO;EAAE;EAAW,qBAHlB,SAAS,GAAG,KAAK,OAAO,IAAI,wBAAwB,WAChD,IAAI,sBACJ;CACkC;AAC1C;;AAGA,SAAS,wBACP,KACA,SACsB;CACtB,MAAM,aACJ,SAAS,GAAG,KAAK,MAAM,QAAQ,IAAI,UAAU,IAAI,IAAI,aAAa,KAAA;CACpE,IAAI,CAAC,YACH,MAAM,IAAI,MACR,GAAG,QAAQ,kDACb;CAEF,OAAO;AACT;;;;;;AAWA,SAAS,kBAAkB,OAAkB,OAAuB;CAClE,MAAM,SAAS,MAAM;CACrB,IAAI,aAAa,MAAM,GAAG,MAAM,2BAA2B,SAAS;CACpE,IAAI,OAAO,SAAS,QAClB,OAAO,OAAO;CAEhB,IAAI,OAAO,MAAM,WAAW,OAAO,GAAG;EACpC,MAAM,QAAQ,OAAO,MAAM,QAAQ,GAAG;EACtC,IAAI,UAAU,IACZ,OAAO,OAAO,MAAM,MAAM,QAAQ,CAAC;CAEvC;CACA,MAAM,IAAI,MACR,mHACoD,MAAM,EAC5D;AACF;;AAGA,SAAS,MAAS,OAAiB,MAA+B;CAChE,MAAM,SAA0B,CAAC;CACjC,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,MACrC,OAAO,KAAK,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC;CAEtC,OAAO;AACT;;;;;;AAOA,eAAe,mBACb,OACA,OACA,IACyB;CACzB,MAAM,UAAU,IAAI,MAAe,MAAM,MAAM;CAC/C,IAAI,OAAO;CACX,MAAM,SAAS,YAA2B;EACxC,OAAO,OAAO,MAAM,QAAQ;GAC1B,MAAM,QAAQ;GACd,MAAM,OAAO,MAAM;GACnB,IAAI,SAAS,KAAA,GAAW;GACxB,QAAQ,SAAS,MAAM,GAAG,MAAM,KAAK;EACvC;CACF;CACA,MAAM,cAAc,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,MAAM,MAAM,CAAC;CAC7D,MAAM,QAAQ,IAAI,MAAM,KAAK,EAAE,QAAQ,YAAY,SAAS,OAAO,CAAC,CAAC;CACrE,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,uBACd,OACA,QACA,QACiC;CAEjC,OAAO,IAAI,wBAAwB;EAAE,GAAG;EAAQ;CAAO,GAAG,KAAK;AACjE;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,iBACd,OACA,QACiC;CACjC,OAAO,IAAI,wBAAwB,UAAU,CAAC,GAAG,KAAK;AACxD"}
@@ -1,10 +1,10 @@
1
- import { OpenAIBaseResponsesTextAdapter } from '@tanstack/openai-base';
1
+ import { OpenAIBaseResponsesTextAdapter, OpenAIBaseTextAdapterOptions } from '@tanstack/openai-base';
2
2
  import { Modality } from '@tanstack/ai';
3
3
  import { BedrockClientConfig } from '../utils/client.js';
4
4
  import { BedrockMessageMetadataByModality } from '../message-types.js';
5
5
  import { BedrockChatModelToolCapabilitiesByName, BedrockResponsesModels, ResolveInputModalities } from '../model-meta.js';
6
6
  import { ExternalResponsesProviderOptions } from '../text/responses-provider-options.js';
7
- export interface BedrockResponsesConfig extends BedrockClientConfig {
7
+ export interface BedrockResponsesConfig extends BedrockClientConfig, OpenAIBaseTextAdapterOptions {
8
8
  }
9
9
  export type { ExternalResponsesProviderOptions as BedrockResponsesProviderOptions } from '../text/responses-provider-options.js';
10
10
  type ResolveToolCapabilities<TModel extends string> = TModel extends keyof BedrockChatModelToolCapabilitiesByName ? NonNullable<BedrockChatModelToolCapabilitiesByName[TModel]> : readonly [];
@@ -12,7 +12,7 @@ var BedrockResponsesTextAdapter = class extends OpenAIBaseResponsesTextAdapter {
12
12
  kind = "text";
13
13
  name = "bedrock-responses";
14
14
  constructor(config, model) {
15
- super(model, "bedrock-responses", new OpenAI(withBedrockDefaults(config, "mantle", model)));
15
+ super(model, "bedrock-responses", new OpenAI(withBedrockDefaults(config, "mantle", model)), config);
16
16
  }
17
17
  };
18
18
  /** Responses adapter with an explicit API key (low-level; the public branching factory delegates here). */
@@ -1 +1 @@
1
- {"version":3,"file":"responses-text.js","names":[],"sources":["../../../src/adapters/responses-text.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { OpenAIBaseResponsesTextAdapter } from '@tanstack/openai-base'\nimport { withBedrockDefaults } from '../utils/client'\nimport type { Modality } from '@tanstack/ai'\nimport type { BedrockClientConfig } from '../utils/client'\nimport type { BedrockMessageMetadataByModality } from '../message-types'\nimport type {\n BedrockChatModelToolCapabilitiesByName,\n BedrockResponsesModels,\n ResolveInputModalities,\n} from '../model-meta'\nimport type { ExternalResponsesProviderOptions } from '../text/responses-provider-options'\n\nexport interface BedrockResponsesConfig extends BedrockClientConfig {}\n\nexport type { ExternalResponsesProviderOptions as BedrockResponsesProviderOptions } from '../text/responses-provider-options'\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof BedrockChatModelToolCapabilitiesByName\n ? NonNullable<BedrockChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/**\n * Bedrock Responses adapter. Drives mantle's OpenAI-compatible `/responses`\n * endpoint via the OpenAI SDK (`client.responses.create`) — the same base\n * class ai-openai's `openaiText` uses. Responses is mantle-only, so the\n * constructor forces the mantle baseURL.\n */\nexport class BedrockResponsesTextAdapter<\n TModel extends BedrockResponsesModels,\n // Constraint mirrors the chat adapter (and ai-groq / ai-openai) and the base,\n // which parameterises `TProviderOptions extends Record<string, any>`. Our\n // default `ExternalResponsesProviderOptions` is an interface that (lacking an\n // implicit index signature) `Record<string, unknown>` would reject but\n // `Record<string, any>` accepts. This `any` is confined to the generic\n // constraint — no value/shape `as` cast is introduced.\n TProviderOptions extends Record<string, any> =\n ExternalResponsesProviderOptions,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends OpenAIBaseResponsesTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n BedrockMessageMetadataByModality,\n TToolCapabilities\n> {\n override readonly kind = 'text' as const\n override readonly name = 'bedrock-responses' as const\n\n constructor(config: BedrockResponsesConfig, model: TModel) {\n // Responses is mantle-only — force the mantle base URL (an explicit\n // config.baseURL still wins, e.g. E2E pointing at aimock).\n super(\n model,\n 'bedrock-responses',\n new OpenAI(withBedrockDefaults(config, 'mantle', model)),\n )\n }\n}\n\n/** Responses adapter with an explicit API key (low-level; the public branching factory delegates here). */\nexport function createBedrockResponsesText<\n TModel extends BedrockResponsesModels,\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<BedrockResponsesConfig, 'apiKey'>,\n): BedrockResponsesTextAdapter<TModel> {\n return new BedrockResponsesTextAdapter({ ...config, apiKey }, model)\n}\n"],"mappings":";;;;;;;;;;AA4BA,IAAa,8BAAb,cAcU,+BAMR;CACA,OAAyB;CACzB,OAAyB;CAEzB,YAAY,QAAgC,OAAe;EAGzD,MACE,OACA,qBACA,IAAI,OAAO,oBAAoB,QAAQ,UAAU,KAAK,CAAC,CACzD;CACF;AACF;;AAGA,SAAgB,2BAGd,OACA,QACA,QACqC;CACrC,OAAO,IAAI,4BAA4B;EAAE,GAAG;EAAQ;CAAO,GAAG,KAAK;AACrE"}
1
+ {"version":3,"file":"responses-text.js","names":[],"sources":["../../../src/adapters/responses-text.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { OpenAIBaseResponsesTextAdapter } from '@tanstack/openai-base'\nimport { withBedrockDefaults } from '../utils/client'\nimport type { Modality } from '@tanstack/ai'\nimport type { BedrockClientConfig } from '../utils/client'\nimport type { BedrockMessageMetadataByModality } from '../message-types'\nimport type {\n BedrockChatModelToolCapabilitiesByName,\n BedrockResponsesModels,\n ResolveInputModalities,\n} from '../model-meta'\nimport type { ExternalResponsesProviderOptions } from '../text/responses-provider-options'\nimport type { OpenAIBaseTextAdapterOptions } from '@tanstack/openai-base'\n\nexport interface BedrockResponsesConfig\n extends BedrockClientConfig, OpenAIBaseTextAdapterOptions {}\n\nexport type { ExternalResponsesProviderOptions as BedrockResponsesProviderOptions } from '../text/responses-provider-options'\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof BedrockChatModelToolCapabilitiesByName\n ? NonNullable<BedrockChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/**\n * Bedrock Responses adapter. Drives mantle's OpenAI-compatible `/responses`\n * endpoint via the OpenAI SDK (`client.responses.create`) — the same base\n * class ai-openai's `openaiText` uses. Responses is mantle-only, so the\n * constructor forces the mantle baseURL.\n */\nexport class BedrockResponsesTextAdapter<\n TModel extends BedrockResponsesModels,\n // Constraint mirrors the chat adapter (and ai-groq / ai-openai) and the base,\n // which parameterises `TProviderOptions extends Record<string, any>`. Our\n // default `ExternalResponsesProviderOptions` is an interface that (lacking an\n // implicit index signature) `Record<string, unknown>` would reject but\n // `Record<string, any>` accepts. This `any` is confined to the generic\n // constraint — no value/shape `as` cast is introduced.\n TProviderOptions extends Record<string, any> =\n ExternalResponsesProviderOptions,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends OpenAIBaseResponsesTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n BedrockMessageMetadataByModality,\n TToolCapabilities\n> {\n override readonly kind = 'text' as const\n override readonly name = 'bedrock-responses' as const\n\n constructor(config: BedrockResponsesConfig, model: TModel) {\n // Responses is mantle-only — force the mantle base URL (an explicit\n // config.baseURL still wins, e.g. E2E pointing at aimock).\n super(\n model,\n 'bedrock-responses',\n new OpenAI(withBedrockDefaults(config, 'mantle', model)),\n config,\n )\n }\n}\n\n/** Responses adapter with an explicit API key (low-level; the public branching factory delegates here). */\nexport function createBedrockResponsesText<\n TModel extends BedrockResponsesModels,\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<BedrockResponsesConfig, 'apiKey'>,\n): BedrockResponsesTextAdapter<TModel> {\n return new BedrockResponsesTextAdapter({ ...config, apiKey }, model)\n}\n"],"mappings":";;;;;;;;;;AA8BA,IAAa,8BAAb,cAcU,+BAMR;CACA,OAAyB;CACzB,OAAyB;CAEzB,YAAY,QAAgC,OAAe;EAGzD,MACE,OACA,qBACA,IAAI,OAAO,oBAAoB,QAAQ,UAAU,KAAK,CAAC,GACvD,MACF;CACF;AACF;;AAGA,SAAgB,2BAGd,OACA,QACA,QACqC;CACrC,OAAO,IAAI,4BAA4B;EAAE,GAAG;EAAQ;CAAO,GAAG,KAAK;AACrE"}
@@ -1,9 +1,9 @@
1
- import { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base';
1
+ import { OpenAIBaseChatCompletionsTextAdapter, OpenAIBaseTextAdapterOptions } from '@tanstack/openai-base';
2
2
  import { Modality } from '@tanstack/ai';
3
3
  import { BedrockClientConfig } from '../utils/client.js';
4
4
  import { BedrockMessageMetadataByModality } from '../message-types.js';
5
5
  import { BedrockChatModelToolCapabilitiesByName, BedrockChatModels, ResolveInputModalities, ResolveProviderOptions } from '../model-meta.js';
6
- export interface BedrockTextConfig extends BedrockClientConfig {
6
+ export interface BedrockTextConfig extends BedrockClientConfig, OpenAIBaseTextAdapterOptions {
7
7
  }
8
8
  export type { ExternalTextProviderOptions as BedrockTextProviderOptions } from '../text/text-provider-options.js';
9
9
  type ResolveToolCapabilities<TModel extends string> = TModel extends keyof BedrockChatModelToolCapabilitiesByName ? NonNullable<BedrockChatModelToolCapabilitiesByName[TModel]> : readonly [];
@@ -12,7 +12,7 @@ var BedrockTextAdapter = class extends OpenAIBaseChatCompletionsTextAdapter {
12
12
  kind = "text";
13
13
  name = "bedrock";
14
14
  constructor(config, model) {
15
- super(model, "bedrock", new OpenAI(withBedrockDefaults(config, void 0, model)));
15
+ super(model, "bedrock", new OpenAI(withBedrockDefaults(config, void 0, model)), config);
16
16
  }
17
17
  /**
18
18
  * Surface reasoning deltas (gpt-oss / Claude reasoning) the OpenAI-compatible
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","names":[],"sources":["../../../src/adapters/text.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base'\nimport { withBedrockDefaults } from '../utils/client'\nimport type { Modality } from '@tanstack/ai'\nimport type { BedrockClientConfig } from '../utils/client'\nimport type { BedrockMessageMetadataByModality } from '../message-types'\nimport type {\n BedrockChatModelToolCapabilitiesByName,\n BedrockChatModels,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\n\nexport interface BedrockTextConfig extends BedrockClientConfig {}\n\nexport type { ExternalTextProviderOptions as BedrockTextProviderOptions } from '../text/text-provider-options'\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof BedrockChatModelToolCapabilitiesByName\n ? NonNullable<BedrockChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/**\n * Bedrock Chat Completions adapter. Drives Bedrock's OpenAI-compatible\n * `/chat/completions` endpoint via the OpenAI SDK with a baseURL override\n * (same pattern as ai-groq). Tool conversion, streaming, structured output,\n * and the agent loop come from the base.\n */\nexport class BedrockTextAdapter<\n TModel extends BedrockChatModels,\n // Constraint mirrors ai-groq and the base, which parameterises\n // `TProviderOptions extends Record<string, any>`. Our default\n // `ResolveProviderOptions<TModel>` resolves to the `BedrockTextProviderOptions`\n // interface, which (lacking an implicit index signature) `Record<string,\n // unknown>` would reject but `Record<string, any>` accepts. This `any` is\n // confined to the generic constraint (the established ai-groq pattern) — no\n // value/shape `as` cast is introduced.\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends OpenAIBaseChatCompletionsTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n BedrockMessageMetadataByModality,\n TToolCapabilities\n> {\n override readonly kind = 'text' as const\n override readonly name = 'bedrock' as const\n\n constructor(config: BedrockTextConfig, model: TModel) {\n // No `forced` -> honors config.endpoint ('runtime' default, 'mantle' allowed).\n super(\n model,\n 'bedrock',\n new OpenAI(withBedrockDefaults(config, undefined, model)),\n )\n }\n\n /**\n * Surface reasoning deltas (gpt-oss / Claude reasoning) the OpenAI-compatible\n * way. Base types the chunk as `unknown`; narrow with runtime guards — no\n * `as` casts, no `any`.\n */\n protected override extractReasoning(\n chunk: unknown,\n ): { text: string } | undefined {\n return readDeltaReasoning(chunk)\n }\n}\n\n/** Cast-free narrowing of a Chat Completions chunk's reasoning delta. */\nfunction readDeltaReasoning(chunk: unknown): { text: string } | undefined {\n if (typeof chunk !== 'object' || chunk === null || !('choices' in chunk))\n return undefined\n if (!Array.isArray(chunk.choices)) return undefined\n const choice: unknown = chunk.choices[0]\n if (typeof choice !== 'object' || choice === null || !('delta' in choice))\n return undefined\n const delta = choice.delta\n if (typeof delta !== 'object' || delta === null) return undefined\n const raw =\n 'reasoning' in delta && typeof delta.reasoning === 'string'\n ? delta.reasoning\n : 'reasoning_content' in delta &&\n typeof delta.reasoning_content === 'string'\n ? delta.reasoning_content\n : undefined\n return raw && raw.length > 0 ? { text: raw } : undefined\n}\n\n/** Chat adapter with an explicit API key (low-level; the public branching factory delegates here). */\nexport function createBedrockChat<TModel extends BedrockChatModels>(\n model: TModel,\n apiKey: string,\n config?: Omit<BedrockTextConfig, 'apiKey'>,\n): BedrockTextAdapter<TModel> {\n return new BedrockTextAdapter({ ...config, apiKey }, model)\n}\n"],"mappings":";;;;;;;;;;AA4BA,IAAa,qBAAb,cAcU,qCAMR;CACA,OAAyB;CACzB,OAAyB;CAEzB,YAAY,QAA2B,OAAe;EAEpD,MACE,OACA,WACA,IAAI,OAAO,oBAAoB,QAAQ,KAAA,GAAW,KAAK,CAAC,CAC1D;CACF;;;;;;CAOA,iBACE,OAC8B;EAC9B,OAAO,mBAAmB,KAAK;CACjC;AACF;;AAGA,SAAS,mBAAmB,OAA8C;CACxE,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,EAAE,aAAa,QAChE,OAAO,KAAA;CACT,IAAI,CAAC,MAAM,QAAQ,MAAM,OAAO,GAAG,OAAO,KAAA;CAC1C,MAAM,SAAkB,MAAM,QAAQ;CACtC,IAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,EAAE,WAAW,SAChE,OAAO,KAAA;CACT,MAAM,QAAQ,OAAO;CACrB,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO,KAAA;CACxD,MAAM,MACJ,eAAe,SAAS,OAAO,MAAM,cAAc,WAC/C,MAAM,YACN,uBAAuB,SACrB,OAAO,MAAM,sBAAsB,WACnC,MAAM,oBACN,KAAA;CACR,OAAO,OAAO,IAAI,SAAS,IAAI,EAAE,MAAM,IAAI,IAAI,KAAA;AACjD;;AAGA,SAAgB,kBACd,OACA,QACA,QAC4B;CAC5B,OAAO,IAAI,mBAAmB;EAAE,GAAG;EAAQ;CAAO,GAAG,KAAK;AAC5D"}
1
+ {"version":3,"file":"text.js","names":[],"sources":["../../../src/adapters/text.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base'\nimport { withBedrockDefaults } from '../utils/client'\nimport type { Modality } from '@tanstack/ai'\nimport type { BedrockClientConfig } from '../utils/client'\nimport type { BedrockMessageMetadataByModality } from '../message-types'\nimport type {\n BedrockChatModelToolCapabilitiesByName,\n BedrockChatModels,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type { OpenAIBaseTextAdapterOptions } from '@tanstack/openai-base'\n\nexport interface BedrockTextConfig\n extends BedrockClientConfig, OpenAIBaseTextAdapterOptions {}\n\nexport type { ExternalTextProviderOptions as BedrockTextProviderOptions } from '../text/text-provider-options'\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof BedrockChatModelToolCapabilitiesByName\n ? NonNullable<BedrockChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/**\n * Bedrock Chat Completions adapter. Drives Bedrock's OpenAI-compatible\n * `/chat/completions` endpoint via the OpenAI SDK with a baseURL override\n * (same pattern as ai-groq). Tool conversion, streaming, structured output,\n * and the agent loop come from the base.\n */\nexport class BedrockTextAdapter<\n TModel extends BedrockChatModels,\n // Constraint mirrors ai-groq and the base, which parameterises\n // `TProviderOptions extends Record<string, any>`. Our default\n // `ResolveProviderOptions<TModel>` resolves to the `BedrockTextProviderOptions`\n // interface, which (lacking an implicit index signature) `Record<string,\n // unknown>` would reject but `Record<string, any>` accepts. This `any` is\n // confined to the generic constraint (the established ai-groq pattern) — no\n // value/shape `as` cast is introduced.\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends OpenAIBaseChatCompletionsTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n BedrockMessageMetadataByModality,\n TToolCapabilities\n> {\n override readonly kind = 'text' as const\n override readonly name = 'bedrock' as const\n\n constructor(config: BedrockTextConfig, model: TModel) {\n // No `forced` -> honors config.endpoint ('runtime' default, 'mantle' allowed).\n super(\n model,\n 'bedrock',\n new OpenAI(withBedrockDefaults(config, undefined, model)),\n config,\n )\n }\n\n /**\n * Surface reasoning deltas (gpt-oss / Claude reasoning) the OpenAI-compatible\n * way. Base types the chunk as `unknown`; narrow with runtime guards — no\n * `as` casts, no `any`.\n */\n protected override extractReasoning(\n chunk: unknown,\n ): { text: string } | undefined {\n return readDeltaReasoning(chunk)\n }\n}\n\n/** Cast-free narrowing of a Chat Completions chunk's reasoning delta. */\nfunction readDeltaReasoning(chunk: unknown): { text: string } | undefined {\n if (typeof chunk !== 'object' || chunk === null || !('choices' in chunk))\n return undefined\n if (!Array.isArray(chunk.choices)) return undefined\n const choice: unknown = chunk.choices[0]\n if (typeof choice !== 'object' || choice === null || !('delta' in choice))\n return undefined\n const delta = choice.delta\n if (typeof delta !== 'object' || delta === null) return undefined\n const raw =\n 'reasoning' in delta && typeof delta.reasoning === 'string'\n ? delta.reasoning\n : 'reasoning_content' in delta &&\n typeof delta.reasoning_content === 'string'\n ? delta.reasoning_content\n : undefined\n return raw && raw.length > 0 ? { text: raw } : undefined\n}\n\n/** Chat adapter with an explicit API key (low-level; the public branching factory delegates here). */\nexport function createBedrockChat<TModel extends BedrockChatModels>(\n model: TModel,\n apiKey: string,\n config?: Omit<BedrockTextConfig, 'apiKey'>,\n): BedrockTextAdapter<TModel> {\n return new BedrockTextAdapter({ ...config, apiKey }, model)\n}\n"],"mappings":";;;;;;;;;;AA8BA,IAAa,qBAAb,cAcU,qCAMR;CACA,OAAyB;CACzB,OAAyB;CAEzB,YAAY,QAA2B,OAAe;EAEpD,MACE,OACA,WACA,IAAI,OAAO,oBAAoB,QAAQ,KAAA,GAAW,KAAK,CAAC,GACxD,MACF;CACF;;;;;;CAOA,iBACE,OAC8B;EAC9B,OAAO,mBAAmB,KAAK;CACjC;AACF;;AAGA,SAAS,mBAAmB,OAA8C;CACxE,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,EAAE,aAAa,QAChE,OAAO,KAAA;CACT,IAAI,CAAC,MAAM,QAAQ,MAAM,OAAO,GAAG,OAAO,KAAA;CAC1C,MAAM,SAAkB,MAAM,QAAQ;CACtC,IAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,EAAE,WAAW,SAChE,OAAO,KAAA;CACT,MAAM,QAAQ,OAAO;CACrB,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO,KAAA;CACxD,MAAM,MACJ,eAAe,SAAS,OAAO,MAAM,cAAc,WAC/C,MAAM,YACN,uBAAuB,SACrB,OAAO,MAAM,sBAAsB,WACnC,MAAM,oBACN,KAAA;CACR,OAAO,OAAO,IAAI,SAAS,IAAI,EAAE,MAAM,IAAI,IAAI,KAAA;AACjD;;AAGA,SAAgB,kBACd,OACA,QACA,QAC4B;CAC5B,OAAO,IAAI,mBAAmB;EAAE,GAAG;EAAQ;CAAO,GAAG,KAAK;AAC5D"}
@@ -3,7 +3,8 @@ import { Message, SystemContentBlock } from '@aws-sdk/client-bedrock-runtime';
3
3
  /**
4
4
  * Convert TanStack AI messages + system prompts into the Converse API format.
5
5
  *
6
- * - System prompts are lifted into `SystemContentBlock[]`.
6
+ * - System prompts are lifted into `SystemContentBlock[]`; a prompt whose
7
+ * `metadata.cachePoint` is set is followed by a `cachePoint` block.
7
8
  * - `tool` role messages are remapped to `user` role `toolResult` blocks.
8
9
  * - Consecutive messages with the same Converse role are merged (Converse
9
10
  * requires strict user/assistant alternation).
@@ -1,4 +1,4 @@
1
- import { normalizeSystemPrompts } from "@tanstack/ai";
1
+ import { isFileSource, normalizeSystemPrompts, unsupportedFileSourceError } from "@tanstack/ai";
2
2
  //#region src/converse/message-converter.ts
3
3
  function base64ToBytes(b64) {
4
4
  return new Uint8Array(Buffer.from(b64, "base64"));
@@ -49,6 +49,7 @@ function contentPartToBlock(part, docIndex) {
49
49
  if (isTextPart(part)) return { text: part.content };
50
50
  if (isImagePart(part)) {
51
51
  const { source } = part;
52
+ if (isFileSource(source)) throw unsupportedFileSourceError("bedrock");
52
53
  if (!isDataSource(source)) throw new Error("Bedrock Converse requires inline image bytes; URL image sources are not supported.");
53
54
  return { image: {
54
55
  format: imageFormat(source.mimeType),
@@ -57,6 +58,7 @@ function contentPartToBlock(part, docIndex) {
57
58
  }
58
59
  if (isDocumentPart(part)) {
59
60
  const { source } = part;
61
+ if (isFileSource(source)) throw unsupportedFileSourceError("bedrock");
60
62
  if (!isDataSource(source)) throw new Error("Bedrock Converse requires inline document bytes; URL document sources are not supported.");
61
63
  return { document: {
62
64
  format: documentFormat(source.mimeType),
@@ -83,6 +85,10 @@ function messageToBlocks(msg, docCounter) {
83
85
  } else if (Array.isArray(msg.content)) for (const part of msg.content) {
84
86
  const docIndex = isDocumentPart(part) ? ++docCounter.value : 0;
85
87
  blocks.push(contentPartToBlock(part, docIndex));
88
+ if (isTextPart(part)) {
89
+ const { cachePoint } = part.metadata ?? {};
90
+ if (cachePoint) blocks.push({ cachePoint });
91
+ }
86
92
  }
87
93
  if (msg.role === "assistant" && msg.toolCalls) for (const call of msg.toolCalls) {
88
94
  const rawArguments = call.function.arguments || "{}";
@@ -104,13 +110,14 @@ function messageToBlocks(msg, docCounter) {
104
110
  /**
105
111
  * Convert TanStack AI messages + system prompts into the Converse API format.
106
112
  *
107
- * - System prompts are lifted into `SystemContentBlock[]`.
113
+ * - System prompts are lifted into `SystemContentBlock[]`; a prompt whose
114
+ * `metadata.cachePoint` is set is followed by a `cachePoint` block.
108
115
  * - `tool` role messages are remapped to `user` role `toolResult` blocks.
109
116
  * - Consecutive messages with the same Converse role are merged (Converse
110
117
  * requires strict user/assistant alternation).
111
118
  */
112
119
  function toConverseMessages(messages, systemPrompts) {
113
- const system = normalizeSystemPrompts(systemPrompts).map((p) => ({ text: p.content }));
120
+ const system = normalizeSystemPrompts(systemPrompts).flatMap((p) => p.metadata?.cachePoint ? [{ text: p.content }, { cachePoint: p.metadata.cachePoint }] : [{ text: p.content }]);
114
121
  const converseMessages = [];
115
122
  const docCounter = { value: 0 };
116
123
  for (const msg of messages) {
@@ -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(\n content: string | null | undefined | Array<ContentPart>,\n): string {\n if (content === null || content === undefined) 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 {\n isFileSource,\n normalizeSystemPrompts,\n unsupportedFileSourceError,\n} 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'\nimport type {\n BedrockSystemPromptMetadata,\n BedrockTextMetadata,\n} from '../message-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(\n content: string | null | undefined | Array<ContentPart>,\n): string {\n if (content === null || content === undefined) 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 (isFileSource(source)) throw unsupportedFileSourceError('bedrock')\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 (isFileSource(source)) throw unsupportedFileSourceError('bedrock')\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 if (isTextPart(part)) {\n const { cachePoint } = (part.metadata ?? {}) as BedrockTextMetadata\n if (cachePoint) blocks.push({ cachePoint })\n }\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[]`; a prompt whose\n * `metadata.cachePoint` is set is followed by a `cachePoint` block.\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> =\n normalizeSystemPrompts<BedrockSystemPromptMetadata>(systemPrompts).flatMap(\n (p) =>\n p.metadata?.cachePoint\n ? [{ text: p.content }, { cachePoint: p.metadata.cachePoint }]\n : [{ text: p.content }],\n )\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,8 +1,11 @@
1
1
  import { ToolConfiguration } from '@aws-sdk/client-bedrock-runtime';
2
+ import { BedrockCachePoint } from '../message-types.js';
2
3
  export interface ConverseToolInput {
3
4
  name: string;
4
5
  description?: string;
5
6
  inputSchema: unknown;
7
+ /** Emit a `cachePoint` entry right after this tool. */
8
+ cachePoint?: BedrockCachePoint;
6
9
  }
7
10
  export type ToolChoiceInput = 'auto' | 'required' | 'none' | {
8
11
  type: 'tool';
@@ -4,11 +4,11 @@ function toToolConfig(tools, choice) {
4
4
  if (choice === "none") return void 0;
5
5
  const toolChoice = mapChoice(choice);
6
6
  return {
7
- tools: tools.map((t) => ({ toolSpec: {
7
+ tools: tools.flatMap((t) => [{ toolSpec: {
8
8
  name: t.name,
9
9
  ...t.description ? { description: t.description } : {},
10
10
  inputSchema: { json: t.inputSchema }
11
- } })),
11
+ } }, ...t.cachePoint ? [{ cachePoint: t.cachePoint }] : []]),
12
12
  ...toolChoice ? { toolChoice } : {}
13
13
  };
14
14
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tool-converter.js","names":[],"sources":["../../../src/converse/tool-converter.ts"],"sourcesContent":["import type {\n ToolChoice,\n ToolConfiguration,\n} from '@aws-sdk/client-bedrock-runtime'\nimport type { DocumentType } from '@smithy/types'\n\nexport interface ConverseToolInput {\n name: string\n description?: string\n inputSchema: unknown\n}\n\nexport type ToolChoiceInput =\n | 'auto'\n | 'required'\n | 'none'\n | { type: 'tool'; name: string }\n\nexport function toToolConfig(\n tools: Array<ConverseToolInput>,\n choice: ToolChoiceInput | undefined,\n): ToolConfiguration | undefined {\n if (!tools.length) return undefined\n // `none` means \"don't call tools\" — omit the tool config entirely, since\n // Bedrock treats a present tool config with no `toolChoice` as auto.\n if (choice === 'none') return undefined\n const toolChoice = mapChoice(choice)\n return {\n tools: tools.map((t) => ({\n toolSpec: {\n name: t.name,\n ...(t.description ? { description: t.description } : {}),\n inputSchema: { json: t.inputSchema as DocumentType },\n },\n })),\n ...(toolChoice ? { toolChoice } : {}),\n }\n}\n\nfunction mapChoice(\n choice: ToolChoiceInput | undefined,\n): ToolChoice | undefined {\n if (!choice || choice === 'auto') return { auto: {} }\n if (choice === 'required') return { any: {} }\n // `none` is handled earlier in toToolConfig (omits the tool config); this\n // branch keeps the string union narrowed so `choice.name` type-checks.\n if (choice === 'none') return undefined\n return { tool: { name: choice.name } }\n}\n"],"mappings":";AAkBA,SAAgB,aACd,OACA,QAC+B;CAC/B,IAAI,CAAC,MAAM,QAAQ,OAAO,KAAA;CAG1B,IAAI,WAAW,QAAQ,OAAO,KAAA;CAC9B,MAAM,aAAa,UAAU,MAAM;CACnC,OAAO;EACL,OAAO,MAAM,KAAK,OAAO,EACvB,UAAU;GACR,MAAM,EAAE;GACR,GAAI,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,IAAI,CAAC;GACtD,aAAa,EAAE,MAAM,EAAE,YAA4B;EACrD,EACF,EAAE;EACF,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;CACrC;AACF;AAEA,SAAS,UACP,QACwB;CACxB,IAAI,CAAC,UAAU,WAAW,QAAQ,OAAO,EAAE,MAAM,CAAC,EAAE;CACpD,IAAI,WAAW,YAAY,OAAO,EAAE,KAAK,CAAC,EAAE;CAG5C,IAAI,WAAW,QAAQ,OAAO,KAAA;CAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,KAAK,EAAE;AACvC"}
1
+ {"version":3,"file":"tool-converter.js","names":[],"sources":["../../../src/converse/tool-converter.ts"],"sourcesContent":["import type {\n ToolChoice,\n ToolConfiguration,\n} from '@aws-sdk/client-bedrock-runtime'\nimport type { DocumentType } from '@smithy/types'\nimport type { BedrockCachePoint } from '../message-types'\n\nexport interface ConverseToolInput {\n name: string\n description?: string\n inputSchema: unknown\n /** Emit a `cachePoint` entry right after this tool. */\n cachePoint?: BedrockCachePoint\n}\n\nexport type ToolChoiceInput =\n | 'auto'\n | 'required'\n | 'none'\n | { type: 'tool'; name: string }\n\nexport function toToolConfig(\n tools: Array<ConverseToolInput>,\n choice: ToolChoiceInput | undefined,\n): ToolConfiguration | undefined {\n if (!tools.length) return undefined\n // `none` means \"don't call tools\" — omit the tool config entirely, since\n // Bedrock treats a present tool config with no `toolChoice` as auto.\n if (choice === 'none') return undefined\n const toolChoice = mapChoice(choice)\n return {\n tools: tools.flatMap((t) => [\n {\n toolSpec: {\n name: t.name,\n ...(t.description ? { description: t.description } : {}),\n inputSchema: { json: t.inputSchema as DocumentType },\n },\n },\n ...(t.cachePoint ? [{ cachePoint: t.cachePoint }] : []),\n ]),\n ...(toolChoice ? { toolChoice } : {}),\n }\n}\n\nfunction mapChoice(\n choice: ToolChoiceInput | undefined,\n): ToolChoice | undefined {\n if (!choice || choice === 'auto') return { auto: {} }\n if (choice === 'required') return { any: {} }\n // `none` is handled earlier in toToolConfig (omits the tool config); this\n // branch keeps the string union narrowed so `choice.name` type-checks.\n if (choice === 'none') return undefined\n return { tool: { name: choice.name } }\n}\n"],"mappings":";AAqBA,SAAgB,aACd,OACA,QAC+B;CAC/B,IAAI,CAAC,MAAM,QAAQ,OAAO,KAAA;CAG1B,IAAI,WAAW,QAAQ,OAAO,KAAA;CAC9B,MAAM,aAAa,UAAU,MAAM;CACnC,OAAO;EACL,OAAO,MAAM,SAAS,MAAM,CAC1B,EACE,UAAU;GACR,MAAM,EAAE;GACR,GAAI,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,IAAI,CAAC;GACtD,aAAa,EAAE,MAAM,EAAE,YAA4B;EACrD,EACF,GACA,GAAI,EAAE,aAAa,CAAC,EAAE,YAAY,EAAE,WAAW,CAAC,IAAI,CAAC,CACvD,CAAC;EACD,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;CACrC;AACF;AAEA,SAAS,UACP,QACwB;CACxB,IAAI,CAAC,UAAU,WAAW,QAAQ,OAAO,EAAE,MAAM,CAAC,EAAE;CACpD,IAAI,WAAW,YAAY,OAAO,EAAE,KAAK,CAAC,EAAE;CAG5C,IAAI,WAAW,QAAQ,OAAO,KAAA;CAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,KAAK,EAAE;AACvC"}
@@ -28,4 +28,4 @@ export type { BedrockCohereEmbeddingInputType, BedrockCohereEmbeddingProviderOpt
28
28
  export type { BedrockConverseProviderOptions } from './converse/provider-options.js';
29
29
  export { resolveBedrockAuth, withBedrockDefaults, type BedrockClientConfig, type BedrockEndpoint, type ResolvedBedrockAuth, } from './utils/client.js';
30
30
  export { BEDROCK_CHAT_MODELS, BEDROCK_RESPONSES_MODELS, BEDROCK_CONVERSE_MODELS, BEDROCK_EMBEDDING_MODELS, type BedrockEmbeddingModel, type BedrockEmbeddingModelProviderOptionsByName, type BedrockEmbeddingModelInputModalitiesByName, type BedrockChatModels, type BedrockResponsesModels, type BedrockConverseModels, type BedrockChatModelProviderOptionsByName, type BedrockChatModelToolCapabilitiesByName, type BedrockModelInputModalitiesByName, } from './model-meta.js';
31
- export type { BedrockMessageMetadataByModality, BedrockTextMetadata, BedrockImageMetadata, BedrockAudioMetadata, BedrockVideoMetadata, BedrockDocumentMetadata, } from './message-types.js';
31
+ export type { BedrockCachePoint, BedrockSystemPromptMetadata, BedrockToolMetadata, BedrockMessageMetadataByModality, BedrockTextMetadata, BedrockImageMetadata, BedrockAudioMetadata, BedrockVideoMetadata, BedrockDocumentMetadata, } from './message-types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/index.ts"],"sourcesContent":["/**\n * @module @tanstack/ai-bedrock\n *\n * Amazon Bedrock adapter for TanStack AI via Bedrock's OpenAI-compatible APIs\n * and the native Converse API. The public `bedrockText` / `createBedrockText`\n * factory branches between the Converse adapter (DEFAULT), the Chat Completions\n * adapter (`api: 'chat'`), and the Responses adapter (`api: 'responses'`).\n */\n\nimport { BedrockTextAdapter } from './adapters/text'\nimport { BedrockResponsesTextAdapter } from './adapters/responses-text'\nimport { BedrockConverseTextAdapter } from './adapters/converse-text'\nimport {\n BEDROCK_CHAT_MODELS,\n BEDROCK_CONVERSE_MODELS,\n BEDROCK_RESPONSES_MODELS,\n} from './model-meta'\nimport type { BedrockTextConfig } from './adapters/text'\nimport type { BedrockResponsesConfig } from './adapters/responses-text'\nimport type { BedrockConverseConfig } from './adapters/converse-text'\nimport type { BedrockClientConfig } from './utils/client'\nimport type {\n BedrockChatModels,\n BedrockConverseModels,\n BedrockResponsesModels,\n} from './model-meta'\n\n/** Config for the branching factory's converse mode (default, or api: 'converse'). */\nexport type BedrockConverseApiConfig = BedrockConverseConfig & {\n api?: 'converse'\n}\n/** Config for the branching factory's chat mode (api: 'chat' required). */\nexport type BedrockChatApiConfig = BedrockTextConfig & {\n api: 'chat'\n}\n/** Config for the branching factory's responses mode (api: 'responses' required). */\nexport type BedrockResponsesApiConfig = BedrockResponsesConfig & {\n api: 'responses'\n}\n\ntype AnyBedrockAdapter =\n | BedrockConverseTextAdapter<BedrockConverseModels>\n | BedrockTextAdapter<BedrockChatModels>\n | BedrockResponsesTextAdapter<BedrockResponsesModels>\n\n/** Any catalog id the branching factory can receive. */\ntype BedrockFactoryModel =\n | BedrockConverseModels\n | BedrockChatModels\n | BedrockResponsesModels\n\n/** Cast-free runtime guard: is this model in the Responses-capable subset? */\nfunction isResponsesModel(model: string): model is BedrockResponsesModels {\n return BEDROCK_RESPONSES_MODELS.some((m) => m === model)\n}\n\n/** Cast-free runtime guard: is this model in the Chat-capable subset? */\nfunction isChatModel(model: string): model is BedrockChatModels {\n return BEDROCK_CHAT_MODELS.some((m) => m === model)\n}\n\n/** Cast-free runtime guard: is this model in the Converse-capable subset? */\nfunction isConverseModel(model: string): model is BedrockConverseModels {\n return BEDROCK_CONVERSE_MODELS.some((m) => m === model)\n}\n\n/** Strip the `api` discriminator from a config without an unused-var lint error. */\nfunction stripApi<T extends { api?: unknown }>(config: T): Omit<T, 'api'> {\n const { api, ...rest } = config\n void api\n return rest\n}\n\n/**\n * Shared branching used by both public factories. Constructs the adapter\n * classes directly so their constructors run the full auth cascade lazily\n * (config.apiKey → BEDROCK_API_KEY → AWS_BEARER_TOKEN_BEDROCK → SigV4). No\n * eager env-key fetch here, so `auth: 'sigv4'` never throws for a missing key.\n *\n * Default path → Converse adapter; opt-in via `api: 'chat'` or `api: 'responses'`.\n */\nfunction build(\n model: BedrockFactoryModel,\n config?: BedrockClientConfig & { api?: 'converse' | 'chat' | 'responses' },\n): AnyBedrockAdapter {\n if (config?.api === 'responses') {\n const rest = stripApi(config)\n if (!isResponsesModel(model)) {\n throw new Error(\n `Model \"${model}\" is not available on the Bedrock Responses API. ` +\n `Responses-capable models: ${BEDROCK_RESPONSES_MODELS.join(', ')}.`,\n )\n }\n return new BedrockResponsesTextAdapter(rest, model)\n }\n if (config?.api === 'chat') {\n if (!isChatModel(model)) {\n throw new Error(\n `Model \"${model}\" is not available on the Bedrock Chat Completions API. ` +\n `Chat-capable models: ${BEDROCK_CHAT_MODELS.join(', ')}.`,\n )\n }\n return new BedrockTextAdapter(stripApi(config), model)\n }\n if (!isConverseModel(model)) {\n throw new Error(\n `Model \"${model}\" is not available on the Bedrock Converse API. ` +\n `Converse-capable models: ${BEDROCK_CONVERSE_MODELS.join(', ')}.`,\n )\n }\n return new BedrockConverseTextAdapter(config ? stripApi(config) : {}, model)\n}\n\n// --- createBedrockText: explicit key, overloaded on `api` ---\nexport function createBedrockText<TModel extends BedrockConverseModels>(\n model: TModel,\n apiKey: string,\n config?: BedrockConverseApiConfig,\n): BedrockConverseTextAdapter<TModel>\nexport function createBedrockText<TModel extends BedrockChatModels>(\n model: TModel,\n apiKey: string,\n config: BedrockChatApiConfig,\n): BedrockTextAdapter<TModel>\nexport function createBedrockText<TModel extends BedrockResponsesModels>(\n model: TModel,\n apiKey: string,\n config: BedrockResponsesApiConfig,\n): BedrockResponsesTextAdapter<TModel>\nexport function createBedrockText(\n model: BedrockFactoryModel,\n apiKey: string,\n config?:\n | BedrockConverseApiConfig\n | BedrockChatApiConfig\n | BedrockResponsesApiConfig,\n): AnyBedrockAdapter {\n // Explicit apiKey is authoritative — spread config first so it can't override.\n return build(model, { ...config, apiKey })\n}\n\n// --- bedrockText: env-key counterpart, same overloads ---\nexport function bedrockText<TModel extends BedrockConverseModels>(\n model: TModel,\n config?: BedrockConverseApiConfig,\n): BedrockConverseTextAdapter<TModel>\nexport function bedrockText<TModel extends BedrockChatModels>(\n model: TModel,\n config: BedrockChatApiConfig,\n): BedrockTextAdapter<TModel>\nexport function bedrockText<TModel extends BedrockResponsesModels>(\n model: TModel,\n config: BedrockResponsesApiConfig,\n): BedrockResponsesTextAdapter<TModel>\nexport function bedrockText(\n model: BedrockFactoryModel,\n config?:\n | BedrockConverseApiConfig\n | BedrockChatApiConfig\n | BedrockResponsesApiConfig,\n): AnyBedrockAdapter {\n // No eager env-key fetch: the adapter constructor resolves auth lazily so\n // SigV4 (and the env-key fallback) work without a forced API key here.\n return build(model, config)\n}\n\n// --- Re-exports ---\nexport {\n BedrockTextAdapter,\n createBedrockChat,\n type BedrockTextConfig,\n type BedrockTextProviderOptions,\n} from './adapters/text'\nexport {\n BedrockResponsesTextAdapter,\n createBedrockResponsesText,\n type BedrockResponsesConfig,\n type BedrockResponsesProviderOptions,\n} from './adapters/responses-text'\nexport {\n BedrockConverseTextAdapter,\n createBedrockConverse,\n type BedrockConverseConfig,\n} from './adapters/converse-text'\nexport {\n BedrockEmbeddingAdapter,\n bedrockEmbedding,\n createBedrockEmbedding,\n type BedrockEmbeddingConfig,\n} from './adapters/embedding'\nexport type {\n BedrockCohereEmbeddingInputType,\n BedrockCohereEmbeddingProviderOptions,\n BedrockEmbeddingProviderOptions,\n BedrockTitanImageEmbeddingProviderOptions,\n BedrockTitanTextEmbeddingProviderOptions,\n} from './embedding/embedding-provider-options'\nexport type { BedrockConverseProviderOptions } from './converse/provider-options'\nexport {\n resolveBedrockAuth,\n withBedrockDefaults,\n type BedrockClientConfig,\n type BedrockEndpoint,\n type ResolvedBedrockAuth,\n} from './utils/client'\nexport {\n BEDROCK_CHAT_MODELS,\n BEDROCK_RESPONSES_MODELS,\n BEDROCK_CONVERSE_MODELS,\n BEDROCK_EMBEDDING_MODELS,\n type BedrockEmbeddingModel,\n type BedrockEmbeddingModelProviderOptionsByName,\n type BedrockEmbeddingModelInputModalitiesByName,\n type BedrockChatModels,\n type BedrockResponsesModels,\n type BedrockConverseModels,\n type BedrockChatModelProviderOptionsByName,\n type BedrockChatModelToolCapabilitiesByName,\n type BedrockModelInputModalitiesByName,\n} from './model-meta'\nexport type {\n BedrockMessageMetadataByModality,\n BedrockTextMetadata,\n BedrockImageMetadata,\n BedrockAudioMetadata,\n BedrockVideoMetadata,\n BedrockDocumentMetadata,\n} from './message-types'\n"],"mappings":";;;;;;;;;;;;;;;;;AAoDA,SAAS,iBAAiB,OAAgD;CACxE,OAAO,yBAAyB,MAAM,MAAM,MAAM,KAAK;AACzD;;AAGA,SAAS,YAAY,OAA2C;CAC9D,OAAO,oBAAoB,MAAM,MAAM,MAAM,KAAK;AACpD;;AAGA,SAAS,gBAAgB,OAA+C;CACtE,OAAO,wBAAwB,MAAM,MAAM,MAAM,KAAK;AACxD;;AAGA,SAAS,SAAsC,QAA2B;CACxE,MAAM,EAAE,KAAK,GAAG,SAAS;CAEzB,OAAO;AACT;;;;;;;;;AAUA,SAAS,MACP,OACA,QACmB;CACnB,IAAI,QAAQ,QAAQ,aAAa;EAC/B,MAAM,OAAO,SAAS,MAAM;EAC5B,IAAI,CAAC,iBAAiB,KAAK,GACzB,MAAM,IAAI,MACR,UAAU,MAAM,6EACe,yBAAyB,KAAK,IAAI,EAAE,EACrE;EAEF,OAAO,IAAI,4BAA4B,MAAM,KAAK;CACpD;CACA,IAAI,QAAQ,QAAQ,QAAQ;EAC1B,IAAI,CAAC,YAAY,KAAK,GACpB,MAAM,IAAI,MACR,UAAU,MAAM,+EACU,oBAAoB,KAAK,IAAI,EAAE,EAC3D;EAEF,OAAO,IAAI,mBAAmB,SAAS,MAAM,GAAG,KAAK;CACvD;CACA,IAAI,CAAC,gBAAgB,KAAK,GACxB,MAAM,IAAI,MACR,UAAU,MAAM,2EACc,wBAAwB,KAAK,IAAI,EAAE,EACnE;CAEF,OAAO,IAAI,2BAA2B,SAAS,SAAS,MAAM,IAAI,CAAC,GAAG,KAAK;AAC7E;AAkBA,SAAgB,kBACd,OACA,QACA,QAImB;CAEnB,OAAO,MAAM,OAAO;EAAE,GAAG;EAAQ;CAAO,CAAC;AAC3C;AAeA,SAAgB,YACd,OACA,QAImB;CAGnB,OAAO,MAAM,OAAO,MAAM;AAC5B"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/index.ts"],"sourcesContent":["/**\n * @module @tanstack/ai-bedrock\n *\n * Amazon Bedrock adapter for TanStack AI via Bedrock's OpenAI-compatible APIs\n * and the native Converse API. The public `bedrockText` / `createBedrockText`\n * factory branches between the Converse adapter (DEFAULT), the Chat Completions\n * adapter (`api: 'chat'`), and the Responses adapter (`api: 'responses'`).\n */\n\nimport { BedrockTextAdapter } from './adapters/text'\nimport { BedrockResponsesTextAdapter } from './adapters/responses-text'\nimport { BedrockConverseTextAdapter } from './adapters/converse-text'\nimport {\n BEDROCK_CHAT_MODELS,\n BEDROCK_CONVERSE_MODELS,\n BEDROCK_RESPONSES_MODELS,\n} from './model-meta'\nimport type { BedrockTextConfig } from './adapters/text'\nimport type { BedrockResponsesConfig } from './adapters/responses-text'\nimport type { BedrockConverseConfig } from './adapters/converse-text'\nimport type { BedrockClientConfig } from './utils/client'\nimport type {\n BedrockChatModels,\n BedrockConverseModels,\n BedrockResponsesModels,\n} from './model-meta'\n\n/** Config for the branching factory's converse mode (default, or api: 'converse'). */\nexport type BedrockConverseApiConfig = BedrockConverseConfig & {\n api?: 'converse'\n}\n/** Config for the branching factory's chat mode (api: 'chat' required). */\nexport type BedrockChatApiConfig = BedrockTextConfig & {\n api: 'chat'\n}\n/** Config for the branching factory's responses mode (api: 'responses' required). */\nexport type BedrockResponsesApiConfig = BedrockResponsesConfig & {\n api: 'responses'\n}\n\ntype AnyBedrockAdapter =\n | BedrockConverseTextAdapter<BedrockConverseModels>\n | BedrockTextAdapter<BedrockChatModels>\n | BedrockResponsesTextAdapter<BedrockResponsesModels>\n\n/** Any catalog id the branching factory can receive. */\ntype BedrockFactoryModel =\n | BedrockConverseModels\n | BedrockChatModels\n | BedrockResponsesModels\n\n/** Cast-free runtime guard: is this model in the Responses-capable subset? */\nfunction isResponsesModel(model: string): model is BedrockResponsesModels {\n return BEDROCK_RESPONSES_MODELS.some((m) => m === model)\n}\n\n/** Cast-free runtime guard: is this model in the Chat-capable subset? */\nfunction isChatModel(model: string): model is BedrockChatModels {\n return BEDROCK_CHAT_MODELS.some((m) => m === model)\n}\n\n/** Cast-free runtime guard: is this model in the Converse-capable subset? */\nfunction isConverseModel(model: string): model is BedrockConverseModels {\n return BEDROCK_CONVERSE_MODELS.some((m) => m === model)\n}\n\n/** Strip the `api` discriminator from a config without an unused-var lint error. */\nfunction stripApi<T extends { api?: unknown }>(config: T): Omit<T, 'api'> {\n const { api, ...rest } = config\n void api\n return rest\n}\n\n/**\n * Shared branching used by both public factories. Constructs the adapter\n * classes directly so their constructors run the full auth cascade lazily\n * (config.apiKey → BEDROCK_API_KEY → AWS_BEARER_TOKEN_BEDROCK → SigV4). No\n * eager env-key fetch here, so `auth: 'sigv4'` never throws for a missing key.\n *\n * Default path → Converse adapter; opt-in via `api: 'chat'` or `api: 'responses'`.\n */\nfunction build(\n model: BedrockFactoryModel,\n config?: BedrockClientConfig & { api?: 'converse' | 'chat' | 'responses' },\n): AnyBedrockAdapter {\n if (config?.api === 'responses') {\n const rest = stripApi(config)\n if (!isResponsesModel(model)) {\n throw new Error(\n `Model \"${model}\" is not available on the Bedrock Responses API. ` +\n `Responses-capable models: ${BEDROCK_RESPONSES_MODELS.join(', ')}.`,\n )\n }\n return new BedrockResponsesTextAdapter(rest, model)\n }\n if (config?.api === 'chat') {\n if (!isChatModel(model)) {\n throw new Error(\n `Model \"${model}\" is not available on the Bedrock Chat Completions API. ` +\n `Chat-capable models: ${BEDROCK_CHAT_MODELS.join(', ')}.`,\n )\n }\n return new BedrockTextAdapter(stripApi(config), model)\n }\n if (!isConverseModel(model)) {\n throw new Error(\n `Model \"${model}\" is not available on the Bedrock Converse API. ` +\n `Converse-capable models: ${BEDROCK_CONVERSE_MODELS.join(', ')}.`,\n )\n }\n return new BedrockConverseTextAdapter(config ? stripApi(config) : {}, model)\n}\n\n// --- createBedrockText: explicit key, overloaded on `api` ---\nexport function createBedrockText<TModel extends BedrockConverseModels>(\n model: TModel,\n apiKey: string,\n config?: BedrockConverseApiConfig,\n): BedrockConverseTextAdapter<TModel>\nexport function createBedrockText<TModel extends BedrockChatModels>(\n model: TModel,\n apiKey: string,\n config: BedrockChatApiConfig,\n): BedrockTextAdapter<TModel>\nexport function createBedrockText<TModel extends BedrockResponsesModels>(\n model: TModel,\n apiKey: string,\n config: BedrockResponsesApiConfig,\n): BedrockResponsesTextAdapter<TModel>\nexport function createBedrockText(\n model: BedrockFactoryModel,\n apiKey: string,\n config?:\n | BedrockConverseApiConfig\n | BedrockChatApiConfig\n | BedrockResponsesApiConfig,\n): AnyBedrockAdapter {\n // Explicit apiKey is authoritative — spread config first so it can't override.\n return build(model, { ...config, apiKey })\n}\n\n// --- bedrockText: env-key counterpart, same overloads ---\nexport function bedrockText<TModel extends BedrockConverseModels>(\n model: TModel,\n config?: BedrockConverseApiConfig,\n): BedrockConverseTextAdapter<TModel>\nexport function bedrockText<TModel extends BedrockChatModels>(\n model: TModel,\n config: BedrockChatApiConfig,\n): BedrockTextAdapter<TModel>\nexport function bedrockText<TModel extends BedrockResponsesModels>(\n model: TModel,\n config: BedrockResponsesApiConfig,\n): BedrockResponsesTextAdapter<TModel>\nexport function bedrockText(\n model: BedrockFactoryModel,\n config?:\n | BedrockConverseApiConfig\n | BedrockChatApiConfig\n | BedrockResponsesApiConfig,\n): AnyBedrockAdapter {\n // No eager env-key fetch: the adapter constructor resolves auth lazily so\n // SigV4 (and the env-key fallback) work without a forced API key here.\n return build(model, config)\n}\n\n// --- Re-exports ---\nexport {\n BedrockTextAdapter,\n createBedrockChat,\n type BedrockTextConfig,\n type BedrockTextProviderOptions,\n} from './adapters/text'\nexport {\n BedrockResponsesTextAdapter,\n createBedrockResponsesText,\n type BedrockResponsesConfig,\n type BedrockResponsesProviderOptions,\n} from './adapters/responses-text'\nexport {\n BedrockConverseTextAdapter,\n createBedrockConverse,\n type BedrockConverseConfig,\n} from './adapters/converse-text'\nexport {\n BedrockEmbeddingAdapter,\n bedrockEmbedding,\n createBedrockEmbedding,\n type BedrockEmbeddingConfig,\n} from './adapters/embedding'\nexport type {\n BedrockCohereEmbeddingInputType,\n BedrockCohereEmbeddingProviderOptions,\n BedrockEmbeddingProviderOptions,\n BedrockTitanImageEmbeddingProviderOptions,\n BedrockTitanTextEmbeddingProviderOptions,\n} from './embedding/embedding-provider-options'\nexport type { BedrockConverseProviderOptions } from './converse/provider-options'\nexport {\n resolveBedrockAuth,\n withBedrockDefaults,\n type BedrockClientConfig,\n type BedrockEndpoint,\n type ResolvedBedrockAuth,\n} from './utils/client'\nexport {\n BEDROCK_CHAT_MODELS,\n BEDROCK_RESPONSES_MODELS,\n BEDROCK_CONVERSE_MODELS,\n BEDROCK_EMBEDDING_MODELS,\n type BedrockEmbeddingModel,\n type BedrockEmbeddingModelProviderOptionsByName,\n type BedrockEmbeddingModelInputModalitiesByName,\n type BedrockChatModels,\n type BedrockResponsesModels,\n type BedrockConverseModels,\n type BedrockChatModelProviderOptionsByName,\n type BedrockChatModelToolCapabilitiesByName,\n type BedrockModelInputModalitiesByName,\n} from './model-meta'\nexport type {\n BedrockCachePoint,\n BedrockSystemPromptMetadata,\n BedrockToolMetadata,\n BedrockMessageMetadataByModality,\n BedrockTextMetadata,\n BedrockImageMetadata,\n BedrockAudioMetadata,\n BedrockVideoMetadata,\n BedrockDocumentMetadata,\n} from './message-types'\n"],"mappings":";;;;;;;;;;;;;;;;;AAoDA,SAAS,iBAAiB,OAAgD;CACxE,OAAO,yBAAyB,MAAM,MAAM,MAAM,KAAK;AACzD;;AAGA,SAAS,YAAY,OAA2C;CAC9D,OAAO,oBAAoB,MAAM,MAAM,MAAM,KAAK;AACpD;;AAGA,SAAS,gBAAgB,OAA+C;CACtE,OAAO,wBAAwB,MAAM,MAAM,MAAM,KAAK;AACxD;;AAGA,SAAS,SAAsC,QAA2B;CACxE,MAAM,EAAE,KAAK,GAAG,SAAS;CAEzB,OAAO;AACT;;;;;;;;;AAUA,SAAS,MACP,OACA,QACmB;CACnB,IAAI,QAAQ,QAAQ,aAAa;EAC/B,MAAM,OAAO,SAAS,MAAM;EAC5B,IAAI,CAAC,iBAAiB,KAAK,GACzB,MAAM,IAAI,MACR,UAAU,MAAM,6EACe,yBAAyB,KAAK,IAAI,EAAE,EACrE;EAEF,OAAO,IAAI,4BAA4B,MAAM,KAAK;CACpD;CACA,IAAI,QAAQ,QAAQ,QAAQ;EAC1B,IAAI,CAAC,YAAY,KAAK,GACpB,MAAM,IAAI,MACR,UAAU,MAAM,+EACU,oBAAoB,KAAK,IAAI,EAAE,EAC3D;EAEF,OAAO,IAAI,mBAAmB,SAAS,MAAM,GAAG,KAAK;CACvD;CACA,IAAI,CAAC,gBAAgB,KAAK,GACxB,MAAM,IAAI,MACR,UAAU,MAAM,2EACc,wBAAwB,KAAK,IAAI,EAAE,EACnE;CAEF,OAAO,IAAI,2BAA2B,SAAS,SAAS,MAAM,IAAI,CAAC,GAAG,KAAK;AAC7E;AAkBA,SAAgB,kBACd,OACA,QACA,QAImB;CAEnB,OAAO,MAAM,OAAO;EAAE,GAAG;EAAQ;CAAO,CAAC;AAC3C;AAeA,SAAgB,YACd,OACA,QAImB;CAGnB,OAAO,MAAM,OAAO,MAAM;AAC5B"}
@@ -1,10 +1,33 @@
1
+ /**
2
+ * A Bedrock prompt-cache checkpoint. Bedrock caches everything in the request
3
+ * before this block and reads it back at the cache rate while the entry lives.
4
+ * A request may carry up to four. Omit `ttl` for the 5-minute default.
5
+ *
6
+ * @see https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html
7
+ */
8
+ export interface BedrockCachePoint {
9
+ type: 'default';
10
+ ttl?: '5m' | '1h';
11
+ }
12
+ /** Metadata on a `systemPrompts` entry, read by the Converse adapter. */
13
+ export interface BedrockSystemPromptMetadata {
14
+ /** Place a cache checkpoint right after this system prompt. */
15
+ cachePoint?: BedrockCachePoint;
16
+ }
17
+ /** Metadata on a tool definition, read by the Converse adapter. */
18
+ export interface BedrockToolMetadata {
19
+ /** Place a cache checkpoint right after this tool's definition. */
20
+ cachePoint?: BedrockCachePoint;
21
+ }
1
22
  /**
2
23
  * Bedrock content-part metadata by modality, used for type inference when
3
24
  * constructing multimodal messages. Bedrock's OpenAI-compatible Chat
4
- * Completions accepts the standard OpenAI image-detail hint; other modalities
5
- * carry no extra metadata today.
25
+ * Completions accepts the standard OpenAI image-detail hint; the Converse
26
+ * adapter reads `cachePoint` on text parts.
6
27
  */
7
28
  export interface BedrockTextMetadata {
29
+ /** Place a cache checkpoint right after this text block (Converse only). */
30
+ cachePoint?: BedrockCachePoint;
8
31
  }
9
32
  export interface BedrockImageMetadata {
10
33
  /** Image processing detail: 'auto' (default), 'low', or 'high'. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-bedrock",
3
- "version": "0.3.13",
3
+ "version": "0.4.1",
4
4
  "type": "module",
5
5
  "description": "Amazon Bedrock adapter for TanStack AI — OpenAI-compatible chat, responses, tools, and reasoning.",
6
6
  "author": "",
@@ -43,7 +43,7 @@
43
43
  "vite": "^8.2.1"
44
44
  },
45
45
  "peerDependencies": {
46
- "@tanstack/ai": "^0.59.0"
46
+ "@tanstack/ai": "^0.63.0"
47
47
  },
48
48
  "dependencies": {
49
49
  "@aws-crypto/sha256-js": "^5.2.0",
@@ -52,7 +52,7 @@
52
52
  "@smithy/signature-v4": "^5.4.5",
53
53
  "@smithy/types": "^4.14.2",
54
54
  "openai": "^6.41.0",
55
- "@tanstack/openai-base": "^0.10.16"
55
+ "@tanstack/openai-base": "^0.12.1"
56
56
  },
57
57
  "scripts": {
58
58
  "build": "vite build",
@@ -60,6 +60,7 @@
60
60
  "lint:fix": "oxlint src --type-aware --fix",
61
61
  "test:build": "publint --strict",
62
62
  "test:oxlint": "oxlint src --type-aware",
63
+ "test:coverage": "vitest run --coverage --coverage.include='src/**' --coverage.reporter=text-summary --coverage.reporter=json-summary",
63
64
  "test:lib": "vitest run",
64
65
  "test:lib:dev": "pnpm test:lib --watch",
65
66
  "test:types": "tsc"
@@ -38,7 +38,11 @@ import type {
38
38
  StructuredOutputResult,
39
39
  } from '@tanstack/ai/adapters'
40
40
  import type { BedrockClientConfig } from '../utils/client'
41
- import type { BedrockMessageMetadataByModality } from '../message-types'
41
+ import type {
42
+ BedrockMessageMetadataByModality,
43
+ BedrockSystemPromptMetadata,
44
+ BedrockToolMetadata,
45
+ } from '../message-types'
42
46
  import type {
43
47
  BedrockConverseModels,
44
48
  ResolveConverseProviderOptions,
@@ -78,7 +82,15 @@ export class BedrockConverseTextAdapter<
78
82
  TModel,
79
83
  TProviderOptions,
80
84
  TInputModalities,
81
- BedrockMessageMetadataByModality
85
+ BedrockMessageMetadataByModality,
86
+ // TToolCapabilities — Converse has no per-model tool-capability table; the
87
+ // base default.
88
+ ReadonlyArray<string>,
89
+ // TToolCallMetadata — Converse has no tool-call metadata round-tripping.
90
+ unknown,
91
+ // TSystemPromptMetadata — narrows `systemPrompts[i].metadata` at the chat()
92
+ // call site so users get `cachePoint` autocomplete.
93
+ BedrockSystemPromptMetadata
82
94
  > {
83
95
  override readonly kind = 'text' as const
84
96
  override readonly name = 'bedrock-converse' as const
@@ -593,10 +605,12 @@ function convertTools(tools: Array<Tool>): Array<ConverseToolInput> {
593
605
  const inputSchema: JSONSchema = convertSchemaToJsonSchema(
594
606
  tool.inputSchema,
595
607
  ) ?? { type: 'object', properties: {}, required: [] }
608
+ const { cachePoint }: BedrockToolMetadata = tool.metadata ?? {}
596
609
  return {
597
610
  name: tool.name,
598
611
  description: tool.description,
599
612
  inputSchema,
613
+ ...(cachePoint && { cachePoint }),
600
614
  }
601
615
  })
602
616
  }
@@ -1,8 +1,10 @@
1
1
  import { BaseEmbeddingAdapter } from '@tanstack/ai/adapters'
2
2
  import { toRunErrorPayload } from '@tanstack/ai/adapter-internals'
3
3
  import {
4
+ isFileSource,
4
5
  requireTextOnlyEmbeddingInput,
5
6
  resolveEmbeddingInput,
7
+ unsupportedFileSourceError,
6
8
  } from '@tanstack/ai'
7
9
  import { resolveBedrockAuth } from '../utils/auth'
8
10
  import { BEDROCK_EMBEDDING_MODELS } from '../model-meta'
@@ -432,6 +434,7 @@ function readCohereEmbeddingBody(
432
434
  */
433
435
  function toTitanInputImage(image: ImagePart, model: string): string {
434
436
  const source = image.source
437
+ if (isFileSource(source)) throw unsupportedFileSourceError('bedrock')
435
438
  if (source.type === 'data') {
436
439
  return source.value
437
440
  }
@@ -10,8 +10,10 @@ import type {
10
10
  ResolveInputModalities,
11
11
  } from '../model-meta'
12
12
  import type { ExternalResponsesProviderOptions } from '../text/responses-provider-options'
13
+ import type { OpenAIBaseTextAdapterOptions } from '@tanstack/openai-base'
13
14
 
14
- export interface BedrockResponsesConfig extends BedrockClientConfig {}
15
+ export interface BedrockResponsesConfig
16
+ extends BedrockClientConfig, OpenAIBaseTextAdapterOptions {}
15
17
 
16
18
  export type { ExternalResponsesProviderOptions as BedrockResponsesProviderOptions } from '../text/responses-provider-options'
17
19
 
@@ -57,6 +59,7 @@ export class BedrockResponsesTextAdapter<
57
59
  model,
58
60
  'bedrock-responses',
59
61
  new OpenAI(withBedrockDefaults(config, 'mantle', model)),
62
+ config,
60
63
  )
61
64
  }
62
65
  }
@@ -10,8 +10,10 @@ import type {
10
10
  ResolveInputModalities,
11
11
  ResolveProviderOptions,
12
12
  } from '../model-meta'
13
+ import type { OpenAIBaseTextAdapterOptions } from '@tanstack/openai-base'
13
14
 
14
- export interface BedrockTextConfig extends BedrockClientConfig {}
15
+ export interface BedrockTextConfig
16
+ extends BedrockClientConfig, OpenAIBaseTextAdapterOptions {}
15
17
 
16
18
  export type { ExternalTextProviderOptions as BedrockTextProviderOptions } from '../text/text-provider-options'
17
19
 
@@ -56,6 +58,7 @@ export class BedrockTextAdapter<
56
58
  model,
57
59
  'bedrock',
58
60
  new OpenAI(withBedrockDefaults(config, undefined, model)),
61
+ config,
59
62
  )
60
63
  }
61
64
 
@@ -1,4 +1,8 @@
1
- import { normalizeSystemPrompts } from '@tanstack/ai'
1
+ import {
2
+ isFileSource,
3
+ normalizeSystemPrompts,
4
+ unsupportedFileSourceError,
5
+ } from '@tanstack/ai'
2
6
  import type {
3
7
  ContentPart,
4
8
  ContentPartDataSource,
@@ -15,6 +19,10 @@ import type {
15
19
  ToolResultContentBlock,
16
20
  } from '@aws-sdk/client-bedrock-runtime'
17
21
  import type { DocumentType } from '@smithy/types'
22
+ import type {
23
+ BedrockSystemPromptMetadata,
24
+ BedrockTextMetadata,
25
+ } from '../message-types'
18
26
 
19
27
  // ---------------------------------------------------------------------------
20
28
  // Helpers
@@ -108,6 +116,7 @@ function contentPartToBlock(part: ContentPart, docIndex: number): ContentBlock {
108
116
 
109
117
  if (isImagePart(part)) {
110
118
  const { source } = part
119
+ if (isFileSource(source)) throw unsupportedFileSourceError('bedrock')
111
120
  if (!isDataSource(source)) {
112
121
  throw new Error(
113
122
  'Bedrock Converse requires inline image bytes; URL image sources are not supported.',
@@ -123,6 +132,7 @@ function contentPartToBlock(part: ContentPart, docIndex: number): ContentBlock {
123
132
 
124
133
  if (isDocumentPart(part)) {
125
134
  const { source } = part
135
+ if (isFileSource(source)) throw unsupportedFileSourceError('bedrock')
126
136
  if (!isDataSource(source)) {
127
137
  throw new Error(
128
138
  'Bedrock Converse requires inline document bytes; URL document sources are not supported.',
@@ -176,6 +186,10 @@ function messageToBlocks(
176
186
  for (const part of msg.content) {
177
187
  const docIndex = isDocumentPart(part) ? ++docCounter.value : 0
178
188
  blocks.push(contentPartToBlock(part, docIndex))
189
+ if (isTextPart(part)) {
190
+ const { cachePoint } = (part.metadata ?? {}) as BedrockTextMetadata
191
+ if (cachePoint) blocks.push({ cachePoint })
192
+ }
179
193
  }
180
194
  }
181
195
  // null → no text blocks
@@ -225,7 +239,8 @@ function messageToBlocks(
225
239
  /**
226
240
  * Convert TanStack AI messages + system prompts into the Converse API format.
227
241
  *
228
- * - System prompts are lifted into `SystemContentBlock[]`.
242
+ * - System prompts are lifted into `SystemContentBlock[]`; a prompt whose
243
+ * `metadata.cachePoint` is set is followed by a `cachePoint` block.
229
244
  * - `tool` role messages are remapped to `user` role `toolResult` blocks.
230
245
  * - Consecutive messages with the same Converse role are merged (Converse
231
246
  * requires strict user/assistant alternation).
@@ -235,9 +250,13 @@ export function toConverseMessages(
235
250
  systemPrompts?: Array<SystemPrompt>,
236
251
  ): { system: Array<SystemContentBlock>; messages: Array<Message> } {
237
252
  // Build system blocks (uses normalizeSystemPrompts for runtime validation)
238
- const system: Array<SystemContentBlock> = normalizeSystemPrompts(
239
- systemPrompts,
240
- ).map((p) => ({ text: p.content }))
253
+ const system: Array<SystemContentBlock> =
254
+ normalizeSystemPrompts<BedrockSystemPromptMetadata>(systemPrompts).flatMap(
255
+ (p) =>
256
+ p.metadata?.cachePoint
257
+ ? [{ text: p.content }, { cachePoint: p.metadata.cachePoint }]
258
+ : [{ text: p.content }],
259
+ )
241
260
 
242
261
  // Convert each ModelMessage to a Converse Message, merging same-role pairs
243
262
  const converseMessages: Array<Message> = []
@@ -3,11 +3,14 @@ import type {
3
3
  ToolConfiguration,
4
4
  } from '@aws-sdk/client-bedrock-runtime'
5
5
  import type { DocumentType } from '@smithy/types'
6
+ import type { BedrockCachePoint } from '../message-types'
6
7
 
7
8
  export interface ConverseToolInput {
8
9
  name: string
9
10
  description?: string
10
11
  inputSchema: unknown
12
+ /** Emit a `cachePoint` entry right after this tool. */
13
+ cachePoint?: BedrockCachePoint
11
14
  }
12
15
 
13
16
  export type ToolChoiceInput =
@@ -26,13 +29,16 @@ export function toToolConfig(
26
29
  if (choice === 'none') return undefined
27
30
  const toolChoice = mapChoice(choice)
28
31
  return {
29
- tools: tools.map((t) => ({
30
- toolSpec: {
31
- name: t.name,
32
- ...(t.description ? { description: t.description } : {}),
33
- inputSchema: { json: t.inputSchema as DocumentType },
32
+ tools: tools.flatMap((t) => [
33
+ {
34
+ toolSpec: {
35
+ name: t.name,
36
+ ...(t.description ? { description: t.description } : {}),
37
+ inputSchema: { json: t.inputSchema as DocumentType },
38
+ },
34
39
  },
35
- })),
40
+ ...(t.cachePoint ? [{ cachePoint: t.cachePoint }] : []),
41
+ ]),
36
42
  ...(toolChoice ? { toolChoice } : {}),
37
43
  }
38
44
  }
package/src/index.ts CHANGED
@@ -219,6 +219,9 @@ export {
219
219
  type BedrockModelInputModalitiesByName,
220
220
  } from './model-meta'
221
221
  export type {
222
+ BedrockCachePoint,
223
+ BedrockSystemPromptMetadata,
224
+ BedrockToolMetadata,
222
225
  BedrockMessageMetadataByModality,
223
226
  BedrockTextMetadata,
224
227
  BedrockImageMetadata,
@@ -1,10 +1,37 @@
1
+ /**
2
+ * A Bedrock prompt-cache checkpoint. Bedrock caches everything in the request
3
+ * before this block and reads it back at the cache rate while the entry lives.
4
+ * A request may carry up to four. Omit `ttl` for the 5-minute default.
5
+ *
6
+ * @see https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html
7
+ */
8
+ export interface BedrockCachePoint {
9
+ type: 'default'
10
+ ttl?: '5m' | '1h'
11
+ }
12
+
13
+ /** Metadata on a `systemPrompts` entry, read by the Converse adapter. */
14
+ export interface BedrockSystemPromptMetadata {
15
+ /** Place a cache checkpoint right after this system prompt. */
16
+ cachePoint?: BedrockCachePoint
17
+ }
18
+
19
+ /** Metadata on a tool definition, read by the Converse adapter. */
20
+ export interface BedrockToolMetadata {
21
+ /** Place a cache checkpoint right after this tool's definition. */
22
+ cachePoint?: BedrockCachePoint
23
+ }
24
+
1
25
  /**
2
26
  * Bedrock content-part metadata by modality, used for type inference when
3
27
  * constructing multimodal messages. Bedrock's OpenAI-compatible Chat
4
- * Completions accepts the standard OpenAI image-detail hint; other modalities
5
- * carry no extra metadata today.
28
+ * Completions accepts the standard OpenAI image-detail hint; the Converse
29
+ * adapter reads `cachePoint` on text parts.
6
30
  */
7
- export interface BedrockTextMetadata {}
31
+ export interface BedrockTextMetadata {
32
+ /** Place a cache checkpoint right after this text block (Converse only). */
33
+ cachePoint?: BedrockCachePoint
34
+ }
8
35
 
9
36
  export interface BedrockImageMetadata {
10
37
  /** Image processing detail: 'auto' (default), 'low', or 'high'. */