@djangocfg/ui-tools 2.1.336 → 2.1.338

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.
@@ -1,7 +1,8 @@
1
1
  import { MarkdownMessage } from './chunk-2ZLKZ5VR.mjs';
2
2
  import { __name } from './chunk-N2XQF2OL.mjs';
3
3
  import { createContext, forwardRef, memo, useCallback, useReducer, useRef, useEffect, useState, useMemo, useSyncExternalStore, useContext } from 'react';
4
- import { cn } from '@djangocfg/ui-core/lib';
4
+ import { cn, isDev } from '@djangocfg/ui-core/lib';
5
+ import { consola } from 'consola';
5
6
  import { useLocalStorage, useMediaQuery } from '@djangocfg/ui-core/hooks';
6
7
  import { create } from 'zustand';
7
8
  import { persist, createJSONStorage } from 'zustand/middleware';
@@ -294,6 +295,32 @@ function createId(prefix = "m") {
294
295
  return `${prefix}_${Date.now().toString(36)}_${counter.toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
295
296
  }
296
297
  __name(createId, "createId");
298
+ var SCOPES = ["bootstrap", "transport", "stream", "lifecycle", "tools", "error"];
299
+ var cache = /* @__PURE__ */ new Map();
300
+ function buildLogger(enabled) {
301
+ const root = consola.withTag("chat");
302
+ const subs = Object.fromEntries(
303
+ SCOPES.map((scope) => [scope, root.withTag(scope)])
304
+ );
305
+ if (!enabled) {
306
+ for (const scope of SCOPES) {
307
+ if (scope === "error") continue;
308
+ subs[scope].level = -999;
309
+ }
310
+ }
311
+ return { ...subs, enabled };
312
+ }
313
+ __name(buildLogger, "buildLogger");
314
+ function getChatLogger(debug) {
315
+ const enabled = debug ?? isDev;
316
+ let logger = cache.get(enabled);
317
+ if (!logger) {
318
+ logger = buildLogger(enabled);
319
+ cache.set(enabled, logger);
320
+ }
321
+ return logger;
322
+ }
323
+ __name(getChatLogger, "getChatLogger");
297
324
 
298
325
  // src/tools/Chat/core/markdown.ts
299
326
  function createTokenBuffer(onFlush, windowMs = LIMITS.streamCoalesceMs) {
@@ -339,32 +366,53 @@ function useChat(config) {
339
366
  stateRef.current = state;
340
367
  const abortRef = useRef(null);
341
368
  const lastErrorRef = useRef(null);
342
- const initRef = useRef(false);
343
369
  const streamingMsgIdRef = useRef(null);
370
+ const bootstrapRef = useRef(null);
344
371
  const { transport, autoCreateSession = true, streaming = true, pageSize = LIMITS.pageSize } = config;
372
+ const log = getChatLogger(config.debug);
345
373
  useEffect(() => {
346
- if (initRef.current) return;
347
- initRef.current = true;
348
374
  let cancelled = false;
375
+ if (stateRef.current.sessionId) {
376
+ return;
377
+ }
378
+ if (config.initialSessionId || autoCreateSession) {
379
+ dispatch({ type: "HISTORY_LOAD_START" });
380
+ }
381
+ log.bootstrap.info("start", {
382
+ mode: config.initialSessionId ? "resume" : autoCreateSession ? "create" : "idle",
383
+ initialSessionId: config.initialSessionId
384
+ });
349
385
  const run = /* @__PURE__ */ __name(async () => {
386
+ const t0 = performance.now();
350
387
  try {
351
388
  if (config.initialSessionId) {
352
- dispatch({
353
- type: "SESSION_SET",
354
- sessionId: config.initialSessionId
355
- });
356
- dispatch({ type: "HISTORY_LOAD_START" });
389
+ if (!cancelled) {
390
+ dispatch({
391
+ type: "SESSION_SET",
392
+ sessionId: config.initialSessionId
393
+ });
394
+ }
357
395
  const page = await transport.loadHistory(config.initialSessionId, null, pageSize);
358
- if (cancelled) return;
396
+ if (cancelled) {
397
+ log.bootstrap.debug("cancelled (post-loadHistory)");
398
+ return config.initialSessionId;
399
+ }
359
400
  dispatch({
360
401
  type: "HISTORY_LOAD_DONE",
361
402
  messages: page.messages,
362
403
  hasMore: page.hasMore,
363
404
  cursor: page.nextCursor
364
405
  });
365
- } else if (autoCreateSession) {
406
+ log.bootstrap.success("resumed", {
407
+ sessionId: config.initialSessionId,
408
+ messages: page.messages.length,
409
+ hasMore: page.hasMore,
410
+ elapsedMs: Math.round(performance.now() - t0)
411
+ });
412
+ return config.initialSessionId;
413
+ }
414
+ if (autoCreateSession) {
366
415
  const info = await transport.createSession({ metadata: config.metadata });
367
- if (cancelled) return;
368
416
  dispatch({
369
417
  type: "SESSION_SET",
370
418
  sessionId: info.sessionId,
@@ -372,19 +420,48 @@ function useChat(config) {
372
420
  hasMore: info.hasMore ?? false,
373
421
  cursor: info.cursor ?? null
374
422
  });
423
+ dispatch({
424
+ type: "HISTORY_LOAD_DONE",
425
+ messages: info.messages ?? [],
426
+ hasMore: info.hasMore ?? false,
427
+ cursor: info.cursor ?? null
428
+ });
429
+ log.bootstrap.success(cancelled ? "created (post-cancel)" : "created", {
430
+ sessionId: info.sessionId,
431
+ resumed: info.resumed ?? false,
432
+ cancelled,
433
+ elapsedMs: Math.round(performance.now() - t0)
434
+ });
435
+ return info.sessionId;
375
436
  }
437
+ log.bootstrap.debug("idle (no initialSessionId, autoCreateSession=false)");
438
+ return null;
376
439
  } catch (err) {
377
440
  const e = err instanceof Error ? err : new Error(String(err));
441
+ if (cancelled) {
442
+ log.bootstrap.debug("cancelled (in catch)", { message: e.message });
443
+ return null;
444
+ }
378
445
  lastErrorRef.current = e;
379
446
  dispatch({ type: "ERROR_SET", error: e.message });
380
447
  config.onError?.(e);
448
+ log.error.error("bootstrap failed", { message: e.message, elapsedMs: Math.round(performance.now() - t0) });
449
+ return null;
381
450
  }
382
451
  }, "run");
383
- void run();
452
+ bootstrapRef.current = run();
384
453
  return () => {
385
454
  cancelled = true;
386
455
  };
387
456
  }, []);
457
+ const awaitSession = useCallback(async () => {
458
+ if (stateRef.current.sessionId) return stateRef.current.sessionId;
459
+ if (bootstrapRef.current) {
460
+ const id = await bootstrapRef.current;
461
+ if (id) return id;
462
+ }
463
+ return stateRef.current.sessionId;
464
+ }, []);
388
465
  const consumeStream = useCallback(
389
466
  async (sessionId, content, attachments) => {
390
467
  const ctrl = new AbortController();
@@ -393,9 +470,13 @@ function useChat(config) {
393
470
  streamingMsgIdRef.current = assistantId;
394
471
  dispatch({ type: "STREAM_START", id: assistantId });
395
472
  config.onStreamStart?.(assistantId);
473
+ log.stream.info("start", { sessionId, assistantId, chars: content.length });
396
474
  const tokenBuffer = createTokenBuffer(
397
475
  (delta) => dispatch({ type: "STREAM_CHUNK", delta })
398
476
  );
477
+ let chunkCount = 0;
478
+ let charsReceived = 0;
479
+ const t0 = performance.now();
399
480
  try {
400
481
  const iterator = transport.stream(sessionId, content, {
401
482
  signal: ctrl.signal,
@@ -412,17 +493,25 @@ function useChat(config) {
412
493
  }
413
494
  const finalMsg = stateRef.current.messages.find((m) => m.id === assistantId);
414
495
  if (finalMsg) config.onMessageEnd?.(finalMsg);
496
+ log.stream.success("done", {
497
+ assistantId,
498
+ chunks: chunkCount,
499
+ chars: charsReceived,
500
+ elapsedMs: Math.round(performance.now() - t0)
501
+ });
415
502
  } catch (err) {
416
503
  tokenBuffer.close();
417
504
  if (ctrl.signal.aborted) {
418
505
  const partial = stateRef.current.messages.find((m) => m.id === assistantId)?.content ?? "";
419
506
  dispatch({ type: "STREAM_CANCELLED", id: assistantId, partialText: partial });
507
+ log.stream.warn("cancelled", { assistantId, partialChars: partial.length });
420
508
  return;
421
509
  }
422
510
  const e = err instanceof Error ? err : new Error(String(err));
423
511
  lastErrorRef.current = e;
424
512
  dispatch({ type: "STREAM_ERROR", id: assistantId, message: e.message });
425
513
  config.onError?.(e);
514
+ log.error.error("stream failed", { assistantId, message: e.message });
426
515
  } finally {
427
516
  tokenBuffer.close();
428
517
  if (abortRef.current === ctrl) abortRef.current = null;
@@ -432,13 +521,17 @@ function useChat(config) {
432
521
  switch (ev.type) {
433
522
  case "message_start":
434
523
  ev.messageId;
524
+ log.stream.debug("message_start", { messageId: ev.messageId });
435
525
  return;
436
526
  case "chunk":
437
527
  tokenBuffer.push(ev.delta);
528
+ chunkCount += 1;
529
+ charsReceived += ev.delta.length;
438
530
  return;
439
531
  case "tool_activity":
440
532
  tokenBuffer.flush();
441
533
  dispatch({ type: "STREAM_TOOL_ACTIVITY", tool: ev.tool });
534
+ log.tools.debug("activity", { tool: ev.tool, status: ev.status });
442
535
  return;
443
536
  case "tool_call_start": {
444
537
  tokenBuffer.flush();
@@ -455,6 +548,7 @@ function useChat(config) {
455
548
  messageId: assistantId,
456
549
  toolCall
457
550
  });
551
+ log.tools.info("call_start", { toolId: ev.toolId, name: ev.name });
458
552
  return;
459
553
  }
460
554
  case "tool_call_delta":
@@ -473,6 +567,7 @@ function useChat(config) {
473
567
  output: ev.output,
474
568
  status: ev.status
475
569
  });
570
+ log.tools.info("call_end", { toolId: ev.toolId, status: ev.status });
476
571
  return;
477
572
  case "message_end":
478
573
  tokenBuffer.flush();
@@ -483,6 +578,11 @@ function useChat(config) {
483
578
  tokensOut: ev.tokensOut,
484
579
  sources: ev.sources
485
580
  });
581
+ log.stream.debug("message_end", {
582
+ tokensIn: ev.tokensIn,
583
+ tokensOut: ev.tokensOut,
584
+ sources: ev.sources?.length ?? 0
585
+ });
486
586
  return;
487
587
  case "error":
488
588
  tokenBuffer.flush();
@@ -491,6 +591,7 @@ function useChat(config) {
491
591
  id: assistantId,
492
592
  message: ev.message
493
593
  });
594
+ log.error.error("stream event error", { code: ev.code, message: ev.message });
494
595
  return;
495
596
  }
496
597
  }
@@ -527,16 +628,28 @@ function useChat(config) {
527
628
  );
528
629
  const sendMessage = useCallback(
529
630
  async (content, attachments) => {
530
- const sessionId = stateRef.current.sessionId;
631
+ log.lifecycle.info("sendMessage", {
632
+ chars: content.length,
633
+ attachments: attachments?.length ?? 0,
634
+ hasSession: !!stateRef.current.sessionId
635
+ });
636
+ const sessionId = await awaitSession();
531
637
  if (!sessionId) {
532
638
  const e = new Error("No active session");
533
639
  lastErrorRef.current = e;
534
640
  dispatch({ type: "ERROR_SET", error: e.message });
535
641
  config.onError?.(e);
642
+ log.error.error("sendMessage aborted: no session");
643
+ return;
644
+ }
645
+ if (!content.trim() && !(attachments && attachments.length > 0)) {
646
+ log.lifecycle.debug("sendMessage skipped (empty)");
647
+ return;
648
+ }
649
+ if (stateRef.current.isStreaming) {
650
+ log.lifecycle.debug("sendMessage skipped (already streaming)");
536
651
  return;
537
652
  }
538
- if (!content.trim() && !(attachments && attachments.length > 0)) return;
539
- if (stateRef.current.isStreaming) return;
540
653
  const userMsg = {
541
654
  id: createId("u"),
542
655
  role: "user",
@@ -553,13 +666,14 @@ function useChat(config) {
553
666
  await consumeBuffered(sessionId, content, attachments);
554
667
  }
555
668
  },
556
- [streaming, consumeStream, consumeBuffered, config]
669
+ [streaming, consumeStream, consumeBuffered, config, awaitSession]
557
670
  );
558
671
  const cancelStream = useCallback(() => {
559
672
  abortRef.current?.abort();
560
673
  }, []);
561
674
  const regenerate = useCallback(
562
675
  async (messageId) => {
676
+ log.lifecycle.info("regenerate", { messageId: messageId ?? "(last)" });
563
677
  const messages = stateRef.current.messages;
564
678
  let targetUserIdx = -1;
565
679
  if (messageId) {
@@ -575,7 +689,7 @@ function useChat(config) {
575
689
  for (let i = messages.length - 1; i > targetUserIdx; i -= 1) {
576
690
  dispatch({ type: "MESSAGE_DELETE", id: messages[i].id });
577
691
  }
578
- const sessionId = stateRef.current.sessionId;
692
+ const sessionId = await awaitSession();
579
693
  if (!sessionId) return;
580
694
  if (streaming) {
581
695
  await consumeStream(sessionId, userMsg.content, userMsg.attachments);
@@ -583,7 +697,7 @@ function useChat(config) {
583
697
  await consumeBuffered(sessionId, userMsg.content, userMsg.attachments);
584
698
  }
585
699
  },
586
- [streaming, consumeStream, consumeBuffered]
700
+ [streaming, consumeStream, consumeBuffered, awaitSession]
587
701
  );
588
702
  const editMessage = useCallback(
589
703
  async (id, content) => {
@@ -627,6 +741,7 @@ function useChat(config) {
627
741
  }
628
742
  }, [transport, pageSize, config]);
629
743
  const newSession = useCallback(async () => {
744
+ log.lifecycle.info("newSession", { previous: stateRef.current.sessionId });
630
745
  abortRef.current?.abort();
631
746
  const previous = stateRef.current.sessionId;
632
747
  if (previous) {
@@ -645,11 +760,13 @@ function useChat(config) {
645
760
  hasMore: info.hasMore ?? false,
646
761
  cursor: info.cursor ?? null
647
762
  });
763
+ log.lifecycle.success("newSession ok", { sessionId: info.sessionId });
648
764
  } catch (err) {
649
765
  const e = err instanceof Error ? err : new Error(String(err));
650
766
  lastErrorRef.current = e;
651
767
  dispatch({ type: "ERROR_SET", error: e.message });
652
768
  config.onError?.(e);
769
+ log.error.error("newSession failed", { message: e.message });
653
770
  }
654
771
  }, [transport, config]);
655
772
  return {
@@ -763,14 +880,14 @@ function createAudioBus(options) {
763
880
  return noopBus();
764
881
  }
765
882
  let sounds = options.sounds;
766
- const cache = /* @__PURE__ */ new Map();
883
+ const cache2 = /* @__PURE__ */ new Map();
767
884
  const getOrCreate = /* @__PURE__ */ __name((url) => {
768
- const hit = cache.get(url);
885
+ const hit = cache2.get(url);
769
886
  if (hit) return hit;
770
887
  const el = new Audio(url);
771
888
  el.preload = "auto";
772
889
  el.crossOrigin = "anonymous";
773
- cache.set(url, el);
890
+ cache2.set(url, el);
774
891
  return el;
775
892
  }, "getOrCreate");
776
893
  const resolveUrl = /* @__PURE__ */ __name((event) => {
@@ -804,7 +921,7 @@ function createAudioBus(options) {
804
921
  }, "preload");
805
922
  const unlock = /* @__PURE__ */ __name(() => {
806
923
  if (unlocked) return;
807
- for (const el of cache.values()) {
924
+ for (const el of cache2.values()) {
808
925
  const wasMuted = el.muted;
809
926
  el.muted = true;
810
927
  const p = el.play();
@@ -836,7 +953,7 @@ function createAudioBus(options) {
836
953
  sounds = next;
837
954
  },
838
955
  dispose() {
839
- cache.clear();
956
+ cache2.clear();
840
957
  }
841
958
  };
842
959
  }
@@ -1048,6 +1165,7 @@ function ChatProvider({
1048
1165
  autoCreateSession,
1049
1166
  streaming,
1050
1167
  audio,
1168
+ debug,
1051
1169
  children
1052
1170
  }) {
1053
1171
  const audioApi = useChatAudio(audio ?? {});
@@ -1062,6 +1180,7 @@ function ChatProvider({
1062
1180
  initialSessionId,
1063
1181
  autoCreateSession,
1064
1182
  streaming,
1183
+ debug,
1065
1184
  metadata: {
1066
1185
  locale: config.locale ?? config.prefs?.locale,
1067
1186
  slug: config.slug
@@ -2093,7 +2212,7 @@ function copy(text) {
2093
2212
  }
2094
2213
  __name(copy, "copy");
2095
2214
  function ChatRoot(props) {
2096
- const { transport, config, initialSessionId, autoCreateSession, streaming, audio, className, ...slots } = props;
2215
+ const { transport, config, initialSessionId, autoCreateSession, streaming, audio, debug, className, ...slots } = props;
2097
2216
  return /* @__PURE__ */ jsx(
2098
2217
  ChatProvider,
2099
2218
  {
@@ -2103,6 +2222,7 @@ function ChatRoot(props) {
2103
2222
  autoCreateSession,
2104
2223
  streaming,
2105
2224
  audio,
2225
+ debug,
2106
2226
  children: /* @__PURE__ */ jsx(ChatRootShell, { className, slots })
2107
2227
  }
2108
2228
  );
@@ -2213,6 +2333,6 @@ function copy2(text) {
2213
2333
  }
2214
2334
  __name(copy2, "copy");
2215
2335
 
2216
- export { Attachments, AttachmentsGrid, AttachmentsList, CHAT_EVENT_NAME, CSS_VARS, ChatProvider, ChatRoot, Composer, DEFAULT_LABELS, DEFAULT_SIDEBAR, DEFAULT_Z_INDEX, EmptyState, ErrorBanner, HOTKEYS, JumpToLatest, LIMITS, MessageActions, MessageBubble, MessageList, STORAGE_KEYS, Sources, StreamingIndicator, ToolCalls, createId, createTokenBuffer, deriveInitials, initialState, reducer, resolvePersona, useChat, useChatAudio, useChatAudioPrefs, useChatComposer, useChatContext, useChatContextOptional, useChatHistory, useChatLayout, useChatScroll };
2217
- //# sourceMappingURL=chunk-KRETIZU6.mjs.map
2218
- //# sourceMappingURL=chunk-KRETIZU6.mjs.map
2336
+ export { Attachments, AttachmentsGrid, AttachmentsList, CHAT_EVENT_NAME, CSS_VARS, ChatProvider, ChatRoot, Composer, DEFAULT_LABELS, DEFAULT_SIDEBAR, DEFAULT_Z_INDEX, EmptyState, ErrorBanner, HOTKEYS, JumpToLatest, LIMITS, MessageActions, MessageBubble, MessageList, STORAGE_KEYS, Sources, StreamingIndicator, ToolCalls, createId, createTokenBuffer, deriveInitials, getChatLogger, initialState, reducer, resolvePersona, useChat, useChatAudio, useChatAudioPrefs, useChatComposer, useChatContext, useChatContextOptional, useChatHistory, useChatLayout, useChatScroll };
2337
+ //# sourceMappingURL=chunk-QE25H6DP.mjs.map
2338
+ //# sourceMappingURL=chunk-QE25H6DP.mjs.map