@gendive/chatllm 0.17.0 → 0.17.2
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 +18 -0
- package/dist/react/index.d.ts +18 -0
- package/dist/react/index.js +90 -21
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +90 -21
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/react/index.d.mts
CHANGED
|
@@ -396,6 +396,11 @@ interface ChatToolDefinition {
|
|
|
396
396
|
* @Todo vibecode - MIME 와일드카드 ('image/*'), 확장자 ('.pdf') 지원
|
|
397
397
|
*/
|
|
398
398
|
acceptedTypes?: string[];
|
|
399
|
+
/**
|
|
400
|
+
* @description 파일 첨부 시 File 객체를 base64 문자열로 자동 변환
|
|
401
|
+
* @Todo vibecode - true 시 execute()에 { name, mimeType, base64, size } 형태로 전달
|
|
402
|
+
*/
|
|
403
|
+
autoConvertBase64?: boolean;
|
|
399
404
|
}
|
|
400
405
|
interface ChatMessage {
|
|
401
406
|
id: string;
|
|
@@ -541,6 +546,11 @@ interface SkillConfig<TParams = Record<string, unknown>> {
|
|
|
541
546
|
* @example ['image/*', 'application/pdf', '.docx']
|
|
542
547
|
*/
|
|
543
548
|
acceptedTypes?: string[];
|
|
549
|
+
/**
|
|
550
|
+
* @description 파일 첨부 시 File 객체를 base64 문자열로 자동 변환
|
|
551
|
+
* @Todo vibecode - true 시 execute()에 { name, mimeType, base64, size } 형태로 전달
|
|
552
|
+
*/
|
|
553
|
+
autoConvertBase64?: boolean;
|
|
544
554
|
}
|
|
545
555
|
/**
|
|
546
556
|
* @description 스킬 실행 상태 (메시지에 첨부)
|
|
@@ -576,6 +586,14 @@ interface ModelConfig {
|
|
|
576
586
|
name: string;
|
|
577
587
|
provider: ProviderType;
|
|
578
588
|
description?: string;
|
|
589
|
+
/** @Todo vibecode - UI 표시용 아이콘 (URL 또는 아이콘 이름) */
|
|
590
|
+
icon?: string;
|
|
591
|
+
/** @Todo vibecode - 최대 출력 토큰 수 */
|
|
592
|
+
maxTokens?: number;
|
|
593
|
+
/** @Todo vibecode - 컨텍스트 윈도우 크기 (토큰) */
|
|
594
|
+
contextWindow?: number;
|
|
595
|
+
/** @Todo vibecode - 호스트 커스텀 필드 허용 */
|
|
596
|
+
[key: string]: unknown;
|
|
579
597
|
}
|
|
580
598
|
interface PromptTemplate {
|
|
581
599
|
id: string;
|
package/dist/react/index.d.ts
CHANGED
|
@@ -396,6 +396,11 @@ interface ChatToolDefinition {
|
|
|
396
396
|
* @Todo vibecode - MIME 와일드카드 ('image/*'), 확장자 ('.pdf') 지원
|
|
397
397
|
*/
|
|
398
398
|
acceptedTypes?: string[];
|
|
399
|
+
/**
|
|
400
|
+
* @description 파일 첨부 시 File 객체를 base64 문자열로 자동 변환
|
|
401
|
+
* @Todo vibecode - true 시 execute()에 { name, mimeType, base64, size } 형태로 전달
|
|
402
|
+
*/
|
|
403
|
+
autoConvertBase64?: boolean;
|
|
399
404
|
}
|
|
400
405
|
interface ChatMessage {
|
|
401
406
|
id: string;
|
|
@@ -541,6 +546,11 @@ interface SkillConfig<TParams = Record<string, unknown>> {
|
|
|
541
546
|
* @example ['image/*', 'application/pdf', '.docx']
|
|
542
547
|
*/
|
|
543
548
|
acceptedTypes?: string[];
|
|
549
|
+
/**
|
|
550
|
+
* @description 파일 첨부 시 File 객체를 base64 문자열로 자동 변환
|
|
551
|
+
* @Todo vibecode - true 시 execute()에 { name, mimeType, base64, size } 형태로 전달
|
|
552
|
+
*/
|
|
553
|
+
autoConvertBase64?: boolean;
|
|
544
554
|
}
|
|
545
555
|
/**
|
|
546
556
|
* @description 스킬 실행 상태 (메시지에 첨부)
|
|
@@ -576,6 +586,14 @@ interface ModelConfig {
|
|
|
576
586
|
name: string;
|
|
577
587
|
provider: ProviderType;
|
|
578
588
|
description?: string;
|
|
589
|
+
/** @Todo vibecode - UI 표시용 아이콘 (URL 또는 아이콘 이름) */
|
|
590
|
+
icon?: string;
|
|
591
|
+
/** @Todo vibecode - 최대 출력 토큰 수 */
|
|
592
|
+
maxTokens?: number;
|
|
593
|
+
/** @Todo vibecode - 컨텍스트 윈도우 크기 (토큰) */
|
|
594
|
+
contextWindow?: number;
|
|
595
|
+
/** @Todo vibecode - 호스트 커스텀 필드 허용 */
|
|
596
|
+
[key: string]: unknown;
|
|
579
597
|
}
|
|
580
598
|
interface PromptTemplate {
|
|
581
599
|
id: string;
|
package/dist/react/index.js
CHANGED
|
@@ -1199,7 +1199,7 @@ var useProject = (options) => {
|
|
|
1199
1199
|
const currentProject = projects.find((p) => p.id === currentProjectId) || null;
|
|
1200
1200
|
(0, import_react4.useEffect)(() => {
|
|
1201
1201
|
if (!enabled || useExternalStorage || !initializedRef.current) return;
|
|
1202
|
-
if (projects.length > 0) {
|
|
1202
|
+
if (projects.length > 0 && typeof window !== "undefined") {
|
|
1203
1203
|
localStorage.setItem(storageKey, JSON.stringify(projects));
|
|
1204
1204
|
}
|
|
1205
1205
|
}, [enabled, projects, storageKey, useExternalStorage]);
|
|
@@ -1233,7 +1233,7 @@ var useProject = (options) => {
|
|
|
1233
1233
|
}
|
|
1234
1234
|
} else {
|
|
1235
1235
|
try {
|
|
1236
|
-
const saved = localStorage.getItem(storageKey);
|
|
1236
|
+
const saved = typeof window !== "undefined" ? localStorage.getItem(storageKey) : null;
|
|
1237
1237
|
if (saved) {
|
|
1238
1238
|
const parsed = JSON.parse(saved);
|
|
1239
1239
|
if (!parsed.find((p) => p.id === DEFAULT_PROJECT_ID)) {
|
|
@@ -1540,6 +1540,7 @@ var convertToolsToSkills = (tools, onToolCall) => {
|
|
|
1540
1540
|
label: tool.label || tool.name,
|
|
1541
1541
|
icon: tool.icon,
|
|
1542
1542
|
acceptedTypes: tool.acceptedTypes,
|
|
1543
|
+
autoConvertBase64: tool.autoConvertBase64,
|
|
1543
1544
|
parameters: {
|
|
1544
1545
|
type: "object",
|
|
1545
1546
|
properties: Object.fromEntries(
|
|
@@ -1613,6 +1614,20 @@ var DEFAULT_KEEP_RECENT = 6;
|
|
|
1613
1614
|
var DEFAULT_RECOMPRESSION_THRESHOLD = 10;
|
|
1614
1615
|
var DEFAULT_TOKEN_LIMIT = 8e3;
|
|
1615
1616
|
var generateId3 = (prefix) => `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
1617
|
+
var fileToBase64 = (file) => new Promise((resolve, reject) => {
|
|
1618
|
+
const reader = new FileReader();
|
|
1619
|
+
reader.onload = () => resolve(reader.result.split(",")[1] || "");
|
|
1620
|
+
reader.onerror = reject;
|
|
1621
|
+
reader.readAsDataURL(file);
|
|
1622
|
+
});
|
|
1623
|
+
var convertAttachmentsToBase64 = async (attachments) => Promise.all(
|
|
1624
|
+
attachments.map(async (att) => ({
|
|
1625
|
+
name: att.name,
|
|
1626
|
+
mimeType: att.mimeType,
|
|
1627
|
+
base64: await fileToBase64(att.file),
|
|
1628
|
+
size: att.size
|
|
1629
|
+
}))
|
|
1630
|
+
);
|
|
1616
1631
|
var generateTitle = (messages) => {
|
|
1617
1632
|
const firstUserMessage = messages.find((m) => m.role === "user");
|
|
1618
1633
|
if (!firstUserMessage) return "\uC0C8 \uB300\uD654";
|
|
@@ -1835,11 +1850,13 @@ var useChatUI = (options) => {
|
|
|
1835
1850
|
}).finally(() => {
|
|
1836
1851
|
setIsSessionsLoading(false);
|
|
1837
1852
|
});
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1853
|
+
if (typeof window !== "undefined") {
|
|
1854
|
+
const savedPersonalization2 = localStorage.getItem(`${storageKey}_personalization`);
|
|
1855
|
+
if (savedPersonalization2) {
|
|
1856
|
+
try {
|
|
1857
|
+
setPersonalization(JSON.parse(savedPersonalization2));
|
|
1858
|
+
} catch {
|
|
1859
|
+
}
|
|
1843
1860
|
}
|
|
1844
1861
|
}
|
|
1845
1862
|
return;
|
|
@@ -1847,6 +1864,7 @@ var useChatUI = (options) => {
|
|
|
1847
1864
|
if (enableProjects) {
|
|
1848
1865
|
migrateSessionsToProjects(storageKey);
|
|
1849
1866
|
}
|
|
1867
|
+
if (typeof window === "undefined") return;
|
|
1850
1868
|
const saved = localStorage.getItem(storageKey);
|
|
1851
1869
|
if (saved) {
|
|
1852
1870
|
try {
|
|
@@ -2335,7 +2353,8 @@ ${newConversation}
|
|
|
2335
2353
|
}, []);
|
|
2336
2354
|
const sendMessage = (0, import_react5.useCallback)(async (content, options2) => {
|
|
2337
2355
|
const messageContent = content || input;
|
|
2338
|
-
if (!messageContent.trim() || isLoading) return;
|
|
2356
|
+
if (!messageContent.trim() && attachments.length === 0 || isLoading) return;
|
|
2357
|
+
setIsLoading(true);
|
|
2339
2358
|
let sessionId = currentSessionId;
|
|
2340
2359
|
if (!sessionId) {
|
|
2341
2360
|
if (useExternalStorage && onCreateSessionRef.current) {
|
|
@@ -2426,7 +2445,8 @@ ${finalContent}`;
|
|
|
2426
2445
|
});
|
|
2427
2446
|
if (matchedFiles.length === 0) continue;
|
|
2428
2447
|
try {
|
|
2429
|
-
const
|
|
2448
|
+
const filesToPass = skillConfig.autoConvertBase64 ? await convertAttachmentsToBase64(matchedFiles) : matchedFiles;
|
|
2449
|
+
const result = await skillConfig.execute({ files: filesToPass, userMessage: finalContent });
|
|
2430
2450
|
attachmentResults.push({
|
|
2431
2451
|
type: "tool_result",
|
|
2432
2452
|
toolName: skillName,
|
|
@@ -2444,6 +2464,20 @@ ${finalContent}`;
|
|
|
2444
2464
|
}
|
|
2445
2465
|
}
|
|
2446
2466
|
}
|
|
2467
|
+
let shouldContinueAfterAttachment = continueAfterToolResult;
|
|
2468
|
+
if (attachmentResults.length > 0) {
|
|
2469
|
+
for (const part of attachmentResults) {
|
|
2470
|
+
if (part.type === "tool_result" && onSkillCompleteRef.current) {
|
|
2471
|
+
const decision = onSkillCompleteRef.current(part.toolName, {
|
|
2472
|
+
content: part.result.content,
|
|
2473
|
+
metadata: part.result.metadata,
|
|
2474
|
+
sources: part.result.sources
|
|
2475
|
+
});
|
|
2476
|
+
shouldContinueAfterAttachment = decision === "continue";
|
|
2477
|
+
if (!shouldContinueAfterAttachment) break;
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2447
2481
|
const assistantMessageId = generateId3("msg");
|
|
2448
2482
|
const assistantMessage = {
|
|
2449
2483
|
id: assistantMessageId,
|
|
@@ -2491,7 +2525,10 @@ ${finalContent}`;
|
|
|
2491
2525
|
}).catch(() => {
|
|
2492
2526
|
});
|
|
2493
2527
|
}
|
|
2494
|
-
|
|
2528
|
+
if (attachmentResults.length > 0 && !shouldContinueAfterAttachment) {
|
|
2529
|
+
setIsLoading(false);
|
|
2530
|
+
return;
|
|
2531
|
+
}
|
|
2495
2532
|
abortControllerRef.current = new AbortController();
|
|
2496
2533
|
try {
|
|
2497
2534
|
let messagesToSend = [...existingMessages, userMessage];
|
|
@@ -2550,6 +2587,20 @@ ${currentContextSummary}` },
|
|
|
2550
2587
|
} else {
|
|
2551
2588
|
chatMessages = messagesToSend.map((m) => ({ role: m.role, content: m.content }));
|
|
2552
2589
|
}
|
|
2590
|
+
if (attachmentResults.length > 0 && shouldContinueAfterAttachment) {
|
|
2591
|
+
const attachmentContext = attachmentResults.filter((part) => part.type === "tool_result").map((part) => `[${part.label || part.toolName} \uACB0\uACFC]
|
|
2592
|
+
${part.result.content}`).join("\n\n");
|
|
2593
|
+
if (attachmentContext) {
|
|
2594
|
+
chatMessages.push({
|
|
2595
|
+
role: "user",
|
|
2596
|
+
content: `\uD30C\uC77C \uBD84\uC11D \uACB0\uACFC:
|
|
2597
|
+
|
|
2598
|
+
${attachmentContext}
|
|
2599
|
+
|
|
2600
|
+
\uC704 \uACB0\uACFC\uB97C \uCC38\uACE0\uD558\uC5EC \uB2F5\uBCC0\uD574\uC8FC\uC138\uC694.`
|
|
2601
|
+
});
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2553
2604
|
console.log("[ChatUI] Messages to send:", chatMessages.length, chatMessages.map((m) => ({ role: m.role, content: m.content.slice(0, 50) })));
|
|
2554
2605
|
const baseSystemPrompt = buildSystemPrompt();
|
|
2555
2606
|
const combinedSystemPrompt = [baseSystemPrompt, actionPrompt].filter(Boolean).join("\n\n");
|
|
@@ -2557,8 +2608,8 @@ ${currentContextSummary}` },
|
|
|
2557
2608
|
const modelConfig = models.find((m) => m.id === selectedModel);
|
|
2558
2609
|
const provider = modelConfig?.provider || "ollama";
|
|
2559
2610
|
let response;
|
|
2560
|
-
if (
|
|
2561
|
-
const result = await
|
|
2611
|
+
if (onSendMessageRef.current) {
|
|
2612
|
+
const result = await onSendMessageRef.current({
|
|
2562
2613
|
messages: messagesForApi,
|
|
2563
2614
|
model: selectedModel,
|
|
2564
2615
|
provider,
|
|
@@ -2983,14 +3034,12 @@ ${result.content}
|
|
|
2983
3034
|
keepRecentMessages,
|
|
2984
3035
|
buildSystemPrompt,
|
|
2985
3036
|
compressContext,
|
|
2986
|
-
onSendMessage,
|
|
2987
|
-
onError,
|
|
2988
|
-
generateTitleCallback,
|
|
2989
|
-
onTitleChange,
|
|
2990
3037
|
useExternalStorage,
|
|
2991
|
-
onSaveMessages,
|
|
2992
3038
|
handleSkillCall,
|
|
2993
|
-
resolvedSkills
|
|
3039
|
+
resolvedSkills,
|
|
3040
|
+
/** @Todo vibecode - attachments, continueAfterToolResult를 deps에 추가하여 stale closure 방지 */
|
|
3041
|
+
attachments,
|
|
3042
|
+
continueAfterToolResult
|
|
2994
3043
|
]);
|
|
2995
3044
|
const handlePollSubmit = (0, import_react5.useCallback)(
|
|
2996
3045
|
(messageId, responses) => {
|
|
@@ -3258,8 +3307,8 @@ ${currentSession.contextSummary}` },
|
|
|
3258
3307
|
const provider = modelConfig?.provider || "ollama";
|
|
3259
3308
|
let responseContent = "";
|
|
3260
3309
|
let responseSources;
|
|
3261
|
-
if (
|
|
3262
|
-
const result = await
|
|
3310
|
+
if (onSendMessageRef.current) {
|
|
3311
|
+
const result = await onSendMessageRef.current({
|
|
3263
3312
|
messages: messagesForApi,
|
|
3264
3313
|
model: targetModel,
|
|
3265
3314
|
provider,
|
|
@@ -3537,7 +3586,25 @@ ${currentSession.contextSummary}` },
|
|
|
3537
3586
|
systemPrompt: `\uC0AC\uC6A9\uC790\uAC00 "${toolConfig.label || skillName}" \uB3C4\uAD6C\uB97C \uC0AC\uC6A9\uD558\uB824\uACE0 \uD569\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uC758 \uBA54\uC2DC\uC9C0\uB97C \uBC14\uD0D5\uC73C\uB85C \uBC18\uB4DC\uC2DC <skill_use name="${skillName}"> \uD0DC\uADF8\uB97C \uC0AC\uC6A9\uD558\uC5EC \uC774 \uB3C4\uAD6C\uB97C \uD638\uCD9C\uD558\uC138\uC694. \uB3C4\uAD6C\uB97C \uC0AC\uC6A9\uD558\uC9C0 \uC54A\uACE0 \uD14D\uC2A4\uD2B8\uB85C\uB9CC \uB2F5\uBCC0\uD558\uC9C0 \uB9C8\uC138\uC694.`
|
|
3538
3587
|
});
|
|
3539
3588
|
} else {
|
|
3540
|
-
|
|
3589
|
+
const currentQuery = input;
|
|
3590
|
+
executeManualSkill(skillName, { query: currentQuery }).then((result) => {
|
|
3591
|
+
if (!result || !result.content) return;
|
|
3592
|
+
let shouldContinue = continueAfterToolResult;
|
|
3593
|
+
if (onSkillCompleteRef.current) {
|
|
3594
|
+
const decision = onSkillCompleteRef.current(skillName, result);
|
|
3595
|
+
shouldContinue = decision === "continue";
|
|
3596
|
+
}
|
|
3597
|
+
if (!shouldContinue) return;
|
|
3598
|
+
skipNextSkillParsingRef.current = true;
|
|
3599
|
+
const resultPrompt = `\uC2A4\uD0AC "${skillName}" \uC2E4\uD589 \uACB0\uACFC:
|
|
3600
|
+
|
|
3601
|
+
${result.content}
|
|
3602
|
+
|
|
3603
|
+
\uC704 \uACB0\uACFC\uB97C \uBC14\uD0D5\uC73C\uB85C \uC0AC\uC6A9\uC790\uC758 \uC6D0\uB798 \uC9C8\uBB38\uC5D0 \uB2F5\uBCC0\uD574\uC8FC\uC138\uC694. skill_use \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694.`;
|
|
3604
|
+
setTimeout(() => {
|
|
3605
|
+
sendMessage(resultPrompt, { hiddenUserMessage: true });
|
|
3606
|
+
}, 100);
|
|
3607
|
+
});
|
|
3541
3608
|
}
|
|
3542
3609
|
},
|
|
3543
3610
|
// Project
|
|
@@ -3968,6 +4035,7 @@ var ChatSidebar = ({
|
|
|
3968
4035
|
className: `chatllm-sidebar chatllm-sidebar-transition ${theme ? `chatllm-root ${themeClass}` : ""}`,
|
|
3969
4036
|
style: {
|
|
3970
4037
|
width: isOpen ? sidebarWidth : "0",
|
|
4038
|
+
height: "100%",
|
|
3971
4039
|
flexShrink: 0,
|
|
3972
4040
|
backgroundColor: "var(--chatllm-sidebar-bg)",
|
|
3973
4041
|
borderRight: isOpen ? "1px solid var(--chatllm-border)" : "none",
|
|
@@ -10371,6 +10439,7 @@ var ChatUIView = ({
|
|
|
10371
10439
|
currentProjectId,
|
|
10372
10440
|
onSelectProject: projects.length > 0 ? selectProject : void 0,
|
|
10373
10441
|
onNewProject: projects.length > 0 ? () => {
|
|
10442
|
+
if (typeof window === "undefined") return;
|
|
10374
10443
|
const title = window.prompt("\uD504\uB85C\uC81D\uD2B8 \uC774\uB984\uC744 \uC785\uB825\uD558\uC138\uC694");
|
|
10375
10444
|
if (title?.trim()) {
|
|
10376
10445
|
createProject({ title: title.trim() });
|