@namzu/bedrock 4.0.0 → 5.0.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.
- package/CHANGELOG.md +98 -0
- package/dist/client.d.ts +75 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +216 -27
- package/dist/client.js.map +1 -1
- package/dist/health.d.ts +77 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +131 -0
- package/dist/health.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/package.json +12 -2
- package/src/client.ts +238 -32
- package/src/health.ts +194 -0
- package/src/index.ts +1 -0
- package/dist/__tests__/bedrock.test.d.ts +0 -2
- package/dist/__tests__/bedrock.test.d.ts.map +0 -1
- package/dist/__tests__/bedrock.test.js +0 -94
- package/dist/__tests__/bedrock.test.js.map +0 -1
- package/dist/__tests__/error-taxonomy.test.d.ts +0 -17
- package/dist/__tests__/error-taxonomy.test.d.ts.map +0 -1
- package/dist/__tests__/error-taxonomy.test.js +0 -204
- package/dist/__tests__/error-taxonomy.test.js.map +0 -1
- package/dist/__tests__/model-reachability.test.d.ts +0 -2
- package/dist/__tests__/model-reachability.test.d.ts.map +0 -1
- package/dist/__tests__/model-reachability.test.js +0 -64
- package/dist/__tests__/model-reachability.test.js.map +0 -1
- package/dist/__tests__/prompt-cache.test.d.ts +0 -2
- package/dist/__tests__/prompt-cache.test.d.ts.map +0 -1
- package/dist/__tests__/prompt-cache.test.js +0 -68
- package/dist/__tests__/prompt-cache.test.js.map +0 -1
- package/dist/__tests__/thinking-refused.test.d.ts +0 -2
- package/dist/__tests__/thinking-refused.test.d.ts.map +0 -1
- package/dist/__tests__/thinking-refused.test.js +0 -35
- package/dist/__tests__/thinking-refused.test.js.map +0 -1
- package/dist/__tests__/tool-choice-none.test.d.ts +0 -2
- package/dist/__tests__/tool-choice-none.test.d.ts.map +0 -1
- package/dist/__tests__/tool-choice-none.test.js +0 -76
- package/dist/__tests__/tool-choice-none.test.js.map +0 -1
- package/dist/__tests__/tool-content-blocks.test.d.ts +0 -2
- package/dist/__tests__/tool-content-blocks.test.d.ts.map +0 -1
- package/dist/__tests__/tool-content-blocks.test.js +0 -92
- package/dist/__tests__/tool-content-blocks.test.js.map +0 -1
- package/dist/__tests__/tool-schema-dialect.test.d.ts +0 -2
- package/dist/__tests__/tool-schema-dialect.test.d.ts.map +0 -1
- package/dist/__tests__/tool-schema-dialect.test.js +0 -98
- package/dist/__tests__/tool-schema-dialect.test.js.map +0 -1
- package/dist/__tests__/vision.test.d.ts +0 -2
- package/dist/__tests__/vision.test.d.ts.map +0 -1
- package/dist/__tests__/vision.test.js +0 -77
- package/dist/__tests__/vision.test.js.map +0 -1
- package/src/__tests__/bedrock.test.ts +0 -103
- package/src/__tests__/error-taxonomy.test.ts +0 -248
- package/src/__tests__/model-reachability.test.ts +0 -74
- package/src/__tests__/prompt-cache.test.ts +0 -81
- package/src/__tests__/thinking-refused.test.ts +0 -44
- package/src/__tests__/tool-choice-none.test.ts +0 -87
- package/src/__tests__/tool-content-blocks.test.ts +0 -119
- package/src/__tests__/tool-schema-dialect.test.ts +0 -108
- package/src/__tests__/vision.test.ts +0 -98
package/src/health.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a health probe against this wire concluded, and why.
|
|
3
|
+
*
|
|
4
|
+
* `healthCheck` used to answer with one bit produced by `catch { return false }`.
|
|
5
|
+
* The three failures a caller actually needs apart — the credential is wrong,
|
|
6
|
+
* the service is down, that model is not invokable here — all arrived as
|
|
7
|
+
* `false`, and so did success, because the id it probed was one the driver's
|
|
8
|
+
* own `assertModelReachable` classifies as unreachable. A check that cannot
|
|
9
|
+
* pass is the same defect class as a check that cannot fail: neither carries
|
|
10
|
+
* information, and this one carried none in a shape that looked like an outage.
|
|
11
|
+
*
|
|
12
|
+
* So the reason is a value rather than a sentence. `status` is the
|
|
13
|
+
* `DoctorCheckResult` field `runDoctor()` reads; `reason` is what a caller
|
|
14
|
+
* switches on.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { DoctorCheckResult } from '@namzu/sdk'
|
|
18
|
+
import { providerVendorError } from '@namzu/sdk'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Why the probe reached the verdict it did.
|
|
22
|
+
*
|
|
23
|
+
* Named for what an operator would do next, which is why `credentials` and
|
|
24
|
+
* `unreachable-model` are separate from `refused`: the first is a key to
|
|
25
|
+
* rotate, the second an id to change, the third a request the service looked
|
|
26
|
+
* at and would not take.
|
|
27
|
+
*/
|
|
28
|
+
export type BedrockHealthReason =
|
|
29
|
+
/** The model answered. */
|
|
30
|
+
| 'ok'
|
|
31
|
+
/** Nothing was probed: no model id was given and this driver holds none. */
|
|
32
|
+
| 'no-model'
|
|
33
|
+
/** The driver's own reachability rule refuses this id; nothing was sent. */
|
|
34
|
+
| 'unreachable-model'
|
|
35
|
+
/** No AWS credentials could be resolved on this machine. Never left it. */
|
|
36
|
+
| 'no-credentials'
|
|
37
|
+
/** AWS answered and rejected the credential or its permissions. */
|
|
38
|
+
| 'credentials'
|
|
39
|
+
/** The id is well-formed but this region serves no such model. */
|
|
40
|
+
| 'unknown-model'
|
|
41
|
+
/** The service looked at the request and refused it. */
|
|
42
|
+
| 'refused'
|
|
43
|
+
/** Reached, authenticated, and rate limited. The probe did not complete. */
|
|
44
|
+
| 'throttled'
|
|
45
|
+
/** The service answered and could not serve the request. */
|
|
46
|
+
| 'service'
|
|
47
|
+
/** Nothing was learned: the request did not get an answer. */
|
|
48
|
+
| 'unreachable-service'
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A `DoctorCheckResult` with the reason kept.
|
|
52
|
+
*
|
|
53
|
+
* Extends rather than replaces so it satisfies `LLMProvider.doctorCheck`
|
|
54
|
+
* unchanged: `runDoctor()` reads `status` and never sees the extra field, and a
|
|
55
|
+
* caller holding the concrete driver reads `reason` without a cast.
|
|
56
|
+
*/
|
|
57
|
+
export interface BedrockHealthReport extends DoctorCheckResult {
|
|
58
|
+
readonly reason: BedrockHealthReason
|
|
59
|
+
/** The id that was probed. Absent only when nothing was probed. */
|
|
60
|
+
readonly model?: string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* AWS models its failures as distinct exception CLASSES rather than as status
|
|
65
|
+
* codes, and the class is the only thing that separates the cases an operator
|
|
66
|
+
* acts on differently.
|
|
67
|
+
*
|
|
68
|
+
* The SDK's `providerVendorError` deliberately flattens these into six kinds
|
|
69
|
+
* for the request path, where the question is "retry, compact, or give up".
|
|
70
|
+
* That is the right shape there and the wrong one here: it merges
|
|
71
|
+
* `ResourceNotFoundException` ("no such model in this region") with
|
|
72
|
+
* `ValidationException` ("the request was invalid") into `bad_request`, and
|
|
73
|
+
* those are the two answers a health check exists to tell apart. So the class
|
|
74
|
+
* is read here, where the raw error is still in hand.
|
|
75
|
+
*
|
|
76
|
+
* Matched on `name` rather than with `instanceof`: the exception classes are
|
|
77
|
+
* re-exported by every minor of the AWS SDK, and a caller running two copies of
|
|
78
|
+
* `@aws-sdk/client-bedrock-runtime` in one tree would fail every `instanceof`
|
|
79
|
+
* against errors that are otherwise correct.
|
|
80
|
+
*/
|
|
81
|
+
const REASON_BY_EXCEPTION: ReadonlyArray<readonly [RegExp, BedrockHealthReason]> = [
|
|
82
|
+
// Client-side, before a packet leaves: the credential chain found nothing.
|
|
83
|
+
// This is a definite answer about the machine, not a failure to reach AWS,
|
|
84
|
+
// and filing it as unreachable would tell someone with no credentials at
|
|
85
|
+
// all to go and check their network.
|
|
86
|
+
[/CredentialsProviderError|CredentialsError/i, 'no-credentials'],
|
|
87
|
+
[/AccessDenied|UnrecognizedClient|InvalidSignature|Unauthorized|Forbidden/i, 'credentials'],
|
|
88
|
+
[/ResourceNotFound/i, 'unknown-model'],
|
|
89
|
+
[/ThrottlingException|TooManyRequests|ServiceQuotaExceeded/i, 'throttled'],
|
|
90
|
+
[
|
|
91
|
+
/ServiceUnavailable|InternalServer|ModelNotReady|ModelTimeout|ModelStreamError|ModelError/i,
|
|
92
|
+
'service',
|
|
93
|
+
],
|
|
94
|
+
[/Validation/i, 'refused'],
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
const STATUS_BY_REASON: Readonly<Record<BedrockHealthReason, DoctorCheckResult['status']>> = {
|
|
98
|
+
ok: 'pass',
|
|
99
|
+
'no-model': 'skipped',
|
|
100
|
+
'unreachable-model': 'fail',
|
|
101
|
+
'no-credentials': 'fail',
|
|
102
|
+
credentials: 'fail',
|
|
103
|
+
'unknown-model': 'fail',
|
|
104
|
+
refused: 'fail',
|
|
105
|
+
// Reached, authenticated, throttled. The service is up, so this is not a
|
|
106
|
+
// failure of the integration — but the probe did not complete, so it is
|
|
107
|
+
// not a pass either, and reporting it as one would let a caller start
|
|
108
|
+
// sending traffic on the strength of a request that never ran.
|
|
109
|
+
throttled: 'warn',
|
|
110
|
+
service: 'fail',
|
|
111
|
+
// The check did not answer. Distinct from `fail` because nothing is known:
|
|
112
|
+
// telling an operator on broken wifi that Bedrock is down sends them to
|
|
113
|
+
// the wrong place.
|
|
114
|
+
'unreachable-service': 'inconclusive',
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const REMEDIATION_BY_REASON: Readonly<Partial<Record<BedrockHealthReason, string>>> = {
|
|
118
|
+
'no-credentials':
|
|
119
|
+
'Pass accessKeyId/secretAccessKey to the driver, or make the AWS default credential chain resolve here (env, shared config, SSO, or an instance role).',
|
|
120
|
+
credentials:
|
|
121
|
+
'Check the credential is current and that its IAM policy allows bedrock:InvokeModelWithResponseStream on this model in this region.',
|
|
122
|
+
'unknown-model':
|
|
123
|
+
'This region serves no model by that id. Enable model access in the target region, or use the inference-profile form (us./eu./apac./jp./global.) for a model served cross-region.',
|
|
124
|
+
refused:
|
|
125
|
+
'The service rejected the request itself. A model served only through an inference profile refuses the bare id — try the profile-prefixed form.',
|
|
126
|
+
throttled: 'Retry. The service is reachable and the credential authenticated.',
|
|
127
|
+
service: 'Nothing to do here; the service could not serve the request. Retry later.',
|
|
128
|
+
'unreachable-service':
|
|
129
|
+
'Nothing was learned about the service. Check network reachability of the Bedrock endpoint for this region before changing any credential.',
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** The reason an AWS failure implies; "nothing was learned" when unrecognised. */
|
|
133
|
+
export function reasonForError(err: unknown): BedrockHealthReason {
|
|
134
|
+
// Read off the object rather than gated on an instanceof check. Bedrock
|
|
135
|
+
// reports several failures as members of the stream's output union, and a
|
|
136
|
+
// deserialiser that hands one back as a plain shape would otherwise fall all
|
|
137
|
+
// the way through to "nothing was learned" while naming its own class.
|
|
138
|
+
const raw = (err as { name?: unknown } | null)?.name
|
|
139
|
+
const name = typeof raw === 'string' ? raw : ''
|
|
140
|
+
for (const [pattern, reason] of REASON_BY_EXCEPTION) {
|
|
141
|
+
if (pattern.test(name)) return reason
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// A region that was never configured also never leaves the machine, and the
|
|
145
|
+
// AWS SDK reports it as a plain `Error`, so there is no class to match.
|
|
146
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
147
|
+
if (/region is missing|missing region/i.test(message)) return 'no-credentials'
|
|
148
|
+
|
|
149
|
+
return 'unreachable-service'
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Build the report for a failure.
|
|
154
|
+
*
|
|
155
|
+
* The vendor error's message is taken from `providerVendorError` rather than
|
|
156
|
+
* from `err.message`, and that is not stylistic. AWS builds an exception
|
|
157
|
+
* message from the response body, so a credential the service echoed back is
|
|
158
|
+
* already inside `err.message` before this code sees it; `providerVendorError`
|
|
159
|
+
* is the one place in this estate that strips it. The class name is read off
|
|
160
|
+
* the raw error first — a name is not a body — and then the raw error is
|
|
161
|
+
* dropped.
|
|
162
|
+
*/
|
|
163
|
+
export function reportForError(
|
|
164
|
+
err: unknown,
|
|
165
|
+
model: string,
|
|
166
|
+
durationMs: number,
|
|
167
|
+
): BedrockHealthReport {
|
|
168
|
+
const reason = reasonForError(err)
|
|
169
|
+
const safe = providerVendorError({ providerId: 'bedrock', error: err })
|
|
170
|
+
return {
|
|
171
|
+
status: STATUS_BY_REASON[reason],
|
|
172
|
+
reason,
|
|
173
|
+
model,
|
|
174
|
+
message: safe.message,
|
|
175
|
+
...(REMEDIATION_BY_REASON[reason] ? { remediation: REMEDIATION_BY_REASON[reason] } : {}),
|
|
176
|
+
durationMs,
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** The report for a reason that needs no vendor error to describe it. */
|
|
181
|
+
export function report(
|
|
182
|
+
reason: BedrockHealthReason,
|
|
183
|
+
message: string,
|
|
184
|
+
extra: { readonly model?: string; readonly durationMs?: number } = {},
|
|
185
|
+
): BedrockHealthReport {
|
|
186
|
+
return {
|
|
187
|
+
status: STATUS_BY_REASON[reason],
|
|
188
|
+
reason,
|
|
189
|
+
message,
|
|
190
|
+
...(extra.model !== undefined ? { model: extra.model } : {}),
|
|
191
|
+
...(REMEDIATION_BY_REASON[reason] ? { remediation: REMEDIATION_BY_REASON[reason] } : {}),
|
|
192
|
+
...(extra.durationMs !== undefined ? { durationMs: extra.durationMs } : {}),
|
|
193
|
+
}
|
|
194
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -23,4 +23,5 @@ export function registerBedrock(options?: RegisterOptions): void {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export { BEDROCK_CAPABILITIES, BedrockProvider } from './client.js'
|
|
26
|
+
export type { BedrockHealthReason, BedrockHealthReport } from './health.js'
|
|
26
27
|
export type { BedrockConfig, BedrockProviderConfig } from './types.js'
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bedrock.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/bedrock.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { DuplicateProviderError, ProviderRegistry } from '@namzu/sdk';
|
|
2
|
-
import { beforeEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import { BEDROCK_CAPABILITIES, BedrockProvider, registerBedrock } from '../index.js';
|
|
4
|
-
// Ensure a clean slate between tests. The sdk pre-registers 'mock' on import
|
|
5
|
-
// via its sideEffects whitelist; we only need to clear 'bedrock' to make
|
|
6
|
-
// re-registration deterministic across tests.
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
if (ProviderRegistry.isSupported('bedrock')) {
|
|
9
|
-
ProviderRegistry.unregister('bedrock');
|
|
10
|
-
}
|
|
11
|
-
});
|
|
12
|
-
describe('@namzu/bedrock', () => {
|
|
13
|
-
describe('registerBedrock()', () => {
|
|
14
|
-
it("adds 'bedrock' to the ProviderRegistry", () => {
|
|
15
|
-
expect(ProviderRegistry.isSupported('bedrock')).toBe(false);
|
|
16
|
-
registerBedrock();
|
|
17
|
-
expect(ProviderRegistry.isSupported('bedrock')).toBe(true);
|
|
18
|
-
expect(ProviderRegistry.listTypes()).toContain('bedrock');
|
|
19
|
-
});
|
|
20
|
-
it('throws DuplicateProviderError when called twice without options', () => {
|
|
21
|
-
registerBedrock();
|
|
22
|
-
expect(() => registerBedrock()).toThrowError(DuplicateProviderError);
|
|
23
|
-
});
|
|
24
|
-
it('allows re-registration when { replace: true } is passed', () => {
|
|
25
|
-
registerBedrock();
|
|
26
|
-
expect(() => registerBedrock({ replace: true })).not.toThrow();
|
|
27
|
-
expect(ProviderRegistry.isSupported('bedrock')).toBe(true);
|
|
28
|
-
});
|
|
29
|
-
it('exposes capabilities through the registry after registration', () => {
|
|
30
|
-
registerBedrock();
|
|
31
|
-
const caps = ProviderRegistry.getCapabilities('bedrock');
|
|
32
|
-
expect(caps).toEqual(BEDROCK_CAPABILITIES);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
describe('BEDROCK_CAPABILITIES', () => {
|
|
36
|
-
it('declares the expected capability flags', () => {
|
|
37
|
-
expect(BEDROCK_CAPABILITIES).toEqual({
|
|
38
|
-
supportsTools: true,
|
|
39
|
-
supportsStreaming: true,
|
|
40
|
-
supportsFunctionCalling: true,
|
|
41
|
-
supportsVision: false,
|
|
42
|
-
supportsDocuments: false,
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
it('is exposed on the provider instance for runtime negotiation', () => {
|
|
46
|
-
const provider = new BedrockProvider({ region: 'us-east-1' });
|
|
47
|
-
expect(provider.capabilities).toEqual(BEDROCK_CAPABILITIES);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
it('keeps the Namzu enforcement hint out of the Bedrock command', async () => {
|
|
51
|
-
const provider = new BedrockProvider({ region: 'us-east-1' });
|
|
52
|
-
let commandInput;
|
|
53
|
-
provider.client = {
|
|
54
|
-
send: async (command) => {
|
|
55
|
-
commandInput = command.input;
|
|
56
|
-
return {
|
|
57
|
-
$metadata: { requestId: 'request-test' },
|
|
58
|
-
stream: (async function* () { })(),
|
|
59
|
-
};
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
for await (const _chunk of provider.chatStream({
|
|
63
|
-
model: 'anthropic.claude-sonnet-5-v1:0',
|
|
64
|
-
messages: [{ role: 'user', content: 'edit' }],
|
|
65
|
-
tools: [
|
|
66
|
-
{
|
|
67
|
-
type: 'function',
|
|
68
|
-
function: {
|
|
69
|
-
name: 'edit',
|
|
70
|
-
description: 'Edit',
|
|
71
|
-
parameters: { type: 'object' },
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
enforceToolInputSchema: ['edit'],
|
|
76
|
-
})) {
|
|
77
|
-
// Drain the empty test stream.
|
|
78
|
-
}
|
|
79
|
-
expect(commandInput).not.toHaveProperty('enforceToolInputSchema');
|
|
80
|
-
expect(commandInput?.toolConfig).toEqual({
|
|
81
|
-
tools: [
|
|
82
|
-
{
|
|
83
|
-
toolSpec: {
|
|
84
|
-
name: 'edit',
|
|
85
|
-
description: 'Edit',
|
|
86
|
-
inputSchema: { json: { type: 'object' } },
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
],
|
|
90
|
-
toolChoice: { auto: {} },
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
//# sourceMappingURL=bedrock.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bedrock.test.js","sourceRoot":"","sources":["../../src/__tests__/bedrock.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Provider error taxonomy — Bedrock driver.
|
|
3
|
-
*
|
|
4
|
-
* `chatStream` awaits `this.client.send(command)` with no catch, so an AWS
|
|
5
|
-
* `BedrockRuntimeServiceException` (`ThrottlingException`,
|
|
6
|
-
* `ValidationException`, `ServiceUnavailableException`, …) escapes verbatim.
|
|
7
|
-
* A caller cannot classify it without importing `@aws-sdk/client-bedrock-runtime`.
|
|
8
|
-
*
|
|
9
|
-
* Transport seam: `BedrockConfig` exposes no `requestHandler`/`endpoint`, so
|
|
10
|
-
* unlike the fetch-based drivers there is nothing to inject at the HTTP
|
|
11
|
-
* layer. The tests substitute the `BedrockRuntimeClient`'s `send` and reject
|
|
12
|
-
* with the REAL AWS exception classes — which is exactly what the driver
|
|
13
|
-
* sees from a live call, and the only surface the driver's (currently
|
|
14
|
-
* absent) error mapping would act on.
|
|
15
|
-
*/
|
|
16
|
-
export {};
|
|
17
|
-
//# sourceMappingURL=error-taxonomy.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"error-taxonomy.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/error-taxonomy.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Provider error taxonomy — Bedrock driver.
|
|
3
|
-
*
|
|
4
|
-
* `chatStream` awaits `this.client.send(command)` with no catch, so an AWS
|
|
5
|
-
* `BedrockRuntimeServiceException` (`ThrottlingException`,
|
|
6
|
-
* `ValidationException`, `ServiceUnavailableException`, …) escapes verbatim.
|
|
7
|
-
* A caller cannot classify it without importing `@aws-sdk/client-bedrock-runtime`.
|
|
8
|
-
*
|
|
9
|
-
* Transport seam: `BedrockConfig` exposes no `requestHandler`/`endpoint`, so
|
|
10
|
-
* unlike the fetch-based drivers there is nothing to inject at the HTTP
|
|
11
|
-
* layer. The tests substitute the `BedrockRuntimeClient`'s `send` and reject
|
|
12
|
-
* with the REAL AWS exception classes — which is exactly what the driver
|
|
13
|
-
* sees from a live call, and the only surface the driver's (currently
|
|
14
|
-
* absent) error mapping would act on.
|
|
15
|
-
*/
|
|
16
|
-
import { AccessDeniedException, ModelErrorException, ServiceQuotaExceededException, ServiceUnavailableException, ThrottlingException, ValidationException, } from '@aws-sdk/client-bedrock-runtime';
|
|
17
|
-
import { describe, expect, it } from 'vitest';
|
|
18
|
-
import { BedrockProvider } from '../client.js';
|
|
19
|
-
/** Replace the AWS client's `send` so it rejects with `err`. */
|
|
20
|
-
function providerRejectingWith(err) {
|
|
21
|
-
const provider = new BedrockProvider({ region: 'eu-west-1' });
|
|
22
|
-
provider.client = {
|
|
23
|
-
send: () => Promise.reject(err),
|
|
24
|
-
};
|
|
25
|
-
return provider;
|
|
26
|
-
}
|
|
27
|
-
function providerStreaming(...events) {
|
|
28
|
-
const provider = new BedrockProvider({ region: 'eu-west-1' });
|
|
29
|
-
provider.client = {
|
|
30
|
-
send: async () => ({
|
|
31
|
-
$metadata: { requestId: 'bedrock-test' },
|
|
32
|
-
stream: (async function* () {
|
|
33
|
-
yield* events;
|
|
34
|
-
})(),
|
|
35
|
-
}),
|
|
36
|
-
};
|
|
37
|
-
return provider;
|
|
38
|
-
}
|
|
39
|
-
async function captureChatStreamError(provider, signal) {
|
|
40
|
-
try {
|
|
41
|
-
for await (const _chunk of provider.chatStream({
|
|
42
|
-
model: 'anthropic.claude-sonnet-4-5-20250929-v1:0',
|
|
43
|
-
messages: [{ role: 'user', content: 'hi' }],
|
|
44
|
-
signal,
|
|
45
|
-
})) {
|
|
46
|
-
// drain
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
catch (err) {
|
|
50
|
-
return err;
|
|
51
|
-
}
|
|
52
|
-
throw new Error('expected chatStream to throw');
|
|
53
|
-
}
|
|
54
|
-
describe('@namzu/bedrock — provider error taxonomy', () => {
|
|
55
|
-
it('(a) ThrottlingException with `retry-after: 2` becomes a throttle-classified error carrying retryAfterMs', async () => {
|
|
56
|
-
const aws = new ThrottlingException({
|
|
57
|
-
message: 'Too many requests, please wait before trying again.',
|
|
58
|
-
$metadata: { httpStatusCode: 429 },
|
|
59
|
-
});
|
|
60
|
-
aws.$response = {
|
|
61
|
-
statusCode: 429,
|
|
62
|
-
headers: { 'retry-after': '2' },
|
|
63
|
-
};
|
|
64
|
-
const err = await captureChatStreamError(providerRejectingWith(aws));
|
|
65
|
-
expect(err).toMatchObject({
|
|
66
|
-
name: 'ProviderRequestError',
|
|
67
|
-
kind: 'throttle',
|
|
68
|
-
status: 429,
|
|
69
|
-
retryAfterMs: 2000,
|
|
70
|
-
providerId: 'bedrock',
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
it("(c) a ValidationException reporting too many input tokens classifies as 'context_overflow'", async () => {
|
|
74
|
-
const aws = new ValidationException({
|
|
75
|
-
message: 'Input is too long for requested model. The input token count exceeds the maximum for this model.',
|
|
76
|
-
$metadata: { httpStatusCode: 400 },
|
|
77
|
-
});
|
|
78
|
-
const err = await captureChatStreamError(providerRejectingWith(aws));
|
|
79
|
-
expect(err).toMatchObject({
|
|
80
|
-
name: 'ProviderRequestError',
|
|
81
|
-
kind: 'context_overflow',
|
|
82
|
-
status: 400,
|
|
83
|
-
providerId: 'bedrock',
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
it("(c) a ValidationException that is not an overflow classifies as 'bad_request'", async () => {
|
|
87
|
-
const aws = new ValidationException({
|
|
88
|
-
message: 'The value at toolConfig.tools failed to satisfy constraint.',
|
|
89
|
-
$metadata: { httpStatusCode: 400 },
|
|
90
|
-
});
|
|
91
|
-
const err = await captureChatStreamError(providerRejectingWith(aws));
|
|
92
|
-
expect(err).toMatchObject({
|
|
93
|
-
name: 'ProviderRequestError',
|
|
94
|
-
kind: 'bad_request',
|
|
95
|
-
status: 400,
|
|
96
|
-
providerId: 'bedrock',
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
it("an AccessDeniedException classifies as 'auth'", async () => {
|
|
100
|
-
const aws = new AccessDeniedException({
|
|
101
|
-
message: 'You do not have access to the model with the specified model ID.',
|
|
102
|
-
$metadata: { httpStatusCode: 403 },
|
|
103
|
-
});
|
|
104
|
-
const err = await captureChatStreamError(providerRejectingWith(aws));
|
|
105
|
-
expect(err).toMatchObject({
|
|
106
|
-
name: 'ProviderRequestError',
|
|
107
|
-
kind: 'auth',
|
|
108
|
-
status: 403,
|
|
109
|
-
providerId: 'bedrock',
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
it("a ServiceUnavailableException classifies as 'server'", async () => {
|
|
113
|
-
const aws = new ServiceUnavailableException({
|
|
114
|
-
message: 'The service is unavailable, try again later.',
|
|
115
|
-
$metadata: { httpStatusCode: 503 },
|
|
116
|
-
});
|
|
117
|
-
const err = await captureChatStreamError(providerRejectingWith(aws));
|
|
118
|
-
expect(err).toMatchObject({
|
|
119
|
-
name: 'ProviderRequestError',
|
|
120
|
-
kind: 'server',
|
|
121
|
-
status: 503,
|
|
122
|
-
providerId: 'bedrock',
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
it("a ModelErrorException with HTTP 424 still classifies as 'server'", async () => {
|
|
126
|
-
const aws = new ModelErrorException({
|
|
127
|
-
message: 'The model failed while processing the request.',
|
|
128
|
-
originalStatusCode: 500,
|
|
129
|
-
$metadata: { httpStatusCode: 424 },
|
|
130
|
-
});
|
|
131
|
-
const err = await captureChatStreamError(providerRejectingWith(aws));
|
|
132
|
-
expect(err).toMatchObject({
|
|
133
|
-
name: 'ProviderRequestError',
|
|
134
|
-
kind: 'server',
|
|
135
|
-
status: 424,
|
|
136
|
-
providerId: 'bedrock',
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
it("a ServiceQuotaExceededException classifies as 'throttle'", async () => {
|
|
140
|
-
const aws = new ServiceQuotaExceededException({
|
|
141
|
-
message: 'Account inference quota exceeded.',
|
|
142
|
-
$metadata: { httpStatusCode: 400 },
|
|
143
|
-
});
|
|
144
|
-
const err = await captureChatStreamError(providerRejectingWith(aws));
|
|
145
|
-
expect(err).toMatchObject({
|
|
146
|
-
name: 'ProviderRequestError',
|
|
147
|
-
kind: 'throttle',
|
|
148
|
-
status: 400,
|
|
149
|
-
providerId: 'bedrock',
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
it('classifies a throttling exception delivered as a stream union event', async () => {
|
|
153
|
-
const aws = new ThrottlingException({
|
|
154
|
-
message: 'stream quota exceeded',
|
|
155
|
-
$metadata: { httpStatusCode: 429 },
|
|
156
|
-
});
|
|
157
|
-
const err = await captureChatStreamError(providerStreaming({ throttlingException: aws }));
|
|
158
|
-
expect(err).toMatchObject({
|
|
159
|
-
name: 'ProviderRequestError',
|
|
160
|
-
kind: 'throttle',
|
|
161
|
-
status: 429,
|
|
162
|
-
providerId: 'bedrock',
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
it('keeps a validation stream event’s complaint, scrubbed, and still retains no cause', async () => {
|
|
166
|
-
// This test used to assert the opposite half: that the vendor's sentence
|
|
167
|
-
// was dropped whole. That was the safe-looking reading of "an error body
|
|
168
|
-
// can echo a credential", and it cost a day of production downtime —
|
|
169
|
-
// the wire had been naming the exact offending field the entire time and
|
|
170
|
-
// the SDK deleted the sentence before anyone could read it.
|
|
171
|
-
//
|
|
172
|
-
// So the guarantee changed shape rather than weakening: what is scrubbed
|
|
173
|
-
// is anything CREDENTIAL-SHAPED, and what survives is the complaint. The
|
|
174
|
-
// `cause` half is unchanged and still matters — a `cause` survives every
|
|
175
|
-
// logger that serializes an error chain, which is how the raw body would
|
|
176
|
-
// get out regardless of what the message says.
|
|
177
|
-
const key = 'AKIAIOSFODNN7EXAMPLE';
|
|
178
|
-
const aws = new ValidationException({
|
|
179
|
-
message: `inputSchema is invalid, echoed from key ${key}`,
|
|
180
|
-
$metadata: { httpStatusCode: 400 },
|
|
181
|
-
});
|
|
182
|
-
const err = await captureChatStreamError(providerStreaming({ validationException: aws }));
|
|
183
|
-
expect(err).toMatchObject({
|
|
184
|
-
name: 'ProviderRequestError',
|
|
185
|
-
kind: 'bad_request',
|
|
186
|
-
providerId: 'bedrock',
|
|
187
|
-
});
|
|
188
|
-
expect(err.message).toContain('inputSchema is invalid');
|
|
189
|
-
expect(err.message).not.toContain(key);
|
|
190
|
-
expect('cause' in err).toBe(false);
|
|
191
|
-
});
|
|
192
|
-
it('preserves the caller abort reason when the SDK replaces the error object', async () => {
|
|
193
|
-
const controller = new AbortController();
|
|
194
|
-
const reason = new Error('user stopped');
|
|
195
|
-
const sdkAbort = Object.assign(new Error('request aborted'), {
|
|
196
|
-
name: 'AbortError',
|
|
197
|
-
});
|
|
198
|
-
controller.abort(reason);
|
|
199
|
-
const err = await captureChatStreamError(providerRejectingWith(sdkAbort), controller.signal);
|
|
200
|
-
expect(sdkAbort).not.toBe(reason);
|
|
201
|
-
expect(err).toBe(reason);
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
//# sourceMappingURL=error-taxonomy.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"error-taxonomy.test.js","sourceRoot":"","sources":["../../src/__tests__/error-taxonomy.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"model-reachability.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/model-reachability.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { BedrockProvider } from '../client.js';
|
|
3
|
-
import { assertModelReachable } from '../model-reachability.js';
|
|
4
|
-
/**
|
|
5
|
-
* This driver speaks Converse with ARN-versioned model ids. The current Claude
|
|
6
|
-
* generation is served by a different Bedrock integration whose ids carry no
|
|
7
|
-
* version suffix, and pointing this wire at one produced an opaque AWS
|
|
8
|
-
* validation error naming neither the cause nor the remedy.
|
|
9
|
-
*/
|
|
10
|
-
describe('model ids this wire can serve', () => {
|
|
11
|
-
const reachable = [
|
|
12
|
-
'anthropic.claude-sonnet-4-5-20250929-v1:0',
|
|
13
|
-
'us.anthropic.claude-sonnet-4-5-20250929-v1:0',
|
|
14
|
-
'eu.anthropic.claude-opus-4-5-20251101-v1:0',
|
|
15
|
-
'global.anthropic.claude-opus-4-6-v1',
|
|
16
|
-
'apac.anthropic.claude-sonnet-4-20250514-v1:0',
|
|
17
|
-
'anthropic.claude-haiku-4-5-20251001-v1:0',
|
|
18
|
-
];
|
|
19
|
-
for (const model of reachable) {
|
|
20
|
-
it(`accepts ${model}`, () => {
|
|
21
|
-
expect(() => assertModelReachable(model)).not.toThrow();
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
const unreachable = [
|
|
25
|
-
'anthropic.claude-opus-5',
|
|
26
|
-
'anthropic.claude-sonnet-5',
|
|
27
|
-
'anthropic.claude-fable-5',
|
|
28
|
-
'anthropic.claude-opus-4-8',
|
|
29
|
-
'anthropic.claude-opus-4-7',
|
|
30
|
-
'us.anthropic.claude-opus-5',
|
|
31
|
-
];
|
|
32
|
-
for (const model of unreachable) {
|
|
33
|
-
it(`refuses ${model} and says why`, () => {
|
|
34
|
-
expect(() => assertModelReachable(model)).toThrow(/cannot reach/);
|
|
35
|
-
expect(() => assertModelReachable(model)).toThrow(/Converse API/);
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
it('leaves a non-Claude model alone', () => {
|
|
39
|
-
// This driver serves whatever Bedrock serves. The check is about one
|
|
40
|
-
// vendor's id scheme, not about policing the catalogue.
|
|
41
|
-
expect(() => assertModelReachable('amazon.nova-pro-v1:0')).not.toThrow();
|
|
42
|
-
expect(() => assertModelReachable('meta.llama3-70b-instruct-v1:0')).not.toThrow();
|
|
43
|
-
});
|
|
44
|
-
it('accepts an unknown versioned Claude id', () => {
|
|
45
|
-
// A model this file has never heard of passes as long as its id has
|
|
46
|
-
// the shape this wire carries. A list that must be edited for every
|
|
47
|
-
// new model is a list that is wrong before anyone reads it.
|
|
48
|
-
expect(() => assertModelReachable('anthropic.claude-sonnet-9-20991231-v1:0')).not.toThrow();
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
describe('the driver refuses before it calls AWS', () => {
|
|
52
|
-
it('throws on an unreachable model with no credentials in play', async () => {
|
|
53
|
-
const provider = new BedrockProvider({ region: 'us-east-1' });
|
|
54
|
-
await expect(async () => {
|
|
55
|
-
for await (const _ of provider.chatStream({
|
|
56
|
-
model: 'anthropic.claude-opus-5',
|
|
57
|
-
messages: [{ role: 'user', content: 'hi' }],
|
|
58
|
-
})) {
|
|
59
|
-
// drain
|
|
60
|
-
}
|
|
61
|
-
}).rejects.toThrow(/cannot reach "anthropic\.claude-opus-5"/);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
//# sourceMappingURL=model-reachability.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"model-reachability.test.js","sourceRoot":"","sources":["../../src/__tests__/model-reachability.test.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AAE/D;;;;;GAKG;AAEH,QAAQ,CAAC,+BAA+B,EAAE,GAAG,EAAE;IAC9C,MAAM,SAAS,GAAG;QACjB,2CAA2C;QAC3C,8CAA8C;QAC9C,4CAA4C;QAC5C,qCAAqC;QACrC,8CAA8C;QAC9C,0CAA0C;KAC1C,CAAA;IAED,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC/B,EAAE,CAAC,WAAW,KAAK,EAAE,EAAE,GAAG,EAAE;YAC3B,MAAM,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAA;QACxD,CAAC,CAAC,CAAA;IACH,CAAC;IAED,MAAM,WAAW,GAAG;QACnB,yBAAyB;QACzB,2BAA2B;QAC3B,0BAA0B;QAC1B,2BAA2B;QAC3B,2BAA2B;QAC3B,4BAA4B;KAC5B,CAAA;IAED,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE,CAAC;QACjC,EAAE,CAAC,WAAW,KAAK,eAAe,EAAE,GAAG,EAAE;YACxC,MAAM,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAA;YACjE,MAAM,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAA;QAClE,CAAC,CAAC,CAAA;IACH,CAAC;IAED,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC1C,qEAAqE;QACrE,wDAAwD;QACxD,MAAM,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,sBAAsB,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAA;QACxE,MAAM,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,+BAA+B,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAA;IAClF,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QACjD,oEAAoE;QACpE,oEAAoE;QACpE,4DAA4D;QAC5D,MAAM,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,yCAAyC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAA;IAC5F,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,wCAAwC,EAAE,GAAG,EAAE;IACvD,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,QAAQ,GAAG,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,WAAW,EAAW,CAAC,CAAA;QAEtE,MAAM,MAAM,CAAC,KAAK,IAAI,EAAE;YACvB,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC;gBACzC,KAAK,EAAE,yBAAyB;gBAChC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;aACnB,CAAC,EAAE,CAAC;gBAC5B,QAAQ;YACT,CAAC;QACF,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,yCAAyC,CAAC,CAAA;IAC9D,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-cache.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/prompt-cache.test.ts"],"names":[],"mappings":""}
|