diff --git a/API.md b/API.md index fa6ad20..69a4eaf 100644 --- a/API.md +++ b/API.md @@ -332,6 +332,7 @@ composable integration value: | `streamfold/vercel-ai` | `vercelAI` | | `streamfold/openai` | `openAI` | | `streamfold/anthropic` | `anthropic` | +| `streamfold/bedrock` | `bedrock` | | `streamfold/gemini` | `gemini` | | `streamfold/langchain` | `langchain` | | `streamfold/ag-ui` | `agUI` | @@ -379,6 +380,14 @@ boundaries, `tool-input-end` continues to complete a call immediately. `tool-call` and UI `tool-input-available` events also work without preceding deltas. Repeated completion events do not emit duplicate results. +For Amazon Bedrock, pass the `stream` returned by `ConverseStreamCommand` to +`readStructured` with `integration: bedrock`. The adapter correlates tool-use +blocks by `contentBlockIndex` and exposes `toolUseId` as the call ID. Text, +reasoning, and metadata events are ignored. Calls complete at their block stop +or EOF; malformed or truncated JSON fails and releases active parsers. Service +error events also fail the integration. AWS SDK types are accepted directly, +but the AWS SDK is not a runtime dependency of Streamfold. + The Gemini adapter accepts the Interactions API's `arguments_delta` events and the older `arguments` / `partial_arguments` shape. The empty object at `step.start` is a placeholder while deltas arrive. A call with no argument diff --git a/CHANGELOG.md b/CHANGELOG.md index 00f2fd0..6859859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Add a Bedrock ConverseStream adapter for streaming tool-use arguments. - Accept Anthropic stable and beta SDK message event interfaces directly in the Anthropic adapter and managed reader. - Accept OpenAI SDK response stream event types directly in the OpenAI adapter diff --git a/README.md b/README.md index cf150ef..addf393 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ Streamfold includes isolated adapters for decoded SDK events: | `streamfold/vercel-ai` | Vercel AI SDK | | `streamfold/openai` | OpenAI Responses | | `streamfold/anthropic` | Anthropic Messages | +| `streamfold/bedrock` | Amazon Bedrock ConverseStream | | `streamfold/gemini` | Gemini Interactions | | `streamfold/langchain` | LangChain message chunks | | `streamfold/ag-ui` | AG-UI events | diff --git a/examples/tsconfig.json b/examples/tsconfig.json index 2ae8283..5a01f2e 100644 --- a/examples/tsconfig.json +++ b/examples/tsconfig.json @@ -5,5 +5,5 @@ "checkJs": true, "skipLibCheck": true }, - "files": ["assistant-ui.mjs", "vercel-ai.mjs", "custom.mjs", "../tests/langchain-sdk.ts", "../tests/gemini-sdk.ts", "../tests/openai-sdk.ts", "../tests/anthropic-sdk.ts"] + "files": ["assistant-ui.mjs", "vercel-ai.mjs", "custom.mjs", "../tests/langchain-sdk.ts", "../tests/gemini-sdk.ts", "../tests/openai-sdk.ts", "../tests/anthropic-sdk.ts", "../tests/bedrock-sdk.ts"] } diff --git a/package.json b/package.json index 14e6f3a..33e6fdd 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ }, "devDependencies": { "@anthropic-ai/sdk": "1.3.0", + "@aws-sdk/client-bedrock-runtime": "3.1159.0", "@google/genai": "2.31.0", "@langchain/core": "1.3.0", "ai": "7.0.152", diff --git a/packages/core/package.json b/packages/core/package.json index 03d6158..7d0fe43 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -28,6 +28,10 @@ "types": "./src/anthropic.d.ts", "default": "./src/anthropic.js" }, + "./bedrock": { + "types": "./src/bedrock.d.ts", + "default": "./src/bedrock.js" + }, "./assistant-ui": { "types": "./src/assistant-ui.d.ts", "default": "./src/assistant-ui.js" diff --git a/packages/core/src/bedrock.d.ts b/packages/core/src/bedrock.d.ts new file mode 100644 index 0000000..699ab08 --- /dev/null +++ b/packages/core/src/bedrock.d.ts @@ -0,0 +1,31 @@ +import type { + BatchEventStructuredStream, + BatchStructuredStreamIntegration, + StructuredStreamIntegrationOptions, + StructuredStreamPool, +} from "./index.js"; + +export type BedrockToolCallEvent = + | { + readonly contentBlockStart?: { + readonly contentBlockIndex?: number | undefined; + readonly start?: { + readonly toolUse?: { readonly toolUseId?: string | undefined } | undefined; + } | undefined; + } | undefined; + readonly contentBlockDelta?: { + readonly contentBlockIndex?: number | undefined; + readonly delta?: { + readonly toolUse?: { readonly input?: string | undefined } | undefined; + } | undefined; + } | undefined; + readonly contentBlockStop?: { readonly contentBlockIndex?: number | undefined } | undefined; + } + | { readonly [key: string]: unknown }; + +export const bedrock: BatchStructuredStreamIntegration; + +export function createStructuredStream( + pool?: StructuredStreamPool, + options?: StructuredStreamIntegrationOptions, +): BatchEventStructuredStream; diff --git a/packages/core/src/bedrock.js b/packages/core/src/bedrock.js new file mode 100644 index 0000000..e1a94b7 --- /dev/null +++ b/packages/core/src/bedrock.js @@ -0,0 +1,51 @@ +import { createStructuredStreamPool } from "./index.js"; +import { createIntegration } from "./internal/integration.js"; + +export const bedrock = (pool = createStructuredStreamPool(), options) => { + const idsByIndex = new Map(); + return createIntegration( + pool, + (event, calls) => { + const failure = event.internalServerException ?? event.modelStreamErrorException ?? + event.validationException ?? event.throttlingException ?? event.serviceUnavailableException; + if (failure) throw failure instanceof Error ? failure : new Error(failure.message ?? "Bedrock stream failed"); + + const start = event.contentBlockStart; + if (start?.start?.toolUse) { + if (start.contentBlockIndex === undefined) { + calls.unmatched("Tool block start has no content block index"); + return undefined; + } + const id = start.start.toolUse.toolUseId; + if (id !== undefined) idsByIndex.set(start.contentBlockIndex, id); + return calls.start(id); + } + const delta = event.contentBlockDelta; + if (delta?.delta?.toolUse) { + const id = idsByIndex.get(delta.contentBlockIndex); + if (id === undefined) { + calls.unmatched("Arguments delta has no matching tool block index"); + return undefined; + } + return calls.push(id, delta.delta.toolUse.input ?? ""); + } + const stop = event.contentBlockStop; + if (stop) { + const id = idsByIndex.get(stop.contentBlockIndex); + if (id !== undefined) { + idsByIndex.delete(stop.contentBlockIndex); + return calls.complete(id); + } + } + return undefined; + }, + (complete) => { + for (const id of idsByIndex.values()) complete(id); + idsByIndex.clear(); + }, + "bedrock", + options, + ); +}; + +export { bedrock as createStructuredStream }; diff --git a/packages/core/src/bedrock.test.js b/packages/core/src/bedrock.test.js new file mode 100644 index 0000000..6b5607d --- /dev/null +++ b/packages/core/src/bedrock.test.js @@ -0,0 +1,60 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { createStructuredStreamPool, readStructured } from "./index.js"; +import { bedrock } from "./bedrock.js"; + +const start = (index, id) => ({ contentBlockStart: { contentBlockIndex: index, start: { toolUse: { toolUseId: id, name: "weather" } } } }); +const delta = (index, input) => ({ contentBlockDelta: { contentBlockIndex: index, delta: { toolUse: { input } } } }); +const stop = (index) => ({ contentBlockStop: { contentBlockIndex: index } }); + +test("Bedrock correlates interleaved tool blocks and ignores other content", async () => { + const events = [ + { messageStart: { role: "assistant" } }, + { contentBlockDelta: { contentBlockIndex: 0, delta: { text: "Checking weather" } } }, + stop(0), start(1, "paris"), start(2, "tokyo"), + delta(1, '{"city":"Pa'), delta(2, '{"city":"Tokyo"}'), delta(1, 'ris"}'), + stop(2), stop(1), stop(1), { messageStop: { stopReason: "tool_use" } }, + { metadata: { usage: { inputTokens: 10, outputTokens: 20 } } }, + ]; + const updates = []; + for await (const update of readStructured(events, { integration: bedrock, limits: { snapshots: "immutable" } })) updates.push(update); + assert.deepEqual(updates.map(({ type, id }) => [type, id]), [ + ["start", "paris"], ["start", "tokyo"], ["update", "paris"], ["update", "tokyo"], + ["update", "paris"], ["complete", "tokyo"], ["complete", "paris"], + ]); + assert.deepEqual(updates[2].partialValue, { city: "Pa" }); + assert.deepEqual(updates.slice(-2).map(({ value }) => value), [{ city: "Tokyo" }, { city: "Paris" }]); +}); + +test("Bedrock finalizes complete pending inputs at EOF and preserves manual history", () => { + const stream = bedrock(); + stream.push(start(0, "empty")); + stream.push(delta(0, "{}")); + assert.deepEqual(stream.finish().map(({ id, value }) => ({ id, value })), [{ id: "empty", value: {} }]); + assert.equal(stream.finish().length, 1); +}); + +test("Bedrock rejects truncation, parser limits, and service errors without retaining active calls", () => { + for (const [options, input, ending, message] of [ + [{}, '{"city":', stop(0), /complete|incomplete/i], + [{ maxBytes: 4 }, '{"city":"Paris"}', stop(0), /maxBytes/], + [{}, '{"city":"Paris"}', { modelStreamErrorException: { message: "model failed" } }, /model failed/], + ]) { + const pool = createStructuredStreamPool(options); + const stream = bedrock(pool); + stream.push(start(0, "first")); + stream.push(start(1, "second")); + assert.throws(() => { stream.push(delta(0, input)); stream.push(ending); }, message); + assert.equal(pool.size, 0); + assert.throws(() => stream.finish(), message); + } +}); + +test("Bedrock diagnoses uncorrelated deltas and missing block indexes", () => { + const diagnostics = []; + const stream = bedrock(undefined, { onDiagnostic: (diagnostic) => diagnostics.push(diagnostic) }); + stream.push({ contentBlockStart: { start: { toolUse: { toolUseId: "missing-index" } } } }); + stream.push(delta(8, "{}")); + assert.deepEqual(stream.finish(), []); + assert.equal(diagnostics.filter(({ code }) => code === "UNMATCHED_TOOL_EVENT").length, 2); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a114986..f422b00 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,12 +11,15 @@ importers: '@anthropic-ai/sdk': specifier: 1.3.0 version: 1.3.0(zod@4.6.7) + '@aws-sdk/client-bedrock-runtime': + specifier: 3.1159.0 + version: 3.1159.0 '@google/genai': specifier: 2.31.0 version: 2.31.0 '@langchain/core': specifier: 1.3.0 - version: 1.3.0(@opentelemetry/api@1.10.0)(openai@7.45.1(google-auth-library@10.9.1)(undici@7.30.1)(ws@8.22.0)(zod@4.6.7))(ws@8.22.0) + version: 1.3.0(@opentelemetry/api@1.10.0)(openai@7.45.1(@aws-sdk/credential-provider-node@3.972.90)(@smithy/signature-v4@5.7.4)(google-auth-library@10.9.1)(undici@7.30.1)(ws@8.22.0)(zod@4.6.7))(ws@8.22.0) ai: specifier: 7.0.152 version: 7.0.152(zod@4.6.7) @@ -31,7 +34,7 @@ importers: version: 4.10.2 openai: specifier: 7.45.1 - version: 7.45.1(google-auth-library@10.9.1)(undici@7.30.1)(ws@8.22.0)(zod@4.6.7) + version: 7.45.1(@aws-sdk/credential-provider-node@3.972.90)(@smithy/signature-v4@5.7.4)(google-auth-library@10.9.1)(undici@7.30.1)(ws@8.22.0)(zod@4.6.7) playwright: specifier: ^1.63.0 version: 1.63.0 @@ -74,6 +77,82 @@ packages: zod: optional: true + '@aws-sdk/client-bedrock-runtime@3.1159.0': + resolution: {integrity: sha512-Zj744tR9wNs+jjvb7C8zDBDxjJhGJSMwIHnOc9zdsDdrRirai4CtR5iQ5BMP1qfazjH6jYKCxGpZC1BOUnslKg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.978.7': + resolution: {integrity: sha512-QsYdn5xhvEBzcDC6bgKuvPv87kFtXK/3f5NqKfR+eok5AAn7RaZnkgBKncX+UW3ZMcjSELyAMATN0iqgZmvWVg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.78': + resolution: {integrity: sha512-d8V47OxkM/FKe7bVfk5Rga4aNGUaQNk3ip1PFIYhxtup9r8SuNIHLs4HfBAmHT25ZbzowluMHp833UXfFMQbhA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.80': + resolution: {integrity: sha512-OQqanXiRhnl/wBIJc3TtA7TYmnN7VO0mP95eVoHsLXtPwHz6HeccMpi7cqtnhHw+K7p6VIoAt2IK4M9Ixd9uGg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.973.23': + resolution: {integrity: sha512-pTTRK7xoi8NNbrcjVUSd9YdMTUxiWEPfk8newBQCzV+VS5STh5sDPAy3iO29fSqipqNuCpILJcD6wAawxHvG+w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.85': + resolution: {integrity: sha512-UHjnTeS+5OBFfRL+BQwCGJPJvnzjhGD6dIwncBJDa+ziyQjPUte9Q0I4BCkybGrHotnyiiluXj13bgelP14J5Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.90': + resolution: {integrity: sha512-+McgOQuAUze95AumVmoOBRp++Z8IjsZ0qOASb6+igHdKNzA0FiIf5kbTFaZ64txfd9KTBDpgvlEDGQE41lSq/A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.78': + resolution: {integrity: sha512-bzB92SriflYf6LVRJcDv/a9I87tmkdmdE8hAqT1oRGKFkEH2rxxHeHZVK/4vsiRsFi44cUj0J9+bUSmqYX+26w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.973.22': + resolution: {integrity: sha512-MXUkWHgcf6G+TowL+QPvn4kJTEYKd9qHKMV6eNGXSA5lilDgGRrP3EXPHChZ3eZ7z+oprn/ZvOovLoS2WR3D8g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.84': + resolution: {integrity: sha512-6I3zYcDHt5ZM0J4gEuRgCHgN505k5haUiiPM46iIQgrHm2p70zpnssHFVYqHYfgJe3Mf+a5+nsF3BZmW4xcZNg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/eventstream-handler-node@3.972.39': + resolution: {integrity: sha512-V+AvQ+KFWApgm9134igy78cgoCEAFfV8K3KmlVi511vjkRWAgvy0tGUePrZsER+9EvrmUWEsrA2NBQspiGVBMQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-eventstream@3.972.35': + resolution: {integrity: sha512-JAyjfoYxHCXNnCXtbTviEpb1+gxe8tsWQ3URwZIT1Bqhrm4fgDDuCA72n4HVnHMNutBhZLXnYgc97ehkhfGpMA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-websocket@3.972.60': + resolution: {integrity: sha512-IgxzXK1c0r/2yjC/qJc06OZPzQ3FSxuIc/Au6chqMb0H60k1+sHIOgjcqsxl1BxUTGQr//BU5A3niytx0IvICA==} + engines: {node: '>= 14.0.0'} + + '@aws-sdk/nested-clients@3.997.52': + resolution: {integrity: sha512-/bNl6b3VEoWQg3Z8yHt3gzYjSFoOOv7+eUglzITmGP8H7jovN8c9NoOI+6bU25+lSLvyRpJOs4n8YSaX+BcUPg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.996.50': + resolution: {integrity: sha512-Ob9GCZ3YnGPws696zTYW/rLFYHa9QLh6DgTCnkHb+0jhHNBgHWM8Ui2b+yrIjkfDsRVsglyrOey6tOOG/VWxMA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1159.0': + resolution: {integrity: sha512-IJvghXXcEpqTWGjrjJwxBdEecHeXp+t9FaybRf8Zf0wYW84KN1snXBsYOdfDBBx78ChwfJxH8KIHqmhNZuDLcw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.975.0': + resolution: {integrity: sha512-WQlhs6juByzWOzj5fwxOx0mPJaEqMcG/sDpl3Id61B3XhKE63xY+0QWcnG+sWR/sCfprmreyUo1lM7IKZmHMzw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/xml-builder@3.972.42': + resolution: {integrity: sha512-smX8/gNP8Kjq+CWTBMZQLQ912vOQjiWExzwzvMO+eujPB5RY8IbtF0AGdUUXHKN7KoDWK9dIcuW0WG269kiP6w==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.3.0': + resolution: {integrity: sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==} + engines: {node: '>=18.0.0'} + '@babel/runtime@7.29.10': resolution: {integrity: sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA==} engines: {node: '>=6.9.0'} @@ -128,6 +207,30 @@ packages: '@quansync/fs@1.0.0': resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} + '@smithy/core@3.37.0': + resolution: {integrity: sha512-97imTcCyiPd8k0e9+ts/Sp0W65Dn9Ti357J7M09Ov4tI8Q1JDJVFSoAJtjEVKqfHEr3Pdo2jfolqKCaaiszH0g==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.5.2': + resolution: {integrity: sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.8.0': + resolution: {integrity: sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.13.0': + resolution: {integrity: sha512-/GGcPoNJ2nzGytdAanpbx7mpp9Coa8LP+/Rpmbktr8YeLuIXs9B0SEz+X3AeonHdyzOIFJdKbN7jOZ+Tj+pMGw==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.7.4': + resolution: {integrity: sha512-tHy0K0VtqNd5Y7Y41h0a0Lhh0L1GzC08dTWg0F7vRJWFtTENg7IZikf3wQkanYIRdb7ngoIPMTmqgUi401fEeQ==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.19.0': + resolution: {integrity: sha512-r7jh49VJxGerfAcTQA6gXcKc+98zOp/tqRwzYjgOE+iSQsP6cEU1hq2QzbuipmP68QtYdY9wKEhiCQZIzHgZ4Q==} + engines: {node: '>=18.0.0'} + '@stablelib/base64@1.0.1': resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} @@ -297,6 +400,9 @@ packages: bignumber.js@9.3.1: resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + bowser@2.15.0: + resolution: {integrity: sha512-xic6KSZglg/I4ymp45fdM4GWfYpBK79Fxd/TOvJktlaPgQqqDxvmGwOPZl2V0dyeS3Lj5c180bLhXF43iyAjZQ==} + buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} @@ -546,6 +652,9 @@ packages: ts-algebra@2.0.0: resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + typescript@7.0.2: resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} engines: {node: '>=16.20.0'} @@ -622,6 +731,179 @@ snapshots: optionalDependencies: zod: 4.6.7 + '@aws-sdk/client-bedrock-runtime@3.1159.0': + dependencies: + '@aws-sdk/core': 3.978.7 + '@aws-sdk/credential-provider-node': 3.972.90 + '@aws-sdk/eventstream-handler-node': 3.972.39 + '@aws-sdk/middleware-eventstream': 3.972.35 + '@aws-sdk/middleware-websocket': 3.972.60 + '@aws-sdk/token-providers': 3.1159.0 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.37.0 + '@smithy/fetch-http-handler': 5.8.0 + '@smithy/node-http-handler': 4.13.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/core@3.978.7': + dependencies: + '@aws-sdk/types': 3.975.0 + '@aws-sdk/xml-builder': 3.972.42 + '@aws/lambda-invoke-store': 0.3.0 + '@smithy/core': 3.37.0 + '@smithy/signature-v4': 5.7.4 + '@smithy/types': 4.19.0 + bowser: 2.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.78': + dependencies: + '@aws-sdk/core': 3.978.7 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.37.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.80': + dependencies: + '@aws-sdk/core': 3.978.7 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.37.0 + '@smithy/fetch-http-handler': 5.8.0 + '@smithy/node-http-handler': 4.13.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.973.23': + dependencies: + '@aws-sdk/core': 3.978.7 + '@aws-sdk/credential-provider-env': 3.972.78 + '@aws-sdk/credential-provider-http': 3.972.80 + '@aws-sdk/credential-provider-login': 3.972.85 + '@aws-sdk/credential-provider-process': 3.972.78 + '@aws-sdk/credential-provider-sso': 3.973.22 + '@aws-sdk/credential-provider-web-identity': 3.972.84 + '@aws-sdk/nested-clients': 3.997.52 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.37.0 + '@smithy/credential-provider-imds': 4.5.2 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-login@3.972.85': + dependencies: + '@aws-sdk/core': 3.978.7 + '@aws-sdk/nested-clients': 3.997.52 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.37.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-node@3.972.90': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.78 + '@aws-sdk/credential-provider-http': 3.972.80 + '@aws-sdk/credential-provider-ini': 3.973.23 + '@aws-sdk/credential-provider-process': 3.972.78 + '@aws-sdk/credential-provider-sso': 3.973.22 + '@aws-sdk/credential-provider-web-identity': 3.972.84 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.37.0 + '@smithy/credential-provider-imds': 4.5.2 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-process@3.972.78': + dependencies: + '@aws-sdk/core': 3.978.7 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.37.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.973.22': + dependencies: + '@aws-sdk/core': 3.978.7 + '@aws-sdk/nested-clients': 3.997.52 + '@aws-sdk/token-providers': 3.1159.0 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.37.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-web-identity@3.972.84': + dependencies: + '@aws-sdk/core': 3.978.7 + '@aws-sdk/nested-clients': 3.997.52 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.37.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/eventstream-handler-node@3.972.39': + dependencies: + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.37.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-eventstream@3.972.35': + dependencies: + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.37.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-websocket@3.972.60': + dependencies: + '@aws-sdk/core': 3.978.7 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.37.0 + '@smithy/fetch-http-handler': 5.8.0 + '@smithy/signature-v4': 5.7.4 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.997.52': + dependencies: + '@aws-sdk/core': 3.978.7 + '@aws-sdk/signature-v4-multi-region': 3.996.50 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.37.0 + '@smithy/fetch-http-handler': 5.8.0 + '@smithy/node-http-handler': 4.13.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.996.50': + dependencies: + '@aws-sdk/types': 3.975.0 + '@smithy/signature-v4': 5.7.4 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1159.0': + dependencies: + '@aws-sdk/core': 3.978.7 + '@aws-sdk/nested-clients': 3.997.52 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.37.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/types@3.975.0': + dependencies: + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.42': + dependencies: + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.3.0': {} + '@babel/runtime@7.29.10': {} '@cfworker/json-schema@4.1.1': {} @@ -637,12 +919,12 @@ snapshots: - supports-color - utf-8-validate - '@langchain/core@1.3.0(@opentelemetry/api@1.10.0)(openai@7.45.1(google-auth-library@10.9.1)(undici@7.30.1)(ws@8.22.0)(zod@4.6.7))(ws@8.22.0)': + '@langchain/core@1.3.0(@opentelemetry/api@1.10.0)(openai@7.45.1(@aws-sdk/credential-provider-node@3.972.90)(@smithy/signature-v4@5.7.4)(google-auth-library@10.9.1)(undici@7.30.1)(ws@8.22.0)(zod@4.6.7))(ws@8.22.0)': dependencies: '@cfworker/json-schema': 4.1.1 '@standard-schema/spec': 1.1.0 js-tiktoken: 1.0.21 - langsmith: 0.11.4(@opentelemetry/api@1.10.0)(openai@7.45.1(google-auth-library@10.9.1)(undici@7.30.1)(ws@8.22.0)(zod@4.6.7))(ws@8.22.0) + langsmith: 0.11.4(@opentelemetry/api@1.10.0)(openai@7.45.1(@aws-sdk/credential-provider-node@3.972.90)(@smithy/signature-v4@5.7.4)(google-auth-library@10.9.1)(undici@7.30.1)(ws@8.22.0)(zod@4.6.7))(ws@8.22.0) mustache: 4.2.0 p-queue: 6.6.2 zod: 4.6.7 @@ -679,6 +961,39 @@ snapshots: dependencies: quansync: 1.0.0 + '@smithy/core@3.37.0': + dependencies: + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.5.2': + dependencies: + '@smithy/core': 3.37.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.8.0': + dependencies: + '@smithy/core': 3.37.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.13.0': + dependencies: + '@smithy/core': 3.37.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/signature-v4@5.7.4': + dependencies: + '@smithy/core': 3.37.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/types@4.19.0': + dependencies: + tslib: 2.8.1 + '@stablelib/base64@1.0.1': {} '@standard-schema/spec@1.1.0': {} @@ -774,6 +1089,8 @@ snapshots: bignumber.js@9.3.1: {} + bowser@2.15.0: {} + buffer-equal-constant-time@1.0.1: {} bumpp@12.3.0: @@ -893,12 +1210,12 @@ snapshots: jwa: 2.0.1 safe-buffer: 5.2.1 - langsmith@0.11.4(@opentelemetry/api@1.10.0)(openai@7.45.1(google-auth-library@10.9.1)(undici@7.30.1)(ws@8.22.0)(zod@4.6.7))(ws@8.22.0): + langsmith@0.11.4(@opentelemetry/api@1.10.0)(openai@7.45.1(@aws-sdk/credential-provider-node@3.972.90)(@smithy/signature-v4@5.7.4)(google-auth-library@10.9.1)(undici@7.30.1)(ws@8.22.0)(zod@4.6.7))(ws@8.22.0): dependencies: p-queue: 6.6.2 optionalDependencies: '@opentelemetry/api': 1.10.0 - openai: 7.45.1(google-auth-library@10.9.1)(undici@7.30.1)(ws@8.22.0)(zod@4.6.7) + openai: 7.45.1(@aws-sdk/credential-provider-node@3.972.90)(@smithy/signature-v4@5.7.4)(google-auth-library@10.9.1)(undici@7.30.1)(ws@8.22.0)(zod@4.6.7) ws: 8.22.0 long@5.3.2: {} @@ -917,8 +1234,10 @@ snapshots: fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 - openai@7.45.1(google-auth-library@10.9.1)(undici@7.30.1)(ws@8.22.0)(zod@4.6.7): + openai@7.45.1(@aws-sdk/credential-provider-node@3.972.90)(@smithy/signature-v4@5.7.4)(google-auth-library@10.9.1)(undici@7.30.1)(ws@8.22.0)(zod@4.6.7): optionalDependencies: + '@aws-sdk/credential-provider-node': 3.972.90 + '@smithy/signature-v4': 5.7.4 google-auth-library: 10.9.1 undici: 7.30.1 ws: 8.22.0 @@ -988,6 +1307,8 @@ snapshots: ts-algebra@2.0.0: {} + tslib@2.8.1: {} + typescript@7.0.2: optionalDependencies: '@typescript/typescript-aix-ppc64': 7.0.2 diff --git a/tests/bedrock-sdk.ts b/tests/bedrock-sdk.ts new file mode 100644 index 0000000..7ba4fc9 --- /dev/null +++ b/tests/bedrock-sdk.ts @@ -0,0 +1,13 @@ +import { BedrockRuntimeClient, ConverseStreamCommand } from "@aws-sdk/client-bedrock-runtime"; +import { readStructured } from "streamfold"; +import { bedrock } from "streamfold/bedrock"; + +const client = new BedrockRuntimeClient({ region: "us-east-1" }); +const response = await client.send(new ConverseStreamCommand({ + modelId: "fixture", messages: [{ role: "user", content: [{ text: "Weather in Paris?" }] }], +})); +if (response.stream) { + readStructured(response.stream, { integration: bedrock }); + const stream = bedrock(); + for await (const event of response.stream) stream.pushAll(event); +}