@tanstack/ai-bedrock 0.1.6 → 0.2.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.
- package/dist/esm/adapters/converse-text.js +9 -2
- package/dist/esm/adapters/converse-text.js.map +1 -1
- package/dist/esm/adapters/embedding.d.ts +128 -0
- package/dist/esm/adapters/embedding.js +337 -0
- package/dist/esm/adapters/embedding.js.map +1 -0
- package/dist/esm/embedding/embedding-provider-options.d.ts +51 -0
- package/dist/esm/index.d.ts +3 -1
- package/dist/esm/index.js +3 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/model-meta.d.ts +31 -0
- package/dist/esm/model-meta.js +12 -1
- package/dist/esm/model-meta.js.map +1 -1
- package/package.json +3 -3
- package/src/adapters/converse-text.ts +8 -0
- package/src/adapters/embedding.ts +547 -0
- package/src/embedding/embedding-provider-options.ts +63 -0
- package/src/index.ts +17 -0
- package/src/model-meta.ts +53 -0
|
@@ -148,11 +148,18 @@ var BedrockConverseTextAdapter = class extends BaseTextAdapter {
|
|
|
148
148
|
...this.buildInput(chatOptions),
|
|
149
149
|
toolConfig: buildStructuredToolConfig(outputSchema)
|
|
150
150
|
};
|
|
151
|
-
const
|
|
151
|
+
const res = await this.send(input);
|
|
152
|
+
const structured = extractStructuredToolInput(res);
|
|
152
153
|
if (structured === void 0) throw new Error(`${this.name}.structuredOutput: response contained no forced-tool output`);
|
|
154
|
+
const usage = res.usage;
|
|
153
155
|
return {
|
|
154
156
|
data: structured,
|
|
155
|
-
rawText: JSON.stringify(structured)
|
|
157
|
+
rawText: JSON.stringify(structured),
|
|
158
|
+
...usage && { usage: {
|
|
159
|
+
promptTokens: usage.inputTokens ?? 0,
|
|
160
|
+
completionTokens: usage.outputTokens ?? 0,
|
|
161
|
+
totalTokens: usage.totalTokens ?? 0
|
|
162
|
+
} }
|
|
156
163
|
};
|
|
157
164
|
} catch (error) {
|
|
158
165
|
chatOptions.logger.errors(`${this.name}.structuredOutput fatal`, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"converse-text.js","names":[],"sources":["../../../src/adapters/converse-text.ts"],"sourcesContent":["import { EventType, convertSchemaToJsonSchema } from '@tanstack/ai'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { resolveBedrockAuth } from '../utils/auth'\nimport { toConverseMessages } from '../converse/message-converter'\nimport { toToolConfig } from '../converse/tool-converter'\nimport {\n processConverseStream,\n throwIfConverseStreamError,\n} from '../converse/stream-processor'\nimport {\n STRUCTURED_TOOL_NAME,\n buildStructuredToolConfig,\n} from '../converse/structured-output'\nimport type { ResolvedBedrockAuth } from '../utils/auth'\nimport type { ConverseToolInput } from '../converse/tool-converter'\nimport type * as BedrockRuntime from '@aws-sdk/client-bedrock-runtime'\nimport type {\n BedrockRuntimeClient,\n BedrockRuntimeClientConfig,\n ContentBlock,\n ConverseCommandInput,\n ConverseCommandOutput,\n ConverseStreamCommandInput,\n ConverseStreamOutput,\n} from '@aws-sdk/client-bedrock-runtime'\nimport type {\n JSONSchema,\n Modality,\n StreamChunk,\n TextOptions,\n Tool,\n} from '@tanstack/ai'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type { BedrockClientConfig } from '../utils/client'\nimport type { BedrockMessageMetadataByModality } from '../message-types'\nimport type {\n BedrockConverseModels,\n ResolveConverseProviderOptions,\n ResolveInputModalities,\n} from '../model-meta'\n\n/** Config for the Converse adapter — same client config as the chat adapter. */\nexport interface BedrockConverseConfig extends BedrockClientConfig {}\n\n/**\n * Bedrock Converse text adapter. Wires the Converse translation modules (message\n * converter, tool converter, stream processor, structured-output forced-tool\n * builder) onto `@tanstack/ai`'s `BaseTextAdapter` and the\n * `@aws-sdk/client-bedrock-runtime` `BedrockRuntimeClient`.\n *\n * The success-path AG-UI lifecycle (`RUN_STARTED`..`RUN_FINISHED`) is owned by\n * `processConverseStream`; this adapter only owns the catch/`RUN_ERROR` path,\n * mirroring openai-base's `chatStream`.\n *\n * The actual SDK calls live behind two protected seams (`sendStream` / `send`)\n * so tests can subclass and inject canned Converse SDK shapes without a real\n * AWS request.\n */\nexport class BedrockConverseTextAdapter<\n TModel extends BedrockConverseModels,\n // Constraint mirrors the chat adapter (text.ts): the base parameterises\n // `TProviderOptions extends Record<string, any>`, and our default\n // `ResolveConverseProviderOptions<TModel>` resolves to an interface lacking an\n // implicit index signature — which `Record<string, unknown>` would reject but\n // `Record<string, any>` accepts. Confined to the generic constraint (the\n // established adapter pattern) — no value `as` cast is introduced.\n TProviderOptions extends Record<string, any> =\n ResolveConverseProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n BedrockMessageMetadataByModality\n> {\n override readonly kind = 'text' as const\n override readonly name = 'bedrock-converse' as const\n private clientPromise?: Promise<BedrockRuntimeClient>\n private readonly clientConfig: BedrockConverseConfig\n\n constructor(config: BedrockConverseConfig, model: TModel) {\n super({}, model)\n // Defer client construction and auth resolution: the AWS SDK is Node/\n // server-only, so we must not pull it into the static graph here. The\n // client (and its dynamic import) is built lazily on first SDK call.\n this.clientConfig = config\n }\n\n /**\n * Dynamically import `@aws-sdk/client-bedrock-runtime`. The specifier is held\n * in a variable (not a string literal) so bundler dep scanners (e.g. Vite/\n * esbuild optimizeDeps) cannot statically discover the AWS SDK and try to\n * pre-bundle it for the browser — it would fail on the SDK's Node-only\n * `fromTokenFile` export chain. The SDK is Node/server-only and is only\n * reached on a real request. `typeof import(...)` is a type-only reference\n * (erased at emit) so the imported members keep full typing.\n */\n protected importBedrockRuntime(): Promise<typeof BedrockRuntime> {\n const mod = '@aws-sdk/client-bedrock-runtime'\n return import(/* @vite-ignore */ mod) as Promise<typeof BedrockRuntime>\n }\n\n /**\n * Lazily construct the `BedrockRuntimeClient`. The dynamic import keeps\n * `@aws-sdk/client-bedrock-runtime` out of the static/browser graph and\n * defers `resolveBedrockAuth` until a real request is made.\n */\n protected async getClient(): Promise<BedrockRuntimeClient> {\n if (!this.clientPromise) {\n this.clientPromise = (async () => {\n const { BedrockRuntimeClient } = await this.importBedrockRuntime()\n const region = this.clientConfig.region ?? 'us-east-1'\n const resolved = resolveBedrockAuth(\n {\n apiKey: this.clientConfig.apiKey,\n region,\n auth: this.clientConfig.auth,\n },\n 'runtime',\n )\n return new BedrockRuntimeClient(\n this.buildClientConfig(resolved, region, this.clientConfig.baseURL),\n )\n })().catch((error: unknown) => {\n // Don't cache a rejected promise — clear it so a later call can retry\n // (e.g. after a transient import failure or fixed auth config).\n this.clientPromise = undefined\n throw error\n })\n }\n return this.clientPromise\n }\n\n /**\n * Map resolved auth + endpoint to a `BedrockRuntimeClientConfig`.\n *\n * Recent `@aws-sdk/client-bedrock-runtime` exposes a first-class `token`\n * config field for Bedrock API-key bearer auth. But the client's default\n * auth-scheme order is SigV4 first, then bearer — so passing `token` alone is\n * not enough: the SDK still resolves SigV4 and throws \"Could not load\n * credentials from any providers\". Pinning `authSchemePreference` to the\n * bearer scheme makes the API key actually get used. SigV4 uses the AWS\n * credential provider chain and the default scheme order.\n */\n protected buildClientConfig(\n resolved: ResolvedBedrockAuth,\n region: string,\n endpoint: string | undefined,\n ): BedrockRuntimeClientConfig {\n if (resolved.kind === 'bearer') {\n return {\n region,\n token: { token: resolved.token },\n authSchemePreference: ['httpBearerAuth'],\n ...(endpoint ? { endpoint } : {}),\n }\n }\n return {\n region: resolved.region,\n credentials: resolved.credentials,\n ...(endpoint ? { endpoint } : {}),\n }\n }\n\n // ---------------------------------------------------------------------------\n // SDK seams (overridden in tests so no real AWS call happens)\n // ---------------------------------------------------------------------------\n\n protected async sendStream(\n input: ConverseStreamCommandInput,\n ): Promise<AsyncIterable<ConverseStreamOutput>> {\n const { ConverseStreamCommand } = await this.importBedrockRuntime()\n const client = await this.getClient()\n const res = await client.send(new ConverseStreamCommand(input))\n if (!res.stream) {\n throw new Error('Bedrock Converse: empty stream response')\n }\n return res.stream\n }\n\n protected async send(\n input: ConverseCommandInput,\n ): Promise<ConverseCommandOutput> {\n const { ConverseCommand } = await this.importBedrockRuntime()\n const client = await this.getClient()\n return client.send(new ConverseCommand(input))\n }\n\n // ---------------------------------------------------------------------------\n // Public adapter surface\n // ---------------------------------------------------------------------------\n\n async *chatStream(\n options: TextOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n try {\n options.logger.request(\n `activity=chat provider=${this.name} model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: this.name, model: this.model },\n )\n const input = this.buildInput(options)\n const stream = await this.sendStream(input)\n yield* processConverseStream(stream, () => this.generateId(), {\n threadId: options.threadId,\n parentRunId: options.parentRunId,\n model: options.model,\n })\n } catch (error: unknown) {\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.chatStream failed`,\n )\n options.logger.errors(`${this.name}.chatStream fatal`, {\n error: errorPayload,\n source: `${this.name}.chatStream`,\n })\n // Conditional `code` spread keeps the wire shape spec-compliant under\n // `exactOptionalPropertyTypes` (AG-UI's `RunErrorEvent.code` is optional).\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n error: {\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n },\n }\n }\n }\n\n /**\n * Structured output via the forced-tool strategy. Converse has no native\n * json_schema response_format, so we force a single tool whose input schema\n * is the requested output schema and read the model's `toolUse.input` back as\n * the structured result.\n */\n async structuredOutput(\n options: StructuredOutputOptions<TProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n try {\n chatOptions.logger.request(\n `activity=structuredOutput provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n const input: ConverseCommandInput = {\n ...this.buildInput(chatOptions),\n toolConfig: buildStructuredToolConfig(outputSchema),\n }\n const res = await this.send(input)\n const structured = extractStructuredToolInput(res)\n if (structured === undefined) {\n throw new Error(\n `${this.name}.structuredOutput: response contained no forced-tool output`,\n )\n }\n return {\n data: structured,\n rawText: JSON.stringify(structured),\n }\n } catch (error: unknown) {\n chatOptions.logger.errors(`${this.name}.structuredOutput fatal`, {\n error: toRunErrorPayload(error, `${this.name}.structuredOutput failed`),\n source: `${this.name}.structuredOutput`,\n })\n throw error\n }\n }\n\n /**\n * Streaming structured output. Same forced-tool strategy as\n * `structuredOutput`, but streamed: the forced tool's `toolUse.input` JSON\n * fragments are accumulated from the Converse stream and a terminal\n * `CUSTOM 'structured-output.complete'` event carries `{ object, raw }`,\n * mirroring openai-base's `structuredOutputStream` contract exactly.\n */\n async *structuredOutputStream(\n options: StructuredOutputOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const { chatOptions, outputSchema } = options\n const timestamp = Date.now()\n const runId = this.generateId()\n const threadId = chatOptions.threadId ?? this.generateId()\n const messageId = this.generateId()\n\n let hasEmittedRunStarted = false\n let hasEmittedTextMessageStart = false\n let accumulatedRaw = ''\n let finishReason: 'stop' | 'length' | 'content_filter' = 'stop'\n\n try {\n chatOptions.logger.request(\n `activity=structuredOutputStream provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n const input: ConverseStreamCommandInput = {\n ...this.buildInput(chatOptions),\n toolConfig: buildStructuredToolConfig(outputSchema),\n }\n const stream = await this.sendStream(input)\n\n // The forced tool streams its `input` as partial-JSON fragments inside\n // `contentBlockDelta.delta.toolUse.input`. We surface them as\n // TEXT_MESSAGE_CONTENT deltas (raw JSON text), matching openai-base which\n // carries the structured JSON as text deltas.\n for await (const ev of stream) {\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp,\n parentRunId: chatOptions.parentRunId,\n }\n }\n\n // Surface in-band server/throttle/validation errors instead of\n // letting them fall through and masquerade as an empty response.\n throwIfConverseStreamError(ev)\n\n if ('contentBlockDelta' in ev) {\n const delta = ev.contentBlockDelta?.delta\n const fragment =\n delta && 'toolUse' in delta ? delta.toolUse?.input : undefined\n if (fragment !== undefined) {\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n role: 'assistant',\n model: chatOptions.model,\n timestamp,\n }\n }\n accumulatedRaw += fragment\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n delta: fragment,\n content: accumulatedRaw,\n model: chatOptions.model,\n timestamp,\n }\n }\n continue\n }\n\n if ('messageStop' in ev) {\n const stopReason = ev.messageStop?.stopReason\n // The forced structured-output tool produces stopReason 'tool_use' on\n // success, but that's an implementation detail — a cleanly-completed\n // structured run reports 'stop', matching openai-base's contract.\n finishReason =\n stopReason === 'max_tokens'\n ? 'length'\n : stopReason === 'content_filtered'\n ? 'content_filter'\n : 'stop'\n continue\n }\n }\n\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp,\n parentRunId: chatOptions.parentRunId,\n }\n }\n\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model: chatOptions.model,\n timestamp,\n }\n }\n\n if (accumulatedRaw.length === 0) {\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: chatOptions.model,\n timestamp,\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n error: {\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n },\n }\n return\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(accumulatedRaw)\n } catch {\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: chatOptions.model,\n timestamp,\n message: `Failed to parse structured output as JSON. Content: ${accumulatedRaw.slice(0, 200)}${accumulatedRaw.length > 200 ? '...' : ''}`,\n code: 'parse-error',\n error: {\n message: 'Failed to parse structured output as JSON',\n code: 'parse-error',\n },\n }\n return\n }\n\n yield {\n type: EventType.CUSTOM,\n name: 'structured-output.complete',\n value: {\n object: parsed,\n raw: accumulatedRaw,\n },\n model: chatOptions.model,\n timestamp,\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp,\n finishReason,\n }\n } catch (error: unknown) {\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp,\n parentRunId: chatOptions.parentRunId,\n }\n }\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.structuredOutputStream failed`,\n )\n chatOptions.logger.errors(`${this.name}.structuredOutputStream fatal`, {\n error: errorPayload,\n source: `${this.name}.structuredOutputStream`,\n })\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: chatOptions.model,\n timestamp,\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n error: {\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n },\n }\n }\n }\n\n /**\n * Converse sends `tools` and a forced structured-output tool via two separate\n * mechanisms, never together. Declaring `false` makes the engine run the\n * agent loop without `outputSchema` and finalize via `structuredOutput` /\n * `structuredOutputStream`.\n */\n supportsCombinedToolsAndSchema(): boolean {\n return false\n }\n\n // ---------------------------------------------------------------------------\n // Request construction\n // ---------------------------------------------------------------------------\n\n /**\n * Translate `TextOptions` into a `ConverseCommandInput`. Shared by chatStream,\n * structuredOutput, and structuredOutputStream (the latter two override\n * `toolConfig` with the forced structured tool afterwards).\n */\n protected buildInput(\n options: TextOptions<TProviderOptions>,\n ): ConverseCommandInput {\n const { system, messages } = toConverseMessages(\n options.messages,\n options.systemPrompts,\n )\n\n const toolConfig = options.tools\n ? toToolConfig(convertTools(options.tools), 'auto')\n : undefined\n\n // Sampling options live on `modelOptions` (typed as the narrowed\n // `BedrockConverseProviderOptions`, which surfaces the OpenAI Chat\n // Completions field names); translate them into Converse's `inferenceConfig`,\n // which uses AWS-native camelCase keys.\n const modelOptions = options.modelOptions\n const temperature = modelOptions?.temperature\n const topP = modelOptions?.top_p\n const maxTokens = modelOptions?.max_completion_tokens\n const stop = modelOptions?.stop\n const stopSequences =\n stop == null ? undefined : Array.isArray(stop) ? stop : [stop]\n\n const inferenceConfig =\n temperature != null ||\n topP != null ||\n maxTokens != null ||\n stopSequences != null\n ? {\n ...(temperature != null && { temperature }),\n ...(topP != null && { topP }),\n ...(maxTokens != null && { maxTokens }),\n ...(stopSequences != null && { stopSequences }),\n }\n : undefined\n\n return {\n modelId: this.model,\n messages,\n ...(system.length > 0 && { system }),\n ...(toolConfig && { toolConfig }),\n ...(inferenceConfig && { inferenceConfig }),\n }\n }\n}\n\n/**\n * Convert TanStack `Tool[]` to the Converse tool-converter input shape. Reuses\n * the SAME `convertSchemaToJsonSchema` the other adapters use so the Converse\n * tool input schemas match what every other provider sends.\n */\nfunction convertTools(tools: Array<Tool>): Array<ConverseToolInput> {\n return tools.map((tool) => {\n const inputSchema: JSONSchema = convertSchemaToJsonSchema(\n tool.inputSchema,\n ) ?? { type: 'object', properties: {}, required: [] }\n return {\n name: tool.name,\n description: tool.description,\n inputSchema,\n }\n })\n}\n\n/**\n * Find the forced structured-output tool's `input` in a non-streaming Converse\n * response. SDK-boundary narrowing only — `ConverseOutput` is a tagged union\n * (`{ message }`) and a tool-use block is `{ toolUse: { input } }`.\n */\nfunction extractStructuredToolInput(\n res: ConverseCommandOutput,\n): unknown | undefined {\n const message =\n res.output && 'message' in res.output ? res.output.message : undefined\n const content: Array<ContentBlock> = message?.content ?? []\n for (const block of content) {\n if ('toolUse' in block && block.toolUse) {\n // Only accept the forced structured tool (an unnamed block is allowed,\n // since the forced tool is the only one configured). A differently-named\n // tool-use block is a hallucinated/leftover call whose arbitrary input\n // must not be returned as the validated result — leave it to the caller's\n // `throw` so the failure is accurate instead of silently wrong.\n if (\n block.toolUse.name === STRUCTURED_TOOL_NAME ||\n block.toolUse.name === undefined\n ) {\n return block.toolUse.input\n }\n }\n }\n return undefined\n}\n\n/** Converse adapter with an explicit API key (low-level; mirrors createBedrockChat). */\nexport function createBedrockConverse<TModel extends BedrockConverseModels>(\n model: TModel,\n apiKey: string,\n config?: Omit<BedrockConverseConfig, 'apiKey'>,\n): BedrockConverseTextAdapter<TModel> {\n return new BedrockConverseTextAdapter({ ...config, apiKey }, model)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"converse-text.js","names":[],"sources":["../../../src/adapters/converse-text.ts"],"sourcesContent":["import { EventType, convertSchemaToJsonSchema } from '@tanstack/ai'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { resolveBedrockAuth } from '../utils/auth'\nimport { toConverseMessages } from '../converse/message-converter'\nimport { toToolConfig } from '../converse/tool-converter'\nimport {\n processConverseStream,\n throwIfConverseStreamError,\n} from '../converse/stream-processor'\nimport {\n STRUCTURED_TOOL_NAME,\n buildStructuredToolConfig,\n} from '../converse/structured-output'\nimport type { ResolvedBedrockAuth } from '../utils/auth'\nimport type { ConverseToolInput } from '../converse/tool-converter'\nimport type * as BedrockRuntime from '@aws-sdk/client-bedrock-runtime'\nimport type {\n BedrockRuntimeClient,\n BedrockRuntimeClientConfig,\n ContentBlock,\n ConverseCommandInput,\n ConverseCommandOutput,\n ConverseStreamCommandInput,\n ConverseStreamOutput,\n} from '@aws-sdk/client-bedrock-runtime'\nimport type {\n JSONSchema,\n Modality,\n StreamChunk,\n TextOptions,\n Tool,\n} from '@tanstack/ai'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type { BedrockClientConfig } from '../utils/client'\nimport type { BedrockMessageMetadataByModality } from '../message-types'\nimport type {\n BedrockConverseModels,\n ResolveConverseProviderOptions,\n ResolveInputModalities,\n} from '../model-meta'\n\n/** Config for the Converse adapter — same client config as the chat adapter. */\nexport interface BedrockConverseConfig extends BedrockClientConfig {}\n\n/**\n * Bedrock Converse text adapter. Wires the Converse translation modules (message\n * converter, tool converter, stream processor, structured-output forced-tool\n * builder) onto `@tanstack/ai`'s `BaseTextAdapter` and the\n * `@aws-sdk/client-bedrock-runtime` `BedrockRuntimeClient`.\n *\n * The success-path AG-UI lifecycle (`RUN_STARTED`..`RUN_FINISHED`) is owned by\n * `processConverseStream`; this adapter only owns the catch/`RUN_ERROR` path,\n * mirroring openai-base's `chatStream`.\n *\n * The actual SDK calls live behind two protected seams (`sendStream` / `send`)\n * so tests can subclass and inject canned Converse SDK shapes without a real\n * AWS request.\n */\nexport class BedrockConverseTextAdapter<\n TModel extends BedrockConverseModels,\n // Constraint mirrors the chat adapter (text.ts): the base parameterises\n // `TProviderOptions extends Record<string, any>`, and our default\n // `ResolveConverseProviderOptions<TModel>` resolves to an interface lacking an\n // implicit index signature — which `Record<string, unknown>` would reject but\n // `Record<string, any>` accepts. Confined to the generic constraint (the\n // established adapter pattern) — no value `as` cast is introduced.\n TProviderOptions extends Record<string, any> =\n ResolveConverseProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n BedrockMessageMetadataByModality\n> {\n override readonly kind = 'text' as const\n override readonly name = 'bedrock-converse' as const\n private clientPromise?: Promise<BedrockRuntimeClient>\n private readonly clientConfig: BedrockConverseConfig\n\n constructor(config: BedrockConverseConfig, model: TModel) {\n super({}, model)\n // Defer client construction and auth resolution: the AWS SDK is Node/\n // server-only, so we must not pull it into the static graph here. The\n // client (and its dynamic import) is built lazily on first SDK call.\n this.clientConfig = config\n }\n\n /**\n * Dynamically import `@aws-sdk/client-bedrock-runtime`. The specifier is held\n * in a variable (not a string literal) so bundler dep scanners (e.g. Vite/\n * esbuild optimizeDeps) cannot statically discover the AWS SDK and try to\n * pre-bundle it for the browser — it would fail on the SDK's Node-only\n * `fromTokenFile` export chain. The SDK is Node/server-only and is only\n * reached on a real request. `typeof import(...)` is a type-only reference\n * (erased at emit) so the imported members keep full typing.\n */\n protected importBedrockRuntime(): Promise<typeof BedrockRuntime> {\n const mod = '@aws-sdk/client-bedrock-runtime'\n return import(/* @vite-ignore */ mod) as Promise<typeof BedrockRuntime>\n }\n\n /**\n * Lazily construct the `BedrockRuntimeClient`. The dynamic import keeps\n * `@aws-sdk/client-bedrock-runtime` out of the static/browser graph and\n * defers `resolveBedrockAuth` until a real request is made.\n */\n protected async getClient(): Promise<BedrockRuntimeClient> {\n if (!this.clientPromise) {\n this.clientPromise = (async () => {\n const { BedrockRuntimeClient } = await this.importBedrockRuntime()\n const region = this.clientConfig.region ?? 'us-east-1'\n const resolved = resolveBedrockAuth(\n {\n apiKey: this.clientConfig.apiKey,\n region,\n auth: this.clientConfig.auth,\n },\n 'runtime',\n )\n return new BedrockRuntimeClient(\n this.buildClientConfig(resolved, region, this.clientConfig.baseURL),\n )\n })().catch((error: unknown) => {\n // Don't cache a rejected promise — clear it so a later call can retry\n // (e.g. after a transient import failure or fixed auth config).\n this.clientPromise = undefined\n throw error\n })\n }\n return this.clientPromise\n }\n\n /**\n * Map resolved auth + endpoint to a `BedrockRuntimeClientConfig`.\n *\n * Recent `@aws-sdk/client-bedrock-runtime` exposes a first-class `token`\n * config field for Bedrock API-key bearer auth. But the client's default\n * auth-scheme order is SigV4 first, then bearer — so passing `token` alone is\n * not enough: the SDK still resolves SigV4 and throws \"Could not load\n * credentials from any providers\". Pinning `authSchemePreference` to the\n * bearer scheme makes the API key actually get used. SigV4 uses the AWS\n * credential provider chain and the default scheme order.\n */\n protected buildClientConfig(\n resolved: ResolvedBedrockAuth,\n region: string,\n endpoint: string | undefined,\n ): BedrockRuntimeClientConfig {\n if (resolved.kind === 'bearer') {\n return {\n region,\n token: { token: resolved.token },\n authSchemePreference: ['httpBearerAuth'],\n ...(endpoint ? { endpoint } : {}),\n }\n }\n return {\n region: resolved.region,\n credentials: resolved.credentials,\n ...(endpoint ? { endpoint } : {}),\n }\n }\n\n // ---------------------------------------------------------------------------\n // SDK seams (overridden in tests so no real AWS call happens)\n // ---------------------------------------------------------------------------\n\n protected async sendStream(\n input: ConverseStreamCommandInput,\n ): Promise<AsyncIterable<ConverseStreamOutput>> {\n const { ConverseStreamCommand } = await this.importBedrockRuntime()\n const client = await this.getClient()\n const res = await client.send(new ConverseStreamCommand(input))\n if (!res.stream) {\n throw new Error('Bedrock Converse: empty stream response')\n }\n return res.stream\n }\n\n protected async send(\n input: ConverseCommandInput,\n ): Promise<ConverseCommandOutput> {\n const { ConverseCommand } = await this.importBedrockRuntime()\n const client = await this.getClient()\n return client.send(new ConverseCommand(input))\n }\n\n // ---------------------------------------------------------------------------\n // Public adapter surface\n // ---------------------------------------------------------------------------\n\n async *chatStream(\n options: TextOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n try {\n options.logger.request(\n `activity=chat provider=${this.name} model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: this.name, model: this.model },\n )\n const input = this.buildInput(options)\n const stream = await this.sendStream(input)\n yield* processConverseStream(stream, () => this.generateId(), {\n threadId: options.threadId,\n parentRunId: options.parentRunId,\n model: options.model,\n })\n } catch (error: unknown) {\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.chatStream failed`,\n )\n options.logger.errors(`${this.name}.chatStream fatal`, {\n error: errorPayload,\n source: `${this.name}.chatStream`,\n })\n // Conditional `code` spread keeps the wire shape spec-compliant under\n // `exactOptionalPropertyTypes` (AG-UI's `RunErrorEvent.code` is optional).\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n error: {\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n },\n }\n }\n }\n\n /**\n * Structured output via the forced-tool strategy. Converse has no native\n * json_schema response_format, so we force a single tool whose input schema\n * is the requested output schema and read the model's `toolUse.input` back as\n * the structured result.\n */\n async structuredOutput(\n options: StructuredOutputOptions<TProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n try {\n chatOptions.logger.request(\n `activity=structuredOutput provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n const input: ConverseCommandInput = {\n ...this.buildInput(chatOptions),\n toolConfig: buildStructuredToolConfig(outputSchema),\n }\n const res = await this.send(input)\n const structured = extractStructuredToolInput(res)\n if (structured === undefined) {\n throw new Error(\n `${this.name}.structuredOutput: response contained no forced-tool output`,\n )\n }\n const usage = res.usage\n return {\n data: structured,\n rawText: JSON.stringify(structured),\n ...(usage && {\n usage: {\n promptTokens: usage.inputTokens ?? 0,\n completionTokens: usage.outputTokens ?? 0,\n totalTokens: usage.totalTokens ?? 0,\n },\n }),\n }\n } catch (error: unknown) {\n chatOptions.logger.errors(`${this.name}.structuredOutput fatal`, {\n error: toRunErrorPayload(error, `${this.name}.structuredOutput failed`),\n source: `${this.name}.structuredOutput`,\n })\n throw error\n }\n }\n\n /**\n * Streaming structured output. Same forced-tool strategy as\n * `structuredOutput`, but streamed: the forced tool's `toolUse.input` JSON\n * fragments are accumulated from the Converse stream and a terminal\n * `CUSTOM 'structured-output.complete'` event carries `{ object, raw }`,\n * mirroring openai-base's `structuredOutputStream` contract exactly.\n */\n async *structuredOutputStream(\n options: StructuredOutputOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const { chatOptions, outputSchema } = options\n const timestamp = Date.now()\n const runId = this.generateId()\n const threadId = chatOptions.threadId ?? this.generateId()\n const messageId = this.generateId()\n\n let hasEmittedRunStarted = false\n let hasEmittedTextMessageStart = false\n let accumulatedRaw = ''\n let finishReason: 'stop' | 'length' | 'content_filter' = 'stop'\n\n try {\n chatOptions.logger.request(\n `activity=structuredOutputStream provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n const input: ConverseStreamCommandInput = {\n ...this.buildInput(chatOptions),\n toolConfig: buildStructuredToolConfig(outputSchema),\n }\n const stream = await this.sendStream(input)\n\n // The forced tool streams its `input` as partial-JSON fragments inside\n // `contentBlockDelta.delta.toolUse.input`. We surface them as\n // TEXT_MESSAGE_CONTENT deltas (raw JSON text), matching openai-base which\n // carries the structured JSON as text deltas.\n for await (const ev of stream) {\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp,\n parentRunId: chatOptions.parentRunId,\n }\n }\n\n // Surface in-band server/throttle/validation errors instead of\n // letting them fall through and masquerade as an empty response.\n throwIfConverseStreamError(ev)\n\n if ('contentBlockDelta' in ev) {\n const delta = ev.contentBlockDelta?.delta\n const fragment =\n delta && 'toolUse' in delta ? delta.toolUse?.input : undefined\n if (fragment !== undefined) {\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n role: 'assistant',\n model: chatOptions.model,\n timestamp,\n }\n }\n accumulatedRaw += fragment\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n delta: fragment,\n content: accumulatedRaw,\n model: chatOptions.model,\n timestamp,\n }\n }\n continue\n }\n\n if ('messageStop' in ev) {\n const stopReason = ev.messageStop?.stopReason\n // The forced structured-output tool produces stopReason 'tool_use' on\n // success, but that's an implementation detail — a cleanly-completed\n // structured run reports 'stop', matching openai-base's contract.\n finishReason =\n stopReason === 'max_tokens'\n ? 'length'\n : stopReason === 'content_filtered'\n ? 'content_filter'\n : 'stop'\n continue\n }\n }\n\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp,\n parentRunId: chatOptions.parentRunId,\n }\n }\n\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model: chatOptions.model,\n timestamp,\n }\n }\n\n if (accumulatedRaw.length === 0) {\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: chatOptions.model,\n timestamp,\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n error: {\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n },\n }\n return\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(accumulatedRaw)\n } catch {\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: chatOptions.model,\n timestamp,\n message: `Failed to parse structured output as JSON. Content: ${accumulatedRaw.slice(0, 200)}${accumulatedRaw.length > 200 ? '...' : ''}`,\n code: 'parse-error',\n error: {\n message: 'Failed to parse structured output as JSON',\n code: 'parse-error',\n },\n }\n return\n }\n\n yield {\n type: EventType.CUSTOM,\n name: 'structured-output.complete',\n value: {\n object: parsed,\n raw: accumulatedRaw,\n },\n model: chatOptions.model,\n timestamp,\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp,\n finishReason,\n }\n } catch (error: unknown) {\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model: chatOptions.model,\n timestamp,\n parentRunId: chatOptions.parentRunId,\n }\n }\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.structuredOutputStream failed`,\n )\n chatOptions.logger.errors(`${this.name}.structuredOutputStream fatal`, {\n error: errorPayload,\n source: `${this.name}.structuredOutputStream`,\n })\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: chatOptions.model,\n timestamp,\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n error: {\n message: errorPayload.message,\n ...(errorPayload.code !== undefined && { code: errorPayload.code }),\n },\n }\n }\n }\n\n /**\n * Converse sends `tools` and a forced structured-output tool via two separate\n * mechanisms, never together. Declaring `false` makes the engine run the\n * agent loop without `outputSchema` and finalize via `structuredOutput` /\n * `structuredOutputStream`.\n */\n supportsCombinedToolsAndSchema(): boolean {\n return false\n }\n\n // ---------------------------------------------------------------------------\n // Request construction\n // ---------------------------------------------------------------------------\n\n /**\n * Translate `TextOptions` into a `ConverseCommandInput`. Shared by chatStream,\n * structuredOutput, and structuredOutputStream (the latter two override\n * `toolConfig` with the forced structured tool afterwards).\n */\n protected buildInput(\n options: TextOptions<TProviderOptions>,\n ): ConverseCommandInput {\n const { system, messages } = toConverseMessages(\n options.messages,\n options.systemPrompts,\n )\n\n const toolConfig = options.tools\n ? toToolConfig(convertTools(options.tools), 'auto')\n : undefined\n\n // Sampling options live on `modelOptions` (typed as the narrowed\n // `BedrockConverseProviderOptions`, which surfaces the OpenAI Chat\n // Completions field names); translate them into Converse's `inferenceConfig`,\n // which uses AWS-native camelCase keys.\n const modelOptions = options.modelOptions\n const temperature = modelOptions?.temperature\n const topP = modelOptions?.top_p\n const maxTokens = modelOptions?.max_completion_tokens\n const stop = modelOptions?.stop\n const stopSequences =\n stop == null ? undefined : Array.isArray(stop) ? stop : [stop]\n\n const inferenceConfig =\n temperature != null ||\n topP != null ||\n maxTokens != null ||\n stopSequences != null\n ? {\n ...(temperature != null && { temperature }),\n ...(topP != null && { topP }),\n ...(maxTokens != null && { maxTokens }),\n ...(stopSequences != null && { stopSequences }),\n }\n : undefined\n\n return {\n modelId: this.model,\n messages,\n ...(system.length > 0 && { system }),\n ...(toolConfig && { toolConfig }),\n ...(inferenceConfig && { inferenceConfig }),\n }\n }\n}\n\n/**\n * Convert TanStack `Tool[]` to the Converse tool-converter input shape. Reuses\n * the SAME `convertSchemaToJsonSchema` the other adapters use so the Converse\n * tool input schemas match what every other provider sends.\n */\nfunction convertTools(tools: Array<Tool>): Array<ConverseToolInput> {\n return tools.map((tool) => {\n const inputSchema: JSONSchema = convertSchemaToJsonSchema(\n tool.inputSchema,\n ) ?? { type: 'object', properties: {}, required: [] }\n return {\n name: tool.name,\n description: tool.description,\n inputSchema,\n }\n })\n}\n\n/**\n * Find the forced structured-output tool's `input` in a non-streaming Converse\n * response. SDK-boundary narrowing only — `ConverseOutput` is a tagged union\n * (`{ message }`) and a tool-use block is `{ toolUse: { input } }`.\n */\nfunction extractStructuredToolInput(\n res: ConverseCommandOutput,\n): unknown | undefined {\n const message =\n res.output && 'message' in res.output ? res.output.message : undefined\n const content: Array<ContentBlock> = message?.content ?? []\n for (const block of content) {\n if ('toolUse' in block && block.toolUse) {\n // Only accept the forced structured tool (an unnamed block is allowed,\n // since the forced tool is the only one configured). A differently-named\n // tool-use block is a hallucinated/leftover call whose arbitrary input\n // must not be returned as the validated result — leave it to the caller's\n // `throw` so the failure is accurate instead of silently wrong.\n if (\n block.toolUse.name === STRUCTURED_TOOL_NAME ||\n block.toolUse.name === undefined\n ) {\n return block.toolUse.input\n }\n }\n }\n return undefined\n}\n\n/** Converse adapter with an explicit API key (low-level; mirrors createBedrockChat). */\nexport function createBedrockConverse<TModel extends BedrockConverseModels>(\n model: TModel,\n apiKey: string,\n config?: Omit<BedrockConverseConfig, 'apiKey'>,\n): BedrockConverseTextAdapter<TModel> {\n return new BedrockConverseTextAdapter({ ...config, apiKey }, model)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { BaseEmbeddingAdapter } from '@tanstack/ai/adapters';
|
|
2
|
+
import { BedrockRuntimeClient, BedrockRuntimeClientConfig } from '@aws-sdk/client-bedrock-runtime';
|
|
3
|
+
import { EmbeddingOptions, EmbeddingResult } from '@tanstack/ai';
|
|
4
|
+
import { ResolvedBedrockAuth } from '../utils/auth.js';
|
|
5
|
+
import { BedrockClientConfig } from '../utils/client.js';
|
|
6
|
+
import { BedrockEmbeddingModel, BedrockEmbeddingModelInputModalitiesByName, BedrockEmbeddingModelProviderOptionsByName, ResolveEmbeddingProviderOptions } from '../model-meta.js';
|
|
7
|
+
import type * as BedrockRuntime from '@aws-sdk/client-bedrock-runtime';
|
|
8
|
+
/**
|
|
9
|
+
* Config for the Bedrock embedding adapter — the same auth surface as the
|
|
10
|
+
* other Bedrock adapters (apiKey → env → SigV4 via `resolveBedrockAuth`),
|
|
11
|
+
* minus the OpenAI-compat client options that don't apply to `InvokeModel`.
|
|
12
|
+
*/
|
|
13
|
+
export interface BedrockEmbeddingConfig extends Pick<BedrockClientConfig, 'apiKey' | 'region' | 'auth' | 'baseURL'> {
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Bedrock Embedding Adapter
|
|
17
|
+
*
|
|
18
|
+
* Tree-shakeable adapter for embeddings served through Bedrock's native
|
|
19
|
+
* `InvokeModel` API (embedding models have no Converse surface). Each model
|
|
20
|
+
* family has its own JSON body dialect:
|
|
21
|
+
*
|
|
22
|
+
* - `amazon.titan-embed-text-v2:0` — text-only, ONE text per call; the batch
|
|
23
|
+
* is fanned out with a small concurrency cap and per-call
|
|
24
|
+
* `inputTextTokenCount`s are summed into usage.
|
|
25
|
+
* - `amazon.titan-embed-image-v1` — MULTIMODAL: text, image, or a fused
|
|
26
|
+
* text+image item embedded into a single vector; one item per call.
|
|
27
|
+
* - `cohere.embed-english-v3` / `cohere.embed-multilingual-v3` — text-only,
|
|
28
|
+
* batched natively (chunked at 96 texts per call). `inputType` is required.
|
|
29
|
+
*
|
|
30
|
+
* The SDK call lives behind a protected `invokeModel` seam so tests can
|
|
31
|
+
* subclass and inject canned response bodies without a real AWS request, and
|
|
32
|
+
* the AWS SDK itself is imported lazily (it's Node/server-only).
|
|
33
|
+
*/
|
|
34
|
+
export declare class BedrockEmbeddingAdapter<TModel extends BedrockEmbeddingModel, TProviderOptions extends Record<string, any> = ResolveEmbeddingProviderOptions<TModel>> extends BaseEmbeddingAdapter<TModel, TProviderOptions, BedrockEmbeddingModelProviderOptionsByName, BedrockEmbeddingModelInputModalitiesByName> {
|
|
35
|
+
readonly name: "bedrock";
|
|
36
|
+
private clientPromise?;
|
|
37
|
+
private readonly clientConfig;
|
|
38
|
+
constructor(config: BedrockEmbeddingConfig, model: TModel);
|
|
39
|
+
/**
|
|
40
|
+
* Dynamically import `@aws-sdk/client-bedrock-runtime`. The specifier is
|
|
41
|
+
* held in a variable (not a string literal) so bundler dep scanners cannot
|
|
42
|
+
* statically discover the AWS SDK and try to pre-bundle it for the browser.
|
|
43
|
+
* Same pattern as the Converse text adapter.
|
|
44
|
+
*/
|
|
45
|
+
protected importBedrockRuntime(): Promise<typeof BedrockRuntime>;
|
|
46
|
+
/**
|
|
47
|
+
* Lazily construct the `BedrockRuntimeClient`, deferring
|
|
48
|
+
* `resolveBedrockAuth` until a real request is made.
|
|
49
|
+
*/
|
|
50
|
+
protected getClient(): Promise<BedrockRuntimeClient>;
|
|
51
|
+
/**
|
|
52
|
+
* Map resolved auth + endpoint to a `BedrockRuntimeClientConfig`. Bearer
|
|
53
|
+
* auth needs `authSchemePreference` pinned or the SDK still tries SigV4
|
|
54
|
+
* first — same reasoning as the Converse text adapter.
|
|
55
|
+
*/
|
|
56
|
+
protected buildClientConfig(resolved: ResolvedBedrockAuth, region: string, endpoint: string | undefined): BedrockRuntimeClientConfig;
|
|
57
|
+
/** Send one InvokeModel call and parse its JSON response body. */
|
|
58
|
+
protected invokeModel(modelId: string, body: Record<string, unknown>): Promise<unknown>;
|
|
59
|
+
createEmbeddings(options: EmbeddingOptions<TProviderOptions>): Promise<EmbeddingResult>;
|
|
60
|
+
/**
|
|
61
|
+
* `amazon.titan-embed-text-v2:0` — one text per InvokeModel call, fanned
|
|
62
|
+
* out with a concurrency cap; result order matches input order and per-call
|
|
63
|
+
* `inputTextTokenCount`s are summed into usage.
|
|
64
|
+
*/
|
|
65
|
+
private embedTitanText;
|
|
66
|
+
/**
|
|
67
|
+
* `amazon.titan-embed-image-v1` (Titan Multimodal) — one item per
|
|
68
|
+
* InvokeModel call. An item may carry text, an image, or both (a fused
|
|
69
|
+
* item embedded into a single vector). Titan accepts at most one image per
|
|
70
|
+
* request and never fetches remote URLs.
|
|
71
|
+
*/
|
|
72
|
+
private embedTitanImage;
|
|
73
|
+
/**
|
|
74
|
+
* `cohere.embed-*-v3` — natively batched (chunked at 96 texts per call,
|
|
75
|
+
* order preserved across chunks). `inputType` is required by the Cohere
|
|
76
|
+
* API; output dimensionality is fixed, so `dimensions` is rejected.
|
|
77
|
+
*/
|
|
78
|
+
private embedCohere;
|
|
79
|
+
/** Assemble an EmbeddingResult from per-item Titan responses. */
|
|
80
|
+
private toTitanResult;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Creates a Bedrock embedding adapter with an explicit API key (bearer).
|
|
84
|
+
* Type resolution happens here at the call site.
|
|
85
|
+
*
|
|
86
|
+
* @param model - The model name (e.g., 'amazon.titan-embed-text-v2:0')
|
|
87
|
+
* @param apiKey - Your Bedrock API key
|
|
88
|
+
* @param config - Optional additional configuration (region, baseURL, ...)
|
|
89
|
+
* @returns Configured Bedrock embedding adapter instance with resolved types
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* const adapter = createBedrockEmbedding(
|
|
94
|
+
* 'amazon.titan-embed-text-v2:0',
|
|
95
|
+
* 'bedrock-api-key',
|
|
96
|
+
* );
|
|
97
|
+
*
|
|
98
|
+
* const result = await embed({
|
|
99
|
+
* adapter,
|
|
100
|
+
* input: 'a red guitar',
|
|
101
|
+
* });
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export declare function createBedrockEmbedding<TModel extends BedrockEmbeddingModel>(model: TModel, apiKey: string, config?: Omit<BedrockEmbeddingConfig, 'apiKey'>): BedrockEmbeddingAdapter<TModel>;
|
|
105
|
+
/**
|
|
106
|
+
* Creates a Bedrock embedding adapter using the ambient auth cascade:
|
|
107
|
+
* `config.apiKey` → `BEDROCK_API_KEY` → `AWS_BEARER_TOKEN_BEDROCK` → SigV4
|
|
108
|
+
* (AWS credential provider chain). Auth resolves lazily on the first
|
|
109
|
+
* request, so `auth: 'sigv4'` never requires an API key.
|
|
110
|
+
*
|
|
111
|
+
* @param model - The model name (e.g., 'cohere.embed-english-v3')
|
|
112
|
+
* @param config - Optional configuration (region, auth, baseURL, ...)
|
|
113
|
+
* @returns Configured Bedrock embedding adapter instance with resolved types
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* const adapter = bedrockEmbedding('cohere.embed-english-v3');
|
|
118
|
+
*
|
|
119
|
+
* const result = await embed({
|
|
120
|
+
* adapter,
|
|
121
|
+
* input: ['a red guitar', 'a blue drum kit'],
|
|
122
|
+
* modelOptions: { inputType: 'search_document' },
|
|
123
|
+
* });
|
|
124
|
+
*
|
|
125
|
+
* console.log(result.embeddings[0].vector)
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export declare function bedrockEmbedding<TModel extends BedrockEmbeddingModel>(model: TModel, config?: BedrockEmbeddingConfig): BedrockEmbeddingAdapter<TModel>;
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { resolveBedrockAuth } from "../utils/auth.js";
|
|
2
|
+
import { BEDROCK_EMBEDDING_MODELS } from "../model-meta.js";
|
|
3
|
+
import { requireTextOnlyEmbeddingInput, resolveEmbeddingInput } from "@tanstack/ai";
|
|
4
|
+
import { BaseEmbeddingAdapter } from "@tanstack/ai/adapters";
|
|
5
|
+
import { toRunErrorPayload } from "@tanstack/ai/adapter-internals";
|
|
6
|
+
//#region src/adapters/embedding.ts
|
|
7
|
+
/** InvokeModel calls issued concurrently during a per-item fan-out. */
|
|
8
|
+
var MAX_CONCURRENT_INVOCATIONS = 5;
|
|
9
|
+
/** Valid `dimensions` for `amazon.titan-embed-text-v2:0`. */
|
|
10
|
+
var TITAN_TEXT_DIMENSIONS = [
|
|
11
|
+
256,
|
|
12
|
+
512,
|
|
13
|
+
1024
|
|
14
|
+
];
|
|
15
|
+
/** Valid `dimensions` (outputEmbeddingLength) for `amazon.titan-embed-image-v1`. */
|
|
16
|
+
var TITAN_IMAGE_DIMENSIONS = [
|
|
17
|
+
256,
|
|
18
|
+
384,
|
|
19
|
+
1024
|
|
20
|
+
];
|
|
21
|
+
var TITAN_IMAGE_DEFAULT_DIMENSIONS = 1024;
|
|
22
|
+
/** Cohere embed accepts at most 96 texts per InvokeModel call. */
|
|
23
|
+
var COHERE_MAX_BATCH_SIZE = 96;
|
|
24
|
+
/**
|
|
25
|
+
* Bedrock Embedding Adapter
|
|
26
|
+
*
|
|
27
|
+
* Tree-shakeable adapter for embeddings served through Bedrock's native
|
|
28
|
+
* `InvokeModel` API (embedding models have no Converse surface). Each model
|
|
29
|
+
* family has its own JSON body dialect:
|
|
30
|
+
*
|
|
31
|
+
* - `amazon.titan-embed-text-v2:0` — text-only, ONE text per call; the batch
|
|
32
|
+
* is fanned out with a small concurrency cap and per-call
|
|
33
|
+
* `inputTextTokenCount`s are summed into usage.
|
|
34
|
+
* - `amazon.titan-embed-image-v1` — MULTIMODAL: text, image, or a fused
|
|
35
|
+
* text+image item embedded into a single vector; one item per call.
|
|
36
|
+
* - `cohere.embed-english-v3` / `cohere.embed-multilingual-v3` — text-only,
|
|
37
|
+
* batched natively (chunked at 96 texts per call). `inputType` is required.
|
|
38
|
+
*
|
|
39
|
+
* The SDK call lives behind a protected `invokeModel` seam so tests can
|
|
40
|
+
* subclass and inject canned response bodies without a real AWS request, and
|
|
41
|
+
* the AWS SDK itself is imported lazily (it's Node/server-only).
|
|
42
|
+
*/
|
|
43
|
+
var BedrockEmbeddingAdapter = class extends BaseEmbeddingAdapter {
|
|
44
|
+
name = "bedrock";
|
|
45
|
+
clientPromise;
|
|
46
|
+
clientConfig;
|
|
47
|
+
constructor(config, model) {
|
|
48
|
+
super(model, {});
|
|
49
|
+
this.clientConfig = config;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Dynamically import `@aws-sdk/client-bedrock-runtime`. The specifier is
|
|
53
|
+
* held in a variable (not a string literal) so bundler dep scanners cannot
|
|
54
|
+
* statically discover the AWS SDK and try to pre-bundle it for the browser.
|
|
55
|
+
* Same pattern as the Converse text adapter.
|
|
56
|
+
*/
|
|
57
|
+
importBedrockRuntime() {
|
|
58
|
+
return import(
|
|
59
|
+
/* @vite-ignore */
|
|
60
|
+
"@aws-sdk/client-bedrock-runtime"
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Lazily construct the `BedrockRuntimeClient`, deferring
|
|
65
|
+
* `resolveBedrockAuth` until a real request is made.
|
|
66
|
+
*/
|
|
67
|
+
async getClient() {
|
|
68
|
+
if (!this.clientPromise) this.clientPromise = (async () => {
|
|
69
|
+
const { BedrockRuntimeClient } = await this.importBedrockRuntime();
|
|
70
|
+
const region = this.clientConfig.region ?? "us-east-1";
|
|
71
|
+
const resolved = resolveBedrockAuth({
|
|
72
|
+
apiKey: this.clientConfig.apiKey,
|
|
73
|
+
region,
|
|
74
|
+
auth: this.clientConfig.auth
|
|
75
|
+
}, "runtime");
|
|
76
|
+
return new BedrockRuntimeClient(this.buildClientConfig(resolved, region, this.clientConfig.baseURL));
|
|
77
|
+
})().catch((error) => {
|
|
78
|
+
this.clientPromise = void 0;
|
|
79
|
+
throw error;
|
|
80
|
+
});
|
|
81
|
+
return this.clientPromise;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Map resolved auth + endpoint to a `BedrockRuntimeClientConfig`. Bearer
|
|
85
|
+
* auth needs `authSchemePreference` pinned or the SDK still tries SigV4
|
|
86
|
+
* first — same reasoning as the Converse text adapter.
|
|
87
|
+
*/
|
|
88
|
+
buildClientConfig(resolved, region, endpoint) {
|
|
89
|
+
if (resolved.kind === "bearer") return {
|
|
90
|
+
region,
|
|
91
|
+
token: { token: resolved.token },
|
|
92
|
+
authSchemePreference: ["httpBearerAuth"],
|
|
93
|
+
...endpoint ? { endpoint } : {}
|
|
94
|
+
};
|
|
95
|
+
return {
|
|
96
|
+
region: resolved.region,
|
|
97
|
+
credentials: resolved.credentials,
|
|
98
|
+
...endpoint ? { endpoint } : {}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** Send one InvokeModel call and parse its JSON response body. */
|
|
102
|
+
async invokeModel(modelId, body) {
|
|
103
|
+
const { InvokeModelCommand } = await this.importBedrockRuntime();
|
|
104
|
+
const response = await (await this.getClient()).send(new InvokeModelCommand({
|
|
105
|
+
modelId,
|
|
106
|
+
contentType: "application/json",
|
|
107
|
+
accept: "application/json",
|
|
108
|
+
body: JSON.stringify(body)
|
|
109
|
+
}));
|
|
110
|
+
return JSON.parse(new TextDecoder().decode(response.body));
|
|
111
|
+
}
|
|
112
|
+
async createEmbeddings(options) {
|
|
113
|
+
const { model, logger } = options;
|
|
114
|
+
try {
|
|
115
|
+
logger.request(`activity=embed provider=${this.name} model=${model} inputs=${options.input.length}`, {
|
|
116
|
+
provider: this.name,
|
|
117
|
+
model
|
|
118
|
+
});
|
|
119
|
+
switch (model) {
|
|
120
|
+
case "amazon.titan-embed-text-v2:0": return await this.embedTitanText(options);
|
|
121
|
+
case "amazon.titan-embed-image-v1": return await this.embedTitanImage(options);
|
|
122
|
+
case "cohere.embed-english-v3":
|
|
123
|
+
case "cohere.embed-multilingual-v3": return await this.embedCohere(options);
|
|
124
|
+
default: throw new Error(`Unknown Bedrock embedding model "${model}". Supported models: ${BEDROCK_EMBEDDING_MODELS.join(", ")}.`);
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
logger.errors(`${this.name}.createEmbeddings fatal`, {
|
|
128
|
+
error: toRunErrorPayload(error, `${this.name}.createEmbeddings failed`),
|
|
129
|
+
source: `${this.name}.createEmbeddings`
|
|
130
|
+
});
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* `amazon.titan-embed-text-v2:0` — one text per InvokeModel call, fanned
|
|
136
|
+
* out with a concurrency cap; result order matches input order and per-call
|
|
137
|
+
* `inputTextTokenCount`s are summed into usage.
|
|
138
|
+
*/
|
|
139
|
+
async embedTitanText(options) {
|
|
140
|
+
const { model, dimensions } = options;
|
|
141
|
+
if (dimensions !== void 0 && !TITAN_TEXT_DIMENSIONS.includes(dimensions)) throw new Error(`${model} supports dimensions 256, 512, or 1024; got ${dimensions}`);
|
|
142
|
+
const normalize = options.modelOptions?.normalize;
|
|
143
|
+
const responses = await mapWithConcurrency(requireTextOnlyEmbeddingInput(options.input, this.name, model), MAX_CONCURRENT_INVOCATIONS, async (text) => {
|
|
144
|
+
const body = { inputText: text };
|
|
145
|
+
if (dimensions !== void 0) body.dimensions = dimensions;
|
|
146
|
+
if (normalize !== void 0) body.normalize = normalize;
|
|
147
|
+
return readTitanEmbeddingBody(await this.invokeModel(model, body), `${this.name} ${model}`);
|
|
148
|
+
});
|
|
149
|
+
return this.toTitanResult(model, responses);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* `amazon.titan-embed-image-v1` (Titan Multimodal) — one item per
|
|
153
|
+
* InvokeModel call. An item may carry text, an image, or both (a fused
|
|
154
|
+
* item embedded into a single vector). Titan accepts at most one image per
|
|
155
|
+
* request and never fetches remote URLs.
|
|
156
|
+
*/
|
|
157
|
+
async embedTitanImage(options) {
|
|
158
|
+
const { model, dimensions } = options;
|
|
159
|
+
const outputEmbeddingLength = dimensions ?? TITAN_IMAGE_DEFAULT_DIMENSIONS;
|
|
160
|
+
if (!TITAN_IMAGE_DIMENSIONS.includes(outputEmbeddingLength)) throw new Error(`${model} supports dimensions 256, 384, or 1024; got ${outputEmbeddingLength}`);
|
|
161
|
+
const responses = await mapWithConcurrency(resolveEmbeddingInput(options.input), MAX_CONCURRENT_INVOCATIONS, async (item, index) => {
|
|
162
|
+
if (item.images.length > 1) throw new Error(`${model} accepts at most one image per input item; input item at index ${index} contains ${item.images.length} images. Pass them as separate input items (one vector each).`);
|
|
163
|
+
const body = { embeddingConfig: { outputEmbeddingLength } };
|
|
164
|
+
if (item.texts.length > 0) body.inputText = item.texts.join("\n");
|
|
165
|
+
const image = item.images[0];
|
|
166
|
+
if (image) body.inputImage = toTitanInputImage(image, model);
|
|
167
|
+
return readTitanEmbeddingBody(await this.invokeModel(model, body), `${this.name} ${model}`);
|
|
168
|
+
});
|
|
169
|
+
return this.toTitanResult(model, responses);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* `cohere.embed-*-v3` — natively batched (chunked at 96 texts per call,
|
|
173
|
+
* order preserved across chunks). `inputType` is required by the Cohere
|
|
174
|
+
* API; output dimensionality is fixed, so `dimensions` is rejected.
|
|
175
|
+
*/
|
|
176
|
+
async embedCohere(options) {
|
|
177
|
+
const { model, dimensions } = options;
|
|
178
|
+
if (dimensions !== void 0) throw new Error(`${model} does not support the dimensions option; its output size is fixed`);
|
|
179
|
+
const inputType = options.modelOptions?.inputType;
|
|
180
|
+
if (inputType === void 0) throw new Error(`${model} requires modelOptions.inputType ('search_document' | 'search_query' | 'classification' | 'clustering')`);
|
|
181
|
+
const truncate = options.modelOptions?.truncate;
|
|
182
|
+
const responses = await mapWithConcurrency(chunk(requireTextOnlyEmbeddingInput(options.input, this.name, model), COHERE_MAX_BATCH_SIZE), MAX_CONCURRENT_INVOCATIONS, async (batch) => {
|
|
183
|
+
const body = {
|
|
184
|
+
texts: batch,
|
|
185
|
+
input_type: inputType
|
|
186
|
+
};
|
|
187
|
+
if (truncate !== void 0) body.truncate = truncate;
|
|
188
|
+
return readCohereEmbeddingBody(await this.invokeModel(model, body), `${this.name} ${model}`);
|
|
189
|
+
});
|
|
190
|
+
return {
|
|
191
|
+
id: this.generateId(),
|
|
192
|
+
model,
|
|
193
|
+
embeddings: responses.flat().map((vector, index) => ({
|
|
194
|
+
vector,
|
|
195
|
+
index
|
|
196
|
+
}))
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/** Assemble an EmbeddingResult from per-item Titan responses. */
|
|
200
|
+
toTitanResult(model, responses) {
|
|
201
|
+
let promptTokens = 0;
|
|
202
|
+
const embeddings = responses.map((response, index) => {
|
|
203
|
+
promptTokens += response.inputTextTokenCount;
|
|
204
|
+
return {
|
|
205
|
+
vector: response.embedding,
|
|
206
|
+
index
|
|
207
|
+
};
|
|
208
|
+
});
|
|
209
|
+
const usage = {
|
|
210
|
+
promptTokens,
|
|
211
|
+
completionTokens: 0,
|
|
212
|
+
totalTokens: promptTokens
|
|
213
|
+
};
|
|
214
|
+
return {
|
|
215
|
+
id: this.generateId(),
|
|
216
|
+
model,
|
|
217
|
+
embeddings,
|
|
218
|
+
usage
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
function isRecord(value) {
|
|
223
|
+
return typeof value === "object" && value !== null;
|
|
224
|
+
}
|
|
225
|
+
/** Narrow a Titan InvokeModel JSON body: `{ embedding, inputTextTokenCount? }`. */
|
|
226
|
+
function readTitanEmbeddingBody(raw, context) {
|
|
227
|
+
const embedding = isRecord(raw) && Array.isArray(raw.embedding) ? raw.embedding : void 0;
|
|
228
|
+
if (!embedding) throw new Error(`${context}: response body is missing the "embedding" array`);
|
|
229
|
+
return {
|
|
230
|
+
embedding,
|
|
231
|
+
inputTextTokenCount: isRecord(raw) && typeof raw.inputTextTokenCount === "number" ? raw.inputTextTokenCount : 0
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/** Narrow a Cohere InvokeModel JSON body: `{ embeddings: number[][] }` (float). */
|
|
235
|
+
function readCohereEmbeddingBody(raw, context) {
|
|
236
|
+
const embeddings = isRecord(raw) && Array.isArray(raw.embeddings) ? raw.embeddings : void 0;
|
|
237
|
+
if (!embeddings) throw new Error(`${context}: response body is missing the "embeddings" array`);
|
|
238
|
+
return embeddings;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Map an ImagePart to Titan's `inputImage` (RAW base64, no data: prefix).
|
|
242
|
+
* Accepts `data` sources as-is and `url` sources ONLY when the value is a
|
|
243
|
+
* `data:` URI; Titan cannot fetch remote http(s) URLs.
|
|
244
|
+
*/
|
|
245
|
+
function toTitanInputImage(image, model) {
|
|
246
|
+
const source = image.source;
|
|
247
|
+
if (source.type === "data") return source.value;
|
|
248
|
+
if (source.value.startsWith("data:")) {
|
|
249
|
+
const comma = source.value.indexOf(",");
|
|
250
|
+
if (comma !== -1) return source.value.slice(comma + 1);
|
|
251
|
+
}
|
|
252
|
+
throw new Error(`Bedrock Titan does not fetch remote image URLs; pass base64 data (a { type: 'data' } source or a data: URI) for ${model}.`);
|
|
253
|
+
}
|
|
254
|
+
/** Split into runs of at most `size`, preserving order. */
|
|
255
|
+
function chunk(items, size) {
|
|
256
|
+
const chunks = [];
|
|
257
|
+
for (let i = 0; i < items.length; i += size) chunks.push(items.slice(i, i + size));
|
|
258
|
+
return chunks;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Map `fn` over `items` with at most `limit` calls in flight, returning
|
|
262
|
+
* results in input order (result[i] corresponds to items[i] regardless of
|
|
263
|
+
* completion order). Rejects with the first error.
|
|
264
|
+
*/
|
|
265
|
+
async function mapWithConcurrency(items, limit, fn) {
|
|
266
|
+
const results = new Array(items.length);
|
|
267
|
+
let next = 0;
|
|
268
|
+
const worker = async () => {
|
|
269
|
+
while (next < items.length) {
|
|
270
|
+
const index = next++;
|
|
271
|
+
const item = items[index];
|
|
272
|
+
if (item === void 0) continue;
|
|
273
|
+
results[index] = await fn(item, index);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
const workerCount = Math.max(1, Math.min(limit, items.length));
|
|
277
|
+
await Promise.all(Array.from({ length: workerCount }, () => worker()));
|
|
278
|
+
return results;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Creates a Bedrock embedding adapter with an explicit API key (bearer).
|
|
282
|
+
* Type resolution happens here at the call site.
|
|
283
|
+
*
|
|
284
|
+
* @param model - The model name (e.g., 'amazon.titan-embed-text-v2:0')
|
|
285
|
+
* @param apiKey - Your Bedrock API key
|
|
286
|
+
* @param config - Optional additional configuration (region, baseURL, ...)
|
|
287
|
+
* @returns Configured Bedrock embedding adapter instance with resolved types
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* ```typescript
|
|
291
|
+
* const adapter = createBedrockEmbedding(
|
|
292
|
+
* 'amazon.titan-embed-text-v2:0',
|
|
293
|
+
* 'bedrock-api-key',
|
|
294
|
+
* );
|
|
295
|
+
*
|
|
296
|
+
* const result = await embed({
|
|
297
|
+
* adapter,
|
|
298
|
+
* input: 'a red guitar',
|
|
299
|
+
* });
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
function createBedrockEmbedding(model, apiKey, config) {
|
|
303
|
+
return new BedrockEmbeddingAdapter({
|
|
304
|
+
...config,
|
|
305
|
+
apiKey
|
|
306
|
+
}, model);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Creates a Bedrock embedding adapter using the ambient auth cascade:
|
|
310
|
+
* `config.apiKey` → `BEDROCK_API_KEY` → `AWS_BEARER_TOKEN_BEDROCK` → SigV4
|
|
311
|
+
* (AWS credential provider chain). Auth resolves lazily on the first
|
|
312
|
+
* request, so `auth: 'sigv4'` never requires an API key.
|
|
313
|
+
*
|
|
314
|
+
* @param model - The model name (e.g., 'cohere.embed-english-v3')
|
|
315
|
+
* @param config - Optional configuration (region, auth, baseURL, ...)
|
|
316
|
+
* @returns Configured Bedrock embedding adapter instance with resolved types
|
|
317
|
+
*
|
|
318
|
+
* @example
|
|
319
|
+
* ```typescript
|
|
320
|
+
* const adapter = bedrockEmbedding('cohere.embed-english-v3');
|
|
321
|
+
*
|
|
322
|
+
* const result = await embed({
|
|
323
|
+
* adapter,
|
|
324
|
+
* input: ['a red guitar', 'a blue drum kit'],
|
|
325
|
+
* modelOptions: { inputType: 'search_document' },
|
|
326
|
+
* });
|
|
327
|
+
*
|
|
328
|
+
* console.log(result.embeddings[0].vector)
|
|
329
|
+
* ```
|
|
330
|
+
*/
|
|
331
|
+
function bedrockEmbedding(model, config) {
|
|
332
|
+
return new BedrockEmbeddingAdapter(config ?? {}, model);
|
|
333
|
+
}
|
|
334
|
+
//#endregion
|
|
335
|
+
export { BedrockEmbeddingAdapter, bedrockEmbedding, createBedrockEmbedding };
|
|
336
|
+
|
|
337
|
+
//# sourceMappingURL=embedding.js.map
|