@strands-agents/sdk 0.5.0 → 0.7.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/README.md +64 -0
- package/dist/src/__fixtures__/agent-helpers.d.ts +37 -4
- package/dist/src/__fixtures__/agent-helpers.d.ts.map +1 -1
- package/dist/src/__fixtures__/agent-helpers.js +31 -4
- package/dist/src/__fixtures__/agent-helpers.js.map +1 -1
- package/dist/src/__fixtures__/metrics-helpers.d.ts +55 -0
- package/dist/src/__fixtures__/metrics-helpers.d.ts.map +1 -0
- package/dist/src/__fixtures__/metrics-helpers.js +57 -0
- package/dist/src/__fixtures__/metrics-helpers.js.map +1 -0
- package/dist/src/__fixtures__/mock-message-model.d.ts +8 -4
- package/dist/src/__fixtures__/mock-message-model.d.ts.map +1 -1
- package/dist/src/__fixtures__/mock-message-model.js +25 -7
- package/dist/src/__fixtures__/mock-message-model.js.map +1 -1
- package/dist/src/__fixtures__/mock-meter.d.ts +32 -0
- package/dist/src/__fixtures__/mock-meter.d.ts.map +1 -0
- package/dist/src/__fixtures__/mock-meter.js +47 -0
- package/dist/src/__fixtures__/mock-meter.js.map +1 -0
- package/dist/src/__fixtures__/mock-plugin.d.ts +13 -0
- package/dist/src/__fixtures__/mock-plugin.d.ts.map +1 -0
- package/dist/src/__fixtures__/{mock-hook-provider.js → mock-plugin.js} +8 -5
- package/dist/src/__fixtures__/mock-plugin.js.map +1 -0
- package/dist/src/__fixtures__/mock-storage-provider.d.ts +5 -0
- package/dist/src/__fixtures__/mock-storage-provider.d.ts.map +1 -1
- package/dist/src/__fixtures__/mock-storage-provider.js +23 -6
- package/dist/src/__fixtures__/mock-storage-provider.js.map +1 -1
- package/dist/src/__fixtures__/slim-types.d.ts +2 -1
- package/dist/src/__fixtures__/slim-types.d.ts.map +1 -1
- package/dist/src/__fixtures__/tool-helpers.d.ts.map +1 -1
- package/dist/src/__fixtures__/tool-helpers.js +5 -2
- package/dist/src/__fixtures__/tool-helpers.js.map +1 -1
- package/dist/src/__tests__/index.test.js +21 -0
- package/dist/src/__tests__/index.test.js.map +1 -1
- package/dist/src/__tests__/mcp.test.js +45 -15
- package/dist/src/__tests__/mcp.test.js.map +1 -1
- package/dist/src/__tests__/mime.test.d.ts +2 -0
- package/dist/src/__tests__/mime.test.d.ts.map +1 -0
- package/dist/src/__tests__/mime.test.js +83 -0
- package/dist/src/__tests__/mime.test.js.map +1 -0
- package/dist/src/__tests__/state-store.test.d.ts +2 -0
- package/dist/src/__tests__/state-store.test.d.ts.map +1 -0
- package/dist/src/__tests__/{app-state.test.js → state-store.test.js} +86 -51
- package/dist/src/__tests__/state-store.test.js.map +1 -0
- package/dist/src/a2a/__tests__/a2a-agent.test.d.ts +2 -0
- package/dist/src/a2a/__tests__/a2a-agent.test.d.ts.map +1 -0
- package/dist/src/a2a/__tests__/a2a-agent.test.js +364 -0
- package/dist/src/a2a/__tests__/a2a-agent.test.js.map +1 -0
- package/dist/src/a2a/__tests__/adapters.test.d.ts +2 -0
- package/dist/src/a2a/__tests__/adapters.test.d.ts.map +1 -0
- package/dist/src/a2a/__tests__/adapters.test.js +151 -0
- package/dist/src/a2a/__tests__/adapters.test.js.map +1 -0
- package/dist/src/a2a/__tests__/executor.test.d.ts +2 -0
- package/dist/src/a2a/__tests__/executor.test.d.ts.map +1 -0
- package/dist/src/a2a/__tests__/executor.test.js +196 -0
- package/dist/src/a2a/__tests__/executor.test.js.map +1 -0
- package/dist/src/a2a/__tests__/server.test.d.ts +2 -0
- package/dist/src/a2a/__tests__/server.test.d.ts.map +1 -0
- package/dist/src/a2a/__tests__/server.test.js +51 -0
- package/dist/src/a2a/__tests__/server.test.js.map +1 -0
- package/dist/src/a2a/__tests__/server.test.node.d.ts +2 -0
- package/dist/src/a2a/__tests__/server.test.node.d.ts.map +1 -0
- package/dist/src/a2a/__tests__/server.test.node.js +110 -0
- package/dist/src/a2a/__tests__/server.test.node.js.map +1 -0
- package/dist/src/a2a/a2a-agent.d.ts +132 -0
- package/dist/src/a2a/a2a-agent.d.ts.map +1 -0
- package/dist/src/a2a/a2a-agent.js +255 -0
- package/dist/src/a2a/a2a-agent.js.map +1 -0
- package/dist/src/a2a/adapters.d.ts +27 -0
- package/dist/src/a2a/adapters.d.ts.map +1 -0
- package/dist/src/a2a/adapters.js +175 -0
- package/dist/src/a2a/adapters.js.map +1 -0
- package/dist/src/a2a/events.d.ts +42 -0
- package/dist/src/a2a/events.d.ts.map +1 -0
- package/dist/src/a2a/events.js +35 -0
- package/dist/src/a2a/events.js.map +1 -0
- package/dist/src/a2a/executor.d.ts +57 -0
- package/dist/src/a2a/executor.d.ts.map +1 -0
- package/dist/src/a2a/executor.js +130 -0
- package/dist/src/a2a/executor.js.map +1 -0
- package/dist/src/a2a/express-server.d.ts +67 -0
- package/dist/src/a2a/express-server.d.ts.map +1 -0
- package/dist/src/a2a/express-server.js +95 -0
- package/dist/src/a2a/express-server.js.map +1 -0
- package/dist/src/a2a/index.d.ts +16 -0
- package/dist/src/a2a/index.d.ts.map +1 -0
- package/dist/src/a2a/index.js +16 -0
- package/dist/src/a2a/index.js.map +1 -0
- package/dist/src/a2a/logging.d.ts +8 -0
- package/dist/src/a2a/logging.d.ts.map +1 -0
- package/dist/src/a2a/logging.js +15 -0
- package/dist/src/a2a/logging.js.map +1 -0
- package/dist/src/a2a/server.d.ts +67 -0
- package/dist/src/a2a/server.d.ts.map +1 -0
- package/dist/src/a2a/server.js +67 -0
- package/dist/src/a2a/server.js.map +1 -0
- package/dist/src/agent/__tests__/agent.hook.test.js +87 -51
- package/dist/src/agent/__tests__/agent.hook.test.js.map +1 -1
- package/dist/src/agent/__tests__/agent.test.js +176 -101
- package/dist/src/agent/__tests__/agent.test.js.map +1 -1
- package/dist/src/agent/__tests__/agent.tracer.test.js +10 -10
- package/dist/src/agent/__tests__/agent.tracer.test.js.map +1 -1
- package/dist/src/agent/__tests__/snapshot.test.js +11 -11
- package/dist/src/agent/__tests__/snapshot.test.js.map +1 -1
- package/dist/src/agent/agent.d.ts +71 -58
- package/dist/src/agent/agent.d.ts.map +1 -1
- package/dist/src/agent/agent.js +177 -93
- package/dist/src/agent/agent.js.map +1 -1
- package/dist/src/agent/snapshot.d.ts.map +1 -1
- package/dist/src/agent/snapshot.js +3 -2
- package/dist/src/agent/snapshot.js.map +1 -1
- package/dist/src/conversation-manager/__tests__/conversation-manager.test.d.ts +2 -0
- package/dist/src/conversation-manager/__tests__/conversation-manager.test.d.ts.map +1 -0
- package/dist/src/conversation-manager/__tests__/conversation-manager.test.js +100 -0
- package/dist/src/conversation-manager/__tests__/conversation-manager.test.js.map +1 -0
- package/dist/src/conversation-manager/__tests__/null-conversation-manager.test.js +26 -10
- package/dist/src/conversation-manager/__tests__/null-conversation-manager.test.js.map +1 -1
- package/dist/src/conversation-manager/__tests__/sliding-window-conversation-manager.test.js +84 -21
- package/dist/src/conversation-manager/__tests__/sliding-window-conversation-manager.test.js.map +1 -1
- package/dist/src/conversation-manager/conversation-manager.d.ts +87 -0
- package/dist/src/conversation-manager/conversation-manager.d.ts.map +1 -0
- package/dist/src/conversation-manager/conversation-manager.js +59 -0
- package/dist/src/conversation-manager/conversation-manager.js.map +1 -0
- package/dist/src/conversation-manager/index.d.ts +1 -0
- package/dist/src/conversation-manager/index.d.ts.map +1 -1
- package/dist/src/conversation-manager/index.js +1 -0
- package/dist/src/conversation-manager/index.js.map +1 -1
- package/dist/src/conversation-manager/null-conversation-manager.d.ts +12 -8
- package/dist/src/conversation-manager/null-conversation-manager.d.ts.map +1 -1
- package/dist/src/conversation-manager/null-conversation-manager.js +13 -7
- package/dist/src/conversation-manager/null-conversation-manager.js.map +1 -1
- package/dist/src/conversation-manager/sliding-window-conversation-manager.d.ts +28 -19
- package/dist/src/conversation-manager/sliding-window-conversation-manager.d.ts.map +1 -1
- package/dist/src/conversation-manager/sliding-window-conversation-manager.js +44 -36
- package/dist/src/conversation-manager/sliding-window-conversation-manager.js.map +1 -1
- package/dist/src/errors.d.ts +6 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +9 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/hooks/__tests__/events.test.js +2 -0
- package/dist/src/hooks/__tests__/events.test.js.map +1 -1
- package/dist/src/hooks/__tests__/registry.test.js +10 -154
- package/dist/src/hooks/__tests__/registry.test.js.map +1 -1
- package/dist/src/hooks/events.d.ts +60 -44
- package/dist/src/hooks/events.d.ts.map +1 -1
- package/dist/src/hooks/events.js +11 -11
- package/dist/src/hooks/events.js.map +1 -1
- package/dist/src/hooks/index.d.ts +4 -4
- package/dist/src/hooks/index.d.ts.map +1 -1
- package/dist/src/hooks/index.js +2 -2
- package/dist/src/hooks/registry.d.ts +1 -32
- package/dist/src/hooks/registry.d.ts.map +1 -1
- package/dist/src/hooks/registry.js +1 -47
- package/dist/src/hooks/registry.js.map +1 -1
- package/dist/src/hooks/types.d.ts +0 -31
- package/dist/src/hooks/types.d.ts.map +1 -1
- package/dist/src/index.d.ts +30 -15
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +21 -8
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp.d.ts +38 -0
- package/dist/src/mcp.d.ts.map +1 -1
- package/dist/src/mcp.js +23 -6
- package/dist/src/mcp.js.map +1 -1
- package/dist/src/mime.d.ts +24 -0
- package/dist/src/mime.d.ts.map +1 -0
- package/dist/src/mime.js +82 -0
- package/dist/src/mime.js.map +1 -0
- package/dist/src/models/__tests__/anthropic.test.js +78 -1
- package/dist/src/models/__tests__/anthropic.test.js.map +1 -1
- package/dist/src/models/__tests__/bedrock.test.js +2050 -131
- package/dist/src/models/__tests__/bedrock.test.js.map +1 -1
- package/dist/src/models/__tests__/gemini.test.js +100 -1
- package/dist/src/models/__tests__/gemini.test.js.map +1 -1
- package/dist/src/models/__tests__/model.test.js +131 -0
- package/dist/src/models/__tests__/model.test.js.map +1 -1
- package/dist/src/models/__tests__/openai.test.js +184 -32
- package/dist/src/models/__tests__/openai.test.js.map +1 -1
- package/dist/src/models/__tests__/streaming.test.d.ts +2 -0
- package/dist/src/models/__tests__/streaming.test.d.ts.map +1 -0
- package/dist/src/models/__tests__/streaming.test.js +50 -0
- package/dist/src/models/__tests__/streaming.test.js.map +1 -0
- package/dist/src/models/anthropic.d.ts.map +1 -1
- package/dist/src/models/anthropic.js +6 -7
- package/dist/src/models/anthropic.js.map +1 -1
- package/dist/src/models/bedrock.d.ts +144 -11
- package/dist/src/models/bedrock.d.ts.map +1 -1
- package/dist/src/models/bedrock.js +416 -28
- package/dist/src/models/bedrock.js.map +1 -1
- package/dist/src/models/gemini/adapters.d.ts.map +1 -1
- package/dist/src/models/gemini/adapters.js +65 -14
- package/dist/src/models/gemini/adapters.js.map +1 -1
- package/dist/src/models/model.d.ts +18 -0
- package/dist/src/models/model.d.ts.map +1 -1
- package/dist/src/models/model.js +57 -11
- package/dist/src/models/model.js.map +1 -1
- package/dist/src/models/openai.d.ts +15 -0
- package/dist/src/models/openai.d.ts.map +1 -1
- package/dist/src/models/openai.js +108 -64
- package/dist/src/models/openai.js.map +1 -1
- package/dist/src/models/streaming.d.ts +88 -2
- package/dist/src/models/streaming.d.ts.map +1 -1
- package/dist/src/models/streaming.js +26 -0
- package/dist/src/models/streaming.js.map +1 -1
- package/dist/src/multiagent/__tests__/events.test.js +41 -8
- package/dist/src/multiagent/__tests__/events.test.js.map +1 -1
- package/dist/src/multiagent/__tests__/graph.test.d.ts +2 -0
- package/dist/src/multiagent/__tests__/graph.test.d.ts.map +1 -0
- package/dist/src/multiagent/__tests__/graph.test.js +453 -0
- package/dist/src/multiagent/__tests__/graph.test.js.map +1 -0
- package/dist/src/multiagent/__tests__/nodes.test.js +34 -16
- package/dist/src/multiagent/__tests__/nodes.test.js.map +1 -1
- package/dist/src/multiagent/__tests__/queue.test.js +22 -0
- package/dist/src/multiagent/__tests__/queue.test.js.map +1 -1
- package/dist/src/multiagent/__tests__/swarm.test.d.ts +2 -0
- package/dist/src/multiagent/__tests__/swarm.test.d.ts.map +1 -0
- package/dist/src/multiagent/__tests__/swarm.test.js +264 -0
- package/dist/src/multiagent/__tests__/swarm.test.js.map +1 -0
- package/dist/src/multiagent/edge.d.ts +9 -2
- package/dist/src/multiagent/edge.d.ts.map +1 -1
- package/dist/src/multiagent/events.d.ts +63 -15
- package/dist/src/multiagent/events.d.ts.map +1 -1
- package/dist/src/multiagent/events.js +28 -3
- package/dist/src/multiagent/events.js.map +1 -1
- package/dist/src/multiagent/graph.d.ts +135 -0
- package/dist/src/multiagent/graph.d.ts.map +1 -0
- package/dist/src/multiagent/graph.js +400 -0
- package/dist/src/multiagent/graph.js.map +1 -0
- package/dist/src/multiagent/index.d.ts +8 -3
- package/dist/src/multiagent/index.d.ts.map +1 -1
- package/dist/src/multiagent/index.js +3 -1
- package/dist/src/multiagent/index.js.map +1 -1
- package/dist/src/multiagent/multiagent.d.ts +41 -0
- package/dist/src/multiagent/multiagent.d.ts.map +1 -0
- package/dist/src/multiagent/multiagent.js +2 -0
- package/dist/src/multiagent/multiagent.js.map +1 -0
- package/dist/src/multiagent/nodes.d.ts +24 -25
- package/dist/src/multiagent/nodes.d.ts.map +1 -1
- package/dist/src/multiagent/nodes.js +42 -15
- package/dist/src/multiagent/nodes.js.map +1 -1
- package/dist/src/multiagent/plugins.d.ts +70 -0
- package/dist/src/multiagent/plugins.d.ts.map +1 -0
- package/dist/src/multiagent/plugins.js +70 -0
- package/dist/src/multiagent/plugins.js.map +1 -0
- package/dist/src/multiagent/queue.d.ts +6 -0
- package/dist/src/multiagent/queue.d.ts.map +1 -1
- package/dist/src/multiagent/queue.js +13 -0
- package/dist/src/multiagent/queue.js.map +1 -1
- package/dist/src/multiagent/state.d.ts +4 -2
- package/dist/src/multiagent/state.d.ts.map +1 -1
- package/dist/src/multiagent/state.js +5 -2
- package/dist/src/multiagent/state.js.map +1 -1
- package/dist/src/multiagent/swarm.d.ts +112 -0
- package/dist/src/multiagent/swarm.d.ts.map +1 -0
- package/dist/src/multiagent/swarm.js +256 -0
- package/dist/src/multiagent/swarm.js.map +1 -0
- package/dist/src/plugins/__tests__/plugin.test.d.ts +2 -0
- package/dist/src/plugins/__tests__/plugin.test.d.ts.map +1 -0
- package/dist/src/plugins/__tests__/plugin.test.js +114 -0
- package/dist/src/plugins/__tests__/plugin.test.js.map +1 -0
- package/dist/src/plugins/__tests__/registry.test.d.ts +2 -0
- package/dist/src/plugins/__tests__/registry.test.d.ts.map +1 -0
- package/dist/src/plugins/__tests__/registry.test.js +147 -0
- package/dist/src/plugins/__tests__/registry.test.js.map +1 -0
- package/dist/src/plugins/index.d.ts +30 -0
- package/dist/src/plugins/index.d.ts.map +1 -0
- package/dist/src/plugins/index.js +30 -0
- package/dist/src/plugins/index.js.map +1 -0
- package/dist/src/plugins/plugin.d.ts +74 -0
- package/dist/src/plugins/plugin.d.ts.map +1 -0
- package/dist/src/plugins/plugin.js +8 -0
- package/dist/src/plugins/plugin.js.map +1 -0
- package/dist/src/plugins/registry.d.ts +25 -0
- package/dist/src/plugins/registry.d.ts.map +1 -0
- package/dist/src/plugins/registry.js +41 -0
- package/dist/src/plugins/registry.js.map +1 -0
- package/dist/src/registry/__tests__/tool-registry.test.d.ts +2 -0
- package/dist/src/registry/__tests__/tool-registry.test.d.ts.map +1 -0
- package/dist/src/registry/__tests__/tool-registry.test.js +124 -0
- package/dist/src/registry/__tests__/tool-registry.test.js.map +1 -0
- package/dist/src/registry/tool-registry.d.ts +32 -20
- package/dist/src/registry/tool-registry.d.ts.map +1 -1
- package/dist/src/registry/tool-registry.js +60 -158
- package/dist/src/registry/tool-registry.js.map +1 -1
- package/dist/src/session/__tests__/file-storage.test.node.js +75 -15
- package/dist/src/session/__tests__/file-storage.test.node.js.map +1 -1
- package/dist/src/session/__tests__/s3-storage.test.d.ts +2 -0
- package/dist/src/session/__tests__/s3-storage.test.d.ts.map +1 -0
- package/dist/src/session/__tests__/{s3-storage.test.node.js → s3-storage.test.js} +161 -75
- package/dist/src/session/__tests__/s3-storage.test.js.map +1 -0
- package/dist/src/session/__tests__/session-manager.test.d.ts +2 -0
- package/dist/src/session/__tests__/session-manager.test.d.ts.map +1 -0
- package/dist/src/session/__tests__/session-manager.test.js +443 -0
- package/dist/src/session/__tests__/session-manager.test.js.map +1 -0
- package/dist/src/session/__tests__/validation.test.js +28 -1
- package/dist/src/session/__tests__/validation.test.js.map +1 -1
- package/dist/src/session/file-storage.d.ts +53 -27
- package/dist/src/session/file-storage.d.ts.map +1 -1
- package/dist/src/session/file-storage.js +103 -52
- package/dist/src/session/file-storage.js.map +1 -1
- package/dist/src/session/index.d.ts +6 -14
- package/dist/src/session/index.d.ts.map +1 -1
- package/dist/src/session/index.js +4 -13
- package/dist/src/session/index.js.map +1 -1
- package/dist/src/session/s3-storage.d.ts +49 -20
- package/dist/src/session/s3-storage.d.ts.map +1 -1
- package/dist/src/session/s3-storage.js +120 -35
- package/dist/src/session/s3-storage.js.map +1 -1
- package/dist/src/session/session-manager.d.ts +87 -0
- package/dist/src/session/session-manager.d.ts.map +1 -0
- package/dist/src/session/session-manager.js +128 -0
- package/dist/src/session/session-manager.js.map +1 -0
- package/dist/src/session/storage.d.ts +20 -12
- package/dist/src/session/storage.d.ts.map +1 -1
- package/dist/src/session/types.d.ts +8 -20
- package/dist/src/session/types.d.ts.map +1 -1
- package/dist/src/session/validation.d.ts +7 -0
- package/dist/src/session/validation.d.ts.map +1 -1
- package/dist/src/session/validation.js +12 -0
- package/dist/src/session/validation.js.map +1 -1
- package/dist/src/{app-state.d.ts → state-store.d.ts} +11 -11
- package/dist/src/state-store.d.ts.map +1 -0
- package/dist/src/{app-state.js → state-store.js} +8 -7
- package/dist/src/state-store.js.map +1 -0
- package/dist/src/structured-output/__tests__/context.test.js +13 -13
- package/dist/src/structured-output/__tests__/context.test.js.map +1 -1
- package/dist/src/structured-output/context.js +1 -1
- package/dist/src/structured-output/context.js.map +1 -1
- package/dist/src/telemetry/__tests__/config.test.d.ts +2 -0
- package/dist/src/telemetry/__tests__/config.test.d.ts.map +1 -0
- package/dist/src/telemetry/__tests__/config.test.js +64 -0
- package/dist/src/telemetry/__tests__/config.test.js.map +1 -0
- package/dist/src/telemetry/__tests__/config.test.node.js +66 -36
- package/dist/src/telemetry/__tests__/config.test.node.js.map +1 -1
- package/dist/src/telemetry/__tests__/meter.test.d.ts +2 -0
- package/dist/src/telemetry/__tests__/meter.test.d.ts.map +1 -0
- package/dist/src/telemetry/__tests__/meter.test.js +624 -0
- package/dist/src/telemetry/__tests__/meter.test.js.map +1 -0
- package/dist/src/telemetry/__tests__/tracer.test.node.js +123 -2
- package/dist/src/telemetry/__tests__/tracer.test.node.js.map +1 -1
- package/dist/src/telemetry/config.d.ts +104 -23
- package/dist/src/telemetry/config.d.ts.map +1 -1
- package/dist/src/telemetry/config.js +152 -43
- package/dist/src/telemetry/config.js.map +1 -1
- package/dist/src/telemetry/index.d.ts +10 -7
- package/dist/src/telemetry/index.d.ts.map +1 -1
- package/dist/src/telemetry/index.js +9 -6
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/meter.d.ts +296 -0
- package/dist/src/telemetry/meter.d.ts.map +1 -0
- package/dist/src/telemetry/meter.js +365 -0
- package/dist/src/telemetry/meter.js.map +1 -0
- package/dist/src/telemetry/tracer.d.ts +27 -0
- package/dist/src/telemetry/tracer.d.ts.map +1 -1
- package/dist/src/telemetry/tracer.js +79 -4
- package/dist/src/telemetry/tracer.js.map +1 -1
- package/dist/src/telemetry/types.d.ts +2 -0
- package/dist/src/telemetry/types.d.ts.map +1 -1
- package/dist/src/telemetry/utils.d.ts +10 -0
- package/dist/src/telemetry/utils.d.ts.map +1 -0
- package/dist/src/telemetry/utils.js +13 -0
- package/dist/src/telemetry/utils.js.map +1 -0
- package/dist/src/tools/__tests__/tool-factory.test.d.ts +2 -0
- package/dist/src/tools/__tests__/tool-factory.test.d.ts.map +1 -0
- package/dist/src/tools/__tests__/tool-factory.test.js +98 -0
- package/dist/src/tools/__tests__/tool-factory.test.js.map +1 -0
- package/dist/src/tools/__tests__/tool.test.js +22 -1
- package/dist/src/tools/__tests__/tool.test.js.map +1 -1
- package/dist/src/tools/__tests__/zod-tool.test-d.js +1 -1
- package/dist/src/tools/__tests__/zod-tool.test-d.js.map +1 -1
- package/dist/src/tools/__tests__/zod-tool.test.js +3 -4
- package/dist/src/tools/__tests__/zod-tool.test.js.map +1 -1
- package/dist/src/tools/function-tool.d.ts +26 -3
- package/dist/src/tools/function-tool.d.ts.map +1 -1
- package/dist/src/tools/function-tool.js +88 -3
- package/dist/src/tools/function-tool.js.map +1 -1
- package/dist/src/tools/tool-factory.d.ts +22 -0
- package/dist/src/tools/tool-factory.d.ts.map +1 -0
- package/dist/src/tools/tool-factory.js +55 -0
- package/dist/src/tools/tool-factory.js.map +1 -0
- package/dist/src/tools/tool.d.ts +2 -2
- package/dist/src/tools/tool.d.ts.map +1 -1
- package/dist/src/tools/zod-tool.d.ts +55 -52
- package/dist/src/tools/zod-tool.d.ts.map +1 -1
- package/dist/src/tools/zod-tool.js +7 -61
- package/dist/src/tools/zod-tool.js.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/src/types/__tests__/agent.test.js +11 -0
- package/dist/src/types/__tests__/agent.test.js.map +1 -1
- package/dist/src/types/__tests__/citations.test.d.ts +2 -0
- package/dist/src/types/__tests__/citations.test.d.ts.map +1 -0
- package/dist/src/types/__tests__/citations.test.js +104 -0
- package/dist/src/types/__tests__/citations.test.js.map +1 -0
- package/dist/src/types/__tests__/media.test.js +22 -16
- package/dist/src/types/__tests__/media.test.js.map +1 -1
- package/dist/src/types/__tests__/messages.test.js +26 -0
- package/dist/src/types/__tests__/messages.test.js.map +1 -1
- package/dist/src/types/agent.d.ts +82 -7
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/agent.js +9 -0
- package/dist/src/types/agent.js.map +1 -1
- package/dist/src/types/citations.d.ts +180 -0
- package/dist/src/types/citations.d.ts.map +1 -0
- package/dist/src/types/citations.js +45 -0
- package/dist/src/types/citations.js.map +1 -0
- package/dist/src/types/media.d.ts +27 -30
- package/dist/src/types/media.d.ts.map +1 -1
- package/dist/src/types/media.js +15 -56
- package/dist/src/types/media.js.map +1 -1
- package/dist/src/types/messages.d.ts +23 -5
- package/dist/src/types/messages.d.ts.map +1 -1
- package/dist/src/types/messages.js +26 -26
- package/dist/src/types/messages.js.map +1 -1
- package/dist/src/types/serializable.d.ts +34 -4
- package/dist/src/types/serializable.d.ts.map +1 -1
- package/dist/src/types/serializable.js +31 -2
- package/dist/src/types/serializable.js.map +1 -1
- package/dist/src/vended-tools/bash/__tests__/bash.test.node.js +5 -4
- package/dist/src/vended-tools/bash/__tests__/bash.test.node.js.map +1 -1
- package/dist/src/vended-tools/bash/bash.d.ts.map +1 -1
- package/dist/src/vended-tools/bash/bash.js +1 -2
- package/dist/src/vended-tools/bash/bash.js.map +1 -1
- package/dist/src/vended-tools/file-editor/__tests__/file-editor.test.node.d.ts.map +1 -0
- package/dist/src/vended-tools/{file_editor → file-editor}/__tests__/file-editor.test.node.js +11 -4
- package/dist/src/vended-tools/file-editor/__tests__/file-editor.test.node.js.map +1 -0
- package/dist/src/vended-tools/{file_editor → file-editor}/file-editor.d.ts +1 -1
- package/dist/src/vended-tools/{file_editor → file-editor}/file-editor.d.ts.map +1 -1
- package/dist/src/vended-tools/{file_editor → file-editor}/file-editor.js +3 -3
- package/dist/src/vended-tools/{file_editor → file-editor}/file-editor.js.map +1 -1
- package/dist/src/vended-tools/{file_editor → file-editor}/index.d.ts.map +1 -1
- package/dist/src/vended-tools/file-editor/index.js.map +1 -0
- package/dist/src/vended-tools/{file_editor → file-editor}/types.d.ts.map +1 -1
- package/dist/src/vended-tools/file-editor/types.js.map +1 -0
- package/dist/src/vended-tools/http-request/__tests__/http-request.test.d.ts.map +1 -0
- package/dist/src/vended-tools/{http_request → http-request}/__tests__/http-request.test.js.map +1 -1
- package/dist/src/vended-tools/http-request/http-request.d.ts.map +1 -0
- package/dist/src/vended-tools/{http_request → http-request}/http-request.js +1 -2
- package/dist/src/vended-tools/http-request/http-request.js.map +1 -0
- package/dist/src/vended-tools/{http_request → http-request}/index.d.ts.map +1 -1
- package/dist/src/vended-tools/http-request/index.js.map +1 -0
- package/dist/src/vended-tools/{http_request → http-request}/types.d.ts.map +1 -1
- package/dist/src/vended-tools/http-request/types.js.map +1 -0
- package/dist/src/vended-tools/notebook/__tests__/notebook.test.js +5 -4
- package/dist/src/vended-tools/notebook/__tests__/notebook.test.js.map +1 -1
- package/dist/src/vended-tools/notebook/notebook.d.ts +1 -1
- package/dist/src/vended-tools/notebook/notebook.js +2 -2
- package/dist/src/vended-tools/notebook/notebook.js.map +1 -1
- package/package.json +66 -12
- package/dist/src/__fixtures__/mock-hook-provider.d.ts +0 -10
- package/dist/src/__fixtures__/mock-hook-provider.d.ts.map +0 -1
- package/dist/src/__fixtures__/mock-hook-provider.js.map +0 -1
- package/dist/src/__tests__/app-state.test.d.ts +0 -2
- package/dist/src/__tests__/app-state.test.d.ts.map +0 -1
- package/dist/src/__tests__/app-state.test.js.map +0 -1
- package/dist/src/app-state.d.ts.map +0 -1
- package/dist/src/app-state.js.map +0 -1
- package/dist/src/multiagent/base.d.ts +0 -25
- package/dist/src/multiagent/base.d.ts.map +0 -1
- package/dist/src/multiagent/base.js +0 -2
- package/dist/src/multiagent/base.js.map +0 -1
- package/dist/src/registry/registry.d.ts +0 -117
- package/dist/src/registry/registry.d.ts.map +0 -1
- package/dist/src/registry/registry.js +0 -298
- package/dist/src/registry/registry.js.map +0 -1
- package/dist/src/session/__tests__/s3-storage.test.node.d.ts +0 -2
- package/dist/src/session/__tests__/s3-storage.test.node.d.ts.map +0 -1
- package/dist/src/session/__tests__/s3-storage.test.node.js.map +0 -1
- package/dist/src/vended-tools/file_editor/__tests__/file-editor.test.node.d.ts.map +0 -1
- package/dist/src/vended-tools/file_editor/__tests__/file-editor.test.node.js.map +0 -1
- package/dist/src/vended-tools/file_editor/index.js.map +0 -1
- package/dist/src/vended-tools/file_editor/types.js.map +0 -1
- package/dist/src/vended-tools/http_request/__tests__/http-request.test.d.ts.map +0 -1
- package/dist/src/vended-tools/http_request/http-request.d.ts.map +0 -1
- package/dist/src/vended-tools/http_request/http-request.js.map +0 -1
- package/dist/src/vended-tools/http_request/index.js.map +0 -1
- package/dist/src/vended-tools/http_request/types.js.map +0 -1
- /package/dist/src/vended-tools/{file_editor → file-editor}/__tests__/file-editor.test.node.d.ts +0 -0
- /package/dist/src/vended-tools/{file_editor → file-editor}/index.d.ts +0 -0
- /package/dist/src/vended-tools/{file_editor → file-editor}/index.js +0 -0
- /package/dist/src/vended-tools/{file_editor → file-editor}/types.d.ts +0 -0
- /package/dist/src/vended-tools/{file_editor → file-editor}/types.js +0 -0
- /package/dist/src/vended-tools/{http_request → http-request}/__tests__/http-request.test.d.ts +0 -0
- /package/dist/src/vended-tools/{http_request → http-request}/__tests__/http-request.test.js +0 -0
- /package/dist/src/vended-tools/{http_request → http-request}/http-request.d.ts +0 -0
- /package/dist/src/vended-tools/{http_request → http-request}/index.d.ts +0 -0
- /package/dist/src/vended-tools/{http_request → http-request}/index.js +0 -0
- /package/dist/src/vended-tools/{http_request → http-request}/types.d.ts +0 -0
- /package/dist/src/vended-tools/{http_request → http-request}/types.js +0 -0
|
@@ -5,6 +5,8 @@ import { BedrockModel } from '../bedrock.js';
|
|
|
5
5
|
import { ContextWindowOverflowError, ModelThrottledError } from '../../errors.js';
|
|
6
6
|
import { Message, ReasoningBlock, ToolUseBlock, ToolResultBlock, JsonBlock } from '../../types/messages.js';
|
|
7
7
|
import { TextBlock, GuardContentBlock, CachePointBlock } from '../../types/messages.js';
|
|
8
|
+
import { ImageBlock, VideoBlock, DocumentBlock } from '../../types/media.js';
|
|
9
|
+
import { CitationsBlock } from '../../types/citations.js';
|
|
8
10
|
import { collectIterator } from '../../__fixtures__/model-test-helpers.js';
|
|
9
11
|
/**
|
|
10
12
|
* Helper function to mock BedrockRuntimeClient implementation with customizable config.
|
|
@@ -264,13 +266,12 @@ describe('BedrockModel', () => {
|
|
|
264
266
|
it('formats the request to bedrock properly', async () => {
|
|
265
267
|
const provider = new BedrockModel({
|
|
266
268
|
region: 'us-west-2',
|
|
267
|
-
modelId: 'test-model',
|
|
269
|
+
modelId: 'anthropic.claude-test-model',
|
|
268
270
|
maxTokens: 1024,
|
|
269
271
|
temperature: 0.7,
|
|
270
272
|
topP: 0.9,
|
|
271
273
|
stopSequences: ['STOP'],
|
|
272
|
-
|
|
273
|
-
cacheTools: 'default',
|
|
274
|
+
cacheConfig: { strategy: 'auto' },
|
|
274
275
|
additionalResponseFieldPaths: ['Hello!'],
|
|
275
276
|
additionalRequestFields: ['World!'],
|
|
276
277
|
additionalArgs: {
|
|
@@ -296,14 +297,14 @@ describe('BedrockModel', () => {
|
|
|
296
297
|
MyExtraArg: 'ExtraArg',
|
|
297
298
|
additionalModelRequestFields: ['World!'],
|
|
298
299
|
additionalModelResponseFieldPaths: ['Hello!'],
|
|
299
|
-
modelId: 'test-model',
|
|
300
|
+
modelId: 'anthropic.claude-test-model',
|
|
300
301
|
messages: [
|
|
301
302
|
{
|
|
302
303
|
role: 'user',
|
|
303
|
-
content: [{ text: 'Hello' }],
|
|
304
|
+
content: [{ text: 'Hello' }, { cachePoint: { type: 'default' } }],
|
|
304
305
|
},
|
|
305
306
|
],
|
|
306
|
-
system: [{ text: 'You are a helpful assistant' }
|
|
307
|
+
system: [{ text: 'You are a helpful assistant' }],
|
|
307
308
|
toolConfig: {
|
|
308
309
|
toolChoice: { auto: {} },
|
|
309
310
|
tools: [
|
|
@@ -687,6 +688,77 @@ describe('BedrockModel', () => {
|
|
|
687
688
|
metrics: { latencyMs: 110 },
|
|
688
689
|
});
|
|
689
690
|
});
|
|
691
|
+
it('yields and validates citationsContent events correctly', async () => {
|
|
692
|
+
// Bedrock wire format uses object-key discrimination
|
|
693
|
+
const bedrockCitationsData = {
|
|
694
|
+
citations: [
|
|
695
|
+
{
|
|
696
|
+
location: { documentChar: { documentIndex: 0, start: 10, end: 50 } },
|
|
697
|
+
sourceContent: [{ text: 'source text' }],
|
|
698
|
+
source: 'doc-0',
|
|
699
|
+
title: 'Test Doc',
|
|
700
|
+
},
|
|
701
|
+
],
|
|
702
|
+
content: [{ text: 'generated text' }],
|
|
703
|
+
};
|
|
704
|
+
const mockSend = vi.fn(async () => {
|
|
705
|
+
if (stream) {
|
|
706
|
+
return {
|
|
707
|
+
stream: (async function* () {
|
|
708
|
+
yield { messageStart: { role: 'assistant' } };
|
|
709
|
+
yield { contentBlockStart: {} };
|
|
710
|
+
yield {
|
|
711
|
+
contentBlockDelta: {
|
|
712
|
+
delta: { citationsContent: bedrockCitationsData },
|
|
713
|
+
},
|
|
714
|
+
};
|
|
715
|
+
yield { contentBlockStop: {} };
|
|
716
|
+
yield { messageStop: { stopReason: 'end_turn' } };
|
|
717
|
+
yield {
|
|
718
|
+
metadata: { usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 }, metrics: { latencyMs: 100 } },
|
|
719
|
+
};
|
|
720
|
+
})(),
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
else {
|
|
724
|
+
return {
|
|
725
|
+
output: {
|
|
726
|
+
message: {
|
|
727
|
+
role: 'assistant',
|
|
728
|
+
content: [{ citationsContent: bedrockCitationsData }],
|
|
729
|
+
},
|
|
730
|
+
},
|
|
731
|
+
stopReason: 'end_turn',
|
|
732
|
+
usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
733
|
+
metrics: { latencyMs: 100 },
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
mockBedrockClientImplementation({ send: mockSend });
|
|
738
|
+
const provider = new BedrockModel({ stream });
|
|
739
|
+
const messages = [new Message({ role: 'user', content: [new TextBlock('Cite this.')] })];
|
|
740
|
+
const events = await collectIterator(provider.stream(messages));
|
|
741
|
+
// SDK events should use type-field discrimination
|
|
742
|
+
expect(events).toContainEqual({ role: 'assistant', type: 'modelMessageStartEvent' });
|
|
743
|
+
expect(events).toContainEqual({ type: 'modelContentBlockStartEvent' });
|
|
744
|
+
expect(events).toContainEqual({
|
|
745
|
+
type: 'modelContentBlockDeltaEvent',
|
|
746
|
+
delta: {
|
|
747
|
+
type: 'citationsDelta',
|
|
748
|
+
citations: [
|
|
749
|
+
{
|
|
750
|
+
location: { type: 'documentChar', documentIndex: 0, start: 10, end: 50 },
|
|
751
|
+
sourceContent: [{ text: 'source text' }],
|
|
752
|
+
source: 'doc-0',
|
|
753
|
+
title: 'Test Doc',
|
|
754
|
+
},
|
|
755
|
+
],
|
|
756
|
+
content: [{ text: 'generated text' }],
|
|
757
|
+
},
|
|
758
|
+
});
|
|
759
|
+
expect(events).toContainEqual({ type: 'modelContentBlockStopEvent' });
|
|
760
|
+
expect(events).toContainEqual({ stopReason: 'endTurn', type: 'modelMessageStopEvent' });
|
|
761
|
+
});
|
|
690
762
|
describe('error handling', async () => {
|
|
691
763
|
it.each([
|
|
692
764
|
{
|
|
@@ -972,8 +1044,8 @@ describe('BedrockModel', () => {
|
|
|
972
1044
|
beforeEach(() => {
|
|
973
1045
|
vi.clearAllMocks();
|
|
974
1046
|
});
|
|
975
|
-
it('
|
|
976
|
-
const provider = new BedrockModel({
|
|
1047
|
+
it('does not add cache points to string system prompt with cacheConfig', async () => {
|
|
1048
|
+
const provider = new BedrockModel({ cacheConfig: { strategy: 'auto' } });
|
|
977
1049
|
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello')] })];
|
|
978
1050
|
const options = {
|
|
979
1051
|
systemPrompt: 'You are a helpful assistant',
|
|
@@ -984,10 +1056,10 @@ describe('BedrockModel', () => {
|
|
|
984
1056
|
messages: [
|
|
985
1057
|
{
|
|
986
1058
|
role: 'user',
|
|
987
|
-
content: [{ text: 'Hello' }],
|
|
1059
|
+
content: [{ text: 'Hello' }, { cachePoint: { type: 'default' } }],
|
|
988
1060
|
},
|
|
989
1061
|
],
|
|
990
|
-
system: [{ text: 'You are a helpful assistant' }
|
|
1062
|
+
system: [{ text: 'You are a helpful assistant' }],
|
|
991
1063
|
});
|
|
992
1064
|
});
|
|
993
1065
|
it('formats array system prompt with text blocks only', async () => {
|
|
@@ -1037,9 +1109,9 @@ describe('BedrockModel', () => {
|
|
|
1037
1109
|
],
|
|
1038
1110
|
});
|
|
1039
1111
|
});
|
|
1040
|
-
it('
|
|
1112
|
+
it('does not warn when array system prompt is provided without cacheConfig', async () => {
|
|
1041
1113
|
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
1042
|
-
const provider = new BedrockModel(
|
|
1114
|
+
const provider = new BedrockModel();
|
|
1043
1115
|
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello')] })];
|
|
1044
1116
|
const options = {
|
|
1045
1117
|
systemPrompt: [
|
|
@@ -1048,9 +1120,9 @@ describe('BedrockModel', () => {
|
|
|
1048
1120
|
],
|
|
1049
1121
|
};
|
|
1050
1122
|
collectIterator(provider.stream(messages, options));
|
|
1051
|
-
// Verify warning was logged
|
|
1052
|
-
expect(warnSpy).
|
|
1053
|
-
// Verify array is used as-is
|
|
1123
|
+
// Verify no warning was logged
|
|
1124
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
1125
|
+
// Verify array is used as-is
|
|
1054
1126
|
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith({
|
|
1055
1127
|
modelId: 'global.anthropic.claude-sonnet-4-5-20250929-v1:0',
|
|
1056
1128
|
messages: [
|
|
@@ -1063,6 +1135,131 @@ describe('BedrockModel', () => {
|
|
|
1063
1135
|
});
|
|
1064
1136
|
warnSpy.mockRestore();
|
|
1065
1137
|
});
|
|
1138
|
+
it('adds cache point after tools when cacheConfig enabled', async () => {
|
|
1139
|
+
const provider = new BedrockModel({ cacheConfig: { strategy: 'auto' } });
|
|
1140
|
+
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello')] })];
|
|
1141
|
+
const options = {
|
|
1142
|
+
toolSpecs: [
|
|
1143
|
+
{
|
|
1144
|
+
name: 'calculator',
|
|
1145
|
+
description: 'Calculate',
|
|
1146
|
+
inputSchema: { type: 'object' },
|
|
1147
|
+
},
|
|
1148
|
+
],
|
|
1149
|
+
};
|
|
1150
|
+
collectIterator(provider.stream(messages, options));
|
|
1151
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith({
|
|
1152
|
+
modelId: 'global.anthropic.claude-sonnet-4-5-20250929-v1:0',
|
|
1153
|
+
messages: [
|
|
1154
|
+
{
|
|
1155
|
+
role: 'user',
|
|
1156
|
+
content: [{ text: 'Hello' }, { cachePoint: { type: 'default' } }],
|
|
1157
|
+
},
|
|
1158
|
+
],
|
|
1159
|
+
toolConfig: {
|
|
1160
|
+
tools: [
|
|
1161
|
+
{
|
|
1162
|
+
toolSpec: {
|
|
1163
|
+
name: 'calculator',
|
|
1164
|
+
description: 'Calculate',
|
|
1165
|
+
inputSchema: { json: { type: 'object' } },
|
|
1166
|
+
},
|
|
1167
|
+
},
|
|
1168
|
+
{ cachePoint: { type: 'default' } },
|
|
1169
|
+
],
|
|
1170
|
+
},
|
|
1171
|
+
});
|
|
1172
|
+
});
|
|
1173
|
+
it('adds cache points to tools and messages when cacheConfig enabled', async () => {
|
|
1174
|
+
const provider = new BedrockModel({ cacheConfig: { strategy: 'auto' } });
|
|
1175
|
+
const messages = [
|
|
1176
|
+
new Message({ role: 'user', content: [new TextBlock('Hello')] }),
|
|
1177
|
+
new Message({ role: 'assistant', content: [new TextBlock('Hi')] }),
|
|
1178
|
+
];
|
|
1179
|
+
const options = {
|
|
1180
|
+
systemPrompt: 'You are a helpful assistant',
|
|
1181
|
+
toolSpecs: [
|
|
1182
|
+
{
|
|
1183
|
+
name: 'calculator',
|
|
1184
|
+
description: 'Calculate',
|
|
1185
|
+
inputSchema: { type: 'object' },
|
|
1186
|
+
},
|
|
1187
|
+
],
|
|
1188
|
+
};
|
|
1189
|
+
collectIterator(provider.stream(messages, options));
|
|
1190
|
+
const call = mockConverseStreamCommand.mock.lastCall?.[0];
|
|
1191
|
+
expect(call?.system).toStrictEqual([{ text: 'You are a helpful assistant' }]);
|
|
1192
|
+
expect(call?.toolConfig?.tools).toStrictEqual([
|
|
1193
|
+
{
|
|
1194
|
+
toolSpec: {
|
|
1195
|
+
name: 'calculator',
|
|
1196
|
+
description: 'Calculate',
|
|
1197
|
+
inputSchema: { json: { type: 'object' } },
|
|
1198
|
+
},
|
|
1199
|
+
},
|
|
1200
|
+
{ cachePoint: { type: 'default' } },
|
|
1201
|
+
]);
|
|
1202
|
+
const userMsg = call?.messages?.[0];
|
|
1203
|
+
const lastBlock = userMsg?.content?.[userMsg.content.length - 1];
|
|
1204
|
+
expect(lastBlock).toStrictEqual({ cachePoint: { type: 'default' } });
|
|
1205
|
+
const assistantMsg = call?.messages?.[1];
|
|
1206
|
+
const assistantLastBlock = assistantMsg?.content?.[assistantMsg.content.length - 1];
|
|
1207
|
+
expect(assistantLastBlock).not.toStrictEqual({ cachePoint: { type: 'default' } });
|
|
1208
|
+
});
|
|
1209
|
+
it('does not mutate the original messages array', async () => {
|
|
1210
|
+
const provider = new BedrockModel({ cacheConfig: { strategy: 'auto' } });
|
|
1211
|
+
const originalMessages = [
|
|
1212
|
+
new Message({ role: 'user', content: [new TextBlock('Hello')] }),
|
|
1213
|
+
new Message({ role: 'assistant', content: [new TextBlock('Hi')] }),
|
|
1214
|
+
];
|
|
1215
|
+
// Create a deep copy to compare against
|
|
1216
|
+
const messagesCopy = JSON.parse(JSON.stringify(originalMessages));
|
|
1217
|
+
collectIterator(provider.stream(originalMessages));
|
|
1218
|
+
// Verify original messages are unchanged
|
|
1219
|
+
expect(JSON.stringify(originalMessages)).toBe(JSON.stringify(messagesCopy));
|
|
1220
|
+
});
|
|
1221
|
+
it('logs warning and disables caching for non-caching models', async () => {
|
|
1222
|
+
const warnSpy = vi.spyOn(console, 'warn');
|
|
1223
|
+
const provider = new BedrockModel({
|
|
1224
|
+
modelId: 'amazon.titan-text-express-v1',
|
|
1225
|
+
cacheConfig: { strategy: 'auto' },
|
|
1226
|
+
});
|
|
1227
|
+
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello')] })];
|
|
1228
|
+
const options = {
|
|
1229
|
+
systemPrompt: 'You are a helpful assistant',
|
|
1230
|
+
};
|
|
1231
|
+
collectIterator(provider.stream(messages, options));
|
|
1232
|
+
// Verify warning was logged
|
|
1233
|
+
expect(warnSpy).toHaveBeenCalled();
|
|
1234
|
+
// Verify no cache points were added
|
|
1235
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith({
|
|
1236
|
+
modelId: 'amazon.titan-text-express-v1',
|
|
1237
|
+
messages: [
|
|
1238
|
+
{
|
|
1239
|
+
role: 'user',
|
|
1240
|
+
content: [{ text: 'Hello' }],
|
|
1241
|
+
},
|
|
1242
|
+
],
|
|
1243
|
+
system: [{ text: 'You are a helpful assistant' }],
|
|
1244
|
+
});
|
|
1245
|
+
warnSpy.mockRestore();
|
|
1246
|
+
});
|
|
1247
|
+
it('enables caching with anthropic strategy for application inference profiles', async () => {
|
|
1248
|
+
const provider = new BedrockModel({
|
|
1249
|
+
modelId: 'arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/abc123',
|
|
1250
|
+
cacheConfig: { strategy: 'anthropic' },
|
|
1251
|
+
});
|
|
1252
|
+
const messages = [
|
|
1253
|
+
new Message({ role: 'user', content: [new TextBlock('Hello')] }),
|
|
1254
|
+
new Message({ role: 'assistant', content: [new TextBlock('Hi')] }),
|
|
1255
|
+
];
|
|
1256
|
+
collectIterator(provider.stream(messages));
|
|
1257
|
+
const call = mockConverseStreamCommand.mock.lastCall?.[0];
|
|
1258
|
+
// Cache point should be on the user message (index 0)
|
|
1259
|
+
const userMsg = call?.messages?.[0];
|
|
1260
|
+
const lastBlock = userMsg?.content?.[userMsg.content.length - 1];
|
|
1261
|
+
expect(lastBlock).toStrictEqual({ cachePoint: { type: 'default' } });
|
|
1262
|
+
});
|
|
1066
1263
|
it('handles empty array system prompt', async () => {
|
|
1067
1264
|
const provider = new BedrockModel();
|
|
1068
1265
|
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello')] })];
|
|
@@ -1308,188 +1505,1910 @@ describe('BedrockModel', () => {
|
|
|
1308
1505
|
});
|
|
1309
1506
|
});
|
|
1310
1507
|
});
|
|
1311
|
-
describe('
|
|
1508
|
+
describe('media blocks in tool results', () => {
|
|
1312
1509
|
const mockConverseStreamCommand = vi.mocked(ConverseStreamCommand);
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1510
|
+
it('formats image block in tool result', async () => {
|
|
1511
|
+
const provider = new BedrockModel();
|
|
1512
|
+
const imageBytes = new Uint8Array([1, 2, 3]);
|
|
1513
|
+
const messages = [
|
|
1514
|
+
new Message({
|
|
1515
|
+
role: 'user',
|
|
1516
|
+
content: [
|
|
1517
|
+
new ToolResultBlock({
|
|
1518
|
+
toolUseId: 'tool-1',
|
|
1519
|
+
status: 'success',
|
|
1520
|
+
content: [new ImageBlock({ format: 'png', source: { bytes: imageBytes } })],
|
|
1521
|
+
}),
|
|
1522
|
+
],
|
|
1523
|
+
}),
|
|
1524
|
+
];
|
|
1525
|
+
collectIterator(provider.stream(messages));
|
|
1526
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1527
|
+
messages: [
|
|
1528
|
+
{
|
|
1318
1529
|
role: 'user',
|
|
1319
1530
|
content: [
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
],
|
|
1326
|
-
}),
|
|
1327
|
-
];
|
|
1328
|
-
collectIterator(provider.stream(messages));
|
|
1329
|
-
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith({
|
|
1330
|
-
messages: [
|
|
1331
|
-
{
|
|
1332
|
-
content: [
|
|
1333
|
-
{
|
|
1334
|
-
toolResult: {
|
|
1335
|
-
content: [{ text: 'Result' }],
|
|
1336
|
-
status: 'success',
|
|
1337
|
-
toolUseId: 'tool-123',
|
|
1338
|
-
},
|
|
1531
|
+
{
|
|
1532
|
+
toolResult: {
|
|
1533
|
+
toolUseId: 'tool-1',
|
|
1534
|
+
content: [{ image: { format: 'png', source: { bytes: imageBytes } } }],
|
|
1535
|
+
status: 'success',
|
|
1339
1536
|
},
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1537
|
+
},
|
|
1538
|
+
],
|
|
1539
|
+
},
|
|
1540
|
+
],
|
|
1541
|
+
}));
|
|
1542
|
+
});
|
|
1543
|
+
it('formats video block in tool result with 3gp format mapping', async () => {
|
|
1544
|
+
const provider = new BedrockModel();
|
|
1545
|
+
const videoBytes = new Uint8Array([4, 5, 6]);
|
|
1546
|
+
const messages = [
|
|
1547
|
+
new Message({
|
|
1548
|
+
role: 'user',
|
|
1549
|
+
content: [
|
|
1550
|
+
new ToolResultBlock({
|
|
1551
|
+
toolUseId: 'tool-1',
|
|
1552
|
+
status: 'success',
|
|
1553
|
+
content: [new VideoBlock({ format: '3gp', source: { bytes: videoBytes } })],
|
|
1554
|
+
}),
|
|
1343
1555
|
],
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1556
|
+
}),
|
|
1557
|
+
];
|
|
1558
|
+
collectIterator(provider.stream(messages));
|
|
1559
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1560
|
+
messages: [
|
|
1561
|
+
{
|
|
1562
|
+
role: 'user',
|
|
1563
|
+
content: [
|
|
1564
|
+
{
|
|
1565
|
+
toolResult: {
|
|
1566
|
+
toolUseId: 'tool-1',
|
|
1567
|
+
content: [{ video: { format: 'three_gp', source: { bytes: videoBytes } } }],
|
|
1568
|
+
status: 'success',
|
|
1569
|
+
},
|
|
1570
|
+
},
|
|
1571
|
+
],
|
|
1572
|
+
},
|
|
1573
|
+
],
|
|
1574
|
+
}));
|
|
1347
1575
|
});
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1576
|
+
it('formats document block in tool result', async () => {
|
|
1577
|
+
const provider = new BedrockModel();
|
|
1578
|
+
const docBytes = new Uint8Array([7, 8, 9]);
|
|
1579
|
+
const messages = [
|
|
1580
|
+
new Message({
|
|
1581
|
+
role: 'user',
|
|
1582
|
+
content: [
|
|
1583
|
+
new ToolResultBlock({
|
|
1584
|
+
toolUseId: 'tool-1',
|
|
1585
|
+
status: 'success',
|
|
1586
|
+
content: [new DocumentBlock({ name: 'report.pdf', format: 'pdf', source: { bytes: docBytes } })],
|
|
1587
|
+
}),
|
|
1588
|
+
],
|
|
1589
|
+
}),
|
|
1590
|
+
];
|
|
1591
|
+
collectIterator(provider.stream(messages));
|
|
1592
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1593
|
+
messages: [
|
|
1594
|
+
{
|
|
1353
1595
|
role: 'user',
|
|
1354
1596
|
content: [
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1597
|
+
{
|
|
1598
|
+
toolResult: {
|
|
1599
|
+
toolUseId: 'tool-1',
|
|
1600
|
+
content: [{ document: { name: 'report.pdf', format: 'pdf', source: { bytes: docBytes } } }],
|
|
1601
|
+
status: 'success',
|
|
1602
|
+
},
|
|
1603
|
+
},
|
|
1360
1604
|
],
|
|
1361
|
-
}
|
|
1362
|
-
]
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1605
|
+
},
|
|
1606
|
+
],
|
|
1607
|
+
}));
|
|
1608
|
+
});
|
|
1609
|
+
it('formats mixed text and media content in tool result', async () => {
|
|
1610
|
+
const provider = new BedrockModel();
|
|
1611
|
+
const imageBytes = new Uint8Array([1, 2]);
|
|
1612
|
+
const messages = [
|
|
1613
|
+
new Message({
|
|
1614
|
+
role: 'user',
|
|
1615
|
+
content: [
|
|
1616
|
+
new ToolResultBlock({
|
|
1617
|
+
toolUseId: 'tool-1',
|
|
1618
|
+
status: 'success',
|
|
1619
|
+
content: [
|
|
1620
|
+
new TextBlock('Here is the image:'),
|
|
1621
|
+
new ImageBlock({ format: 'jpeg', source: { bytes: imageBytes } }),
|
|
1622
|
+
],
|
|
1623
|
+
}),
|
|
1624
|
+
],
|
|
1625
|
+
}),
|
|
1626
|
+
];
|
|
1627
|
+
collectIterator(provider.stream(messages));
|
|
1628
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1629
|
+
messages: [
|
|
1630
|
+
{
|
|
1631
|
+
role: 'user',
|
|
1632
|
+
content: [
|
|
1633
|
+
{
|
|
1634
|
+
toolResult: {
|
|
1635
|
+
toolUseId: 'tool-1',
|
|
1636
|
+
content: [
|
|
1637
|
+
{ text: 'Here is the image:' },
|
|
1638
|
+
{ image: { format: 'jpeg', source: { bytes: imageBytes } } },
|
|
1639
|
+
],
|
|
1640
|
+
status: 'success',
|
|
1641
|
+
},
|
|
1642
|
+
},
|
|
1643
|
+
],
|
|
1644
|
+
},
|
|
1645
|
+
],
|
|
1646
|
+
}));
|
|
1647
|
+
});
|
|
1648
|
+
});
|
|
1649
|
+
describe('media blocks in messages', () => {
|
|
1650
|
+
const mockConverseStreamCommand = vi.mocked(ConverseStreamCommand);
|
|
1651
|
+
it('formats top-level image block', async () => {
|
|
1652
|
+
const provider = new BedrockModel();
|
|
1653
|
+
const imageBytes = new Uint8Array([1, 2, 3]);
|
|
1654
|
+
const messages = [
|
|
1655
|
+
new Message({
|
|
1656
|
+
role: 'user',
|
|
1657
|
+
content: [new ImageBlock({ format: 'png', source: { bytes: imageBytes } })],
|
|
1658
|
+
}),
|
|
1659
|
+
];
|
|
1660
|
+
collectIterator(provider.stream(messages));
|
|
1661
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1662
|
+
messages: [
|
|
1663
|
+
{
|
|
1664
|
+
role: 'user',
|
|
1665
|
+
content: [{ image: { format: 'png', source: { bytes: imageBytes } } }],
|
|
1666
|
+
},
|
|
1667
|
+
],
|
|
1668
|
+
}));
|
|
1669
|
+
});
|
|
1670
|
+
it('formats top-level image block with S3 source', async () => {
|
|
1671
|
+
const provider = new BedrockModel();
|
|
1672
|
+
const messages = [
|
|
1673
|
+
new Message({
|
|
1674
|
+
role: 'user',
|
|
1675
|
+
content: [
|
|
1676
|
+
new ImageBlock({ format: 'png', source: { location: { type: 's3', uri: 's3://bucket/image.png' } } }),
|
|
1677
|
+
],
|
|
1678
|
+
}),
|
|
1679
|
+
];
|
|
1680
|
+
collectIterator(provider.stream(messages));
|
|
1681
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1682
|
+
messages: [
|
|
1683
|
+
{
|
|
1684
|
+
role: 'user',
|
|
1685
|
+
content: [{ image: { format: 'png', source: { s3Location: { uri: 's3://bucket/image.png' } } } }],
|
|
1686
|
+
},
|
|
1687
|
+
],
|
|
1688
|
+
}));
|
|
1689
|
+
});
|
|
1690
|
+
it('formats top-level video block with 3gp format mapping', async () => {
|
|
1691
|
+
const provider = new BedrockModel();
|
|
1692
|
+
const videoBytes = new Uint8Array([4, 5, 6]);
|
|
1693
|
+
const messages = [
|
|
1694
|
+
new Message({
|
|
1695
|
+
role: 'user',
|
|
1696
|
+
content: [new VideoBlock({ format: '3gp', source: { bytes: videoBytes } })],
|
|
1697
|
+
}),
|
|
1698
|
+
];
|
|
1699
|
+
collectIterator(provider.stream(messages));
|
|
1700
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1701
|
+
messages: [
|
|
1702
|
+
{
|
|
1703
|
+
role: 'user',
|
|
1704
|
+
content: [{ video: { format: 'three_gp', source: { bytes: videoBytes } } }],
|
|
1705
|
+
},
|
|
1706
|
+
],
|
|
1707
|
+
}));
|
|
1708
|
+
});
|
|
1709
|
+
it('formats top-level document block with text source converted to bytes', async () => {
|
|
1710
|
+
const provider = new BedrockModel();
|
|
1711
|
+
const messages = [
|
|
1712
|
+
new Message({
|
|
1713
|
+
role: 'user',
|
|
1714
|
+
content: [new DocumentBlock({ name: 'notes.txt', format: 'txt', source: { text: 'Hello world' } })],
|
|
1715
|
+
}),
|
|
1716
|
+
];
|
|
1717
|
+
collectIterator(provider.stream(messages));
|
|
1718
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1719
|
+
messages: [
|
|
1720
|
+
{
|
|
1721
|
+
role: 'user',
|
|
1722
|
+
content: [
|
|
1723
|
+
{
|
|
1724
|
+
document: {
|
|
1725
|
+
name: 'notes.txt',
|
|
1726
|
+
format: 'txt',
|
|
1727
|
+
source: { bytes: new TextEncoder().encode('Hello world') },
|
|
1728
|
+
},
|
|
1729
|
+
},
|
|
1730
|
+
],
|
|
1731
|
+
},
|
|
1732
|
+
],
|
|
1733
|
+
}));
|
|
1734
|
+
});
|
|
1735
|
+
});
|
|
1736
|
+
describe('citations content block formatting', () => {
|
|
1737
|
+
const mockConverseStreamCommand = vi.mocked(ConverseStreamCommand);
|
|
1738
|
+
it('maps SDK CitationLocation types to Bedrock object-key format through formatting pipeline', async () => {
|
|
1739
|
+
const provider = new BedrockModel();
|
|
1740
|
+
const sdkCitations = [
|
|
1741
|
+
{
|
|
1742
|
+
location: { type: 'documentChar', documentIndex: 0, start: 150, end: 300 },
|
|
1743
|
+
source: 'doc-0',
|
|
1744
|
+
sourceContent: [{ text: 'char source' }],
|
|
1745
|
+
title: 'Text Document',
|
|
1746
|
+
},
|
|
1747
|
+
{
|
|
1748
|
+
location: { type: 'documentPage', documentIndex: 0, start: 2, end: 3 },
|
|
1749
|
+
source: 'doc-0',
|
|
1750
|
+
sourceContent: [{ text: 'page source' }],
|
|
1751
|
+
title: 'PDF Document',
|
|
1752
|
+
},
|
|
1753
|
+
{
|
|
1754
|
+
location: { type: 'documentChunk', documentIndex: 1, start: 5, end: 8 },
|
|
1755
|
+
source: 'doc-1',
|
|
1756
|
+
sourceContent: [{ text: 'chunk source' }],
|
|
1757
|
+
title: 'Chunked Document',
|
|
1758
|
+
},
|
|
1759
|
+
{
|
|
1760
|
+
location: { type: 'searchResult', searchResultIndex: 0, start: 25, end: 150 },
|
|
1761
|
+
source: 'search-0',
|
|
1762
|
+
sourceContent: [{ text: 'search source' }],
|
|
1763
|
+
title: 'Search Result',
|
|
1764
|
+
},
|
|
1765
|
+
{
|
|
1766
|
+
location: { type: 'web', url: 'https://example.com/doc', domain: 'example.com' },
|
|
1767
|
+
source: 'web-0',
|
|
1768
|
+
sourceContent: [{ text: 'web source' }],
|
|
1769
|
+
title: 'Web Page',
|
|
1770
|
+
},
|
|
1771
|
+
];
|
|
1772
|
+
const messages = [
|
|
1773
|
+
new Message({
|
|
1774
|
+
role: 'assistant',
|
|
1775
|
+
content: [
|
|
1776
|
+
new CitationsBlock({
|
|
1777
|
+
citations: sdkCitations,
|
|
1778
|
+
content: [{ text: 'generated text with all citation types' }],
|
|
1779
|
+
}),
|
|
1780
|
+
],
|
|
1781
|
+
}),
|
|
1782
|
+
new Message({
|
|
1783
|
+
role: 'user',
|
|
1784
|
+
content: [new TextBlock('Follow up')],
|
|
1785
|
+
}),
|
|
1786
|
+
];
|
|
1787
|
+
collectIterator(provider.stream(messages));
|
|
1788
|
+
// Bedrock wire format uses object-key discrimination
|
|
1789
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1790
|
+
messages: [
|
|
1791
|
+
{
|
|
1792
|
+
role: 'assistant',
|
|
1793
|
+
content: [
|
|
1794
|
+
{
|
|
1795
|
+
citationsContent: {
|
|
1796
|
+
citations: [
|
|
1797
|
+
{
|
|
1798
|
+
location: { documentChar: { documentIndex: 0, start: 150, end: 300 } },
|
|
1799
|
+
source: 'doc-0',
|
|
1800
|
+
sourceContent: [{ text: 'char source' }],
|
|
1801
|
+
title: 'Text Document',
|
|
1802
|
+
},
|
|
1803
|
+
{
|
|
1804
|
+
location: { documentPage: { documentIndex: 0, start: 2, end: 3 } },
|
|
1805
|
+
source: 'doc-0',
|
|
1806
|
+
sourceContent: [{ text: 'page source' }],
|
|
1807
|
+
title: 'PDF Document',
|
|
1808
|
+
},
|
|
1809
|
+
{
|
|
1810
|
+
location: { documentChunk: { documentIndex: 1, start: 5, end: 8 } },
|
|
1811
|
+
source: 'doc-1',
|
|
1812
|
+
sourceContent: [{ text: 'chunk source' }],
|
|
1813
|
+
title: 'Chunked Document',
|
|
1814
|
+
},
|
|
1815
|
+
{
|
|
1816
|
+
location: {
|
|
1817
|
+
searchResultLocation: { searchResultIndex: 0, start: 25, end: 150 },
|
|
1818
|
+
},
|
|
1819
|
+
source: 'search-0',
|
|
1820
|
+
sourceContent: [{ text: 'search source' }],
|
|
1821
|
+
title: 'Search Result',
|
|
1822
|
+
},
|
|
1823
|
+
{
|
|
1824
|
+
location: { web: { url: 'https://example.com/doc', domain: 'example.com' } },
|
|
1825
|
+
source: 'web-0',
|
|
1826
|
+
sourceContent: [{ text: 'web source' }],
|
|
1827
|
+
title: 'Web Page',
|
|
1828
|
+
},
|
|
1829
|
+
],
|
|
1830
|
+
content: [{ text: 'generated text with all citation types' }],
|
|
1831
|
+
},
|
|
1832
|
+
},
|
|
1833
|
+
],
|
|
1834
|
+
},
|
|
1835
|
+
{
|
|
1836
|
+
role: 'user',
|
|
1837
|
+
content: [{ text: 'Follow up' }],
|
|
1838
|
+
},
|
|
1839
|
+
],
|
|
1840
|
+
}));
|
|
1841
|
+
});
|
|
1842
|
+
});
|
|
1843
|
+
describe('media blocks in tool results', () => {
|
|
1844
|
+
const mockConverseStreamCommand = vi.mocked(ConverseStreamCommand);
|
|
1845
|
+
it('formats image block in tool result', async () => {
|
|
1846
|
+
const provider = new BedrockModel();
|
|
1847
|
+
const imageBytes = new Uint8Array([1, 2, 3]);
|
|
1848
|
+
const messages = [
|
|
1849
|
+
new Message({
|
|
1850
|
+
role: 'user',
|
|
1851
|
+
content: [
|
|
1852
|
+
new ToolResultBlock({
|
|
1853
|
+
toolUseId: 'tool-1',
|
|
1854
|
+
status: 'success',
|
|
1855
|
+
content: [new ImageBlock({ format: 'png', source: { bytes: imageBytes } })],
|
|
1856
|
+
}),
|
|
1857
|
+
],
|
|
1858
|
+
}),
|
|
1859
|
+
];
|
|
1860
|
+
collectIterator(provider.stream(messages));
|
|
1861
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1862
|
+
messages: [
|
|
1863
|
+
{
|
|
1864
|
+
role: 'user',
|
|
1865
|
+
content: [
|
|
1866
|
+
{
|
|
1867
|
+
toolResult: {
|
|
1868
|
+
toolUseId: 'tool-1',
|
|
1869
|
+
content: [{ image: { format: 'png', source: { bytes: imageBytes } } }],
|
|
1870
|
+
status: 'success',
|
|
1871
|
+
},
|
|
1872
|
+
},
|
|
1873
|
+
],
|
|
1874
|
+
},
|
|
1875
|
+
],
|
|
1876
|
+
}));
|
|
1877
|
+
});
|
|
1878
|
+
it('formats video block in tool result with 3gp format mapping', async () => {
|
|
1879
|
+
const provider = new BedrockModel();
|
|
1880
|
+
const videoBytes = new Uint8Array([4, 5, 6]);
|
|
1881
|
+
const messages = [
|
|
1882
|
+
new Message({
|
|
1883
|
+
role: 'user',
|
|
1884
|
+
content: [
|
|
1885
|
+
new ToolResultBlock({
|
|
1886
|
+
toolUseId: 'tool-1',
|
|
1887
|
+
status: 'success',
|
|
1888
|
+
content: [new VideoBlock({ format: '3gp', source: { bytes: videoBytes } })],
|
|
1889
|
+
}),
|
|
1890
|
+
],
|
|
1891
|
+
}),
|
|
1892
|
+
];
|
|
1893
|
+
collectIterator(provider.stream(messages));
|
|
1894
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1895
|
+
messages: [
|
|
1896
|
+
{
|
|
1897
|
+
role: 'user',
|
|
1898
|
+
content: [
|
|
1899
|
+
{
|
|
1900
|
+
toolResult: {
|
|
1901
|
+
toolUseId: 'tool-1',
|
|
1902
|
+
content: [{ video: { format: 'three_gp', source: { bytes: videoBytes } } }],
|
|
1903
|
+
status: 'success',
|
|
1904
|
+
},
|
|
1905
|
+
},
|
|
1906
|
+
],
|
|
1907
|
+
},
|
|
1908
|
+
],
|
|
1909
|
+
}));
|
|
1910
|
+
});
|
|
1911
|
+
it('formats document block in tool result', async () => {
|
|
1912
|
+
const provider = new BedrockModel();
|
|
1913
|
+
const docBytes = new Uint8Array([7, 8, 9]);
|
|
1914
|
+
const messages = [
|
|
1915
|
+
new Message({
|
|
1916
|
+
role: 'user',
|
|
1917
|
+
content: [
|
|
1918
|
+
new ToolResultBlock({
|
|
1919
|
+
toolUseId: 'tool-1',
|
|
1920
|
+
status: 'success',
|
|
1921
|
+
content: [new DocumentBlock({ name: 'report.pdf', format: 'pdf', source: { bytes: docBytes } })],
|
|
1922
|
+
}),
|
|
1923
|
+
],
|
|
1924
|
+
}),
|
|
1925
|
+
];
|
|
1926
|
+
collectIterator(provider.stream(messages));
|
|
1927
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1928
|
+
messages: [
|
|
1929
|
+
{
|
|
1930
|
+
role: 'user',
|
|
1931
|
+
content: [
|
|
1932
|
+
{
|
|
1933
|
+
toolResult: {
|
|
1934
|
+
toolUseId: 'tool-1',
|
|
1935
|
+
content: [{ document: { name: 'report.pdf', format: 'pdf', source: { bytes: docBytes } } }],
|
|
1936
|
+
status: 'success',
|
|
1937
|
+
},
|
|
1938
|
+
},
|
|
1939
|
+
],
|
|
1940
|
+
},
|
|
1941
|
+
],
|
|
1942
|
+
}));
|
|
1943
|
+
});
|
|
1944
|
+
it('formats mixed text and media content in tool result', async () => {
|
|
1945
|
+
const provider = new BedrockModel();
|
|
1946
|
+
const imageBytes = new Uint8Array([1, 2]);
|
|
1947
|
+
const messages = [
|
|
1948
|
+
new Message({
|
|
1949
|
+
role: 'user',
|
|
1950
|
+
content: [
|
|
1951
|
+
new ToolResultBlock({
|
|
1952
|
+
toolUseId: 'tool-1',
|
|
1953
|
+
status: 'success',
|
|
1954
|
+
content: [
|
|
1955
|
+
new TextBlock('Here is the image:'),
|
|
1956
|
+
new ImageBlock({ format: 'jpeg', source: { bytes: imageBytes } }),
|
|
1957
|
+
],
|
|
1958
|
+
}),
|
|
1959
|
+
],
|
|
1960
|
+
}),
|
|
1961
|
+
];
|
|
1962
|
+
collectIterator(provider.stream(messages));
|
|
1963
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1964
|
+
messages: [
|
|
1965
|
+
{
|
|
1966
|
+
role: 'user',
|
|
1967
|
+
content: [
|
|
1968
|
+
{
|
|
1969
|
+
toolResult: {
|
|
1970
|
+
toolUseId: 'tool-1',
|
|
1971
|
+
content: [
|
|
1972
|
+
{ text: 'Here is the image:' },
|
|
1973
|
+
{ image: { format: 'jpeg', source: { bytes: imageBytes } } },
|
|
1974
|
+
],
|
|
1975
|
+
status: 'success',
|
|
1976
|
+
},
|
|
1977
|
+
},
|
|
1978
|
+
],
|
|
1979
|
+
},
|
|
1980
|
+
],
|
|
1981
|
+
}));
|
|
1982
|
+
});
|
|
1983
|
+
});
|
|
1984
|
+
describe('media blocks in messages', () => {
|
|
1985
|
+
const mockConverseStreamCommand = vi.mocked(ConverseStreamCommand);
|
|
1986
|
+
it('formats top-level image block', async () => {
|
|
1987
|
+
const provider = new BedrockModel();
|
|
1988
|
+
const imageBytes = new Uint8Array([1, 2, 3]);
|
|
1989
|
+
const messages = [
|
|
1990
|
+
new Message({
|
|
1991
|
+
role: 'user',
|
|
1992
|
+
content: [new ImageBlock({ format: 'png', source: { bytes: imageBytes } })],
|
|
1993
|
+
}),
|
|
1994
|
+
];
|
|
1995
|
+
collectIterator(provider.stream(messages));
|
|
1996
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1997
|
+
messages: [
|
|
1998
|
+
{
|
|
1999
|
+
role: 'user',
|
|
2000
|
+
content: [{ image: { format: 'png', source: { bytes: imageBytes } } }],
|
|
2001
|
+
},
|
|
2002
|
+
],
|
|
2003
|
+
}));
|
|
2004
|
+
});
|
|
2005
|
+
it('formats top-level image block with S3 source', async () => {
|
|
2006
|
+
const provider = new BedrockModel();
|
|
2007
|
+
const messages = [
|
|
2008
|
+
new Message({
|
|
2009
|
+
role: 'user',
|
|
2010
|
+
content: [
|
|
2011
|
+
new ImageBlock({ format: 'png', source: { location: { type: 's3', uri: 's3://bucket/image.png' } } }),
|
|
2012
|
+
],
|
|
2013
|
+
}),
|
|
2014
|
+
];
|
|
2015
|
+
collectIterator(provider.stream(messages));
|
|
2016
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
2017
|
+
messages: [
|
|
2018
|
+
{
|
|
2019
|
+
role: 'user',
|
|
2020
|
+
content: [{ image: { format: 'png', source: { s3Location: { uri: 's3://bucket/image.png' } } } }],
|
|
2021
|
+
},
|
|
2022
|
+
],
|
|
2023
|
+
}));
|
|
2024
|
+
});
|
|
2025
|
+
it('formats top-level video block with 3gp format mapping', async () => {
|
|
2026
|
+
const provider = new BedrockModel();
|
|
2027
|
+
const videoBytes = new Uint8Array([4, 5, 6]);
|
|
2028
|
+
const messages = [
|
|
2029
|
+
new Message({
|
|
2030
|
+
role: 'user',
|
|
2031
|
+
content: [new VideoBlock({ format: '3gp', source: { bytes: videoBytes } })],
|
|
2032
|
+
}),
|
|
2033
|
+
];
|
|
2034
|
+
collectIterator(provider.stream(messages));
|
|
2035
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
2036
|
+
messages: [
|
|
2037
|
+
{
|
|
2038
|
+
role: 'user',
|
|
2039
|
+
content: [{ video: { format: 'three_gp', source: { bytes: videoBytes } } }],
|
|
2040
|
+
},
|
|
2041
|
+
],
|
|
2042
|
+
}));
|
|
2043
|
+
});
|
|
2044
|
+
it('formats top-level document block with text source converted to bytes', async () => {
|
|
2045
|
+
const provider = new BedrockModel();
|
|
2046
|
+
const messages = [
|
|
2047
|
+
new Message({
|
|
2048
|
+
role: 'user',
|
|
2049
|
+
content: [new DocumentBlock({ name: 'notes.txt', format: 'txt', source: { text: 'Hello world' } })],
|
|
2050
|
+
}),
|
|
2051
|
+
];
|
|
2052
|
+
collectIterator(provider.stream(messages));
|
|
2053
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
2054
|
+
messages: [
|
|
2055
|
+
{
|
|
2056
|
+
role: 'user',
|
|
2057
|
+
content: [
|
|
2058
|
+
{
|
|
2059
|
+
document: {
|
|
2060
|
+
name: 'notes.txt',
|
|
2061
|
+
format: 'txt',
|
|
2062
|
+
source: { bytes: new TextEncoder().encode('Hello world') },
|
|
2063
|
+
},
|
|
2064
|
+
},
|
|
2065
|
+
],
|
|
2066
|
+
},
|
|
2067
|
+
],
|
|
2068
|
+
}));
|
|
2069
|
+
});
|
|
2070
|
+
});
|
|
2071
|
+
describe('includeToolResultStatus configuration', async () => {
|
|
2072
|
+
const mockConverseStreamCommand = vi.mocked(ConverseStreamCommand);
|
|
2073
|
+
describe('when includeToolResultStatus is true', () => {
|
|
2074
|
+
it('always includes status field in tool results', async () => {
|
|
2075
|
+
const provider = new BedrockModel({ includeToolResultStatus: true });
|
|
2076
|
+
const messages = [
|
|
2077
|
+
new Message({
|
|
2078
|
+
role: 'user',
|
|
2079
|
+
content: [
|
|
2080
|
+
new ToolResultBlock({
|
|
2081
|
+
toolUseId: 'tool-123',
|
|
2082
|
+
status: 'success',
|
|
2083
|
+
content: [new TextBlock('Result')],
|
|
2084
|
+
}),
|
|
2085
|
+
],
|
|
2086
|
+
}),
|
|
2087
|
+
];
|
|
2088
|
+
collectIterator(provider.stream(messages));
|
|
2089
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith({
|
|
2090
|
+
messages: [
|
|
2091
|
+
{
|
|
2092
|
+
content: [
|
|
2093
|
+
{
|
|
2094
|
+
toolResult: {
|
|
2095
|
+
content: [{ text: 'Result' }],
|
|
2096
|
+
status: 'success',
|
|
2097
|
+
toolUseId: 'tool-123',
|
|
2098
|
+
},
|
|
2099
|
+
},
|
|
2100
|
+
],
|
|
2101
|
+
role: 'user',
|
|
2102
|
+
},
|
|
2103
|
+
],
|
|
2104
|
+
modelId: expect.any(String),
|
|
2105
|
+
});
|
|
2106
|
+
});
|
|
2107
|
+
});
|
|
2108
|
+
describe('when includeToolResultStatus is false', () => {
|
|
2109
|
+
it('never includes status field in tool results', async () => {
|
|
2110
|
+
const provider = new BedrockModel({ includeToolResultStatus: false });
|
|
2111
|
+
const messages = [
|
|
2112
|
+
new Message({
|
|
2113
|
+
role: 'user',
|
|
2114
|
+
content: [
|
|
2115
|
+
new ToolResultBlock({
|
|
2116
|
+
toolUseId: 'tool-123',
|
|
2117
|
+
status: 'success',
|
|
2118
|
+
content: [new TextBlock('Result')],
|
|
2119
|
+
}),
|
|
2120
|
+
],
|
|
2121
|
+
}),
|
|
2122
|
+
];
|
|
2123
|
+
collectIterator(provider.stream(messages));
|
|
2124
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith({
|
|
2125
|
+
messages: [
|
|
2126
|
+
{
|
|
2127
|
+
content: [
|
|
2128
|
+
{
|
|
2129
|
+
toolResult: {
|
|
2130
|
+
content: [{ text: 'Result' }],
|
|
2131
|
+
toolUseId: 'tool-123',
|
|
2132
|
+
},
|
|
2133
|
+
},
|
|
2134
|
+
],
|
|
2135
|
+
role: 'user',
|
|
2136
|
+
},
|
|
2137
|
+
],
|
|
2138
|
+
modelId: expect.any(String),
|
|
2139
|
+
});
|
|
2140
|
+
});
|
|
2141
|
+
});
|
|
2142
|
+
describe('when includeToolResultStatus is auto', () => {
|
|
2143
|
+
it('includes status field for Claude models', async () => {
|
|
2144
|
+
const provider = new BedrockModel({
|
|
2145
|
+
modelId: 'anthropic.claude-3-5-sonnet-20241022-v2:0',
|
|
2146
|
+
includeToolResultStatus: 'auto',
|
|
2147
|
+
});
|
|
2148
|
+
const messages = [
|
|
2149
|
+
new Message({
|
|
2150
|
+
role: 'user',
|
|
2151
|
+
content: [
|
|
2152
|
+
new ToolResultBlock({
|
|
2153
|
+
toolUseId: 'tool-123',
|
|
2154
|
+
status: 'success',
|
|
2155
|
+
content: [new TextBlock('Result')],
|
|
2156
|
+
}),
|
|
2157
|
+
],
|
|
2158
|
+
}),
|
|
2159
|
+
];
|
|
2160
|
+
collectIterator(provider.stream(messages));
|
|
2161
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith({
|
|
2162
|
+
messages: [
|
|
2163
|
+
{
|
|
2164
|
+
content: [
|
|
2165
|
+
{
|
|
2166
|
+
toolResult: {
|
|
2167
|
+
content: [{ text: 'Result' }],
|
|
2168
|
+
status: 'success',
|
|
2169
|
+
toolUseId: 'tool-123',
|
|
2170
|
+
},
|
|
2171
|
+
},
|
|
2172
|
+
],
|
|
2173
|
+
role: 'user',
|
|
2174
|
+
},
|
|
2175
|
+
],
|
|
2176
|
+
modelId: 'anthropic.claude-3-5-sonnet-20241022-v2:0',
|
|
2177
|
+
});
|
|
2178
|
+
});
|
|
2179
|
+
});
|
|
2180
|
+
describe('when includeToolResultStatus is undefined (default)', () => {
|
|
2181
|
+
it('follows auto logic for non-Claude models', async () => {
|
|
2182
|
+
const provider = new BedrockModel({
|
|
2183
|
+
modelId: 'amazon.nova-lite-v1:0',
|
|
2184
|
+
});
|
|
2185
|
+
const messages = [
|
|
2186
|
+
new Message({
|
|
2187
|
+
role: 'user',
|
|
2188
|
+
content: [
|
|
2189
|
+
new ToolResultBlock({
|
|
2190
|
+
toolUseId: 'tool-123',
|
|
2191
|
+
status: 'success',
|
|
2192
|
+
content: [new TextBlock('Result')],
|
|
2193
|
+
}),
|
|
2194
|
+
],
|
|
2195
|
+
}),
|
|
2196
|
+
];
|
|
2197
|
+
collectIterator(provider.stream(messages));
|
|
2198
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith({
|
|
2199
|
+
messages: [
|
|
2200
|
+
{
|
|
2201
|
+
content: [
|
|
2202
|
+
{
|
|
2203
|
+
toolResult: {
|
|
2204
|
+
content: [{ text: 'Result' }],
|
|
2205
|
+
toolUseId: 'tool-123',
|
|
2206
|
+
},
|
|
2207
|
+
},
|
|
2208
|
+
],
|
|
2209
|
+
role: 'user',
|
|
2210
|
+
},
|
|
2211
|
+
],
|
|
2212
|
+
modelId: 'amazon.nova-lite-v1:0',
|
|
2213
|
+
});
|
|
2214
|
+
});
|
|
2215
|
+
});
|
|
2216
|
+
});
|
|
2217
|
+
describe('region configuration', () => {
|
|
2218
|
+
beforeEach(() => {
|
|
2219
|
+
vi.clearAllMocks();
|
|
2220
|
+
});
|
|
2221
|
+
it('uses explicit region when provided', async () => {
|
|
2222
|
+
mockBedrockClientImplementation();
|
|
2223
|
+
const provider = new BedrockModel({ region: 'eu-west-1' });
|
|
2224
|
+
// After applyDefaultRegion wraps the config functions, verify they still return the correct value
|
|
2225
|
+
const regionResult = await provider['_client'].config.region();
|
|
2226
|
+
expect(regionResult).toBe('eu-west-1');
|
|
2227
|
+
});
|
|
2228
|
+
it('defaults to us-west-2 when region is missing', async () => {
|
|
2229
|
+
mockBedrockClientImplementation({
|
|
2230
|
+
region: async () => {
|
|
2231
|
+
throw new Error('Region is missing');
|
|
2232
|
+
},
|
|
2233
|
+
useFipsEndpoint: async () => {
|
|
2234
|
+
throw new Error('Region is missing');
|
|
2235
|
+
},
|
|
2236
|
+
});
|
|
2237
|
+
const provider = new BedrockModel();
|
|
2238
|
+
// After applyDefaultRegion wraps the config functions
|
|
2239
|
+
const regionResult = await provider['_client'].config.region();
|
|
2240
|
+
expect(regionResult).toBe('us-west-2');
|
|
2241
|
+
const fipsResult = await provider['_client'].config.useFipsEndpoint();
|
|
2242
|
+
expect(fipsResult).toBe(false);
|
|
2243
|
+
});
|
|
2244
|
+
it('rethrows other region errors', async () => {
|
|
2245
|
+
mockBedrockClientImplementation({
|
|
2246
|
+
region: async () => {
|
|
2247
|
+
throw new Error('Network error');
|
|
2248
|
+
},
|
|
2249
|
+
});
|
|
2250
|
+
const provider = new BedrockModel();
|
|
2251
|
+
// Should rethrow the error
|
|
2252
|
+
await expect(provider['_client'].config.region()).rejects.toThrow('Network error');
|
|
2253
|
+
});
|
|
2254
|
+
});
|
|
2255
|
+
describe('guardrail configuration', () => {
|
|
2256
|
+
const mockConverseStreamCommand = vi.mocked(ConverseStreamCommand);
|
|
2257
|
+
beforeEach(() => {
|
|
2258
|
+
vi.clearAllMocks();
|
|
2259
|
+
});
|
|
2260
|
+
describe('constructor', () => {
|
|
2261
|
+
it('accepts guardrailConfig in options', () => {
|
|
2262
|
+
const provider = new BedrockModel({
|
|
2263
|
+
guardrailConfig: {
|
|
2264
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2265
|
+
guardrailVersion: '1',
|
|
2266
|
+
},
|
|
2267
|
+
});
|
|
2268
|
+
expect(provider.getConfig().guardrailConfig).toStrictEqual({
|
|
2269
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2270
|
+
guardrailVersion: '1',
|
|
2271
|
+
});
|
|
2272
|
+
});
|
|
2273
|
+
it('accepts guardrailConfig with all options', () => {
|
|
2274
|
+
const provider = new BedrockModel({
|
|
2275
|
+
guardrailConfig: {
|
|
2276
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2277
|
+
guardrailVersion: '1',
|
|
2278
|
+
trace: 'enabled_full',
|
|
2279
|
+
streamProcessingMode: 'sync',
|
|
2280
|
+
redaction: {
|
|
2281
|
+
input: true,
|
|
2282
|
+
inputMessage: '[Custom input redacted.]',
|
|
2283
|
+
output: true,
|
|
2284
|
+
outputMessage: '[Custom output redacted.]',
|
|
2285
|
+
},
|
|
2286
|
+
},
|
|
2287
|
+
});
|
|
2288
|
+
expect(provider.getConfig().guardrailConfig).toStrictEqual({
|
|
2289
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2290
|
+
guardrailVersion: '1',
|
|
2291
|
+
trace: 'enabled_full',
|
|
2292
|
+
streamProcessingMode: 'sync',
|
|
2293
|
+
redaction: {
|
|
2294
|
+
input: true,
|
|
2295
|
+
inputMessage: '[Custom input redacted.]',
|
|
2296
|
+
output: true,
|
|
2297
|
+
outputMessage: '[Custom output redacted.]',
|
|
2298
|
+
},
|
|
2299
|
+
});
|
|
2300
|
+
});
|
|
2301
|
+
});
|
|
2302
|
+
describe('request formatting', () => {
|
|
2303
|
+
it('includes guardrailConfig in request with default trace', async () => {
|
|
2304
|
+
const provider = new BedrockModel({
|
|
2305
|
+
guardrailConfig: {
|
|
2306
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2307
|
+
guardrailVersion: '1',
|
|
2308
|
+
},
|
|
2309
|
+
});
|
|
2310
|
+
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello')] })];
|
|
2311
|
+
collectIterator(provider.stream(messages));
|
|
2312
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
2313
|
+
guardrailConfig: {
|
|
2314
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2315
|
+
guardrailVersion: '1',
|
|
2316
|
+
trace: 'enabled',
|
|
2317
|
+
},
|
|
2318
|
+
}));
|
|
2319
|
+
});
|
|
2320
|
+
it('includes guardrailConfig in request with custom trace', async () => {
|
|
2321
|
+
const provider = new BedrockModel({
|
|
2322
|
+
guardrailConfig: {
|
|
2323
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2324
|
+
guardrailVersion: '1',
|
|
2325
|
+
trace: 'disabled',
|
|
2326
|
+
},
|
|
2327
|
+
});
|
|
2328
|
+
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello')] })];
|
|
2329
|
+
collectIterator(provider.stream(messages));
|
|
2330
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
2331
|
+
guardrailConfig: {
|
|
2332
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2333
|
+
guardrailVersion: '1',
|
|
2334
|
+
trace: 'disabled',
|
|
2335
|
+
},
|
|
2336
|
+
}));
|
|
2337
|
+
});
|
|
2338
|
+
it('includes streamProcessingMode when specified', async () => {
|
|
2339
|
+
const provider = new BedrockModel({
|
|
2340
|
+
guardrailConfig: {
|
|
2341
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2342
|
+
guardrailVersion: '1',
|
|
2343
|
+
streamProcessingMode: 'sync',
|
|
2344
|
+
},
|
|
2345
|
+
});
|
|
2346
|
+
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello')] })];
|
|
2347
|
+
collectIterator(provider.stream(messages));
|
|
2348
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
2349
|
+
guardrailConfig: {
|
|
2350
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2351
|
+
guardrailVersion: '1',
|
|
2352
|
+
trace: 'enabled',
|
|
2353
|
+
streamProcessingMode: 'sync',
|
|
2354
|
+
},
|
|
2355
|
+
}));
|
|
2356
|
+
});
|
|
2357
|
+
it('does not include guardrailConfig when not configured', async () => {
|
|
2358
|
+
const provider = new BedrockModel();
|
|
2359
|
+
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello')] })];
|
|
2360
|
+
collectIterator(provider.stream(messages));
|
|
2361
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.not.objectContaining({
|
|
2362
|
+
guardrailConfig: expect.anything(),
|
|
2363
|
+
}));
|
|
2364
|
+
});
|
|
2365
|
+
});
|
|
2366
|
+
describe('blocked guardrail detection', () => {
|
|
2367
|
+
it('detects blocked guardrail in inputAssessment', async () => {
|
|
2368
|
+
setupMockSend(async function* () {
|
|
2369
|
+
yield { messageStart: { role: 'assistant' } };
|
|
2370
|
+
yield { contentBlockStart: {} };
|
|
2371
|
+
yield { contentBlockDelta: { delta: { text: 'Hello' } } };
|
|
2372
|
+
yield { contentBlockStop: {} };
|
|
2373
|
+
yield { messageStop: { stopReason: 'guardrail_intervened' } };
|
|
2374
|
+
yield {
|
|
2375
|
+
metadata: {
|
|
2376
|
+
usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
2377
|
+
trace: {
|
|
2378
|
+
guardrail: {
|
|
2379
|
+
inputAssessment: {
|
|
2380
|
+
'1234': {
|
|
2381
|
+
topicPolicy: {
|
|
2382
|
+
topics: [{ name: 'Harmful', action: 'BLOCKED', detected: true }],
|
|
2383
|
+
},
|
|
2384
|
+
},
|
|
2385
|
+
},
|
|
2386
|
+
},
|
|
2387
|
+
},
|
|
2388
|
+
},
|
|
2389
|
+
};
|
|
2390
|
+
});
|
|
2391
|
+
const provider = new BedrockModel({
|
|
2392
|
+
guardrailConfig: {
|
|
2393
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2394
|
+
guardrailVersion: '1',
|
|
2395
|
+
},
|
|
2396
|
+
});
|
|
2397
|
+
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello')] })];
|
|
2398
|
+
const events = await collectIterator(provider.stream(messages));
|
|
2399
|
+
const redactEvent = events.find((e) => e.type === 'modelRedactionEvent');
|
|
2400
|
+
expect(redactEvent).toBeDefined();
|
|
2401
|
+
expect(redactEvent).toStrictEqual({
|
|
2402
|
+
type: 'modelRedactionEvent',
|
|
2403
|
+
inputRedaction: { replaceContent: '[User input redacted.]' },
|
|
2404
|
+
});
|
|
2405
|
+
});
|
|
2406
|
+
it('detects blocked guardrail in outputAssessments', async () => {
|
|
2407
|
+
setupMockSend(async function* () {
|
|
2408
|
+
yield { messageStart: { role: 'assistant' } };
|
|
2409
|
+
yield { contentBlockStart: {} };
|
|
2410
|
+
yield { contentBlockDelta: { delta: { text: 'Hello' } } };
|
|
2411
|
+
yield { contentBlockStop: {} };
|
|
2412
|
+
yield { messageStop: { stopReason: 'guardrail_intervened' } };
|
|
2413
|
+
yield {
|
|
2414
|
+
metadata: {
|
|
2415
|
+
usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
2416
|
+
trace: {
|
|
2417
|
+
guardrail: {
|
|
2418
|
+
outputAssessments: {
|
|
2419
|
+
'1234': {
|
|
2420
|
+
contentPolicy: {
|
|
2421
|
+
filters: [{ type: 'VIOLENCE', action: 'BLOCKED', detected: true }],
|
|
2422
|
+
},
|
|
2423
|
+
},
|
|
2424
|
+
},
|
|
2425
|
+
},
|
|
2426
|
+
},
|
|
2427
|
+
},
|
|
2428
|
+
};
|
|
2429
|
+
});
|
|
2430
|
+
const provider = new BedrockModel({
|
|
2431
|
+
guardrailConfig: {
|
|
2432
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2433
|
+
guardrailVersion: '1',
|
|
2434
|
+
},
|
|
2435
|
+
});
|
|
2436
|
+
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello')] })];
|
|
2437
|
+
const events = await collectIterator(provider.stream(messages));
|
|
2438
|
+
const redactEvent = events.find((e) => e.type === 'modelRedactionEvent');
|
|
2439
|
+
expect(redactEvent).toBeDefined();
|
|
2440
|
+
});
|
|
2441
|
+
it('does not emit redaction events when guardrail not blocked', async () => {
|
|
2442
|
+
setupMockSend(async function* () {
|
|
2443
|
+
yield { messageStart: { role: 'assistant' } };
|
|
2444
|
+
yield { contentBlockStart: {} };
|
|
2445
|
+
yield { contentBlockDelta: { delta: { text: 'Hello' } } };
|
|
2446
|
+
yield { contentBlockStop: {} };
|
|
2447
|
+
yield { messageStop: { stopReason: 'end_turn' } };
|
|
2448
|
+
yield {
|
|
2449
|
+
metadata: {
|
|
2450
|
+
usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
2451
|
+
trace: {
|
|
2452
|
+
guardrail: {
|
|
2453
|
+
inputAssessment: {
|
|
2454
|
+
'1234': {
|
|
2455
|
+
topicPolicy: {
|
|
2456
|
+
topics: [{ name: 'Safe', action: 'NONE', detected: false }],
|
|
2457
|
+
},
|
|
2458
|
+
},
|
|
2459
|
+
},
|
|
2460
|
+
},
|
|
2461
|
+
},
|
|
2462
|
+
},
|
|
2463
|
+
};
|
|
2464
|
+
});
|
|
2465
|
+
const provider = new BedrockModel({
|
|
2466
|
+
guardrailConfig: {
|
|
2467
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2468
|
+
guardrailVersion: '1',
|
|
2469
|
+
},
|
|
2470
|
+
});
|
|
2471
|
+
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello')] })];
|
|
2472
|
+
const events = await collectIterator(provider.stream(messages));
|
|
2473
|
+
const redactEvent = events.find((e) => e.type === 'modelRedactionEvent');
|
|
2474
|
+
expect(redactEvent).toBeUndefined();
|
|
2475
|
+
});
|
|
2476
|
+
it('does not emit redaction events without guardrailConfig', async () => {
|
|
2477
|
+
setupMockSend(async function* () {
|
|
2478
|
+
yield { messageStart: { role: 'assistant' } };
|
|
2479
|
+
yield { contentBlockStart: {} };
|
|
2480
|
+
yield { contentBlockDelta: { delta: { text: 'Hello' } } };
|
|
2481
|
+
yield { contentBlockStop: {} };
|
|
2482
|
+
yield { messageStop: { stopReason: 'guardrail_intervened' } };
|
|
2483
|
+
yield {
|
|
2484
|
+
metadata: {
|
|
2485
|
+
usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
2486
|
+
trace: {
|
|
2487
|
+
guardrail: {
|
|
2488
|
+
inputAssessment: {
|
|
2489
|
+
'1234': {
|
|
2490
|
+
topicPolicy: {
|
|
2491
|
+
topics: [{ name: 'Harmful', action: 'BLOCKED', detected: true }],
|
|
2492
|
+
},
|
|
2493
|
+
},
|
|
2494
|
+
},
|
|
2495
|
+
},
|
|
2496
|
+
},
|
|
2497
|
+
},
|
|
2498
|
+
};
|
|
2499
|
+
});
|
|
2500
|
+
const provider = new BedrockModel();
|
|
2501
|
+
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello')] })];
|
|
2502
|
+
const events = await collectIterator(provider.stream(messages));
|
|
2503
|
+
const redactEvent = events.find((e) => e.type === 'modelRedactionEvent');
|
|
2504
|
+
expect(redactEvent).toBeUndefined();
|
|
2505
|
+
});
|
|
2506
|
+
});
|
|
2507
|
+
describe('redaction event generation', () => {
|
|
2508
|
+
it('emits input redaction with default message', async () => {
|
|
2509
|
+
setupMockSend(async function* () {
|
|
2510
|
+
yield { messageStart: { role: 'assistant' } };
|
|
2511
|
+
yield { messageStop: { stopReason: 'guardrail_intervened' } };
|
|
2512
|
+
yield {
|
|
2513
|
+
metadata: {
|
|
2514
|
+
usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
2515
|
+
trace: {
|
|
2516
|
+
guardrail: {
|
|
2517
|
+
inputAssessment: { '1': { topicPolicy: { topics: [{ action: 'BLOCKED', detected: true }] } } },
|
|
2518
|
+
},
|
|
2519
|
+
},
|
|
2520
|
+
},
|
|
2521
|
+
};
|
|
2522
|
+
});
|
|
2523
|
+
const provider = new BedrockModel({
|
|
2524
|
+
guardrailConfig: {
|
|
2525
|
+
guardrailIdentifier: 'id',
|
|
2526
|
+
guardrailVersion: '1',
|
|
2527
|
+
},
|
|
2528
|
+
});
|
|
2529
|
+
const events = await collectIterator(provider.stream([new Message({ role: 'user', content: [new TextBlock('Hello')] })]));
|
|
2530
|
+
expect(events).toContainEqual({
|
|
2531
|
+
type: 'modelRedactionEvent',
|
|
2532
|
+
inputRedaction: { replaceContent: '[User input redacted.]' },
|
|
2533
|
+
});
|
|
2534
|
+
});
|
|
2535
|
+
it('emits input redaction with custom message', async () => {
|
|
2536
|
+
setupMockSend(async function* () {
|
|
2537
|
+
yield { messageStart: { role: 'assistant' } };
|
|
2538
|
+
yield { messageStop: { stopReason: 'guardrail_intervened' } };
|
|
2539
|
+
yield {
|
|
2540
|
+
metadata: {
|
|
2541
|
+
usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
2542
|
+
trace: {
|
|
2543
|
+
guardrail: {
|
|
2544
|
+
inputAssessment: { '1': { topicPolicy: { topics: [{ action: 'BLOCKED', detected: true }] } } },
|
|
2545
|
+
},
|
|
2546
|
+
},
|
|
2547
|
+
},
|
|
2548
|
+
};
|
|
2549
|
+
});
|
|
2550
|
+
const provider = new BedrockModel({
|
|
2551
|
+
guardrailConfig: {
|
|
2552
|
+
guardrailIdentifier: 'id',
|
|
2553
|
+
guardrailVersion: '1',
|
|
2554
|
+
redaction: {
|
|
2555
|
+
inputMessage: '[Custom input message]',
|
|
2556
|
+
},
|
|
2557
|
+
},
|
|
2558
|
+
});
|
|
2559
|
+
const events = await collectIterator(provider.stream([new Message({ role: 'user', content: [new TextBlock('Hello')] })]));
|
|
2560
|
+
expect(events).toContainEqual({
|
|
2561
|
+
type: 'modelRedactionEvent',
|
|
2562
|
+
inputRedaction: { replaceContent: '[Custom input message]' },
|
|
2563
|
+
});
|
|
2564
|
+
});
|
|
2565
|
+
it('does not emit input redaction when redactInput is false', async () => {
|
|
2566
|
+
setupMockSend(async function* () {
|
|
2567
|
+
yield { messageStart: { role: 'assistant' } };
|
|
2568
|
+
yield { messageStop: { stopReason: 'guardrail_intervened' } };
|
|
2569
|
+
yield {
|
|
2570
|
+
metadata: {
|
|
2571
|
+
usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
2572
|
+
trace: {
|
|
2573
|
+
guardrail: {
|
|
2574
|
+
inputAssessment: { '1': { topicPolicy: { topics: [{ action: 'BLOCKED', detected: true }] } } },
|
|
2575
|
+
},
|
|
2576
|
+
},
|
|
2577
|
+
},
|
|
2578
|
+
};
|
|
2579
|
+
});
|
|
2580
|
+
const provider = new BedrockModel({
|
|
2581
|
+
guardrailConfig: {
|
|
2582
|
+
guardrailIdentifier: 'id',
|
|
2583
|
+
guardrailVersion: '1',
|
|
2584
|
+
redaction: {
|
|
2585
|
+
input: false,
|
|
2586
|
+
},
|
|
2587
|
+
},
|
|
2588
|
+
});
|
|
2589
|
+
const events = await collectIterator(provider.stream([new Message({ role: 'user', content: [new TextBlock('Hello')] })]));
|
|
2590
|
+
const inputRedactEvent = events.find((e) => e.type === 'modelRedactionEvent' && 'inputRedaction' in e);
|
|
2591
|
+
expect(inputRedactEvent).toBeUndefined();
|
|
2592
|
+
});
|
|
2593
|
+
it('emits output redaction when redactOutput is true', async () => {
|
|
2594
|
+
setupMockSend(async function* () {
|
|
2595
|
+
yield { messageStart: { role: 'assistant' } };
|
|
2596
|
+
yield { messageStop: { stopReason: 'guardrail_intervened' } };
|
|
2597
|
+
yield {
|
|
2598
|
+
metadata: {
|
|
2599
|
+
usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
2600
|
+
trace: {
|
|
2601
|
+
guardrail: {
|
|
2602
|
+
inputAssessment: { '1': { topicPolicy: { topics: [{ action: 'BLOCKED', detected: true }] } } },
|
|
2603
|
+
},
|
|
2604
|
+
},
|
|
2605
|
+
},
|
|
2606
|
+
};
|
|
2607
|
+
});
|
|
2608
|
+
const provider = new BedrockModel({
|
|
2609
|
+
guardrailConfig: {
|
|
2610
|
+
guardrailIdentifier: 'id',
|
|
2611
|
+
guardrailVersion: '1',
|
|
2612
|
+
redaction: {
|
|
2613
|
+
output: true,
|
|
2614
|
+
},
|
|
2615
|
+
},
|
|
2616
|
+
});
|
|
2617
|
+
const events = await collectIterator(provider.stream([new Message({ role: 'user', content: [new TextBlock('Hello')] })]));
|
|
2618
|
+
expect(events).toContainEqual({
|
|
2619
|
+
type: 'modelRedactionEvent',
|
|
2620
|
+
outputRedaction: { replaceContent: '[Assistant output redacted.]' },
|
|
2621
|
+
});
|
|
2622
|
+
});
|
|
2623
|
+
it('emits output redaction with custom message', async () => {
|
|
2624
|
+
setupMockSend(async function* () {
|
|
2625
|
+
yield { messageStart: { role: 'assistant' } };
|
|
2626
|
+
yield { messageStop: { stopReason: 'guardrail_intervened' } };
|
|
2627
|
+
yield {
|
|
2628
|
+
metadata: {
|
|
2629
|
+
usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
2630
|
+
trace: {
|
|
2631
|
+
guardrail: {
|
|
2632
|
+
inputAssessment: { '1': { topicPolicy: { topics: [{ action: 'BLOCKED', detected: true }] } } },
|
|
2633
|
+
},
|
|
2634
|
+
},
|
|
2635
|
+
},
|
|
2636
|
+
};
|
|
2637
|
+
});
|
|
2638
|
+
const provider = new BedrockModel({
|
|
2639
|
+
guardrailConfig: {
|
|
2640
|
+
guardrailIdentifier: 'id',
|
|
2641
|
+
guardrailVersion: '1',
|
|
2642
|
+
redaction: {
|
|
2643
|
+
output: true,
|
|
2644
|
+
outputMessage: '[Custom output message]',
|
|
2645
|
+
},
|
|
2646
|
+
},
|
|
2647
|
+
});
|
|
2648
|
+
const events = await collectIterator(provider.stream([new Message({ role: 'user', content: [new TextBlock('Hello')] })]));
|
|
2649
|
+
expect(events).toContainEqual({
|
|
2650
|
+
type: 'modelRedactionEvent',
|
|
2651
|
+
outputRedaction: { replaceContent: '[Custom output message]' },
|
|
2652
|
+
});
|
|
2653
|
+
});
|
|
2654
|
+
it('emits both input and output redaction when both are enabled', async () => {
|
|
2655
|
+
setupMockSend(async function* () {
|
|
2656
|
+
yield { messageStart: { role: 'assistant' } };
|
|
2657
|
+
yield { messageStop: { stopReason: 'guardrail_intervened' } };
|
|
2658
|
+
yield {
|
|
2659
|
+
metadata: {
|
|
2660
|
+
usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
2661
|
+
trace: {
|
|
2662
|
+
guardrail: {
|
|
2663
|
+
inputAssessment: { '1': { topicPolicy: { topics: [{ action: 'BLOCKED', detected: true }] } } },
|
|
2664
|
+
},
|
|
2665
|
+
},
|
|
2666
|
+
},
|
|
2667
|
+
};
|
|
2668
|
+
});
|
|
2669
|
+
const provider = new BedrockModel({
|
|
2670
|
+
guardrailConfig: {
|
|
2671
|
+
guardrailIdentifier: 'id',
|
|
2672
|
+
guardrailVersion: '1',
|
|
2673
|
+
redaction: {
|
|
2674
|
+
input: true,
|
|
2675
|
+
output: true,
|
|
2676
|
+
},
|
|
2677
|
+
},
|
|
2678
|
+
});
|
|
2679
|
+
const events = await collectIterator(provider.stream([new Message({ role: 'user', content: [new TextBlock('Hello')] })]));
|
|
2680
|
+
expect(events).toContainEqual({
|
|
2681
|
+
type: 'modelRedactionEvent',
|
|
2682
|
+
inputRedaction: { replaceContent: '[User input redacted.]' },
|
|
2683
|
+
});
|
|
2684
|
+
expect(events).toContainEqual({
|
|
2685
|
+
type: 'modelRedactionEvent',
|
|
2686
|
+
outputRedaction: { replaceContent: '[Assistant output redacted.]' },
|
|
2687
|
+
});
|
|
2688
|
+
});
|
|
2689
|
+
it('includes redactedContent from modelOutput when available', async () => {
|
|
2690
|
+
setupMockSend(async function* () {
|
|
2691
|
+
yield { messageStart: { role: 'assistant' } };
|
|
2692
|
+
yield { contentBlockStart: {} };
|
|
2693
|
+
yield { contentBlockDelta: { delta: { text: 'This content was blocked' } } };
|
|
2694
|
+
yield { contentBlockStop: {} };
|
|
2695
|
+
yield { messageStop: { stopReason: 'guardrail_intervened' } };
|
|
2696
|
+
yield {
|
|
2697
|
+
metadata: {
|
|
2698
|
+
usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
2699
|
+
trace: {
|
|
2700
|
+
guardrail: {
|
|
2701
|
+
modelOutput: ['This content ', 'was blocked'],
|
|
2702
|
+
outputAssessments: {
|
|
2703
|
+
'0': [{ topicPolicy: { topics: [{ action: 'BLOCKED', detected: true }] } }],
|
|
2704
|
+
},
|
|
2705
|
+
},
|
|
2706
|
+
},
|
|
2707
|
+
},
|
|
2708
|
+
};
|
|
2709
|
+
});
|
|
2710
|
+
const provider = new BedrockModel({
|
|
2711
|
+
guardrailConfig: {
|
|
2712
|
+
guardrailIdentifier: 'id',
|
|
2713
|
+
guardrailVersion: '1',
|
|
2714
|
+
redaction: {
|
|
2715
|
+
output: true,
|
|
2716
|
+
outputMessage: '[Blocked]',
|
|
2717
|
+
},
|
|
2718
|
+
},
|
|
2719
|
+
});
|
|
2720
|
+
const events = await collectIterator(provider.stream([new Message({ role: 'user', content: [new TextBlock('Hello')] })]));
|
|
2721
|
+
expect(events).toContainEqual({
|
|
2722
|
+
type: 'modelRedactionEvent',
|
|
2723
|
+
outputRedaction: {
|
|
2724
|
+
replaceContent: '[Blocked]',
|
|
2725
|
+
redactedContent: 'This content was blocked',
|
|
2726
|
+
},
|
|
2727
|
+
});
|
|
2728
|
+
});
|
|
2729
|
+
});
|
|
2730
|
+
describe('non-streaming mode', () => {
|
|
2731
|
+
it('emits redaction events in non-streaming mode when guardrail blocks', async () => {
|
|
2732
|
+
const mockSend = vi.fn(async () => ({
|
|
2733
|
+
output: {
|
|
2734
|
+
message: {
|
|
2735
|
+
role: 'assistant',
|
|
2736
|
+
content: [{ text: 'Hello' }],
|
|
2737
|
+
},
|
|
2738
|
+
},
|
|
2739
|
+
stopReason: 'guardrail_intervened',
|
|
2740
|
+
usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
2741
|
+
trace: {
|
|
2742
|
+
guardrail: {
|
|
2743
|
+
inputAssessment: { '1': { topicPolicy: { topics: [{ action: 'BLOCKED', detected: true }] } } },
|
|
2744
|
+
},
|
|
2745
|
+
},
|
|
2746
|
+
}));
|
|
2747
|
+
mockBedrockClientImplementation({ send: mockSend });
|
|
2748
|
+
const provider = new BedrockModel({
|
|
2749
|
+
stream: false,
|
|
2750
|
+
guardrailConfig: {
|
|
2751
|
+
guardrailIdentifier: 'id',
|
|
2752
|
+
guardrailVersion: '1',
|
|
2753
|
+
},
|
|
2754
|
+
});
|
|
2755
|
+
const events = await collectIterator(provider.stream([new Message({ role: 'user', content: [new TextBlock('Hello')] })]));
|
|
2756
|
+
expect(events).toContainEqual({
|
|
2757
|
+
type: 'modelRedactionEvent',
|
|
2758
|
+
inputRedaction: { replaceContent: '[User input redacted.]' },
|
|
2759
|
+
});
|
|
2760
|
+
});
|
|
2761
|
+
});
|
|
2762
|
+
describe('guardLatestUserMessage', () => {
|
|
2763
|
+
const mockConverseStreamCommand = vi.mocked(ConverseStreamCommand);
|
|
2764
|
+
beforeEach(() => {
|
|
2765
|
+
vi.clearAllMocks();
|
|
2766
|
+
});
|
|
2767
|
+
it('accepts guardLatestUserMessage in guardrailConfig', () => {
|
|
2768
|
+
const provider = new BedrockModel({
|
|
2769
|
+
guardrailConfig: {
|
|
2770
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2771
|
+
guardrailVersion: '1',
|
|
2772
|
+
guardLatestUserMessage: true,
|
|
2773
|
+
},
|
|
2774
|
+
});
|
|
2775
|
+
expect(provider.getConfig().guardrailConfig).toStrictEqual({
|
|
2776
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2777
|
+
guardrailVersion: '1',
|
|
2778
|
+
guardLatestUserMessage: true,
|
|
2779
|
+
});
|
|
2780
|
+
});
|
|
2781
|
+
it('wraps latest user message text content in guardContent when enabled', async () => {
|
|
2782
|
+
const provider = new BedrockModel({
|
|
2783
|
+
guardrailConfig: {
|
|
2784
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2785
|
+
guardrailVersion: '1',
|
|
2786
|
+
guardLatestUserMessage: true,
|
|
2787
|
+
},
|
|
2788
|
+
});
|
|
2789
|
+
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello world')] })];
|
|
2790
|
+
collectIterator(provider.stream(messages));
|
|
2791
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
2792
|
+
messages: [
|
|
2793
|
+
{
|
|
2794
|
+
role: 'user',
|
|
2795
|
+
content: [
|
|
2796
|
+
{
|
|
2797
|
+
guardContent: {
|
|
2798
|
+
text: {
|
|
2799
|
+
text: 'Hello world',
|
|
2800
|
+
},
|
|
2801
|
+
},
|
|
2802
|
+
},
|
|
2803
|
+
],
|
|
2804
|
+
},
|
|
2805
|
+
],
|
|
2806
|
+
}));
|
|
2807
|
+
});
|
|
2808
|
+
it('wraps latest user message image content in guardContent when enabled', async () => {
|
|
2809
|
+
const imageBytes = new Uint8Array([1, 2, 3, 4]);
|
|
2810
|
+
const provider = new BedrockModel({
|
|
2811
|
+
guardrailConfig: {
|
|
2812
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2813
|
+
guardrailVersion: '1',
|
|
2814
|
+
guardLatestUserMessage: true,
|
|
2815
|
+
},
|
|
2816
|
+
});
|
|
2817
|
+
const messages = [
|
|
2818
|
+
new Message({
|
|
2819
|
+
role: 'user',
|
|
2820
|
+
content: [
|
|
2821
|
+
new ImageBlock({
|
|
2822
|
+
format: 'jpeg',
|
|
2823
|
+
source: { bytes: imageBytes },
|
|
2824
|
+
}),
|
|
2825
|
+
],
|
|
2826
|
+
}),
|
|
2827
|
+
];
|
|
2828
|
+
collectIterator(provider.stream(messages));
|
|
2829
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
2830
|
+
messages: [
|
|
2831
|
+
{
|
|
2832
|
+
role: 'user',
|
|
2833
|
+
content: [
|
|
2834
|
+
{
|
|
2835
|
+
guardContent: {
|
|
2836
|
+
image: {
|
|
2837
|
+
format: 'jpeg',
|
|
2838
|
+
source: { bytes: imageBytes },
|
|
2839
|
+
},
|
|
2840
|
+
},
|
|
2841
|
+
},
|
|
2842
|
+
],
|
|
2843
|
+
},
|
|
2844
|
+
],
|
|
2845
|
+
}));
|
|
2846
|
+
});
|
|
2847
|
+
it('does not wrap toolResult messages even though role is user', async () => {
|
|
2848
|
+
const provider = new BedrockModel({
|
|
2849
|
+
guardrailConfig: {
|
|
2850
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2851
|
+
guardrailVersion: '1',
|
|
2852
|
+
guardLatestUserMessage: true,
|
|
2853
|
+
},
|
|
2854
|
+
});
|
|
2855
|
+
const messages = [
|
|
2856
|
+
new Message({ role: 'user', content: [new TextBlock('What is 2+2?')] }),
|
|
2857
|
+
new Message({
|
|
2858
|
+
role: 'assistant',
|
|
2859
|
+
content: [
|
|
2860
|
+
new ToolUseBlock({
|
|
2861
|
+
name: 'calculator',
|
|
2862
|
+
toolUseId: 'tool-123',
|
|
2863
|
+
input: { expression: '2+2' },
|
|
2864
|
+
}),
|
|
2865
|
+
],
|
|
2866
|
+
}),
|
|
2867
|
+
new Message({
|
|
2868
|
+
role: 'user',
|
|
2869
|
+
content: [
|
|
2870
|
+
new ToolResultBlock({
|
|
2871
|
+
toolUseId: 'tool-123',
|
|
2872
|
+
status: 'success',
|
|
2873
|
+
content: [new TextBlock('4')],
|
|
2874
|
+
}),
|
|
2875
|
+
],
|
|
2876
|
+
}),
|
|
2877
|
+
];
|
|
2878
|
+
collectIterator(provider.stream(messages));
|
|
2879
|
+
// The latest message is a toolResult, but guardContent should wrap the FIRST user message
|
|
2880
|
+
// which contains text, not the toolResult
|
|
2881
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
2882
|
+
messages: [
|
|
2883
|
+
{
|
|
2884
|
+
role: 'user',
|
|
2885
|
+
content: [
|
|
2886
|
+
{
|
|
2887
|
+
guardContent: {
|
|
2888
|
+
text: {
|
|
2889
|
+
text: 'What is 2+2?',
|
|
2890
|
+
},
|
|
2891
|
+
},
|
|
2892
|
+
},
|
|
2893
|
+
],
|
|
2894
|
+
},
|
|
2895
|
+
{
|
|
2896
|
+
role: 'assistant',
|
|
2897
|
+
content: [
|
|
2898
|
+
{
|
|
2899
|
+
toolUse: {
|
|
2900
|
+
name: 'calculator',
|
|
2901
|
+
toolUseId: 'tool-123',
|
|
2902
|
+
input: { expression: '2+2' },
|
|
2903
|
+
},
|
|
2904
|
+
},
|
|
2905
|
+
],
|
|
2906
|
+
},
|
|
2907
|
+
{
|
|
2908
|
+
role: 'user',
|
|
2909
|
+
content: [
|
|
2910
|
+
{
|
|
2911
|
+
toolResult: expect.objectContaining({
|
|
2912
|
+
toolUseId: 'tool-123',
|
|
2913
|
+
}),
|
|
2914
|
+
},
|
|
2915
|
+
],
|
|
2916
|
+
},
|
|
2917
|
+
],
|
|
2918
|
+
}));
|
|
2919
|
+
});
|
|
2920
|
+
it('does not wrap messages when guardLatestUserMessage is false', async () => {
|
|
2921
|
+
const provider = new BedrockModel({
|
|
2922
|
+
guardrailConfig: {
|
|
2923
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2924
|
+
guardrailVersion: '1',
|
|
2925
|
+
guardLatestUserMessage: false,
|
|
2926
|
+
},
|
|
2927
|
+
});
|
|
2928
|
+
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello world')] })];
|
|
2929
|
+
collectIterator(provider.stream(messages));
|
|
2930
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
2931
|
+
messages: [
|
|
2932
|
+
{
|
|
2933
|
+
role: 'user',
|
|
2934
|
+
content: [{ text: 'Hello world' }],
|
|
2935
|
+
},
|
|
2936
|
+
],
|
|
2937
|
+
}));
|
|
2938
|
+
});
|
|
2939
|
+
it('does not wrap messages when guardLatestUserMessage is undefined', async () => {
|
|
2940
|
+
const provider = new BedrockModel({
|
|
2941
|
+
guardrailConfig: {
|
|
2942
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2943
|
+
guardrailVersion: '1',
|
|
2944
|
+
},
|
|
2945
|
+
});
|
|
2946
|
+
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello world')] })];
|
|
2947
|
+
collectIterator(provider.stream(messages));
|
|
2948
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
2949
|
+
messages: [
|
|
2950
|
+
{
|
|
2951
|
+
role: 'user',
|
|
2952
|
+
content: [{ text: 'Hello world' }],
|
|
2953
|
+
},
|
|
2954
|
+
],
|
|
2955
|
+
}));
|
|
2956
|
+
});
|
|
2957
|
+
it('does not wrap assistant messages', async () => {
|
|
2958
|
+
const provider = new BedrockModel({
|
|
2959
|
+
guardrailConfig: {
|
|
2960
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2961
|
+
guardrailVersion: '1',
|
|
2962
|
+
guardLatestUserMessage: true,
|
|
2963
|
+
},
|
|
2964
|
+
});
|
|
2965
|
+
const messages = [
|
|
2966
|
+
new Message({ role: 'user', content: [new TextBlock('Hello')] }),
|
|
2967
|
+
new Message({ role: 'assistant', content: [new TextBlock('Hi there!')] }),
|
|
2968
|
+
];
|
|
2969
|
+
collectIterator(provider.stream(messages));
|
|
2970
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
2971
|
+
messages: [
|
|
2972
|
+
{
|
|
2973
|
+
role: 'user',
|
|
2974
|
+
content: [
|
|
2975
|
+
{
|
|
2976
|
+
guardContent: {
|
|
2977
|
+
text: {
|
|
2978
|
+
text: 'Hello',
|
|
2979
|
+
},
|
|
2980
|
+
},
|
|
2981
|
+
},
|
|
2982
|
+
],
|
|
2983
|
+
},
|
|
2984
|
+
{
|
|
2985
|
+
role: 'assistant',
|
|
2986
|
+
content: [{ text: 'Hi there!' }],
|
|
2987
|
+
},
|
|
2988
|
+
],
|
|
2989
|
+
}));
|
|
2990
|
+
});
|
|
2991
|
+
it('wraps only the last user text/image message in multi-turn conversation', async () => {
|
|
2992
|
+
const provider = new BedrockModel({
|
|
2993
|
+
guardrailConfig: {
|
|
2994
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
2995
|
+
guardrailVersion: '1',
|
|
2996
|
+
guardLatestUserMessage: true,
|
|
2997
|
+
},
|
|
2998
|
+
});
|
|
2999
|
+
const messages = [
|
|
3000
|
+
new Message({ role: 'user', content: [new TextBlock('First message')] }),
|
|
3001
|
+
new Message({ role: 'assistant', content: [new TextBlock('First response')] }),
|
|
3002
|
+
new Message({ role: 'user', content: [new TextBlock('Second message')] }),
|
|
3003
|
+
];
|
|
3004
|
+
collectIterator(provider.stream(messages));
|
|
3005
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
3006
|
+
messages: [
|
|
3007
|
+
{
|
|
3008
|
+
role: 'user',
|
|
3009
|
+
content: [{ text: 'First message' }],
|
|
3010
|
+
},
|
|
3011
|
+
{
|
|
3012
|
+
role: 'assistant',
|
|
3013
|
+
content: [{ text: 'First response' }],
|
|
3014
|
+
},
|
|
3015
|
+
{
|
|
3016
|
+
role: 'user',
|
|
1367
3017
|
content: [
|
|
1368
3018
|
{
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
3019
|
+
guardContent: {
|
|
3020
|
+
text: {
|
|
3021
|
+
text: 'Second message',
|
|
3022
|
+
},
|
|
1372
3023
|
},
|
|
1373
3024
|
},
|
|
1374
3025
|
],
|
|
1375
|
-
role: 'user',
|
|
1376
3026
|
},
|
|
1377
3027
|
],
|
|
1378
|
-
|
|
3028
|
+
}));
|
|
3029
|
+
});
|
|
3030
|
+
it('handles no user messages with text/image content gracefully', async () => {
|
|
3031
|
+
const provider = new BedrockModel({
|
|
3032
|
+
guardrailConfig: {
|
|
3033
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
3034
|
+
guardrailVersion: '1',
|
|
3035
|
+
guardLatestUserMessage: true,
|
|
3036
|
+
},
|
|
1379
3037
|
});
|
|
3038
|
+
// Only assistant message, no user text/image content
|
|
3039
|
+
const messages = [new Message({ role: 'assistant', content: [new TextBlock('Hello!')] })];
|
|
3040
|
+
collectIterator(provider.stream(messages));
|
|
3041
|
+
// Should not throw and should not wrap anything
|
|
3042
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
3043
|
+
messages: [
|
|
3044
|
+
{
|
|
3045
|
+
role: 'assistant',
|
|
3046
|
+
content: [{ text: 'Hello!' }],
|
|
3047
|
+
},
|
|
3048
|
+
],
|
|
3049
|
+
}));
|
|
1380
3050
|
});
|
|
1381
|
-
|
|
1382
|
-
describe('when includeToolResultStatus is auto', () => {
|
|
1383
|
-
it('includes status field for Claude models', async () => {
|
|
3051
|
+
it('preserves explicit GuardContentBlock in messages without double-wrapping', async () => {
|
|
1384
3052
|
const provider = new BedrockModel({
|
|
1385
|
-
|
|
1386
|
-
|
|
3053
|
+
guardrailConfig: {
|
|
3054
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
3055
|
+
guardrailVersion: '1',
|
|
3056
|
+
guardLatestUserMessage: true,
|
|
3057
|
+
},
|
|
1387
3058
|
});
|
|
1388
3059
|
const messages = [
|
|
1389
3060
|
new Message({
|
|
1390
3061
|
role: 'user',
|
|
1391
3062
|
content: [
|
|
1392
|
-
new
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
3063
|
+
new GuardContentBlock({
|
|
3064
|
+
text: {
|
|
3065
|
+
qualifiers: ['grounding_source'],
|
|
3066
|
+
text: 'Already guarded content',
|
|
3067
|
+
},
|
|
1396
3068
|
}),
|
|
1397
3069
|
],
|
|
1398
3070
|
}),
|
|
1399
3071
|
];
|
|
1400
3072
|
collectIterator(provider.stream(messages));
|
|
1401
|
-
|
|
3073
|
+
// Explicit GuardContentBlock should be preserved as-is (no text/image content to wrap)
|
|
3074
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1402
3075
|
messages: [
|
|
1403
3076
|
{
|
|
3077
|
+
role: 'user',
|
|
1404
3078
|
content: [
|
|
1405
3079
|
{
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
3080
|
+
guardContent: {
|
|
3081
|
+
text: {
|
|
3082
|
+
text: 'Already guarded content',
|
|
3083
|
+
qualifiers: ['grounding_source'],
|
|
3084
|
+
},
|
|
1410
3085
|
},
|
|
1411
3086
|
},
|
|
1412
3087
|
],
|
|
3088
|
+
},
|
|
3089
|
+
],
|
|
3090
|
+
}));
|
|
3091
|
+
});
|
|
3092
|
+
it('wraps all text and image blocks in the latest user message', async () => {
|
|
3093
|
+
const imageBytes = new Uint8Array([5, 6, 7, 8]);
|
|
3094
|
+
const provider = new BedrockModel({
|
|
3095
|
+
guardrailConfig: {
|
|
3096
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
3097
|
+
guardrailVersion: '1',
|
|
3098
|
+
guardLatestUserMessage: true,
|
|
3099
|
+
},
|
|
3100
|
+
});
|
|
3101
|
+
const messages = [
|
|
3102
|
+
new Message({
|
|
3103
|
+
role: 'user',
|
|
3104
|
+
content: [
|
|
3105
|
+
new TextBlock('Check this text'),
|
|
3106
|
+
new ImageBlock({
|
|
3107
|
+
format: 'png',
|
|
3108
|
+
source: { bytes: imageBytes },
|
|
3109
|
+
}),
|
|
3110
|
+
new TextBlock('And this text too'),
|
|
3111
|
+
],
|
|
3112
|
+
}),
|
|
3113
|
+
];
|
|
3114
|
+
collectIterator(provider.stream(messages));
|
|
3115
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
3116
|
+
messages: [
|
|
3117
|
+
{
|
|
1413
3118
|
role: 'user',
|
|
3119
|
+
content: [
|
|
3120
|
+
{
|
|
3121
|
+
guardContent: {
|
|
3122
|
+
text: {
|
|
3123
|
+
text: 'Check this text',
|
|
3124
|
+
},
|
|
3125
|
+
},
|
|
3126
|
+
},
|
|
3127
|
+
{
|
|
3128
|
+
guardContent: {
|
|
3129
|
+
image: {
|
|
3130
|
+
format: 'png',
|
|
3131
|
+
source: { bytes: imageBytes },
|
|
3132
|
+
},
|
|
3133
|
+
},
|
|
3134
|
+
},
|
|
3135
|
+
{
|
|
3136
|
+
guardContent: {
|
|
3137
|
+
text: {
|
|
3138
|
+
text: 'And this text too',
|
|
3139
|
+
},
|
|
3140
|
+
},
|
|
3141
|
+
},
|
|
3142
|
+
],
|
|
1414
3143
|
},
|
|
1415
3144
|
],
|
|
1416
|
-
|
|
3145
|
+
}));
|
|
3146
|
+
});
|
|
3147
|
+
it('skips wrapping images with unsupported formats (gif)', async () => {
|
|
3148
|
+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
3149
|
+
const imageBytes = new Uint8Array([1, 2, 3, 4]);
|
|
3150
|
+
const provider = new BedrockModel({
|
|
3151
|
+
guardrailConfig: {
|
|
3152
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
3153
|
+
guardrailVersion: '1',
|
|
3154
|
+
guardLatestUserMessage: true,
|
|
3155
|
+
},
|
|
1417
3156
|
});
|
|
3157
|
+
const messages = [
|
|
3158
|
+
new Message({
|
|
3159
|
+
role: 'user',
|
|
3160
|
+
content: [
|
|
3161
|
+
new ImageBlock({
|
|
3162
|
+
format: 'gif',
|
|
3163
|
+
source: { bytes: imageBytes },
|
|
3164
|
+
}),
|
|
3165
|
+
],
|
|
3166
|
+
}),
|
|
3167
|
+
];
|
|
3168
|
+
collectIterator(provider.stream(messages));
|
|
3169
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith("Image format 'gif' not supported by Bedrock guardrails, skipping guardContent wrap");
|
|
3170
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
3171
|
+
messages: [
|
|
3172
|
+
{
|
|
3173
|
+
role: 'user',
|
|
3174
|
+
content: [
|
|
3175
|
+
{
|
|
3176
|
+
image: {
|
|
3177
|
+
format: 'gif',
|
|
3178
|
+
source: { bytes: imageBytes },
|
|
3179
|
+
},
|
|
3180
|
+
},
|
|
3181
|
+
],
|
|
3182
|
+
},
|
|
3183
|
+
],
|
|
3184
|
+
}));
|
|
3185
|
+
consoleWarnSpy.mockRestore();
|
|
1418
3186
|
});
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
3187
|
+
it('skips wrapping images with unsupported formats (webp)', async () => {
|
|
3188
|
+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
3189
|
+
const imageBytes = new Uint8Array([1, 2, 3, 4]);
|
|
1422
3190
|
const provider = new BedrockModel({
|
|
1423
|
-
|
|
3191
|
+
guardrailConfig: {
|
|
3192
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
3193
|
+
guardrailVersion: '1',
|
|
3194
|
+
guardLatestUserMessage: true,
|
|
3195
|
+
},
|
|
1424
3196
|
});
|
|
1425
3197
|
const messages = [
|
|
1426
3198
|
new Message({
|
|
1427
3199
|
role: 'user',
|
|
1428
3200
|
content: [
|
|
1429
|
-
new
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
content: [new TextBlock('Result')],
|
|
3201
|
+
new ImageBlock({
|
|
3202
|
+
format: 'webp',
|
|
3203
|
+
source: { bytes: imageBytes },
|
|
1433
3204
|
}),
|
|
1434
3205
|
],
|
|
1435
3206
|
}),
|
|
1436
3207
|
];
|
|
1437
3208
|
collectIterator(provider.stream(messages));
|
|
1438
|
-
expect(
|
|
3209
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith("Image format 'webp' not supported by Bedrock guardrails, skipping guardContent wrap");
|
|
3210
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
1439
3211
|
messages: [
|
|
1440
3212
|
{
|
|
3213
|
+
role: 'user',
|
|
1441
3214
|
content: [
|
|
1442
3215
|
{
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
3216
|
+
image: {
|
|
3217
|
+
format: 'webp',
|
|
3218
|
+
source: { bytes: imageBytes },
|
|
1446
3219
|
},
|
|
1447
3220
|
},
|
|
1448
3221
|
],
|
|
3222
|
+
},
|
|
3223
|
+
],
|
|
3224
|
+
}));
|
|
3225
|
+
consoleWarnSpy.mockRestore();
|
|
3226
|
+
});
|
|
3227
|
+
it('skips wrapping images with S3 source', async () => {
|
|
3228
|
+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
3229
|
+
const provider = new BedrockModel({
|
|
3230
|
+
guardrailConfig: {
|
|
3231
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
3232
|
+
guardrailVersion: '1',
|
|
3233
|
+
guardLatestUserMessage: true,
|
|
3234
|
+
},
|
|
3235
|
+
});
|
|
3236
|
+
const messages = [
|
|
3237
|
+
new Message({
|
|
3238
|
+
role: 'user',
|
|
3239
|
+
content: [
|
|
3240
|
+
new ImageBlock({
|
|
3241
|
+
format: 'png',
|
|
3242
|
+
source: {
|
|
3243
|
+
location: {
|
|
3244
|
+
type: 's3',
|
|
3245
|
+
uri: 's3://bucket/image.png',
|
|
3246
|
+
},
|
|
3247
|
+
},
|
|
3248
|
+
}),
|
|
3249
|
+
],
|
|
3250
|
+
}),
|
|
3251
|
+
];
|
|
3252
|
+
collectIterator(provider.stream(messages));
|
|
3253
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith('Image source must be bytes for Bedrock guardrails, skipping guardContent wrap');
|
|
3254
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
3255
|
+
messages: [
|
|
3256
|
+
{
|
|
1449
3257
|
role: 'user',
|
|
3258
|
+
content: [
|
|
3259
|
+
{
|
|
3260
|
+
image: {
|
|
3261
|
+
format: 'png',
|
|
3262
|
+
source: {
|
|
3263
|
+
s3Location: {
|
|
3264
|
+
uri: 's3://bucket/image.png',
|
|
3265
|
+
},
|
|
3266
|
+
},
|
|
3267
|
+
},
|
|
3268
|
+
},
|
|
3269
|
+
],
|
|
1450
3270
|
},
|
|
1451
3271
|
],
|
|
1452
|
-
|
|
3272
|
+
}));
|
|
3273
|
+
consoleWarnSpy.mockRestore();
|
|
3274
|
+
});
|
|
3275
|
+
it('skips wrapping images with URL source', async () => {
|
|
3276
|
+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
3277
|
+
const provider = new BedrockModel({
|
|
3278
|
+
guardrailConfig: {
|
|
3279
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
3280
|
+
guardrailVersion: '1',
|
|
3281
|
+
guardLatestUserMessage: true,
|
|
3282
|
+
},
|
|
1453
3283
|
});
|
|
3284
|
+
const messages = [
|
|
3285
|
+
new Message({
|
|
3286
|
+
role: 'user',
|
|
3287
|
+
content: [
|
|
3288
|
+
new ImageBlock({
|
|
3289
|
+
format: 'jpeg',
|
|
3290
|
+
source: { url: 'https://example.com/image.jpg' },
|
|
3291
|
+
}),
|
|
3292
|
+
],
|
|
3293
|
+
}),
|
|
3294
|
+
];
|
|
3295
|
+
collectIterator(provider.stream(messages));
|
|
3296
|
+
// URL sources return undefined in _formatMediaSource, resulting in source: undefined
|
|
3297
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith('Ignoring imageSourceUrl content block as its not supported by bedrock');
|
|
3298
|
+
// The image block still appears but with undefined source (Bedrock will reject this)
|
|
3299
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
3300
|
+
messages: [
|
|
3301
|
+
{
|
|
3302
|
+
role: 'user',
|
|
3303
|
+
content: [
|
|
3304
|
+
{
|
|
3305
|
+
image: {
|
|
3306
|
+
format: 'jpeg',
|
|
3307
|
+
source: undefined,
|
|
3308
|
+
},
|
|
3309
|
+
},
|
|
3310
|
+
],
|
|
3311
|
+
},
|
|
3312
|
+
],
|
|
3313
|
+
}));
|
|
3314
|
+
consoleWarnSpy.mockRestore();
|
|
1454
3315
|
});
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
3316
|
+
it('wraps supported image formats (png and jpeg) with bytes source', async () => {
|
|
3317
|
+
const imageBytes = new Uint8Array([1, 2, 3, 4]);
|
|
3318
|
+
const provider = new BedrockModel({
|
|
3319
|
+
guardrailConfig: {
|
|
3320
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
3321
|
+
guardrailVersion: '1',
|
|
3322
|
+
guardLatestUserMessage: true,
|
|
3323
|
+
},
|
|
3324
|
+
});
|
|
3325
|
+
const messages = [
|
|
3326
|
+
new Message({
|
|
3327
|
+
role: 'user',
|
|
3328
|
+
content: [
|
|
3329
|
+
new ImageBlock({
|
|
3330
|
+
format: 'png',
|
|
3331
|
+
source: { bytes: imageBytes },
|
|
3332
|
+
}),
|
|
3333
|
+
new ImageBlock({
|
|
3334
|
+
format: 'jpeg',
|
|
3335
|
+
source: { bytes: imageBytes },
|
|
3336
|
+
}),
|
|
3337
|
+
],
|
|
3338
|
+
}),
|
|
3339
|
+
];
|
|
3340
|
+
collectIterator(provider.stream(messages));
|
|
3341
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
3342
|
+
messages: [
|
|
3343
|
+
{
|
|
3344
|
+
role: 'user',
|
|
3345
|
+
content: [
|
|
3346
|
+
{
|
|
3347
|
+
guardContent: {
|
|
3348
|
+
image: {
|
|
3349
|
+
format: 'png',
|
|
3350
|
+
source: { bytes: imageBytes },
|
|
3351
|
+
},
|
|
3352
|
+
},
|
|
3353
|
+
},
|
|
3354
|
+
{
|
|
3355
|
+
guardContent: {
|
|
3356
|
+
image: {
|
|
3357
|
+
format: 'jpeg',
|
|
3358
|
+
source: { bytes: imageBytes },
|
|
3359
|
+
},
|
|
3360
|
+
},
|
|
3361
|
+
},
|
|
3362
|
+
],
|
|
3363
|
+
},
|
|
3364
|
+
],
|
|
3365
|
+
}));
|
|
1476
3366
|
});
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
3367
|
+
it('does not wrap reasoning or cachePoint blocks', async () => {
|
|
3368
|
+
const provider = new BedrockModel({
|
|
3369
|
+
guardrailConfig: {
|
|
3370
|
+
guardrailIdentifier: 'my-guardrail-id',
|
|
3371
|
+
guardrailVersion: '1',
|
|
3372
|
+
guardLatestUserMessage: true,
|
|
3373
|
+
},
|
|
3374
|
+
});
|
|
3375
|
+
const messages = [
|
|
3376
|
+
new Message({
|
|
3377
|
+
role: 'user',
|
|
3378
|
+
content: [
|
|
3379
|
+
new TextBlock('User message'),
|
|
3380
|
+
new ReasoningBlock({ text: 'thinking...', signature: 'sig' }),
|
|
3381
|
+
new CachePointBlock({ cacheType: 'default' }),
|
|
3382
|
+
],
|
|
3383
|
+
}),
|
|
3384
|
+
];
|
|
3385
|
+
collectIterator(provider.stream(messages));
|
|
3386
|
+
expect(mockConverseStreamCommand).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
3387
|
+
messages: [
|
|
3388
|
+
{
|
|
3389
|
+
role: 'user',
|
|
3390
|
+
content: [
|
|
3391
|
+
{
|
|
3392
|
+
guardContent: {
|
|
3393
|
+
text: {
|
|
3394
|
+
text: 'User message',
|
|
3395
|
+
},
|
|
3396
|
+
},
|
|
3397
|
+
},
|
|
3398
|
+
{
|
|
3399
|
+
reasoningContent: {
|
|
3400
|
+
reasoningText: {
|
|
3401
|
+
text: 'thinking...',
|
|
3402
|
+
signature: 'sig',
|
|
3403
|
+
},
|
|
3404
|
+
},
|
|
3405
|
+
},
|
|
3406
|
+
{ cachePoint: { type: 'default' } },
|
|
3407
|
+
],
|
|
3408
|
+
},
|
|
3409
|
+
],
|
|
3410
|
+
}));
|
|
1489
3411
|
});
|
|
1490
|
-
const provider = new BedrockModel();
|
|
1491
|
-
// Should rethrow the error
|
|
1492
|
-
await expect(provider['_client'].config.region()).rejects.toThrow('Network error');
|
|
1493
3412
|
});
|
|
1494
3413
|
});
|
|
1495
3414
|
});
|