@gendive/chatllm 0.3.1 → 0.4.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/dist/react/index.d.mts +6 -0
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.js +267 -3
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +267 -3
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/react/index.d.mts
CHANGED
|
@@ -191,6 +191,8 @@ interface MessageListProps {
|
|
|
191
191
|
onEdit: (message: ChatMessage) => void;
|
|
192
192
|
onRegenerate: (id: string) => void;
|
|
193
193
|
onQuote: (text: string) => void;
|
|
194
|
+
onAskOtherModel?: (messageId: string, targetModel: string) => void;
|
|
195
|
+
models?: ModelConfig[];
|
|
194
196
|
copiedId: string | null;
|
|
195
197
|
editingId: string | null;
|
|
196
198
|
}
|
|
@@ -203,6 +205,8 @@ interface MessageBubbleProps {
|
|
|
203
205
|
onEdit: () => void;
|
|
204
206
|
onRegenerate?: () => void;
|
|
205
207
|
onQuote?: (text: string) => void;
|
|
208
|
+
onAskOtherModel?: (targetModel: string) => void;
|
|
209
|
+
models?: ModelConfig[];
|
|
206
210
|
alternatives?: AlternativeResponse[];
|
|
207
211
|
activeAlternativeIndex?: number;
|
|
208
212
|
onAlternativeChange?: (index: number) => void;
|
|
@@ -281,7 +285,9 @@ interface UseChatUIReturn {
|
|
|
281
285
|
cancelEdit: () => void;
|
|
282
286
|
saveEdit: (content: string) => Promise<void>;
|
|
283
287
|
regenerate: (messageId: string) => Promise<void>;
|
|
288
|
+
askOtherModel: (messageId: string, targetModel: string) => Promise<void>;
|
|
284
289
|
updatePersonalization: (config: Partial<PersonalizationConfig>) => void;
|
|
290
|
+
models: ModelConfig[];
|
|
285
291
|
}
|
|
286
292
|
|
|
287
293
|
/**
|
package/dist/react/index.d.ts
CHANGED
|
@@ -191,6 +191,8 @@ interface MessageListProps {
|
|
|
191
191
|
onEdit: (message: ChatMessage) => void;
|
|
192
192
|
onRegenerate: (id: string) => void;
|
|
193
193
|
onQuote: (text: string) => void;
|
|
194
|
+
onAskOtherModel?: (messageId: string, targetModel: string) => void;
|
|
195
|
+
models?: ModelConfig[];
|
|
194
196
|
copiedId: string | null;
|
|
195
197
|
editingId: string | null;
|
|
196
198
|
}
|
|
@@ -203,6 +205,8 @@ interface MessageBubbleProps {
|
|
|
203
205
|
onEdit: () => void;
|
|
204
206
|
onRegenerate?: () => void;
|
|
205
207
|
onQuote?: (text: string) => void;
|
|
208
|
+
onAskOtherModel?: (targetModel: string) => void;
|
|
209
|
+
models?: ModelConfig[];
|
|
206
210
|
alternatives?: AlternativeResponse[];
|
|
207
211
|
activeAlternativeIndex?: number;
|
|
208
212
|
onAlternativeChange?: (index: number) => void;
|
|
@@ -281,7 +285,9 @@ interface UseChatUIReturn {
|
|
|
281
285
|
cancelEdit: () => void;
|
|
282
286
|
saveEdit: (content: string) => Promise<void>;
|
|
283
287
|
regenerate: (messageId: string) => Promise<void>;
|
|
288
|
+
askOtherModel: (messageId: string, targetModel: string) => Promise<void>;
|
|
284
289
|
updatePersonalization: (config: Partial<PersonalizationConfig>) => void;
|
|
290
|
+
models: ModelConfig[];
|
|
285
291
|
}
|
|
286
292
|
|
|
287
293
|
/**
|
package/dist/react/index.js
CHANGED
|
@@ -539,6 +539,153 @@ ${contextSummary}` },
|
|
|
539
539
|
setInput(userMessage.content);
|
|
540
540
|
await sendMessage(userMessage.content);
|
|
541
541
|
}, [currentSession, currentSessionId, isLoading, sendMessage]);
|
|
542
|
+
const askOtherModel = (0, import_react.useCallback)(async (messageId, targetModel) => {
|
|
543
|
+
if (!currentSession || !currentSessionId || isLoading) return;
|
|
544
|
+
const assistantIndex = currentSession.messages.findIndex((m) => m.id === messageId);
|
|
545
|
+
if (assistantIndex === -1) return;
|
|
546
|
+
const assistantMessage = currentSession.messages[assistantIndex];
|
|
547
|
+
if (assistantMessage.role !== "assistant") return;
|
|
548
|
+
const userMessage = currentSession.messages[assistantIndex - 1];
|
|
549
|
+
if (!userMessage || userMessage.role !== "user") return;
|
|
550
|
+
setIsLoading(true);
|
|
551
|
+
abortControllerRef.current = new AbortController();
|
|
552
|
+
try {
|
|
553
|
+
const messagesToSend = currentSession.messages.slice(0, assistantIndex);
|
|
554
|
+
let chatMessages;
|
|
555
|
+
if (currentSession.contextSummary) {
|
|
556
|
+
const recentMessages = messagesToSend.slice(-keepRecentMessages);
|
|
557
|
+
chatMessages = [
|
|
558
|
+
{ role: "system", content: `[\uC774\uC804 \uB300\uD654 \uC694\uC57D]
|
|
559
|
+
${currentSession.contextSummary}` },
|
|
560
|
+
...recentMessages.map((m) => ({ role: m.role, content: m.content }))
|
|
561
|
+
];
|
|
562
|
+
} else {
|
|
563
|
+
chatMessages = messagesToSend.map((m) => ({ role: m.role, content: m.content }));
|
|
564
|
+
}
|
|
565
|
+
const baseSystemPrompt = buildSystemPrompt();
|
|
566
|
+
const messagesForApi = baseSystemPrompt ? [{ role: "system", content: baseSystemPrompt }, ...chatMessages] : chatMessages;
|
|
567
|
+
const modelConfig = models.find((m) => m.id === targetModel);
|
|
568
|
+
const provider = modelConfig?.provider || "ollama";
|
|
569
|
+
let responseContent = "";
|
|
570
|
+
if (onSendMessage) {
|
|
571
|
+
const result = await onSendMessage({
|
|
572
|
+
messages: messagesForApi,
|
|
573
|
+
model: targetModel,
|
|
574
|
+
provider,
|
|
575
|
+
apiKey,
|
|
576
|
+
systemPrompt: baseSystemPrompt
|
|
577
|
+
});
|
|
578
|
+
if (typeof result === "string") {
|
|
579
|
+
responseContent = result;
|
|
580
|
+
} else {
|
|
581
|
+
const reader = result.getReader();
|
|
582
|
+
const decoder = new TextDecoder();
|
|
583
|
+
let buffer = "";
|
|
584
|
+
while (true) {
|
|
585
|
+
const { done, value } = await reader.read();
|
|
586
|
+
if (done) break;
|
|
587
|
+
buffer += decoder.decode(value, { stream: true });
|
|
588
|
+
const lines = buffer.split("\n");
|
|
589
|
+
buffer = lines.pop() || "";
|
|
590
|
+
for (const line of lines) {
|
|
591
|
+
if (line.startsWith("data: ")) {
|
|
592
|
+
const data = line.slice(6);
|
|
593
|
+
if (data === "[DONE]") continue;
|
|
594
|
+
try {
|
|
595
|
+
const parsed = JSON.parse(data);
|
|
596
|
+
if (parsed.content) responseContent += parsed.content;
|
|
597
|
+
} catch {
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
} else {
|
|
604
|
+
const response = await fetch(apiEndpoint, {
|
|
605
|
+
method: "POST",
|
|
606
|
+
headers: { "Content-Type": "application/json" },
|
|
607
|
+
body: JSON.stringify({
|
|
608
|
+
messages: messagesForApi,
|
|
609
|
+
model: targetModel,
|
|
610
|
+
provider,
|
|
611
|
+
apiKey: provider === "devdive" ? apiKey : void 0
|
|
612
|
+
}),
|
|
613
|
+
signal: abortControllerRef.current.signal
|
|
614
|
+
});
|
|
615
|
+
if (!response.ok) throw new Error("API error");
|
|
616
|
+
const reader = response.body?.getReader();
|
|
617
|
+
if (!reader) throw new Error("No reader");
|
|
618
|
+
const decoder = new TextDecoder();
|
|
619
|
+
let buffer = "";
|
|
620
|
+
while (true) {
|
|
621
|
+
const { done, value } = await reader.read();
|
|
622
|
+
if (done) break;
|
|
623
|
+
buffer += decoder.decode(value, { stream: true });
|
|
624
|
+
const lines = buffer.split("\n");
|
|
625
|
+
buffer = lines.pop() || "";
|
|
626
|
+
for (const line of lines) {
|
|
627
|
+
if (line.startsWith("data: ")) {
|
|
628
|
+
const data = line.slice(6);
|
|
629
|
+
if (data === "[DONE]") continue;
|
|
630
|
+
try {
|
|
631
|
+
const parsed = JSON.parse(data);
|
|
632
|
+
if (parsed.content) responseContent += parsed.content;
|
|
633
|
+
} catch {
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
const alternative = {
|
|
640
|
+
id: generateId("alt"),
|
|
641
|
+
model: targetModel,
|
|
642
|
+
content: responseContent,
|
|
643
|
+
timestamp: Date.now()
|
|
644
|
+
};
|
|
645
|
+
const capturedSessionId = currentSessionId;
|
|
646
|
+
setSessions(
|
|
647
|
+
(prev) => prev.map((s) => {
|
|
648
|
+
if (s.id === capturedSessionId) {
|
|
649
|
+
return {
|
|
650
|
+
...s,
|
|
651
|
+
messages: s.messages.map((m) => {
|
|
652
|
+
if (m.id === messageId) {
|
|
653
|
+
const existingAlts = m.alternatives || [];
|
|
654
|
+
return {
|
|
655
|
+
...m,
|
|
656
|
+
alternatives: [...existingAlts, alternative]
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
return m;
|
|
660
|
+
}),
|
|
661
|
+
updatedAt: Date.now()
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
return s;
|
|
665
|
+
})
|
|
666
|
+
);
|
|
667
|
+
} catch (error) {
|
|
668
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
const err = error instanceof Error ? error : new Error("Unknown error");
|
|
672
|
+
onError?.(err);
|
|
673
|
+
} finally {
|
|
674
|
+
setIsLoading(false);
|
|
675
|
+
abortControllerRef.current = null;
|
|
676
|
+
}
|
|
677
|
+
}, [
|
|
678
|
+
currentSession,
|
|
679
|
+
currentSessionId,
|
|
680
|
+
isLoading,
|
|
681
|
+
keepRecentMessages,
|
|
682
|
+
buildSystemPrompt,
|
|
683
|
+
models,
|
|
684
|
+
apiEndpoint,
|
|
685
|
+
apiKey,
|
|
686
|
+
onSendMessage,
|
|
687
|
+
onError
|
|
688
|
+
]);
|
|
542
689
|
return {
|
|
543
690
|
// State
|
|
544
691
|
sessions,
|
|
@@ -573,7 +720,9 @@ ${contextSummary}` },
|
|
|
573
720
|
cancelEdit,
|
|
574
721
|
saveEdit,
|
|
575
722
|
regenerate,
|
|
576
|
-
|
|
723
|
+
askOtherModel,
|
|
724
|
+
updatePersonalization,
|
|
725
|
+
models
|
|
577
726
|
};
|
|
578
727
|
};
|
|
579
728
|
|
|
@@ -2033,13 +2182,17 @@ var MessageBubble = ({
|
|
|
2033
2182
|
onEdit,
|
|
2034
2183
|
onRegenerate,
|
|
2035
2184
|
onQuote,
|
|
2185
|
+
onAskOtherModel,
|
|
2186
|
+
models,
|
|
2036
2187
|
alternatives,
|
|
2037
2188
|
activeAlternativeIndex = 0,
|
|
2038
2189
|
onAlternativeChange
|
|
2039
2190
|
}) => {
|
|
2040
2191
|
const [showActions, setShowActions] = (0, import_react6.useState)(false);
|
|
2192
|
+
const [showModelMenu, setShowModelMenu] = (0, import_react6.useState)(false);
|
|
2041
2193
|
const isUser = message.role === "user";
|
|
2042
2194
|
const isAssistant = message.role === "assistant";
|
|
2195
|
+
const otherModels = models?.filter((m) => m.id !== message.model) || [];
|
|
2043
2196
|
const displayContent = alternatives && alternatives.length > 0 && activeAlternativeIndex > 0 ? alternatives[activeAlternativeIndex - 1]?.content || message.content : message.content;
|
|
2044
2197
|
const displayModel = alternatives && alternatives.length > 0 && activeAlternativeIndex > 0 ? alternatives[activeAlternativeIndex - 1]?.model : message.model;
|
|
2045
2198
|
const handleMouseUp = () => {
|
|
@@ -2230,7 +2383,110 @@ var MessageBubble = ({
|
|
|
2230
2383
|
}
|
|
2231
2384
|
) }),
|
|
2232
2385
|
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)" }) }),
|
|
2233
|
-
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)" }) })
|
|
2386
|
+
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)" }) }),
|
|
2387
|
+
isAssistant && onAskOtherModel && otherModels.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { position: "relative" }, children: [
|
|
2388
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2389
|
+
"button",
|
|
2390
|
+
{
|
|
2391
|
+
onClick: () => setShowModelMenu(!showModelMenu),
|
|
2392
|
+
style: actionButtonStyle,
|
|
2393
|
+
title: "\uB2E4\uB978 \uBAA8\uB378\uC5D0\uAC8C \uC9C8\uBB38",
|
|
2394
|
+
children: [
|
|
2395
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconSvg, { name: "robot-line", size: 16, color: "var(--chatllm-text-muted, #9ca3af)" }),
|
|
2396
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2397
|
+
IconSvg,
|
|
2398
|
+
{
|
|
2399
|
+
name: "arrow-down-s-line",
|
|
2400
|
+
size: 12,
|
|
2401
|
+
color: "var(--chatllm-text-muted, #9ca3af)",
|
|
2402
|
+
style: { marginLeft: "2px" }
|
|
2403
|
+
}
|
|
2404
|
+
)
|
|
2405
|
+
]
|
|
2406
|
+
}
|
|
2407
|
+
),
|
|
2408
|
+
showModelMenu && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2409
|
+
"div",
|
|
2410
|
+
{
|
|
2411
|
+
style: {
|
|
2412
|
+
position: "absolute",
|
|
2413
|
+
bottom: "100%",
|
|
2414
|
+
left: 0,
|
|
2415
|
+
marginBottom: "4px",
|
|
2416
|
+
backgroundColor: "var(--chatllm-bg, #ffffff)",
|
|
2417
|
+
border: "1px solid var(--chatllm-border, #e5e7eb)",
|
|
2418
|
+
borderRadius: "8px",
|
|
2419
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.1)",
|
|
2420
|
+
minWidth: "160px",
|
|
2421
|
+
zIndex: 100,
|
|
2422
|
+
overflow: "hidden"
|
|
2423
|
+
},
|
|
2424
|
+
onMouseLeave: () => setShowModelMenu(false),
|
|
2425
|
+
children: [
|
|
2426
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2427
|
+
"div",
|
|
2428
|
+
{
|
|
2429
|
+
style: {
|
|
2430
|
+
padding: "8px 12px",
|
|
2431
|
+
fontSize: "11px",
|
|
2432
|
+
fontWeight: 600,
|
|
2433
|
+
color: "var(--chatllm-text-muted, #9ca3af)",
|
|
2434
|
+
textTransform: "uppercase",
|
|
2435
|
+
borderBottom: "1px solid var(--chatllm-border-light, #f3f4f6)"
|
|
2436
|
+
},
|
|
2437
|
+
children: "\uB2E4\uB978 \uBAA8\uB378\uC5D0\uAC8C \uC9C8\uBB38"
|
|
2438
|
+
}
|
|
2439
|
+
),
|
|
2440
|
+
otherModels.map((model) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2441
|
+
"button",
|
|
2442
|
+
{
|
|
2443
|
+
onClick: () => {
|
|
2444
|
+
onAskOtherModel(model.id);
|
|
2445
|
+
setShowModelMenu(false);
|
|
2446
|
+
},
|
|
2447
|
+
style: {
|
|
2448
|
+
width: "100%",
|
|
2449
|
+
padding: "10px 12px",
|
|
2450
|
+
display: "flex",
|
|
2451
|
+
alignItems: "center",
|
|
2452
|
+
gap: "8px",
|
|
2453
|
+
backgroundColor: "transparent",
|
|
2454
|
+
border: "none",
|
|
2455
|
+
cursor: "pointer",
|
|
2456
|
+
fontSize: "13px",
|
|
2457
|
+
color: "var(--chatllm-text, #1f2937)",
|
|
2458
|
+
textAlign: "left"
|
|
2459
|
+
},
|
|
2460
|
+
onMouseEnter: (e) => {
|
|
2461
|
+
e.currentTarget.style.backgroundColor = "var(--chatllm-bg-hover, #f3f4f6)";
|
|
2462
|
+
},
|
|
2463
|
+
onMouseLeave: (e) => {
|
|
2464
|
+
e.currentTarget.style.backgroundColor = "transparent";
|
|
2465
|
+
},
|
|
2466
|
+
children: [
|
|
2467
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconSvg, { name: "robot-line", size: 14, color: "var(--chatllm-primary, #3b82f6)" }),
|
|
2468
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { flex: 1 }, children: model.name }),
|
|
2469
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2470
|
+
"span",
|
|
2471
|
+
{
|
|
2472
|
+
style: {
|
|
2473
|
+
fontSize: "10px",
|
|
2474
|
+
padding: "2px 6px",
|
|
2475
|
+
backgroundColor: "var(--chatllm-bg-tertiary, #f3f4f6)",
|
|
2476
|
+
borderRadius: "4px",
|
|
2477
|
+
color: "var(--chatllm-text-muted, #9ca3af)"
|
|
2478
|
+
},
|
|
2479
|
+
children: model.provider
|
|
2480
|
+
}
|
|
2481
|
+
)
|
|
2482
|
+
]
|
|
2483
|
+
},
|
|
2484
|
+
model.id
|
|
2485
|
+
))
|
|
2486
|
+
]
|
|
2487
|
+
}
|
|
2488
|
+
)
|
|
2489
|
+
] })
|
|
2234
2490
|
]
|
|
2235
2491
|
}
|
|
2236
2492
|
)
|
|
@@ -2277,6 +2533,8 @@ var MessageList = ({
|
|
|
2277
2533
|
onEdit,
|
|
2278
2534
|
onRegenerate,
|
|
2279
2535
|
onQuote,
|
|
2536
|
+
onAskOtherModel,
|
|
2537
|
+
models,
|
|
2280
2538
|
copiedId,
|
|
2281
2539
|
editingId
|
|
2282
2540
|
}) => {
|
|
@@ -2341,6 +2599,8 @@ var MessageList = ({
|
|
|
2341
2599
|
onEdit: () => onEdit(message),
|
|
2342
2600
|
onRegenerate: message.role === "assistant" ? () => onRegenerate(message.id) : void 0,
|
|
2343
2601
|
onQuote,
|
|
2602
|
+
onAskOtherModel: message.role === "assistant" && onAskOtherModel ? (targetModel) => onAskOtherModel(message.id, targetModel) : void 0,
|
|
2603
|
+
models,
|
|
2344
2604
|
alternatives: message.alternatives
|
|
2345
2605
|
},
|
|
2346
2606
|
message.id
|
|
@@ -3644,7 +3904,9 @@ var ChatUI = ({
|
|
|
3644
3904
|
cancelEdit,
|
|
3645
3905
|
saveEdit,
|
|
3646
3906
|
regenerate,
|
|
3647
|
-
|
|
3907
|
+
askOtherModel,
|
|
3908
|
+
updatePersonalization,
|
|
3909
|
+
models: hookModels
|
|
3648
3910
|
} = useChatUI(hookOptions);
|
|
3649
3911
|
const greeting = currentPersonalization.userProfile.nickname ? `\uC548\uB155\uD558\uC138\uC694, ${currentPersonalization.userProfile.nickname}\uB2D8` : "\uC548\uB155\uD558\uC138\uC694";
|
|
3650
3912
|
const handleTemplateClick = (template) => {
|
|
@@ -3762,6 +4024,8 @@ var ChatUI = ({
|
|
|
3762
4024
|
onEdit: startEdit,
|
|
3763
4025
|
onRegenerate: regenerate,
|
|
3764
4026
|
onQuote: setQuotedText,
|
|
4027
|
+
onAskOtherModel: askOtherModel,
|
|
4028
|
+
models: hookModels,
|
|
3765
4029
|
copiedId: copiedMessageId,
|
|
3766
4030
|
editingId: editingMessageId
|
|
3767
4031
|
}
|