@kortyx/react 0.2.1 → 0.3.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/CHANGELOG.md +28 -0
- package/README.md +68 -2
- package/dist/index.d.ts +55 -15
- package/dist/index.js +158 -35
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @kortyx/react
|
|
2
2
|
|
|
3
|
+
## [0.3.1](https://github.com/kortyx-io/kortyx/compare/react-v0.3.0...react-v0.3.1) (2026-05-22)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **react:** preserve interrupt routing metadata ([59898e2](https://github.com/kortyx-io/kortyx/commit/59898e24e291641edcdef3d09e6be4d9e66b1bf0))
|
|
9
|
+
* **react:** preserve interrupt routing metadata ([0d258a1](https://github.com/kortyx-io/kortyx/commit/0d258a17f0c49ebb1b3c9980bea7262e1badc4e3))
|
|
10
|
+
|
|
11
|
+
## [0.3.0](https://github.com/kortyx-io/kortyx/compare/react-v0.2.1...react-v0.3.0) (2026-05-20)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* **chat:** add request context plumbing ([7210a6f](https://github.com/kortyx-io/kortyx/commit/7210a6f9275728628e1862bd7b0f200112bd9b08))
|
|
17
|
+
* **react:** tighten useChat lifecycle controls ([1672c72](https://github.com/kortyx-io/kortyx/commit/1672c72a524d16fae2f23dcbec1b91191747dc25))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* **react:** bucket empty-node text deltas into a single stream ([fb01809](https://github.com/kortyx-io/kortyx/commit/fb01809752f138414cda13daa5ffe5e67016b0c6))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Dependencies
|
|
26
|
+
|
|
27
|
+
* The following workspace dependencies were updated
|
|
28
|
+
* dependencies
|
|
29
|
+
* @kortyx/stream bumped to 0.7.0
|
|
30
|
+
|
|
3
31
|
## [0.2.1](https://github.com/kortyx-io/kortyx/compare/react-v0.2.0...react-v0.2.1) (2026-05-03)
|
|
4
32
|
|
|
5
33
|
|
package/README.md
CHANGED
|
@@ -1,17 +1,83 @@
|
|
|
1
1
|
# @kortyx/react
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@kortyx/react)
|
|
4
|
+
[](https://github.com/kortyx-io/kortyx/actions/workflows/ci.yml)
|
|
5
|
+
[](https://github.com/kortyx-io/kortyx/blob/main/LICENSE)
|
|
6
|
+
[](https://react.dev/)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
|
|
9
|
+
React hooks and transport helpers for Kortyx client streaming experiences.
|
|
4
10
|
|
|
5
11
|
## Install
|
|
6
12
|
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @kortyx/react
|
|
15
|
+
```
|
|
16
|
+
|
|
7
17
|
```bash
|
|
8
18
|
npm install @kortyx/react
|
|
9
19
|
```
|
|
10
20
|
|
|
11
21
|
## Exports
|
|
12
22
|
|
|
23
|
+
- `useChat`
|
|
24
|
+
- `createRouteChatTransport`
|
|
25
|
+
- `createChatTransport`
|
|
26
|
+
- `createBrowserChatStorage`
|
|
13
27
|
- `useStructuredStreams`
|
|
28
|
+
- `createLiveChatPieces`
|
|
29
|
+
- `toHumanInputPiece`
|
|
30
|
+
|
|
31
|
+
`useChat` keeps finalized chat history in `messages` and the active assistant
|
|
32
|
+
response in `streamContentPieces` while a stream is running.
|
|
33
|
+
|
|
34
|
+
It also exposes stream controls for UI integrations:
|
|
35
|
+
|
|
36
|
+
- `abort()` / `canAbort`
|
|
37
|
+
- `error` / `clearError()`
|
|
38
|
+
- `clearMessages()`
|
|
39
|
+
- `resetSession()`
|
|
40
|
+
- `resetChat()`
|
|
41
|
+
- `respondToInterrupt(...)`
|
|
42
|
+
|
|
43
|
+
`clearMessages()` keeps the current session. `resetChat()` clears messages,
|
|
44
|
+
active stream state, errors, and the session id. Route transports forward
|
|
45
|
+
`abort()` through an `AbortSignal`; custom transports should forward
|
|
46
|
+
`context.signal` to their own request layer.
|
|
47
|
+
|
|
48
|
+
`useChat({ context })` forwards request context to the transport. Route
|
|
49
|
+
transports send `{ sessionId, workflowId, messages, context }` by default, and
|
|
50
|
+
`createRouteChatTransport({ createBody })` can customize that body.
|
|
51
|
+
`prepareContextMessages` lets apps replace the default history with summaries
|
|
52
|
+
or server-owned history while `useChat` still appends the outgoing message.
|
|
53
|
+
|
|
54
|
+
## Example
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
"use client";
|
|
58
|
+
|
|
59
|
+
import { createRouteChatTransport, useChat } from "@kortyx/react";
|
|
60
|
+
|
|
61
|
+
export function Chat() {
|
|
62
|
+
const chat = useChat({
|
|
63
|
+
transport: createRouteChatTransport({ endpoint: "/api/chat" }),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<button onClick={() => chat.send("Summarize this account")}>
|
|
68
|
+
Send
|
|
69
|
+
</button>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Documentation
|
|
75
|
+
|
|
76
|
+
- [Documentation](https://kortyx.io/docs)
|
|
77
|
+
- [React client](https://kortyx.io/docs/reference/react-client)
|
|
78
|
+
- [Quickstart: Next.js API Route](https://kortyx.io/docs/getting-started/quickstart-nextjs)
|
|
79
|
+
- [Stream protocol](https://kortyx.io/docs/reference/stream-protocol)
|
|
14
80
|
|
|
15
81
|
## License
|
|
16
82
|
|
|
17
|
-
Apache-2.0
|
|
83
|
+
Apache-2.0. See [LICENSE](https://github.com/kortyx-io/kortyx/blob/main/LICENSE).
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,10 @@ type HumanInputPiece = {
|
|
|
14
14
|
label: string;
|
|
15
15
|
description?: string;
|
|
16
16
|
}>;
|
|
17
|
+
schemaId?: string;
|
|
18
|
+
schemaVersion?: string;
|
|
19
|
+
interruptId?: string;
|
|
20
|
+
meta?: Record<string, unknown>;
|
|
17
21
|
};
|
|
18
22
|
type ContentPiece = {
|
|
19
23
|
id: string;
|
|
@@ -61,38 +65,47 @@ type BrowserChatStorageKeys = {
|
|
|
61
65
|
workflowId: string;
|
|
62
66
|
};
|
|
63
67
|
type BrowserChatStorageOptions<TMessage> = {
|
|
64
|
-
parseMessage
|
|
65
|
-
serializeMessage
|
|
68
|
+
parseMessage?: ((value: unknown) => TMessage | null) | undefined;
|
|
69
|
+
serializeMessage?: ((message: TMessage) => unknown) | undefined;
|
|
66
70
|
maxMessages?: number | undefined;
|
|
67
71
|
keys?: Partial<BrowserChatStorageKeys> | undefined;
|
|
68
72
|
};
|
|
69
73
|
declare function createBrowserChatStorage<TMessage = unknown>(options?: BrowserChatStorageOptions<TMessage>): ChatStorage<TMessage>;
|
|
70
74
|
|
|
71
75
|
type MaybePromise<T> = T | Promise<T>;
|
|
76
|
+
type DefaultChatContext$1 = Record<string, unknown>;
|
|
72
77
|
type OutgoingChatMessage = {
|
|
73
78
|
role: "user" | "assistant" | "system";
|
|
74
79
|
content: string;
|
|
75
80
|
metadata?: Record<string, unknown>;
|
|
76
81
|
};
|
|
77
|
-
type ChatTransportContext = {
|
|
82
|
+
type ChatTransportContext<TContext = DefaultChatContext$1> = {
|
|
78
83
|
sessionId: string;
|
|
79
84
|
workflowId: string;
|
|
80
85
|
messages: OutgoingChatMessage[];
|
|
86
|
+
context: TContext;
|
|
87
|
+
signal?: AbortSignal | undefined;
|
|
81
88
|
};
|
|
82
89
|
type ChatTransportChunkHandler = (chunk: StreamChunk) => undefined | boolean | Promise<boolean | undefined>;
|
|
83
|
-
type ChatTransport = {
|
|
84
|
-
stream(args: ChatTransportContext & {
|
|
90
|
+
type ChatTransport<TContext = DefaultChatContext$1> = {
|
|
91
|
+
stream(args: ChatTransportContext<TContext> & {
|
|
85
92
|
onChunk: ChatTransportChunkHandler;
|
|
86
93
|
}): Promise<void>;
|
|
87
94
|
};
|
|
88
95
|
type ChatTransportChunkSource = Iterable<StreamChunk> | AsyncIterable<StreamChunk>;
|
|
89
|
-
declare function createChatTransport(args: {
|
|
90
|
-
stream: (context: ChatTransportContext) => MaybePromise<ChatTransportChunkSource>;
|
|
91
|
-
}): ChatTransport
|
|
92
|
-
type
|
|
93
|
-
|
|
96
|
+
declare function createChatTransport<TContext = DefaultChatContext$1>(args: {
|
|
97
|
+
stream: (context: ChatTransportContext<TContext>) => MaybePromise<ChatTransportChunkSource>;
|
|
98
|
+
}): ChatTransport<TContext>;
|
|
99
|
+
type RouteChatRequestBody<TContext = DefaultChatContext$1> = {
|
|
100
|
+
sessionId: string;
|
|
101
|
+
workflowId: string;
|
|
102
|
+
messages: OutgoingChatMessage[];
|
|
103
|
+
context: TContext;
|
|
94
104
|
};
|
|
95
|
-
|
|
105
|
+
type CreateRouteChatTransportArgs<TBody, TContext> = Omit<StreamFromRouteArgs<TBody>, "body"> & {
|
|
106
|
+
createBody?: ((context: ChatTransportContext<TContext>) => TBody) | undefined;
|
|
107
|
+
};
|
|
108
|
+
declare function createRouteChatTransport<TContext = DefaultChatContext$1, TBody = RouteChatRequestBody<TContext>>(args: CreateRouteChatTransportArgs<TBody, TContext>): ChatTransport<TContext>;
|
|
96
109
|
|
|
97
110
|
type StructuredStreamItem<TData = unknown> = {
|
|
98
111
|
id: string;
|
|
@@ -147,14 +160,29 @@ declare function createLiveChatPieces<TStructuredData = unknown, TCustomPiece ex
|
|
|
147
160
|
getPieces: () => LiveChatPiece<TStructuredData, TCustomPiece>[];
|
|
148
161
|
};
|
|
149
162
|
|
|
163
|
+
type ToHumanInputPiece = (args: {
|
|
164
|
+
chunk: StreamChunk;
|
|
165
|
+
createId: () => string;
|
|
166
|
+
}) => HumanInputPiece;
|
|
150
167
|
declare function toHumanInputPiece(args: {
|
|
151
168
|
chunk: StreamChunk;
|
|
152
169
|
createId: () => string;
|
|
153
170
|
}): HumanInputPiece;
|
|
154
171
|
|
|
172
|
+
type DefaultChatContext = Record<string, unknown>;
|
|
173
|
+
type PrepareContextMessagesArgs<TContext = DefaultChatContext> = {
|
|
174
|
+
messages: ChatMsg[];
|
|
175
|
+
sessionId: string;
|
|
176
|
+
workflowId: string;
|
|
177
|
+
reason: "send" | "resume";
|
|
178
|
+
context: TContext;
|
|
179
|
+
};
|
|
180
|
+
type PrepareContextMessages<TContext = DefaultChatContext> = (args: PrepareContextMessagesArgs<TContext>) => OutgoingChatMessage[] | Promise<OutgoingChatMessage[]>;
|
|
155
181
|
type UseChatValue = {
|
|
156
182
|
messages: ChatMsg[];
|
|
157
183
|
isStreaming: boolean;
|
|
184
|
+
canAbort: boolean;
|
|
185
|
+
error: Error | null;
|
|
158
186
|
streamContentPieces: ContentPiece[];
|
|
159
187
|
streamDebug: StreamChunk[];
|
|
160
188
|
lastAssistantId: string | null;
|
|
@@ -167,17 +195,29 @@ type UseChatValue = {
|
|
|
167
195
|
selected: string[];
|
|
168
196
|
text?: string;
|
|
169
197
|
}) => Promise<void> | void;
|
|
198
|
+
respondToInterrupt: (piece: HumanInputPiece, response?: {
|
|
199
|
+
selected?: string[] | undefined;
|
|
200
|
+
text?: string | undefined;
|
|
201
|
+
}) => Promise<void> | void;
|
|
170
202
|
setMessages: React.Dispatch<React.SetStateAction<ChatMsg[]>>;
|
|
203
|
+
clearMessages: () => void;
|
|
204
|
+
resetSession: () => void;
|
|
205
|
+
resetChat: () => void;
|
|
171
206
|
clearChat: () => void;
|
|
207
|
+
abort: () => void;
|
|
208
|
+
clearError: () => void;
|
|
172
209
|
includeHistory: boolean;
|
|
173
210
|
setIncludeHistory: React.Dispatch<React.SetStateAction<boolean>>;
|
|
174
211
|
};
|
|
175
|
-
type UseChatOptions = {
|
|
176
|
-
transport: ChatTransport
|
|
212
|
+
type UseChatOptions<TContext = DefaultChatContext> = {
|
|
213
|
+
transport: ChatTransport<TContext>;
|
|
177
214
|
storage?: ChatStorage<ChatMsg> | undefined;
|
|
178
215
|
createId?: (() => string) | undefined;
|
|
216
|
+
context?: TContext | undefined;
|
|
217
|
+
prepareContextMessages?: PrepareContextMessages<TContext> | undefined;
|
|
218
|
+
toHumanInputPiece?: ToHumanInputPiece | undefined;
|
|
179
219
|
};
|
|
180
|
-
declare function useChat(options: UseChatOptions): UseChatValue;
|
|
220
|
+
declare function useChat<TContext = DefaultChatContext>(options: UseChatOptions<TContext>): UseChatValue;
|
|
181
221
|
|
|
182
222
|
declare function useChatStreamDebug(): {
|
|
183
223
|
streamDebug: ({
|
|
@@ -432,4 +472,4 @@ declare function useChatStreamDebug(): {
|
|
|
432
472
|
};
|
|
433
473
|
};
|
|
434
474
|
|
|
435
|
-
export { type BrowserChatStorageKeys, type BrowserChatStorageOptions, type ChatMsg, type ChatStorage, type ChatTransport, type ChatTransportChunkHandler, type ChatTransportChunkSource, type ChatTransportContext, type ContentPiece, type CreateRouteChatTransportArgs, type HumanInputPiece, type LiveChatErrorPiece, type LiveChatPiece, type LiveChatStructuredPiece, type LiveChatTextPiece, type OutgoingChatMessage, type PersistedChatState, type StructuredData, type StructuredStreamItem, type UseChatOptions, type UseChatValue, type UseStructuredStreamsOptions, type UseStructuredStreamsResult, buildAssistantMessage, createBrowserChatStorage, createChatTransport, createLiveChatPieces, createRouteChatTransport, toHumanInputPiece, useChat, useChatStreamDebug, useStructuredStreams };
|
|
475
|
+
export { type BrowserChatStorageKeys, type BrowserChatStorageOptions, type ChatMsg, type ChatStorage, type ChatTransport, type ChatTransportChunkHandler, type ChatTransportChunkSource, type ChatTransportContext, type ContentPiece, type CreateRouteChatTransportArgs, type HumanInputPiece, type LiveChatErrorPiece, type LiveChatPiece, type LiveChatStructuredPiece, type LiveChatTextPiece, type OutgoingChatMessage, type PersistedChatState, type PrepareContextMessages, type PrepareContextMessagesArgs, type RouteChatRequestBody, type StructuredData, type StructuredStreamItem, type ToHumanInputPiece, type UseChatOptions, type UseChatValue, type UseStructuredStreamsOptions, type UseStructuredStreamsResult, buildAssistantMessage, createBrowserChatStorage, createChatTransport, createLiveChatPieces, createRouteChatTransport, toHumanInputPiece, useChat, useChatStreamDebug, useStructuredStreams };
|
package/dist/index.js
CHANGED
|
@@ -133,12 +133,19 @@ function createChatTransport(args) {
|
|
|
133
133
|
function createRouteChatTransport(args) {
|
|
134
134
|
return createChatTransport({
|
|
135
135
|
stream: (context) => {
|
|
136
|
+
const body = args.createBody?.(context) ?? {
|
|
137
|
+
sessionId: context.sessionId,
|
|
138
|
+
workflowId: context.workflowId,
|
|
139
|
+
messages: context.messages,
|
|
140
|
+
context: context.context
|
|
141
|
+
};
|
|
136
142
|
const routeArgs = {
|
|
137
143
|
endpoint: args.endpoint,
|
|
138
|
-
body
|
|
144
|
+
body,
|
|
139
145
|
...args.method ? { method: args.method } : {},
|
|
140
146
|
...args.headers ? { headers: args.headers } : {},
|
|
141
|
-
...args.fetchImpl ? { fetchImpl: args.fetchImpl } : {}
|
|
147
|
+
...args.fetchImpl ? { fetchImpl: args.fetchImpl } : {},
|
|
148
|
+
...context.signal ? { signal: context.signal } : {}
|
|
142
149
|
};
|
|
143
150
|
return (0, import_browser.streamFromRoute)({
|
|
144
151
|
...routeArgs
|
|
@@ -189,10 +196,10 @@ function createLiveChatPieces(args) {
|
|
|
189
196
|
return `${op}:${node}`;
|
|
190
197
|
}
|
|
191
198
|
}
|
|
192
|
-
return fallbackNode
|
|
199
|
+
return fallbackNode && fallbackNode.length > 0 ? fallbackNode : "__unknown__";
|
|
193
200
|
};
|
|
194
201
|
const ensureTextPiece = (key) => {
|
|
195
|
-
const pieceKey = `text:${key
|
|
202
|
+
const pieceKey = `text:${key}`;
|
|
196
203
|
const existingIndex = findPieceIndex(pieceKey);
|
|
197
204
|
if (existingIndex >= 0) {
|
|
198
205
|
return keyedPieces[existingIndex]?.piece;
|
|
@@ -270,11 +277,29 @@ function createLiveChatPieces(args) {
|
|
|
270
277
|
}
|
|
271
278
|
|
|
272
279
|
// src/to-human-input-piece.ts
|
|
280
|
+
var isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
281
|
+
var nonEmptyString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
|
|
282
|
+
var firstNonEmptyString = (...values) => {
|
|
283
|
+
for (const value of values) {
|
|
284
|
+
const result = nonEmptyString(value);
|
|
285
|
+
if (result) return result;
|
|
286
|
+
}
|
|
287
|
+
return void 0;
|
|
288
|
+
};
|
|
273
289
|
function toHumanInputPiece(args) {
|
|
274
290
|
const hi = args.chunk;
|
|
275
291
|
const input = hi.input ?? {};
|
|
276
292
|
const kind = input.kind || (input.multiple ? "multi-choice" : "choice");
|
|
277
293
|
const isText = kind === "text";
|
|
294
|
+
const schemaId = firstNonEmptyString(hi.schemaId, input.schemaId);
|
|
295
|
+
const schemaVersion = firstNonEmptyString(
|
|
296
|
+
hi.schemaVersion,
|
|
297
|
+
input.schemaVersion
|
|
298
|
+
);
|
|
299
|
+
const interruptId = firstNonEmptyString(hi.id, input.id);
|
|
300
|
+
const inputMeta = isRecord(input.meta) ? input.meta : void 0;
|
|
301
|
+
const chunkMeta = isRecord(hi.meta) ? hi.meta : void 0;
|
|
302
|
+
const meta = inputMeta || chunkMeta ? { ...inputMeta ?? {}, ...chunkMeta ?? {} } : void 0;
|
|
278
303
|
const question = isText ? input.question : typeof input.question === "string" ? input.question : "Please choose";
|
|
279
304
|
const optionsSrc = Array.isArray(input.options) ? input.options : [];
|
|
280
305
|
const optionsArr = optionsSrc.map((option) => ({
|
|
@@ -290,7 +315,11 @@ function toHumanInputPiece(args) {
|
|
|
290
315
|
kind,
|
|
291
316
|
...question !== void 0 ? { question } : {},
|
|
292
317
|
multiple: Boolean(input.multiple),
|
|
293
|
-
options: optionsArr
|
|
318
|
+
options: optionsArr,
|
|
319
|
+
...schemaId ? { schemaId } : {},
|
|
320
|
+
...schemaVersion ? { schemaVersion } : {},
|
|
321
|
+
...interruptId ? { interruptId } : {},
|
|
322
|
+
...meta ? { meta } : {}
|
|
294
323
|
};
|
|
295
324
|
}
|
|
296
325
|
|
|
@@ -433,21 +462,17 @@ var defaultCreateId2 = () => {
|
|
|
433
462
|
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
434
463
|
};
|
|
435
464
|
var defaultStorage = createBrowserChatStorage();
|
|
436
|
-
function
|
|
437
|
-
const ref = (0, import_react3.useRef)(
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
return ref.current;
|
|
465
|
+
function useLatestRef(value) {
|
|
466
|
+
const ref = (0, import_react3.useRef)(value);
|
|
467
|
+
ref.current = value;
|
|
468
|
+
return ref;
|
|
442
469
|
}
|
|
443
470
|
function findActiveTextInterrupt(args) {
|
|
444
471
|
const liveInterrupt = args.streamContentPieces.find(
|
|
445
472
|
(piece) => piece.type === "interrupt" && piece.kind === "text"
|
|
446
473
|
);
|
|
447
474
|
if (liveInterrupt) return liveInterrupt;
|
|
448
|
-
for (
|
|
449
|
-
const message = args.messages[i];
|
|
450
|
-
if (!message) continue;
|
|
475
|
+
for (const message of [...args.messages].reverse()) {
|
|
451
476
|
if (message.role !== "assistant" || !message.contentPieces) continue;
|
|
452
477
|
const messageInterrupt = [...message.contentPieces].reverse().find(
|
|
453
478
|
(piece) => piece.type === "interrupt" && piece.kind === "text"
|
|
@@ -457,23 +482,31 @@ function findActiveTextInterrupt(args) {
|
|
|
457
482
|
return void 0;
|
|
458
483
|
}
|
|
459
484
|
function useChat(options) {
|
|
460
|
-
const
|
|
461
|
-
const
|
|
462
|
-
const
|
|
485
|
+
const resolvedStorage = options.storage ?? defaultStorage;
|
|
486
|
+
const transportRef = useLatestRef(options.transport);
|
|
487
|
+
const storageRef = useLatestRef(resolvedStorage);
|
|
488
|
+
const toHumanInputPieceRef = useLatestRef(
|
|
489
|
+
options.toHumanInputPiece ?? toHumanInputPiece
|
|
490
|
+
);
|
|
491
|
+
const requestContext = options.context ?? {};
|
|
492
|
+
const createId = (0, import_react3.useRef)(options.createId ?? defaultCreateId2).current;
|
|
463
493
|
const [messages, setMessages] = (0, import_react3.useState)([]);
|
|
464
494
|
const [isStreaming, setIsStreaming] = (0, import_react3.useState)(false);
|
|
495
|
+
const [error, setError] = (0, import_react3.useState)(null);
|
|
465
496
|
const [streamContentPieces, setStreamContentPieces] = (0, import_react3.useState)([]);
|
|
466
497
|
const [lastAssistantId, setLastAssistantId] = (0, import_react3.useState)(null);
|
|
467
498
|
const [includeHistory, setIncludeHistory] = (0, import_react3.useState)(true);
|
|
468
499
|
const [workflowId, setWorkflowId] = (0, import_react3.useState)("");
|
|
469
500
|
const [sessionId, setSessionId] = (0, import_react3.useState)(null);
|
|
470
501
|
const [didHydrateStorage, setDidHydrateStorage] = (0, import_react3.useState)(false);
|
|
502
|
+
const abortControllerRef = (0, import_react3.useRef)(null);
|
|
471
503
|
const { streamDebug, clearStreamDebug, createRecorder } = useChatStreamDebug();
|
|
472
504
|
const { applyStreamChunk, clear: clearStructuredStreams } = useStructuredStreams({
|
|
473
505
|
createId
|
|
474
506
|
});
|
|
475
507
|
(0, import_react3.useEffect)(() => {
|
|
476
508
|
let cancelled = false;
|
|
509
|
+
const storage = resolvedStorage;
|
|
477
510
|
void Promise.resolve(storage.load()).then((storedState) => {
|
|
478
511
|
if (cancelled) return;
|
|
479
512
|
if (typeof storedState.sessionId === "string") {
|
|
@@ -493,9 +526,10 @@ function useChat(options) {
|
|
|
493
526
|
return () => {
|
|
494
527
|
cancelled = true;
|
|
495
528
|
};
|
|
496
|
-
}, [
|
|
529
|
+
}, [resolvedStorage]);
|
|
497
530
|
(0, import_react3.useEffect)(() => {
|
|
498
531
|
if (!didHydrateStorage) return;
|
|
532
|
+
const storage = storageRef.current;
|
|
499
533
|
void storage.save({
|
|
500
534
|
sessionId,
|
|
501
535
|
workflowId,
|
|
@@ -507,16 +541,38 @@ function useChat(options) {
|
|
|
507
541
|
includeHistory,
|
|
508
542
|
messages,
|
|
509
543
|
sessionId,
|
|
510
|
-
|
|
544
|
+
storageRef,
|
|
511
545
|
workflowId
|
|
512
546
|
]);
|
|
513
|
-
const
|
|
547
|
+
const clearActiveStream = () => {
|
|
548
|
+
abortControllerRef.current?.abort();
|
|
549
|
+
abortControllerRef.current = null;
|
|
550
|
+
setIsStreaming(false);
|
|
514
551
|
setMessages([]);
|
|
515
552
|
setStreamContentPieces([]);
|
|
516
553
|
clearStreamDebug();
|
|
517
554
|
clearStructuredStreams();
|
|
518
555
|
setLastAssistantId(null);
|
|
519
|
-
|
|
556
|
+
};
|
|
557
|
+
const clearMessages = () => {
|
|
558
|
+
clearActiveStream();
|
|
559
|
+
void storageRef.current.clearMessages();
|
|
560
|
+
};
|
|
561
|
+
const resetSession = () => {
|
|
562
|
+
setSessionId(null);
|
|
563
|
+
};
|
|
564
|
+
const resetChat = () => {
|
|
565
|
+
clearActiveStream();
|
|
566
|
+
resetSession();
|
|
567
|
+
setError(null);
|
|
568
|
+
void storageRef.current.clearMessages();
|
|
569
|
+
};
|
|
570
|
+
const clearChat = resetChat;
|
|
571
|
+
const clearError = () => {
|
|
572
|
+
setError(null);
|
|
573
|
+
};
|
|
574
|
+
const abort = () => {
|
|
575
|
+
abortControllerRef.current?.abort();
|
|
520
576
|
};
|
|
521
577
|
const persistSessionId = (sid) => {
|
|
522
578
|
setSessionId(sid);
|
|
@@ -526,6 +582,20 @@ function useChat(options) {
|
|
|
526
582
|
if (!sessionId) persistSessionId(sid);
|
|
527
583
|
return sid;
|
|
528
584
|
};
|
|
585
|
+
const toOutgoingHistoryMessage = (message) => ({
|
|
586
|
+
role: message.role,
|
|
587
|
+
content: message.content
|
|
588
|
+
});
|
|
589
|
+
const prepareMessagesToSend = async (args) => {
|
|
590
|
+
const prepared = options.prepareContextMessages ? await options.prepareContextMessages({
|
|
591
|
+
messages,
|
|
592
|
+
sessionId: args.sid,
|
|
593
|
+
workflowId,
|
|
594
|
+
reason: args.reason,
|
|
595
|
+
context: requestContext
|
|
596
|
+
}) : includeHistory ? messages.map(toOutgoingHistoryMessage) : [];
|
|
597
|
+
return [...prepared, args.outgoingMessage];
|
|
598
|
+
};
|
|
529
599
|
const streamAssistantResponse = async (args) => {
|
|
530
600
|
clearStructuredStreams();
|
|
531
601
|
setStreamContentPieces([]);
|
|
@@ -536,15 +606,17 @@ function useChat(options) {
|
|
|
536
606
|
structuredStreams: {
|
|
537
607
|
applyStreamChunk
|
|
538
608
|
},
|
|
539
|
-
toHumanInputPiece: (chunk) =>
|
|
609
|
+
toHumanInputPiece: (chunk) => toHumanInputPieceRef.current({
|
|
540
610
|
chunk,
|
|
541
611
|
createId
|
|
542
612
|
})
|
|
543
613
|
});
|
|
544
|
-
await
|
|
614
|
+
await transportRef.current.stream({
|
|
545
615
|
sessionId: args.sid,
|
|
546
616
|
workflowId,
|
|
547
617
|
messages: args.messagesToSend,
|
|
618
|
+
context: requestContext,
|
|
619
|
+
signal: args.signal,
|
|
548
620
|
onChunk: (chunk) => {
|
|
549
621
|
debug.push(chunk);
|
|
550
622
|
if (chunk.type === "session") {
|
|
@@ -553,11 +625,15 @@ function useChat(options) {
|
|
|
553
625
|
}
|
|
554
626
|
return;
|
|
555
627
|
}
|
|
628
|
+
if (chunk.type === "error") {
|
|
629
|
+
setError(new Error(chunk.message));
|
|
630
|
+
}
|
|
556
631
|
return pieces.processChunk(chunk, {
|
|
557
632
|
openDebugOnInterrupt: args.openDebugOnInterrupt
|
|
558
633
|
});
|
|
559
634
|
}
|
|
560
635
|
});
|
|
636
|
+
if (args.signal.aborted) return;
|
|
561
637
|
const assistant = buildAssistantMessage({
|
|
562
638
|
createId,
|
|
563
639
|
pieces: pieces.getPieces(),
|
|
@@ -569,6 +645,11 @@ function useChat(options) {
|
|
|
569
645
|
setStreamContentPieces([]);
|
|
570
646
|
clearStreamDebug();
|
|
571
647
|
};
|
|
648
|
+
const isAbortError = (value, signal) => {
|
|
649
|
+
if (signal.aborted) return true;
|
|
650
|
+
if (!(value instanceof Error)) return false;
|
|
651
|
+
return value.name === "AbortError";
|
|
652
|
+
};
|
|
572
653
|
const respondToHumanInput = async ({
|
|
573
654
|
resumeToken,
|
|
574
655
|
requestId,
|
|
@@ -576,6 +657,7 @@ function useChat(options) {
|
|
|
576
657
|
text
|
|
577
658
|
}) => {
|
|
578
659
|
if (isStreaming) return;
|
|
660
|
+
setError(null);
|
|
579
661
|
const label = typeof text === "string" && text.length > 0 ? text : selected.length > 0 ? selected.length === 1 ? selected[0] : selected.join(", ") : "(selection)";
|
|
580
662
|
const userId = createId();
|
|
581
663
|
const userMsg = { id: userId, role: "user", content: label };
|
|
@@ -584,6 +666,8 @@ function useChat(options) {
|
|
|
584
666
|
setStreamContentPieces([]);
|
|
585
667
|
clearStreamDebug();
|
|
586
668
|
clearStructuredStreams();
|
|
669
|
+
const abortController = new AbortController();
|
|
670
|
+
abortControllerRef.current = abortController;
|
|
587
671
|
try {
|
|
588
672
|
const resumePayload = {
|
|
589
673
|
resume: { token: resumeToken, requestId, selected }
|
|
@@ -594,23 +678,43 @@ function useChat(options) {
|
|
|
594
678
|
metadata: resumePayload
|
|
595
679
|
};
|
|
596
680
|
const sid = resolveSessionId();
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
681
|
+
const messagesToSend = await prepareMessagesToSend({
|
|
682
|
+
sid,
|
|
683
|
+
outgoingMessage: outgoing,
|
|
684
|
+
reason: "resume"
|
|
685
|
+
});
|
|
602
686
|
await streamAssistantResponse({
|
|
603
687
|
sid,
|
|
604
688
|
messagesToSend,
|
|
605
|
-
debugLabel: "runChat (resume)"
|
|
689
|
+
debugLabel: "runChat (resume)",
|
|
690
|
+
signal: abortController.signal
|
|
606
691
|
});
|
|
692
|
+
} catch (err) {
|
|
693
|
+
if (isAbortError(err, abortController.signal)) return;
|
|
694
|
+
const nextError = err instanceof Error ? err : new Error(String(err));
|
|
695
|
+
setError(nextError);
|
|
696
|
+
throw err;
|
|
607
697
|
} finally {
|
|
698
|
+
if (abortControllerRef.current === abortController) {
|
|
699
|
+
abortControllerRef.current = null;
|
|
700
|
+
}
|
|
608
701
|
setIsStreaming(false);
|
|
609
702
|
}
|
|
610
703
|
};
|
|
704
|
+
const respondToInterrupt = async (piece, response) => {
|
|
705
|
+
const text = response?.text;
|
|
706
|
+
const selected = response?.selected ?? (typeof text === "string" && text.length > 0 ? [text] : []);
|
|
707
|
+
return respondToHumanInput({
|
|
708
|
+
resumeToken: piece.resumeToken,
|
|
709
|
+
requestId: piece.requestId,
|
|
710
|
+
selected,
|
|
711
|
+
...text !== void 0 ? { text } : {}
|
|
712
|
+
});
|
|
713
|
+
};
|
|
611
714
|
const send = async (text) => {
|
|
612
715
|
const content = text.trim();
|
|
613
716
|
if (!content || isStreaming) return;
|
|
717
|
+
setError(null);
|
|
614
718
|
const activeTextInterrupt = findActiveTextInterrupt({
|
|
615
719
|
messages,
|
|
616
720
|
streamContentPieces
|
|
@@ -631,26 +735,39 @@ function useChat(options) {
|
|
|
631
735
|
setStreamContentPieces([]);
|
|
632
736
|
clearStreamDebug();
|
|
633
737
|
clearStructuredStreams();
|
|
738
|
+
const abortController = new AbortController();
|
|
739
|
+
abortControllerRef.current = abortController;
|
|
634
740
|
try {
|
|
635
741
|
const sid = resolveSessionId();
|
|
636
|
-
const
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
742
|
+
const messagesToSend = await prepareMessagesToSend({
|
|
743
|
+
sid,
|
|
744
|
+
outgoingMessage: { role: "user", content },
|
|
745
|
+
reason: "send"
|
|
746
|
+
});
|
|
641
747
|
await streamAssistantResponse({
|
|
642
748
|
sid,
|
|
643
749
|
messagesToSend,
|
|
644
750
|
debugLabel: "runChat (send)",
|
|
645
|
-
openDebugOnInterrupt: true
|
|
751
|
+
openDebugOnInterrupt: true,
|
|
752
|
+
signal: abortController.signal
|
|
646
753
|
});
|
|
754
|
+
} catch (err) {
|
|
755
|
+
if (isAbortError(err, abortController.signal)) return;
|
|
756
|
+
const nextError = err instanceof Error ? err : new Error(String(err));
|
|
757
|
+
setError(nextError);
|
|
758
|
+
throw err;
|
|
647
759
|
} finally {
|
|
760
|
+
if (abortControllerRef.current === abortController) {
|
|
761
|
+
abortControllerRef.current = null;
|
|
762
|
+
}
|
|
648
763
|
setIsStreaming(false);
|
|
649
764
|
}
|
|
650
765
|
};
|
|
651
766
|
return {
|
|
652
767
|
messages,
|
|
653
768
|
isStreaming,
|
|
769
|
+
canAbort: isStreaming,
|
|
770
|
+
error,
|
|
654
771
|
streamContentPieces,
|
|
655
772
|
streamDebug,
|
|
656
773
|
lastAssistantId,
|
|
@@ -658,8 +775,14 @@ function useChat(options) {
|
|
|
658
775
|
setWorkflowId,
|
|
659
776
|
send,
|
|
660
777
|
respondToHumanInput,
|
|
778
|
+
respondToInterrupt,
|
|
661
779
|
setMessages,
|
|
780
|
+
clearMessages,
|
|
781
|
+
resetSession,
|
|
782
|
+
resetChat,
|
|
662
783
|
clearChat,
|
|
784
|
+
abort,
|
|
785
|
+
clearError,
|
|
663
786
|
includeHistory,
|
|
664
787
|
setIncludeHistory
|
|
665
788
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/build-assistant-message.ts","../src/chat-storage.ts","../src/chat-transport.ts","../src/create-live-chat-pieces.ts","../src/to-human-input-piece.ts","../src/use-chat.ts","../src/use-chat-stream-debug.ts","../src/use-structured-streams.ts"],"sourcesContent":["export * from \"./build-assistant-message\";\nexport * from \"./chat-storage\";\nexport * from \"./chat-transport\";\nexport * from \"./chat-types\";\nexport * from \"./create-live-chat-pieces\";\nexport * from \"./to-human-input-piece\";\nexport * from \"./use-chat\";\nexport * from \"./use-chat-stream-debug\";\nexport * from \"./use-structured-streams\";\n","import type { StreamChunk } from \"@kortyx/stream/browser\";\nimport type { ChatMsg, ContentPiece } from \"./chat-types\";\n\nexport function buildAssistantMessage(args: {\n createId: () => string;\n pieces: ContentPiece[];\n debug: StreamChunk[];\n}): ChatMsg {\n const plainTextContent = args.pieces\n .filter(\n (piece): piece is Extract<ContentPiece, { type: \"text\" }> =>\n piece.type === \"text\",\n )\n .map((piece) => piece.content)\n .join(\"\");\n\n const base = {\n id: args.createId(),\n role: \"assistant\" as const,\n content: plainTextContent,\n debug: args.debug,\n };\n\n return args.pieces.length > 0\n ? { ...base, contentPieces: args.pieces }\n : base;\n}\n","type MaybePromise<T> = T | Promise<T>;\ntype StorageLike = {\n getItem: (key: string) => string | null;\n setItem: (key: string, value: string) => void;\n removeItem: (key: string) => void;\n};\n\nexport type PersistedChatState<TMessage> = {\n sessionId: string | null;\n workflowId: string;\n includeHistory: boolean;\n messages: TMessage[];\n};\n\nexport type ChatStorage<TMessage> = {\n load: () => MaybePromise<Partial<PersistedChatState<TMessage>>>;\n save: (state: PersistedChatState<TMessage>) => MaybePromise<void>;\n clearMessages: () => MaybePromise<void>;\n};\n\nexport type BrowserChatStorageKeys = {\n messages: string;\n includeHistory: string;\n sessionId: string;\n workflowId: string;\n};\n\nexport type BrowserChatStorageOptions<TMessage> = {\n parseMessage: (value: unknown) => TMessage | null;\n serializeMessage: (message: TMessage) => unknown;\n maxMessages?: number | undefined;\n keys?: Partial<BrowserChatStorageKeys> | undefined;\n};\n\nconst DEFAULT_STORAGE_KEYS: BrowserChatStorageKeys = {\n messages: \"chat.messages.v1\",\n includeHistory: \"chat.includeHistory.v1\",\n sessionId: \"chat.sessionId\",\n workflowId: \"chat.workflowId\",\n};\n\nconst getLocalStorage = (): StorageLike | undefined => {\n const candidate = globalThis as { localStorage?: StorageLike };\n return candidate.localStorage;\n};\n\nconst defaultParseMessage = <TMessage>(value: unknown): TMessage | null =>\n value as TMessage;\n\nconst defaultSerializeMessage = <TMessage>(message: TMessage): unknown =>\n message;\n\n// TODO: Make remote/custom persistence more robust before freezing this API:\n// - add debouncing so repeated message updates do not trigger a full save each time\n// - surface save error handling/retry hooks instead of silently ignoring failures\n// - define a conflict/version strategy for multi-tab and server-synced storage\n// - support partial saves instead of rewriting the full persisted state on each change\nexport function createBrowserChatStorage<TMessage = unknown>(\n options?: BrowserChatStorageOptions<TMessage>,\n): ChatStorage<TMessage> {\n const keys = {\n ...DEFAULT_STORAGE_KEYS,\n ...(options?.keys ?? {}),\n };\n const maxMessages = options?.maxMessages ?? 60;\n const parseMessage = options?.parseMessage ?? defaultParseMessage<TMessage>;\n const serializeMessage =\n options?.serializeMessage ?? defaultSerializeMessage<TMessage>;\n\n return {\n load() {\n try {\n const storage = getLocalStorage();\n if (!storage) {\n return {};\n }\n\n const sessionId = storage.getItem(keys.sessionId);\n const workflowId = storage.getItem(keys.workflowId) ?? \"\";\n const includeHistoryRaw = storage.getItem(keys.includeHistory);\n const rawMessages = storage.getItem(keys.messages);\n const parsedMessages = rawMessages\n ? (JSON.parse(rawMessages) as unknown)\n : [];\n const messages = Array.isArray(parsedMessages)\n ? parsedMessages\n .map((item) => parseMessage(item))\n .filter((message): message is TMessage => message !== null)\n : [];\n\n return {\n sessionId,\n workflowId,\n includeHistory: includeHistoryRaw !== \"0\",\n messages,\n };\n } catch {\n return {};\n }\n },\n\n save(state) {\n try {\n const storage = getLocalStorage();\n if (!storage) return;\n\n if (state.sessionId) {\n storage.setItem(keys.sessionId, state.sessionId);\n } else {\n storage.removeItem(keys.sessionId);\n }\n\n storage.setItem(keys.workflowId, state.workflowId);\n storage.setItem(keys.includeHistory, state.includeHistory ? \"1\" : \"0\");\n\n const trimmedMessages = state.messages\n .slice(-maxMessages)\n .map((message) => serializeMessage(message));\n\n storage.setItem(keys.messages, JSON.stringify(trimmedMessages));\n } catch {}\n },\n\n clearMessages() {\n try {\n const storage = getLocalStorage();\n if (!storage) return;\n storage.removeItem(keys.messages);\n } catch {}\n },\n };\n}\n","import type { StreamChunk, StreamFromRouteArgs } from \"@kortyx/stream/browser\";\nimport { streamFromRoute } from \"@kortyx/stream/browser\";\n\ntype MaybePromise<T> = T | Promise<T>;\n\nexport type OutgoingChatMessage = {\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n metadata?: Record<string, unknown>;\n};\n\nexport type ChatTransportContext = {\n sessionId: string;\n workflowId: string;\n messages: OutgoingChatMessage[];\n};\n\nexport type ChatTransportChunkHandler = (\n chunk: StreamChunk,\n) => undefined | boolean | Promise<boolean | undefined>;\n\nexport type ChatTransport = {\n stream(\n args: ChatTransportContext & { onChunk: ChatTransportChunkHandler },\n ): Promise<void>;\n};\n\nexport type ChatTransportChunkSource =\n | Iterable<StreamChunk>\n | AsyncIterable<StreamChunk>;\n\nexport function createChatTransport(args: {\n stream: (\n context: ChatTransportContext,\n ) => MaybePromise<ChatTransportChunkSource>;\n}): ChatTransport {\n return {\n async stream({ onChunk, ...context }) {\n for await (const chunk of await args.stream(context)) {\n const shouldContinue = await onChunk(chunk);\n if (chunk.type === \"done\" || shouldContinue === false) {\n break;\n }\n }\n },\n };\n}\n\nexport type CreateRouteChatTransportArgs<TBody> = Omit<\n StreamFromRouteArgs<TBody>,\n \"body\"\n> & {\n getBody: (context: ChatTransportContext) => TBody;\n};\n\nexport function createRouteChatTransport<TBody>(\n args: CreateRouteChatTransportArgs<TBody>,\n): ChatTransport {\n return createChatTransport({\n stream: (context) => {\n const routeArgs: StreamFromRouteArgs<TBody> = {\n endpoint: args.endpoint,\n body: args.getBody(context),\n ...(args.method ? { method: args.method } : {}),\n ...(args.headers ? { headers: args.headers } : {}),\n ...(args.fetchImpl ? { fetchImpl: args.fetchImpl } : {}),\n };\n\n return streamFromRoute({\n ...routeArgs,\n });\n },\n });\n}\n","import type {\n StreamChunk,\n StructuredStreamState,\n} from \"@kortyx/stream/browser\";\nimport type { StructuredStreamItem } from \"./use-structured-streams\";\n\nexport type LiveChatTextPiece = {\n id: string;\n type: \"text\";\n content: string;\n};\n\nexport type LiveChatStructuredPiece<TStructuredData = unknown> = {\n id: string;\n type: \"structured\";\n data: StructuredStreamState<TStructuredData>;\n};\n\nexport type LiveChatErrorPiece = {\n id: string;\n type: \"error\";\n content: string;\n};\n\ntype LiveChatCustomPiece = {\n id: string;\n type: string;\n};\n\nexport type LiveChatPiece<\n TStructuredData = unknown,\n TCustomPiece extends LiveChatCustomPiece = never,\n> =\n | LiveChatTextPiece\n | LiveChatStructuredPiece<TStructuredData>\n | LiveChatErrorPiece\n | TCustomPiece;\n\ntype StructuredStreamsController<TStructuredData> = {\n applyStreamChunk: (\n chunk: StreamChunk,\n ) => StructuredStreamItem<TStructuredData> | undefined;\n};\n\nconst isNonEmptyTextPiece = <\n TStructuredData,\n TCustomPiece extends LiveChatCustomPiece,\n>(\n piece: LiveChatPiece<TStructuredData, TCustomPiece>,\n) =>\n piece.type !== \"text\" ||\n (\"content\" in piece &&\n typeof piece.content === \"string\" &&\n piece.content.length > 0);\n\nexport function createLiveChatPieces<\n TStructuredData = unknown,\n TCustomPiece extends LiveChatCustomPiece = never,\n>(args: {\n createId: () => string;\n onChange: (pieces: LiveChatPiece<TStructuredData, TCustomPiece>[]) => void;\n structuredStreams: StructuredStreamsController<TStructuredData>;\n toHumanInputPiece: (chunk: StreamChunk) => TCustomPiece;\n}) {\n const keyedPieces: Array<{\n key: string;\n piece: LiveChatPiece<TStructuredData, TCustomPiece>;\n }> = [];\n let sawTextDelta = false;\n\n const emit = () => {\n args.onChange(\n keyedPieces\n .map((entry) => entry.piece)\n .filter((piece) => isNonEmptyTextPiece(piece)),\n );\n };\n\n const findPieceIndex = (key: string) =>\n keyedPieces.findIndex((entry) => entry.key === key);\n\n const upsertPiece = (\n key: string,\n piece: LiveChatPiece<TStructuredData, TCustomPiece>,\n ) => {\n const existingIndex = findPieceIndex(key);\n if (existingIndex >= 0) {\n keyedPieces[existingIndex] = { key, piece };\n } else {\n keyedPieces.push({ key, piece });\n }\n emit();\n };\n\n const pushPiece = (piece: LiveChatPiece<TStructuredData, TCustomPiece>) => {\n keyedPieces.push({\n key: `${piece.type}:${piece.id}`,\n piece,\n });\n emit();\n };\n\n const resolveTextStreamKey = (\n chunk: StreamChunk,\n fallbackNode?: string,\n ): string => {\n if (\"segmentId\" in chunk && typeof chunk.segmentId === \"string\") {\n const seg = chunk.segmentId.trim();\n if (seg.length > 0) {\n const op =\n \"opId\" in chunk && typeof chunk.opId === \"string\"\n ? chunk.opId.trim()\n : \"\";\n return op.length > 0 ? `${op}:${seg}` : seg;\n }\n }\n\n if (\"opId\" in chunk && typeof chunk.opId === \"string\") {\n const op = chunk.opId.trim();\n if (op.length > 0) {\n const node =\n (\"node\" in chunk && typeof chunk.node === \"string\"\n ? chunk.node\n : fallbackNode) ?? \"__unknown__\";\n return `${op}:${node}`;\n }\n }\n\n return fallbackNode ?? \"__unknown__\";\n };\n\n const ensureTextPiece = (key: string): LiveChatTextPiece => {\n const pieceKey = `text:${key || \"__unknown__\"}`;\n const existingIndex = findPieceIndex(pieceKey);\n if (existingIndex >= 0) {\n return keyedPieces[existingIndex]?.piece as LiveChatTextPiece;\n }\n\n const nextPiece: LiveChatTextPiece = {\n id: args.createId(),\n type: \"text\",\n content: \"\",\n };\n keyedPieces.push({ key: pieceKey, piece: nextPiece });\n return nextPiece;\n };\n\n const processChunk = (\n chunk: StreamChunk,\n _options?: { openDebugOnInterrupt?: boolean | undefined },\n ): boolean => {\n if (chunk.type === \"text-start\") {\n ensureTextPiece(resolveTextStreamKey(chunk, chunk.node));\n emit();\n return true;\n }\n\n if (chunk.type === \"text-delta\") {\n sawTextDelta = true;\n const streamKey = resolveTextStreamKey(chunk, chunk.node);\n const current = ensureTextPiece(streamKey);\n upsertPiece(`text:${streamKey}`, {\n ...current,\n content: current.content + chunk.delta,\n });\n return true;\n }\n\n if (chunk.type === \"text-end\") {\n emit();\n return true;\n }\n\n if (chunk.type === \"structured-data\") {\n const nextItem = args.structuredStreams.applyStreamChunk(chunk);\n if (nextItem) {\n upsertPiece(`structured:${nextItem.streamId}`, {\n id: nextItem.id,\n type: \"structured\",\n data: nextItem.state,\n });\n }\n return true;\n }\n\n if (chunk.type === \"interrupt\") {\n pushPiece(args.toHumanInputPiece(chunk));\n return true;\n }\n\n if (chunk.type === \"message\") {\n if (!sawTextDelta) {\n pushPiece({\n id: args.createId(),\n type: \"text\",\n content: chunk.content ?? \"\",\n });\n }\n return true;\n }\n\n if (chunk.type === \"error\") {\n pushPiece({\n id: args.createId(),\n type: \"error\",\n content: chunk.message ?? \"An error occurred\",\n });\n return true;\n }\n\n if (chunk.type === \"done\") {\n return false;\n }\n\n return true;\n };\n\n return {\n processChunk,\n getPieces: () =>\n keyedPieces\n .map((entry) => entry.piece)\n .filter((piece) => isNonEmptyTextPiece(piece)),\n };\n}\n","import type { StreamChunk } from \"@kortyx/stream/browser\";\nimport type { HumanInputPiece } from \"./chat-types\";\n\ninterface HumanInputStreamChunk {\n type: \"interrupt\";\n requestId: string | undefined;\n resumeToken: string | undefined;\n input?: {\n kind?: \"text\" | \"choice\" | \"multi-choice\";\n question?: string;\n multiple?: boolean;\n options?: Array<{\n id?: string | number;\n label?: string;\n description?: string;\n }>;\n };\n}\n\nexport function toHumanInputPiece(args: {\n chunk: StreamChunk;\n createId: () => string;\n}): HumanInputPiece {\n const hi = args.chunk as unknown as HumanInputStreamChunk;\n const input = hi.input ?? {};\n const kind = input.kind || (input.multiple ? \"multi-choice\" : \"choice\");\n const isText = kind === \"text\";\n\n const question = isText\n ? input.question\n : typeof input.question === \"string\"\n ? input.question\n : \"Please choose\";\n\n const optionsSrc = Array.isArray(input.options) ? input.options : [];\n const optionsArr: Array<{\n id: string;\n label: string;\n description?: string;\n }> = optionsSrc\n .map((option) => ({\n id: String(option.id ?? \"\"),\n label: String(option.label ?? \"\"),\n ...(typeof option.description === \"string\" && option.description\n ? { description: option.description }\n : {}),\n }))\n .filter((option) => option.id && option.label);\n\n return {\n id: args.createId(),\n type: \"interrupt\",\n resumeToken: String(hi.resumeToken ?? \"\"),\n requestId: String(hi.requestId ?? \"\"),\n kind,\n ...(question !== undefined ? { question } : {}),\n multiple: Boolean(input.multiple),\n options: optionsArr,\n };\n}\n","\"use client\";\n\nimport type { StreamChunk } from \"@kortyx/stream/browser\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { buildAssistantMessage } from \"./build-assistant-message\";\nimport { type ChatStorage, createBrowserChatStorage } from \"./chat-storage\";\nimport type { ChatTransport, OutgoingChatMessage } from \"./chat-transport\";\nimport type { ChatMsg, ContentPiece, HumanInputPiece } from \"./chat-types\";\nimport { createLiveChatPieces } from \"./create-live-chat-pieces\";\nimport { toHumanInputPiece } from \"./to-human-input-piece\";\nimport { useChatStreamDebug } from \"./use-chat-stream-debug\";\nimport { useStructuredStreams } from \"./use-structured-streams\";\n\nconst defaultCreateId = () => {\n try {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID();\n }\n } catch {}\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`;\n};\n\nexport type UseChatValue = {\n messages: ChatMsg[];\n isStreaming: boolean;\n streamContentPieces: ContentPiece[];\n streamDebug: StreamChunk[];\n lastAssistantId: string | null;\n workflowId: string;\n setWorkflowId: React.Dispatch<React.SetStateAction<string>>;\n send: (text: string) => Promise<void> | void;\n respondToHumanInput: (args: {\n resumeToken: string;\n requestId: string;\n selected: string[];\n text?: string;\n }) => Promise<void> | void;\n setMessages: React.Dispatch<React.SetStateAction<ChatMsg[]>>;\n clearChat: () => void;\n includeHistory: boolean;\n setIncludeHistory: React.Dispatch<React.SetStateAction<boolean>>;\n};\n\nexport type UseChatOptions = {\n transport: ChatTransport;\n storage?: ChatStorage<ChatMsg> | undefined;\n createId?: (() => string) | undefined;\n};\n\nconst defaultStorage = createBrowserChatStorage<ChatMsg>();\n\nfunction useInitialValue<T>(value: T): T {\n const ref = useRef<T | undefined>(undefined);\n\n if (ref.current === undefined) {\n ref.current = value;\n }\n\n return ref.current;\n}\n\nfunction findActiveTextInterrupt(args: {\n messages: ChatMsg[];\n streamContentPieces: ContentPiece[];\n}): HumanInputPiece | undefined {\n const liveInterrupt = args.streamContentPieces.find(\n (piece): piece is HumanInputPiece =>\n piece.type === \"interrupt\" && piece.kind === \"text\",\n );\n if (liveInterrupt) return liveInterrupt;\n\n for (let i = args.messages.length - 1; i >= 0; i -= 1) {\n const message = args.messages[i];\n if (!message) continue;\n if (message.role !== \"assistant\" || !message.contentPieces) continue;\n\n const messageInterrupt = [...message.contentPieces]\n .reverse()\n .find(\n (piece): piece is HumanInputPiece =>\n piece.type === \"interrupt\" && piece.kind === \"text\",\n );\n if (messageInterrupt) return messageInterrupt;\n }\n\n return undefined;\n}\n\nexport function useChat(options: UseChatOptions): UseChatValue {\n const transport = useInitialValue(options.transport);\n const storage = useInitialValue(options.storage ?? defaultStorage);\n const createId = useInitialValue(options.createId ?? defaultCreateId);\n\n const [messages, setMessages] = useState<ChatMsg[]>([]);\n const [isStreaming, setIsStreaming] = useState(false);\n const [streamContentPieces, setStreamContentPieces] = useState<\n ContentPiece[]\n >([]);\n const [lastAssistantId, setLastAssistantId] = useState<string | null>(null);\n const [includeHistory, setIncludeHistory] = useState(true);\n const [workflowId, setWorkflowId] = useState(\"\");\n const [sessionId, setSessionId] = useState<string | null>(null);\n const [didHydrateStorage, setDidHydrateStorage] = useState(false);\n const { streamDebug, clearStreamDebug, createRecorder } =\n useChatStreamDebug();\n const { applyStreamChunk, clear: clearStructuredStreams } =\n useStructuredStreams<Record<string, unknown>>({\n createId,\n });\n\n useEffect(() => {\n let cancelled = false;\n\n void Promise.resolve(storage.load()).then((storedState) => {\n if (cancelled) return;\n\n if (typeof storedState.sessionId === \"string\") {\n setSessionId(storedState.sessionId);\n }\n if (typeof storedState.workflowId === \"string\") {\n setWorkflowId(storedState.workflowId);\n }\n if (typeof storedState.includeHistory === \"boolean\") {\n setIncludeHistory(storedState.includeHistory);\n }\n if (\n Array.isArray(storedState.messages) &&\n storedState.messages.length > 0\n ) {\n setMessages(storedState.messages);\n }\n\n setDidHydrateStorage(true);\n });\n\n return () => {\n cancelled = true;\n };\n }, [storage]);\n\n useEffect(() => {\n if (!didHydrateStorage) return;\n\n void storage.save({\n sessionId,\n workflowId,\n includeHistory,\n messages,\n });\n }, [\n didHydrateStorage,\n includeHistory,\n messages,\n sessionId,\n storage,\n workflowId,\n ]);\n\n const clearChat = () => {\n setMessages([]);\n setStreamContentPieces([]);\n clearStreamDebug();\n clearStructuredStreams();\n setLastAssistantId(null);\n void storage.clearMessages();\n };\n\n const persistSessionId = (sid: string) => {\n setSessionId(sid);\n };\n\n const resolveSessionId = () => {\n const sid = sessionId ?? createId();\n if (!sessionId) persistSessionId(sid);\n return sid;\n };\n\n const streamAssistantResponse = async (args: {\n sid: string;\n messagesToSend: OutgoingChatMessage[];\n debugLabel: string;\n openDebugOnInterrupt?: boolean;\n }) => {\n clearStructuredStreams();\n setStreamContentPieces([]);\n\n const debug = createRecorder(`${args.debugLabel} sessionId=${args.sid}`);\n const pieces = createLiveChatPieces({\n createId,\n onChange: setStreamContentPieces,\n structuredStreams: {\n applyStreamChunk,\n },\n toHumanInputPiece: (chunk) =>\n toHumanInputPiece({\n chunk,\n createId,\n }),\n });\n\n await transport.stream({\n sessionId: args.sid,\n workflowId,\n messages: args.messagesToSend,\n onChunk: (chunk: StreamChunk) => {\n debug.push(chunk);\n\n if (chunk.type === \"session\") {\n if (typeof chunk.sessionId === \"string\") {\n persistSessionId(chunk.sessionId);\n }\n return;\n }\n\n return pieces.processChunk(chunk, {\n openDebugOnInterrupt: args.openDebugOnInterrupt,\n });\n },\n });\n\n const assistant = buildAssistantMessage({\n createId,\n pieces: pieces.getPieces(),\n debug: debug.getAll(),\n });\n setMessages((prev) => [...prev, assistant]);\n setLastAssistantId(assistant.id);\n clearStructuredStreams();\n setStreamContentPieces([]);\n clearStreamDebug();\n };\n\n const respondToHumanInput = async ({\n resumeToken,\n requestId,\n selected,\n text,\n }: {\n resumeToken: string;\n requestId: string;\n selected: string[];\n text?: string;\n }) => {\n if (isStreaming) return;\n const label: string =\n typeof text === \"string\" && text.length > 0\n ? text\n : selected.length > 0\n ? selected.length === 1\n ? (selected[0] as string)\n : selected.join(\", \")\n : \"(selection)\";\n const userId = createId();\n const userMsg: ChatMsg = { id: userId, role: \"user\", content: label };\n setMessages((prev) => [...prev, userMsg]);\n setIsStreaming(true);\n setStreamContentPieces([]);\n clearStreamDebug();\n clearStructuredStreams();\n\n try {\n const resumePayload: Record<string, unknown> = {\n resume: { token: resumeToken, requestId, selected },\n };\n const outgoing = {\n role: \"user\" as const,\n content: label,\n metadata: resumePayload,\n };\n const sid = resolveSessionId();\n const history = includeHistory\n ? messages.map((message) => ({\n role: message.role,\n content: message.content,\n }))\n : [];\n const messagesToSend = [...history, outgoing];\n\n await streamAssistantResponse({\n sid,\n messagesToSend,\n debugLabel: \"runChat (resume)\",\n });\n } finally {\n setIsStreaming(false);\n }\n };\n\n const send = async (text: string) => {\n const content = text.trim();\n if (!content || isStreaming) return;\n\n const activeTextInterrupt = findActiveTextInterrupt({\n messages,\n streamContentPieces,\n });\n\n if (activeTextInterrupt) {\n await respondToHumanInput({\n resumeToken: activeTextInterrupt.resumeToken,\n requestId: activeTextInterrupt.requestId,\n selected: [content],\n text: content,\n });\n return;\n }\n\n const userId = createId();\n const userMsg: ChatMsg = { id: userId, role: \"user\", content };\n setMessages((prev) => [...prev, userMsg]);\n\n setIsStreaming(true);\n setStreamContentPieces([]);\n clearStreamDebug();\n clearStructuredStreams();\n\n try {\n const sid = resolveSessionId();\n const history = includeHistory\n ? messages.map((message) => ({\n role: message.role,\n content: message.content,\n }))\n : [];\n const messagesToSend = [...history, { role: \"user\" as const, content }];\n await streamAssistantResponse({\n sid,\n messagesToSend,\n debugLabel: \"runChat (send)\",\n openDebugOnInterrupt: true,\n });\n } finally {\n setIsStreaming(false);\n }\n };\n\n return {\n messages,\n isStreaming,\n streamContentPieces,\n streamDebug,\n lastAssistantId,\n workflowId,\n setWorkflowId,\n send,\n respondToHumanInput,\n setMessages,\n clearChat,\n includeHistory,\n setIncludeHistory,\n };\n}\n","\"use client\";\n\nimport type { StreamChunk } from \"@kortyx/stream/browser\";\nimport { useCallback, useState } from \"react\";\n\ntype DebugChunk = StreamChunk & {\n _ts: number;\n _dt: number;\n _seq: number;\n};\n\nexport function useChatStreamDebug() {\n const [streamDebug, setStreamDebug] = useState<StreamChunk[]>([]);\n\n const clearStreamDebug = useCallback(() => {\n setStreamDebug([]);\n }, []);\n\n const createRecorder = useCallback((initialMessage: string) => {\n let seq = 0;\n let lastTs = 0;\n const accDebug: StreamChunk[] = [];\n\n const push = (chunk: StreamChunk) => {\n const ts = Date.now();\n const withMeta: DebugChunk = {\n ...(chunk as StreamChunk),\n _ts: ts,\n _dt: lastTs ? ts - lastTs : 0,\n _seq: seq++,\n };\n lastTs = ts;\n accDebug.push(withMeta);\n setStreamDebug((current) =>\n current.length > 1000\n ? [...current.slice(-1000), withMeta]\n : [...current, withMeta],\n );\n };\n\n push({\n type: \"status\",\n message: initialMessage,\n } as StreamChunk);\n\n return {\n push,\n getAll: () => accDebug,\n };\n }, []);\n\n return {\n streamDebug,\n clearStreamDebug,\n createRecorder,\n };\n}\n","import {\n createStructuredStreamAccumulator,\n type StreamChunk,\n type StructuredDataChunk,\n type StructuredStreamState,\n} from \"@kortyx/stream/browser\";\nimport { useCallback, useMemo, useRef, useState } from \"react\";\n\nexport type StructuredStreamItem<TData = unknown> = {\n id: string;\n streamId: string;\n state: StructuredStreamState<TData>;\n};\n\nexport type UseStructuredStreamsOptions<_TData = unknown> = {\n createId?: (() => string) | undefined;\n initialChunks?: Iterable<StructuredDataChunk> | undefined;\n};\n\nexport type UseStructuredStreamsResult<TData = unknown> = {\n items: StructuredStreamItem<TData>[];\n byStreamId: Record<string, StructuredStreamState<TData>>;\n applyStreamChunk: (\n chunk: StreamChunk,\n ) => StructuredStreamItem<TData> | undefined;\n clear: () => void;\n get: (streamId: string) => StructuredStreamItem<TData> | undefined;\n};\n\nconst defaultCreateId = () => {\n try {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID();\n }\n } catch {}\n\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`;\n};\n\nexport function useStructuredStreams<TData = unknown>(\n options?: UseStructuredStreamsOptions<TData> | undefined,\n): UseStructuredStreamsResult<TData> {\n const createId = useRef(options?.createId ?? defaultCreateId).current;\n const initialChunks = useRef(options?.initialChunks ?? []).current;\n const accumulatorRef = useRef(\n createStructuredStreamAccumulator<TData>(initialChunks),\n );\n const initialItemsRef = useRef<StructuredStreamItem<TData>[] | undefined>(\n undefined,\n );\n\n if (initialItemsRef.current === undefined) {\n initialItemsRef.current = accumulatorRef.current\n .entries()\n .map(([streamId, state]) => ({\n id: createId(),\n streamId,\n state,\n }));\n }\n\n const itemsRef = useRef(initialItemsRef.current);\n const [items, setItems] = useState(initialItemsRef.current);\n\n const setNextItems = useCallback(\n (nextItems: StructuredStreamItem<TData>[]) => {\n itemsRef.current = nextItems;\n setItems(nextItems);\n },\n [],\n );\n\n const applyStreamChunk = useCallback(\n (chunk: StreamChunk): StructuredStreamItem<TData> | undefined => {\n const nextState = accumulatorRef.current.applyStreamChunk(chunk);\n if (!nextState) return undefined;\n\n const currentItems = itemsRef.current;\n const existingIndex = currentItems.findIndex(\n (item) => item.streamId === nextState.streamId,\n );\n const existingItem =\n existingIndex >= 0 ? currentItems[existingIndex] : undefined;\n\n const nextItem: StructuredStreamItem<TData> = existingItem\n ? {\n ...existingItem,\n state: nextState,\n }\n : {\n id: createId(),\n streamId: nextState.streamId,\n state: nextState,\n };\n\n const nextItems =\n existingIndex >= 0\n ? currentItems.map((item, index) =>\n index === existingIndex ? nextItem : item,\n )\n : [...currentItems, nextItem];\n\n setNextItems(nextItems);\n return nextItem;\n },\n [createId, setNextItems],\n );\n\n const clear = useCallback(() => {\n accumulatorRef.current = createStructuredStreamAccumulator<TData>();\n setNextItems([]);\n }, [setNextItems]);\n\n const get = useCallback((streamId: string) => {\n return itemsRef.current.find((item) => item.streamId === streamId);\n }, []);\n\n const byStreamId = useMemo(\n () =>\n Object.fromEntries(\n items.map((item) => [item.streamId, item.state]),\n ) as Record<string, StructuredStreamState<TData>>,\n [items],\n );\n\n return {\n items,\n byStreamId,\n applyStreamChunk,\n clear,\n get,\n };\n}\n"],"mappings":"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","names":["import_react","import_browser","import_react","defaultCreateId"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/build-assistant-message.ts","../src/chat-storage.ts","../src/chat-transport.ts","../src/create-live-chat-pieces.ts","../src/to-human-input-piece.ts","../src/use-chat.ts","../src/use-chat-stream-debug.ts","../src/use-structured-streams.ts"],"sourcesContent":["export * from \"./build-assistant-message\";\nexport * from \"./chat-storage\";\nexport * from \"./chat-transport\";\nexport * from \"./chat-types\";\nexport * from \"./create-live-chat-pieces\";\nexport * from \"./to-human-input-piece\";\nexport * from \"./use-chat\";\nexport * from \"./use-chat-stream-debug\";\nexport * from \"./use-structured-streams\";\n","import type { StreamChunk } from \"@kortyx/stream/browser\";\nimport type { ChatMsg, ContentPiece } from \"./chat-types\";\n\nexport function buildAssistantMessage(args: {\n createId: () => string;\n pieces: ContentPiece[];\n debug: StreamChunk[];\n}): ChatMsg {\n const plainTextContent = args.pieces\n .filter(\n (piece): piece is Extract<ContentPiece, { type: \"text\" }> =>\n piece.type === \"text\",\n )\n .map((piece) => piece.content)\n .join(\"\");\n\n const base = {\n id: args.createId(),\n role: \"assistant\" as const,\n content: plainTextContent,\n debug: args.debug,\n };\n\n return args.pieces.length > 0\n ? { ...base, contentPieces: args.pieces }\n : base;\n}\n","type MaybePromise<T> = T | Promise<T>;\ntype StorageLike = {\n getItem: (key: string) => string | null;\n setItem: (key: string, value: string) => void;\n removeItem: (key: string) => void;\n};\n\nexport type PersistedChatState<TMessage> = {\n sessionId: string | null;\n workflowId: string;\n includeHistory: boolean;\n messages: TMessage[];\n};\n\nexport type ChatStorage<TMessage> = {\n load: () => MaybePromise<Partial<PersistedChatState<TMessage>>>;\n save: (state: PersistedChatState<TMessage>) => MaybePromise<void>;\n clearMessages: () => MaybePromise<void>;\n};\n\nexport type BrowserChatStorageKeys = {\n messages: string;\n includeHistory: string;\n sessionId: string;\n workflowId: string;\n};\n\nexport type BrowserChatStorageOptions<TMessage> = {\n parseMessage?: ((value: unknown) => TMessage | null) | undefined;\n serializeMessage?: ((message: TMessage) => unknown) | undefined;\n maxMessages?: number | undefined;\n keys?: Partial<BrowserChatStorageKeys> | undefined;\n};\n\nconst DEFAULT_STORAGE_KEYS: BrowserChatStorageKeys = {\n messages: \"chat.messages.v1\",\n includeHistory: \"chat.includeHistory.v1\",\n sessionId: \"chat.sessionId\",\n workflowId: \"chat.workflowId\",\n};\n\nconst getLocalStorage = (): StorageLike | undefined => {\n const candidate = globalThis as { localStorage?: StorageLike };\n return candidate.localStorage;\n};\n\nconst defaultParseMessage = <TMessage>(value: unknown): TMessage | null =>\n value as TMessage;\n\nconst defaultSerializeMessage = <TMessage>(message: TMessage): unknown =>\n message;\n\n// TODO: Make remote/custom persistence more robust before freezing this API:\n// - add debouncing so repeated message updates do not trigger a full save each time\n// - surface save error handling/retry hooks instead of silently ignoring failures\n// - define a conflict/version strategy for multi-tab and server-synced storage\n// - support partial saves instead of rewriting the full persisted state on each change\nexport function createBrowserChatStorage<TMessage = unknown>(\n options?: BrowserChatStorageOptions<TMessage>,\n): ChatStorage<TMessage> {\n const keys = {\n ...DEFAULT_STORAGE_KEYS,\n ...(options?.keys ?? {}),\n };\n const maxMessages = options?.maxMessages ?? 60;\n const parseMessage = options?.parseMessage ?? defaultParseMessage<TMessage>;\n const serializeMessage =\n options?.serializeMessage ?? defaultSerializeMessage<TMessage>;\n\n return {\n load() {\n try {\n const storage = getLocalStorage();\n if (!storage) {\n return {};\n }\n\n const sessionId = storage.getItem(keys.sessionId);\n const workflowId = storage.getItem(keys.workflowId) ?? \"\";\n const includeHistoryRaw = storage.getItem(keys.includeHistory);\n const rawMessages = storage.getItem(keys.messages);\n const parsedMessages = rawMessages\n ? (JSON.parse(rawMessages) as unknown)\n : [];\n const messages = Array.isArray(parsedMessages)\n ? parsedMessages\n .map((item) => parseMessage(item))\n .filter((message): message is TMessage => message !== null)\n : [];\n\n return {\n sessionId,\n workflowId,\n includeHistory: includeHistoryRaw !== \"0\",\n messages,\n };\n } catch {\n return {};\n }\n },\n\n save(state) {\n try {\n const storage = getLocalStorage();\n if (!storage) return;\n\n if (state.sessionId) {\n storage.setItem(keys.sessionId, state.sessionId);\n } else {\n storage.removeItem(keys.sessionId);\n }\n\n storage.setItem(keys.workflowId, state.workflowId);\n storage.setItem(keys.includeHistory, state.includeHistory ? \"1\" : \"0\");\n\n const trimmedMessages = state.messages\n .slice(-maxMessages)\n .map((message) => serializeMessage(message));\n\n storage.setItem(keys.messages, JSON.stringify(trimmedMessages));\n } catch {}\n },\n\n clearMessages() {\n try {\n const storage = getLocalStorage();\n if (!storage) return;\n storage.removeItem(keys.messages);\n } catch {}\n },\n };\n}\n","import type { StreamChunk, StreamFromRouteArgs } from \"@kortyx/stream/browser\";\nimport { streamFromRoute } from \"@kortyx/stream/browser\";\n\ntype MaybePromise<T> = T | Promise<T>;\ntype DefaultChatContext = Record<string, unknown>;\n\nexport type OutgoingChatMessage = {\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n metadata?: Record<string, unknown>;\n};\n\nexport type ChatTransportContext<TContext = DefaultChatContext> = {\n sessionId: string;\n workflowId: string;\n messages: OutgoingChatMessage[];\n context: TContext;\n signal?: AbortSignal | undefined;\n};\n\nexport type ChatTransportChunkHandler = (\n chunk: StreamChunk,\n) => undefined | boolean | Promise<boolean | undefined>;\n\nexport type ChatTransport<TContext = DefaultChatContext> = {\n stream(\n args: ChatTransportContext<TContext> & {\n onChunk: ChatTransportChunkHandler;\n },\n ): Promise<void>;\n};\n\nexport type ChatTransportChunkSource =\n | Iterable<StreamChunk>\n | AsyncIterable<StreamChunk>;\n\nexport function createChatTransport<TContext = DefaultChatContext>(args: {\n stream: (\n context: ChatTransportContext<TContext>,\n ) => MaybePromise<ChatTransportChunkSource>;\n}): ChatTransport<TContext> {\n return {\n async stream({ onChunk, ...context }) {\n for await (const chunk of await args.stream(context)) {\n const shouldContinue = await onChunk(chunk);\n if (chunk.type === \"done\" || shouldContinue === false) {\n break;\n }\n }\n },\n };\n}\n\nexport type RouteChatRequestBody<TContext = DefaultChatContext> = {\n sessionId: string;\n workflowId: string;\n messages: OutgoingChatMessage[];\n context: TContext;\n};\n\nexport type CreateRouteChatTransportArgs<TBody, TContext> = Omit<\n StreamFromRouteArgs<TBody>,\n \"body\"\n> & {\n createBody?: ((context: ChatTransportContext<TContext>) => TBody) | undefined;\n};\n\nexport function createRouteChatTransport<\n TContext = DefaultChatContext,\n TBody = RouteChatRequestBody<TContext>,\n>(\n args: CreateRouteChatTransportArgs<TBody, TContext>,\n): ChatTransport<TContext> {\n return createChatTransport<TContext>({\n stream: (context) => {\n const body =\n args.createBody?.(context) ??\n ({\n sessionId: context.sessionId,\n workflowId: context.workflowId,\n messages: context.messages,\n context: context.context,\n } as TBody);\n const routeArgs: StreamFromRouteArgs<TBody> = {\n endpoint: args.endpoint,\n body,\n ...(args.method ? { method: args.method } : {}),\n ...(args.headers ? { headers: args.headers } : {}),\n ...(args.fetchImpl ? { fetchImpl: args.fetchImpl } : {}),\n ...(context.signal ? { signal: context.signal } : {}),\n };\n\n return streamFromRoute({\n ...routeArgs,\n });\n },\n });\n}\n","import type {\n StreamChunk,\n StructuredStreamState,\n} from \"@kortyx/stream/browser\";\nimport type { StructuredStreamItem } from \"./use-structured-streams\";\n\nexport type LiveChatTextPiece = {\n id: string;\n type: \"text\";\n content: string;\n};\n\nexport type LiveChatStructuredPiece<TStructuredData = unknown> = {\n id: string;\n type: \"structured\";\n data: StructuredStreamState<TStructuredData>;\n};\n\nexport type LiveChatErrorPiece = {\n id: string;\n type: \"error\";\n content: string;\n};\n\ntype LiveChatCustomPiece = {\n id: string;\n type: string;\n};\n\nexport type LiveChatPiece<\n TStructuredData = unknown,\n TCustomPiece extends LiveChatCustomPiece = never,\n> =\n | LiveChatTextPiece\n | LiveChatStructuredPiece<TStructuredData>\n | LiveChatErrorPiece\n | TCustomPiece;\n\ntype StructuredStreamsController<TStructuredData> = {\n applyStreamChunk: (\n chunk: StreamChunk,\n ) => StructuredStreamItem<TStructuredData> | undefined;\n};\n\nconst isNonEmptyTextPiece = <\n TStructuredData,\n TCustomPiece extends LiveChatCustomPiece,\n>(\n piece: LiveChatPiece<TStructuredData, TCustomPiece>,\n) =>\n piece.type !== \"text\" ||\n (\"content\" in piece &&\n typeof piece.content === \"string\" &&\n piece.content.length > 0);\n\nexport function createLiveChatPieces<\n TStructuredData = unknown,\n TCustomPiece extends LiveChatCustomPiece = never,\n>(args: {\n createId: () => string;\n onChange: (pieces: LiveChatPiece<TStructuredData, TCustomPiece>[]) => void;\n structuredStreams: StructuredStreamsController<TStructuredData>;\n toHumanInputPiece: (chunk: StreamChunk) => TCustomPiece;\n}) {\n const keyedPieces: Array<{\n key: string;\n piece: LiveChatPiece<TStructuredData, TCustomPiece>;\n }> = [];\n let sawTextDelta = false;\n\n const emit = () => {\n args.onChange(\n keyedPieces\n .map((entry) => entry.piece)\n .filter((piece) => isNonEmptyTextPiece(piece)),\n );\n };\n\n const findPieceIndex = (key: string) =>\n keyedPieces.findIndex((entry) => entry.key === key);\n\n const upsertPiece = (\n key: string,\n piece: LiveChatPiece<TStructuredData, TCustomPiece>,\n ) => {\n const existingIndex = findPieceIndex(key);\n if (existingIndex >= 0) {\n keyedPieces[existingIndex] = { key, piece };\n } else {\n keyedPieces.push({ key, piece });\n }\n emit();\n };\n\n const pushPiece = (piece: LiveChatPiece<TStructuredData, TCustomPiece>) => {\n keyedPieces.push({\n key: `${piece.type}:${piece.id}`,\n piece,\n });\n emit();\n };\n\n const resolveTextStreamKey = (\n chunk: StreamChunk,\n fallbackNode?: string,\n ): string => {\n if (\"segmentId\" in chunk && typeof chunk.segmentId === \"string\") {\n const seg = chunk.segmentId.trim();\n if (seg.length > 0) {\n const op =\n \"opId\" in chunk && typeof chunk.opId === \"string\"\n ? chunk.opId.trim()\n : \"\";\n return op.length > 0 ? `${op}:${seg}` : seg;\n }\n }\n\n if (\"opId\" in chunk && typeof chunk.opId === \"string\") {\n const op = chunk.opId.trim();\n if (op.length > 0) {\n const node =\n (\"node\" in chunk && typeof chunk.node === \"string\"\n ? chunk.node\n : fallbackNode) ?? \"__unknown__\";\n return `${op}:${node}`;\n }\n }\n\n return fallbackNode && fallbackNode.length > 0\n ? fallbackNode\n : \"__unknown__\";\n };\n\n const ensureTextPiece = (key: string): LiveChatTextPiece => {\n const pieceKey = `text:${key}`;\n const existingIndex = findPieceIndex(pieceKey);\n if (existingIndex >= 0) {\n return keyedPieces[existingIndex]?.piece as LiveChatTextPiece;\n }\n\n const nextPiece: LiveChatTextPiece = {\n id: args.createId(),\n type: \"text\",\n content: \"\",\n };\n keyedPieces.push({ key: pieceKey, piece: nextPiece });\n return nextPiece;\n };\n\n const processChunk = (\n chunk: StreamChunk,\n _options?: { openDebugOnInterrupt?: boolean | undefined },\n ): boolean => {\n if (chunk.type === \"text-start\") {\n ensureTextPiece(resolveTextStreamKey(chunk, chunk.node));\n emit();\n return true;\n }\n\n if (chunk.type === \"text-delta\") {\n sawTextDelta = true;\n const streamKey = resolveTextStreamKey(chunk, chunk.node);\n const current = ensureTextPiece(streamKey);\n upsertPiece(`text:${streamKey}`, {\n ...current,\n content: current.content + chunk.delta,\n });\n return true;\n }\n\n if (chunk.type === \"text-end\") {\n emit();\n return true;\n }\n\n if (chunk.type === \"structured-data\") {\n const nextItem = args.structuredStreams.applyStreamChunk(chunk);\n if (nextItem) {\n upsertPiece(`structured:${nextItem.streamId}`, {\n id: nextItem.id,\n type: \"structured\",\n data: nextItem.state,\n });\n }\n return true;\n }\n\n if (chunk.type === \"interrupt\") {\n pushPiece(args.toHumanInputPiece(chunk));\n return true;\n }\n\n if (chunk.type === \"message\") {\n if (!sawTextDelta) {\n pushPiece({\n id: args.createId(),\n type: \"text\",\n content: chunk.content ?? \"\",\n });\n }\n return true;\n }\n\n if (chunk.type === \"error\") {\n pushPiece({\n id: args.createId(),\n type: \"error\",\n content: chunk.message ?? \"An error occurred\",\n });\n return true;\n }\n\n if (chunk.type === \"done\") {\n return false;\n }\n\n return true;\n };\n\n return {\n processChunk,\n getPieces: () =>\n keyedPieces\n .map((entry) => entry.piece)\n .filter((piece) => isNonEmptyTextPiece(piece)),\n };\n}\n","import type { StreamChunk } from \"@kortyx/stream/browser\";\nimport type { HumanInputPiece } from \"./chat-types\";\n\ninterface HumanInputStreamChunk {\n type: \"interrupt\";\n requestId: string | undefined;\n resumeToken: string | undefined;\n id?: string;\n schemaId?: string;\n schemaVersion?: string;\n meta?: Record<string, unknown>;\n input?: {\n kind?: \"text\" | \"choice\" | \"multi-choice\";\n question?: string;\n multiple?: boolean;\n id?: string;\n schemaId?: string;\n schemaVersion?: string;\n meta?: Record<string, unknown>;\n options?: Array<{\n id?: string | number;\n label?: string;\n description?: string;\n }>;\n };\n}\n\nexport type ToHumanInputPiece = (args: {\n chunk: StreamChunk;\n createId: () => string;\n}) => HumanInputPiece;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n value !== null && typeof value === \"object\" && !Array.isArray(value);\n\nconst nonEmptyString = (value: unknown): string | undefined =>\n typeof value === \"string\" && value.length > 0 ? value : undefined;\n\nconst firstNonEmptyString = (...values: unknown[]): string | undefined => {\n for (const value of values) {\n const result = nonEmptyString(value);\n if (result) return result;\n }\n return undefined;\n};\n\nexport function toHumanInputPiece(args: {\n chunk: StreamChunk;\n createId: () => string;\n}): HumanInputPiece {\n const hi = args.chunk as unknown as HumanInputStreamChunk;\n const input = hi.input ?? {};\n const kind = input.kind || (input.multiple ? \"multi-choice\" : \"choice\");\n const isText = kind === \"text\";\n const schemaId = firstNonEmptyString(hi.schemaId, input.schemaId);\n const schemaVersion = firstNonEmptyString(\n hi.schemaVersion,\n input.schemaVersion,\n );\n const interruptId = firstNonEmptyString(hi.id, input.id);\n const inputMeta = isRecord(input.meta) ? input.meta : undefined;\n const chunkMeta = isRecord(hi.meta) ? hi.meta : undefined;\n const meta =\n inputMeta || chunkMeta\n ? { ...(inputMeta ?? {}), ...(chunkMeta ?? {}) }\n : undefined;\n\n const question = isText\n ? input.question\n : typeof input.question === \"string\"\n ? input.question\n : \"Please choose\";\n\n const optionsSrc = Array.isArray(input.options) ? input.options : [];\n const optionsArr: Array<{\n id: string;\n label: string;\n description?: string;\n }> = optionsSrc\n .map((option) => ({\n id: String(option.id ?? \"\"),\n label: String(option.label ?? \"\"),\n ...(typeof option.description === \"string\" && option.description\n ? { description: option.description }\n : {}),\n }))\n .filter((option) => option.id && option.label);\n\n return {\n id: args.createId(),\n type: \"interrupt\",\n resumeToken: String(hi.resumeToken ?? \"\"),\n requestId: String(hi.requestId ?? \"\"),\n kind,\n ...(question !== undefined ? { question } : {}),\n multiple: Boolean(input.multiple),\n options: optionsArr,\n ...(schemaId ? { schemaId } : {}),\n ...(schemaVersion ? { schemaVersion } : {}),\n ...(interruptId ? { interruptId } : {}),\n ...(meta ? { meta } : {}),\n };\n}\n","\"use client\";\n\nimport type { StreamChunk } from \"@kortyx/stream/browser\";\nimport { type RefObject, useEffect, useRef, useState } from \"react\";\nimport { buildAssistantMessage } from \"./build-assistant-message\";\nimport { type ChatStorage, createBrowserChatStorage } from \"./chat-storage\";\nimport type { ChatTransport, OutgoingChatMessage } from \"./chat-transport\";\nimport type { ChatMsg, ContentPiece, HumanInputPiece } from \"./chat-types\";\nimport { createLiveChatPieces } from \"./create-live-chat-pieces\";\nimport {\n toHumanInputPiece as defaultToHumanInputPiece,\n type ToHumanInputPiece,\n} from \"./to-human-input-piece\";\nimport { useChatStreamDebug } from \"./use-chat-stream-debug\";\nimport { useStructuredStreams } from \"./use-structured-streams\";\n\nconst defaultCreateId = () => {\n try {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID();\n }\n } catch {}\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`;\n};\n\ntype DefaultChatContext = Record<string, unknown>;\n\nexport type PrepareContextMessagesArgs<TContext = DefaultChatContext> = {\n messages: ChatMsg[];\n sessionId: string;\n workflowId: string;\n reason: \"send\" | \"resume\";\n context: TContext;\n};\n\nexport type PrepareContextMessages<TContext = DefaultChatContext> = (\n args: PrepareContextMessagesArgs<TContext>,\n) => OutgoingChatMessage[] | Promise<OutgoingChatMessage[]>;\n\nexport type UseChatValue = {\n messages: ChatMsg[];\n isStreaming: boolean;\n canAbort: boolean;\n error: Error | null;\n streamContentPieces: ContentPiece[];\n streamDebug: StreamChunk[];\n lastAssistantId: string | null;\n workflowId: string;\n setWorkflowId: React.Dispatch<React.SetStateAction<string>>;\n send: (text: string) => Promise<void> | void;\n respondToHumanInput: (args: {\n resumeToken: string;\n requestId: string;\n selected: string[];\n text?: string;\n }) => Promise<void> | void;\n respondToInterrupt: (\n piece: HumanInputPiece,\n response?: { selected?: string[] | undefined; text?: string | undefined },\n ) => Promise<void> | void;\n setMessages: React.Dispatch<React.SetStateAction<ChatMsg[]>>;\n clearMessages: () => void;\n resetSession: () => void;\n resetChat: () => void;\n clearChat: () => void;\n abort: () => void;\n clearError: () => void;\n includeHistory: boolean;\n setIncludeHistory: React.Dispatch<React.SetStateAction<boolean>>;\n};\n\nexport type UseChatOptions<TContext = DefaultChatContext> = {\n transport: ChatTransport<TContext>;\n storage?: ChatStorage<ChatMsg> | undefined;\n createId?: (() => string) | undefined;\n context?: TContext | undefined;\n prepareContextMessages?: PrepareContextMessages<TContext> | undefined;\n toHumanInputPiece?: ToHumanInputPiece | undefined;\n};\n\nconst defaultStorage = createBrowserChatStorage<ChatMsg>();\n\nfunction useLatestRef<T>(value: T): RefObject<T> {\n const ref = useRef(value);\n ref.current = value;\n return ref;\n}\n\nfunction findActiveTextInterrupt(args: {\n messages: ChatMsg[];\n streamContentPieces: ContentPiece[];\n}): HumanInputPiece | undefined {\n const liveInterrupt = args.streamContentPieces.find(\n (piece): piece is HumanInputPiece =>\n piece.type === \"interrupt\" && piece.kind === \"text\",\n );\n if (liveInterrupt) return liveInterrupt;\n\n for (const message of [...args.messages].reverse()) {\n if (message.role !== \"assistant\" || !message.contentPieces) continue;\n\n const messageInterrupt = [...message.contentPieces]\n .reverse()\n .find(\n (piece): piece is HumanInputPiece =>\n piece.type === \"interrupt\" && piece.kind === \"text\",\n );\n if (messageInterrupt) return messageInterrupt;\n }\n\n return undefined;\n}\n\nexport function useChat<TContext = DefaultChatContext>(\n options: UseChatOptions<TContext>,\n): UseChatValue {\n const resolvedStorage = options.storage ?? defaultStorage;\n const transportRef = useLatestRef(options.transport);\n const storageRef = useLatestRef(resolvedStorage);\n const toHumanInputPieceRef = useLatestRef(\n options.toHumanInputPiece ?? defaultToHumanInputPiece,\n );\n const requestContext =\n options.context ?? ({} as unknown as NonNullable<TContext>);\n const createId = useRef(options.createId ?? defaultCreateId).current;\n\n const [messages, setMessages] = useState<ChatMsg[]>([]);\n const [isStreaming, setIsStreaming] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [streamContentPieces, setStreamContentPieces] = useState<\n ContentPiece[]\n >([]);\n const [lastAssistantId, setLastAssistantId] = useState<string | null>(null);\n const [includeHistory, setIncludeHistory] = useState(true);\n const [workflowId, setWorkflowId] = useState(\"\");\n const [sessionId, setSessionId] = useState<string | null>(null);\n const [didHydrateStorage, setDidHydrateStorage] = useState(false);\n const abortControllerRef = useRef<AbortController | null>(null);\n const { streamDebug, clearStreamDebug, createRecorder } =\n useChatStreamDebug();\n const { applyStreamChunk, clear: clearStructuredStreams } =\n useStructuredStreams<Record<string, unknown>>({\n createId,\n });\n\n useEffect(() => {\n let cancelled = false;\n const storage = resolvedStorage;\n\n void Promise.resolve(storage.load()).then((storedState) => {\n if (cancelled) return;\n\n if (typeof storedState.sessionId === \"string\") {\n setSessionId(storedState.sessionId);\n }\n if (typeof storedState.workflowId === \"string\") {\n setWorkflowId(storedState.workflowId);\n }\n if (typeof storedState.includeHistory === \"boolean\") {\n setIncludeHistory(storedState.includeHistory);\n }\n if (\n Array.isArray(storedState.messages) &&\n storedState.messages.length > 0\n ) {\n setMessages(storedState.messages);\n }\n\n setDidHydrateStorage(true);\n });\n\n return () => {\n cancelled = true;\n };\n }, [resolvedStorage]);\n\n useEffect(() => {\n if (!didHydrateStorage) return;\n const storage = storageRef.current;\n\n void storage.save({\n sessionId,\n workflowId,\n includeHistory,\n messages,\n });\n }, [\n didHydrateStorage,\n includeHistory,\n messages,\n sessionId,\n storageRef,\n workflowId,\n ]);\n\n const clearActiveStream = () => {\n abortControllerRef.current?.abort();\n abortControllerRef.current = null;\n setIsStreaming(false);\n setMessages([]);\n setStreamContentPieces([]);\n clearStreamDebug();\n clearStructuredStreams();\n setLastAssistantId(null);\n };\n\n const clearMessages = () => {\n clearActiveStream();\n void storageRef.current.clearMessages();\n };\n\n const resetSession = () => {\n setSessionId(null);\n };\n\n const resetChat = () => {\n clearActiveStream();\n resetSession();\n setError(null);\n void storageRef.current.clearMessages();\n };\n\n const clearChat = resetChat;\n\n const clearError = () => {\n setError(null);\n };\n\n const abort = () => {\n abortControllerRef.current?.abort();\n };\n\n const persistSessionId = (sid: string) => {\n setSessionId(sid);\n };\n\n const resolveSessionId = () => {\n const sid = sessionId ?? createId();\n if (!sessionId) persistSessionId(sid);\n return sid;\n };\n\n const toOutgoingHistoryMessage = (message: ChatMsg): OutgoingChatMessage => ({\n role: message.role,\n content: message.content,\n });\n\n const prepareMessagesToSend = async (args: {\n sid: string;\n outgoingMessage: OutgoingChatMessage;\n reason: \"send\" | \"resume\";\n }): Promise<OutgoingChatMessage[]> => {\n const prepared = options.prepareContextMessages\n ? await options.prepareContextMessages({\n messages,\n sessionId: args.sid,\n workflowId,\n reason: args.reason,\n context: requestContext,\n })\n : includeHistory\n ? messages.map(toOutgoingHistoryMessage)\n : [];\n\n return [...prepared, args.outgoingMessage];\n };\n\n const streamAssistantResponse = async (args: {\n sid: string;\n messagesToSend: OutgoingChatMessage[];\n debugLabel: string;\n openDebugOnInterrupt?: boolean;\n signal: AbortSignal;\n }) => {\n clearStructuredStreams();\n setStreamContentPieces([]);\n\n const debug = createRecorder(`${args.debugLabel} sessionId=${args.sid}`);\n const pieces = createLiveChatPieces({\n createId,\n onChange: setStreamContentPieces,\n structuredStreams: {\n applyStreamChunk,\n },\n toHumanInputPiece: (chunk) =>\n toHumanInputPieceRef.current({\n chunk,\n createId,\n }),\n });\n\n await transportRef.current.stream({\n sessionId: args.sid,\n workflowId,\n messages: args.messagesToSend,\n context: requestContext,\n signal: args.signal,\n onChunk: (chunk: StreamChunk) => {\n debug.push(chunk);\n\n if (chunk.type === \"session\") {\n if (typeof chunk.sessionId === \"string\") {\n persistSessionId(chunk.sessionId);\n }\n return;\n }\n\n if (chunk.type === \"error\") {\n setError(new Error(chunk.message));\n }\n\n return pieces.processChunk(chunk, {\n openDebugOnInterrupt: args.openDebugOnInterrupt,\n });\n },\n });\n\n if (args.signal.aborted) return;\n\n const assistant = buildAssistantMessage({\n createId,\n pieces: pieces.getPieces(),\n debug: debug.getAll(),\n });\n setMessages((prev) => [...prev, assistant]);\n setLastAssistantId(assistant.id);\n clearStructuredStreams();\n setStreamContentPieces([]);\n clearStreamDebug();\n };\n\n const isAbortError = (value: unknown, signal: AbortSignal): boolean => {\n if (signal.aborted) return true;\n if (!(value instanceof Error)) return false;\n return value.name === \"AbortError\";\n };\n\n const respondToHumanInput = async ({\n resumeToken,\n requestId,\n selected,\n text,\n }: {\n resumeToken: string;\n requestId: string;\n selected: string[];\n text?: string;\n }) => {\n if (isStreaming) return;\n setError(null);\n const label: string =\n typeof text === \"string\" && text.length > 0\n ? text\n : selected.length > 0\n ? selected.length === 1\n ? (selected[0] as string)\n : selected.join(\", \")\n : \"(selection)\";\n const userId = createId();\n const userMsg: ChatMsg = { id: userId, role: \"user\", content: label };\n setMessages((prev) => [...prev, userMsg]);\n setIsStreaming(true);\n setStreamContentPieces([]);\n clearStreamDebug();\n clearStructuredStreams();\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n try {\n const resumePayload: Record<string, unknown> = {\n resume: { token: resumeToken, requestId, selected },\n };\n const outgoing = {\n role: \"user\" as const,\n content: label,\n metadata: resumePayload,\n };\n const sid = resolveSessionId();\n const messagesToSend = await prepareMessagesToSend({\n sid,\n outgoingMessage: outgoing,\n reason: \"resume\",\n });\n\n await streamAssistantResponse({\n sid,\n messagesToSend,\n debugLabel: \"runChat (resume)\",\n signal: abortController.signal,\n });\n } catch (err) {\n if (isAbortError(err, abortController.signal)) return;\n const nextError = err instanceof Error ? err : new Error(String(err));\n setError(nextError);\n throw err;\n } finally {\n if (abortControllerRef.current === abortController) {\n abortControllerRef.current = null;\n }\n setIsStreaming(false);\n }\n };\n\n const respondToInterrupt = async (\n piece: HumanInputPiece,\n response?: { selected?: string[] | undefined; text?: string | undefined },\n ) => {\n const text = response?.text;\n const selected =\n response?.selected ??\n (typeof text === \"string\" && text.length > 0 ? [text] : []);\n\n return respondToHumanInput({\n resumeToken: piece.resumeToken,\n requestId: piece.requestId,\n selected,\n ...(text !== undefined ? { text } : {}),\n });\n };\n\n const send = async (text: string) => {\n const content = text.trim();\n if (!content || isStreaming) return;\n setError(null);\n\n const activeTextInterrupt = findActiveTextInterrupt({\n messages,\n streamContentPieces,\n });\n\n if (activeTextInterrupt) {\n await respondToHumanInput({\n resumeToken: activeTextInterrupt.resumeToken,\n requestId: activeTextInterrupt.requestId,\n selected: [content],\n text: content,\n });\n return;\n }\n\n const userId = createId();\n const userMsg: ChatMsg = { id: userId, role: \"user\", content };\n setMessages((prev) => [...prev, userMsg]);\n\n setIsStreaming(true);\n setStreamContentPieces([]);\n clearStreamDebug();\n clearStructuredStreams();\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n try {\n const sid = resolveSessionId();\n const messagesToSend = await prepareMessagesToSend({\n sid,\n outgoingMessage: { role: \"user\" as const, content },\n reason: \"send\",\n });\n await streamAssistantResponse({\n sid,\n messagesToSend,\n debugLabel: \"runChat (send)\",\n openDebugOnInterrupt: true,\n signal: abortController.signal,\n });\n } catch (err) {\n if (isAbortError(err, abortController.signal)) return;\n const nextError = err instanceof Error ? err : new Error(String(err));\n setError(nextError);\n throw err;\n } finally {\n if (abortControllerRef.current === abortController) {\n abortControllerRef.current = null;\n }\n setIsStreaming(false);\n }\n };\n\n return {\n messages,\n isStreaming,\n canAbort: isStreaming,\n error,\n streamContentPieces,\n streamDebug,\n lastAssistantId,\n workflowId,\n setWorkflowId,\n send,\n respondToHumanInput,\n respondToInterrupt,\n setMessages,\n clearMessages,\n resetSession,\n resetChat,\n clearChat,\n abort,\n clearError,\n includeHistory,\n setIncludeHistory,\n };\n}\n","\"use client\";\n\nimport type { StreamChunk } from \"@kortyx/stream/browser\";\nimport { useCallback, useState } from \"react\";\n\ntype DebugChunk = StreamChunk & {\n _ts: number;\n _dt: number;\n _seq: number;\n};\n\nexport function useChatStreamDebug() {\n const [streamDebug, setStreamDebug] = useState<StreamChunk[]>([]);\n\n const clearStreamDebug = useCallback(() => {\n setStreamDebug([]);\n }, []);\n\n const createRecorder = useCallback((initialMessage: string) => {\n let seq = 0;\n let lastTs = 0;\n const accDebug: StreamChunk[] = [];\n\n const push = (chunk: StreamChunk) => {\n const ts = Date.now();\n const withMeta: DebugChunk = {\n ...(chunk as StreamChunk),\n _ts: ts,\n _dt: lastTs ? ts - lastTs : 0,\n _seq: seq++,\n };\n lastTs = ts;\n accDebug.push(withMeta);\n setStreamDebug((current) =>\n current.length > 1000\n ? [...current.slice(-1000), withMeta]\n : [...current, withMeta],\n );\n };\n\n push({\n type: \"status\",\n message: initialMessage,\n } as StreamChunk);\n\n return {\n push,\n getAll: () => accDebug,\n };\n }, []);\n\n return {\n streamDebug,\n clearStreamDebug,\n createRecorder,\n };\n}\n","import {\n createStructuredStreamAccumulator,\n type StreamChunk,\n type StructuredDataChunk,\n type StructuredStreamState,\n} from \"@kortyx/stream/browser\";\nimport { useCallback, useMemo, useRef, useState } from \"react\";\n\nexport type StructuredStreamItem<TData = unknown> = {\n id: string;\n streamId: string;\n state: StructuredStreamState<TData>;\n};\n\nexport type UseStructuredStreamsOptions<_TData = unknown> = {\n createId?: (() => string) | undefined;\n initialChunks?: Iterable<StructuredDataChunk> | undefined;\n};\n\nexport type UseStructuredStreamsResult<TData = unknown> = {\n items: StructuredStreamItem<TData>[];\n byStreamId: Record<string, StructuredStreamState<TData>>;\n applyStreamChunk: (\n chunk: StreamChunk,\n ) => StructuredStreamItem<TData> | undefined;\n clear: () => void;\n get: (streamId: string) => StructuredStreamItem<TData> | undefined;\n};\n\nconst defaultCreateId = () => {\n try {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID();\n }\n } catch {}\n\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`;\n};\n\nexport function useStructuredStreams<TData = unknown>(\n options?: UseStructuredStreamsOptions<TData> | undefined,\n): UseStructuredStreamsResult<TData> {\n const createId = useRef(options?.createId ?? defaultCreateId).current;\n const initialChunks = useRef(options?.initialChunks ?? []).current;\n const accumulatorRef = useRef(\n createStructuredStreamAccumulator<TData>(initialChunks),\n );\n const initialItemsRef = useRef<StructuredStreamItem<TData>[] | undefined>(\n undefined,\n );\n\n if (initialItemsRef.current === undefined) {\n initialItemsRef.current = accumulatorRef.current\n .entries()\n .map(([streamId, state]) => ({\n id: createId(),\n streamId,\n state,\n }));\n }\n\n const itemsRef = useRef(initialItemsRef.current);\n const [items, setItems] = useState(initialItemsRef.current);\n\n const setNextItems = useCallback(\n (nextItems: StructuredStreamItem<TData>[]) => {\n itemsRef.current = nextItems;\n setItems(nextItems);\n },\n [],\n );\n\n const applyStreamChunk = useCallback(\n (chunk: StreamChunk): StructuredStreamItem<TData> | undefined => {\n const nextState = accumulatorRef.current.applyStreamChunk(chunk);\n if (!nextState) return undefined;\n\n const currentItems = itemsRef.current;\n const existingIndex = currentItems.findIndex(\n (item) => item.streamId === nextState.streamId,\n );\n const existingItem =\n existingIndex >= 0 ? currentItems[existingIndex] : undefined;\n\n const nextItem: StructuredStreamItem<TData> = existingItem\n ? {\n ...existingItem,\n state: nextState,\n }\n : {\n id: createId(),\n streamId: nextState.streamId,\n state: nextState,\n };\n\n const nextItems =\n existingIndex >= 0\n ? currentItems.map((item, index) =>\n index === existingIndex ? nextItem : item,\n )\n : [...currentItems, nextItem];\n\n setNextItems(nextItems);\n return nextItem;\n },\n [createId, setNextItems],\n );\n\n const clear = useCallback(() => {\n accumulatorRef.current = createStructuredStreamAccumulator<TData>();\n setNextItems([]);\n }, [setNextItems]);\n\n const get = useCallback((streamId: string) => {\n return itemsRef.current.find((item) => item.streamId === streamId);\n }, []);\n\n const byStreamId = useMemo(\n () =>\n Object.fromEntries(\n items.map((item) => [item.streamId, item.state]),\n ) as Record<string, StructuredStreamState<TData>>,\n [items],\n );\n\n return {\n items,\n byStreamId,\n applyStreamChunk,\n clear,\n get,\n };\n}\n"],"mappings":"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","names":["import_react","import_browser","import_react","defaultCreateId"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kortyx/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "React hooks for Kortyx client streaming experiences.",
|
|
6
6
|
"keywords": [
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"CHANGELOG.md"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@kortyx/stream": "0.
|
|
35
|
+
"@kortyx/stream": "0.7.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"react": ">=19"
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
"turbo": "2.6.1",
|
|
47
47
|
"tsup": "^8.5.1",
|
|
48
48
|
"typescript": "5.9.3",
|
|
49
|
-
"vitest": "^4.1.5"
|
|
49
|
+
"vitest": "^4.1.5",
|
|
50
|
+
"@vitest/coverage-v8": "^4.1.5"
|
|
50
51
|
},
|
|
51
52
|
"sideEffects": false,
|
|
52
53
|
"repository": {
|
|
@@ -63,6 +64,7 @@
|
|
|
63
64
|
"dev": "tsc --watch",
|
|
64
65
|
"lint": "biome check src test",
|
|
65
66
|
"test": "vitest run && pnpm -s type-check",
|
|
66
|
-
"
|
|
67
|
+
"coverage": "vitest run --coverage && pnpm -s type-check",
|
|
68
|
+
"type-check": "tsc --noEmit -p tsconfig.test.json"
|
|
67
69
|
}
|
|
68
70
|
}
|