@deuz-sdk/react 1.8.0 → 1.9.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/dist/index.cjs +420 -135
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +197 -10
- package/dist/index.d.ts +197 -10
- package/dist/index.js +422 -138
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
|
-
import { Message, ToolApprovalRequest, ToolApprovalResponse, DeepPartial } from '@deuz-sdk/core';
|
|
1
|
+
import { Message, ToolApprovalRequest, Usage, FinishReason, ToolApprovalResponse, ImagePart, DeepPartial } from '@deuz-sdk/core';
|
|
2
2
|
export { Message, ToolApprovalRequest, ToolApprovalResponse } from '@deuz-sdk/core';
|
|
3
|
-
import { UIMessage, AssistantTurnState } from '@deuz-sdk/core/chat';
|
|
4
|
-
export { AssistantTurnState, ChatHistory, UIMessage, UIToolCall } from '@deuz-sdk/core/chat';
|
|
3
|
+
import { UIMessage, ChatHistory, UIToolCall, AssistantTurnState, ChatInput } from '@deuz-sdk/core/chat';
|
|
4
|
+
export { AssistantTurnState, ChatHistory, ChatInput, UIMessage, UIMessagePart, UIToolCall } from '@deuz-sdk/core/chat';
|
|
5
5
|
import * as react from 'react';
|
|
6
6
|
import { ReactNode } from 'react';
|
|
7
7
|
export { DeuzUIPart } from '@deuz-sdk/core/ui';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Picked files → canonical parts for a {@link ChatInput}. A thin, null-tolerant
|
|
11
|
+
* wrapper over core's `filesToImageParts`: the bytes work (Web APIs only, no
|
|
12
|
+
* `FileReader`, no `Buffer`) lives THERE and is deliberately not reimplemented
|
|
13
|
+
* here. `ImagePart` is the single carrier for all binary media, so images and
|
|
14
|
+
* PDFs both ride this call.
|
|
15
|
+
*
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <input type="file" multiple onChange={async (e) => {
|
|
18
|
+
* const parts = await partsFromFiles(e.target.files);
|
|
19
|
+
* await sendMessage({ text: 'what is in these?', parts });
|
|
20
|
+
* }} />
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
declare function partsFromFiles(files: FileList | Iterable<Blob & {
|
|
24
|
+
type?: string;
|
|
25
|
+
}> | null | undefined): Promise<ImagePart[]>;
|
|
9
26
|
/** Live cumulative USD cost, from the wire's `cost` parts (last one wins). */
|
|
10
27
|
interface UseChatCost {
|
|
11
28
|
costUsd: number;
|
|
@@ -17,6 +34,21 @@ interface UseChatBudgetExceeded {
|
|
|
17
34
|
limit: number;
|
|
18
35
|
value: number;
|
|
19
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Where the resume cursor lives ACROSS a page reload.
|
|
39
|
+
*
|
|
40
|
+
* An adapter rather than a hardcoded `localStorage`: core's own
|
|
41
|
+
* `connectDeuzStream` frames the cursor as the caller's choice, and hardcoding
|
|
42
|
+
* web storage would break SSR (no `window`), React Native (no `localStorage`)
|
|
43
|
+
* and privacy modes that throw on write. Both methods may throw — the hook
|
|
44
|
+
* swallows it, because losing a cursor must never kill a live stream.
|
|
45
|
+
*/
|
|
46
|
+
interface UseChatCursorStore {
|
|
47
|
+
/** The persisted cursor, or `undefined` when there is nothing to resume. */
|
|
48
|
+
load(): string | undefined;
|
|
49
|
+
/** Persist the cursor. Called on every advance. */
|
|
50
|
+
save(id: string): void;
|
|
51
|
+
}
|
|
20
52
|
interface UseChatResumeOptions {
|
|
21
53
|
/**
|
|
22
54
|
* Resume endpoint (see core's `resumeDeuzStreamResponse`): a GET URL or a
|
|
@@ -28,48 +60,125 @@ interface UseChatResumeOptions {
|
|
|
28
60
|
}) => Response | Promise<Response>);
|
|
29
61
|
/** Cursor to resume from (e.g. persisted across a page reload). */
|
|
30
62
|
lastEventId?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Attempt `reconnect()` ONCE per mounted hook — the canonical scenario
|
|
65
|
+
* resumability exists for (the user refreshes mid-generation) with zero app
|
|
66
|
+
* wiring. Fires exactly once under React 18/19 StrictMode's deliberate
|
|
67
|
+
* mount → unmount → mount double-invoke.
|
|
68
|
+
*
|
|
69
|
+
* An AUTOMATIC attempt that fails is NOT an error state and does NOT reach
|
|
70
|
+
* `onError`: the user did not ask for it, and every cold load of a chat app
|
|
71
|
+
* whose resume endpoint answers 404 (nothing in flight) would otherwise paint
|
|
72
|
+
* a permanent error. A caught-up endpoint (immediate `[DONE]`) is a silent
|
|
73
|
+
* no-op — no bubble, no state change. Call `reconnect()` yourself when you
|
|
74
|
+
* want the failure surfaced.
|
|
75
|
+
*/
|
|
76
|
+
auto?: boolean;
|
|
77
|
+
/** Cursor persistence adapter (see {@link UseChatCursorStore}). */
|
|
78
|
+
cursor?: UseChatCursorStore;
|
|
31
79
|
}
|
|
32
80
|
interface UseChatOptions {
|
|
33
81
|
/** Endpoint serving `toDeuzStreamResponse` output. */
|
|
34
82
|
api: string;
|
|
35
|
-
/**
|
|
83
|
+
/**
|
|
84
|
+
* Seed canonical history (e.g. restored from a ChatStore) — rendered via
|
|
85
|
+
* `uiFromMessages`.
|
|
86
|
+
*
|
|
87
|
+
* READ ONCE, at mount, like any `useState` initial value. It is deliberately
|
|
88
|
+
* NOT re-adopted when the prop changes: apps pass an inline array literal, so
|
|
89
|
+
* a new identity arrives on EVERY render and adopting it would reset the
|
|
90
|
+
* transcript continuously — and mid-stream it would clobber the turn being
|
|
91
|
+
* folded. Switching chats or hydrating later is an explicit action: call
|
|
92
|
+
* `setHistory` (both views) or `setMessages` (re-derives canonical).
|
|
93
|
+
*/
|
|
36
94
|
initialMessages?: Message[];
|
|
37
95
|
headers?: Record<string, string>;
|
|
38
96
|
/** Extra fields merged into every request body. */
|
|
39
97
|
body?: Record<string, unknown>;
|
|
40
98
|
/** Chat identity — merged into every request body (server-side ChatStore persistence). */
|
|
41
99
|
chatId?: string;
|
|
42
|
-
/** Enables `reconnect()` against a resume endpoint (wire v2
|
|
100
|
+
/** Enables `reconnect()` / `resume.auto` against a resume endpoint (wire v2). */
|
|
43
101
|
resume?: UseChatResumeOptions;
|
|
44
102
|
/** Id source for UI messages/turns. Default: `crypto.randomUUID` (with a fallback). */
|
|
45
103
|
generateId?: () => string;
|
|
104
|
+
/**
|
|
105
|
+
* Coalesce React commits to at most one per `throttleMs` (trailing edge).
|
|
106
|
+
* Default `0` — commit on every wire part, exactly as 1.8 did.
|
|
107
|
+
*
|
|
108
|
+
* A fast model emits hundreds of text deltas per second; with markdown
|
|
109
|
+
* rendering over a long transcript that visibly janks. The terminal frame is
|
|
110
|
+
* ALWAYS flushed (stream end, error, abort, an external tool result), so the
|
|
111
|
+
* final text can never be lost to coalescing. A plain `setTimeout` is fine
|
|
112
|
+
* here: unlike core's `src/**`, this package is not edge-lint-constrained and
|
|
113
|
+
* has no `deps.clock` seam.
|
|
114
|
+
*/
|
|
115
|
+
throttleMs?: number;
|
|
46
116
|
/**
|
|
47
117
|
* Client-tool executor: called for every streamed tool call the SERVER did
|
|
48
118
|
* not execute. The return value is appended as its tool_result and the chat
|
|
49
119
|
* auto-continues; a throw self-heals as an is_error result.
|
|
120
|
+
*
|
|
121
|
+
* OMIT it to drive client tools from outside the hook instead: the
|
|
122
|
+
* round-trip PARKS, the calls appear in `pendingToolCalls`, and
|
|
123
|
+
* `addToolResult` answers them.
|
|
50
124
|
*/
|
|
51
125
|
onToolCall?: (call: {
|
|
52
126
|
toolCallId: string;
|
|
53
127
|
toolName: string;
|
|
54
128
|
input: unknown;
|
|
55
129
|
}) => Promise<unknown> | unknown;
|
|
130
|
+
/**
|
|
131
|
+
* Fired for every `data-{name}` part as it arrives, BEFORE reconciliation —
|
|
132
|
+
* so a caller sees each intermediate write of an addressable `(name, id)`
|
|
133
|
+
* entry, which `dataParts` collapses to the last one. A throw is swallowed
|
|
134
|
+
* (G2: a consumer callback is not a transport error).
|
|
135
|
+
*/
|
|
136
|
+
onData?: (data: {
|
|
137
|
+
name: string;
|
|
138
|
+
id?: string;
|
|
139
|
+
payload: unknown;
|
|
140
|
+
}) => void;
|
|
56
141
|
onError?: (error: Error) => void;
|
|
142
|
+
/**
|
|
143
|
+
* How a NON-2xx response from `api` is reported (default `'error-part'`: the
|
|
144
|
+
* failure becomes `status: 'error'` instead of an empty assistant bubble).
|
|
145
|
+
* Forwarded verbatim to core's `readDeuzStream`.
|
|
146
|
+
*/
|
|
147
|
+
onHttpError?: 'error-part' | 'ignore';
|
|
57
148
|
/** Injectable for tests / custom transports. Defaults to global fetch. */
|
|
58
149
|
fetch?: typeof fetch;
|
|
59
150
|
}
|
|
60
151
|
interface UseChatResult {
|
|
152
|
+
/** The render view. Each message carries its ordered `parts` (1.9) when it has any. */
|
|
61
153
|
messages: UIMessage[];
|
|
154
|
+
/**
|
|
155
|
+
* BOTH views in one object (`{ ui, canonical }`). Only the canonical one is
|
|
156
|
+
* POSTable and only it survives a round-trip through a provider, so an app
|
|
157
|
+
* that persists a chat should persist `history.canonical`. Identity is stable
|
|
158
|
+
* until either view changes.
|
|
159
|
+
*/
|
|
160
|
+
history: ChatHistory;
|
|
62
161
|
status: 'idle' | 'streaming' | 'error';
|
|
63
162
|
error: Error | undefined;
|
|
64
163
|
/** Gated tool calls awaiting verdicts — the chat is PAUSED while non-empty. */
|
|
65
164
|
pendingApprovals: ToolApprovalRequest[];
|
|
165
|
+
/**
|
|
166
|
+
* Client tool calls the round-trip is PARKED on (no `onToolCall` executor).
|
|
167
|
+
* Answer each with `addToolResult`; the chat continues once all are answered.
|
|
168
|
+
*/
|
|
169
|
+
pendingToolCalls: UIToolCall[];
|
|
66
170
|
/** Live cumulative cost from the wire's `cost` parts (last one wins). */
|
|
67
171
|
cost?: UseChatCost;
|
|
68
172
|
/** Set when the server's budget guardrail tripped this turn. */
|
|
69
173
|
budgetExceeded?: UseChatBudgetExceeded;
|
|
70
|
-
/**
|
|
174
|
+
/**
|
|
175
|
+
* This turn's app-defined `data-{name}` parts. Entries written with an `id`
|
|
176
|
+
* are RECONCILED by `(name, id)` — one live status widget stays one entry, at
|
|
177
|
+
* its original position — while id-less entries stay append-only (1.7/1.8).
|
|
178
|
+
*/
|
|
71
179
|
dataParts: Array<{
|
|
72
180
|
name: string;
|
|
181
|
+
id?: string;
|
|
73
182
|
payload: unknown;
|
|
74
183
|
}>;
|
|
75
184
|
/** This turn's RAG citations. */
|
|
@@ -78,13 +187,80 @@ interface UseChatResult {
|
|
|
78
187
|
plan?: AssistantTurnState['plan'];
|
|
79
188
|
/** This turn's live activity feed ("Computer" view, 1.8). */
|
|
80
189
|
activity: AssistantTurnState['activity'];
|
|
81
|
-
|
|
190
|
+
/** This turn's `verifyStep` verdicts, in arrival order (1.9). */
|
|
191
|
+
verifications?: AssistantTurnState['verifications'];
|
|
192
|
+
/**
|
|
193
|
+
* Non-fatal execution notices for this turn, in arrival order (1.9): a
|
|
194
|
+
* sampling param the wire could not carry, a clamped ceiling, an unknown model
|
|
195
|
+
* slug served from the conservative fallback row. Render them as a dismissible
|
|
196
|
+
* banner — the run itself succeeded.
|
|
197
|
+
*/
|
|
198
|
+
warnings?: AssistantTurnState['warnings'];
|
|
199
|
+
/**
|
|
200
|
+
* `false-finish` rejections for this turn (1.9): the server's guard decided
|
|
201
|
+
* the work was not actually done. A trailing entry with `willRetry: false`
|
|
202
|
+
* means the guard's budget ran out, so the answer on screen is the one it
|
|
203
|
+
* stopped on rather than one it was happy with.
|
|
204
|
+
*/
|
|
205
|
+
falseFinishes?: AssistantTurnState['falseFinishes'];
|
|
206
|
+
/**
|
|
207
|
+
* Live sub-agent (`agentTool`) frames for this turn, one per `agentPath`
|
|
208
|
+
* (1.9). Each holds the child's OWN turn — its text, ordered `parts` and tool
|
|
209
|
+
* cards — so a delegated run is renderable instead of invisible, and
|
|
210
|
+
* `afterPart` says where in the parent's `parts` it belongs. Nothing here is
|
|
211
|
+
* mixed into the parent's `content`: a sub-agent's words are its own.
|
|
212
|
+
*/
|
|
213
|
+
subAgents?: AssistantTurnState['subAgents'];
|
|
214
|
+
/** Per-step usage/finish from `step-finish` parts, in arrival order (1.9). */
|
|
215
|
+
steps?: AssistantTurnState['steps'];
|
|
216
|
+
/** Terminal token usage for this turn, from the wire's `finish` part (1.9). */
|
|
217
|
+
usage?: Usage;
|
|
218
|
+
/** Terminal finish reason (1.9) — `'length'` means the answer was truncated. */
|
|
219
|
+
finishReason?: FinishReason;
|
|
220
|
+
/**
|
|
221
|
+
* Send one user turn: plain text (1.8-identical, including the canonical
|
|
222
|
+
* `content` string) or text plus canonical parts — images/PDFs from
|
|
223
|
+
* {@link partsFromFiles}, `imagePart`/`filePart`, anything the wires accept.
|
|
224
|
+
*/
|
|
225
|
+
sendMessage: (input: ChatInput) => Promise<void>;
|
|
82
226
|
/** Abort the in-flight stream (not an error). */
|
|
83
227
|
stop: () => void;
|
|
84
228
|
/** Drop the trailing assistant/tool turns (core `dropTrailingAssistant`) and re-run. */
|
|
85
229
|
regenerate: () => Promise<void>;
|
|
86
|
-
/** Cut history before `messageId` (core `branchBeforeUserMessage`) and send `
|
|
87
|
-
editAndResend: (messageId: string,
|
|
230
|
+
/** Cut history before `messageId` (core `branchBeforeUserMessage`) and send `input`. */
|
|
231
|
+
editAndResend: (messageId: string, input: ChatInput) => Promise<void>;
|
|
232
|
+
/**
|
|
233
|
+
* Replace BOTH views in one commit — the honest primitive, because Deuz keeps
|
|
234
|
+
* two of them and only the canonical one is POSTable. Use it to switch chats,
|
|
235
|
+
* hydrate after mount, or delete/edit a rendered message while keeping a
|
|
236
|
+
* system prompt and provider round-trip data that the UI view cannot carry.
|
|
237
|
+
*
|
|
238
|
+
* Pending gates (approvals, parked client tool calls) are DROPPED: they were
|
|
239
|
+
* anchored to the transcript you just replaced.
|
|
240
|
+
*/
|
|
241
|
+
setHistory: (update: ChatHistory | ((prev: ChatHistory) => ChatHistory)) => void;
|
|
242
|
+
/**
|
|
243
|
+
* Sugar over `setHistory`: re-derives the canonical view with core's
|
|
244
|
+
* `canonicalFromUI`. That inverse projection is LOSSY — system messages,
|
|
245
|
+
* `Message.providerMetadata` and UI-only state do not survive it, and a
|
|
246
|
+
* message without ordered `parts` loses its attachments entirely — so drive
|
|
247
|
+
* `setHistory` directly when your history has any of those.
|
|
248
|
+
*/
|
|
249
|
+
setMessages: (update: UIMessage[] | ((prev: UIMessage[]) => UIMessage[])) => void;
|
|
250
|
+
/**
|
|
251
|
+
* Answer ONE parked client tool call from outside the hook. Feeds the exact
|
|
252
|
+
* path `onToolCall`'s return value takes (core `clientToolResultMessage`), so
|
|
253
|
+
* the chat auto-continues once every call in `pendingToolCalls` is answered.
|
|
254
|
+
* A result for an id that is not parked is ignored — an orphan `tool_result`
|
|
255
|
+
* would 400 the next request.
|
|
256
|
+
*/
|
|
257
|
+
addToolResult: (result: {
|
|
258
|
+
toolCallId: string;
|
|
259
|
+
output: unknown;
|
|
260
|
+
isError?: boolean;
|
|
261
|
+
}) => Promise<void>;
|
|
262
|
+
/** Dismiss the error: `status: 'error'` → `'idle'`. Leaves a live stream alone. */
|
|
263
|
+
clearError: () => void;
|
|
88
264
|
/**
|
|
89
265
|
* Record one verdict (the request's signed `token` is auto-preserved). Once
|
|
90
266
|
* EVERY pending approval has a verdict, the chat auto-resumes with
|
|
@@ -103,6 +279,17 @@ interface UseObjectOptions {
|
|
|
103
279
|
/** Endpoint serving `toDeuzObjectStreamResponse` output. */
|
|
104
280
|
api: string;
|
|
105
281
|
headers?: Record<string, string>;
|
|
282
|
+
/**
|
|
283
|
+
* How a NON-2xx response from `api` is reported (default `'error-part'`,
|
|
284
|
+
* which lands in `error`). Forwarded verbatim to core's `readDeuzStream`.
|
|
285
|
+
*/
|
|
286
|
+
onHttpError?: 'error-part' | 'ignore';
|
|
287
|
+
/**
|
|
288
|
+
* Coalesce partials to at most one React commit per `throttleMs` (trailing
|
|
289
|
+
* edge). Default `0` — commit every partial, exactly as 1.8 did. The final
|
|
290
|
+
* partial is ALWAYS flushed, so the completed object is never lost.
|
|
291
|
+
*/
|
|
292
|
+
throttleMs?: number;
|
|
106
293
|
/** Injectable for tests / custom transports. Defaults to global fetch. */
|
|
107
294
|
fetch?: typeof fetch;
|
|
108
295
|
}
|
|
@@ -143,4 +330,4 @@ interface CostBadgeProps {
|
|
|
143
330
|
/** Live cost readout: `$X.XXXX`, plus ` (saved $Y.YYYY)` when caching saved money. */
|
|
144
331
|
declare function CostBadge({ cost, format }: CostBadgeProps): react.JSX.Element | null;
|
|
145
332
|
|
|
146
|
-
export { CostBadge, type CostBadgeProps, ToolApprovalCard, type ToolApprovalCardProps, type UseChatBudgetExceeded, type UseChatCost, type UseChatOptions, type UseChatResult, type UseChatResumeOptions, type UseObjectOptions, type UseObjectResult, useChat, useObject };
|
|
333
|
+
export { CostBadge, type CostBadgeProps, ToolApprovalCard, type ToolApprovalCardProps, type UseChatBudgetExceeded, type UseChatCost, type UseChatCursorStore, type UseChatOptions, type UseChatResult, type UseChatResumeOptions, type UseObjectOptions, type UseObjectResult, partsFromFiles, useChat, useObject };
|