@wmaurer/effect-ai-amazon-bedrock 0.1.0 → 0.3.0

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +97 -16
  3. package/dist/AmazonBedrockClient.d.ts +135 -23
  4. package/dist/AmazonBedrockClient.d.ts.map +1 -1
  5. package/dist/AmazonBedrockClient.js +36 -92
  6. package/dist/AmazonBedrockClient.js.map +1 -1
  7. package/dist/AmazonBedrockConfig.d.ts +6 -6
  8. package/dist/AmazonBedrockConfig.js +4 -4
  9. package/dist/AmazonBedrockEventStream.d.ts +2 -2
  10. package/dist/AmazonBedrockEventStream.js +3 -3
  11. package/dist/AmazonBedrockLanguageModel.d.ts +22 -22
  12. package/dist/AmazonBedrockLanguageModel.d.ts.map +1 -1
  13. package/dist/AmazonBedrockLanguageModel.js +77 -28
  14. package/dist/AmazonBedrockLanguageModel.js.map +1 -1
  15. package/dist/AmazonBedrockSchema.d.ts +76 -55
  16. package/dist/AmazonBedrockSchema.d.ts.map +1 -1
  17. package/dist/AmazonBedrockSchema.js +63 -50
  18. package/dist/AmazonBedrockSchema.js.map +1 -1
  19. package/dist/index.d.ts +6 -6
  20. package/dist/index.js +6 -6
  21. package/dist/internal/credentials.d.ts +79 -0
  22. package/dist/internal/credentials.d.ts.map +1 -0
  23. package/dist/internal/credentials.js +241 -0
  24. package/dist/internal/credentials.js.map +1 -0
  25. package/dist/internal/errors.d.ts +2 -0
  26. package/dist/internal/errors.d.ts.map +1 -1
  27. package/dist/internal/errors.js +6 -0
  28. package/dist/internal/errors.js.map +1 -1
  29. package/dist/internal/utilities.d.ts +1 -1
  30. package/dist/internal/utilities.d.ts.map +1 -1
  31. package/dist/internal/utilities.js +4 -5
  32. package/dist/internal/utilities.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/AmazonBedrockClient.ts +184 -95
  35. package/src/AmazonBedrockConfig.ts +6 -6
  36. package/src/AmazonBedrockEventStream.ts +3 -3
  37. package/src/AmazonBedrockLanguageModel.ts +136 -62
  38. package/src/AmazonBedrockSchema.ts +77 -54
  39. package/src/index.ts +6 -6
  40. package/src/internal/credentials.ts +319 -0
  41. package/src/internal/errors.ts +5 -0
  42. package/src/internal/utilities.ts +7 -7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @wmaurer/effect-ai-amazon-bedrock
2
2
 
3
+ ## 0.3.0
4
+
5
+ `generateObject` asks models outside Anthropic for the object through a tool. In 0.2.0 it sent them `outputConfig.textFormat`, which Nova, Mistral Large 2402, Pixtral Large, Llama, and Palmyra answered with a 400 in probes in `eu-west-1` and `us-east-1`, so `generateObject` on these models failed unless you set `structuredOutputs: false`.
6
+
7
+ - Choose how `generateObject` asks for the object from the provider that the model ID names. Claude models keep the 0.2.0 behavior. Meta and Writer models get the object tool unforced, with a system prompt line that tells the model to call it, because Llama and Palmyra reject a forced `toolChoice`. Models of every other provider get the forced object tool. An ARN that does not name its model, such as an application inference profile, keeps the 0.2.0 behavior. `Config.structuredOutputs` and `Config.forcedToolChoice` still override the choice.
8
+ - Models of other providers that accepted `outputConfig.textFormat` in the probes, such as gpt-5.x, Grok, Qwen, GLM, MiniMax, DeepSeek V3.2, and Mistral Large 3, also get the object tool now. The object tool replaces the tools that you pass to `generateObject`, and in 0.2.0 these models kept them. Set `structuredOutputs: true` to keep the 0.2.0 behavior.
9
+ - Force the object tool with `toolChoice` `{ any: {} }` in place of `{ tool: { name } }`. The object tool is the only tool in its request, so both force it, and Mistral Large 2402 and Pixtral Large accept only `any`. A `toolChoice` that you pass with your own tools goes to Bedrock as before.
10
+ - Fail `streamText` with an `AiError` whose reason is `InvalidOutputError` when Bedrock streams tool input that is not valid JSON. In 0.2.0 the stream died with a defect. Bedrock streamed such input for Qwen3 32B in each probe run.
11
+ - Decode a stop reason outside the nine that the Converse API names, and give it the finish reason `"other"`. In 0.2.0 such a reason failed `generateText` and `streamText` with an `InvalidOutputError`, and the text before it was lost. The `StopReason` type now admits any string, so a `switch` over it that was exhaustive needs a `default` case, and a `Record` keyed by it no longer compiles. The new `KnownStopReason` schema holds the nine.
12
+
13
+ ## 0.2.0
14
+
15
+ `AmazonBedrockClient.layer`, `layerConfig`, and `make` take one of three ways to authenticate: the fixed keys of 0.1.0, a `credentialProvider`, or a Bedrock `apiKey`. Options that mix two ways, or give none, do not compile. Every 0.1.0 call compiles and runs unchanged.
16
+
17
+ - Add the `credentialProvider` option. It takes a function in the shape of the AWS SDK's credential providers, such as `fromNodeProviderChain()` from `@aws-sdk/credential-providers`, or an `Effect`. The package does not depend on the AWS SDK. The client runs the provider when a request needs credentials, runs it once for concurrent requests, and keeps the credentials until five minutes before their `expiration`, or for at least 10 seconds.
18
+ - Keep the last credentials while the provider fails and they have not expired. Ask the provider again after 30 seconds, or when the last credentials expire if that is sooner.
19
+ - Refresh credentials that Bedrock rejects. After an `AuthenticationError` of kind `ExpiredKey`, `InvalidKey`, or `MissingKey`, the next request gets new credentials, and a function provider gets `{ forceRefresh: true }`. The rejected request fails and is not sent again.
20
+ - Fail a request with an `AuthenticationError` of kind `Unknown` when the promise of a function provider rejects, and of kind `MissingKey` when a provider returns an empty key.
21
+ - An `Effect` provider fails with an `AiError`, which the request fails with unchanged, or with an `AiError` reason, which the client puts in an `AiError`. The services that it requires become requirements of the layer through a new type parameter. It runs with the services of the request first, and the services that the layer was built with fill in the rest, so a span that it opens is a child of the request's span.
22
+ - Add the `apiKey` option for Bedrock API keys. The client sends the key as `Authorization: Bearer <key>` in place of a SigV4 signature, and an `AiError` shows the header as `<redacted>`.
23
+ - AWS rejects an API key that it does not accept with `AccessDeniedException`, as it rejects a missing IAM permission, so the request fails with an `AuthenticationError` of kind `InsufficientPermissions`.
24
+ - Add the `Credentials`, `CredentialProvider`, and `ConfigOptions` types. `Options`, `ConfigOptions`, `CredentialProvider`, `make`, `layer`, and `layerConfig` take a type parameter `R` for the services of an `Effect` provider. It defaults to `never`.
25
+ - A retry policy in `transformClient` or `AmazonBedrockConfig.withClientTransform` signs each attempt again with the credentials of the request, and does not retry a provider that fails.
26
+
3
27
  ## 0.1.0
