@tanstack/ai-bedrock 0.0.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.
Files changed (64) hide show
  1. package/README.md +95 -0
  2. package/dist/esm/adapters/converse-text.d.ts +93 -0
  3. package/dist/esm/adapters/converse-text.js +430 -0
  4. package/dist/esm/adapters/converse-text.js.map +1 -0
  5. package/dist/esm/adapters/responses-text.d.ts +23 -0
  6. package/dist/esm/adapters/responses-text.js +22 -0
  7. package/dist/esm/adapters/responses-text.js.map +1 -0
  8. package/dist/esm/adapters/text.d.ts +30 -0
  9. package/dist/esm/adapters/text.js +38 -0
  10. package/dist/esm/adapters/text.js.map +1 -0
  11. package/dist/esm/converse/message-converter.d.ts +14 -0
  12. package/dist/esm/converse/message-converter.js +181 -0
  13. package/dist/esm/converse/message-converter.js.map +1 -0
  14. package/dist/esm/converse/provider-options.d.ts +19 -0
  15. package/dist/esm/converse/stream-processor.d.ts +46 -0
  16. package/dist/esm/converse/stream-processor.js +203 -0
  17. package/dist/esm/converse/stream-processor.js.map +1 -0
  18. package/dist/esm/converse/structured-output.d.ts +8 -0
  19. package/dist/esm/converse/structured-output.js +20 -0
  20. package/dist/esm/converse/structured-output.js.map +1 -0
  21. package/dist/esm/converse/tool-converter.d.ts +11 -0
  22. package/dist/esm/converse/tool-converter.js +21 -0
  23. package/dist/esm/converse/tool-converter.js.map +1 -0
  24. package/dist/esm/index.d.ts +29 -0
  25. package/dist/esm/index.js +62 -0
  26. package/dist/esm/index.js.map +1 -0
  27. package/dist/esm/message-types.d.ts +25 -0
  28. package/dist/esm/model-catalog.generated.d.ts +127 -0
  29. package/dist/esm/model-catalog.generated.js +90 -0
  30. package/dist/esm/model-catalog.generated.js.map +1 -0
  31. package/dist/esm/model-meta.d.ts +38 -0
  32. package/dist/esm/model-meta.js +14 -0
  33. package/dist/esm/model-meta.js.map +1 -0
  34. package/dist/esm/text/responses-provider-options.d.ts +29 -0
  35. package/dist/esm/text/text-provider-options.d.ts +36 -0
  36. package/dist/esm/utils/auth.d.ts +20 -0
  37. package/dist/esm/utils/auth.js +49 -0
  38. package/dist/esm/utils/auth.js.map +1 -0
  39. package/dist/esm/utils/client.d.ts +19 -0
  40. package/dist/esm/utils/client.js +35 -0
  41. package/dist/esm/utils/client.js.map +1 -0
  42. package/dist/esm/utils/index.d.ts +1 -0
  43. package/dist/esm/utils/openai-sigv4-fetch.d.ts +10 -0
  44. package/dist/esm/utils/openai-sigv4-fetch.js +35 -0
  45. package/dist/esm/utils/openai-sigv4-fetch.js.map +1 -0
  46. package/package.json +67 -0
  47. package/src/adapters/converse-text.ts +603 -0
  48. package/src/adapters/responses-text.ts +73 -0
  49. package/src/adapters/text.ts +97 -0
  50. package/src/converse/message-converter.ts +268 -0
  51. package/src/converse/provider-options.ts +19 -0
  52. package/src/converse/stream-processor.ts +309 -0
  53. package/src/converse/structured-output.ts +24 -0
  54. package/src/converse/tool-converter.ts +49 -0
  55. package/src/index.ts +190 -0
  56. package/src/message-types.ts +24 -0
  57. package/src/model-catalog.generated.ts +88 -0
  58. package/src/model-meta.ts +69 -0
  59. package/src/text/responses-provider-options.ts +28 -0
  60. package/src/text/text-provider-options.ts +33 -0
  61. package/src/utils/auth.ts +80 -0
  62. package/src/utils/client.ts +62 -0
  63. package/src/utils/index.ts +7 -0
  64. package/src/utils/openai-sigv4-fetch.ts +55 -0
