@kortyx/react 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +29 -0
- package/dist/index.d.ts +46 -15
- package/dist/index.js +131 -33
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @kortyx/react
|
|
2
2
|
|
|
3
|
+
## [0.3.0](https://github.com/kortyx-io/kortyx/compare/react-v0.2.1...react-v0.3.0) (2026-05-20)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **chat:** add request context plumbing ([7210a6f](https://github.com/kortyx-io/kortyx/commit/7210a6f9275728628e1862bd7b0f200112bd9b08))
|
|
9
|
+
* **react:** tighten useChat lifecycle controls ([1672c72](https://github.com/kortyx-io/kortyx/commit/1672c72a524d16fae2f23dcbec1b91191747dc25))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **react:** bucket empty-node text deltas into a single stream ([fb01809](https://github.com/kortyx-io/kortyx/commit/fb01809752f138414cda13daa5ffe5e67016b0c6))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Dependencies
|
|
18
|
+
|
|
19
|
+
* The following workspace dependencies were updated
|
|
20
|
+
* dependencies
|
|
21
|
+
* @kortyx/stream bumped to 0.7.0
|
|
22
|
+
|
|
3
23
|
## [0.2.1](https://github.com/kortyx-io/kortyx/compare/react-v0.2.0...react-v0.2.1) (2026-05-03)
|
|
4
24
|
|
|
5
25
|
|
package/README.md
CHANGED
|
@@ -10,7 +10,36 @@ npm install @kortyx/react
|
|
|
10
10
|
|
|
11
11
|
## Exports
|
|
12
12
|
|
|
13
|
+
- `useChat`
|
|
14
|
+
- `createRouteChatTransport`
|
|
15
|
+
- `createChatTransport`
|
|
16
|
+
- `createBrowserChatStorage`
|
|
13
17
|
- `useStructuredStreams`
|
|
18
|
+
- `createLiveChatPieces`
|
|
19
|
+
- `toHumanInputPiece`
|
|
20
|
+
|
|
21
|
+
`useChat` keeps finalized chat history in `messages` and the active assistant
|
|
22
|
+
response in `streamContentPieces` while a stream is running.
|
|
23
|
+
|
|
24
|
+
It also exposes stream controls for UI integrations:
|
|
25
|
+
|
|
26
|
+
- `abort()` / `canAbort`
|
|
27
|
+
- `error` / `clearError()`
|
|
28
|
+
- `clearMessages()`
|
|
29
|
+
- `resetSession()`
|
|
30
|
+
- `resetChat()`
|
|
31
|
+
- `respondToInterrupt(...)`
|
|
32
|
+
|
|
33
|
+
`clearMessages()` keeps the current session. `resetChat()` clears messages,
|
|
34
|
+
active stream state, errors, and the session id. Route transports forward
|
|
35
|
+
`abort()` through an `AbortSignal`; custom transports should forward
|
|
36
|
+
`context.signal` to their own request layer.
|
|
37
|
+
|
|
38
|
+
`useChat({ context })` forwards request context to the transport. Route
|
|
39
|
+
transports send `{ sessionId, workflowId, messages, context }` by default, and
|
|
40
|
+
`createRouteChatTransport({ createBody })` can customize that body.
|
|
41
|
+
`prepareContextMessages` lets apps replace the default history with summaries
|
|
42
|
+
or server-owned history while `useChat` still appends the outgoing message.
|
|
14
43
|
|
|
15
44
|
## License
|
|
16
45
|
|
package/dist/index.d.ts
CHANGED
|
@@ -61,38 +61,47 @@ type BrowserChatStorageKeys = {
|
|
|
61
61
|
workflowId: string;
|
|
62
62
|
};
|
|
63
63
|
type BrowserChatStorageOptions<TMessage> = {
|
|
64
|
-
parseMessage
|
|
65
|
-
serializeMessage
|
|
64
|
+
parseMessage?: ((value: unknown) => TMessage | null) | undefined;
|
|
65
|
+
serializeMessage?: ((message: TMessage) => unknown) | undefined;
|
|
66
66
|
maxMessages?: number | undefined;
|
|
67
67
|
keys?: Partial<BrowserChatStorageKeys> | undefined;
|
|
68
68
|
};
|
|
69
69
|
declare function createBrowserChatStorage<TMessage = unknown>(options?: BrowserChatStorageOptions<TMessage>): ChatStorage<TMessage>;
|
|
70
70
|
|
|
71
71
|
type MaybePromise<T> = T | Promise<T>;
|
|
72
|
+
type DefaultChatContext$1 = Record<string, unknown>;
|
|
72
73
|
type OutgoingChatMessage = {
|
|
73
74
|
role: "user" | "assistant" | "system";
|
|
74
75
|
content: string;
|
|
75
76
|
metadata?: Record<string, unknown>;
|
|
76
77
|
};
|
|
77
|
-
type ChatTransportContext = {
|
|
78
|
+
type ChatTransportContext<TContext = DefaultChatContext$1> = {
|
|
78
79
|
sessionId: string;
|
|
79
80
|
workflowId: string;
|
|
80
81
|
messages: OutgoingChatMessage[];
|
|
82
|
+
context: TContext;
|
|
83
|
+
signal?: AbortSignal | undefined;
|
|
81
84
|
};
|
|
82
85
|
type ChatTransportChunkHandler = (chunk: StreamChunk) => undefined | boolean | Promise<boolean | undefined>;
|
|
83
|
-
type ChatTransport = {
|
|
84
|
-
stream(args: ChatTransportContext & {
|
|
86
|
+
type ChatTransport<TContext = DefaultChatContext$1> = {
|
|
87
|
+
stream(args: ChatTransportContext<TContext> & {
|
|
85
88
|
onChunk: ChatTransportChunkHandler;
|
|
86
89
|
}): Promise<void>;
|
|
87
90
|
};
|
|
88
91
|
type ChatTransportChunkSource = Iterable<StreamChunk> | AsyncIterable<StreamChunk>;
|
|
89
|
-
declare function createChatTransport(args: {
|
|
90
|
-
stream: (context: ChatTransportContext) => MaybePromise<ChatTransportChunkSource>;
|
|
91
|
-
}): ChatTransport
|
|
92
|
-
type
|
|
93
|
-
|
|
92
|
+
declare function createChatTransport<TContext = DefaultChatContext$1>(args: {
|
|
93
|
+
stream: (context: ChatTransportContext<TContext>) => MaybePromise<ChatTransportChunkSource>;
|
|
94
|
+
}): ChatTransport<TContext>;
|
|
95
|
+
type RouteChatRequestBody<TContext = DefaultChatContext$1> = {
|
|
96
|
+
sessionId: string;
|
|
97
|
+
workflowId: string;
|
|
98
|
+
messages: OutgoingChatMessage[];
|
|
99
|
+
context: TContext;
|
|
94
100
|
};
|
|
95
|
-
|
|
101
|
+
type CreateRouteChatTransportArgs<TBody, TContext> = Omit<StreamFromRouteArgs<TBody>, "body"> & {
|
|
102
|
+
createBody?: ((context: ChatTransportContext<TContext>) => TBody) | undefined;
|
|
103
|
+
};
|
|
104
|
+
declare function createRouteChatTransport<TContext = DefaultChatContext$1, TBody = RouteChatRequestBody<TContext>>(args: CreateRouteChatTransportArgs<TBody, TContext>): ChatTransport<TContext>;
|
|
96
105
|
|
|
97
106
|
type StructuredStreamItem<TData = unknown> = {
|
|
98
107
|
id: string;
|
|
@@ -152,9 +161,20 @@ declare function toHumanInputPiece(args: {
|
|
|
152
161
|
createId: () => string;
|
|
153
162
|
}): HumanInputPiece;
|
|
154
163
|
|
|
164
|
+
type DefaultChatContext = Record<string, unknown>;
|
|
165
|
+
type PrepareContextMessagesArgs<TContext = DefaultChatContext> = {
|
|
166
|
+
messages: ChatMsg[];
|
|
167
|
+
sessionId: string;
|
|
168
|
+
workflowId: string;
|
|
169
|
+
reason: "send" | "resume";
|
|
170
|
+
context: TContext;
|
|
171
|
+
};
|
|
172
|
+
type PrepareContextMessages<TContext = DefaultChatContext> = (args: PrepareContextMessagesArgs<TContext>) => OutgoingChatMessage[] | Promise<OutgoingChatMessage[]>;
|
|
155
173
|
type UseChatValue = {
|
|
156
174
|
messages: ChatMsg[];
|
|
157
175
|
isStreaming: boolean;
|
|
176
|
+
canAbort: boolean;
|
|
177
|
+
error: Error | null;
|
|
158
178
|
streamContentPieces: ContentPiece[];
|
|
159
179
|
streamDebug: StreamChunk[];
|
|
160
180
|
lastAssistantId: string | null;
|
|
@@ -167,17 +187,28 @@ type UseChatValue = {
|
|
|
167
187
|
selected: string[];
|
|
168
188
|
text?: string;
|
|
169
189
|
}) => Promise<void> | void;
|
|
190
|
+
respondToInterrupt: (piece: HumanInputPiece, response?: {
|
|
191
|
+
selected?: string[] | undefined;
|
|
192
|
+
text?: string | undefined;
|
|
193
|
+
}) => Promise<void> | void;
|
|
170
194
|
setMessages: React.Dispatch<React.SetStateAction<ChatMsg[]>>;
|
|
195
|
+
clearMessages: () => void;
|
|
196
|
+
resetSession: () => void;
|
|
197
|
+
resetChat: () => void;
|
|
171
198
|
clearChat: () => void;
|
|
199
|
+
abort: () => void;
|
|
200
|
+
clearError: () => void;
|
|
172
201
|
includeHistory: boolean;
|
|
173
202
|
setIncludeHistory: React.Dispatch<React.SetStateAction<boolean>>;
|
|
174
203
|
};
|
|
175
|
-
type UseChatOptions = {
|
|
176
|
-
transport: ChatTransport
|
|
204
|
+
type UseChatOptions<TContext = DefaultChatContext> = {
|
|
205
|
+
transport: ChatTransport<TContext>;
|
|
177
206
|
storage?: ChatStorage<ChatMsg> | undefined;
|
|
178
207
|
createId?: (() => string) | undefined;
|
|
208
|
+
context?: TContext | undefined;
|
|
209
|
+
prepareContextMessages?: PrepareContextMessages<TContext> | undefined;
|
|
179
210
|
};
|
|
180
|
-
declare function useChat(options: UseChatOptions): UseChatValue;
|
|
211
|
+
declare function useChat<TContext = DefaultChatContext>(options: UseChatOptions<TContext>): UseChatValue;
|
|
181
212
|
|
|
182
213
|
declare function useChatStreamDebug(): {
|
|
183
214
|
streamDebug: ({
|
|
@@ -432,4 +463,4 @@ declare function useChatStreamDebug(): {
|
|
|
432
463
|
};
|
|
433
464
|
};
|
|
434
465
|
|
|
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 };
|
|
466
|
+
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 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;
|
|
@@ -433,21 +440,17 @@ var defaultCreateId2 = () => {
|
|
|
433
440
|
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
434
441
|
};
|
|
435
442
|
var defaultStorage = createBrowserChatStorage();
|
|
436
|
-
function
|
|
437
|
-
const ref = (0, import_react3.useRef)(
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
return ref.current;
|
|
443
|
+
function useLatestRef(value) {
|
|
444
|
+
const ref = (0, import_react3.useRef)(value);
|
|
445
|
+
ref.current = value;
|
|
446
|
+
return ref;
|
|
442
447
|
}
|
|
443
448
|
function findActiveTextInterrupt(args) {
|
|
444
449
|
const liveInterrupt = args.streamContentPieces.find(
|
|
445
450
|
(piece) => piece.type === "interrupt" && piece.kind === "text"
|
|
446
451
|
);
|
|
447
452
|
if (liveInterrupt) return liveInterrupt;
|
|
448
|
-
for (
|
|
449
|
-
const message = args.messages[i];
|
|
450
|
-
if (!message) continue;
|
|
453
|
+
for (const message of [...args.messages].reverse()) {
|
|
451
454
|
if (message.role !== "assistant" || !message.contentPieces) continue;
|
|
452
455
|
const messageInterrupt = [...message.contentPieces].reverse().find(
|
|
453
456
|
(piece) => piece.type === "interrupt" && piece.kind === "text"
|
|
@@ -457,23 +460,28 @@ function findActiveTextInterrupt(args) {
|
|
|
457
460
|
return void 0;
|
|
458
461
|
}
|
|
459
462
|
function useChat(options) {
|
|
460
|
-
const
|
|
461
|
-
const
|
|
462
|
-
const
|
|
463
|
+
const resolvedStorage = options.storage ?? defaultStorage;
|
|
464
|
+
const transportRef = useLatestRef(options.transport);
|
|
465
|
+
const storageRef = useLatestRef(resolvedStorage);
|
|
466
|
+
const requestContext = options.context ?? {};
|
|
467
|
+
const createId = (0, import_react3.useRef)(options.createId ?? defaultCreateId2).current;
|
|
463
468
|
const [messages, setMessages] = (0, import_react3.useState)([]);
|
|
464
469
|
const [isStreaming, setIsStreaming] = (0, import_react3.useState)(false);
|
|
470
|
+
const [error, setError] = (0, import_react3.useState)(null);
|
|
465
471
|
const [streamContentPieces, setStreamContentPieces] = (0, import_react3.useState)([]);
|
|
466
472
|
const [lastAssistantId, setLastAssistantId] = (0, import_react3.useState)(null);
|
|
467
473
|
const [includeHistory, setIncludeHistory] = (0, import_react3.useState)(true);
|
|
468
474
|
const [workflowId, setWorkflowId] = (0, import_react3.useState)("");
|
|
469
475
|
const [sessionId, setSessionId] = (0, import_react3.useState)(null);
|
|
470
476
|
const [didHydrateStorage, setDidHydrateStorage] = (0, import_react3.useState)(false);
|
|
477
|
+
const abortControllerRef = (0, import_react3.useRef)(null);
|
|
471
478
|
const { streamDebug, clearStreamDebug, createRecorder } = useChatStreamDebug();
|
|
472
479
|
const { applyStreamChunk, clear: clearStructuredStreams } = useStructuredStreams({
|
|
473
480
|
createId
|
|
474
481
|
});
|
|
475
482
|
(0, import_react3.useEffect)(() => {
|
|
476
483
|
let cancelled = false;
|
|
484
|
+
const storage = resolvedStorage;
|
|
477
485
|
void Promise.resolve(storage.load()).then((storedState) => {
|
|
478
486
|
if (cancelled) return;
|
|
479
487
|
if (typeof storedState.sessionId === "string") {
|
|
@@ -493,9 +501,10 @@ function useChat(options) {
|
|
|
493
501
|
return () => {
|
|
494
502
|
cancelled = true;
|
|
495
503
|
};
|
|
496
|
-
}, [
|
|
504
|
+
}, [resolvedStorage]);
|
|
497
505
|
(0, import_react3.useEffect)(() => {
|
|
498
506
|
if (!didHydrateStorage) return;
|
|
507
|
+
const storage = storageRef.current;
|
|
499
508
|
void storage.save({
|
|
500
509
|
sessionId,
|
|
501
510
|
workflowId,
|
|
@@ -507,16 +516,38 @@ function useChat(options) {
|
|
|
507
516
|
includeHistory,
|
|
508
517
|
messages,
|
|
509
518
|
sessionId,
|
|
510
|
-
|
|
519
|
+
storageRef,
|
|
511
520
|
workflowId
|
|
512
521
|
]);
|
|
513
|
-
const
|
|
522
|
+
const clearActiveStream = () => {
|
|
523
|
+
abortControllerRef.current?.abort();
|
|
524
|
+
abortControllerRef.current = null;
|
|
525
|
+
setIsStreaming(false);
|
|
514
526
|
setMessages([]);
|
|
515
527
|
setStreamContentPieces([]);
|
|
516
528
|
clearStreamDebug();
|
|
517
529
|
clearStructuredStreams();
|
|
518
530
|
setLastAssistantId(null);
|
|
519
|
-
|
|
531
|
+
};
|
|
532
|
+
const clearMessages = () => {
|
|
533
|
+
clearActiveStream();
|
|
534
|
+
void storageRef.current.clearMessages();
|
|
535
|
+
};
|
|
536
|
+
const resetSession = () => {
|
|
537
|
+
setSessionId(null);
|
|
538
|
+
};
|
|
539
|
+
const resetChat = () => {
|
|
540
|
+
clearActiveStream();
|
|
541
|
+
resetSession();
|
|
542
|
+
setError(null);
|
|
543
|
+
void storageRef.current.clearMessages();
|
|
544
|
+
};
|
|
545
|
+
const clearChat = resetChat;
|
|
546
|
+
const clearError = () => {
|
|
547
|
+
setError(null);
|
|
548
|
+
};
|
|
549
|
+
const abort = () => {
|
|
550
|
+
abortControllerRef.current?.abort();
|
|
520
551
|
};
|
|
521
552
|
const persistSessionId = (sid) => {
|
|
522
553
|
setSessionId(sid);
|
|
@@ -526,6 +557,20 @@ function useChat(options) {
|
|
|
526
557
|
if (!sessionId) persistSessionId(sid);
|
|
527
558
|
return sid;
|
|
528
559
|
};
|
|
560
|
+
const toOutgoingHistoryMessage = (message) => ({
|
|
561
|
+
role: message.role,
|
|
562
|
+
content: message.content
|
|
563
|
+
});
|
|
564
|
+
const prepareMessagesToSend = async (args) => {
|
|
565
|
+
const prepared = options.prepareContextMessages ? await options.prepareContextMessages({
|
|
566
|
+
messages,
|
|
567
|
+
sessionId: args.sid,
|
|
568
|
+
workflowId,
|
|
569
|
+
reason: args.reason,
|
|
570
|
+
context: requestContext
|
|
571
|
+
}) : includeHistory ? messages.map(toOutgoingHistoryMessage) : [];
|
|
572
|
+
return [...prepared, args.outgoingMessage];
|
|
573
|
+
};
|
|
529
574
|
const streamAssistantResponse = async (args) => {
|
|
530
575
|
clearStructuredStreams();
|
|
531
576
|
setStreamContentPieces([]);
|
|
@@ -541,10 +586,12 @@ function useChat(options) {
|
|
|
541
586
|
createId
|
|
542
587
|
})
|
|
543
588
|
});
|
|
544
|
-
await
|
|
589
|
+
await transportRef.current.stream({
|
|
545
590
|
sessionId: args.sid,
|
|
546
591
|
workflowId,
|
|
547
592
|
messages: args.messagesToSend,
|
|
593
|
+
context: requestContext,
|
|
594
|
+
signal: args.signal,
|
|
548
595
|
onChunk: (chunk) => {
|
|
549
596
|
debug.push(chunk);
|
|
550
597
|
if (chunk.type === "session") {
|
|
@@ -553,11 +600,15 @@ function useChat(options) {
|
|
|
553
600
|
}
|
|
554
601
|
return;
|
|
555
602
|
}
|
|
603
|
+
if (chunk.type === "error") {
|
|
604
|
+
setError(new Error(chunk.message));
|
|
605
|
+
}
|
|
556
606
|
return pieces.processChunk(chunk, {
|
|
557
607
|
openDebugOnInterrupt: args.openDebugOnInterrupt
|
|
558
608
|
});
|
|
559
609
|
}
|
|
560
610
|
});
|
|
611
|
+
if (args.signal.aborted) return;
|
|
561
612
|
const assistant = buildAssistantMessage({
|
|
562
613
|
createId,
|
|
563
614
|
pieces: pieces.getPieces(),
|
|
@@ -569,6 +620,11 @@ function useChat(options) {
|
|
|
569
620
|
setStreamContentPieces([]);
|
|
570
621
|
clearStreamDebug();
|
|
571
622
|
};
|
|
623
|
+
const isAbortError = (value, signal) => {
|
|
624
|
+
if (signal.aborted) return true;
|
|
625
|
+
if (!(value instanceof Error)) return false;
|
|
626
|
+
return value.name === "AbortError";
|
|
627
|
+
};
|
|
572
628
|
const respondToHumanInput = async ({
|
|
573
629
|
resumeToken,
|
|
574
630
|
requestId,
|
|
@@ -576,6 +632,7 @@ function useChat(options) {
|
|
|
576
632
|
text
|
|
577
633
|
}) => {
|
|
578
634
|
if (isStreaming) return;
|
|
635
|
+
setError(null);
|
|
579
636
|
const label = typeof text === "string" && text.length > 0 ? text : selected.length > 0 ? selected.length === 1 ? selected[0] : selected.join(", ") : "(selection)";
|
|
580
637
|
const userId = createId();
|
|
581
638
|
const userMsg = { id: userId, role: "user", content: label };
|
|
@@ -584,6 +641,8 @@ function useChat(options) {
|
|
|
584
641
|
setStreamContentPieces([]);
|
|
585
642
|
clearStreamDebug();
|
|
586
643
|
clearStructuredStreams();
|
|
644
|
+
const abortController = new AbortController();
|
|
645
|
+
abortControllerRef.current = abortController;
|
|
587
646
|
try {
|
|
588
647
|
const resumePayload = {
|
|
589
648
|
resume: { token: resumeToken, requestId, selected }
|
|
@@ -594,23 +653,43 @@ function useChat(options) {
|
|
|
594
653
|
metadata: resumePayload
|
|
595
654
|
};
|
|
596
655
|
const sid = resolveSessionId();
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
656
|
+
const messagesToSend = await prepareMessagesToSend({
|
|
657
|
+
sid,
|
|
658
|
+
outgoingMessage: outgoing,
|
|
659
|
+
reason: "resume"
|
|
660
|
+
});
|
|
602
661
|
await streamAssistantResponse({
|
|
603
662
|
sid,
|
|
604
663
|
messagesToSend,
|
|
605
|
-
debugLabel: "runChat (resume)"
|
|
664
|
+
debugLabel: "runChat (resume)",
|
|
665
|
+
signal: abortController.signal
|
|
606
666
|
});
|
|
667
|
+
} catch (err) {
|
|
668
|
+
if (isAbortError(err, abortController.signal)) return;
|
|
669
|
+
const nextError = err instanceof Error ? err : new Error(String(err));
|
|
670
|
+
setError(nextError);
|
|
671
|
+
throw err;
|
|
607
672
|
} finally {
|
|
673
|
+
if (abortControllerRef.current === abortController) {
|
|
674
|
+
abortControllerRef.current = null;
|
|
675
|
+
}
|
|
608
676
|
setIsStreaming(false);
|
|
609
677
|
}
|
|
610
678
|
};
|
|
679
|
+
const respondToInterrupt = async (piece, response) => {
|
|
680
|
+
const text = response?.text;
|
|
681
|
+
const selected = response?.selected ?? (typeof text === "string" && text.length > 0 ? [text] : []);
|
|
682
|
+
return respondToHumanInput({
|
|
683
|
+
resumeToken: piece.resumeToken,
|
|
684
|
+
requestId: piece.requestId,
|
|
685
|
+
selected,
|
|
686
|
+
...text !== void 0 ? { text } : {}
|
|
687
|
+
});
|
|
688
|
+
};
|
|
611
689
|
const send = async (text) => {
|
|
612
690
|
const content = text.trim();
|
|
613
691
|
if (!content || isStreaming) return;
|
|
692
|
+
setError(null);
|
|
614
693
|
const activeTextInterrupt = findActiveTextInterrupt({
|
|
615
694
|
messages,
|
|
616
695
|
streamContentPieces
|
|
@@ -631,26 +710,39 @@ function useChat(options) {
|
|
|
631
710
|
setStreamContentPieces([]);
|
|
632
711
|
clearStreamDebug();
|
|
633
712
|
clearStructuredStreams();
|
|
713
|
+
const abortController = new AbortController();
|
|
714
|
+
abortControllerRef.current = abortController;
|
|
634
715
|
try {
|
|
635
716
|
const sid = resolveSessionId();
|
|
636
|
-
const
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
717
|
+
const messagesToSend = await prepareMessagesToSend({
|
|
718
|
+
sid,
|
|
719
|
+
outgoingMessage: { role: "user", content },
|
|
720
|
+
reason: "send"
|
|
721
|
+
});
|
|
641
722
|
await streamAssistantResponse({
|
|
642
723
|
sid,
|
|
643
724
|
messagesToSend,
|
|
644
725
|
debugLabel: "runChat (send)",
|
|
645
|
-
openDebugOnInterrupt: true
|
|
726
|
+
openDebugOnInterrupt: true,
|
|
727
|
+
signal: abortController.signal
|
|
646
728
|
});
|
|
729
|
+
} catch (err) {
|
|
730
|
+
if (isAbortError(err, abortController.signal)) return;
|
|
731
|
+
const nextError = err instanceof Error ? err : new Error(String(err));
|
|
732
|
+
setError(nextError);
|
|
733
|
+
throw err;
|
|
647
734
|
} finally {
|
|
735
|
+
if (abortControllerRef.current === abortController) {
|
|
736
|
+
abortControllerRef.current = null;
|
|
737
|
+
}
|
|
648
738
|
setIsStreaming(false);
|
|
649
739
|
}
|
|
650
740
|
};
|
|
651
741
|
return {
|
|
652
742
|
messages,
|
|
653
743
|
isStreaming,
|
|
744
|
+
canAbort: isStreaming,
|
|
745
|
+
error,
|
|
654
746
|
streamContentPieces,
|
|
655
747
|
streamDebug,
|
|
656
748
|
lastAssistantId,
|
|
@@ -658,8 +750,14 @@ function useChat(options) {
|
|
|
658
750
|
setWorkflowId,
|
|
659
751
|
send,
|
|
660
752
|
respondToHumanInput,
|
|
753
|
+
respondToInterrupt,
|
|
661
754
|
setMessages,
|
|
755
|
+
clearMessages,
|
|
756
|
+
resetSession,
|
|
757
|
+
resetChat,
|
|
662
758
|
clearChat,
|
|
759
|
+
abort,
|
|
760
|
+
clearError,
|
|
663
761
|
includeHistory,
|
|
664
762
|
setIncludeHistory
|
|
665
763
|
};
|
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 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 { 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 { 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\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};\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 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 toHumanInputPiece({\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.0",
|
|
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
|
}
|