4
28
 
5
29
  The first release. It adds an Amazon Bedrock provider for `effect/ai`. The provider calls the Converse and ConverseStream APIs, signs requests with SigV4 through `aws4fetch`, and decodes the AWS event-stream framing without another dependency. `AmazonBedrockClient.layer` and `AmazonBedrockClient.layerConfig` take an access key and a secret key, and the optional `sessionToken`, `region`, `apiUrl`, and `transformClient`. The region defaults to `us-east-1`. `AmazonBedrockLanguageModel.Config` takes the Converse request fields other than `messages` and `system`.
package/README.md CHANGED
@@ -138,20 +138,88 @@ To run the program, do these steps:
138
138
 
139
139
  `AmazonBedrockClient` needs an `HttpClient`. The example above uses `FetchHttpClient.layer` from `effect/http`.
140
140
 
141
- The client takes explicit credentials. It does not read `~/.aws`, the AWS profile variables, or instance metadata. Give the credentials in one of two ways:
141
+ The client authenticates each request in one of three ways:
142
142
 
143
- - `AmazonBedrockClient.layerConfig` reads each option from a `Config`, as in the example above.
144
- - `AmazonBedrockClient.layer` takes plain values. Pass `secretAccessKey` and `sessionToken` as `Redacted` values.
143
+ - Fixed keys. `accessKeyId`, `secretAccessKey`, and an optional `sessionToken`. The client signs each request with SigV4.
144
+ - A credential provider. `credentialProvider` is a function or an `Effect` that returns credentials. The client signs each request with SigV4 and asks the provider for new credentials before the old ones expire.
145
+ - A Bedrock API key. `apiKey` is sent as `Authorization: Bearer <key>` in place of a SigV4 signature.
145
146
 
146
- `sessionToken` is optional, but `layerConfig` fails with a `ConfigError` if a `Config` that you give it finds no value. For that reason the example above gives `AWS_SESSION_TOKEN` an empty default with `Config.withDefault`. The client sends no session token when the token is empty, so the same program runs with temporary and long-lived keys.
147
+ Give exactly one of them. Options that mix two ways, or give none, do not compile. `AmazonBedrockClient.layerConfig` reads each value from a `Config`. `AmazonBedrockClient.layer` takes plain values, with the secrets as `Redacted` values. The client reads no environment variable and no file by itself.
147
148
 
148
- The client reads the credentials once, when the layer is built, and uses them for every request. Where you get them depends on where the program runs:
149
+ ### Use fixed keys
149
150
 
150
- - On a machine where the AWS CLI signs in through a profile or IAM Identity Center, run `eval "$(aws configure export-credentials --format env)"` in the shell before you start the program. The command needs AWS CLI version 2. It prints the profile's access key ID, secret access key, and session token, if the profile has one, as `export` lines. Add `--profile <name>` to choose a profile.
151
- - AWS Lambda sets `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN` for the function's execution role, so the example above runs without changes.
152
- - On Amazon ECS, Amazon EKS, and Amazon EC2, the role's credentials are not in the environment. Get them yourself, for example with `fromNodeProviderChain()` from `@aws-sdk/credential-providers`, and pass them to `AmazonBedrockClient.layer`.
151
+ The example above reads fixed keys from the environment. `sessionToken` is optional, but `layerConfig` fails with a `ConfigError` if a `Config` that you give it finds no value. For that reason the example gives `AWS_SESSION_TOKEN` an empty default with `Config.withDefault`. The client sends no session token when the token is empty, so the same program runs with temporary and long-lived keys. AWS Lambda sets `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN` for the function's execution role, so the example runs there without changes.
153
152
 