@@ -0,0 +1,24 @@
1
+ import type { ToolConfiguration } from '@aws-sdk/client-bedrock-runtime'
2
+ import type { DocumentType } from '@smithy/types'
3
+
4
+ export const STRUCTURED_TOOL_NAME = 'structured_output'
5
+
6
+ /**
7
+ * Converse has no native json_schema response_format. Structured output is
8
+ * achieved by forcing a single tool whose input schema is the requested output
9
+ * schema; the model's tool-use `input` is the structured result.
10
+ */
11
+ export function buildStructuredToolConfig(schema: unknown): ToolConfiguration {
12
+ return {
13
+ tools: [
14
+ {
15
+ toolSpec: {
16
+ name: STRUCTURED_TOOL_NAME,
17
+ description: 'Return the final answer as structured JSON.',
18
+ inputSchema: { json: schema as DocumentType },
19
+ },
20
+ },
21
+ ],
22
+ toolChoice: { tool: { name: STRUCTURED_TOOL_NAME } },
23
+ }
24
+ }
@@ -0,0 +1,49 @@
1
+ import type {
2
+ ToolChoice,
3
+ ToolConfiguration,
4
+ } from '@aws-sdk/client-bedrock-runtime'
5
+ import type { DocumentType } from '@smithy/types'
6
+
7
+ export interface ConverseToolInput {
8
+ name: string
9
+ description?: string
10
+ inputSchema: unknown
11
+ }
12
+
13
+ export type ToolChoiceInput =
14
+ | 'auto'
15
+ | 'required'
16
+ | 'none'
17
+ | { type: 'tool'; name: string }
18
+
19
+ export function toToolConfig(
20
+ tools: Array<ConverseToolInput>,
21
+ choice: ToolChoiceInput | undefined,
22
+ ): ToolConfiguration | undefined {
23
+ if (!tools.length) return undefined
24
+ // `none` means "don't call tools" — omit the tool config entirely, since
25
+ // Bedrock treats a present tool config with no `toolChoice` as auto.
26
+ if (choice === 'none') return undefined
27
+ const toolChoice = mapChoice(choice)
28
+ return {
29
+ tools: tools.map((t) => ({
30
+ toolSpec: {
31
+ name: t.name,
32
+ ...(t.description ? { description: t.description } : {}),
33
+ inputSchema: { json: t.inputSchema as DocumentType },
34
+ },
35
+ })),
36
+ ...(toolChoice ? { toolChoice } : {}),
37
+ }
38
+ }
39
+
40
+ function mapChoice(
41
+ choice: ToolChoiceInput | undefined,
42
+ ): ToolChoice | undefined {
43
+ if (!choice || choice === 'auto') return { auto: {} }
44
+ if (choice === 'required') return { any: {} }
45
+ // `none` is handled earlier in toToolConfig (omits the tool config); this
46
+ // branch keeps the string union narrowed so `choice.name` type-checks.
47
+ if (choice === 'none') return undefined
48
+ return { tool: { name: choice.name } }
49
+ }
package/src/index.ts ADDED
@@ -0,0 +1,190 @@
1
+ /**
2
+ * @module @tanstack/ai-bedrock
3
+ *
4
+ * Amazon Bedrock adapter for TanStack AI via Bedrock's OpenAI-compatible APIs
5
+ * and the native Converse API. The public `bedrockText` / `createBedrockText`
6
+ * factory branches between the Converse adapter (DEFAULT), the Chat Completions
7
+ * adapter (`api: 'chat'`), and the Responses adapter (`api: 'responses'`).
8
+ */
9
+ import { BedrockTextAdapter } from './adapters/text'
10
+ import { BedrockResponsesTextAdapter } from './adapters/responses-text'
11
+ import { BedrockConverseTextAdapter } from './adapters/converse-text'
12
+ import { BEDROCK_CHAT_MODELS, BEDROCK_RESPONSES_MODELS } from './model-meta'
13
+ import type { BedrockTextConfig } from './adapters/text'
14
+ import type { BedrockResponsesConfig } from './adapters/responses-text'
15
+ import type { BedrockConverseConfig } from './adapters/converse-text'
16
+ import type { BedrockClientConfig } from './utils'
17
+ import type {
18
+ BedrockChatModels,
19
+ BedrockConverseModels,
20
+ BedrockResponsesModels,
21
+ } from './model-meta'
22
+
23
+ /** Config for the branching factory's converse mode (default, or api: 'converse'). */
24
+ export type BedrockConverseApiConfig = BedrockConverseConfig & {
25
+ api?: 'converse'
26
+ }
27
+ /** Config for the branching factory's chat mode (api: 'chat' required). */
28
+ export type BedrockChatApiConfig = BedrockTextConfig & {
29
+ api: 'chat'
30
+ }
31
+ /** Config for the branching factory's responses mode (api: 'responses' required). */
32
+ export type BedrockResponsesApiConfig = BedrockResponsesConfig & {
33
+ api: 'responses'
34
+ }
35
+
36
+ type AnyBedrockAdapter =
37
+ | BedrockConverseTextAdapter<BedrockConverseModels>
38
+ | BedrockTextAdapter<BedrockChatModels>
39
+ | BedrockResponsesTextAdapter<BedrockResponsesModels>
40
+
41
+ /** Cast-free runtime guard: is this model in the Responses-capable subset? */
42
+ function isResponsesModel(model: string): model is BedrockResponsesModels {
43
+ return BEDROCK_RESPONSES_MODELS.some((m) => m === model)
44
+ }
45
+
46
+ /** Cast-free runtime guard: is this model in the Chat-capable subset? */
47
+ function isChatModel(model: string): model is BedrockChatModels {
48
+ return BEDROCK_CHAT_MODELS.some((m) => m === model)
49
+ }
50
+
51
+ /** Strip the `api` discriminator from a config without an unused-var lint error. */
52
+ function stripApi<T extends { api?: unknown }>(config: T): Omit<T, 'api'> {
53
+ const { api, ...rest } = config
54
+ void api
55
+ return rest
56
+ }
57
+
58
+ /**
59
+ * Shared branching used by both public factories. Constructs the adapter
60
+ * classes directly so their constructors run the full auth cascade lazily
61
+ * (config.apiKey → BEDROCK_API_KEY → AWS_BEARER_TOKEN_BEDROCK → SigV4). No
62
+ * eager env-key fetch here, so `auth: 'sigv4'` never throws for a missing key.
63
+ *
64
+ * Default path → Converse adapter; opt-in via `api: 'chat'` or `api: 'responses'`.
65
+ */
66
+ function build(
67
+ model: BedrockConverseModels,
68
+ config?: BedrockClientConfig & { api?: 'converse' | 'chat' | 'responses' },
69
+ ): AnyBedrockAdapter {
70
+ if (config?.api === 'responses') {
71
+ const rest = stripApi(config)
72
+ if (!isResponsesModel(model)) {
73
+ throw new Error(
74
+ `Model "${model}" is not available on the Bedrock Responses API. ` +
75
+ `Responses-capable models: ${BEDROCK_RESPONSES_MODELS.join(', ')}.`,
76
+ )
77
+ }
78
+ return new BedrockResponsesTextAdapter(rest, model)
79
+ }
80
+ if (config?.api === 'chat') {
81
+ if (!isChatModel(model)) {
82
+ throw new Error(
83
+ `Model "${model}" is not available on the Bedrock Chat Completions API. ` +
84
+ `Chat-capable models: ${BEDROCK_CHAT_MODELS.join(', ')}.`,
85
+ )
86
+ }
87
+ return new BedrockTextAdapter(stripApi(config), model)
88
+ }
89
+ // Default + explicit 'converse'
90
+ return new BedrockConverseTextAdapter(config ? stripApi(config) : {}, model)
91
+ }
92
+
93
+ // --- createBedrockText: explicit key, overloaded on `api` ---
94
+ export function createBedrockText<TModel extends BedrockConverseModels>(
95
+ model: TModel,
96
+ apiKey: string,
97
+ config?: BedrockConverseApiConfig,
98
+ ): BedrockConverseTextAdapter<TModel>
99
+ export function createBedrockText<TModel extends BedrockChatModels>(
100
+ model: TModel,
101
+ apiKey: string,
102
+ config: BedrockChatApiConfig,
103
+ ): BedrockTextAdapter<TModel>
104
+ export function createBedrockText<TModel extends BedrockResponsesModels>(
105
+ model: TModel,
106
+ apiKey: string,
107
+ config: BedrockResponsesApiConfig,
108
+ ): BedrockResponsesTextAdapter<TModel>
109
+ export function createBedrockText(
110
+ model: BedrockConverseModels,
111
+ apiKey: string,
112
+ config?:
113
+ | BedrockConverseApiConfig
114
+ | BedrockChatApiConfig
115
+ | BedrockResponsesApiConfig,
116
+ ): AnyBedrockAdapter {
117
+ // Explicit apiKey is authoritative — spread config first so it can't override.
118
+ return build(model, { ...config, apiKey })
119
+ }
120
+
121
+ // --- bedrockText: env-key counterpart, same overloads ---
122
+ export function bedrockText<TModel extends BedrockConverseModels>(
123
+ model: TModel,
124
+ config?: BedrockConverseApiConfig,
125
+ ): BedrockConverseTextAdapter<TModel>
126
+ export function bedrockText<TModel extends BedrockChatModels>(
127
+ model: TModel,
128
+ config: BedrockChatApiConfig,
129
+ ): BedrockTextAdapter<TModel>
130
+ export function bedrockText<TModel extends BedrockResponsesModels>(
131
+ model: TModel,
132
+ config: BedrockResponsesApiConfig,
133
+ ): BedrockResponsesTextAdapter<TModel>
134
+ export function bedrockText(
135
+ model: BedrockConverseModels,
136
+ config?:
137
+ | BedrockConverseApiConfig
138
+ | BedrockChatApiConfig
139
+ | BedrockResponsesApiConfig,
140
+ ): AnyBedrockAdapter {
141
+ // No eager env-key fetch: the adapter constructor resolves auth lazily so
142
+ // SigV4 (and the env-key fallback) work without a forced API key here.
143
+ return build(model, config)
144
+ }
145
+
146
+ // --- Re-exports ---
147
+ export {
148
+ BedrockTextAdapter,
149
+ createBedrockChat,
150
+ type BedrockTextConfig,
151
+ type BedrockTextProviderOptions,
152
+ } from './adapters/text'
153
+ export {
154
+ BedrockResponsesTextAdapter,
155
+ createBedrockResponsesText,
156
+ type BedrockResponsesConfig,
157
+ type BedrockResponsesProviderOptions,
158
+ } from './adapters/responses-text'
159
+ export {
160
+ BedrockConverseTextAdapter,
161
+ createBedrockConverse,
162
+ type BedrockConverseConfig,
163
+ } from './adapters/converse-text'
164
+ export type { BedrockConverseProviderOptions } from './converse/provider-options'
165
+ export {
166
+ resolveBedrockAuth,
167
+ withBedrockDefaults,
168
+ type BedrockClientConfig,
169
+ type BedrockEndpoint,
170
+ type ResolvedBedrockAuth,
171
+ } from './utils'
172
+ export {
173
+ BEDROCK_CHAT_MODELS,
174
+ BEDROCK_RESPONSES_MODELS,
175
+ BEDROCK_CONVERSE_MODELS,
176
+ type BedrockChatModels,
177
+ type BedrockResponsesModels,
178
+ type BedrockConverseModels,
179
+ type BedrockChatModelProviderOptionsByName,
180
+ type BedrockChatModelToolCapabilitiesByName,
181
+ type BedrockModelInputModalitiesByName,
182
+ } from './model-meta'
183
+ export type {
184
+ BedrockMessageMetadataByModality,
185
+ BedrockTextMetadata,
186
+ BedrockImageMetadata,
187
+ BedrockAudioMetadata,
188
+ BedrockVideoMetadata,
189
+ BedrockDocumentMetadata,
190
+ } from './message-types'
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Bedrock content-part metadata by modality, used for type inference when
3
+ * constructing multimodal messages. Bedrock's OpenAI-compatible Chat
4
+ * Completions accepts the standard OpenAI image-detail hint; other modalities
5
+ * carry no extra metadata today.
6
+ */
7
+ export interface BedrockTextMetadata {}
8
+
9
+ export interface BedrockImageMetadata {
10
+ /** Image processing detail: 'auto' (default), 'low', or 'high'. */
11
+ detail?: 'auto' | 'low' | 'high'
12
+ }
13
+
14
+ export interface BedrockAudioMetadata {}
15
+ export interface BedrockVideoMetadata {}
16
+ export interface BedrockDocumentMetadata {}
17
+
18
+ export interface BedrockMessageMetadataByModality {
19
+ text: BedrockTextMetadata
20
+ image: BedrockImageMetadata
21
+ audio: BedrockAudioMetadata
22
+ video: BedrockVideoMetadata
23
+ document: BedrockDocumentMetadata
24
+ }
@@ -0,0 +1,88 @@
1
+ // GENERATED by scripts/fetch-bedrock-models.ts — do not edit by hand.
2
+ // Refresh: AWS_REGION=us-east-1 pnpm tsx scripts/fetch-bedrock-models.ts
3
+ export const GENERATED_BEDROCK_MODELS = [
4
+ {
5
+ id: 'openai.gpt-oss-120b-1:0',
6
+ input: ['text'],
7
+ output: ['text'],
8
+ apis: { converse: true, chat: true, responses: true },
9
+ },
10
+ {
11
+ id: 'openai.gpt-oss-20b-1:0',
12
+ input: ['text'],
13
+ output: ['text'],
14
+ apis: { converse: true, chat: true, responses: true },
15
+ },
16
+ {
17
+ id: 'us.anthropic.claude-sonnet-4-5-20250929-v1:0',
18
+ input: ['text', 'image', 'document'],
19
+ output: ['text'],
20
+ apis: { converse: true, chat: false, responses: false },
21
+ },
22
+ {
23
+ id: 'us.anthropic.claude-haiku-4-5-20251001-v1:0',
24
+ input: ['text', 'image', 'document'],
25
+ output: ['text'],
26
+ apis: { converse: true, chat: false, responses: false },
27
+ },
28
+ {
29
+ id: 'us.anthropic.claude-3-7-sonnet-20250219-v1:0',
30
+ input: ['text', 'image', 'document'],
31
+ output: ['text'],
32
+ apis: { converse: true, chat: false, responses: false },
33
+ },
34
+ {
35
+ id: 'us.anthropic.claude-3-5-sonnet-20241022-v2:0',
36
+ input: ['text', 'image', 'document'],
37
+ output: ['text'],
38
+ apis: { converse: true, chat: false, responses: false },
39
+ },
40
+ {
41
+ id: 'us.anthropic.claude-3-5-haiku-20241022-v1:0',
42
+ input: ['text'],
43
+ output: ['text'],
44
+ apis: { converse: true, chat: false, responses: false },
45
+ },
46
+ {
47
+ id: 'us.amazon.nova-pro-v1:0',
48
+ input: ['text', 'image', 'document'],
49
+ output: ['text'],
50
+ apis: { converse: true, chat: false, responses: false },
51
+ },
52
+ {
53
+ id: 'us.amazon.nova-lite-v1:0',
54
+ input: ['text', 'image', 'document'],
55
+ output: ['text'],
56
+ apis: { converse: true, chat: false, responses: false },
57
+ },
58
+ {
59
+ id: 'us.amazon.nova-micro-v1:0',
60
+ input: ['text'],
61
+ output: ['text'],
62
+ apis: { converse: true, chat: false, responses: false },
63
+ },
64
+ {
65
+ id: 'us.meta.llama3-3-70b-instruct-v1:0',
66
+ input: ['text'],
67
+ output: ['text'],
68
+ apis: { converse: true, chat: false, responses: false },
69
+ },
70
+ {
71
+ id: 'us.meta.llama4-maverick-17b-instruct-v1:0',
72
+ input: ['text', 'image'],
73
+ output: ['text'],
74
+ apis: { converse: true, chat: false, responses: false },
75
+ },
76
+ {
77
+ id: 'us.mistral.pixtral-large-2502-v1:0',
78
+ input: ['text', 'image'],
79
+ output: ['text'],
80
+ apis: { converse: true, chat: false, responses: false },
81
+ },
82
+ {
83
+ id: 'us.deepseek.r1-v1:0',
84
+ input: ['text'],
85
+ output: ['text'],
86
+ apis: { converse: true, chat: false, responses: false },
87
+ },
88
+ ] as const
@@ -0,0 +1,69 @@
1
+ import { GENERATED_BEDROCK_MODELS } from './model-catalog.generated'
2
+ import type { BedrockTextProviderOptions } from './text/text-provider-options'
3
+ import type { BedrockConverseProviderOptions } from './converse/provider-options'
4
+
5
+ type Entry = (typeof GENERATED_BEDROCK_MODELS)[number]
6
+
7
+ /**
8
+ * Type-level per-API filter over the generated catalog. Because the catalog is
9
+ * `as const`, `Extract` preserves literal `id` unions (no widening to `string`).
10
+ */
11
+ type IdsWhere<TApi extends 'converse' | 'chat' | 'responses'> = Extract<
12
+ Entry,
13
+ { apis: Record<TApi, true> }
14
+ >['id']
15
+
16
+ export type BedrockConverseModels = IdsWhere<'converse'>
17
+ export type BedrockChatModels = IdsWhere<'chat'>
18
+ export type BedrockResponsesModels = IdsWhere<'responses'>
19
+
20
+ /** Runtime catalogs. Cast-free narrowing via a type predicate (the ai-bedrock pattern). */
21
+ // Every catalog entry advertises `converse: true` (Converse is the universal
22
+ // Bedrock surface), so the id list is the full catalog — no runtime filter needed.
23
+ export const BEDROCK_CONVERSE_MODELS: ReadonlyArray<BedrockConverseModels> =
24
+ GENERATED_BEDROCK_MODELS.map((m) => m.id)
25
+
26
+ export const BEDROCK_CHAT_MODELS: ReadonlyArray<BedrockChatModels> =
27
+ GENERATED_BEDROCK_MODELS.filter(
28
+ (m): m is Extract<Entry, { apis: { chat: true } }> => m.apis.chat,
29
+ ).map((m) => m.id)
30
+
31
+ export const BEDROCK_RESPONSES_MODELS: ReadonlyArray<BedrockResponsesModels> =
32
+ GENERATED_BEDROCK_MODELS.filter(
33
+ (m): m is Extract<Entry, { apis: { responses: true } }> => m.apis.responses,
34
+ ).map((m) => m.id)
35
+
36
+ /** Per-model input modalities (drives type-safe multimodal content). Covers ALL models. */
37
+ export type BedrockModelInputModalitiesByName = {
38
+ [E in Entry as E['id']]: E['input']
39
+ }
40
+
41
+ /** Provider options per model. Same options for every model; keyed over the full catalog. */
42
+ export type BedrockChatModelProviderOptionsByName = {
43
+ [E in Entry as E['id']]: BedrockTextProviderOptions
44
+ }
45
+
46
+ /** Converse provider options per model (narrower than the Chat Completions set). */
47
+ export type BedrockConverseModelProviderOptionsByName = {
48
+ [E in Entry as E['id']]: BedrockConverseProviderOptions
49
+ }
50
+
51
+ /** No provider-specific tools — empty tuple makes cross-provider ProviderTool a compile error. */
52
+ export type BedrockChatModelToolCapabilitiesByName = {
53
+ [E in Entry as E['id']]: readonly []
54
+ }
55
+
56
+ export type ResolveProviderOptions<TModel extends string> =
57
+ TModel extends keyof BedrockChatModelProviderOptionsByName
58
+ ? BedrockChatModelProviderOptionsByName[TModel]
59
+ : BedrockTextProviderOptions
60
+
61
+ export type ResolveConverseProviderOptions<TModel extends string> =
62
+ TModel extends keyof BedrockConverseModelProviderOptionsByName
63
+ ? BedrockConverseModelProviderOptionsByName[TModel]
64
+ : BedrockConverseProviderOptions
65
+
66
+ export type ResolveInputModalities<TModel extends string> =
67
+ TModel extends keyof BedrockModelInputModalitiesByName
68
+ ? BedrockModelInputModalitiesByName[TModel]
69
+ : readonly ['text']
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Bedrock Responses API provider options. Mantle's Responses endpoint adds
3
+ * stateful conversation management on top of the OpenAI Responses fields.
4
+ *
5
+ * @see https://docs.aws.amazon.com/bedrock/latest/userguide/bedrock-mantle.html
6
+ */
7
+ export interface BedrockResponsesProviderOptions {
8
+ /** Continue a stored conversation from a prior response. */
9
+ previous_response_id?: string | null
10
+ /** Whether Bedrock retains the response for 30 days (default true). Set false to opt out. */
11
+ store?: boolean | null
12
+ metadata?: { [key: string]: string } | null
13
+ max_output_tokens?: number | null
14
+ temperature?: number | null
15
+ top_p?: number | null
16
+ parallel_tool_calls?: boolean | null
17
+ tool_choice?:
18
+ | 'none'
19
+ | 'auto'
20
+ | 'required'
21
+ | { type: 'function'; name: string }
22
+ | null
23
+ /** Reasoning controls for reasoning-capable models. */
24
+ reasoning?: { effort?: 'low' | 'medium' | 'high' } | null
25
+ user?: string | null
26
+ }
27
+
28
+ export type ExternalResponsesProviderOptions = BedrockResponsesProviderOptions
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Bedrock Chat Completions provider options. Bedrock accepts the standard
3
+ * OpenAI Chat Completions request fields; we surface the commonly-used ones
4
+ * plus `reasoning_effort` (supported by gpt-oss and reasoning models).
5
+ *
6
+ * @see https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-openai.html
7
+ */
8
+ export interface BedrockTextProviderOptions {
9
+ frequency_penalty?: number | null
10
+ presence_penalty?: number | null
11
+ logit_bias?: { [token: string]: number } | null
12
+ logprobs?: boolean | null
13
+ top_logprobs?: number | null
14
+ max_completion_tokens?: number | null
15
+ metadata?: { [key: string]: string } | null
16
+ n?: number | null
17
+ parallel_tool_calls?: boolean | null
18
+ /** gpt-oss / reasoning models: 'low' | 'medium' (default) | 'high'. */
19
+ reasoning_effort?: 'low' | 'medium' | 'high' | null
20
+ seed?: number | null
21
+ stop?: string | Array<string> | null
22
+ temperature?: number | null
23
+ tool_choice?:
24
+ | 'none'
25
+ | 'auto'
26
+ | 'required'
27
+ | { type: 'function'; function: { name: string } }
28
+ | null
29
+ top_p?: number | null
30
+ user?: string | null
31
+ }
32
+
33
+ export type ExternalTextProviderOptions = BedrockTextProviderOptions
@@ -0,0 +1,80 @@
1
+ import type { AwsCredentialIdentityProvider } from '@smithy/types'
2
+ import type * as CredentialProviders from '@aws-sdk/credential-providers'
3
+
4
+ export type BedrockEndpoint = 'runtime' | 'mantle'
5
+
6
+ /** SigV4 service name differs per endpoint. */
7
+ export function sigv4Service(endpoint: BedrockEndpoint): string {
8
+ return endpoint === 'mantle' ? 'bedrock-mantle' : 'bedrock'
9
+ }
10
+
11
+ export type ResolvedBedrockAuth =
12
+ | { kind: 'bearer'; token: string }
13
+ | {
14
+ kind: 'sigv4'
15
+ region: string
16
+ service: string
17
+ credentials: AwsCredentialIdentityProvider
18
+ }
19
+
20
+ const DEFAULT_REGION = 'us-east-1'
21
+
22
+ function readApiKeyFromEnv(): string | undefined {
23
+ // Bedrock is server-only (the AWS SDK is Node-only), so the key always comes
24
+ // from process.env. Read it directly: a property access never throws, so —
25
+ // unlike the previous try/catch around getApiKeyFromEnv — we no longer swallow
26
+ // unrelated errors as "key not set". Each var is blank-checked independently
27
+ // so a present-but-blank BEDROCK_API_KEY falls through to
28
+ // AWS_BEARER_TOKEN_BEDROCK (and only then to SigV4) rather than masking it.
29
+ const env = typeof process !== 'undefined' ? process.env : undefined
30
+ for (const value of [env?.BEDROCK_API_KEY, env?.AWS_BEARER_TOKEN_BEDROCK]) {
31
+ if (value && value.trim() !== '') return value
32
+ }
33
+ return undefined
34
+ }
35
+
36
+ export interface BedrockAuthConfig {
37
+ apiKey?: string
38
+ region?: string
39
+ auth?: 'apikey' | 'sigv4' | 'auto'
40
+ }
41
+
42
+ /** apiKey -> BEDROCK_API_KEY -> AWS_BEARER_TOKEN_BEDROCK -> SigV4 (credential chain). */
43
+ export function resolveBedrockAuth(
44
+ config: BedrockAuthConfig,
45
+ endpoint: BedrockEndpoint,
46
+ ): ResolvedBedrockAuth {
47
+ const mode = config.auth ?? 'auto'
48
+ const region = config.region ?? DEFAULT_REGION
49
+
50
+ if (mode !== 'sigv4') {
51
+ const token = config.apiKey ?? readApiKeyFromEnv()
52
+ if (token) return { kind: 'bearer', token }
53
+ if (mode === 'apikey') {
54
+ throw new Error(
55
+ 'No Bedrock API key found. Set BEDROCK_API_KEY (or ' +
56
+ 'AWS_BEARER_TOKEN_BEDROCK), pass `apiKey`, or use auth: "sigv4".',
57
+ )
58
+ }
59
+ }
60
+
61
+ return {
62
+ kind: 'sigv4',
63
+ region,
64
+ service: sigv4Service(endpoint),
65
+ // Lazy credential provider: the AWS SDK is Node/server-only, so we defer the
66
+ // dynamic import until SigV4 actually needs to resolve credentials. The
67
+ // specifier is held in a variable (not a string literal) so bundler dep
68
+ // scanners (e.g. Vite/esbuild optimizeDeps) cannot statically discover the
69
+ // AWS SDK and try to pre-bundle it for the browser — it would fail on the
70
+ // SDK's Node-only `fromTokenFile` export chain. `typeof import(...)` is a
71
+ // type-only reference (erased at emit) so we keep full typing.
72
+ credentials: async (...args) => {
73
+ const mod = '@aws-sdk/credential-providers'
74
+ const { fromNodeProviderChain } = (await import(
75
+ /* @vite-ignore */ mod
76
+ )) as typeof CredentialProviders
77
+ return fromNodeProviderChain()(...args)
78
+ },
79
+ }
80
+ }
@@ -0,0 +1,62 @@
1
+ import { resolveBedrockAuth } from './auth'
2
+ import { createSigV4Fetch } from './openai-sigv4-fetch'
3
+ import type { ClientOptions } from 'openai'
4
+ import type { BedrockEndpoint } from './auth'
5
+
6
+ export type { BedrockEndpoint } from './auth'
7
+ export { resolveBedrockAuth } from './auth'
8
+ export type { ResolvedBedrockAuth } from './auth'
9
+
10
+ export interface BedrockClientConfig extends Omit<
11
+ ClientOptions,
12
+ 'apiKey' | 'baseURL'
13
+ > {
14
+ /** Bedrock API key (bearer). Optional — falls back to env, then SigV4. */
15
+ apiKey?: string
16
+ /** Full AWS region (e.g. 'us-east-1'). Default 'us-east-1'. */
17
+ region?: string
18
+ /** Chat adapter only; the responses adapter forces 'mantle'. Default 'runtime'. */
19
+ endpoint?: BedrockEndpoint
20
+ /** Auth strategy. Default 'auto' (apiKey → env → SigV4). */
21
+ auth?: 'apikey' | 'sigv4' | 'auto'
22
+ /** Explicit override; wins over the computed endpoint URL (used by E2E → aimock). */
23
+ baseURL?: string
24
+ }
25
+
26
+ const DEFAULT_REGION = 'us-east-1'
27
+ /** OpenAI SDK requires a non-empty apiKey even when a signed fetch overrides Authorization. */
28
+ const SIGV4_PLACEHOLDER_KEY = 'bedrock-sigv4'
29
+
30
+ function buildBaseURL(region: string, endpoint: BedrockEndpoint): string {
31
+ return endpoint === 'mantle'
32
+ ? `https://bedrock-mantle.${region}.api.aws/v1`
33
+ : `https://bedrock-runtime.${region}.amazonaws.com/openai/v1`
34
+ }
35
+
36
+ /** Builds OpenAI ClientOptions for the requested endpoint. `forced` pins the endpoint (responses → 'mantle'). */
37
+ export function withBedrockDefaults(
38
+ config: BedrockClientConfig,
39
+ forced?: BedrockEndpoint,
40
+ ): ClientOptions {
41
+ const { region, endpoint, auth, apiKey, baseURL, fetch, ...rest } = config
42
+ const resolvedRegion = region ?? DEFAULT_REGION
43
+ const resolvedEndpoint = forced ?? endpoint ?? 'runtime'
44
+ const resolved = resolveBedrockAuth(
45
+ { apiKey, region: resolvedRegion, auth },
46
+ resolvedEndpoint,
47
+ )
48
+ if (resolved.kind === 'bearer') {
49
+ return {
50
+ ...rest,
51
+ baseURL: baseURL ?? buildBaseURL(resolvedRegion, resolvedEndpoint),
52
+ apiKey: resolved.token,
53
+ ...(fetch ? { fetch } : {}),
54
+ }
55
+ }
56
+ return {
57
+ ...rest,
58
+ baseURL: baseURL ?? buildBaseURL(resolvedRegion, resolvedEndpoint),
59
+ apiKey: SIGV4_PLACEHOLDER_KEY,
60
+ fetch: fetch ?? createSigV4Fetch(resolved),
61
+ }
62
+ }
@@ -0,0 +1,7 @@
1
+ export {
2
+ resolveBedrockAuth,
3
+ withBedrockDefaults,
4
+ type BedrockClientConfig,
5
+ type BedrockEndpoint,
6
+ type ResolvedBedrockAuth,
7
+ } from './client'