@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 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: (value: unknown) => TMessage | null;
65
- serializeMessage: (message: TMessage) => unknown;
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 CreateRouteChatTransportArgs<TBody> = Omit<StreamFromRouteArgs<TBody>, "body"> & {
93
- getBody: (context: ChatTransportContext) => TBody;
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
- declare function createRouteChatTransport<TBody>(args: CreateRouteChatTransportArgs<TBody>): ChatTransport;
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: args.getBody(context),
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 ?? "__unknown__";
199
+ return fallbackNode && fallbackNode.length > 0 ? fallbackNode : "__unknown__";
193
200
  };
194
201
  const ensureTextPiece = (key) => {
195
- const pieceKey = `text:${key || "__unknown__"}`;
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 useInitialValue(value) {
437
- const ref = (0, import_react3.useRef)(void 0);
438
- if (ref.current === void 0) {
439
- ref.current = value;
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 (let i = args.messages.length - 1; i >= 0; i -= 1) {
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 transport = useInitialValue(options.transport);
461
- const storage = useInitialValue(options.storage ?? defaultStorage);
462
- const createId = useInitialValue(options.createId ?? defaultCreateId2);
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
- }, [storage]);
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
- storage,
519
+ storageRef,
511
520
  workflowId
512
521
  ]);
513
- const clearChat = () => {
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
- void storage.clearMessages();
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 transport.stream({
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 history = includeHistory ? messages.map((message) => ({
598
- role: message.role,
599
- content: message.content
600
- })) : [];
601
- const messagesToSend = [...history, outgoing];
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 history = includeHistory ? messages.map((message) => ({
637
- role: message.role,
638
- content: message.content
639
- })) : [];
640
- const messagesToSend = [...history, { role: "user", content }];
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.2.1",
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.6.1"
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
- "type-check": "tsc --noEmit"
67
+ "coverage": "vitest run --coverage && pnpm -s type-check",
68
+ "type-check": "tsc --noEmit -p tsconfig.test.json"
67
69
  }
68
70
  }