154
- The client does not refresh temporary credentials. When the session ends, every request fails with an `AuthenticationError` of kind `ExpiredKey`. Restart the program with new credentials.
153
+ The client reads fixed keys once, when the layer is built. When temporary keys expire, every request fails with an `AuthenticationError` of kind `ExpiredKey`. A program that runs longer than its session needs a credential provider.
154
+
155
+ ### Use a credential provider
156
+
157
+ A function in the shape of the AWS SDK's credential providers fits `credentialProvider` without an adapter. `fromNodeProviderChain()` from `@aws-sdk/credential-providers` looks for credentials where the AWS CLI does, in the environment, in the profiles and IAM Identity Center sessions of `~/.aws`, and in the container credentials of Amazon ECS and Amazon EKS and the instance metadata of Amazon EC2. This package does not depend on the AWS SDK, so install `@aws-sdk/credential-providers` yourself.
158
+
159
+ ```ts
160
+ import { fromNodeProviderChain } from "@aws-sdk/credential-providers"
161
+ import { AmazonBedrockClient } from "@wmaurer/effect-ai-amazon-bedrock"
162
+ import { Layer } from "effect"
163
+ import { FetchHttpClient } from "effect/http"
164
+
165
+ const BedrockClient = AmazonBedrockClient.layer({
166
+ region: "eu-west-1",
167
+ credentialProvider: fromNodeProviderChain()
168
+ }).pipe(Layer.provide(FetchHttpClient.layer))
169
+ ```
170
+
171
+ The client runs the provider when a request needs credentials, and runs it once for requests that need them at the same time:
172
+
173
+ - It keeps the credentials until five minutes before their `expiration`. It keeps credentials that expire in less than five minutes for 10 seconds, and credentials without an `expiration` until Bedrock rejects them.
174
+ - If the provider fails while the last credentials have not expired, the client signs with the last credentials. It asks the provider again after 30 seconds, or when the last credentials expire if that is sooner.
175
+ - If Bedrock rejects the credentials with an `AuthenticationError` of kind `ExpiredKey`, `InvalidKey`, or `MissingKey`, the request fails, and the next request gets new credentials. The client calls a function provider with `{ forceRefresh: true }` for them. Kind `InsufficientPermissions` keeps the credentials, because new credentials for the same role have the same IAM policy.
176
+ - If the promise of a function provider rejects, the request fails with an `AuthenticationError` of kind `Unknown`. If the provider returns an empty access key ID or secret access key, the request fails with an `AuthenticationError` of kind `MissingKey`.
177
+
178
+ An `Effect` provider can load credentials from a service of your own. The layer then requires that service:
179
+
180
+ ```ts
181
+ import { AmazonBedrockClient } from "@wmaurer/effect-ai-amazon-bedrock"
182
+ import { Context, Effect } from "effect"
183
+ import type { AiError } from "effect/ai"
184
+
185
+ class Vault extends Context.Service<Vault, {
186
+ readonly bedrockCredentials: Effect.Effect<AmazonBedrockClient.Credentials, AiError.AiError>
187
+ }>()("Vault") {}
188
+
189
+ // Layer<AmazonBedrockClient, never, HttpClient | Vault>
190
+ export const BedrockClient = AmazonBedrockClient.layer({
191
+ region: "eu-west-1",
192
+ credentialProvider: Effect.gen(function*() {
193
+ const vault = yield* Vault
194
+ return yield* vault.bedrockCredentials
195
+ })
196
+ })
197
+ ```
198
+
199
+ The provider runs with the services of the request first, and the services that the layer was built with fill in the rest. A span that the provider opens is a child of the span of the request that runs the provider.
200
+
201
+ The `Effect` can fail with an `AiError`, which the request fails with unchanged, or with an `AiError` reason such as an `AuthenticationError`, which the client puts in an `AiError`.
202
+
203
+ A retry policy in `transformClient` does not retry a failed provider, because the client gets the credentials before it sends the request. Each retry of a request signs again with the credentials of the first attempt.
204
+
205
+ ### Use a Bedrock API key
206
+
207
+ ```ts
208
+ import { AmazonBedrockClient } from "@wmaurer/effect-ai-amazon-bedrock"
209
+ import { Config, Layer } from "effect"
210
+ import { FetchHttpClient } from "effect/http"
211
+
212
+ const BedrockClient = AmazonBedrockClient.layerConfig({
213
+ region: Config.String("AWS_REGION"),
214
+ apiKey: Config.Redacted("AWS_BEARER_TOKEN_BEDROCK")
215
+ }).pipe(Layer.provide(FetchHttpClient.layer))
216
+ ```
217
+
218
+ `AWS_BEARER_TOKEN_BEDROCK` is the variable that the AWS SDK for JavaScript reads the key from. The client reads it only through the `Config` that you give it. An `AiError` and an HTTP span that records headers show the `authorization` header as `<redacted>`.
219
+
220
+ AWS rejects an API key that it does not accept, such as an expired or revoked key, with `AccessDeniedException`, as it rejects a request that the IAM policy does not allow. The request then fails with an `AuthenticationError` of kind `InsufficientPermissions`, and the client cannot tell the two causes apart.
221
+
222
+ ### Set the other options
155
223
 
156
224
  The other options are these:
157
225
 
@@ -167,7 +235,7 @@ To set request defaults, give them as the second argument of `model` or as the `
167
235
 
168
236
  - `inferenceConfig`, `additionalModelRequestFields`, and `toolConfig`. The provider sends each one as the Converse request field of the same name.
169
237
  - `outputConfig`, which takes only `effort`. The provider sets `outputConfig.textFormat` itself. `effort` is `low`, `medium`, `high`, `xhigh`, or `max`, and a higher level lets the model reason longer before it answers. The Bedrock Runtime API model documents that the level is capped at `high` when extended thinking is off. Not every model takes `effort`. On 2026-10-17 Bedrock rejected it for Claude Sonnet 4.5 and Haiku 4.5 with `This model doesn't support the effort field`.
