@aws-blocks/bb-agent 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -2
- package/dist/index.cdk.d.ts +2 -2
- package/dist/index.cdk.d.ts.map +1 -1
- package/dist/index.cdk.js +4 -3
- package/dist/index.hooks.d.ts +2 -2
- package/dist/index.hooks.d.ts.map +1 -1
- package/dist/index.test.js +22 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +15 -7
- package/src/index.cdk.ts +4 -3
- package/src/index.hooks.ts +2 -2
- package/src/index.test.ts +24 -0
- package/src/version.ts +1 -1
package/README.md
CHANGED
|
@@ -763,7 +763,8 @@ export const api = new ApiNamespace(scope, 'api', (context) => ({
|
|
|
763
763
|
return { conversationId: await agent.createConversationId(userId) };
|
|
764
764
|
},
|
|
765
765
|
async sendMessage(conversationId: string, message: string, channelId: string, userId: string) {
|
|
766
|
-
await agent.stream(message, { conversationId, channelId, userId });
|
|
766
|
+
const result = await agent.stream(message, { conversationId, channelId, userId });
|
|
767
|
+
return { channelId: result.channelId };
|
|
767
768
|
},
|
|
768
769
|
async getConversation(conversationId: string) {
|
|
769
770
|
const messages = await agent.getConversation(conversationId);
|
|
@@ -803,7 +804,87 @@ await chat.sendMessage('Hello!');
|
|
|
803
804
|
await chat.loadConversation('conv-123');
|
|
804
805
|
```
|
|
805
806
|
|
|
806
|
-
|
|
807
|
+
The example above is framework-agnostic on purpose — `useChat` has no React import and works with any UI layer. The two examples below show how to bridge it into a specific framework's reactivity.
|
|
808
|
+
|
|
809
|
+
### 2. React: hold the instance once, drive `useState` from the callbacks
|
|
810
|
+
|
|
811
|
+
`useChat` is a factory, not a React hook, so it must **not** run on every render — recreating it drops the WebSocket subscription and conversation state each time. Hold the single instance in a `useRef` (created lazily so it survives re-renders), and turn the `onMessagesChange` / `onLoadingChange` / `onInterrupt` callbacks into `setState` calls so React re-renders when the mutable instance changes. This example keeps the `api` wiring minimal — it omits the `userId` that the End-to-End example (#1) threads through `createConversation` / `sendMessage`; thread it the same way here when your API needs it (or resolve the user server-side).
|
|
812
|
+
|
|
813
|
+
```tsx
|
|
814
|
+
'use client'; // Next.js only — see the note below. Plain React (Vite/CRA) can omit this.
|
|
815
|
+
|
|
816
|
+
import { useRef, useState, useEffect } from 'react';
|
|
817
|
+
import { useChat, type ChatMessage } from '@aws-blocks/bb-agent/client';
|
|
818
|
+
import { api } from './api'; // your generated aws-blocks API client
|
|
819
|
+
|
|
820
|
+
export function Chat() {
|
|
821
|
+
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
|
822
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
823
|
+
const [input, setInput] = useState('');
|
|
824
|
+
|
|
825
|
+
// Create the instance exactly once. The ref survives every re-render,
|
|
826
|
+
// so the subscription and conversation state are never torn down.
|
|
827
|
+
// Type the ref as `| undefined` and initialize with `undefined` — @types/react 19
|
|
828
|
+
// tightened the useRef overloads, so a bare useRef<T>() no longer compiles.
|
|
829
|
+
const chatRef = useRef<ReturnType<typeof useChat> | undefined>(undefined);
|
|
830
|
+
if (!chatRef.current) {
|
|
831
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks -- useChat is a factory, not a hook; the use-prefix trips the linter's hook heuristic.
|
|
832
|
+
chatRef.current = useChat({
|
|
833
|
+
api: {
|
|
834
|
+
sendMessage: (convId, msg, chId) => api.sendMessage(convId, msg, chId),
|
|
835
|
+
createConversation: () => api.createConversation(),
|
|
836
|
+
getConversation: (id) => api.getConversation(id),
|
|
837
|
+
},
|
|
838
|
+
subscribe: async (channelId, handler) => {
|
|
839
|
+
const channel = await api.getChannel(channelId);
|
|
840
|
+
return channel.subscribe(handler);
|
|
841
|
+
},
|
|
842
|
+
// Bridge the mutable instance into React state — these fire on every change.
|
|
843
|
+
onMessagesChange: setMessages,
|
|
844
|
+
onLoadingChange: setIsLoading,
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
const chat = chatRef.current!; // guaranteed set by the block above
|
|
848
|
+
|
|
849
|
+
// Tear down the WebSocket subscription when the component unmounts.
|
|
850
|
+
useEffect(() => () => chat.destroy(), [chat]);
|
|
851
|
+
|
|
852
|
+
async function handleSend(e: React.FormEvent) {
|
|
853
|
+
e.preventDefault();
|
|
854
|
+
const text = input.trim();
|
|
855
|
+
if (!text || isLoading) return;
|
|
856
|
+
setInput('');
|
|
857
|
+
await chat.sendMessage(text);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
return (
|
|
861
|
+
<div>
|
|
862
|
+
<ul>
|
|
863
|
+
{messages.map((m) => (
|
|
864
|
+
<li key={m.id} data-role={m.role}>
|
|
865
|
+
<strong>{m.role}:</strong> {m.content}
|
|
866
|
+
</li>
|
|
867
|
+
))}
|
|
868
|
+
</ul>
|
|
869
|
+
<form onSubmit={handleSend}>
|
|
870
|
+
<input value={input} onChange={(e) => setInput(e.target.value)} disabled={isLoading} />
|
|
871
|
+
<button type="submit" disabled={isLoading}>Send</button>
|
|
872
|
+
</form>
|
|
873
|
+
</div>
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
Key points:
|
|
879
|
+
|
|
880
|
+
- **One instance, held in a ref.** `useRef` + the lazy `if (!chatRef.current)` guard is the React idiom for "construct once." Because the identifier is `use`-prefixed, `eslint-plugin-react-hooks` (bundled in the default Next.js and CRA configs) flags the guarded call as a conditional hook (`react-hooks/rules-of-hooks`). `useChat` is a factory, not a hook, so this is a false positive — the inline `eslint-disable-next-line` above the call silences it. What you must **not** do is call `useChat(...)` unguarded on every render: that recreates the instance each time and is the footgun the factory note warns about.
|
|
881
|
+
- **Callbacks are your reactivity bridge.** `useChat` mutates its own message list in place; `onMessagesChange` / `onLoadingChange` hand you the new value so you can `setState` and trigger a render. Passing `setMessages` / `setIsLoading` directly is enough.
|
|
882
|
+
- **Clean up on unmount** with `chat.destroy()` in a `useEffect` cleanup, so the Realtime subscription is closed.
|
|
883
|
+
- **Approvals:** wire `resume: (chId, responses, convId) => api.resume(chId, responses, convId)` into the `api` object above (mirroring your backend's resume method — `respondToInterrupt` throws if it is absent), add `onInterrupt: setInterrupts` (with `const [interrupts, setInterrupts] = useState<Array<{ id: string; name: string; reason?: unknown }>>([])` — a bare `useState([])` infers `never[]` and rejects the payload) to render an approval UI, then call `chat.respondToInterrupt([{ interruptId, approved: true }])`.
|
|
884
|
+
|
|
885
|
+
**Next.js:** this is the same component — just keep the `'use client'` directive at the top of the file. `useChat` opens a browser WebSocket and holds client state, so it must run in a Client Component, never a Server Component. No other changes are needed.
|
|
886
|
+
|
|
887
|
+
### 3. Support Agent with Tools
|
|
807
888
|
|
|
808
889
|
Agent with tools that can look up orders and search documentation. Uses tool context to scope queries to the authenticated user.
|
|
809
890
|
|
package/dist/index.cdk.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BuildingBlockScope } from '@aws-blocks/core/cdk';
|
|
2
2
|
import type { ScopeParent } from '@aws-blocks/core';
|
|
3
3
|
import type { AgentConfig } from './types.js';
|
|
4
4
|
export { AgentErrors } from './errors.js';
|
|
5
5
|
export { BedrockModels, OllamaModels } from './models.js';
|
|
6
|
-
export declare class Agent extends
|
|
6
|
+
export declare class Agent extends BuildingBlockScope {
|
|
7
7
|
/**
|
|
8
8
|
* CDK layer for the Agent BB.
|
|
9
9
|
*
|
package/dist/index.cdk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cdk.d.ts","sourceRoot":"","sources":["../src/index.cdk.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.cdk.d.ts","sourceRoot":"","sources":["../src/index.cdk.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAOpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE1D,qBAAa,KAAM,SAAQ,kBAAkB;IAC5C;;;;;;;;;;;OAWG;gBACS,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW;CAyChE"}
|
package/dist/index.cdk.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
import {
|
|
3
|
+
import { BuildingBlockScope } from '@aws-blocks/core/cdk';
|
|
4
4
|
import { DistributedTable } from '@aws-blocks/bb-distributed-table';
|
|
5
5
|
import { Realtime } from '@aws-blocks/bb-realtime';
|
|
6
6
|
import { FileBucket } from '@aws-blocks/bb-file-bucket';
|
|
7
|
+
import * as ec2 from 'aws-cdk-lib/aws-ec2';
|
|
7
8
|
import { AgentCoreRuntime } from './agentcore-runtime.cdk.js';
|
|
8
9
|
import { messageSchema, conversationSchema, agentStreamChunkSchema } from './schemas.js';
|
|
9
10
|
export { AgentErrors } from './errors.js';
|
|
10
11
|
export { BedrockModels, OllamaModels } from './models.js';
|
|
11
|
-
export class Agent extends
|
|
12
|
+
export class Agent extends BuildingBlockScope {
|
|
12
13
|
/**
|
|
13
14
|
* CDK layer for the Agent BB.
|
|
14
15
|
*
|
|
@@ -22,7 +23,7 @@ export class Agent extends Scope {
|
|
|
22
23
|
* handler no longer needs Bedrock access — the runtime's own role gets it (see AgentCoreRuntime).
|
|
23
24
|
*/
|
|
24
25
|
constructor(scope, id, config) {
|
|
25
|
-
super(id, { parent: scope });
|
|
26
|
+
super(id, { parent: scope, vpc: { interfaceEndpoints: [ec2.InterfaceVpcEndpointAwsService.BEDROCK_RUNTIME] } });
|
|
26
27
|
// Session-snapshot bucket. Provisioned here (and granted to the shared execution role that the
|
|
27
28
|
// AgentCore Runtime runs as); the deployed loop re-derives its name from this bucket's `fullId`
|
|
28
29
|
// in-process — the same `'sn'` id → same fullId → same physical bucket — so no name needs to be
|
package/dist/index.hooks.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export interface ChatMessage {
|
|
|
22
22
|
/** Options for creating a chat instance. */
|
|
23
23
|
export interface UseChatOptions {
|
|
24
24
|
api: {
|
|
25
|
-
sendMessage(conversationId: string, message: string, channelId: string): Promise<
|
|
25
|
+
sendMessage(conversationId: string, message: string, channelId: string): Promise<unknown>;
|
|
26
26
|
createConversation(): Promise<{
|
|
27
27
|
conversationId: string;
|
|
28
28
|
}>;
|
|
@@ -39,7 +39,7 @@ export interface UseChatOptions {
|
|
|
39
39
|
trust?: boolean;
|
|
40
40
|
toolName?: string;
|
|
41
41
|
input?: any;
|
|
42
|
-
}>, conversationId?: string): Promise<
|
|
42
|
+
}>, conversationId?: string): Promise<unknown>;
|
|
43
43
|
getPendingInterrupts?(conversationId: string): Promise<{
|
|
44
44
|
interrupts: Array<{
|
|
45
45
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.hooks.d.ts","sourceRoot":"","sources":["../src/index.hooks.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,wDAAwD;AACxD,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED,4CAA4C;AAC5C,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE;QACJ,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"index.hooks.d.ts","sourceRoot":"","sources":["../src/index.hooks.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,wDAAwD;AACxD,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED,4CAA4C;AAC5C,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE;QACJ,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1F,kBAAkB,IAAI,OAAO,CAAC;YAAE,cAAc,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC1D,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;YAAE,QAAQ,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,OAAO,EAAE,MAAM,CAAC;gBAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;aAAE,EAAE,CAAA;SAAE,CAAC,CAAC;QACxH,MAAM,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC;YAAE,WAAW,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,OAAO,CAAC;YAAC,KAAK,CAAC,EAAE,OAAO,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,GAAG,CAAA;SAAE,CAAC,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACrL,oBAAoB,CAAC,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC;YAAE,UAAU,EAAE,KAAK,CAAC;gBAAE,EAAE,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAC;gBAAC,MAAM,CAAC,EAAE,GAAG,CAAA;aAAE,CAAC,CAAA;SAAE,CAAC,CAAC;KAC1H,CAAC;IACF;;;;OAIG;IACH,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,KAAK,OAAO,CAAC;QAAE,WAAW,IAAI,IAAI,CAAC;QAAC,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC,CAAC;IAC3I,gDAAgD;IAChD,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC;IACrD,6CAA6C;IAC7C,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C,sCAAsC;IACtC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC5C,iDAAiD;IACjD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,oEAAoE;IACpE,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC,KAAK,IAAI,CAAC;CACtF;AAED,6BAA6B;AAC7B,MAAM,WAAW,YAAY;IAC5B,uEAAuE;IACvE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,kEAAkE;IAClE,kBAAkB,CAAC,SAAS,EAAE,KAAK,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjJ,wBAAwB;IACxB,WAAW,IAAI,WAAW,EAAE,CAAC;IAC7B,iDAAiD;IACjD,SAAS,IAAI,OAAO,CAAC;IACrB,0DAA0D;IAC1D,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAAC;IACnC,qEAAqE;IACrE,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,wCAAwC;IACxC,OAAO,IAAI,IAAI,CAAC;CAChB;AAOD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,YAAY,CA0J7D"}
|
package/dist/index.test.js
CHANGED
|
@@ -1029,6 +1029,28 @@ describe('model-factory', () => {
|
|
|
1029
1029
|
// ── useChat ──────────────────────────────────────────────────────────────────
|
|
1030
1030
|
import { useChat } from './index.hooks.js';
|
|
1031
1031
|
describe('useChat', () => {
|
|
1032
|
+
// Type-only regression guard for the api return-type contract (PR that widened
|
|
1033
|
+
// sendMessage/resume from Promise<void> to Promise<unknown>). This is compiled by
|
|
1034
|
+
// `tsc --build` before the runtime tests execute, so narrowing either member back
|
|
1035
|
+
// to Promise<void> fails CI here — the durable proof the manual PR check could not
|
|
1036
|
+
// commit. `unknown` must accept BOTH a natural object-returning backend and a
|
|
1037
|
+
// void-returning one; both assignments below must type-check.
|
|
1038
|
+
test('api sendMessage/resume accept object- and void-returning backends (type-only)', () => {
|
|
1039
|
+
const objectBackend = {
|
|
1040
|
+
sendMessage: async () => ({ channelId: 'c' }),
|
|
1041
|
+
createConversation: async () => ({ conversationId: 'c' }),
|
|
1042
|
+
getConversation: async () => ({ messages: [] }),
|
|
1043
|
+
resume: async () => ({ ok: true }),
|
|
1044
|
+
};
|
|
1045
|
+
const voidBackend = {
|
|
1046
|
+
sendMessage: async () => { },
|
|
1047
|
+
createConversation: async () => ({ conversationId: 'c' }),
|
|
1048
|
+
getConversation: async () => ({ messages: [] }),
|
|
1049
|
+
resume: async () => { },
|
|
1050
|
+
};
|
|
1051
|
+
assert.ok(objectBackend.sendMessage);
|
|
1052
|
+
assert.ok(voidBackend.sendMessage);
|
|
1053
|
+
});
|
|
1032
1054
|
test('onError is called when error chunk arrives', async () => {
|
|
1033
1055
|
let chunkHandler;
|
|
1034
1056
|
let errorReceived;
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws-blocks/bb-agent",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"aws-blocks",
|
|
6
|
+
"ai",
|
|
7
|
+
"agent",
|
|
8
|
+
"llm",
|
|
9
|
+
"bedrock",
|
|
10
|
+
"chatbot"
|
|
11
|
+
],
|
|
4
12
|
"repository": {
|
|
5
13
|
"type": "git",
|
|
6
14
|
"url": "git+https://github.com/aws-devtools-labs/aws-blocks.git",
|
|
@@ -47,11 +55,11 @@
|
|
|
47
55
|
"test": "node --test dist/index.test.js && node --conditions=cdk --test dist/index.cdk.test.js && node --test dist/agentcore-bundle.test.js"
|
|
48
56
|
},
|
|
49
57
|
"dependencies": {
|
|
50
|
-
"@aws-blocks/bb-distributed-table": "^0.
|
|
51
|
-
"@aws-blocks/bb-file-bucket": "^0.2.
|
|
52
|
-
"@aws-blocks/bb-logger": "^0.
|
|
53
|
-
"@aws-blocks/bb-realtime": "^0.2.
|
|
54
|
-
"@aws-blocks/core": "^0.
|
|
58
|
+
"@aws-blocks/bb-distributed-table": "^0.2.0",
|
|
59
|
+
"@aws-blocks/bb-file-bucket": "^0.2.1",
|
|
60
|
+
"@aws-blocks/bb-logger": "^0.2.0",
|
|
61
|
+
"@aws-blocks/bb-realtime": "^0.2.1",
|
|
62
|
+
"@aws-blocks/core": "^0.5.0",
|
|
55
63
|
"@aws-sdk/client-bedrock": "^3.700.0",
|
|
56
64
|
"@aws-sdk/client-bedrock-agentcore": "^3.700.0",
|
|
57
65
|
"@opentelemetry/api": "^1.9.0",
|
|
@@ -63,7 +71,7 @@
|
|
|
63
71
|
"zod": "^4.1.12"
|
|
64
72
|
},
|
|
65
73
|
"devDependencies": {
|
|
66
|
-
"@aws-blocks/bb-lambda-compute": "^0.
|
|
74
|
+
"@aws-blocks/bb-lambda-compute": "^0.5.0",
|
|
67
75
|
"@aws-sdk/client-bedrock-runtime": "^3.700.0",
|
|
68
76
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
69
77
|
"@types/node": "^20.0.0",
|
package/src/index.cdk.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { BuildingBlockScope } from '@aws-blocks/core/cdk';
|
|
5
5
|
import type { ScopeParent } from '@aws-blocks/core';
|
|
6
6
|
import { DistributedTable } from '@aws-blocks/bb-distributed-table';
|
|
7
7
|
import { Realtime } from '@aws-blocks/bb-realtime';
|
|
8
8
|
import { FileBucket } from '@aws-blocks/bb-file-bucket';
|
|
9
|
+
import * as ec2 from 'aws-cdk-lib/aws-ec2';
|
|
9
10
|
import { AgentCoreRuntime } from './agentcore-runtime.cdk.js';
|
|
10
11
|
import { messageSchema, conversationSchema, agentStreamChunkSchema } from './schemas.js';
|
|
11
12
|
import type { AgentConfig } from './types.js';
|
|
@@ -13,7 +14,7 @@ import type { AgentConfig } from './types.js';
|
|
|
13
14
|
export { AgentErrors } from './errors.js';
|
|
14
15
|
export { BedrockModels, OllamaModels } from './models.js';
|
|
15
16
|
|
|
16
|
-
export class Agent extends
|
|
17
|
+
export class Agent extends BuildingBlockScope {
|
|
17
18
|
/**
|
|
18
19
|
* CDK layer for the Agent BB.
|
|
19
20
|
*
|
|
@@ -27,7 +28,7 @@ export class Agent extends Scope {
|
|
|
27
28
|
* handler no longer needs Bedrock access — the runtime's own role gets it (see AgentCoreRuntime).
|
|
28
29
|
*/
|
|
29
30
|
constructor(scope: ScopeParent, id: string, config?: AgentConfig) {
|
|
30
|
-
super(id, { parent: scope });
|
|
31
|
+
super(id, { parent: scope, vpc: { interfaceEndpoints: [ec2.InterfaceVpcEndpointAwsService.BEDROCK_RUNTIME] } });
|
|
31
32
|
|
|
32
33
|
// Session-snapshot bucket. Provisioned here (and granted to the shared execution role that the
|
|
33
34
|
// AgentCore Runtime runs as); the deployed loop re-derives its name from this bucket's `fullId`
|
package/src/index.hooks.ts
CHANGED
|
@@ -29,10 +29,10 @@ export interface ChatMessage {
|
|
|
29
29
|
/** Options for creating a chat instance. */
|
|
30
30
|
export interface UseChatOptions {
|
|
31
31
|
api: {
|
|
32
|
-
sendMessage(conversationId: string, message: string, channelId: string): Promise<
|
|
32
|
+
sendMessage(conversationId: string, message: string, channelId: string): Promise<unknown>;
|
|
33
33
|
createConversation(): Promise<{ conversationId: string }>;
|
|
34
34
|
getConversation(id: string): Promise<{ messages: { role: string; content: string; metadata?: Record<string, any> }[] }>;
|
|
35
|
-
resume?(channelId: string, responses: Array<{ interruptId: string; approved: boolean; trust?: boolean; toolName?: string; input?: any }>, conversationId?: string): Promise<
|
|
35
|
+
resume?(channelId: string, responses: Array<{ interruptId: string; approved: boolean; trust?: boolean; toolName?: string; input?: any }>, conversationId?: string): Promise<unknown>;
|
|
36
36
|
getPendingInterrupts?(conversationId: string): Promise<{ interrupts: Array<{ id: string; name: string; reason?: any }> }>;
|
|
37
37
|
};
|
|
38
38
|
/**
|
package/src/index.test.ts
CHANGED
|
@@ -1185,8 +1185,32 @@ describe('model-factory', () => {
|
|
|
1185
1185
|
// ── useChat ──────────────────────────────────────────────────────────────────
|
|
1186
1186
|
|
|
1187
1187
|
import { useChat } from './index.hooks.js';
|
|
1188
|
+
import type { UseChatOptions } from './index.hooks.js';
|
|
1188
1189
|
|
|
1189
1190
|
describe('useChat', () => {
|
|
1191
|
+
// Type-only regression guard for the api return-type contract (PR that widened
|
|
1192
|
+
// sendMessage/resume from Promise<void> to Promise<unknown>). This is compiled by
|
|
1193
|
+
// `tsc --build` before the runtime tests execute, so narrowing either member back
|
|
1194
|
+
// to Promise<void> fails CI here — the durable proof the manual PR check could not
|
|
1195
|
+
// commit. `unknown` must accept BOTH a natural object-returning backend and a
|
|
1196
|
+
// void-returning one; both assignments below must type-check.
|
|
1197
|
+
test('api sendMessage/resume accept object- and void-returning backends (type-only)', () => {
|
|
1198
|
+
const objectBackend: UseChatOptions['api'] = {
|
|
1199
|
+
sendMessage: async () => ({ channelId: 'c' }),
|
|
1200
|
+
createConversation: async () => ({ conversationId: 'c' }),
|
|
1201
|
+
getConversation: async () => ({ messages: [] }),
|
|
1202
|
+
resume: async () => ({ ok: true }),
|
|
1203
|
+
};
|
|
1204
|
+
const voidBackend: UseChatOptions['api'] = {
|
|
1205
|
+
sendMessage: async () => {},
|
|
1206
|
+
createConversation: async () => ({ conversationId: 'c' }),
|
|
1207
|
+
getConversation: async () => ({ messages: [] }),
|
|
1208
|
+
resume: async () => {},
|
|
1209
|
+
};
|
|
1210
|
+
assert.ok(objectBackend.sendMessage);
|
|
1211
|
+
assert.ok(voidBackend.sendMessage);
|
|
1212
|
+
});
|
|
1213
|
+
|
|
1190
1214
|
test('onError is called when error chunk arrives', async () => {
|
|
1191
1215
|
let chunkHandler: (chunk: any) => void;
|
|
1192
1216
|
let errorReceived: string | undefined;
|
package/src/version.ts
CHANGED