@warlock.js/ai-bedrock 4.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["InvalidRequestError","AIError","ProviderTimeoutError","ProviderAuthError","QuotaExceededError","ProviderRateLimitError","ContextLengthExceededError","InvalidRequestError","ProviderError","LOG_MODULE","log","InvokeModelCommand","log","ConverseCommand","ConverseStreamCommand","BedrockRuntimeClient"],"sources":["../../../../../@warlock.js/ai-bedrock/src/utils/map-stop-reason.ts","../../../../../@warlock.js/ai-bedrock/src/utils/to-bedrock-messages.ts","../../../../../@warlock.js/ai-bedrock/src/utils/to-bedrock-tools.ts","../../../../../@warlock.js/ai-bedrock/src/utils/wrap-bedrock-error.ts","../../../../../@warlock.js/ai-bedrock/src/embedder.ts","../../../../../@warlock.js/ai-bedrock/src/known-vision-models.ts","../../../../../@warlock.js/ai-bedrock/src/model.ts","../../../../../@warlock.js/ai-bedrock/src/sdk.ts"],"sourcesContent":["import type { FinishReason } from \"@warlock.js/ai\";\n\nconst stopReasonMap: Record<string, FinishReason> = {\n end_turn: \"stop\",\n stop_sequence: \"stop\",\n max_tokens: \"length\",\n tool_use: \"tool_calls\",\n};\n\n/**\n * Map Bedrock Converse's `stopReason` to the normalized `FinishReason`\n * union.\n *\n * `end_turn` / `stop_sequence` are natural stops. `max_tokens` maps to\n * `length`. `tool_use` maps to `tool_calls`. Everything else —\n * `content_filtered`, `guardrail_intervened`, `malformed_tool_use`,\n * `malformed_model_output`, `model_context_window_exceeded`, `null`,\n * or any future value — falls through to `\"error\"`: none produced a\n * clean terminal answer, so the agent must not treat them as success.\n *\n * @example\n * mapStopReason(\"end_turn\"); // \"stop\"\n * mapStopReason(\"tool_use\"); // \"tool_calls\"\n * mapStopReason(\"guardrail_intervened\"); // \"error\"\n * mapStopReason(undefined); // \"error\"\n */\nexport function mapStopReason(raw: string | null | undefined): FinishReason {\n return stopReasonMap[raw ?? \"\"] ?? \"error\";\n}\n","import { InvalidRequestError, type ContentPart, type Message } from \"@warlock.js/ai\";\nimport type {\n ContentBlock,\n ImageFormat,\n Message as BedrockMessage,\n SystemContentBlock,\n} from \"@aws-sdk/client-bedrock-runtime\";\n\n/**\n * Result of splitting a vendor-neutral `Message[]` for the Bedrock\n * Converse API: system prompts hoist to a separate `SystemContentBlock[]`\n * (Converse has no `\"system\"` role inside `messages`), and the\n * remaining turns map to Bedrock `Message[]`.\n */\nexport type BedrockMessages = {\n system: SystemContentBlock[] | undefined;\n messages: BedrockMessage[];\n};\n\nconst MEDIA_TYPE_TO_FORMAT: Record<string, ImageFormat> = {\n \"image/jpeg\": \"jpeg\",\n \"image/png\": \"png\",\n \"image/gif\": \"gif\",\n \"image/webp\": \"webp\",\n};\n\n/**\n * Convert vendor-neutral `Message[]` into Bedrock Converse's request\n * shape.\n *\n * Converse differs from the OpenAI Chat protocol in three ways this\n * function absorbs:\n *\n * 1. **No `system` role.** System messages become a separate\n * `SystemContentBlock[]` (one `{ text }` block each).\n * 2. **Tool results are `user` turns.** A neutral `tool` message\n * becomes a `user` message carrying a single `toolResult` block.\n * 3. **Tool calls are `toolUse` content blocks.** An assistant message\n * with `toolCalls` becomes an `assistant` message: an optional\n * leading `text` block followed by one `toolUse` block per call.\n *\n * @example\n * const { system, messages } = toBedrockMessages([\n * { role: \"system\", content: \"Be concise.\" },\n * { role: \"user\", content: \"Hi\" },\n * ]);\n */\nexport function toBedrockMessages(messages: Message[]): BedrockMessages {\n const system: SystemContentBlock[] = [];\n const mapped: BedrockMessage[] = [];\n\n for (const message of messages) {\n if (message.role === \"system\") {\n system.push({ text: stringifyContent(message.content) });\n\n continue;\n }\n\n if (message.role === \"tool\") {\n mapped.push({\n role: \"user\",\n content: [\n {\n toolResult: {\n toolUseId: message.toolCallId ?? \"\",\n content: [{ text: stringifyContent(message.content) }],\n },\n },\n ],\n });\n\n continue;\n }\n\n if (message.role === \"assistant\" && message.toolCalls && message.toolCalls.length > 0) {\n const blocks: ContentBlock[] = [];\n const text = stringifyContent(message.content);\n\n if (text) {\n blocks.push({ text });\n }\n\n for (const toolCall of message.toolCalls) {\n blocks.push({\n toolUse: {\n toolUseId: toolCall.id,\n name: toolCall.name,\n input: toolCall.input ?? {},\n },\n } as ContentBlock);\n }\n\n mapped.push({ role: \"assistant\", content: blocks });\n\n continue;\n }\n\n if (message.role === \"user\" && Array.isArray(message.content)) {\n mapped.push({\n role: \"user\",\n content: message.content.map(toBedrockContentBlock),\n });\n\n continue;\n }\n\n mapped.push({\n role: message.role === \"assistant\" ? \"assistant\" : \"user\",\n content: [{ text: stringifyContent(message.content) }],\n });\n }\n\n return {\n system: system.length > 0 ? system : undefined,\n messages: mapped,\n };\n}\n\n/**\n * Multipart content is only meaningful on user messages — for any other\n * role collapse a `ContentPart[]` to its concatenated text. Plain\n * strings pass through unchanged.\n */\nfunction stringifyContent(content: string | ContentPart[]): string {\n if (typeof content === \"string\") {\n return content;\n }\n\n return content\n .filter((part): part is { type: \"text\"; text: string } => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\");\n}\n\n/**\n * Map a resolved `ContentPart` to a Bedrock `ContentBlock`. Bedrock's\n * `ImageSource` only accepts raw bytes or an S3 location — there is no\n * remote-URL source. A neutral `{ url }` image therefore cannot be\n * sent and surfaces a typed `InvalidRequestError` upfront rather than\n * a downstream Bedrock validation fault. The agent has already\n * resolved attachments, so this never fetches or reads anything.\n */\nfunction toBedrockContentBlock(part: ContentPart): ContentBlock {\n if (part.type === \"text\") {\n return { text: part.text };\n }\n\n if (\"url\" in part.source) {\n throw new InvalidRequestError(\n \"Bedrock Converse does not support remote-URL image sources; supply base64 image bytes instead.\",\n );\n }\n\n const format = MEDIA_TYPE_TO_FORMAT[part.source.mediaType];\n\n if (!format) {\n throw new InvalidRequestError(\n `Unsupported image media type for Bedrock: \"${part.source.mediaType}\" (expected image/jpeg, image/png, image/gif, or image/webp).`,\n );\n }\n\n return {\n image: {\n format,\n source: { bytes: Buffer.from(part.source.base64, \"base64\") },\n },\n };\n}\n","import { extractJsonSchema, type ToolConfig } from \"@warlock.js/ai\";\nimport type { Tool, ToolConfiguration, ToolInputSchema } from \"@aws-sdk/client-bedrock-runtime\";\n\n/**\n * Convert vendor-neutral `ToolConfig[]` into Bedrock Converse's\n * `ToolConfiguration`. Each tool becomes a `toolSpec` with a JSON\n * `inputSchema`. Bedrock requires the schema root to be an object —\n * a non-object extraction degrades to a parameterless object schema\n * so registration never fails.\n *\n * Returns `undefined` when there are no tools so the caller can omit\n * `toolConfig` from the request entirely (Bedrock rejects an empty\n * `tools` array).\n *\n * @example\n * const toolConfig = toBedrockToolConfig([weatherTool]);\n * await client.send(new ConverseCommand({ modelId, messages, toolConfig }));\n */\nexport function toBedrockToolConfig(\n tools: ToolConfig<unknown, unknown>[] | undefined,\n): ToolConfiguration | undefined {\n if (!tools || tools.length === 0) {\n return undefined;\n }\n\n return {\n tools: tools.map(\n (tool): Tool => ({\n toolSpec: {\n name: tool.name,\n description: tool.description,\n inputSchema: { json: toJsonSchema(tool.input) } as ToolInputSchema,\n },\n }),\n ),\n };\n}\n\n/**\n * Resolve a tool's input schema to a JSON-Schema object. Bedrock's\n * `ToolInputSchema.json` requires an object root; anything else (or a\n * failed extraction) degrades to a parameterless object so the tool\n * still registers.\n */\nfunction toJsonSchema(input: ToolConfig<unknown, unknown>[\"input\"]): Record<string, unknown> {\n const schema = extractJsonSchema(input);\n\n if (schema && schema.type === \"object\") {\n return schema;\n }\n\n return { type: \"object\" };\n}\n","import {\n AIError,\n ContextLengthExceededError,\n InvalidRequestError,\n ProviderAuthError,\n ProviderError,\n ProviderRateLimitError,\n ProviderTimeoutError,\n QuotaExceededError,\n} from \"@warlock.js/ai\";\n\n/**\n * Raw-error fields the wrapper reads off an AWS SDK exception. Every\n * Bedrock error is a Smithy `__BaseException` with a stable `name`\n * (`\"ThrottlingException\"`, `\"ValidationException\"`, …) and a\n * `$metadata` carrying `httpStatusCode` + `requestId`. We duck-type\n * because retries and proxies sometimes flatten the prototype chain.\n */\ntype BedrockErrorShape = {\n name?: string;\n message?: string;\n httpStatusCode?: number;\n requestId?: string;\n code?: string;\n};\n\nconst TIMEOUT_NAMES = new Set([\n \"ModelTimeoutException\",\n \"TimeoutError\",\n \"RequestTimeout\",\n \"RequestTimeoutException\",\n]);\n\n/**\n * Wrap any thrown value caught inside the Bedrock adapter into the\n * appropriate `@warlock.js/ai` `AIError` subclass.\n *\n * **Dispatch strategy.** AWS errors carry no provider machine `code`;\n * the stable identifier is the Smithy exception `name`. Dispatch keys\n * on `name`, falls back to `$metadata.httpStatusCode` when the name is\n * missing (flattened/proxied errors). `ValidationException` is split:\n * the \"input is too long / exceeds context window\" phrasing maps to\n * `ContextLengthExceededError`, everything else to\n * `InvalidRequestError`.\n *\n * `AIError` instances pass through unchanged so `catch/throw wrap(e)`\n * pipelines never double-wrap.\n *\n * @example\n * try {\n * return await this.client.send(new ConverseCommand(...));\n * } catch (thrown) {\n * throw wrapBedrockError(thrown);\n * }\n */\nexport function wrapBedrockError(thrown: unknown): AIError {\n if (thrown instanceof AIError) {\n return thrown;\n }\n\n const shape = toShape(thrown);\n const context = buildContext(shape);\n const message = shape.message ?? (thrown instanceof Error ? thrown.message : String(thrown));\n\n if (isTimeout(shape)) {\n return new ProviderTimeoutError(message, { cause: thrown, context });\n }\n\n if (shape.name === \"AccessDeniedException\" || shape.httpStatusCode === 403) {\n return new ProviderAuthError(message, { cause: thrown, context });\n }\n\n if (shape.httpStatusCode === 401) {\n return new ProviderAuthError(message, { cause: thrown, context });\n }\n\n if (shape.name === \"ServiceQuotaExceededException\") {\n return new QuotaExceededError(message, { cause: thrown, context });\n }\n\n if (shape.name === \"ThrottlingException\" || shape.httpStatusCode === 429) {\n return new ProviderRateLimitError(message, { cause: thrown, context });\n }\n\n if (shape.name === \"ValidationException\") {\n if (/too long|context window|maximum context|exceeds the maximum/i.test(message)) {\n return new ContextLengthExceededError(message, { cause: thrown, context });\n }\n\n return new InvalidRequestError(message, { cause: thrown, context });\n }\n\n if (\n shape.name === \"ResourceNotFoundException\" ||\n shape.name === \"ConflictException\" ||\n isClientStatus(shape.httpStatusCode)\n ) {\n return new InvalidRequestError(message, { cause: thrown, context });\n }\n\n return new ProviderError(message, { cause: thrown, context });\n}\n\n/**\n * Read the raw error shape without depending on `instanceof`. AWS\n * exceptions expose `$metadata`; plain/proxied errors may carry\n * `status` / `code` instead.\n */\nfunction toShape(thrown: unknown): BedrockErrorShape {\n if (typeof thrown !== \"object\" || thrown === null) {\n return {};\n }\n\n const raw = thrown as Record<string, unknown>;\n const metadata = raw.$metadata as { httpStatusCode?: number; requestId?: string } | undefined;\n\n return {\n name: typeof raw.name === \"string\" ? raw.name : undefined,\n message: typeof raw.message === \"string\" ? raw.message : undefined,\n httpStatusCode:\n metadata && typeof metadata.httpStatusCode === \"number\"\n ? metadata.httpStatusCode\n : typeof raw.status === \"number\"\n ? (raw.status as number)\n : undefined,\n requestId: metadata && typeof metadata.requestId === \"string\" ? metadata.requestId : undefined,\n code: typeof raw.code === \"string\" ? raw.code : undefined,\n };\n}\n\n/**\n * Decide whether the error is a timeout. Bedrock surfaces\n * `ModelTimeoutException`; the AWS transport layer surfaces\n * `TimeoutError` / `ETIMEDOUT` / `ECONNABORTED`.\n */\nfunction isTimeout(shape: BedrockErrorShape): boolean {\n if (shape.name && TIMEOUT_NAMES.has(shape.name)) {\n return true;\n }\n\n return shape.code === \"ETIMEDOUT\" || shape.code === \"ECONNABORTED\";\n}\n\n/** True for HTTP 4xx — a client-side request problem, not a server fault. */\nfunction isClientStatus(status: number | undefined): boolean {\n return typeof status === \"number\" && status >= 400 && status < 500;\n}\n\n/**\n * Attach the raw diagnostic fields to `error.context`. The Smithy\n * exception `name` is the closest thing Bedrock has to a stable error\n * code, so it lands on `context.code`.\n */\nfunction buildContext(shape: BedrockErrorShape): Record<string, unknown> {\n const context: Record<string, unknown> = {};\n\n if (shape.httpStatusCode !== undefined) {\n context.status = shape.httpStatusCode;\n }\n\n if (shape.name) {\n context.code = shape.name;\n }\n\n if (shape.requestId) {\n context.requestId = shape.requestId;\n }\n\n return context;\n}\n","import {\n type EmbeddingBatchResult,\n type EmbeddingResult,\n type EmbeddingUsage,\n type EmbedderContract,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport { InvokeModelCommand, type BedrockRuntimeClient } from \"@aws-sdk/client-bedrock-runtime\";\nimport type { BedrockEmbedderConfig } from \"./config.type\";\nimport { wrapBedrockError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.bedrock\";\n\n/** Shape of the Amazon Titan Text Embeddings response body. */\ntype TitanEmbeddingResponse = {\n embedding: number[];\n inputTextTokenCount: number;\n};\n\n/**\n * Bedrock-backed implementation of `EmbedderContract`, targeting the\n * Amazon Titan Text Embeddings family\n * (`amazon.titan-embed-text-v2:0` / v1) via `InvokeModel`.\n *\n * **Role.** Converts text into floating-point vectors. Standalone\n * primitive — unrelated to Converse / tools / the agent loop.\n *\n * **Single-input only upstream.** Titan's `InvokeModel` body accepts\n * one `inputText` per call — there is no batch endpoint. `embedMany`\n * therefore issues one request per input sequentially and aggregates\n * token usage. This is a deliberate, documented trade-off: a real\n * batch API does not exist for Titan on Bedrock, so the alternative\n * (failing `embedMany`) would be worse. Cohere embeddings on Bedrock\n * *do* batch but use an incompatible body shape — out of scope; use\n * the OpenAI adapter or a future Cohere adapter when batch throughput\n * matters.\n *\n * **Dimensions.** When no `dimensions` override is given,\n * `this.dimensions` starts at `0` and is populated from the first\n * response's vector length, then cached. Passing `dimensions` forwards\n * Titan v2's truncation hint (256 / 512 / 1024) and sets the initial\n * value immediately.\n *\n * @example\n * const embedder = new BedrockEmbedder(client, { name: \"amazon.titan-embed-text-v2:0\" });\n * const { vector } = await embedder.embed(\"Hello world\");\n * const { vectors } = await embedder.embedMany([\"doc 1\", \"doc 2\"]);\n */\nexport class BedrockEmbedder implements EmbedderContract {\n public readonly name: string;\n public readonly provider: string;\n public dimensions: number;\n\n private readonly client: BedrockRuntimeClient;\n private readonly configuredDimensions: number | undefined;\n private readonly logger: Logger = log;\n\n public constructor(\n client: BedrockRuntimeClient,\n config: BedrockEmbedderConfig,\n provider: string = \"bedrock\",\n ) {\n this.client = client;\n this.name = config.name;\n this.provider = provider;\n this.configuredDimensions = config.dimensions;\n this.dimensions = config.dimensions ?? 0;\n }\n\n public async embed(input: string): Promise<EmbeddingResult> {\n const { vector, tokens } = await this.invoke(input);\n\n return {\n vector,\n dimensions: this.dimensions,\n usage: { promptTokens: tokens, totalTokens: tokens },\n };\n }\n\n public async embedMany(inputs: string[]): Promise<EmbeddingBatchResult> {\n const vectors: number[][] = [];\n let tokens = 0;\n\n for (const input of inputs) {\n const result = await this.invoke(input);\n\n vectors.push(result.vector);\n tokens += result.tokens;\n }\n\n const usage: EmbeddingUsage = { promptTokens: tokens, totalTokens: tokens };\n\n return { vectors, dimensions: this.dimensions, usage };\n }\n\n /**\n * Issue a single Titan `InvokeModel` embedding request: encode the\n * JSON body, send, wrap provider errors, decode the response, and\n * cache `dimensions` on the first successful call.\n */\n private async invoke(input: string): Promise<{ vector: number[]; tokens: number }> {\n this.logger.debug(LOG_MODULE, \"embedder.request\", \"InvokeModel embeddings\", {\n model: this.name,\n });\n\n const body = JSON.stringify({\n inputText: input,\n ...(this.configuredDimensions !== undefined\n ? { dimensions: this.configuredDimensions }\n : {}),\n });\n\n let raw;\n\n try {\n raw = await this.client.send(\n new InvokeModelCommand({\n modelId: this.name,\n contentType: \"application/json\",\n accept: \"application/json\",\n body: new TextEncoder().encode(body),\n }),\n );\n } catch (thrown) {\n const wrapped = wrapBedrockError(thrown);\n\n this.logger.error(LOG_MODULE, \"embedder.error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n const decoded = JSON.parse(new TextDecoder().decode(raw.body)) as TitanEmbeddingResponse;\n\n if (this.dimensions === 0) {\n this.dimensions = decoded.embedding.length;\n }\n\n this.logger.debug(LOG_MODULE, \"embedder.response\", \"InvokeModel embeddings returned\", {\n dimensions: decoded.embedding.length,\n tokens: decoded.inputTextTokenCount,\n });\n\n return { vector: decoded.embedding, tokens: decoded.inputTextTokenCount };\n }\n}\n","/**\n * Substrings that identify Bedrock model ids whose family accepts image\n * input on the Converse API.\n *\n * Bedrock model ids are provider-prefixed and version-suffixed\n * (`anthropic.claude-3-5-sonnet-20240620-v1:0`, `us.amazon.nova-pro-v1:0`,\n * `meta.llama3-2-90b-instruct-v1:0`), so a substring match is the only\n * robust check across the cross-region inference-profile prefixes\n * (`us.`, `eu.`, `apac.`) and date/version tags.\n *\n * Multimodal families covered: Anthropic Claude 3 / 3.5 / 3.7 / 4,\n * Amazon Nova Lite/Pro/Premier, Meta Llama 3.2 (11B/90B) and Llama 4.\n * Text-only families (Llama 3/3.1, Titan Text, Mistral 7B, Cohere\n * Command) are intentionally absent. Override per-model via\n * `bedrock.model({ name, vision: true | false })`.\n */\nconst VISION_CAPABLE_SUBSTRINGS = [\n \"claude-3\",\n \"claude-sonnet-4\",\n \"claude-opus-4\",\n \"claude-haiku-4\",\n \"nova-lite\",\n \"nova-pro\",\n \"nova-premier\",\n \"llama3-2-11b\",\n \"llama3-2-90b\",\n \"llama4\",\n];\n\n/**\n * Infer whether a Bedrock model id supports vision based on the known\n * multimodal-family substrings. Unknown ids default to `false` so that\n * passing an image attachment to an unsupported model surfaces a clear,\n * agent-side capability error instead of an opaque Bedrock validation\n * fault.\n *\n * @example\n * inferVisionCapability(\"anthropic.claude-3-5-sonnet-20240620-v1:0\"); // → true\n * inferVisionCapability(\"us.amazon.nova-pro-v1:0\"); // → true\n * inferVisionCapability(\"meta.llama3-1-8b-instruct-v1:0\"); // → false\n * inferVisionCapability(\"amazon.titan-text-express-v1\"); // → false\n */\nexport function inferVisionCapability(modelId: string): boolean {\n const normalized = modelId.toLowerCase();\n\n return VISION_CAPABLE_SUBSTRINGS.some((fragment) => normalized.includes(fragment));\n}\n","import {\n safeJsonParse,\n type Message,\n type ModelCallOptions,\n type ModelCapabilities,\n type ModelContract,\n type ModelPricing,\n type ModelResponse,\n type ModelStreamChunk,\n type ModelToolCallRequest,\n type Usage,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport {\n ConverseCommand,\n ConverseStreamCommand,\n type BedrockRuntimeClient,\n type ContentBlock,\n type ConverseRequest,\n type TokenUsage,\n} from \"@aws-sdk/client-bedrock-runtime\";\nimport type { BedrockModelConfig } from \"./config.type\";\nimport { inferVisionCapability } from \"./known-vision-models\";\nimport { mapStopReason, toBedrockMessages, toBedrockToolConfig, wrapBedrockError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.bedrock\";\n\n/**\n * Bedrock-backed implementation of `ModelContract`.\n *\n * **Role.** The provider-facing bridge between the vendor-neutral\n * `@warlock.js/ai` agent runtime and AWS Bedrock's Converse /\n * ConverseStream API. Converse is the model-agnostic surface — one\n * wire mapping covers every Bedrock-hosted family (Anthropic Claude,\n * Amazon Nova, Meta Llama, Mistral, Cohere) instead of per-family\n * `InvokeModel` body shapes.\n *\n * **Responsibility.**\n * - Owns: a long-lived `BedrockRuntimeClient` + frozen `ModelConfig`\n * (modelId, temperature, maxTokens) used as per-call defaults.\n * - Owns: translating vendor-neutral `Message[]` / `ToolConfig[]` into\n * Converse shapes (system hoisting, `toolUse` / `toolResult` blocks,\n * image bytes) on the way out, and Converse's content-block response\n * (text, tool calls, stop reason, token usage) back into the neutral\n * shapes on the way in.\n * - Does NOT own: dispatching tools, looping, history, retries — those\n * are agent concerns. The model is a per-call protocol adapter.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across calls\"): the AWS client is heavy to construct and reused for\n * the SDK's lifetime.\n *\n * @example\n * import { BedrockRuntimeClient } from \"@aws-sdk/client-bedrock-runtime\";\n * const client = new BedrockRuntimeClient({ region: \"us-east-1\" });\n * const model = new BedrockModel(client, {\n * name: \"anthropic.claude-sonnet-4-5-20250929-v1:0\",\n * });\n *\n * const myAgent = agent({ model, tools: [searchTool] });\n * const result = await myAgent.execute(\"Summarize today's news.\");\n */\nexport class BedrockModel implements ModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly capabilities: ModelCapabilities;\n public readonly pricing?: ModelPricing;\n\n private readonly client: BedrockRuntimeClient;\n private readonly config: BedrockModelConfig;\n private readonly logger: Logger = log;\n\n public constructor(\n client: BedrockRuntimeClient,\n config: BedrockModelConfig,\n provider: string = \"bedrock\",\n ) {\n this.client = client;\n this.config = config;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n this.capabilities = {\n structuredOutput: config.structuredOutput ?? true,\n vision: config.vision ?? inferVisionCapability(config.name),\n };\n }\n\n /**\n * Single-shot completion via the Converse API. Sends the full\n * message list, waits for the terminal response, and reshapes it\n * into a vendor-neutral `ModelResponse`. Per-call `options` override\n * the instance defaults for this call only.\n */\n public async complete(messages: Message[], options?: ModelCallOptions): Promise<ModelResponse> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting Converse call\", {\n model: this.name,\n messageCount: messages.length,\n streaming: false,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response;\n\n try {\n response = await this.client.send(\n new ConverseCommand(this.buildRequest(messages, options)),\n options?.signal ? { abortSignal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const blocks = response.output?.message?.content ?? [];\n const finishReason = mapStopReason(response.stopReason);\n const usage = this.extractUsage(response.usage);\n const toolCalls = this.extractToolCalls(blocks);\n\n this.logger.debug(LOG_MODULE, \"response\", \"Converse call succeeded\", { finishReason, usage });\n\n return {\n content: this.extractText(blocks),\n finishReason,\n usage,\n toolCalls,\n };\n }\n\n /**\n * Incremental streaming completion via ConverseStream. Yields neutral\n * `ModelStreamChunk`s — `delta` for text, `tool-call` once a\n * `toolUse` block's accumulated input JSON is complete, and a\n * terminal `done` with the final finish reason + usage totals.\n */\n public async *stream(\n messages: Message[],\n options?: ModelCallOptions,\n ): AsyncIterable<ModelStreamChunk> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting ConverseStream call\", {\n model: this.name,\n messageCount: messages.length,\n streaming: true,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response;\n\n try {\n response = await this.client.send(\n new ConverseStreamCommand(this.buildRequest(messages, options)),\n options?.signal ? { abortSignal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n let rawStopReason: string | undefined;\n const usage: Usage = { input: 0, output: 0, total: 0 };\n const toolBlocks = new Map<number, { id: string; name: string; json: string }>();\n\n try {\n for await (const event of response.stream ?? []) {\n if (event.contentBlockStart?.start?.toolUse) {\n const start = event.contentBlockStart.start.toolUse;\n\n toolBlocks.set(event.contentBlockStart.contentBlockIndex ?? 0, {\n id: start.toolUseId ?? \"\",\n name: start.name ?? \"\",\n json: \"\",\n });\n\n continue;\n }\n\n if (event.contentBlockDelta?.delta) {\n const delta = event.contentBlockDelta.delta;\n\n if (delta.text) {\n yield { type: \"delta\", content: delta.text };\n } else if (delta.toolUse) {\n const accumulator = toolBlocks.get(event.contentBlockDelta.contentBlockIndex ?? 0);\n\n if (accumulator) {\n accumulator.json += delta.toolUse.input ?? \"\";\n }\n }\n\n continue;\n }\n\n if (event.contentBlockStop) {\n const accumulator = toolBlocks.get(event.contentBlockStop.contentBlockIndex ?? 0);\n\n if (accumulator) {\n yield {\n type: \"tool-call\",\n id: accumulator.id,\n name: accumulator.name,\n input: safeJsonParse<Record<string, unknown>>(accumulator.json, {}),\n };\n\n toolBlocks.delete(event.contentBlockStop.contentBlockIndex ?? 0);\n }\n\n continue;\n }\n\n if (event.messageStop) {\n rawStopReason = event.messageStop.stopReason;\n }\n\n if (event.metadata?.usage) {\n const raw = event.metadata.usage;\n\n usage.input = raw.inputTokens ?? 0;\n usage.output = raw.outputTokens ?? 0;\n usage.total = raw.totalTokens ?? usage.input + usage.output;\n\n if (raw.cacheReadInputTokens && raw.cacheReadInputTokens > 0) {\n usage.cachedTokens = raw.cacheReadInputTokens;\n }\n }\n }\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const finishReason = mapStopReason(rawStopReason);\n\n this.logger.debug(LOG_MODULE, \"response\", \"ConverseStream call succeeded\", {\n finishReason,\n usage,\n });\n\n yield { type: \"done\", finishReason, usage };\n }\n\n /**\n * Assemble the Converse request shared by `complete()` and\n * `stream()` (both command shapes take the same input). Hoists the\n * system prompt, maps inference params, and conditionally attaches\n * tools and native structured output.\n */\n private buildRequest(\n messages: Message[],\n options: ModelCallOptions | undefined,\n ): ConverseRequest {\n const { system, messages: bedrockMessages } = toBedrockMessages(messages);\n const maxTokens = options?.maxTokens ?? this.config.maxTokens;\n const temperature = options?.temperature ?? this.config.temperature;\n\n return {\n modelId: this.name,\n messages: bedrockMessages,\n ...(system ? { system } : {}),\n inferenceConfig: {\n ...(maxTokens !== undefined ? { maxTokens } : {}),\n ...(temperature !== undefined ? { temperature } : {}),\n },\n ...this.buildToolConfig(options?.tools),\n ...this.buildOutputConfig(options?.responseSchema),\n };\n }\n\n /**\n * Spread-friendly tool fragment. Returns an empty object when no\n * tools were supplied (Bedrock rejects an empty `tools` array).\n */\n private buildToolConfig(tools: ModelCallOptions[\"tools\"]): Pick<ConverseRequest, \"toolConfig\"> {\n const toolConfig = toBedrockToolConfig(tools);\n\n return toolConfig ? { toolConfig } : {};\n }\n\n /**\n * Translate the neutral `responseSchema` into Converse's native\n * `outputConfig.textFormat` (JSON-schema structured output). Bedrock\n * requires the schema as a stringified JSON document and only\n * accepts an object root. Emitted only when the model is\n * `structuredOutput`-capable and the schema is an object — otherwise\n * the agent's soft system-prompt hint + client-side `validate()`\n * carry shape (same degradation philosophy as the OpenAI adapter).\n */\n private buildOutputConfig(\n responseSchema: Record<string, unknown> | undefined,\n ): Pick<ConverseRequest, \"outputConfig\"> {\n if (!responseSchema || !this.capabilities.structuredOutput) {\n return {};\n }\n\n if (responseSchema.type !== \"object\" || typeof responseSchema.properties !== \"object\") {\n return {};\n }\n\n return {\n outputConfig: {\n textFormat: {\n type: \"json_schema\",\n structure: {\n jsonSchema: { name: \"response\", schema: JSON.stringify(responseSchema) },\n },\n },\n },\n };\n }\n\n /**\n * Concatenate every `text` content block into the single neutral\n * `content` string. `toolUse` and other block types are surfaced\n * separately via `extractToolCalls`.\n */\n private extractText(blocks: ContentBlock[]): string {\n return blocks\n .map((block) => (\"text\" in block && typeof block.text === \"string\" ? block.text : \"\"))\n .join(\"\");\n }\n\n /**\n * Reshape Converse `toolUse` content blocks into the neutral\n * `ModelToolCallRequest[]`. Returns `undefined` when no tools were\n * requested so callers can branch on presence.\n */\n private extractToolCalls(blocks: ContentBlock[]): ModelToolCallRequest[] | undefined {\n const toolCalls: ModelToolCallRequest[] = [];\n\n for (const block of blocks) {\n if (\"toolUse\" in block && block.toolUse) {\n toolCalls.push({\n id: block.toolUse.toolUseId ?? \"\",\n name: block.toolUse.name ?? \"\",\n input: (block.toolUse.input ?? {}) as Record<string, unknown>,\n });\n }\n }\n\n return toolCalls.length > 0 ? toolCalls : undefined;\n }\n\n /**\n * Normalize Converse's `TokenUsage` into the neutral `Usage` shape.\n * Bedrock supplies a pre-summed `totalTokens`; cache-read tokens are\n * surfaced as `cachedTokens` only when non-zero.\n */\n private extractUsage(raw: TokenUsage | undefined): Usage {\n if (!raw) {\n return { input: 0, output: 0, total: 0 };\n }\n\n const input = raw.inputTokens ?? 0;\n const output = raw.outputTokens ?? 0;\n const cached = raw.cacheReadInputTokens;\n\n return {\n input,\n output,\n total: raw.totalTokens ?? input + output,\n ...(cached && cached > 0 ? { cachedTokens: cached } : {}),\n };\n }\n\n /**\n * Wrap a thrown provider error into the typed `AIError` hierarchy\n * and emit the standard error log line before it propagates. Shared\n * by every catch site so the log shape stays identical.\n */\n private logAndWrap(thrown: unknown) {\n const wrapped = wrapBedrockError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n return wrapped;\n }\n}\n","import { BedrockRuntimeClient } from \"@aws-sdk/client-bedrock-runtime\";\nimport type {\n EmbedderContract,\n ModelContract,\n ModelPricing,\n SDKAdapterContract,\n} from \"@warlock.js/ai\";\nimport { approximateTokenCount } from \"@warlock.js/ai\";\nimport type {\n BedrockEmbedderConfig,\n BedrockModelConfig,\n BedrockSDKConfig,\n} from \"./config.type\";\nimport { BedrockEmbedder } from \"./embedder\";\nimport { BedrockModel } from \"./model\";\n\n/**\n * AWS Bedrock-backed implementation of `SDKAdapterContract`.\n *\n * **Role.** The package entry point for any Bedrock-hosted model via\n * the Converse API. A single `BedrockSDK` holds one live\n * `BedrockRuntimeClient`, shared by every `ModelContract` and\n * `EmbedderContract` it produces. Construct one SDK per AWS\n * account/region and reuse it everywhere.\n *\n * **Responsibility.**\n * - Owns: a long-lived `BedrockRuntimeClient` (region, credential\n * chain) and its lifetime. Factory for `BedrockModel` /\n * `BedrockEmbedder` instances sharing that client.\n * - Does NOT own: anything per-call — those live in `BedrockModel` /\n * `BedrockEmbedder` and the agent runtime.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across many calls\"): the AWS client is heavy to construct and\n * designed for reuse; keeping it on `this` aligns with the\n * `new BedrockRuntimeClient(...)` upstream convention.\n *\n * @example\n * const bedrock = new BedrockSDK({ region: \"us-east-1\" });\n * const model = bedrock.model({ name: \"anthropic.claude-sonnet-4-5-20250929-v1:0\" });\n * const embedder = bedrock.embedder({ name: \"amazon.titan-embed-text-v2:0\" });\n */\nexport class BedrockSDK implements SDKAdapterContract {\n private readonly client: BedrockRuntimeClient;\n private readonly provider: string;\n private readonly pricing?: Record<string, ModelPricing>;\n\n public constructor(config: BedrockSDKConfig) {\n const { provider, pricing, ...clientConfig } = config;\n\n this.client = new BedrockRuntimeClient(clientConfig);\n this.provider = provider ?? \"bedrock\";\n this.pricing = pricing;\n }\n\n /**\n * Build a `BedrockModel` bound to this SDK's client. Each call\n * returns a fresh instance; all instances share the underlying AWS\n * client so connection pools, credential refresh, and retry config\n * stay unified. The SDK's `provider` label is forwarded.\n *\n * Pricing resolution: per-model `config.pricing` wins; otherwise the\n * SDK-level registry entry keyed by `config.name`; otherwise\n * `undefined` (no cost computed).\n */\n public model(config: BedrockModelConfig): ModelContract {\n const resolvedPricing = config.pricing ?? this.pricing?.[config.name];\n const resolvedConfig: BedrockModelConfig =\n resolvedPricing === config.pricing ? config : { ...config, pricing: resolvedPricing };\n\n return new BedrockModel(this.client, resolvedConfig, this.provider);\n }\n\n /**\n * Rough token-count estimate. Uses the character-heuristic\n * (`approximateTokenCount`) from the core package — Bedrock has no\n * offline tokenizer and the per-model tokenizers differ; good enough\n * for budgeting and quota guards, not for billing.\n */\n public async count(text: string, _model?: string): Promise<number> {\n return approximateTokenCount(text);\n }\n\n /**\n * Build a `BedrockEmbedder` (Amazon Titan Text Embeddings) bound to\n * this SDK's client.\n *\n * @example\n * const embedder = bedrock.embedder({ name: \"amazon.titan-embed-text-v2:0\" });\n * const { vector } = await embedder.embed(\"Hello world\");\n */\n public embedder(config: BedrockEmbedderConfig): EmbedderContract {\n return new BedrockEmbedder(this.client, config, this.provider);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import { BedrockRuntimeClientConfig } from "@aws-sdk/client-bedrock-runtime";
2
+ import { EmbedderConfig, ModelConfig, ModelPricing } from "@warlock.js/ai";
3
+
4
+ //#region ../../@warlock.js/ai-bedrock/src/config.type.d.ts
5
+ /**
6
+ * Configuration for the AWS Bedrock SDK adapter.
7
+ *
8
+ * Bedrock authenticates via the AWS credential chain, not an API key.
9
+ * `region` is required by the runtime client; `credentials` is optional
10
+ * — when omitted, the standard AWS provider chain (env vars, shared
11
+ * config, IAM role, SSO) resolves them, exactly like every other AWS
12
+ * SDK client. The whole object is forwarded to `BedrockRuntimeClient`,
13
+ * so any additional client option (custom `requestHandler`, `endpoint`,
14
+ * retry config) is accepted as-is.
15
+ *
16
+ * `provider` labels the SDK upstream — flows through to
17
+ * `ModelContract.provider`, `AgentReport.model`, logs, and
18
+ * provider-aware middleware. Defaults to `"bedrock"`.
19
+ *
20
+ * `pricing` is an optional SDK-level registry keyed by Bedrock model
21
+ * id. Resolution at `model()` call time: per-model `pricing` >
22
+ * this SDK registry > `undefined` (no cost computed).
23
+ *
24
+ * @example
25
+ * // Ambient credential chain (env / role / SSO):
26
+ * new BedrockSDK({ region: "us-east-1" });
27
+ *
28
+ * @example
29
+ * // Explicit static credentials:
30
+ * new BedrockSDK({
31
+ * region: "us-east-1",
32
+ * credentials: { accessKeyId: "AKIA...", secretAccessKey: "..." },
33
+ * pricing: {
34
+ * "anthropic.claude-sonnet-4-5-20250929-v1:0": { input: 3, output: 15 },
35
+ * },
36
+ * });
37
+ */
38
+ type BedrockSDKConfig = BedrockRuntimeClientConfig & {
39
+ provider?: string;
40
+ /**
41
+ * Per-model USD pricing registry, keyed by Bedrock model id.
42
+ * Surfaced onto every `BedrockModel` produced by `model()`;
43
+ * per-model `BedrockModelConfig.pricing` still wins when both are
44
+ * set.
45
+ */
46
+ pricing?: Record<string, ModelPricing>;
47
+ };
48
+ /**
49
+ * Per-model configuration for `BedrockSDK.model()`. `name` is the
50
+ * Bedrock model id or inference-profile id (e.g.
51
+ * `"anthropic.claude-sonnet-4-5-20250929-v1:0"`,
52
+ * `"us.amazon.nova-pro-v1:0"`).
53
+ *
54
+ * @example
55
+ * bedrock.model({ name: "anthropic.claude-sonnet-4-5-20250929-v1:0" });
56
+ * bedrock.model({ name: "meta.llama3-1-8b-instruct-v1:0", vision: false });
57
+ */
58
+ type BedrockModelConfig = ModelConfig & {
59
+ /**
60
+ * Override the auto-inferred vision capability. When omitted, the
61
+ * adapter checks the model id against the known multimodal families
62
+ * (see `known-vision-models.ts`). Explicit `true`/`false` always
63
+ * wins over inference.
64
+ */
65
+ vision?: boolean;
66
+ /**
67
+ * Override the inferred `structuredOutput` capability. When omitted,
68
+ * the adapter treats the model as capable and forwards
69
+ * `responseSchema` via Converse's native `outputConfig.textFormat`
70
+ * (JSON-schema structured outputs). Set `false` for model families
71
+ * that don't support it — the agent then re-injects a soft schema
72
+ * hint into the system prompt instead.
73
+ */
74
+ structuredOutput?: boolean;
75
+ };
76
+ /**
77
+ * Per-embedder configuration for `BedrockSDK.embedder()`. `name` is the
78
+ * embeddings model id — the adapter targets the Amazon Titan Text
79
+ * Embeddings family (`amazon.titan-embed-text-v2:0` and v1).
80
+ * `dimensions` is forwarded to Titan v2's `dimensions` body field
81
+ * (supported truncation: 256 / 512 / 1024).
82
+ *
83
+ * @example
84
+ * bedrock.embedder({ name: "amazon.titan-embed-text-v2:0" });
85
+ * bedrock.embedder({ name: "amazon.titan-embed-text-v2:0", dimensions: 256 });
86
+ */
87
+ type BedrockEmbedderConfig = EmbedderConfig;
88
+ //#endregion
89
+ export { BedrockEmbedderConfig, BedrockModelConfig, BedrockSDKConfig };
90
+ //# sourceMappingURL=config.type.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.type.d.mts","names":[],"sources":["../../../../../@warlock.js/ai-bedrock/src/config.type.ts"],"mappings":";;;;;;AAoCA;;;;;;;;;;;;;;AAQuC;AAavC;;;;;;;;AAgBkB;AAclB;;;;AAAkD;;;KAnDtC,gBAAA,GAAmB,0BAAA;EAC7B,QAAA;;;;;;;EAOA,OAAA,GAAU,MAAA,SAAe,YAAA;AAAA;;;;;;;;;;;KAaf,kBAAA,GAAqB,WAAW;;;;;;;EAO1C,MAAA;;;;;;;;;EASA,gBAAA;AAAA;;;;;;;;;;;;KAcU,qBAAA,GAAwB,cAAc"}
@@ -0,0 +1,117 @@
1
+ import { wrapBedrockError } from "./utils/wrap-bedrock-error.mjs";
2
+ import "./utils/index.mjs";
3
+ import { InvokeModelCommand } from "@aws-sdk/client-bedrock-runtime";
4
+ import { log } from "@warlock.js/logger";
5
+
6
+ //#region ../../@warlock.js/ai-bedrock/src/embedder.ts
7
+ const LOG_MODULE = "ai.bedrock";
8
+ /**
9
+ * Bedrock-backed implementation of `EmbedderContract`, targeting the
10
+ * Amazon Titan Text Embeddings family
11
+ * (`amazon.titan-embed-text-v2:0` / v1) via `InvokeModel`.
12
+ *
13
+ * **Role.** Converts text into floating-point vectors. Standalone
14
+ * primitive — unrelated to Converse / tools / the agent loop.
15
+ *
16
+ * **Single-input only upstream.** Titan's `InvokeModel` body accepts
17
+ * one `inputText` per call — there is no batch endpoint. `embedMany`
18
+ * therefore issues one request per input sequentially and aggregates
19
+ * token usage. This is a deliberate, documented trade-off: a real
20
+ * batch API does not exist for Titan on Bedrock, so the alternative
21
+ * (failing `embedMany`) would be worse. Cohere embeddings on Bedrock
22
+ * *do* batch but use an incompatible body shape — out of scope; use
23
+ * the OpenAI adapter or a future Cohere adapter when batch throughput
24
+ * matters.
25
+ *
26
+ * **Dimensions.** When no `dimensions` override is given,
27
+ * `this.dimensions` starts at `0` and is populated from the first
28
+ * response's vector length, then cached. Passing `dimensions` forwards
29
+ * Titan v2's truncation hint (256 / 512 / 1024) and sets the initial
30
+ * value immediately.
31
+ *
32
+ * @example
33
+ * const embedder = new BedrockEmbedder(client, { name: "amazon.titan-embed-text-v2:0" });
34
+ * const { vector } = await embedder.embed("Hello world");
35
+ * const { vectors } = await embedder.embedMany(["doc 1", "doc 2"]);
36
+ */
37
+ var BedrockEmbedder = class {
38
+ constructor(client, config, provider = "bedrock") {
39
+ this.logger = log;
40
+ this.client = client;
41
+ this.name = config.name;
42
+ this.provider = provider;
43
+ this.configuredDimensions = config.dimensions;
44
+ this.dimensions = config.dimensions ?? 0;
45
+ }
46
+ async embed(input) {
47
+ const { vector, tokens } = await this.invoke(input);
48
+ return {
49
+ vector,
50
+ dimensions: this.dimensions,
51
+ usage: {
52
+ promptTokens: tokens,
53
+ totalTokens: tokens
54
+ }
55
+ };
56
+ }
57
+ async embedMany(inputs) {
58
+ const vectors = [];
59
+ let tokens = 0;
60
+ for (const input of inputs) {
61
+ const result = await this.invoke(input);
62
+ vectors.push(result.vector);
63
+ tokens += result.tokens;
64
+ }
65
+ const usage = {
66
+ promptTokens: tokens,
67
+ totalTokens: tokens
68
+ };
69
+ return {
70
+ vectors,
71
+ dimensions: this.dimensions,
72
+ usage
73
+ };
74
+ }
75
+ /**
76
+ * Issue a single Titan `InvokeModel` embedding request: encode the
77
+ * JSON body, send, wrap provider errors, decode the response, and
78
+ * cache `dimensions` on the first successful call.
79
+ */
80
+ async invoke(input) {
81
+ this.logger.debug(LOG_MODULE, "embedder.request", "InvokeModel embeddings", { model: this.name });
82
+ const body = JSON.stringify({
83
+ inputText: input,
84
+ ...this.configuredDimensions !== void 0 ? { dimensions: this.configuredDimensions } : {}
85
+ });
86
+ let raw;
87
+ try {
88
+ raw = await this.client.send(new InvokeModelCommand({
89
+ modelId: this.name,
90
+ contentType: "application/json",
91
+ accept: "application/json",
92
+ body: new TextEncoder().encode(body)
93
+ }));
94
+ } catch (thrown) {
95
+ const wrapped = wrapBedrockError(thrown);
96
+ this.logger.error(LOG_MODULE, "embedder.error", wrapped.message, {
97
+ code: wrapped.code,
98
+ context: wrapped.context
99
+ });
100
+ throw wrapped;
101
+ }
102
+ const decoded = JSON.parse(new TextDecoder().decode(raw.body));
103
+ if (this.dimensions === 0) this.dimensions = decoded.embedding.length;
104
+ this.logger.debug(LOG_MODULE, "embedder.response", "InvokeModel embeddings returned", {
105
+ dimensions: decoded.embedding.length,
106
+ tokens: decoded.inputTextTokenCount
107
+ });
108
+ return {
109
+ vector: decoded.embedding,
110
+ tokens: decoded.inputTextTokenCount
111
+ };
112
+ }
113
+ };
114
+
115
+ //#endregion
116
+ export { BedrockEmbedder };
117
+ //# sourceMappingURL=embedder.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedder.mjs","names":[],"sources":["../../../../../@warlock.js/ai-bedrock/src/embedder.ts"],"sourcesContent":["import {\n type EmbeddingBatchResult,\n type EmbeddingResult,\n type EmbeddingUsage,\n type EmbedderContract,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport { InvokeModelCommand, type BedrockRuntimeClient } from \"@aws-sdk/client-bedrock-runtime\";\nimport type { BedrockEmbedderConfig } from \"./config.type\";\nimport { wrapBedrockError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.bedrock\";\n\n/** Shape of the Amazon Titan Text Embeddings response body. */\ntype TitanEmbeddingResponse = {\n embedding: number[];\n inputTextTokenCount: number;\n};\n\n/**\n * Bedrock-backed implementation of `EmbedderContract`, targeting the\n * Amazon Titan Text Embeddings family\n * (`amazon.titan-embed-text-v2:0` / v1) via `InvokeModel`.\n *\n * **Role.** Converts text into floating-point vectors. Standalone\n * primitive — unrelated to Converse / tools / the agent loop.\n *\n * **Single-input only upstream.** Titan's `InvokeModel` body accepts\n * one `inputText` per call — there is no batch endpoint. `embedMany`\n * therefore issues one request per input sequentially and aggregates\n * token usage. This is a deliberate, documented trade-off: a real\n * batch API does not exist for Titan on Bedrock, so the alternative\n * (failing `embedMany`) would be worse. Cohere embeddings on Bedrock\n * *do* batch but use an incompatible body shape — out of scope; use\n * the OpenAI adapter or a future Cohere adapter when batch throughput\n * matters.\n *\n * **Dimensions.** When no `dimensions` override is given,\n * `this.dimensions` starts at `0` and is populated from the first\n * response's vector length, then cached. Passing `dimensions` forwards\n * Titan v2's truncation hint (256 / 512 / 1024) and sets the initial\n * value immediately.\n *\n * @example\n * const embedder = new BedrockEmbedder(client, { name: \"amazon.titan-embed-text-v2:0\" });\n * const { vector } = await embedder.embed(\"Hello world\");\n * const { vectors } = await embedder.embedMany([\"doc 1\", \"doc 2\"]);\n */\nexport class BedrockEmbedder implements EmbedderContract {\n public readonly name: string;\n public readonly provider: string;\n public dimensions: number;\n\n private readonly client: BedrockRuntimeClient;\n private readonly configuredDimensions: number | undefined;\n private readonly logger: Logger = log;\n\n public constructor(\n client: BedrockRuntimeClient,\n config: BedrockEmbedderConfig,\n provider: string = \"bedrock\",\n ) {\n this.client = client;\n this.name = config.name;\n this.provider = provider;\n this.configuredDimensions = config.dimensions;\n this.dimensions = config.dimensions ?? 0;\n }\n\n public async embed(input: string): Promise<EmbeddingResult> {\n const { vector, tokens } = await this.invoke(input);\n\n return {\n vector,\n dimensions: this.dimensions,\n usage: { promptTokens: tokens, totalTokens: tokens },\n };\n }\n\n public async embedMany(inputs: string[]): Promise<EmbeddingBatchResult> {\n const vectors: number[][] = [];\n let tokens = 0;\n\n for (const input of inputs) {\n const result = await this.invoke(input);\n\n vectors.push(result.vector);\n tokens += result.tokens;\n }\n\n const usage: EmbeddingUsage = { promptTokens: tokens, totalTokens: tokens };\n\n return { vectors, dimensions: this.dimensions, usage };\n }\n\n /**\n * Issue a single Titan `InvokeModel` embedding request: encode the\n * JSON body, send, wrap provider errors, decode the response, and\n * cache `dimensions` on the first successful call.\n */\n private async invoke(input: string): Promise<{ vector: number[]; tokens: number }> {\n this.logger.debug(LOG_MODULE, \"embedder.request\", \"InvokeModel embeddings\", {\n model: this.name,\n });\n\n const body = JSON.stringify({\n inputText: input,\n ...(this.configuredDimensions !== undefined\n ? { dimensions: this.configuredDimensions }\n : {}),\n });\n\n let raw;\n\n try {\n raw = await this.client.send(\n new InvokeModelCommand({\n modelId: this.name,\n contentType: \"application/json\",\n accept: \"application/json\",\n body: new TextEncoder().encode(body),\n }),\n );\n } catch (thrown) {\n const wrapped = wrapBedrockError(thrown);\n\n this.logger.error(LOG_MODULE, \"embedder.error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n const decoded = JSON.parse(new TextDecoder().decode(raw.body)) as TitanEmbeddingResponse;\n\n if (this.dimensions === 0) {\n this.dimensions = decoded.embedding.length;\n }\n\n this.logger.debug(LOG_MODULE, \"embedder.response\", \"InvokeModel embeddings returned\", {\n dimensions: decoded.embedding.length,\n tokens: decoded.inputTextTokenCount,\n });\n\n return { vector: decoded.embedding, tokens: decoded.inputTextTokenCount };\n }\n}\n"],"mappings":";;;;;;AAWA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCnB,IAAa,kBAAb,MAAyD;CASvD,AAAO,YACL,QACA,QACA,WAAmB,WACnB;gBANgC;EAOhC,KAAK,SAAS;EACd,KAAK,OAAO,OAAO;EACnB,KAAK,WAAW;EAChB,KAAK,uBAAuB,OAAO;EACnC,KAAK,aAAa,OAAO,cAAc;CACzC;CAEA,MAAa,MAAM,OAAyC;EAC1D,MAAM,EAAE,QAAQ,WAAW,MAAM,KAAK,OAAO,KAAK;EAElD,OAAO;GACL;GACA,YAAY,KAAK;GACjB,OAAO;IAAE,cAAc;IAAQ,aAAa;GAAO;EACrD;CACF;CAEA,MAAa,UAAU,QAAiD;EACtE,MAAM,UAAsB,CAAC;EAC7B,IAAI,SAAS;EAEb,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,SAAS,MAAM,KAAK,OAAO,KAAK;GAEtC,QAAQ,KAAK,OAAO,MAAM;GAC1B,UAAU,OAAO;EACnB;EAEA,MAAM,QAAwB;GAAE,cAAc;GAAQ,aAAa;EAAO;EAE1E,OAAO;GAAE;GAAS,YAAY,KAAK;GAAY;EAAM;CACvD;;;;;;CAOA,MAAc,OAAO,OAA8D;EACjF,KAAK,OAAO,MAAM,YAAY,oBAAoB,0BAA0B,EAC1E,OAAO,KAAK,KACd,CAAC;EAED,MAAM,OAAO,KAAK,UAAU;GAC1B,WAAW;GACX,GAAI,KAAK,yBAAyB,SAC9B,EAAE,YAAY,KAAK,qBAAqB,IACxC,CAAC;EACP,CAAC;EAED,IAAI;EAEJ,IAAI;GACF,MAAM,MAAM,KAAK,OAAO,KACtB,IAAI,mBAAmB;IACrB,SAAS,KAAK;IACd,aAAa;IACb,QAAQ;IACR,MAAM,IAAI,YAAY,EAAE,OAAO,IAAI;GACrC,CAAC,CACH;EACF,SAAS,QAAQ;GACf,MAAM,UAAU,iBAAiB,MAAM;GAEvC,KAAK,OAAO,MAAM,YAAY,kBAAkB,QAAQ,SAAS;IAC/D,MAAM,QAAQ;IACd,SAAS,QAAQ;GACnB,CAAC;GAED,MAAM;EACR;EAEA,MAAM,UAAU,KAAK,MAAM,IAAI,YAAY,EAAE,OAAO,IAAI,IAAI,CAAC;EAE7D,IAAI,KAAK,eAAe,GACtB,KAAK,aAAa,QAAQ,UAAU;EAGtC,KAAK,OAAO,MAAM,YAAY,qBAAqB,mCAAmC;GACpF,YAAY,QAAQ,UAAU;GAC9B,QAAQ,QAAQ;EAClB,CAAC;EAED,OAAO;GAAE,QAAQ,QAAQ;GAAW,QAAQ,QAAQ;EAAoB;CAC1E;AACF"}
@@ -0,0 +1,3 @@
1
+ import { BedrockEmbedderConfig, BedrockModelConfig, BedrockSDKConfig } from "./config.type.mjs";
2
+ import { BedrockSDK } from "./sdk.mjs";
3
+ export { type BedrockEmbedderConfig, type BedrockModelConfig, BedrockSDK, type BedrockSDKConfig };
package/esm/index.mjs ADDED
@@ -0,0 +1,3 @@
1
+ import { BedrockSDK } from "./sdk.mjs";
2
+
3
+ export { BedrockSDK };
@@ -0,0 +1,50 @@
1
+ //#region ../../@warlock.js/ai-bedrock/src/known-vision-models.ts
2
+ /**
3
+ * Substrings that identify Bedrock model ids whose family accepts image
4
+ * input on the Converse API.
5
+ *
6
+ * Bedrock model ids are provider-prefixed and version-suffixed
7
+ * (`anthropic.claude-3-5-sonnet-20240620-v1:0`, `us.amazon.nova-pro-v1:0`,
8
+ * `meta.llama3-2-90b-instruct-v1:0`), so a substring match is the only
9
+ * robust check across the cross-region inference-profile prefixes
10
+ * (`us.`, `eu.`, `apac.`) and date/version tags.
11
+ *
12
+ * Multimodal families covered: Anthropic Claude 3 / 3.5 / 3.7 / 4,
13
+ * Amazon Nova Lite/Pro/Premier, Meta Llama 3.2 (11B/90B) and Llama 4.
14
+ * Text-only families (Llama 3/3.1, Titan Text, Mistral 7B, Cohere
15
+ * Command) are intentionally absent. Override per-model via
16
+ * `bedrock.model({ name, vision: true | false })`.
17
+ */
18
+ const VISION_CAPABLE_SUBSTRINGS = [
19
+ "claude-3",
20
+ "claude-sonnet-4",
21
+ "claude-opus-4",
22
+ "claude-haiku-4",
23
+ "nova-lite",
24
+ "nova-pro",
25
+ "nova-premier",
26
+ "llama3-2-11b",
27
+ "llama3-2-90b",
28
+ "llama4"
29
+ ];
30
+ /**
31
+ * Infer whether a Bedrock model id supports vision based on the known
32
+ * multimodal-family substrings. Unknown ids default to `false` so that
33
+ * passing an image attachment to an unsupported model surfaces a clear,
34
+ * agent-side capability error instead of an opaque Bedrock validation
35
+ * fault.
36
+ *
37
+ * @example
38
+ * inferVisionCapability("anthropic.claude-3-5-sonnet-20240620-v1:0"); // → true
39
+ * inferVisionCapability("us.amazon.nova-pro-v1:0"); // → true
40
+ * inferVisionCapability("meta.llama3-1-8b-instruct-v1:0"); // → false
41
+ * inferVisionCapability("amazon.titan-text-express-v1"); // → false
42
+ */
43
+ function inferVisionCapability(modelId) {
44
+ const normalized = modelId.toLowerCase();
45
+ return VISION_CAPABLE_SUBSTRINGS.some((fragment) => normalized.includes(fragment));
46
+ }
47
+
48
+ //#endregion
49
+ export { inferVisionCapability };
50
+ //# sourceMappingURL=known-vision-models.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"known-vision-models.mjs","names":[],"sources":["../../../../../@warlock.js/ai-bedrock/src/known-vision-models.ts"],"sourcesContent":["/**\n * Substrings that identify Bedrock model ids whose family accepts image\n * input on the Converse API.\n *\n * Bedrock model ids are provider-prefixed and version-suffixed\n * (`anthropic.claude-3-5-sonnet-20240620-v1:0`, `us.amazon.nova-pro-v1:0`,\n * `meta.llama3-2-90b-instruct-v1:0`), so a substring match is the only\n * robust check across the cross-region inference-profile prefixes\n * (`us.`, `eu.`, `apac.`) and date/version tags.\n *\n * Multimodal families covered: Anthropic Claude 3 / 3.5 / 3.7 / 4,\n * Amazon Nova Lite/Pro/Premier, Meta Llama 3.2 (11B/90B) and Llama 4.\n * Text-only families (Llama 3/3.1, Titan Text, Mistral 7B, Cohere\n * Command) are intentionally absent. Override per-model via\n * `bedrock.model({ name, vision: true | false })`.\n */\nconst VISION_CAPABLE_SUBSTRINGS = [\n \"claude-3\",\n \"claude-sonnet-4\",\n \"claude-opus-4\",\n \"claude-haiku-4\",\n \"nova-lite\",\n \"nova-pro\",\n \"nova-premier\",\n \"llama3-2-11b\",\n \"llama3-2-90b\",\n \"llama4\",\n];\n\n/**\n * Infer whether a Bedrock model id supports vision based on the known\n * multimodal-family substrings. Unknown ids default to `false` so that\n * passing an image attachment to an unsupported model surfaces a clear,\n * agent-side capability error instead of an opaque Bedrock validation\n * fault.\n *\n * @example\n * inferVisionCapability(\"anthropic.claude-3-5-sonnet-20240620-v1:0\"); // → true\n * inferVisionCapability(\"us.amazon.nova-pro-v1:0\"); // → true\n * inferVisionCapability(\"meta.llama3-1-8b-instruct-v1:0\"); // → false\n * inferVisionCapability(\"amazon.titan-text-express-v1\"); // → false\n */\nexport function inferVisionCapability(modelId: string): boolean {\n const normalized = modelId.toLowerCase();\n\n return VISION_CAPABLE_SUBSTRINGS.some((fragment) => normalized.includes(fragment));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAgBA,MAAM,4BAA4B;CAChC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;;;;;AAeA,SAAgB,sBAAsB,SAA0B;CAC9D,MAAM,aAAa,QAAQ,YAAY;CAEvC,OAAO,0BAA0B,MAAM,aAAa,WAAW,SAAS,QAAQ,CAAC;AACnF"}