170
- - `structuredOutputs` and `forcedToolChoice`, which choose how `generateObject` asks for the object. The provider does not send them to Bedrock. [Get structured output](#get-structured-output) explains them.
238
+ - `structuredOutputs` and `forcedToolChoice`, which override how `generateObject` asks for the object on the model you chose. The provider does not send them to Bedrock. [Get structured output](#get-structured-output) explains them.
171
239
 
172
240
  When `generateObject` asks for the object through a tool, that tool replaces your `toolConfig`. Any other request with tools replaces it with a `toolConfig` built from those tools, unless `toolChoice` is `"none"`. With no tools, or with `toolChoice` set to `"none"`, the provider sends your `toolConfig` unchanged.
173
241
 
@@ -216,22 +284,35 @@ export const adaptiveAnswer = LanguageModel.generateText({
216
284
 
217
285
  ## Get structured output
218
286
 
219
- `LanguageModel.generateText`, `LanguageModel.streamText`, and `LanguageModel.generateObject` all work with this provider. By default, `generateObject` sends the schema in the Converse `outputConfig.textFormat` field, and the tools you pass go with it. The provider converts the schema with the rules for Anthropic models.
287
+ `LanguageModel.generateText`, `LanguageModel.streamText`, and `LanguageModel.generateObject` all work with this provider. `generateObject` asks for the object in one of three ways, and the provider that the model ID names decides which:
288
+
289
+ - A Claude model gets the schema in the Converse `outputConfig.textFormat` field, and the tools you pass go with it.
290
+ - A Meta or Writer model gets the schema as a tool that it is not forced to call, and the provider adds a system prompt line that tells the model to call it.
291
+ - A model of any other provider, such as Amazon Nova, Mistral, OpenAI, or Qwen, gets the schema as a tool that `toolChoice` `{ any: {} }` forces it to call.
292
+
293
+ The provider is the first part of the model ID, or the second part after a geo prefix: `anthropic` in `eu.anthropic.claude-opus-4-8`, `amazon` in `amazon.nova-pro-v1:0`. A foundation model ARN or an inference profile ARN names its model at the end. The tool replaces the tools you pass, and the tool's input becomes the object. If the model calls the tool more than once, the first call is the object. If the model answers in text instead of calling the tool, `effect/ai` decodes the text as the object. On every path, the provider converts the schema with the rules for Anthropic models.
294
+
295
+ Probes on 2026-10-17 sent one Converse request per model and path through the `eu-west-1` and `us-east-1` endpoints. Of the models outside Anthropic:
296
+
297
+ - The models that answered `outputConfig` with a 400 include Nova, Mistral Large 2402, Pixtral Large, Llama, and Palmyra. gpt-oss-120b accepted it, but wrote text before the JSON in 3 of 4 runs.
298
+ - The tool forced with `any` returned the object on 13 of the 14 models in `eu-west-1`, and on every one of the 17 models in `us-east-1` that accept a forced tool. Mistral Large 2402 and Pixtral Large answer a named tool in `toolChoice` with a 400, and Gemma 3 27B ignores it, but all three accept `any`.
299
+ - Llama 3.1, 3.3, and 4 and Palmyra X4 and X5 answered `any` with a 400. The unforced tool returned the object on Llama 3.1 8B, Llama 4 Scout, Llama 4 Maverick, and Palmyra X5, and a wrong object on Llama 3.1 70B and Llama 3.3 70B.
300
+ - No path returned the object on Magistral Small. Llama 3 70B, Mistral 7B, Mixtral 8x7B, and DeepSeek R1 reject tool use and `outputConfig`, so `generateObject` does not work on them.
220
301
 
221
- A probe on 2026-10-17 sent one Converse request per path to 17 Claude inference profiles through the `eu-west-1` endpoint. Every profile that served the probe's requests, 12 of the 17, accepted `outputConfig.textFormat`: Claude Haiku 4.5 and 5.5, Opus 4.5, 4.6, 4.7, 4.8, 5, and 5.5, and Sonnet 4.5, 4.6, 5, and 5.5. On Opus 4.8, Opus 5.5, and Sonnet 5.5 it also accepted `outputConfig.textFormat` together with `effort`, with adaptive thinking, and next to a strict tool. Fable 5 and 5.1 rejected every request, with or without `outputConfig.textFormat`, with `data retention mode 'default' is not available for this model`, and the other three profiles have reached end of life.
302
+ A probe on the same day sent one request per path to 17 Claude inference profiles through the `eu-west-1` endpoint. Every profile that served the probe's requests, 12 of the 17, accepted `outputConfig.textFormat`: Claude Haiku 4.5 and 5.5, Opus 4.5, 4.6, 4.7, 4.8, 5, and 5.5, and Sonnet 4.5, 4.6, 5, and 5.5. On Opus 4.8, Opus 5.5, and Sonnet 5.5 it also accepted `outputConfig.textFormat` together with `effort`, with adaptive thinking, and next to a strict tool. Fable 5 and 5.1 rejected every request, with or without `outputConfig.textFormat`, with `data retention mode 'default' is not available for this model`, and the other three profiles have reached end of life.
222
303
 
223
- The provider keeps a list of models that get the schema as a tool instead: the Claude 3 models and Claude Sonnet 4 and Opus 4. The list comes from Anthropic's table of models with structured output. The probe could not check it on Bedrock. Claude 3 Haiku, Claude 3 Sonnet, and Sonnet 4 have reached end of life in `eu-west-1`, and the probe did not try the other models on the list. The tool replaces the tools you pass, and the tool's input becomes the object. If the model calls the tool more than once, the first call is the object. If the model answers in text instead of calling the tool, `effect/ai` decodes the text as the object.
304
+ The provider keeps a list of Claude models that get the forced tool instead: the Claude 3 models and Claude Sonnet 4 and Opus 4. The list comes from Anthropic's table of models with structured output. The probe could not check it on Bedrock. Claude 3 Haiku, Claude 3 Sonnet, and Sonnet 4 have reached end of life in `eu-west-1`, and the probe did not try the other models on the list.
224
305
 
225
- On the tool path the provider forces the model to call the tool. In the probe, Claude Opus 5.5 and Sonnet 5.5 rejected a forced `toolChoice` with `tool_choice: type "tool" and "any" are not supported for this model`. The provider also treats Fable 5.1 and Mythos 5.1 as rejecting it, which nobody has checked on Bedrock. For these four models the provider leaves the tool unforced and adds a system prompt line that tells the model to call it. They take the tool path only when `structuredOutputs` is `false`, or when the prompt holds tool calls and no tools go out.
306
+ In the probe, Claude Opus 5.5 and Sonnet 5.5 rejected a forced `toolChoice` with `tool_choice: type "tool" and "any" are not supported for this model`. The provider also treats Fable 5.1 and Mythos 5.1 as rejecting it, which nobody has checked on Bedrock. For these four models the provider leaves the tool unforced and adds the system prompt line. They take the tool path only when `structuredOutputs` is `false`, or when the prompt holds tool calls and no tools go out.
226
307
 
227
308
  When the prompt holds tool calls or tool results and the request offers no tools, `generateObject` uses the tool on every model, even with `structuredOutputs: true`. Converse rejects such a request unless it carries a `toolConfig`, and the object tool is one.
228
309
 
229
- The provider matches the model ID against its lists. Two config fields override the match, and the provider sends neither to Bedrock:
310
+ Two config fields override what the model ID decides, and the provider sends neither to Bedrock:
230
311
 
231
312
  - `structuredOutputs` set to `true` sends `outputConfig.textFormat` to any model, except when the prompt holds tool calls and no tools go out. Set to `false`, it sends the tool.
232
313
  - `forcedToolChoice` set to `true` forces the tool, and set to `false` leaves it unforced. It has an effect only on the tool path. It does not change a `toolChoice` you pass with your own tools: `"required"` and a named tool go to Bedrock as `any` and `tool`, and the probe's error from Opus 5.5 and Sonnet 5.5 names both as unsupported.
233
314
 
234
- A model ID that does not name the model, such as an application inference profile ARN, gets `outputConfig.textFormat`, or a forced tool when `structuredOutputs` is `false`. With tool history and no tools it also gets a forced tool by default. Behind an ARN that wraps Opus 5.5 or Sonnet 5.5, set `forcedToolChoice: false` for that case. To send the unforced tool through such an ARN in every case, set both fields:
315
+ A model ID that does not name the model, such as an application inference profile ARN, gets what a Claude model gets: `outputConfig.textFormat`, or a forced tool when `structuredOutputs` is `false`. With tool history and no tools it also gets a forced tool by default. Behind an ARN that wraps Opus 5.5 or Sonnet 5.5, set `forcedToolChoice: false` for that case. Behind an ARN that wraps a model of another provider, set `structuredOutputs: false`, and also `forcedToolChoice: false` for a Meta or Writer model. To send the unforced tool through such an ARN in every case, set both fields:
235
316
 
236
317
  ```ts
237
318
  import { AmazonBedrockLanguageModel } from "@wmaurer/effect-ai-amazon-bedrock"
@@ -1,10 +1,27 @@
1
+ /**
2
+ * The `AmazonBedrockClient` module defines the low-level Effect service used to
3
+ * call Amazon Bedrock's Converse API. It wraps an `HttpClient` with SigV4
4
+ * request signing (via `aws4fetch`), request defaults, response decoding, and
5
+ * error mapping to the unified `AiError` type.
6
+ *
7
+ * **Mental model**
8
+ *
9
+ * `HttpClient.HttpClient` provides the transport. {@link make} turns explicit
10
+ * {@link Options} into an {@link AmazonBedrockClient} service, while
11
+ * {@link layer} and {@link layerConfig} provide that service as a layer. The
12
+ * service exposes handwritten helpers for the `converse` (non-streaming) and
13
+ * `converse-stream` (streaming) endpoints.
14
+ *
15
+ * @stability unstable
16
+ * @since 0.1.0
17
+ */
1
18
  import type * as AiError from "effect/ai/AiError";
2
19
  import type * as Config from "effect/Config";
3
20
  import * as Context from "effect/Context";
4
21
  import * as Effect from "effect/Effect";
5
22
  import * as HttpClient from "effect/http/HttpClient";
6
23
  import * as Layer from "effect/Layer";
7
- import * as Redacted from "effect/Redacted";
24
+ import type * as Redacted from "effect/Redacted";
8
25
  import * as Stream from "effect/Stream";
9
26
  import type { ConverseRequest } from "./AmazonBedrockSchema.ts";
10
27
  import { ConverseResponse, ConverseResponseStreamEvent } from "./AmazonBedrockSchema.ts";
@@ -14,7 +31,7 @@ import { ConverseResponse, ConverseResponseStreamEvent } from "./AmazonBedrockSc
14
31
  *
15
32
  * @stability unstable
16
33
  * @category models
17
- * @since 4.0.0
34
+ * @since 0.1.0
18
35
  */
19
36
  export interface Service {
20
37
  /**
@@ -38,18 +55,59 @@ declare const AmazonBedrockClient_base: Context.ServiceClass<AmazonBedrockClient
38
55
  *
39
56
  * @stability unstable
40
57
  * @category services
41
- * @since 4.0.0
58
+ * @since 0.1.0
42
59
  */
43
60
  export declare class AmazonBedrockClient extends AmazonBedrockClient_base {
44
61
  }
45
62
  /**
46
- * Configuration for creating an Amazon Bedrock client.
63
+ * SigV4 credentials as a {@link CredentialProvider} returns them. The shape is
64
+ * the AWS SDK's `AwsCredentialIdentity`, so the SDK's providers fit without an
65
+ * adapter.
47
66
  *
48
67
  * @stability unstable
49
68
  * @category options
50
- * @since 4.0.0
69
+ * @since 0.2.0
51
70
  */
52
- export type Options = {
71
+ export interface Credentials {
72
+ readonly accessKeyId: string;
73
+ readonly secretAccessKey: string;
74
+ /** An empty token is sent as no token. */
75
+ readonly sessionToken?: string | undefined;
76
+ /** The client asks the provider for new credentials five minutes before this time. */
77
+ readonly expiration?: Date | undefined;
78
+ }
79
+ /**
80
+ * Supplies SigV4 credentials that can change while the program runs.
81
+ *
82
+ * **Details**
83
+ *
84
+ * A provider is one of these:
85
+ *
86
+ * - A function in the shape of the AWS SDK's credential providers, such as
87
+ * `fromNodeProviderChain()` from `@aws-sdk/credential-providers`. A rejected
88
+ * promise fails the request with an `AuthenticationError` of kind
89
+ * `"Unknown"`. After Bedrock rejects the credentials, the client calls the
90
+ * function with `{ forceRefresh: true }`.
91
+ * - An `Effect`. It can fail with an `AiError`, which the request fails with
92
+ * unchanged, or with an `AiError` reason, such as an `AuthenticationError`,
93
+ * which the client wraps in an `AiError`. The services that it requires
94
+ * become requirements of the layer. It runs with the services of the
95
+ * request first, and the services that the layer was built with fill in the
96
+ * rest.
97
+ *
98
+ * The client runs the provider at most once at a time, and keeps the
99
+ * credentials until five minutes before their `expiration`. Credentials with
100
+ * no `expiration` are kept until Bedrock rejects them. Keys that are empty fail
101
+ * the request with an `AuthenticationError` of kind `"MissingKey"`.
102
+ *
103
+ * @stability unstable
104
+ * @category options
105
+ * @since 0.2.0
106
+ */
107
+ export type CredentialProvider<R = never> = ((options?: {
108
+ readonly forceRefresh?: boolean;
109
+ }) => PromiseLike<Credentials>) | Effect.Effect<Credentials, AiError.AiError | AiError.AiErrorReason, R>;
110
+ interface CommonOptions {
53
111
  /**
54
112
  * The base URL for the Bedrock runtime API. Override to use a proxy or a
55
113
  * different endpoint.
@@ -57,9 +115,6 @@ export type Options = {
57
115
  * @default `https://bedrock-runtime.${region}.amazonaws.com`
58
116
  */
59
117
  readonly apiUrl?: string | undefined;
60
- readonly accessKeyId: string;
61
- readonly secretAccessKey: Redacted.Redacted<string>;
62
- readonly sessionToken?: Redacted.Redacted<string> | undefined;
63
118
  /**
64
119
  * The AWS region.
65
120
  *
@@ -67,38 +122,95 @@ export type Options = {
67
122
  */
68
123
  readonly region?: string | undefined;
69
124
  readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined;
70
- };
125
+ }
126
+ interface StaticKeys {
127
+ readonly accessKeyId: string;
128
+ readonly secretAccessKey: Redacted.Redacted<string>;
129
+ readonly sessionToken?: Redacted.Redacted<string> | undefined;
130
+ readonly credentialProvider?: never;
131
+ readonly apiKey?: never;
132
+ }
133
+ interface ProvidedKeys<R> {
134
+ readonly credentialProvider: CredentialProvider<R>;
135
+ readonly accessKeyId?: never;
136
+ readonly secretAccessKey?: never;
137
+ readonly sessionToken?: never;
138
+ readonly apiKey?: never;
139
+ }
140
+ interface ApiKey {
141
+ /** A Bedrock API key, sent as `Authorization: Bearer <key>` in place of a SigV4 signature. */
142
+ readonly apiKey: Redacted.Redacted<string>;
143
+ readonly accessKeyId?: never;
144
+ readonly secretAccessKey?: never;
145
+ readonly sessionToken?: never;
146
+ readonly credentialProvider?: never;
147
+ }
148
+ /**
149
+ * Configuration for creating an Amazon Bedrock client.
150
+ *
151
+ * **Details**
152
+ *
153
+ * The options take exactly one way to authenticate:
154
+ *
155
+ * - `accessKeyId` and `secretAccessKey`, and an optional `sessionToken`.
156
+ * - `credentialProvider`, a {@link CredentialProvider}.
157
+ * - `apiKey`, a Bedrock API key.
158
+ *
159
+ * @stability unstable
160
+ * @category options
161
+ * @since 0.1.0
162
+ */
163
+ export type Options<R = never> = CommonOptions & (StaticKeys | ProvidedKeys<R> | ApiKey);
164
+ /**
165
+ * The {@link Options} of {@link layerConfig}, with a `Config` for each value.
166
+ * `credentialProvider` and `transformClient` are not configuration, so they
167
+ * take the same values as in {@link Options}.
168
+ *
169
+ * @stability unstable
170
+ * @category options
171
+ * @since 0.2.0
172
+ */
173
+ export type ConfigOptions<R = never> = {
174
+ readonly apiUrl?: Config.Config<string> | undefined;
175
+ readonly region?: Config.Config<string> | undefined;
176
+ readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined;
177
+ } & ({
178
+ readonly accessKeyId: Config.Config<string>;
179
+ readonly secretAccessKey: Config.Config<Redacted.Redacted<string>>;
180
+ readonly sessionToken?: Config.Config<Redacted.Redacted<string>> | undefined;
181
+ readonly credentialProvider?: never;
182
+ readonly apiKey?: never;
183
+ } | ProvidedKeys<R> | {
184
+ readonly apiKey: Config.Config<Redacted.Redacted<string>>;
185
+ readonly accessKeyId?: never;
186
+ readonly secretAccessKey?: never;
187
+ readonly sessionToken?: never;
188
+ readonly credentialProvider?: never;
189
+ });
71
190
  /**
72
191
  * Creates an Amazon Bedrock client service with the given options.
73
192
  *
74
193
  * @stability unstable
75
194
  * @category constructors
76
- * @since 4.0.0
195
+ * @since 0.1.0
77
196
  */
78
- export declare const make: (options: Options) => Effect.Effect<Service, never, HttpClient.HttpClient>;
197
+ export declare const make: <R = never>(options: Options<R>) => Effect.Effect<Service, never, HttpClient.HttpClient | R>;
79
198
  /**
80
199
  * Creates a layer for the Amazon Bedrock client with the given options.
81
200
  *
82
201
  * @stability unstable
83
202
  * @category layers
84
- * @since 4.0.0
203
+ * @since 0.1.0
85
204
  */
86
- export declare const layer: (options: Options) => Layer.Layer<AmazonBedrockClient, never, HttpClient.HttpClient>;
205
+ export declare const layer: <R = never>(options: Options<R>) => Layer.Layer<AmazonBedrockClient, never, HttpClient.HttpClient | R>;
87
206
  /**
88
207
  * Creates a layer for the Amazon Bedrock client, loading credentials and other
89
208
  * settings via Effect's `Config` module.
90
209
  *
91
210
  * @stability unstable
92
211
  * @category layers
93
- * @since 4.0.0
212
+ * @since 0.1.0
94
213
  */
95
- export declare const layerConfig: (options: {
96
- readonly apiUrl?: Config.Config<string> | undefined;
97
- readonly accessKeyId: Config.Config<string>;
98
- readonly secretAccessKey: Config.Config<Redacted.Redacted<string>>;
99
- readonly sessionToken?: Config.Config<Redacted.Redacted<string>> | undefined;
100
- readonly region?: Config.Config<string> | undefined;
101
- readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined;
102
- }) => Layer.Layer<AmazonBedrockClient, Config.ConfigError, HttpClient.HttpClient>;
214
+ export declare const layerConfig: <R = never>(options: ConfigOptions<R>) => Layer.Layer<AmazonBedrockClient, Config.ConfigError, HttpClient.HttpClient | R>;
103
215
  export {};
104
216
  //# sourceMappingURL=AmazonBedrockClient.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AmazonBedrockClient.d.ts","sourceRoot":"","sources":["../src/AmazonBedrockClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AmazonBedrockClient.d.ts","sourceRoot":"","sources":["../src/AmazonBedrockClient.ts"],"names":[],"mappings":"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"}
@@ -1,21 +1,3 @@
1
- /**
2
- * The `AmazonBedrockClient` module defines the low-level Effect service used to
3
- * call Amazon Bedrock's Converse API. It wraps an `HttpClient` with SigV4
4
- * request signing (via `aws4fetch`), request defaults, response decoding, and
5
- * error mapping to the unified `AiError` type.
6
- *
7
- * **Mental model**
8
- *
9
- * `HttpClient.HttpClient` provides the transport. {@link make} turns explicit
10
- * {@link Options} into an {@link AmazonBedrockClient} service, while
11
- * {@link layer} and {@link layerConfig} provide that service as a layer. The
12
- * service exposes handwritten helpers for the `converse` (non-streaming) and
13
- * `converse-stream` (streaming) endpoints.
14
- *
15
- * @stability unstable
16
- * @since 4.0.0
17
- */
18
- import { AwsV4Signer } from "aws4fetch";
19
1
  import * as Array from "effect/Array";
20
2
  import * as Context from "effect/Context";
21
3
  import * as Effect from "effect/Effect";
@@ -26,12 +8,11 @@ import * as HttpClient from "effect/http/HttpClient";
26
8
  import * as HttpClientRequest from "effect/http/HttpClientRequest";
27
9
  import * as HttpClientResponse from "effect/http/HttpClientResponse";
28
10
  import * as Layer from "effect/Layer";
29
- import * as Predicate from "effect/Predicate";
30
- import * as Redacted from "effect/Redacted";
31
11
  import * as Stream from "effect/Stream";
32
12
  import { AmazonBedrockConfig } from "./AmazonBedrockConfig.js";
33
13
  import * as EventStream from "./AmazonBedrockEventStream.js";
34
14
  import { ConverseResponse, ConverseResponseStreamEvent } from "./AmazonBedrockSchema.js";
15
+ import * as Authentication from "./internal/credentials.js";
35
16
  import * as Errors from "./internal/errors.js";
36
17
  // =============================================================================
37
18
  // Service Identifier
@@ -41,7 +22,7 @@ import * as Errors from "./internal/errors.js";
41
22
  *
42
23
  * @stability unstable
43
24
  * @category services
44
- * @since 4.0.0
25
+ * @since 0.1.0
45
26
  */
46
27
  export class AmazonBedrockClient extends /*#__PURE__*/Context.Service()("@wmaurer/effect-ai-amazon-bedrock/AmazonBedrockClient") {}
47
28
  // =============================================================================
@@ -57,48 +38,35 @@ const withRedactedHeaders = /*#__PURE__*/Effect.updateService(Headers.CurrentRed
57
38
  *
58
39
  * @stability unstable
59
40
  * @category constructors
60
- * @since 4.0.0
41
+ * @since 0.1.0
61
42
  */
62
- export const make = /*#__PURE__*/Effect.fnUntraced(function* (options) {
43
+ export const make = options => makeUnsafe(options);
44
+ // Takes each authentication field as optional, as `layerConfig` reads them.
45
+ // `Authentication.make` dies on a mix of the ways to authenticate.
46
+ const makeUnsafe = options => Effect.gen(function* () {
63
47
  const region = options.region ?? "us-east-1";
48
+ const authenticator = yield* Authentication.make(options);
64
49
  const baseClient = yield* HttpClient.HttpClient;
65
- const httpClient = baseClient.pipe(HttpClient.mapRequest(request => request.pipe(HttpClientRequest.prependUrl(options.apiUrl ?? `https://bedrock-runtime.${region}.amazonaws.com`), HttpClientRequest.acceptJson)), HttpClient.mapRequestEffect(Effect.fnUntraced(function* (request) {
66
- const originalHeaders = request.headers;
67
- const signer = new AwsV4Signer({
68
- service: "bedrock",
69
- url: request.url,
70
- method: request.method,
71
- headers: Object.entries(originalHeaders),
72
- body: prepareBody(request.body),
73
- region,
74
- accessKeyId: options.accessKeyId,
75
- secretAccessKey: Redacted.value(options.secretAccessKey),
76
- ...(options.sessionToken ? {
77
- sessionToken: Redacted.value(options.sessionToken)
78
- } : {})
79
- });
80
- const {
81
- headers: signedHeaders
82
- } = yield* Effect.promise(() => signer.sign());
83
- const headers = Headers.merge(originalHeaders, Headers.fromInput(signedHeaders));
84
- return HttpClientRequest.setHeaders(request, headers);
85
- })), options.transformClient ? options.transformClient : identity);
50
+ const httpClient = baseClient.pipe(HttpClient.mapRequest(request => request.pipe(HttpClientRequest.prependUrl(options.apiUrl ?? `https://bedrock-runtime.${region}.amazonaws.com`), HttpClientRequest.acceptJson)), HttpClient.mapRequestEffect(Authentication.authenticateRequest(region)), options.transformClient ? options.transformClient : identity);
86
51
  const httpClientOk = HttpClient.filterStatusOk(httpClient);
87
52
  const withConfigClient = Effect.fnUntraced(function* () {
88
53
  const config = yield* AmazonBedrockConfig.getOrUndefined;
89
54
  return config?.transformClient ? config.transformClient(httpClientOk) : httpClientOk;
90
55
  });
56
+ const resolveCredentials = method => Effect.mapError(authenticator.resolve, Errors.mapCredentialProviderError(method));
91
57
  const converse = ({
92
58
  payload: {
93
59
  modelId,
94
60
  ...body
95
61
  }
96
- }) => Effect.flatMap(withConfigClient(), client => {
62
+ }) => Effect.gen(function* () {
63
+ const client = yield* withConfigClient();
64
+ const credentials = yield* resolveCredentials("converse");
97
65
  const request = HttpClientRequest.post(`/model/${modelId}/converse`, {
98
66
  body: HttpBody.jsonUnsafe(body)
99
67
  });
100
- return client.execute(request).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(ConverseResponse)), Effect.catchTags({
101
- HttpClientError: error => Errors.mapHttpClientError(error, "converse"),
68
+ return yield* client.execute(request).pipe(Effect.provideService(Authentication.CurrentRequestCredentials, credentials), Effect.flatMap(HttpClientResponse.schemaBodyJson(ConverseResponse)), Effect.catchTags({
69
+ HttpClientError: error => Errors.mapHttpClientError(error, "converse").pipe(Effect.tapError(Authentication.invalidateIfRejected(authenticator, credentials))),
102
70
  SchemaError: error => Effect.fail(Errors.mapSchemaError(error, "converse"))
103
71
  }), withRedactedHeaders);
104
72
  });
@@ -111,17 +79,21 @@ export const make = /*#__PURE__*/Effect.fnUntraced(function* (options) {
111
79
  const request = HttpClientRequest.post(`/model/${modelId}/converse-stream`, {
112
80
  body: HttpBody.jsonUnsafe(body)
113
81
  });
114
- return Stream.unwrap(Effect.map(withConfigClient(), client => client.execute(request).pipe(Effect.map(response => response.stream), Stream.unwrap, Stream.pipeThroughChannel(EventStream.makeChannel(ConverseResponseStreamEvent)), Stream.catchTags({
115
- HttpClientError: error => Stream.fromEffect(Errors.mapHttpClientError(error, "converseStream")),
116
- SchemaError: error => Stream.fail(Errors.mapSchemaError(error, "converseStream")),
117
- EventStreamError: error => Stream.fail(Errors.mapEventStreamError(error, "converseStream"))
118
- }), Stream.updateService(Headers.CurrentRedactedNames, appendRedactedHeaders))));
82
+ return Stream.unwrap(Effect.gen(function* () {
83
+ const client = yield* withConfigClient();
84
+ const credentials = yield* resolveCredentials("converseStream");
85
+ return client.execute(request).pipe(Effect.provideService(Authentication.CurrentRequestCredentials, credentials), Effect.map(response => response.stream), Stream.unwrap, Stream.pipeThroughChannel(EventStream.makeChannel(ConverseResponseStreamEvent)), Stream.catchTags({
86
+ HttpClientError: error => Stream.fromEffect(Errors.mapHttpClientError(error, "converseStream").pipe(Effect.tapError(Authentication.invalidateIfRejected(authenticator, credentials)))),
87
+ SchemaError: error => Stream.fail(Errors.mapSchemaError(error, "converseStream")),
88
+ EventStreamError: error => Stream.fail(Errors.mapEventStreamError(error, "converseStream"))
89
+ }), Stream.updateService(Headers.CurrentRedactedNames, appendRedactedHeaders));
90
+ }));
119
91
  };
120
92
  return AmazonBedrockClient.of({
121
93
  converse,
122
94
  converseStream
123
95
  });
124
- }, withRedactedHeaders);
96
+ }).pipe(withRedactedHeaders);
125
97
  // =============================================================================
126
98
  // Layers
127
99
  // =============================================================================
@@ -130,7 +102,7 @@ export const make = /*#__PURE__*/Effect.fnUntraced(function* (options) {
130
102
  *
131
103
  * @stability unstable
132
104
  * @category layers
133
- * @since 4.0.0
105
+ * @since 0.1.0
134
106
  */
135
107
  export const layer = options => Layer.effect(AmazonBedrockClient, make(options));
136
108
  /**
@@ -139,47 +111,19 @@ export const layer = options => Layer.effect(AmazonBedrockClient, make(options))
139
111
  *
140
112
  * @stability unstable
141
113
  * @category layers
142
- * @since 4.0.0
114
+ * @since 0.1.0
143
115
  */
144
116
  export const layerConfig = options => Layer.effect(AmazonBedrockClient, Effect.gen(function* () {
145
- const apiUrl = Predicate.isNotUndefined(options.apiUrl) ? yield* options.apiUrl : undefined;
146
- const accessKeyId = yield* options.accessKeyId;
147
- const secretAccessKey = yield* options.secretAccessKey;
148
- const sessionToken = Predicate.isNotUndefined(options.sessionToken) ? yield* options.sessionToken : undefined;
149
- const region = Predicate.isNotUndefined(options.region) ? yield* options.region : undefined;
150
- return yield* make({
151
- apiUrl,
152
- accessKeyId,
153
- secretAccessKey,
154
- sessionToken,
155
- region,
117
+ return yield* makeUnsafe({
118
+ apiUrl: yield* readOptional(options.apiUrl),
119
+ region: yield* readOptional(options.region),
120
+ accessKeyId: yield* readOptional(options.accessKeyId),
121
+ secretAccessKey: yield* readOptional(options.secretAccessKey),
122
+ sessionToken: yield* readOptional(options.sessionToken),
123
+ apiKey: yield* readOptional(options.apiKey),
124
+ credentialProvider: options.credentialProvider,
156
125
  transformClient: options.transformClient
157
126
  });
158
127
  }));
159
- // =============================================================================
160
- // Utilities
161
- // =============================================================================
162
- const prepareBody = body => {
163
- switch (body._tag) {
164
- case "Empty":
165
- {
166
- return "";
167
- }
168
- case "Raw":
169
- case "Uint8Array":
170
- {
171
- if (typeof body.body === "string") {
172
- return body.body;
173
- }
174
- if (body.body instanceof Uint8Array) {
175
- return new TextDecoder().decode(body.body);
176
- }
177
- if (body.body instanceof ArrayBuffer) {
178
- return new TextDecoder().decode(body.body);
179
- }
180
- return JSON.stringify(body.body);
181
- }
182
- }
183
- throw new Error("Unsupported HttpBody: " + body._tag);
184
- };
128
+ const readOptional = config => config === undefined ? Effect.undefined : config;
185
129
  //# sourceMappingURL=AmazonBedrockClient.js.map