@djangocfg/ui-tools 2.1.336 → 2.1.337

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.
@@ -4,6 +4,7 @@ var chunkB5AWZOHJ_cjs = require('./chunk-B5AWZOHJ.cjs');
4
4
  var chunkOLISEQHS_cjs = require('./chunk-OLISEQHS.cjs');
5
5
  var react = require('react');
6
6
  var lib = require('@djangocfg/ui-core/lib');
7
+ var consola = require('consola');
7
8
  var hooks = require('@djangocfg/ui-core/hooks');
8
9
  var zustand = require('zustand');
9
10
  var middleware = require('zustand/middleware');
@@ -296,6 +297,32 @@ function createId(prefix = "m") {
296
297
  return `${prefix}_${Date.now().toString(36)}_${counter.toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
297
298
  }
298
299
  chunkOLISEQHS_cjs.__name(createId, "createId");
300
+ var SCOPES = ["bootstrap", "transport", "stream", "lifecycle", "tools", "error"];
301
+ var cache = /* @__PURE__ */ new Map();
302
+ function buildLogger(enabled) {
303
+ const root = consola.consola.withTag("chat");
304
+ const subs = Object.fromEntries(
305
+ SCOPES.map((scope) => [scope, root.withTag(scope)])
306
+ );
307
+ if (!enabled) {
308
+ for (const scope of SCOPES) {
309
+ if (scope === "error") continue;
310
+ subs[scope].level = -999;
311
+ }
312
+ }
313
+ return { ...subs, enabled };
314
+ }
315
+ chunkOLISEQHS_cjs.__name(buildLogger, "buildLogger");
316
+ function getChatLogger(debug) {
317
+ const enabled = debug ?? lib.isDev;
318
+ let logger = cache.get(enabled);
319
+ if (!logger) {
320
+ logger = buildLogger(enabled);
321
+ cache.set(enabled, logger);
322
+ }
323
+ return logger;
324
+ }
325
+ chunkOLISEQHS_cjs.__name(getChatLogger, "getChatLogger");
299
326
 
300
327
  // src/tools/Chat/core/markdown.ts
301
328
  function createTokenBuffer(onFlush, windowMs = LIMITS.streamCoalesceMs) {
@@ -343,30 +370,53 @@ function useChat(config) {
343
370
  const lastErrorRef = react.useRef(null);
344
371
  const initRef = react.useRef(false);
345
372
  const streamingMsgIdRef = react.useRef(null);
373
+ const bootstrapRef = react.useRef(null);
346
374
  const { transport, autoCreateSession = true, streaming = true, pageSize = LIMITS.pageSize } = config;
375
+ const log = getChatLogger(config.debug);
347
376
  react.useEffect(() => {
348
377
  if (initRef.current) return;
349
378
  initRef.current = true;
350
379
  let cancelled = false;
380
+ if (config.initialSessionId || autoCreateSession) {
381
+ dispatch({ type: "HISTORY_LOAD_START" });
382
+ }
383
+ log.bootstrap.info("start", {
384
+ mode: config.initialSessionId ? "resume" : autoCreateSession ? "create" : "idle",
385
+ initialSessionId: config.initialSessionId
386
+ });
351
387
  const run = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(async () => {
388
+ const t0 = performance.now();
352
389
  try {
353
390
  if (config.initialSessionId) {
354
391
  dispatch({
355
392
  type: "SESSION_SET",
356
393
  sessionId: config.initialSessionId
357
394
  });
358
- dispatch({ type: "HISTORY_LOAD_START" });
359
395
  const page = await transport.loadHistory(config.initialSessionId, null, pageSize);
360
- if (cancelled) return;
396
+ if (cancelled) {
397
+ log.bootstrap.debug("cancelled (post-loadHistory)");
398
+ return null;
399
+ }
361
400
  dispatch({
362
401
  type: "HISTORY_LOAD_DONE",
363
402
  messages: page.messages,
364
403
  hasMore: page.hasMore,
365
404
  cursor: page.nextCursor
366
405
  });
367
- } 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) {
368
415
  const info = await transport.createSession({ metadata: config.metadata });
369
- if (cancelled) return;
416
+ if (cancelled) {
417
+ log.bootstrap.debug("cancelled (post-createSession)");
418
+ return null;
419
+ }
370
420
  dispatch({
371
421
  type: "SESSION_SET",
372
422
  sessionId: info.sessionId,
@@ -374,19 +424,47 @@ function useChat(config) {
374
424
  hasMore: info.hasMore ?? false,
375
425
  cursor: info.cursor ?? null
376
426
  });
427
+ dispatch({
428
+ type: "HISTORY_LOAD_DONE",
429
+ messages: info.messages ?? [],
430
+ hasMore: info.hasMore ?? false,
431
+ cursor: info.cursor ?? null
432
+ });
433
+ log.bootstrap.success("created", {
434
+ sessionId: info.sessionId,
435
+ resumed: info.resumed ?? false,
436
+ elapsedMs: Math.round(performance.now() - t0)
437
+ });
438
+ return info.sessionId;
377
439
  }
440
+ log.bootstrap.debug("idle (no initialSessionId, autoCreateSession=false)");
441
+ return null;
378
442
  } catch (err) {
443
+ if (cancelled) {
444
+ log.bootstrap.debug("cancelled (in catch)");
445
+ return null;
446
+ }
379
447
  const e = err instanceof Error ? err : new Error(String(err));
380
448
  lastErrorRef.current = e;
381
449
  dispatch({ type: "ERROR_SET", error: e.message });
382
450
  config.onError?.(e);
451
+ log.error.error("bootstrap failed", { message: e.message, elapsedMs: Math.round(performance.now() - t0) });
452
+ return null;
383
453
  }
384
454
  }, "run");
385
- void run();
455
+ bootstrapRef.current = run();
386
456
  return () => {
387
457
  cancelled = true;
388
458
  };
389
459
  }, []);
460
+ const awaitSession = react.useCallback(async () => {
461
+ if (stateRef.current.sessionId) return stateRef.current.sessionId;
462
+ if (bootstrapRef.current) {
463
+ const id = await bootstrapRef.current;
464
+ if (id) return id;
465
+ }
466
+ return stateRef.current.sessionId;
467
+ }, []);
390
468
  const consumeStream = react.useCallback(
391
469
  async (sessionId, content, attachments) => {
392
470
  const ctrl = new AbortController();
@@ -395,9 +473,13 @@ function useChat(config) {
395
473
  streamingMsgIdRef.current = assistantId;
396
474
  dispatch({ type: "STREAM_START", id: assistantId });
397
475
  config.onStreamStart?.(assistantId);
476
+ log.stream.info("start", { sessionId, assistantId, chars: content.length });
398
477
  const tokenBuffer = createTokenBuffer(
399
478
  (delta) => dispatch({ type: "STREAM_CHUNK", delta })
400
479
  );
480
+ let chunkCount = 0;
481
+ let charsReceived = 0;
482
+ const t0 = performance.now();
401
483
  try {
402
484
  const iterator = transport.stream(sessionId, content, {
403
485
  signal: ctrl.signal,
@@ -414,17 +496,25 @@ function useChat(config) {
414
496
  }
415
497
  const finalMsg = stateRef.current.messages.find((m) => m.id === assistantId);
416
498
  if (finalMsg) config.onMessageEnd?.(finalMsg);
499
+ log.stream.success("done", {
500
+ assistantId,
501
+ chunks: chunkCount,
502
+ chars: charsReceived,
503
+ elapsedMs: Math.round(performance.now() - t0)
504
+ });
417
505
  } catch (err) {
418
506
  tokenBuffer.close();
419
507
  if (ctrl.signal.aborted) {
420
508
  const partial = stateRef.current.messages.find((m) => m.id === assistantId)?.content ?? "";
421
509
  dispatch({ type: "STREAM_CANCELLED", id: assistantId, partialText: partial });
510
+ log.stream.warn("cancelled", { assistantId, partialChars: partial.length });
422
511
  return;
423
512
  }
424
513
  const e = err instanceof Error ? err : new Error(String(err));
425
514
  lastErrorRef.current = e;
426
515
  dispatch({ type: "STREAM_ERROR", id: assistantId, message: e.message });
427
516
  config.onError?.(e);
517
+ log.error.error("stream failed", { assistantId, message: e.message });
428
518
  } finally {
429
519
  tokenBuffer.close();
430
520
  if (abortRef.current === ctrl) abortRef.current = null;
@@ -434,13 +524,17 @@ function useChat(config) {
434
524
  switch (ev.type) {
435
525
  case "message_start":
436
526
  ev.messageId;
527
+ log.stream.debug("message_start", { messageId: ev.messageId });
437
528
  return;
438
529
  case "chunk":
439
530
  tokenBuffer.push(ev.delta);
531
+ chunkCount += 1;
532
+ charsReceived += ev.delta.length;
440
533
  return;
441
534
  case "tool_activity":
442
535
  tokenBuffer.flush();
443
536
  dispatch({ type: "STREAM_TOOL_ACTIVITY", tool: ev.tool });
537
+ log.tools.debug("activity", { tool: ev.tool, status: ev.status });
444
538
  return;
445
539
  case "tool_call_start": {
446
540
  tokenBuffer.flush();
@@ -457,6 +551,7 @@ function useChat(config) {
457
551
  messageId: assistantId,
458
552
  toolCall
459
553
  });
554
+ log.tools.info("call_start", { toolId: ev.toolId, name: ev.name });
460
555
  return;
461
556
  }
462
557
  case "tool_call_delta":
@@ -475,6 +570,7 @@ function useChat(config) {
475
570
  output: ev.output,
476
571
  status: ev.status
477
572
  });
573
+ log.tools.info("call_end", { toolId: ev.toolId, status: ev.status });
478
574
  return;
479
575
  case "message_end":
480
576
  tokenBuffer.flush();
@@ -485,6 +581,11 @@ function useChat(config) {
485
581
  tokensOut: ev.tokensOut,
486
582
  sources: ev.sources
487
583
  });
584
+ log.stream.debug("message_end", {
585
+ tokensIn: ev.tokensIn,
586
+ tokensOut: ev.tokensOut,
587
+ sources: ev.sources?.length ?? 0
588
+ });
488
589
  return;
489
590
  case "error":
490
591
  tokenBuffer.flush();
@@ -493,6 +594,7 @@ function useChat(config) {
493
594
  id: assistantId,
494
595
  message: ev.message
495
596
  });
597
+ log.error.error("stream event error", { code: ev.code, message: ev.message });
496
598
  return;
497
599
  }
498
600
  }
@@ -529,16 +631,28 @@ function useChat(config) {
529
631
  );
530
632
  const sendMessage = react.useCallback(
531
633
  async (content, attachments) => {
532
- const sessionId = stateRef.current.sessionId;
634
+ log.lifecycle.info("sendMessage", {
635
+ chars: content.length,
636
+ attachments: attachments?.length ?? 0,
637
+ hasSession: !!stateRef.current.sessionId
638
+ });
639
+ const sessionId = await awaitSession();
533
640
  if (!sessionId) {
534
641
  const e = new Error("No active session");
535
642
  lastErrorRef.current = e;
536
643
  dispatch({ type: "ERROR_SET", error: e.message });
537
644
  config.onError?.(e);
645
+ log.error.error("sendMessage aborted: no session");
646
+ return;
647
+ }
648
+ if (!content.trim() && !(attachments && attachments.length > 0)) {
649
+ log.lifecycle.debug("sendMessage skipped (empty)");
650
+ return;
651
+ }
652
+ if (stateRef.current.isStreaming) {
653
+ log.lifecycle.debug("sendMessage skipped (already streaming)");
538
654
  return;
539
655
  }
540
- if (!content.trim() && !(attachments && attachments.length > 0)) return;
541
- if (stateRef.current.isStreaming) return;
542
656
  const userMsg = {
543
657
  id: createId("u"),
544
658
  role: "user",
@@ -555,13 +669,14 @@ function useChat(config) {
555
669
  await consumeBuffered(sessionId, content, attachments);
556
670
  }
557
671
  },
558
- [streaming, consumeStream, consumeBuffered, config]
672
+ [streaming, consumeStream, consumeBuffered, config, awaitSession]
559
673
  );
560
674
  const cancelStream = react.useCallback(() => {
561
675
  abortRef.current?.abort();
562
676
  }, []);
563
677
  const regenerate = react.useCallback(
564
678
  async (messageId) => {
679
+ log.lifecycle.info("regenerate", { messageId: messageId ?? "(last)" });
565
680
  const messages = stateRef.current.messages;
566
681
  let targetUserIdx = -1;
567
682
  if (messageId) {
@@ -577,7 +692,7 @@ function useChat(config) {
577
692
  for (let i = messages.length - 1; i > targetUserIdx; i -= 1) {
578
693
  dispatch({ type: "MESSAGE_DELETE", id: messages[i].id });
579
694
  }
580
- const sessionId = stateRef.current.sessionId;
695
+ const sessionId = await awaitSession();
581
696
  if (!sessionId) return;
582
697
  if (streaming) {
583
698
  await consumeStream(sessionId, userMsg.content, userMsg.attachments);
@@ -585,7 +700,7 @@ function useChat(config) {
585
700
  await consumeBuffered(sessionId, userMsg.content, userMsg.attachments);
586
701
  }
587
702
  },
588
- [streaming, consumeStream, consumeBuffered]
703
+ [streaming, consumeStream, consumeBuffered, awaitSession]
589
704
  );
590
705
  const editMessage = react.useCallback(
591
706
  async (id, content) => {
@@ -629,6 +744,7 @@ function useChat(config) {
629
744
  }
630
745
  }, [transport, pageSize, config]);
631
746
  const newSession = react.useCallback(async () => {
747
+ log.lifecycle.info("newSession", { previous: stateRef.current.sessionId });
632
748
  abortRef.current?.abort();
633
749
  const previous = stateRef.current.sessionId;
634
750
  if (previous) {
@@ -647,11 +763,13 @@ function useChat(config) {
647
763
  hasMore: info.hasMore ?? false,
648
764
  cursor: info.cursor ?? null
649
765
  });
766
+ log.lifecycle.success("newSession ok", { sessionId: info.sessionId });
650
767
  } catch (err) {
651
768
  const e = err instanceof Error ? err : new Error(String(err));
652
769
  lastErrorRef.current = e;
653
770
  dispatch({ type: "ERROR_SET", error: e.message });
654
771
  config.onError?.(e);
772
+ log.error.error("newSession failed", { message: e.message });
655
773
  }
656
774
  }, [transport, config]);
657
775
  return {
@@ -765,14 +883,14 @@ function createAudioBus(options) {
765
883
  return noopBus();
766
884
  }
767
885
  let sounds = options.sounds;
768
- const cache = /* @__PURE__ */ new Map();
886
+ const cache2 = /* @__PURE__ */ new Map();
769
887
  const getOrCreate = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((url) => {
770
- const hit = cache.get(url);
888
+ const hit = cache2.get(url);
771
889
  if (hit) return hit;
772
890
  const el = new Audio(url);
773
891
  el.preload = "auto";
774
892
  el.crossOrigin = "anonymous";
775
- cache.set(url, el);
893
+ cache2.set(url, el);
776
894
  return el;
777
895
  }, "getOrCreate");
778
896
  const resolveUrl = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((event) => {
@@ -806,7 +924,7 @@ function createAudioBus(options) {
806
924
  }, "preload");
807
925
  const unlock = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
808
926
  if (unlocked) return;
809
- for (const el of cache.values()) {
927
+ for (const el of cache2.values()) {
810
928
  const wasMuted = el.muted;
811
929
  el.muted = true;
812
930
  const p = el.play();
@@ -838,7 +956,7 @@ function createAudioBus(options) {
838
956
  sounds = next;
839
957
  },
840
958
  dispose() {
841
- cache.clear();
959
+ cache2.clear();
842
960
  }
843
961
  };
844
962
  }
@@ -1050,6 +1168,7 @@ function ChatProvider({
1050
1168
  autoCreateSession,
1051
1169
  streaming,
1052
1170
  audio,
1171
+ debug,
1053
1172
  children
1054
1173
  }) {
1055
1174
  const audioApi = useChatAudio(audio ?? {});
@@ -1064,6 +1183,7 @@ function ChatProvider({
1064
1183
  initialSessionId,
1065
1184
  autoCreateSession,
1066
1185
  streaming,
1186
+ debug,
1067
1187
  metadata: {
1068
1188
  locale: config.locale ?? config.prefs?.locale,
1069
1189
  slug: config.slug
@@ -2095,7 +2215,7 @@ function copy(text) {
2095
2215
  }
2096
2216
  chunkOLISEQHS_cjs.__name(copy, "copy");
2097
2217
  function ChatRoot(props) {
2098
- const { transport, config, initialSessionId, autoCreateSession, streaming, audio, className, ...slots } = props;
2218
+ const { transport, config, initialSessionId, autoCreateSession, streaming, audio, debug, className, ...slots } = props;
2099
2219
  return /* @__PURE__ */ jsxRuntime.jsx(
2100
2220
  ChatProvider,
2101
2221
  {
@@ -2105,6 +2225,7 @@ function ChatRoot(props) {
2105
2225
  autoCreateSession,
2106
2226
  streaming,
2107
2227
  audio,
2228
+ debug,
2108
2229
  children: /* @__PURE__ */ jsxRuntime.jsx(ChatRootShell, { className, slots })
2109
2230
  }
2110
2231
  );
@@ -2241,6 +2362,7 @@ exports.ToolCalls = ToolCalls;
2241
2362
  exports.createId = createId;
2242
2363
  exports.createTokenBuffer = createTokenBuffer;
2243
2364
  exports.deriveInitials = deriveInitials;
2365
+ exports.getChatLogger = getChatLogger;
2244
2366
  exports.initialState = initialState;
2245
2367
  exports.reducer = reducer;
2246
2368
  exports.resolvePersona = resolvePersona;
@@ -2253,5 +2375,5 @@ exports.useChatContextOptional = useChatContextOptional;
2253
2375
  exports.useChatHistory = useChatHistory;
2254
2376
  exports.useChatLayout = useChatLayout;
2255
2377
  exports.useChatScroll = useChatScroll;
2256
- //# sourceMappingURL=chunk-NRXYYO5V.cjs.map
2257
- //# sourceMappingURL=chunk-NRXYYO5V.cjs.map
2378
+ //# sourceMappingURL=chunk-ZWPBBAR2.cjs.map
2379
+ //# sourceMappingURL=chunk-ZWPBBAR2.cjs.map