@gendive/chatllm 0.5.0 → 0.6.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.
@@ -205,7 +205,9 @@ interface MessageListProps {
205
205
  onEdit: (message: ChatMessage) => void;
206
206
  onRegenerate: (id: string) => void;
207
207
  onQuote: (text: string) => void;
208
- onAskOtherModel?: (messageId: string, targetModel: string) => void;
208
+ onAskOtherModel?: (userMessageId: string, assistantMessageId: string, targetModel: string) => void;
209
+ onSetActiveAlternative?: (assistantMessageId: string, index: number) => void;
210
+ activeAlternatives?: Record<string, number>;
209
211
  models?: ModelConfig[];
210
212
  copiedId: string | null;
211
213
  editingId: string | null;
@@ -220,10 +222,11 @@ interface MessageBubbleProps {
220
222
  onRegenerate?: () => void;
221
223
  onQuote?: (text: string) => void;
222
224
  onAskOtherModel?: (targetModel: string) => void;
225
+ onAlternativeChange?: (index: number) => void;
226
+ nextAssistantMessage?: ChatMessage | null;
227
+ activeAlternativeIndex?: number;
223
228
  models?: ModelConfig[];
224
229
  alternatives?: AlternativeResponse[];
225
- activeAlternativeIndex?: number;
226
- onAlternativeChange?: (index: number) => void;
227
230
  }
228
231
  interface InputProps {
229
232
  value: string;
@@ -282,6 +285,7 @@ interface UseChatUIReturn {
282
285
  copiedMessageId: string | null;
283
286
  editingMessageId: string | null;
284
287
  personalization: PersonalizationConfig;
288
+ activeAlternatives: Record<string, number>;
285
289
  setInput: (value: string) => void;
286
290
  sendMessage: (content?: string) => Promise<void>;
287
291
  stopGeneration: () => void;
@@ -300,6 +304,8 @@ interface UseChatUIReturn {
300
304
  saveEdit: (content: string) => Promise<void>;
301
305
  regenerate: (messageId: string) => Promise<void>;
302
306
  askOtherModel: (messageId: string, targetModel: string) => Promise<void>;
307
+ setActiveAlternative: (assistantMessageId: string, index: number) => void;
308
+ getActiveAlternative: (assistantMessageId: string) => number;
303
309
  updatePersonalization: (config: Partial<PersonalizationConfig>) => void;
304
310
  models: ModelConfig[];
305
311
  }
@@ -205,7 +205,9 @@ interface MessageListProps {
205
205
  onEdit: (message: ChatMessage) => void;
206
206
  onRegenerate: (id: string) => void;
207
207
  onQuote: (text: string) => void;
208
- onAskOtherModel?: (messageId: string, targetModel: string) => void;
208
+ onAskOtherModel?: (userMessageId: string, assistantMessageId: string, targetModel: string) => void;
209
+ onSetActiveAlternative?: (assistantMessageId: string, index: number) => void;
210
+ activeAlternatives?: Record<string, number>;
209
211
  models?: ModelConfig[];
210
212
  copiedId: string | null;
211
213
  editingId: string | null;
@@ -220,10 +222,11 @@ interface MessageBubbleProps {
220
222
  onRegenerate?: () => void;
221
223
  onQuote?: (text: string) => void;
222
224
  onAskOtherModel?: (targetModel: string) => void;
225
+ onAlternativeChange?: (index: number) => void;
226
+ nextAssistantMessage?: ChatMessage | null;
227
+ activeAlternativeIndex?: number;
223
228
  models?: ModelConfig[];
224
229
  alternatives?: AlternativeResponse[];
225
- activeAlternativeIndex?: number;
226
- onAlternativeChange?: (index: number) => void;
227
230
  }
228
231
  interface InputProps {
229
232
  value: string;
@@ -282,6 +285,7 @@ interface UseChatUIReturn {
282
285
  copiedMessageId: string | null;
283
286
  editingMessageId: string | null;
284
287
  personalization: PersonalizationConfig;
288
+ activeAlternatives: Record<string, number>;
285
289
  setInput: (value: string) => void;
286
290
  sendMessage: (content?: string) => Promise<void>;
287
291
  stopGeneration: () => void;
@@ -300,6 +304,8 @@ interface UseChatUIReturn {
300
304
  saveEdit: (content: string) => Promise<void>;
301
305
  regenerate: (messageId: string) => Promise<void>;
302
306
  askOtherModel: (messageId: string, targetModel: string) => Promise<void>;
307
+ setActiveAlternative: (assistantMessageId: string, index: number) => void;
308
+ getActiveAlternative: (assistantMessageId: string) => number;
303
309
  updatePersonalization: (config: Partial<PersonalizationConfig>) => void;
304
310
  models: ModelConfig[];
305
311
  }
@@ -108,6 +108,7 @@ var useChatUI = (options) => {
108
108
  ...DEFAULT_PERSONALIZATION,
109
109
  ...initialPersonalization
110
110
  });
111
+ const [activeAlternatives, setActiveAlternatives] = (0, import_react.useState)({});
111
112
  const abortControllerRef = (0, import_react.useRef)(null);
112
113
  const currentSession = sessions.find((s) => s.id === currentSessionId) || null;
113
114
  const messages = currentSession?.messages || [];
@@ -707,6 +708,15 @@ ${currentSession.contextSummary}` },
707
708
  onSendMessage,
708
709
  onError
709
710
  ]);
711
+ const setActiveAlternative = (0, import_react.useCallback)((assistantMessageId, index) => {
712
+ setActiveAlternatives((prev) => ({
713
+ ...prev,
714
+ [assistantMessageId]: index
715
+ }));
716
+ }, []);
717
+ const getActiveAlternative = (0, import_react.useCallback)((assistantMessageId) => {
718
+ return activeAlternatives[assistantMessageId] ?? 0;
719
+ }, [activeAlternatives]);
710
720
  return {
711
721
  // State
712
722
  sessions,
@@ -723,6 +733,7 @@ ${currentSession.contextSummary}` },
723
733
  copiedMessageId,
724
734
  editingMessageId,
725
735
  personalization,
736
+ activeAlternatives,
726
737
  // Actions
727
738
  setInput,
728
739
  sendMessage,
@@ -742,6 +753,8 @@ ${currentSession.contextSummary}` },
742
753
  saveEdit,
743
754
  regenerate,
744
755
  askOtherModel,
756
+ setActiveAlternative,
757
+ getActiveAlternative,
745
758
  updatePersonalization,
746
759
  models
747
760
  };
@@ -2207,16 +2220,20 @@ var MessageBubble = ({
2207
2220
  models,
2208
2221
  alternatives,
2209
2222
  activeAlternativeIndex = 0,
2210
- onAlternativeChange
2223
+ onAlternativeChange,
2224
+ nextAssistantMessage
2211
2225
  }) => {
2212
2226
  const [showActions, setShowActions] = (0, import_react6.useState)(false);
2213
2227
  const [showModelMenu, setShowModelMenu] = (0, import_react6.useState)(false);
2214
2228
  const isUser = message.role === "user";
2215
2229
  const isAssistant = message.role === "assistant";
2216
- const otherModels = models?.filter((m) => m.id !== message.model) || [];
2217
- const displayContent = alternatives && alternatives.length > 0 && activeAlternativeIndex > 0 ? alternatives[activeAlternativeIndex - 1]?.content || message.content : message.content;
2218
- const displayModel = alternatives && alternatives.length > 0 && activeAlternativeIndex > 0 ? alternatives[activeAlternativeIndex - 1]?.model : message.model;
2219
- const displaySources = alternatives && alternatives.length > 0 && activeAlternativeIndex > 0 ? alternatives[activeAlternativeIndex - 1]?.sources : message.sources;
2230
+ const relevantAlternatives = isUser ? alternatives : message.alternatives;
2231
+ const relevantActiveIndex = activeAlternativeIndex;
2232
+ const currentAssistantModel = isUser ? nextAssistantMessage?.model : message.model;
2233
+ const otherModels = models?.filter((m) => m.id !== currentAssistantModel) || [];
2234
+ const displayContent = isAssistant && relevantAlternatives && relevantAlternatives.length > 0 && relevantActiveIndex > 0 ? relevantAlternatives[relevantActiveIndex - 1]?.content || message.content : message.content;
2235
+ const displayModel = isAssistant && relevantAlternatives && relevantAlternatives.length > 0 && relevantActiveIndex > 0 ? relevantAlternatives[relevantActiveIndex - 1]?.model : message.model;
2236
+ const displaySources = isAssistant && relevantAlternatives && relevantAlternatives.length > 0 && relevantActiveIndex > 0 ? relevantAlternatives[relevantActiveIndex - 1]?.sources : message.sources;
2220
2237
  const handleMouseUp = () => {
2221
2238
  if (!onQuote) return;
2222
2239
  const selection = window.getSelection();
@@ -2378,7 +2395,7 @@ var MessageBubble = ({
2378
2395
  ]
2379
2396
  }
2380
2397
  ),
2381
- alternatives && alternatives.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2398
+ isUser && relevantAlternatives && relevantAlternatives.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2382
2399
  "div",
2383
2400
  {
2384
2401
  style: {
@@ -2393,30 +2410,30 @@ var MessageBubble = ({
2393
2410
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2394
2411
  "button",
2395
2412
  {
2396
- onClick: () => onAlternativeChange?.(Math.max(0, activeAlternativeIndex - 1)),
2397
- disabled: activeAlternativeIndex === 0,
2413
+ onClick: () => onAlternativeChange?.(Math.max(0, relevantActiveIndex - 1)),
2414
+ disabled: relevantActiveIndex === 0,
2398
2415
  style: {
2399
2416
  ...navButtonStyle,
2400
- opacity: activeAlternativeIndex === 0 ? 0.5 : 1,
2401
- cursor: activeAlternativeIndex === 0 ? "not-allowed" : "pointer"
2417
+ opacity: relevantActiveIndex === 0 ? 0.5 : 1,
2418
+ cursor: relevantActiveIndex === 0 ? "not-allowed" : "pointer"
2402
2419
  },
2403
2420
  children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconSvg, { name: "arrow-left-line", size: 14 })
2404
2421
  }
2405
2422
  ),
2406
2423
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: { fontSize: "12px", color: "var(--chatllm-text-muted, #9ca3af)" }, children: [
2407
- activeAlternativeIndex + 1,
2424
+ relevantActiveIndex + 1,
2408
2425
  " / ",
2409
- alternatives.length + 1
2426
+ relevantAlternatives.length + 1
2410
2427
  ] }),
2411
2428
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2412
2429
  "button",
2413
2430
  {
2414
- onClick: () => onAlternativeChange?.(Math.min(alternatives.length, activeAlternativeIndex + 1)),
2415
- disabled: activeAlternativeIndex === alternatives.length,
2431
+ onClick: () => onAlternativeChange?.(Math.min(relevantAlternatives.length, relevantActiveIndex + 1)),
2432
+ disabled: relevantActiveIndex === relevantAlternatives.length,
2416
2433
  style: {
2417
2434
  ...navButtonStyle,
2418
- opacity: activeAlternativeIndex === alternatives.length ? 0.5 : 1,
2419
- cursor: activeAlternativeIndex === alternatives.length ? "not-allowed" : "pointer"
2435
+ opacity: relevantActiveIndex === relevantAlternatives.length ? 0.5 : 1,
2436
+ cursor: relevantActiveIndex === relevantAlternatives.length ? "not-allowed" : "pointer"
2420
2437
  },
2421
2438
  children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconSvg, { name: "arrow-right-line", size: 14 })
2422
2439
  }
@@ -2443,7 +2460,7 @@ var MessageBubble = ({
2443
2460
  ) }),
2444
2461
  isUser && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { onClick: onEdit, style: actionButtonStyle, title: "\uC218\uC815", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconSvg, { name: "edit-line", size: 16, color: "var(--chatllm-text-muted, #9ca3af)" }) }),
2445
2462
  isAssistant && onRegenerate && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { onClick: onRegenerate, style: actionButtonStyle, title: "\uB2E4\uC2DC \uC0DD\uC131", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconSvg, { name: "refresh-line", size: 16, color: "var(--chatllm-text-muted, #9ca3af)" }) }),
2446
- isAssistant && onAskOtherModel && otherModels.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { position: "relative" }, children: [
2463
+ isUser && onAskOtherModel && otherModels.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { position: "relative" }, children: [
2447
2464
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2448
2465
  "button",
2449
2466
  {
@@ -2593,6 +2610,8 @@ var MessageList = ({
2593
2610
  onRegenerate,
2594
2611
  onQuote,
2595
2612
  onAskOtherModel,
2613
+ onSetActiveAlternative,
2614
+ activeAlternatives = {},
2596
2615
  models,
2597
2616
  copiedId,
2598
2617
  editingId
@@ -2647,23 +2666,31 @@ var MessageList = ({
2647
2666
  },
2648
2667
  onMouseUp: handleMouseUp,
2649
2668
  children: [
2650
- messages.map((message, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2651
- MessageBubble,
2652
- {
2653
- message,
2654
- isLoading: isLoading && index === messages.length - 1 && message.role === "assistant",
2655
- isCopied: copiedId === message.id,
2656
- isEditing: editingId === message.id,
2657
- onCopy: () => onCopy(message.content, message.id),
2658
- onEdit: () => onEdit(message),
2659
- onRegenerate: message.role === "assistant" ? () => onRegenerate(message.id) : void 0,
2660
- onQuote,
2661
- onAskOtherModel: message.role === "assistant" && onAskOtherModel ? (targetModel) => onAskOtherModel(message.id, targetModel) : void 0,
2662
- models,
2663
- alternatives: message.alternatives
2664
- },
2665
- message.id
2666
- )),
2669
+ messages.map((message, index) => {
2670
+ const nextAssistant = message.role === "user" && index + 1 < messages.length ? messages[index + 1] : null;
2671
+ const assistantForAlts = nextAssistant?.role === "assistant" ? nextAssistant : null;
2672
+ const activeAltIndex = assistantForAlts ? activeAlternatives[assistantForAlts.id] ?? 0 : 0;
2673
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2674
+ MessageBubble,
2675
+ {
2676
+ message,
2677
+ isLoading: isLoading && index === messages.length - 1 && message.role === "assistant",
2678
+ isCopied: copiedId === message.id,
2679
+ isEditing: editingId === message.id,
2680
+ onCopy: () => onCopy(message.content, message.id),
2681
+ onEdit: () => onEdit(message),
2682
+ onRegenerate: message.role === "assistant" ? () => onRegenerate(message.id) : void 0,
2683
+ onQuote,
2684
+ nextAssistantMessage: assistantForAlts,
2685
+ activeAlternativeIndex: message.role === "user" ? activeAltIndex : activeAlternatives[message.id] ?? 0,
2686
+ onAskOtherModel: message.role === "user" && assistantForAlts && onAskOtherModel ? (targetModel) => onAskOtherModel(message.id, assistantForAlts.id, targetModel) : void 0,
2687
+ onAlternativeChange: message.role === "user" && assistantForAlts && onSetActiveAlternative ? (idx) => onSetActiveAlternative(assistantForAlts.id, idx) : message.role === "assistant" && onSetActiveAlternative ? (idx) => onSetActiveAlternative(message.id, idx) : void 0,
2688
+ models,
2689
+ alternatives: message.role === "assistant" ? message.alternatives : assistantForAlts?.alternatives
2690
+ },
2691
+ message.id
2692
+ );
2693
+ }),
2667
2694
  selectionPosition && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2668
2695
  "div",
2669
2696
  {