@dudousxd/nestjs-agent-react 0.21.0 → 0.22.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 +57 -55
- package/dist/{client-ePlgRwP-.d.cts → client-CYlhK7XD.d.cts} +2 -7
- package/dist/{client-ePlgRwP-.d.ts → client-CYlhK7XD.d.ts} +2 -7
- package/dist/index.cjs +405 -152
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +172 -111
- package/dist/index.d.ts +172 -111
- package/dist/index.js +405 -152
- package/dist/index.js.map +1 -1
- package/dist/media.cjs.map +1 -1
- package/dist/media.d.cts +1 -1
- package/dist/media.d.ts +1 -1
- package/dist/media.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,18 +22,30 @@ function Chat() {
|
|
|
22
22
|
const chat = useAgentChat(); // same-origin, `/agent` — no provider needed
|
|
23
23
|
return (
|
|
24
24
|
<>
|
|
25
|
-
<MessageList
|
|
26
|
-
messages={chat.messages}
|
|
27
|
-
status={chat.status}
|
|
28
|
-
regeneratable
|
|
29
|
-
onRegenerate={() => chat.regenerate()}
|
|
30
|
-
/>
|
|
25
|
+
<MessageList messages={chat.messages} status={chat.status} />
|
|
31
26
|
<ChatInput onSubmit={(text) => chat.sendMessage({ text })} />
|
|
32
27
|
</>
|
|
33
28
|
);
|
|
34
29
|
}
|
|
35
30
|
```
|
|
36
31
|
|
|
32
|
+
`useAgentChat` does the wiring itself: give it a `threadId` and it loads that thread's history and
|
|
33
|
+
re-attaches to a turn still streaming on it (`history: false` / `resume: false` opt out); it gates
|
|
34
|
+
sends on the reported quota; approvals and question sets in the transcript are actionable with no
|
|
35
|
+
handlers passed. On top of the AI SDK chat it returns:
|
|
36
|
+
|
|
37
|
+
| On `chat` | What it is |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `transcript` | `useChatTranscript` already bound to the chat — approve/reject/answer/skip, stop, fork, regenerate, the tool catalog, and timestamps/usage read from message metadata. Override any of it with `useAgentChat({ transcript: { … } })`. |
|
|
40
|
+
| `composer` | `{ text, setText, files, canSend, blockedBy, submit() }` — `files` is `useAttachments` on the chat's backend; `submit()` sends the draft with the ready files attached and clears both; `blockedBy` is `'empty' \| 'busy' \| 'uploading' \| 'quota'`. |
|
|
41
|
+
| `models` | `{ list, providers, selected, select(id), pinToThread(id) }` — loaded the first time `list` is read. |
|
|
42
|
+
| `quota` / `blocked` | `useQuota`'s state, and the window blocking sends (the `blocked` option overrides it). |
|
|
43
|
+
| `approve` / `reject` / `answer` / `skip` | `({ toolCallId, … })` — the same object shape the transcript's handlers take. |
|
|
44
|
+
| `fork` / `truncateFrom` / `promote` | `({ messageId, threadId? })` / `({ threadId? })`, defaulting to this chat's thread. |
|
|
45
|
+
| `cancel`, `regenerate`, `getThreadId`, `backend`, `runId`, `activeRunId`, `isLoadingHistory`, `connection`, `background` | — |
|
|
46
|
+
|
|
47
|
+
Threads (list, rename, delete) are `useThreads()`; the granular hooks stay the escape hatch.
|
|
48
|
+
|
|
37
49
|
### Configure the connection once: `<AgentProvider>`
|
|
38
50
|
|
|
39
51
|
Every hook (`useAgentChat`, `useThreads`, `useModels`, `useAgents`, `useQuota`, `useToolCatalog`,
|
|
@@ -81,14 +93,14 @@ import { useAgentChat, useChatTranscript } from '@dudousxd/nestjs-agent-react';
|
|
|
81
93
|
|
|
82
94
|
function Chat() {
|
|
83
95
|
const chat = useAgentChat();
|
|
96
|
+
// `chat.transcript` is this, pre-wired; `useChatTranscript` directly for any AI SDK chat.
|
|
84
97
|
const transcript = useChatTranscript({
|
|
85
98
|
messages: chat.messages,
|
|
86
99
|
status: chat.status,
|
|
87
100
|
editable: true,
|
|
88
|
-
onEditSubmit: (
|
|
89
|
-
onFork: (
|
|
101
|
+
onEditSubmit: ({ text }) => chat.sendMessage({ text }),
|
|
102
|
+
onFork: ({ messageId }) => chat.fork({ messageId }),
|
|
90
103
|
onStop: () => chat.cancel(),
|
|
91
|
-
getUsage: (message) => readUsage(message),
|
|
92
104
|
});
|
|
93
105
|
|
|
94
106
|
return (
|
|
@@ -446,8 +458,7 @@ function CustomChat({ threadId }: { threadId?: string }) {
|
|
|
446
458
|
// `exactOptionalPropertyTypes`, so an explicit `threadId: undefined` doesn't type-check.
|
|
447
459
|
...(threadId !== undefined ? { threadId } : {}),
|
|
448
460
|
agent: 'support',
|
|
449
|
-
//
|
|
450
|
-
resume: true,
|
|
461
|
+
// A `threadId` loads its history and re-attaches to a turn still streaming — by default.
|
|
451
462
|
onThreadCreated: (newThreadId) => router.replace(`/chat/${newThreadId}`),
|
|
452
463
|
// Fires once per run when the SERVER is done writing (title + terminal state persisted) —
|
|
453
464
|
// the right signal to refetch a thread list/sidebar; `onFinish` only means "a turn rendered".
|
|
@@ -492,10 +503,7 @@ disables itself while busy never noticed; a double-submitted one, and StrictMode
|
|
|
492
503
|
effect, did.
|
|
493
504
|
|
|
494
505
|
`chat` is the AI SDK v7 `useChat` return value (`messages`, `status`, `sendMessage`, `stop`, …) spread
|
|
495
|
-
together with the extras
|
|
496
|
-
under its old name `client`), thread list/CRUD
|
|
497
|
-
(`threads`, `loadThreads`, `loadThread`, `deleteThread`, `forkThread`, `renameThread`, `promoteThread`,
|
|
498
|
-
`truncateFromMessage`), `quota`/`loadQuota`, `cancel`, HITL `approve`/`reject` and `answer`/`skip`, and `regenerate`.
|
|
506
|
+
together with the extras in the table above.
|
|
499
507
|
`MyToolCard`'s `part` prop above types as the exported `AnyToolUIPart` (`ToolUIPart | DynamicToolUIPart`
|
|
500
508
|
— `MessageItem` uses the same union for its own `renderToolPart` callback).
|
|
501
509
|
|
|
@@ -593,14 +601,10 @@ its run persisted (the live message carries `metadata.runId`).
|
|
|
593
601
|
### Picking a model and an agent
|
|
594
602
|
|
|
595
603
|
```tsx
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
const [model, setModel] = useState<string | undefined>();
|
|
599
|
-
const chat = useAgentChat({ model }); // sent as the body's `model` on every turn
|
|
600
|
-
const { providers, find, defaultModel } = useModels({ agent: 'support' });
|
|
601
|
-
const { agents } = useAgents();
|
|
604
|
+
const chat = useAgentChat();
|
|
605
|
+
const { providers, selected, select } = chat.models; // loaded on first read
|
|
602
606
|
|
|
603
|
-
<select value={
|
|
607
|
+
<select value={selected ?? ''} onChange={(e) => select(e.target.value)}>
|
|
604
608
|
{providers.map((p) => (
|
|
605
609
|
<optgroup key={p.id} label={p.label}>
|
|
606
610
|
{p.models.map((m) => (
|
|
@@ -613,10 +617,14 @@ const { agents } = useAgents();
|
|
|
613
617
|
</select>
|
|
614
618
|
```
|
|
615
619
|
|
|
616
|
-
`
|
|
617
|
-
availability; `
|
|
618
|
-
|
|
619
|
-
|
|
620
|
+
`chat.models` reads `GET <base>/models?agent=` (models grouped by provider, with badges and
|
|
621
|
+
availability; `list` is the same flattened) the first time `list`/`providers` is read. `select(id)`
|
|
622
|
+
runs the following turns on it (sent as the body's `model`); `selected` is the pick, else the
|
|
623
|
+
thread's pin, else the server default. `pinToThread(id)` pins it on the thread (`null` unpins) so
|
|
624
|
+
it survives reloads — on a chat with no thread yet, the pin lands when the first send creates one.
|
|
625
|
+
`useAgentChat({ model })` controls the model yourself; a single send can override it with
|
|
626
|
+
`sendMessage(msg, { body: { model } })`. `useModels()` / `useAgents()` are the standalone hooks
|
|
627
|
+
(an agent picker: `useAgents().agents`, sent as `useAgentChat({ agent })`). The server refuses a
|
|
620
628
|
model its catalog does not offer as available.
|
|
621
629
|
|
|
622
630
|
### Quota
|
|
@@ -624,17 +632,19 @@ model its catalog does not offer as available.
|
|
|
624
632
|
```tsx
|
|
625
633
|
import { QuotaBlockedError, useQuota } from '@dudousxd/nestjs-agent-react';
|
|
626
634
|
|
|
627
|
-
const
|
|
628
|
-
const
|
|
635
|
+
const chat = useAgentChat(); // reads GET <base>/quota and gates sends on it
|
|
636
|
+
const { quota } = chat;
|
|
629
637
|
|
|
630
638
|
<meter value={quota.month?.usedUsd} max={quota.month?.limitUsd} />
|
|
631
|
-
{
|
|
639
|
+
{chat.blocked ? <p>{chat.blocked.reason}</p> : null}
|
|
632
640
|
```
|
|
633
641
|
|
|
634
|
-
`useQuota` returns every window (`day`, `month`, …) with its
|
|
635
|
-
one is exhausted; it re-reads after every run a chat on the
|
|
636
|
-
|
|
637
|
-
instead of starting a turn the server
|
|
642
|
+
`chat.quota` (and the standalone `useQuota()`) returns every window (`day`, `month`, …) with its
|
|
643
|
+
usage and ceilings, and `blocked` when one is exhausted; it re-reads after every run a chat on the
|
|
644
|
+
same backend settles. While a window is exhausted, `sendMessage`/`regenerate` reject with
|
|
645
|
+
`QuotaBlockedError` (and `chat.composer.blockedBy` is `'quota'`) instead of starting a turn the server
|
|
646
|
+
would refuse with `429`. `useAgentChat({ blocked })` overrides the gate (`null` never blocks);
|
|
647
|
+
`quota: false` skips the request.
|
|
638
648
|
|
|
639
649
|
### The transport, standalone
|
|
640
650
|
|
|
@@ -670,16 +680,15 @@ contract — for a backend that serves these routes without this library's loop
|
|
|
670
680
|
|
|
671
681
|
### Loading persisted history
|
|
672
682
|
|
|
673
|
-
|
|
674
|
-
|
|
683
|
+
`useAgentChat({ threadId })` loads it for you. To seed a chat with history you already have (SSR,
|
|
684
|
+
a cache), convert the thread's `StoredMessage[]` and pass it as `initialMessages` — the hook then
|
|
685
|
+
skips its own read:
|
|
675
686
|
|
|
676
687
|
```ts
|
|
677
688
|
import { storedThreadToUiMessages } from '@dudousxd/nestjs-agent-react';
|
|
678
689
|
|
|
679
|
-
const detail = await chat.backend.getThread(threadId);
|
|
680
690
|
const initialMessages = storedThreadToUiMessages(detail.messages);
|
|
681
|
-
|
|
682
|
-
// `initialMessages` is only read once, on mount.
|
|
691
|
+
useAgentChat({ threadId, initialMessages });
|
|
683
692
|
```
|
|
684
693
|
|
|
685
694
|
`storedThreadToUiMessages` merges the store's one-row-per-model-iteration turns (a turn with tool
|
|
@@ -691,19 +700,17 @@ persisted pushed components as `data-ui` parts, so a reloaded thread shows what
|
|
|
691
700
|
|
|
692
701
|
### Attachments
|
|
693
702
|
|
|
694
|
-
`useAttachments` is the composer's file tray without the
|
|
695
|
-
with progress and cancel, retry, image previews, and the
|
|
696
|
-
paste.
|
|
703
|
+
`chat.composer.files` (or `useAttachments()` on its own) is the composer's file tray without the
|
|
704
|
+
tray: validation, one upload per file with progress and cancel, retry, image previews, and the
|
|
705
|
+
handlers for a file input, a drop zone and paste. `chat.composer.submit()` sends the ready files
|
|
706
|
+
with the draft and clears them.
|
|
697
707
|
|
|
698
708
|
```tsx
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
// uploads through the provider's backend; or `upload: (file, { signal, onProgress }) => myUpload(file)`
|
|
703
|
-
accept: 'image/*,.pdf',
|
|
704
|
-
maxBytes: 20 * 1024 * 1024,
|
|
705
|
-
maxFiles: 5,
|
|
709
|
+
const chat = useAgentChat({
|
|
710
|
+
// optional — or `upload: (file, { signal, onProgress }) => myUpload(file)`
|
|
711
|
+
composer: { accept: 'image/*,.pdf', maxBytes: 20 * 1024 * 1024, maxFiles: 5 },
|
|
706
712
|
});
|
|
713
|
+
const { files } = chat.composer;
|
|
707
714
|
|
|
708
715
|
<div {...files.dropZoneProps} data-dragging={files.isDragging}>
|
|
709
716
|
<textarea onPaste={files.onPaste} />
|
|
@@ -718,13 +725,8 @@ const files = useAttachments({
|
|
|
718
725
|
))}
|
|
719
726
|
</div>
|
|
720
727
|
|
|
721
|
-
<
|
|
722
|
-
|
|
723
|
-
onClick={async () => {
|
|
724
|
-
await chat.sendMessage({ text }, { body: { attachments: files.refs } });
|
|
725
|
-
files.clear();
|
|
726
|
-
}}
|
|
727
|
-
/>
|
|
728
|
+
<textarea value={chat.composer.text} onChange={(e) => chat.composer.setText(e.target.value)} />
|
|
729
|
+
<button disabled={!chat.composer.canSend} onClick={() => chat.composer.submit()} />
|
|
728
730
|
```
|
|
729
731
|
|
|
730
732
|
An item is `uploading`, `ready`, `error` (retry with `files.retry(id)`) or `rejected` (failed
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ThreadSummary, ThreadDetail, MessageAttachment, ToolCatalogEntry, SkillCatalogEntry,
|
|
1
|
+
import { ThreadSummary, ThreadDetail, MessageAttachment, ToolCatalogEntry, SkillCatalogEntry, QuotaReport, ModelCatalogView, AgentCatalogEntry, MessageFeedbackValue, MessageFeedback } from '@dudousxd/nestjs-agent-core';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Partial update accepted by `PATCH <base>/threads/:threadId`. `defaultAgent: null` clears a
|
|
@@ -117,7 +117,6 @@ interface AgentBackend {
|
|
|
117
117
|
uploadAttachment?(file: File, options?: UploadAttachmentOptions): Promise<MessageAttachment>;
|
|
118
118
|
listTools?(agent?: string): Promise<ToolCatalogEntry[]>;
|
|
119
119
|
listSkills?(threadId?: string): Promise<SkillCatalogEntry[]>;
|
|
120
|
-
getQuotaToday?(): Promise<QuotaView>;
|
|
121
120
|
/** `GET <base>/quota` — every budget window, and which one blocks sends, if any. */
|
|
122
121
|
getQuota?(): Promise<QuotaReport>;
|
|
123
122
|
/** `GET <base>/models?agent=` — what a model picker offers. */
|
|
@@ -152,8 +151,6 @@ declare class AgentHttpError extends Error {
|
|
|
152
151
|
readonly path: string;
|
|
153
152
|
constructor(status: number, method: string, path: string, statusText: string);
|
|
154
153
|
}
|
|
155
|
-
/** The quota-today read-model: usage, the configured limit (null → unlimited), and USD spend. */
|
|
156
|
-
type QuotaToday = QuotaView;
|
|
157
154
|
interface CancelResult {
|
|
158
155
|
aborted: boolean;
|
|
159
156
|
}
|
|
@@ -234,7 +231,6 @@ declare class AgentClient implements AgentBackend {
|
|
|
234
231
|
getThread(id: string): Promise<ThreadDetail>;
|
|
235
232
|
deleteThread(id: string): Promise<void>;
|
|
236
233
|
forkFromMessage(threadId: string, messageId: string): Promise<ThreadSummary>;
|
|
237
|
-
renameThread(id: string, title: string): Promise<OkResult>;
|
|
238
234
|
/** General `PATCH <path>/threads/:threadId` — title and/or the thread's pinned default agent. */
|
|
239
235
|
updateThread(id: string, patch: ThreadPatch): Promise<OkResult>;
|
|
240
236
|
/**
|
|
@@ -252,7 +248,6 @@ declare class AgentClient implements AgentBackend {
|
|
|
252
248
|
listAgents(): Promise<AgentCatalogEntry[]>;
|
|
253
249
|
/** `GET <path>/quota` — the caller's budget windows and the one blocking sends, if any. */
|
|
254
250
|
getQuota(): Promise<QuotaReport>;
|
|
255
|
-
getQuotaToday(): Promise<QuotaToday>;
|
|
256
251
|
cancelStream(runId: string): Promise<CancelResult>;
|
|
257
252
|
/**
|
|
258
253
|
* `remember` approves later calls of the same tool in the same thread; `via` names the surface
|
|
@@ -299,4 +294,4 @@ declare class AgentClient implements AgentBackend {
|
|
|
299
294
|
private handleResponse;
|
|
300
295
|
}
|
|
301
296
|
|
|
302
|
-
export { type AgentBackend as A, type CancelResult as C, type MessageFeedbackInput as M, type
|
|
297
|
+
export { type AgentBackend as A, type CancelResult as C, type MessageFeedbackInput as M, type ResumeStreamRequest as R, type ThreadPatch as T, type UploadAttachmentOptions as U, type AttachmentUploadStrategy as a, AgentBackendUnsupportedError as b, AgentClient as c, type AgentClientOptions as d, type AgentConnection as e, AgentHttpError as f, type ChatStreamRequest as g, type ChatStreamResponse as h, requireBackendMethod as r };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ThreadSummary, ThreadDetail, MessageAttachment, ToolCatalogEntry, SkillCatalogEntry,
|
|
1
|
+
import { ThreadSummary, ThreadDetail, MessageAttachment, ToolCatalogEntry, SkillCatalogEntry, QuotaReport, ModelCatalogView, AgentCatalogEntry, MessageFeedbackValue, MessageFeedback } from '@dudousxd/nestjs-agent-core';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Partial update accepted by `PATCH <base>/threads/:threadId`. `defaultAgent: null` clears a
|
|
@@ -117,7 +117,6 @@ interface AgentBackend {
|
|
|
117
117
|
uploadAttachment?(file: File, options?: UploadAttachmentOptions): Promise<MessageAttachment>;
|
|
118
118
|
listTools?(agent?: string): Promise<ToolCatalogEntry[]>;
|
|
119
119
|
listSkills?(threadId?: string): Promise<SkillCatalogEntry[]>;
|
|
120
|
-
getQuotaToday?(): Promise<QuotaView>;
|
|
121
120
|
/** `GET <base>/quota` — every budget window, and which one blocks sends, if any. */
|
|
122
121
|
getQuota?(): Promise<QuotaReport>;
|
|
123
122
|
/** `GET <base>/models?agent=` — what a model picker offers. */
|
|
@@ -152,8 +151,6 @@ declare class AgentHttpError extends Error {
|
|
|
152
151
|
readonly path: string;
|
|
153
152
|
constructor(status: number, method: string, path: string, statusText: string);
|
|
154
153
|
}
|
|
155
|
-
/** The quota-today read-model: usage, the configured limit (null → unlimited), and USD spend. */
|
|
156
|
-
type QuotaToday = QuotaView;
|
|
157
154
|
interface CancelResult {
|
|
158
155
|
aborted: boolean;
|
|
159
156
|
}
|
|
@@ -234,7 +231,6 @@ declare class AgentClient implements AgentBackend {
|
|
|
234
231
|
getThread(id: string): Promise<ThreadDetail>;
|
|
235
232
|
deleteThread(id: string): Promise<void>;
|
|
236
233
|
forkFromMessage(threadId: string, messageId: string): Promise<ThreadSummary>;
|
|
237
|
-
renameThread(id: string, title: string): Promise<OkResult>;
|
|
238
234
|
/** General `PATCH <path>/threads/:threadId` — title and/or the thread's pinned default agent. */
|
|
239
235
|
updateThread(id: string, patch: ThreadPatch): Promise<OkResult>;
|
|
240
236
|
/**
|
|
@@ -252,7 +248,6 @@ declare class AgentClient implements AgentBackend {
|
|
|
252
248
|
listAgents(): Promise<AgentCatalogEntry[]>;
|
|
253
249
|
/** `GET <path>/quota` — the caller's budget windows and the one blocking sends, if any. */
|
|
254
250
|
getQuota(): Promise<QuotaReport>;
|
|
255
|
-
getQuotaToday(): Promise<QuotaToday>;
|
|
256
251
|
cancelStream(runId: string): Promise<CancelResult>;
|
|
257
252
|
/**
|
|
258
253
|
* `remember` approves later calls of the same tool in the same thread; `via` names the surface
|
|
@@ -299,4 +294,4 @@ declare class AgentClient implements AgentBackend {
|
|
|
299
294
|
private handleResponse;
|
|
300
295
|
}
|
|
301
296
|
|
|
302
|
-
export { type AgentBackend as A, type CancelResult as C, type MessageFeedbackInput as M, type
|
|
297
|
+
export { type AgentBackend as A, type CancelResult as C, type MessageFeedbackInput as M, type ResumeStreamRequest as R, type ThreadPatch as T, type UploadAttachmentOptions as U, type AttachmentUploadStrategy as a, AgentBackendUnsupportedError as b, AgentClient as c, type AgentClientOptions as d, type AgentConnection as e, AgentHttpError as f, type ChatStreamRequest as g, type ChatStreamResponse as h, requireBackendMethod as r };
|