@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,41 @@
1
+ import { extractJsonSchema } from "@warlock.js/ai";
2
+
3
+ //#region ../../@warlock.js/ai-bedrock/src/utils/to-bedrock-tools.ts
4
+ /**
5
+ * Convert vendor-neutral `ToolConfig[]` into Bedrock Converse's
6
+ * `ToolConfiguration`. Each tool becomes a `toolSpec` with a JSON
7
+ * `inputSchema`. Bedrock requires the schema root to be an object —
8
+ * a non-object extraction degrades to a parameterless object schema
9
+ * so registration never fails.
10
+ *
11
+ * Returns `undefined` when there are no tools so the caller can omit
12
+ * `toolConfig` from the request entirely (Bedrock rejects an empty
13
+ * `tools` array).
14
+ *
15
+ * @example
16
+ * const toolConfig = toBedrockToolConfig([weatherTool]);
17
+ * await client.send(new ConverseCommand({ modelId, messages, toolConfig }));
18
+ */
19
+ function toBedrockToolConfig(tools) {
20
+ if (!tools || tools.length === 0) return;
21
+ return { tools: tools.map((tool) => ({ toolSpec: {
22
+ name: tool.name,
23
+ description: tool.description,
24
+ inputSchema: { json: toJsonSchema(tool.input) }
25
+ } })) };
26
+ }
27
+ /**
28
+ * Resolve a tool's input schema to a JSON-Schema object. Bedrock's
29
+ * `ToolInputSchema.json` requires an object root; anything else (or a
30
+ * failed extraction) degrades to a parameterless object so the tool
31
+ * still registers.
32
+ */
33
+ function toJsonSchema(input) {
34
+ const schema = extractJsonSchema(input);
35
+ if (schema && schema.type === "object") return schema;
36
+ return { type: "object" };
37
+ }
38
+
39
+ //#endregion
40
+ export { toBedrockToolConfig };
41
+ //# sourceMappingURL=to-bedrock-tools.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-bedrock-tools.mjs","names":[],"sources":["../../../../../../@warlock.js/ai-bedrock/src/utils/to-bedrock-tools.ts"],"sourcesContent":["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"],"mappings":";;;;;;;;;;;;;;;;;;AAkBA,SAAgB,oBACd,OAC+B;CAC/B,IAAI,CAAC,SAAS,MAAM,WAAW,GAC7B;CAGF,OAAO,EACL,OAAO,MAAM,KACV,UAAgB,EACf,UAAU;EACR,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,aAAa,EAAE,MAAM,aAAa,KAAK,KAAK,EAAE;CAChD,EACF,EACF,EACF;AACF;;;;;;;AAQA,SAAS,aAAa,OAAuE;CAC3F,MAAM,SAAS,kBAAkB,KAAK;CAEtC,IAAI,UAAU,OAAO,SAAS,UAC5B,OAAO;CAGT,OAAO,EAAE,MAAM,SAAS;AAC1B"}
@@ -0,0 +1,121 @@
1
+ import { AIError, ContextLengthExceededError, InvalidRequestError, ProviderAuthError, ProviderError, ProviderRateLimitError, ProviderTimeoutError, QuotaExceededError } from "@warlock.js/ai";
2
+
3
+ //#region ../../@warlock.js/ai-bedrock/src/utils/wrap-bedrock-error.ts
4
+ const TIMEOUT_NAMES = new Set([
5
+ "ModelTimeoutException",
6
+ "TimeoutError",
7
+ "RequestTimeout",
8
+ "RequestTimeoutException"
9
+ ]);
10
+ /**
11
+ * Wrap any thrown value caught inside the Bedrock adapter into the
12
+ * appropriate `@warlock.js/ai` `AIError` subclass.
13
+ *
14
+ * **Dispatch strategy.** AWS errors carry no provider machine `code`;
15
+ * the stable identifier is the Smithy exception `name`. Dispatch keys
16
+ * on `name`, falls back to `$metadata.httpStatusCode` when the name is
17
+ * missing (flattened/proxied errors). `ValidationException` is split:
18
+ * the "input is too long / exceeds context window" phrasing maps to
19
+ * `ContextLengthExceededError`, everything else to
20
+ * `InvalidRequestError`.
21
+ *
22
+ * `AIError` instances pass through unchanged so `catch/throw wrap(e)`
23
+ * pipelines never double-wrap.
24
+ *
25
+ * @example
26
+ * try {
27
+ * return await this.client.send(new ConverseCommand(...));
28
+ * } catch (thrown) {
29
+ * throw wrapBedrockError(thrown);
30
+ * }
31
+ */
32
+ function wrapBedrockError(thrown) {
33
+ if (thrown instanceof AIError) return thrown;
34
+ const shape = toShape(thrown);
35
+ const context = buildContext(shape);
36
+ const message = shape.message ?? (thrown instanceof Error ? thrown.message : String(thrown));
37
+ if (isTimeout(shape)) return new ProviderTimeoutError(message, {
38
+ cause: thrown,
39
+ context
40
+ });
41
+ if (shape.name === "AccessDeniedException" || shape.httpStatusCode === 403) return new ProviderAuthError(message, {
42
+ cause: thrown,
43
+ context
44
+ });
45
+ if (shape.httpStatusCode === 401) return new ProviderAuthError(message, {
46
+ cause: thrown,
47
+ context
48
+ });
49
+ if (shape.name === "ServiceQuotaExceededException") return new QuotaExceededError(message, {
50
+ cause: thrown,
51
+ context
52
+ });
53
+ if (shape.name === "ThrottlingException" || shape.httpStatusCode === 429) return new ProviderRateLimitError(message, {
54
+ cause: thrown,
55
+ context
56
+ });
57
+ if (shape.name === "ValidationException") {
58
+ if (/too long|context window|maximum context|exceeds the maximum/i.test(message)) return new ContextLengthExceededError(message, {
59
+ cause: thrown,
60
+ context
61
+ });
62
+ return new InvalidRequestError(message, {
63
+ cause: thrown,
64
+ context
65
+ });
66
+ }
67
+ if (shape.name === "ResourceNotFoundException" || shape.name === "ConflictException" || isClientStatus(shape.httpStatusCode)) return new InvalidRequestError(message, {
68
+ cause: thrown,
69
+ context
70
+ });
71
+ return new ProviderError(message, {
72
+ cause: thrown,
73
+ context
74
+ });
75
+ }
76
+ /**
77
+ * Read the raw error shape without depending on `instanceof`. AWS
78
+ * exceptions expose `$metadata`; plain/proxied errors may carry
79
+ * `status` / `code` instead.
80
+ */
81
+ function toShape(thrown) {
82
+ if (typeof thrown !== "object" || thrown === null) return {};
83
+ const raw = thrown;
84
+ const metadata = raw.$metadata;
85
+ return {
86
+ name: typeof raw.name === "string" ? raw.name : void 0,
87
+ message: typeof raw.message === "string" ? raw.message : void 0,
88
+ httpStatusCode: metadata && typeof metadata.httpStatusCode === "number" ? metadata.httpStatusCode : typeof raw.status === "number" ? raw.status : void 0,
89
+ requestId: metadata && typeof metadata.requestId === "string" ? metadata.requestId : void 0,
90
+ code: typeof raw.code === "string" ? raw.code : void 0
91
+ };
92
+ }
93
+ /**
94
+ * Decide whether the error is a timeout. Bedrock surfaces
95
+ * `ModelTimeoutException`; the AWS transport layer surfaces
96
+ * `TimeoutError` / `ETIMEDOUT` / `ECONNABORTED`.
97
+ */
98
+ function isTimeout(shape) {
99
+ if (shape.name && TIMEOUT_NAMES.has(shape.name)) return true;
100
+ return shape.code === "ETIMEDOUT" || shape.code === "ECONNABORTED";
101
+ }
102
+ /** True for HTTP 4xx — a client-side request problem, not a server fault. */
103
+ function isClientStatus(status) {
104
+ return typeof status === "number" && status >= 400 && status < 500;
105
+ }
106
+ /**
107
+ * Attach the raw diagnostic fields to `error.context`. The Smithy
108
+ * exception `name` is the closest thing Bedrock has to a stable error
109
+ * code, so it lands on `context.code`.
110
+ */
111
+ function buildContext(shape) {
112
+ const context = {};
113
+ if (shape.httpStatusCode !== void 0) context.status = shape.httpStatusCode;
114
+ if (shape.name) context.code = shape.name;
115
+ if (shape.requestId) context.requestId = shape.requestId;
116
+ return context;
117
+ }
118
+
119
+ //#endregion
120
+ export { wrapBedrockError };
121
+ //# sourceMappingURL=wrap-bedrock-error.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrap-bedrock-error.mjs","names":[],"sources":["../../../../../../@warlock.js/ai-bedrock/src/utils/wrap-bedrock-error.ts"],"sourcesContent":["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"],"mappings":";;;AA0BA,MAAM,gBAAgB,IAAI,IAAI;CAC5B;CACA;CACA;CACA;AACF,CAAC;;;;;;;;;;;;;;;;;;;;;;;AAwBD,SAAgB,iBAAiB,QAA0B;CACzD,IAAI,kBAAkB,SACpB,OAAO;CAGT,MAAM,QAAQ,QAAQ,MAAM;CAC5B,MAAM,UAAU,aAAa,KAAK;CAClC,MAAM,UAAU,MAAM,YAAY,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM;CAE1F,IAAI,UAAU,KAAK,GACjB,OAAO,IAAI,qBAAqB,SAAS;EAAE,OAAO;EAAQ;CAAQ,CAAC;CAGrE,IAAI,MAAM,SAAS,2BAA2B,MAAM,mBAAmB,KACrE,OAAO,IAAI,kBAAkB,SAAS;EAAE,OAAO;EAAQ;CAAQ,CAAC;CAGlE,IAAI,MAAM,mBAAmB,KAC3B,OAAO,IAAI,kBAAkB,SAAS;EAAE,OAAO;EAAQ;CAAQ,CAAC;CAGlE,IAAI,MAAM,SAAS,iCACjB,OAAO,IAAI,mBAAmB,SAAS;EAAE,OAAO;EAAQ;CAAQ,CAAC;CAGnE,IAAI,MAAM,SAAS,yBAAyB,MAAM,mBAAmB,KACnE,OAAO,IAAI,uBAAuB,SAAS;EAAE,OAAO;EAAQ;CAAQ,CAAC;CAGvE,IAAI,MAAM,SAAS,uBAAuB;EACxC,IAAI,+DAA+D,KAAK,OAAO,GAC7E,OAAO,IAAI,2BAA2B,SAAS;GAAE,OAAO;GAAQ;EAAQ,CAAC;EAG3E,OAAO,IAAI,oBAAoB,SAAS;GAAE,OAAO;GAAQ;EAAQ,CAAC;CACpE;CAEA,IACE,MAAM,SAAS,+BACf,MAAM,SAAS,uBACf,eAAe,MAAM,cAAc,GAEnC,OAAO,IAAI,oBAAoB,SAAS;EAAE,OAAO;EAAQ;CAAQ,CAAC;CAGpE,OAAO,IAAI,cAAc,SAAS;EAAE,OAAO;EAAQ;CAAQ,CAAC;AAC9D;;;;;;AAOA,SAAS,QAAQ,QAAoC;CACnD,IAAI,OAAO,WAAW,YAAY,WAAW,MAC3C,OAAO,CAAC;CAGV,MAAM,MAAM;CACZ,MAAM,WAAW,IAAI;CAErB,OAAO;EACL,MAAM,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;EAChD,SAAS,OAAO,IAAI,YAAY,WAAW,IAAI,UAAU;EACzD,gBACE,YAAY,OAAO,SAAS,mBAAmB,WAC3C,SAAS,iBACT,OAAO,IAAI,WAAW,WACnB,IAAI,SACL;EACR,WAAW,YAAY,OAAO,SAAS,cAAc,WAAW,SAAS,YAAY;EACrF,MAAM,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;CAClD;AACF;;;;;;AAOA,SAAS,UAAU,OAAmC;CACpD,IAAI,MAAM,QAAQ,cAAc,IAAI,MAAM,IAAI,GAC5C,OAAO;CAGT,OAAO,MAAM,SAAS,eAAe,MAAM,SAAS;AACtD;;AAGA,SAAS,eAAe,QAAqC;CAC3D,OAAO,OAAO,WAAW,YAAY,UAAU,OAAO,SAAS;AACjE;;;;;;AAOA,SAAS,aAAa,OAAmD;CACvE,MAAM,UAAmC,CAAC;CAE1C,IAAI,MAAM,mBAAmB,QAC3B,QAAQ,SAAS,MAAM;CAGzB,IAAI,MAAM,MACR,QAAQ,OAAO,MAAM;CAGvB,IAAI,MAAM,WACR,QAAQ,YAAY,MAAM;CAG5B,OAAO;AACT"}
package/llms-full.txt ADDED
@@ -0,0 +1,153 @@
1
+ # Warlock AI Bedrock — full skills
2
+
3
+ > Package: `@warlock.js/ai-bedrock`
4
+
5
+ > Generated artifact. Concatenates every SKILL.md and reference file under `@warlock.js/ai-bedrock/skills/`. Re-run `node scripts/generate-llms.mjs` after any change.
6
+
7
+ ## setup-bedrock `@warlock.js/ai-bedrock/setup-bedrock/SKILL.md`
8
+
9
+ ---
10
+ name: setup-bedrock
11
+ description: 'Wire @warlock.js/ai-bedrock — new BedrockSDK({region, credentials?, provider?}) for AWS Bedrock Converse API + Titan embeddings. AWS credential chain (no apiKey). Triggers: `BedrockSDK`, `bedrock.model`, `bedrock.embedder`, `bedrock.count`, `BedrockRuntimeClient`; "how do I use AWS Bedrock", "wire Claude/Nova/Llama on Bedrock", "Titan embeddings", "Bedrock Converse API"; typical import `import { BedrockSDK } from "@warlock.js/ai-bedrock"`. Skip: agent wiring — `@warlock.js/ai/run-ai-agent/SKILL.md`; provider choice — `@warlock.js/ai/pick-ai-provider/SKILL.md`; embeddings concepts — `@warlock.js/ai/embed-text/SKILL.md`; competing libs `@aws-sdk/client-bedrock-runtime`, `@ai-sdk/amazon-bedrock`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`.'
12
+ ---
13
+
14
+ # `@warlock.js/ai-bedrock`
15
+
16
+ Provider adapter that turns AWS Bedrock's **Converse API** into a vendor-neutral `ModelContract`, plus an Amazon **Titan** embedder. One wire mapping covers every Bedrock family.
17
+
18
+ ## Construction
19
+
20
+ ```ts
21
+ import { BedrockSDK } from "@warlock.js/ai-bedrock";
22
+
23
+ // Ambient AWS credential chain (env vars / SSO / IAM role):
24
+ const bedrock = new BedrockSDK({ region: "us-east-1" });
25
+
26
+ // Explicit static credentials:
27
+ const bedrock2 = new BedrockSDK({
28
+ region: "us-east-1",
29
+ credentials: { accessKeyId: "AKIA...", secretAccessKey: "..." },
30
+ provider: "bedrock-eu",
31
+ });
32
+ ```
33
+
34
+ Bedrock authenticates via the **AWS credential chain, not an API key** — there is no `apiKey`. `region` is required; `credentials` is optional (omit to use the standard provider chain). The whole config object is forwarded to `BedrockRuntimeClient`, so any AWS client option (`endpoint`, `requestHandler`, retry config) is accepted. `provider` defaults to `"bedrock"`.
35
+
36
+ ## Producing a model
37
+
38
+ `name` is the Bedrock **model id or inference-profile id**:
39
+
40
+ ```ts
41
+ bedrock.model({ name: "anthropic.claude-sonnet-4-5-20250929-v1:0" })
42
+ bedrock.model({ name: "us.amazon.nova-pro-v1:0", temperature: 0.2 })
43
+ bedrock.model({ name: "meta.llama3-1-8b-instruct-v1:0", maxTokens: 1024 })
44
+ ```
45
+
46
+ Uses the model-agnostic **Converse / ConverseStream** API — one wire mapping covers every Bedrock family (Claude, Nova, Llama, Mistral, Cohere) instead of per-family `InvokeModel` body shapes.
47
+
48
+ ## Capabilities — what's auto-set
49
+
50
+ | Flag | Default |
51
+ | --- | --- |
52
+ | `structuredOutput` | `true` (via Converse `outputConfig.textFormat`) |
53
+ | `vision` | Inferred from model id substring. `true` for Claude 3/3.5/3.7/4, Nova Lite/Pro/Premier, Llama 3.2-11B/90B, Llama 4. |
54
+
55
+ Explicit config always wins.
56
+
57
+ ## System prompt
58
+
59
+ Converse has no `"system"` role in `messages`. The adapter hoists every neutral `role: "system"` message into the separate `system: SystemContentBlock[]` request field. Transparent to the agent.
60
+
61
+ ## Tool calls
62
+
63
+ - Outgoing: neutral tools → `toolConfig.tools[].toolSpec` with a JSON `inputSchema`. No tools → `toolConfig` omitted entirely (Bedrock rejects an empty `tools` array).
64
+ - Assistant tool calls in history → `assistant` message with optional leading `{ text }` + one `{ toolUse }` block per call.
65
+ - Tool results (`role: "tool"`) → a `user` turn with one `{ toolResult: { toolUseId, content: [{ text }] } }` block.
66
+ - Response `toolUse` blocks → neutral `toolCalls`; `stopReason: "tool_use"` → `finishReason: "tool_calls"`.
67
+
68
+ ## Structured output
69
+
70
+ Object-root `responseSchema` + `structuredOutput`-capable → `outputConfig.textFormat = { type: "json_schema", structure: { jsonSchema: { name, schema } } }` — note Bedrock wants the schema as a **stringified JSON document**.
71
+
72
+ ## Multipart messages (vision)
73
+
74
+ - `{ type: "text" }` → `{ text }`
75
+ - `{ type: "image", source: { base64, mediaType } }` → `{ image: { format, source: { bytes } } }` (base64 decoded; media type → `jpeg`/`png`/`gif`/`webp`)
76
+ - `{ type: "image", source: { url } }` → **throws `InvalidRequestError`**. Bedrock's `ImageSource` only accepts raw bytes or an S3 location. Resolve images to base64 first.
77
+
78
+ ## Streaming
79
+
80
+ `model.stream()` drains `ConverseStreamCommand`'s `response.stream`:
81
+
82
+ - `contentBlockDelta.delta.text` → `{ type: "delta", content }`
83
+ - `toolUse`: `contentBlockStart` opens an accumulator, `contentBlockDelta.delta.toolUse.input` fragments append, `contentBlockStop` emits one consolidated `{ type: "tool-call", ... }`
84
+ - terminal `{ type: "done", finishReason, usage }` — usage from `metadata.usage` (a `TokenUsage`)
85
+
86
+ ## Finish-reason mapping
87
+
88
+ `end_turn` / `stop_sequence` → `stop` · `max_tokens` → `length` · `tool_use` → `tool_calls` · `content_filtered` / `guardrail_intervened` / unknown / null → `error`.
89
+
90
+ ## Embeddings — Amazon Titan only
91
+
92
+ ```ts
93
+ const embedder = bedrock.embedder({ name: "amazon.titan-embed-text-v2:0" });
94
+ const { vector } = await embedder.embed("Hello world");
95
+ const { vectors } = await embedder.embedMany(["a", "b"]); // N sequential calls
96
+ ```
97
+
98
+ Targets the **Titan Text Embeddings** family via `InvokeModel`. **Titan has no batch endpoint** — `embedMany` issues one request per input sequentially and aggregates token usage. Cohere-on-Bedrock batch embeddings use an incompatible body shape and are out of scope — use the OpenAI adapter when batch throughput matters.
99
+
100
+ Pass `dimensions` to forward Titan v2's truncation hint (256 / 512 / 1024).
101
+
102
+ ## Errors
103
+
104
+ Raw AWS SDK exceptions are wrapped into the typed `@warlock.js/ai` `AIError` hierarchy. Dispatch keys on the Smithy exception `name`:
105
+
106
+ - `AccessDeniedException` / 401 / 403 → `ProviderAuthError`
107
+ - `ThrottlingException` / 429 → `ProviderRateLimitError`
108
+ - `ServiceQuotaExceededException` → `QuotaExceededError`
109
+ - `ValidationException` with "too long / context window" → `ContextLengthExceededError`, else `InvalidRequestError`
110
+ - `ModelTimeoutException` / `ETIMEDOUT` → `ProviderTimeoutError`
111
+ - 5xx / `ModelError` / `ServiceUnavailable` → `ProviderError`
112
+
113
+ ## Token counting
114
+
115
+ ```ts
116
+ await bedrock.count("some text") // approximate heuristic, offline
117
+ ```
118
+
119
+ Per-model tokenizers differ across Bedrock families.
120
+
121
+ ## Pricing — per-model registry
122
+
123
+ `pricing` is an optional **registry keyed by Bedrock model id** — one entry per model, all rates in **USD per 1,000,000 tokens** (`ModelPricing`: `input`, `output`, optional `cachedInput` / `cachedOutput`). Set it on the SDK so every model it produces inherits the rates:
124
+
125
+ ```ts
126
+ const bedrock = new BedrockSDK({
127
+ region: "us-east-1",
128
+ pricing: {
129
+ // USD per 1M tokens. Key = exact Bedrock model id / inference-profile id.
130
+ "anthropic.claude-sonnet-4-5-20250929-v1:0": { input: 3, output: 15 },
131
+ "us.amazon.nova-pro-v1:0": { input: 0.8, output: 3.2 },
132
+ },
133
+ });
134
+
135
+ const { usage } = await ai.agent({ model: bedrock.model({ name: "us.amazon.nova-pro-v1:0" }) }).execute("hi");
136
+ usage.cost; // per-channel USD breakdown of THIS run, computed from tokens × pricing[model]
137
+ ```
138
+
139
+ Resolution at `model()` time: per-model `pricing` (`bedrock.model({ name, pricing })`) > SDK registry > `undefined` (no cost computed). When the same model id appears under a cross-region inference profile, list each id you actually invoke (`us.`, `eu.`, `apac.` prefixes are distinct keys). Bedrock meters cache-read tokens via Converse `usage` — those surface as `usage.cachedTokens`, so set `cachedInput` to have them priced at the discounted rate. See [`@warlock.js/ai/pick-ai-provider/SKILL.md`](@warlock.js/ai/pick-ai-provider/SKILL.md).
140
+
141
+ ## When NOT to use this skill
142
+
143
+ - Direct `@aws-sdk/client-bedrock-runtime` calls without going through `@warlock.js/ai` agents.
144
+ - OpenAI / Anthropic-direct / Gemini / Ollama models — those have their own adapter packages.
145
+ - Batch embeddings throughput — Titan is single-input upstream; use the OpenAI / Google / Ollama adapter (those batch natively).
146
+
147
+ ## See also
148
+
149
+ - [`@warlock.js/ai/run-ai-agent/SKILL.md`](@warlock.js/ai/run-ai-agent/SKILL.md)
150
+ - [`@warlock.js/ai/pick-ai-provider/SKILL.md`](@warlock.js/ai/pick-ai-provider/SKILL.md)
151
+ - [`@warlock.js/ai/embed-text/SKILL.md`](@warlock.js/ai/embed-text/SKILL.md)
152
+
153
+
package/llms.txt ADDED
@@ -0,0 +1,9 @@
1
+ # Warlock AI Bedrock
2
+
3
+ > Package: `@warlock.js/ai-bedrock`
4
+
5
+ > AWS Bedrock adapter for @warlock.js/ai
6
+
7
+ ## Skills
8
+
9
+ - [setup-bedrock](@warlock.js/ai-bedrock/setup-bedrock/SKILL.md): Wire @warlock.js/ai-bedrock — new BedrockSDK({region, credentials?, provider?}) for AWS Bedrock Converse API + Titan embeddings. AWS credential chain (no apiKey). Triggers: `BedrockSDK`, `bedrock.model`, `bedrock.embedder`, `bedrock.count`, `BedrockRuntimeClient`; "how do I use AWS Bedrock", "wire Claude/Nova/Llama on Bedrock", "Titan embeddings", "Bedrock Converse API"; typical import `import { BedrockSDK } from "@warlock.js/ai-bedrock"`. Skip: agent wiring — `@warlock.js/ai/run-ai-agent/SKILL.md`; provider choice — `@warlock.js/ai/pick-ai-provider/SKILL.md`; embeddings concepts — `@warlock.js/ai/embed-text/SKILL.md`; competing libs `@aws-sdk/client-bedrock-runtime`, `@ai-sdk/amazon-bedrock`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`.
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@warlock.js/ai-bedrock",
3
+ "description": "AWS Bedrock adapter for @warlock.js/ai",
4
+ "keywords": [
5
+ "warlock",
6
+ "ai",
7
+ "bedrock",
8
+ "aws"
9
+ ],
10
+ "author": "Hasan Zohdy",
11
+ "license": "MIT",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/warlockjs/ai-bedrock"
15
+ },
16
+ "dependencies": {
17
+ "@aws-sdk/client-bedrock-runtime": "^3.1048.0",
18
+ "@warlock.js/logger": "*"
19
+ },
20
+ "peerDependencies": {
21
+ "@warlock.js/ai": "*"
22
+ },
23
+ "version": "4.1.1",
24
+ "main": "./cjs/index.cjs",
25
+ "module": "./esm/index.mjs",
26
+ "types": "./esm/index.d.mts",
27
+ "exports": {
28
+ ".": {
29
+ "import": {
30
+ "types": "./esm/index.d.mts",
31
+ "default": "./esm/index.mjs"
32
+ },
33
+ "require": {
34
+ "types": "./esm/index.d.mts",
35
+ "default": "./cjs/index.cjs"
36
+ }
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,9 @@
1
+ # `@warlock.js/ai-bedrock` — skills index
2
+
3
+ Per-task skills. All cross-references use the form `@warlock.js/<pkg>/<skill>/SKILL.md`.
4
+
5
+ ## Skills
6
+
7
+ ### [`setup-bedrock/`](./setup-bedrock/SKILL.md)
8
+
9
+ Wire @warlock.js/ai-bedrock — new BedrockSDK({region, credentials?, provider?}) for AWS Bedrock Converse API + Titan embeddings. AWS credential chain (no apiKey). Load when wiring a Bedrock-hosted model (Claude / Nova / Llama / Mistral / Cohere) into a @warlock.js agent.
@@ -0,0 +1,143 @@
1
+ ---
2
+ name: setup-bedrock
3
+ description: 'Wire @warlock.js/ai-bedrock — new BedrockSDK({region, credentials?, provider?}) for AWS Bedrock Converse API + Titan embeddings. AWS credential chain (no apiKey). Triggers: `BedrockSDK`, `bedrock.model`, `bedrock.embedder`, `bedrock.count`, `BedrockRuntimeClient`; "how do I use AWS Bedrock", "wire Claude/Nova/Llama on Bedrock", "Titan embeddings", "Bedrock Converse API"; typical import `import { BedrockSDK } from "@warlock.js/ai-bedrock"`. Skip: agent wiring — `@warlock.js/ai/run-ai-agent/SKILL.md`; provider choice — `@warlock.js/ai/pick-ai-provider/SKILL.md`; embeddings concepts — `@warlock.js/ai/embed-text/SKILL.md`; competing libs `@aws-sdk/client-bedrock-runtime`, `@ai-sdk/amazon-bedrock`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`.'
4
+ ---
5
+
6
+ # `@warlock.js/ai-bedrock`
7
+
8
+ Provider adapter that turns AWS Bedrock's **Converse API** into a vendor-neutral `ModelContract`, plus an Amazon **Titan** embedder. One wire mapping covers every Bedrock family.
9
+
10
+ ## Construction
11
+
12
+ ```ts
13
+ import { BedrockSDK } from "@warlock.js/ai-bedrock";
14
+
15
+ // Ambient AWS credential chain (env vars / SSO / IAM role):
16
+ const bedrock = new BedrockSDK({ region: "us-east-1" });
17
+
18
+ // Explicit static credentials:
19
+ const bedrock2 = new BedrockSDK({
20
+ region: "us-east-1",
21
+ credentials: { accessKeyId: "AKIA...", secretAccessKey: "..." },
22
+ provider: "bedrock-eu",
23
+ });
24
+ ```
25
+
26
+ Bedrock authenticates via the **AWS credential chain, not an API key** — there is no `apiKey`. `region` is required; `credentials` is optional (omit to use the standard provider chain). The whole config object is forwarded to `BedrockRuntimeClient`, so any AWS client option (`endpoint`, `requestHandler`, retry config) is accepted. `provider` defaults to `"bedrock"`.
27
+
28
+ ## Producing a model
29
+
30
+ `name` is the Bedrock **model id or inference-profile id**:
31
+
32
+ ```ts
33
+ bedrock.model({ name: "anthropic.claude-sonnet-4-5-20250929-v1:0" })
34
+ bedrock.model({ name: "us.amazon.nova-pro-v1:0", temperature: 0.2 })
35
+ bedrock.model({ name: "meta.llama3-1-8b-instruct-v1:0", maxTokens: 1024 })
36
+ ```
37
+
38
+ Uses the model-agnostic **Converse / ConverseStream** API — one wire mapping covers every Bedrock family (Claude, Nova, Llama, Mistral, Cohere) instead of per-family `InvokeModel` body shapes.
39
+
40
+ ## Capabilities — what's auto-set
41
+
42
+ | Flag | Default |
43
+ | --- | --- |
44
+ | `structuredOutput` | `true` (via Converse `outputConfig.textFormat`) |
45
+ | `vision` | Inferred from model id substring. `true` for Claude 3/3.5/3.7/4, Nova Lite/Pro/Premier, Llama 3.2-11B/90B, Llama 4. |
46
+
47
+ Explicit config always wins.
48
+
49
+ ## System prompt
50
+
51
+ Converse has no `"system"` role in `messages`. The adapter hoists every neutral `role: "system"` message into the separate `system: SystemContentBlock[]` request field. Transparent to the agent.
52
+
53
+ ## Tool calls
54
+
55
+ - Outgoing: neutral tools → `toolConfig.tools[].toolSpec` with a JSON `inputSchema`. No tools → `toolConfig` omitted entirely (Bedrock rejects an empty `tools` array).
56
+ - Assistant tool calls in history → `assistant` message with optional leading `{ text }` + one `{ toolUse }` block per call.
57
+ - Tool results (`role: "tool"`) → a `user` turn with one `{ toolResult: { toolUseId, content: [{ text }] } }` block.
58
+ - Response `toolUse` blocks → neutral `toolCalls`; `stopReason: "tool_use"` → `finishReason: "tool_calls"`.
59
+
60
+ ## Structured output
61
+
62
+ Object-root `responseSchema` + `structuredOutput`-capable → `outputConfig.textFormat = { type: "json_schema", structure: { jsonSchema: { name, schema } } }` — note Bedrock wants the schema as a **stringified JSON document**.
63
+
64
+ ## Multipart messages (vision)
65
+
66
+ - `{ type: "text" }` → `{ text }`
67
+ - `{ type: "image", source: { base64, mediaType } }` → `{ image: { format, source: { bytes } } }` (base64 decoded; media type → `jpeg`/`png`/`gif`/`webp`)
68
+ - `{ type: "image", source: { url } }` → **throws `InvalidRequestError`**. Bedrock's `ImageSource` only accepts raw bytes or an S3 location. Resolve images to base64 first.
69
+
70
+ ## Streaming
71
+
72
+ `model.stream()` drains `ConverseStreamCommand`'s `response.stream`:
73
+
74
+ - `contentBlockDelta.delta.text` → `{ type: "delta", content }`
75
+ - `toolUse`: `contentBlockStart` opens an accumulator, `contentBlockDelta.delta.toolUse.input` fragments append, `contentBlockStop` emits one consolidated `{ type: "tool-call", ... }`
76
+ - terminal `{ type: "done", finishReason, usage }` — usage from `metadata.usage` (a `TokenUsage`)
77
+
78
+ ## Finish-reason mapping
79
+
80
+ `end_turn` / `stop_sequence` → `stop` · `max_tokens` → `length` · `tool_use` → `tool_calls` · `content_filtered` / `guardrail_intervened` / unknown / null → `error`.
81
+
82
+ ## Embeddings — Amazon Titan only
83
+
84
+ ```ts
85
+ const embedder = bedrock.embedder({ name: "amazon.titan-embed-text-v2:0" });
86
+ const { vector } = await embedder.embed("Hello world");
87
+ const { vectors } = await embedder.embedMany(["a", "b"]); // N sequential calls
88
+ ```
89
+
90
+ Targets the **Titan Text Embeddings** family via `InvokeModel`. **Titan has no batch endpoint** — `embedMany` issues one request per input sequentially and aggregates token usage. Cohere-on-Bedrock batch embeddings use an incompatible body shape and are out of scope — use the OpenAI adapter when batch throughput matters.
91
+
92
+ Pass `dimensions` to forward Titan v2's truncation hint (256 / 512 / 1024).
93
+
94
+ ## Errors
95
+
96
+ Raw AWS SDK exceptions are wrapped into the typed `@warlock.js/ai` `AIError` hierarchy. Dispatch keys on the Smithy exception `name`:
97
+
98
+ - `AccessDeniedException` / 401 / 403 → `ProviderAuthError`
99
+ - `ThrottlingException` / 429 → `ProviderRateLimitError`
100
+ - `ServiceQuotaExceededException` → `QuotaExceededError`
101
+ - `ValidationException` with "too long / context window" → `ContextLengthExceededError`, else `InvalidRequestError`
102
+ - `ModelTimeoutException` / `ETIMEDOUT` → `ProviderTimeoutError`
103
+ - 5xx / `ModelError` / `ServiceUnavailable` → `ProviderError`
104
+
105
+ ## Token counting
106
+
107
+ ```ts
108
+ await bedrock.count("some text") // approximate heuristic, offline
109
+ ```
110
+
111
+ Per-model tokenizers differ across Bedrock families.
112
+
113
+ ## Pricing — per-model registry
114
+
115
+ `pricing` is an optional **registry keyed by Bedrock model id** — one entry per model, all rates in **USD per 1,000,000 tokens** (`ModelPricing`: `input`, `output`, optional `cachedInput` / `cachedOutput`). Set it on the SDK so every model it produces inherits the rates:
116
+
117
+ ```ts
118
+ const bedrock = new BedrockSDK({
119
+ region: "us-east-1",
120
+ pricing: {
121
+ // USD per 1M tokens. Key = exact Bedrock model id / inference-profile id.
122
+ "anthropic.claude-sonnet-4-5-20250929-v1:0": { input: 3, output: 15 },
123
+ "us.amazon.nova-pro-v1:0": { input: 0.8, output: 3.2 },
124
+ },
125
+ });
126
+
127
+ const { usage } = await ai.agent({ model: bedrock.model({ name: "us.amazon.nova-pro-v1:0" }) }).execute("hi");
128
+ usage.cost; // per-channel USD breakdown of THIS run, computed from tokens × pricing[model]
129
+ ```
130
+
131
+ Resolution at `model()` time: per-model `pricing` (`bedrock.model({ name, pricing })`) > SDK registry > `undefined` (no cost computed). When the same model id appears under a cross-region inference profile, list each id you actually invoke (`us.`, `eu.`, `apac.` prefixes are distinct keys). Bedrock meters cache-read tokens via Converse `usage` — those surface as `usage.cachedTokens`, so set `cachedInput` to have them priced at the discounted rate. See [`@warlock.js/ai/pick-ai-provider/SKILL.md`](@warlock.js/ai/pick-ai-provider/SKILL.md).
132
+
133
+ ## When NOT to use this skill
134
+
135
+ - Direct `@aws-sdk/client-bedrock-runtime` calls without going through `@warlock.js/ai` agents.
136
+ - OpenAI / Anthropic-direct / Gemini / Ollama models — those have their own adapter packages.
137
+ - Batch embeddings throughput — Titan is single-input upstream; use the OpenAI / Google / Ollama adapter (those batch natively).
138
+
139
+ ## See also
140
+
141
+ - [`@warlock.js/ai/run-ai-agent/SKILL.md`](@warlock.js/ai/run-ai-agent/SKILL.md)
142
+ - [`@warlock.js/ai/pick-ai-provider/SKILL.md`](@warlock.js/ai/pick-ai-provider/SKILL.md)
143
+ - [`@warlock.js/ai/embed-text/SKILL.md`](@warlock.js/ai/embed-text/SKILL.md)