@gendive/chatllm 0.8.1 → 0.9.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 +108 -0
- package/dist/react/index.d.ts +108 -0
- package/dist/react/index.js +181 -15
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +181 -15
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/react/index.d.mts
CHANGED
|
@@ -265,6 +265,55 @@ interface ChatUIProps {
|
|
|
265
265
|
enableAutoExtraction?: boolean;
|
|
266
266
|
/** 정보 추출 설정 */
|
|
267
267
|
infoExtractionConfig?: Partial<InfoExtractionConfig>;
|
|
268
|
+
/**
|
|
269
|
+
* @description 외부 스토리지 사용 여부
|
|
270
|
+
* @Todo vibecode - true 시 localStorage 대신 콜백 사용
|
|
271
|
+
*/
|
|
272
|
+
useExternalStorage?: boolean;
|
|
273
|
+
/**
|
|
274
|
+
* @description 세션 목록 로드 콜백
|
|
275
|
+
* @Todo vibecode - 컴포넌트 마운트 시 호출
|
|
276
|
+
*/
|
|
277
|
+
onLoadSessions?: () => Promise<{
|
|
278
|
+
id: string;
|
|
279
|
+
title: string;
|
|
280
|
+
}[]>;
|
|
281
|
+
/**
|
|
282
|
+
* @description 세션 생성 콜백
|
|
283
|
+
* @Todo vibecode - 새 채팅 생성 시 호출
|
|
284
|
+
*/
|
|
285
|
+
onCreateSession?: () => Promise<{
|
|
286
|
+
id: string;
|
|
287
|
+
title: string;
|
|
288
|
+
}>;
|
|
289
|
+
/**
|
|
290
|
+
* @description 세션 상세 로드 콜백 (메시지 포함)
|
|
291
|
+
* @Todo vibecode - 세션 선택 시 호출 (lazy loading)
|
|
292
|
+
*/
|
|
293
|
+
onLoadSession?: (sessionId: string) => Promise<{
|
|
294
|
+
id: string;
|
|
295
|
+
title: string;
|
|
296
|
+
messages: ChatMessage[];
|
|
297
|
+
memory_data?: object;
|
|
298
|
+
}>;
|
|
299
|
+
/**
|
|
300
|
+
* @description 세션 삭제 콜백
|
|
301
|
+
* @Todo vibecode - 세션 삭제 시 호출
|
|
302
|
+
*/
|
|
303
|
+
onDeleteSession?: (sessionId: string) => Promise<void>;
|
|
304
|
+
/**
|
|
305
|
+
* @description 세션 제목 수정 콜백
|
|
306
|
+
* @Todo vibecode - 세션 제목 변경 시 호출
|
|
307
|
+
*/
|
|
308
|
+
onUpdateSessionTitle?: (sessionId: string, title: string) => Promise<void>;
|
|
309
|
+
/**
|
|
310
|
+
* @description 메시지 저장 콜백
|
|
311
|
+
* @Todo vibecode - 메시지 전송 완료 후 호출
|
|
312
|
+
*/
|
|
313
|
+
onSaveMessages?: (sessionId: string, messages: {
|
|
314
|
+
role: 'USER' | 'ASSISTANT';
|
|
315
|
+
message: string;
|
|
316
|
+
}[]) => Promise<void>;
|
|
268
317
|
}
|
|
269
318
|
interface SendMessageParams {
|
|
270
319
|
messages: {
|
|
@@ -437,6 +486,16 @@ interface UseChatUIReturn {
|
|
|
437
486
|
compressionState: CompressionState | null;
|
|
438
487
|
/** 수동으로 정보 추출 트리거 */
|
|
439
488
|
extractUserInfo: () => Promise<void>;
|
|
489
|
+
/**
|
|
490
|
+
* @description 세션 목록 로딩 상태
|
|
491
|
+
* @Todo vibecode - 외부 스토리지 사용 시 세션 목록 로드 중
|
|
492
|
+
*/
|
|
493
|
+
isSessionsLoading: boolean;
|
|
494
|
+
/**
|
|
495
|
+
* @description 단일 세션 로딩 상태
|
|
496
|
+
* @Todo vibecode - 외부 스토리지 사용 시 세션 상세 로드 중
|
|
497
|
+
*/
|
|
498
|
+
isSessionLoading: boolean;
|
|
440
499
|
}
|
|
441
500
|
|
|
442
501
|
/**
|
|
@@ -489,6 +548,55 @@ interface UseChatUIOptions {
|
|
|
489
548
|
globalMemoryConfig?: GlobalMemoryConfig;
|
|
490
549
|
/** 자동 정보 추출 활성화 (기본: true) */
|
|
491
550
|
enableAutoExtraction?: boolean;
|
|
551
|
+
/**
|
|
552
|
+
* @description 외부 스토리지 사용 여부
|
|
553
|
+
* @Todo vibecode - true 시 localStorage 대신 콜백 사용
|
|
554
|
+
*/
|
|
555
|
+
useExternalStorage?: boolean;
|
|
556
|
+
/**
|
|
557
|
+
* @description 세션 목록 로드 콜백
|
|
558
|
+
* @Todo vibecode - 컴포넌트 마운트 시 호출
|
|
559
|
+
*/
|
|
560
|
+
onLoadSessions?: () => Promise<{
|
|
561
|
+
id: string;
|
|
562
|
+
title: string;
|
|
563
|
+
}[]>;
|
|
564
|
+
/**
|
|
565
|
+
* @description 세션 생성 콜백
|
|
566
|
+
* @Todo vibecode - 새 채팅 생성 시 호출
|
|
567
|
+
*/
|
|
568
|
+
onCreateSession?: () => Promise<{
|
|
569
|
+
id: string;
|
|
570
|
+
title: string;
|
|
571
|
+
}>;
|
|
572
|
+
/**
|
|
573
|
+
* @description 세션 상세 로드 콜백 (메시지 포함)
|
|
574
|
+
* @Todo vibecode - 세션 선택 시 호출 (lazy loading)
|
|
575
|
+
*/
|
|
576
|
+
onLoadSession?: (sessionId: string) => Promise<{
|
|
577
|
+
id: string;
|
|
578
|
+
title: string;
|
|
579
|
+
messages: ChatMessage[];
|
|
580
|
+
memory_data?: object;
|
|
581
|
+
}>;
|
|
582
|
+
/**
|
|
583
|
+
* @description 세션 삭제 콜백
|
|
584
|
+
* @Todo vibecode - 세션 삭제 시 호출
|
|
585
|
+
*/
|
|
586
|
+
onDeleteSession?: (sessionId: string) => Promise<void>;
|
|
587
|
+
/**
|
|
588
|
+
* @description 세션 제목 수정 콜백
|
|
589
|
+
* @Todo vibecode - 세션 제목 변경 시 호출
|
|
590
|
+
*/
|
|
591
|
+
onUpdateSessionTitle?: (sessionId: string, title: string) => Promise<void>;
|
|
592
|
+
/**
|
|
593
|
+
* @description 메시지 저장 콜백
|
|
594
|
+
* @Todo vibecode - 메시지 전송 완료 후 호출
|
|
595
|
+
*/
|
|
596
|
+
onSaveMessages?: (sessionId: string, messages: {
|
|
597
|
+
role: 'USER' | 'ASSISTANT';
|
|
598
|
+
message: string;
|
|
599
|
+
}[]) => Promise<void>;
|
|
492
600
|
}
|
|
493
601
|
declare const useChatUI: (options: UseChatUIOptions) => UseChatUIReturn;
|
|
494
602
|
|
package/dist/react/index.d.ts
CHANGED
|
@@ -265,6 +265,55 @@ interface ChatUIProps {
|
|
|
265
265
|
enableAutoExtraction?: boolean;
|
|
266
266
|
/** 정보 추출 설정 */
|
|
267
267
|
infoExtractionConfig?: Partial<InfoExtractionConfig>;
|
|
268
|
+
/**
|
|
269
|
+
* @description 외부 스토리지 사용 여부
|
|
270
|
+
* @Todo vibecode - true 시 localStorage 대신 콜백 사용
|
|
271
|
+
*/
|
|
272
|
+
useExternalStorage?: boolean;
|
|
273
|
+
/**
|
|
274
|
+
* @description 세션 목록 로드 콜백
|
|
275
|
+
* @Todo vibecode - 컴포넌트 마운트 시 호출
|
|
276
|
+
*/
|
|
277
|
+
onLoadSessions?: () => Promise<{
|
|
278
|
+
id: string;
|
|
279
|
+
title: string;
|
|
280
|
+
}[]>;
|
|
281
|
+
/**
|
|
282
|
+
* @description 세션 생성 콜백
|
|
283
|
+
* @Todo vibecode - 새 채팅 생성 시 호출
|
|
284
|
+
*/
|
|
285
|
+
onCreateSession?: () => Promise<{
|
|
286
|
+
id: string;
|
|
287
|
+
title: string;
|
|
288
|
+
}>;
|
|
289
|
+
/**
|
|
290
|
+
* @description 세션 상세 로드 콜백 (메시지 포함)
|
|
291
|
+
* @Todo vibecode - 세션 선택 시 호출 (lazy loading)
|
|
292
|
+
*/
|
|
293
|
+
onLoadSession?: (sessionId: string) => Promise<{
|
|
294
|
+
id: string;
|
|
295
|
+
title: string;
|
|
296
|
+
messages: ChatMessage[];
|
|
297
|
+
memory_data?: object;
|
|
298
|
+
}>;
|
|
299
|
+
/**
|
|
300
|
+
* @description 세션 삭제 콜백
|
|
301
|
+
* @Todo vibecode - 세션 삭제 시 호출
|
|
302
|
+
*/
|
|
303
|
+
onDeleteSession?: (sessionId: string) => Promise<void>;
|
|
304
|
+
/**
|
|
305
|
+
* @description 세션 제목 수정 콜백
|
|
306
|
+
* @Todo vibecode - 세션 제목 변경 시 호출
|
|
307
|
+
*/
|
|
308
|
+
onUpdateSessionTitle?: (sessionId: string, title: string) => Promise<void>;
|
|
309
|
+
/**
|
|
310
|
+
* @description 메시지 저장 콜백
|
|
311
|
+
* @Todo vibecode - 메시지 전송 완료 후 호출
|
|
312
|
+
*/
|
|
313
|
+
onSaveMessages?: (sessionId: string, messages: {
|
|
314
|
+
role: 'USER' | 'ASSISTANT';
|
|
315
|
+
message: string;
|
|
316
|
+
}[]) => Promise<void>;
|
|
268
317
|
}
|
|
269
318
|
interface SendMessageParams {
|
|
270
319
|
messages: {
|
|
@@ -437,6 +486,16 @@ interface UseChatUIReturn {
|
|
|
437
486
|
compressionState: CompressionState | null;
|
|
438
487
|
/** 수동으로 정보 추출 트리거 */
|
|
439
488
|
extractUserInfo: () => Promise<void>;
|
|
489
|
+
/**
|
|
490
|
+
* @description 세션 목록 로딩 상태
|
|
491
|
+
* @Todo vibecode - 외부 스토리지 사용 시 세션 목록 로드 중
|
|
492
|
+
*/
|
|
493
|
+
isSessionsLoading: boolean;
|
|
494
|
+
/**
|
|
495
|
+
* @description 단일 세션 로딩 상태
|
|
496
|
+
* @Todo vibecode - 외부 스토리지 사용 시 세션 상세 로드 중
|
|
497
|
+
*/
|
|
498
|
+
isSessionLoading: boolean;
|
|
440
499
|
}
|
|
441
500
|
|
|
442
501
|
/**
|
|
@@ -489,6 +548,55 @@ interface UseChatUIOptions {
|
|
|
489
548
|
globalMemoryConfig?: GlobalMemoryConfig;
|
|
490
549
|
/** 자동 정보 추출 활성화 (기본: true) */
|
|
491
550
|
enableAutoExtraction?: boolean;
|
|
551
|
+
/**
|
|
552
|
+
* @description 외부 스토리지 사용 여부
|
|
553
|
+
* @Todo vibecode - true 시 localStorage 대신 콜백 사용
|
|
554
|
+
*/
|
|
555
|
+
useExternalStorage?: boolean;
|
|
556
|
+
/**
|
|
557
|
+
* @description 세션 목록 로드 콜백
|
|
558
|
+
* @Todo vibecode - 컴포넌트 마운트 시 호출
|
|
559
|
+
*/
|
|
560
|
+
onLoadSessions?: () => Promise<{
|
|
561
|
+
id: string;
|
|
562
|
+
title: string;
|
|
563
|
+
}[]>;
|
|
564
|
+
/**
|
|
565
|
+
* @description 세션 생성 콜백
|
|
566
|
+
* @Todo vibecode - 새 채팅 생성 시 호출
|
|
567
|
+
*/
|
|
568
|
+
onCreateSession?: () => Promise<{
|
|
569
|
+
id: string;
|
|
570
|
+
title: string;
|
|
571
|
+
}>;
|
|
572
|
+
/**
|
|
573
|
+
* @description 세션 상세 로드 콜백 (메시지 포함)
|
|
574
|
+
* @Todo vibecode - 세션 선택 시 호출 (lazy loading)
|
|
575
|
+
*/
|
|
576
|
+
onLoadSession?: (sessionId: string) => Promise<{
|
|
577
|
+
id: string;
|
|
578
|
+
title: string;
|
|
579
|
+
messages: ChatMessage[];
|
|
580
|
+
memory_data?: object;
|
|
581
|
+
}>;
|
|
582
|
+
/**
|
|
583
|
+
* @description 세션 삭제 콜백
|
|
584
|
+
* @Todo vibecode - 세션 삭제 시 호출
|
|
585
|
+
*/
|
|
586
|
+
onDeleteSession?: (sessionId: string) => Promise<void>;
|
|
587
|
+
/**
|
|
588
|
+
* @description 세션 제목 수정 콜백
|
|
589
|
+
* @Todo vibecode - 세션 제목 변경 시 호출
|
|
590
|
+
*/
|
|
591
|
+
onUpdateSessionTitle?: (sessionId: string, title: string) => Promise<void>;
|
|
592
|
+
/**
|
|
593
|
+
* @description 메시지 저장 콜백
|
|
594
|
+
* @Todo vibecode - 메시지 전송 완료 후 호출
|
|
595
|
+
*/
|
|
596
|
+
onSaveMessages?: (sessionId: string, messages: {
|
|
597
|
+
role: 'USER' | 'ASSISTANT';
|
|
598
|
+
message: string;
|
|
599
|
+
}[]) => Promise<void>;
|
|
492
600
|
}
|
|
493
601
|
declare const useChatUI: (options: UseChatUIOptions) => UseChatUIReturn;
|
|
494
602
|
|
package/dist/react/index.js
CHANGED
|
@@ -568,7 +568,15 @@ var useChatUI = (options) => {
|
|
|
568
568
|
// Memory options
|
|
569
569
|
useGlobalMemoryEnabled = true,
|
|
570
570
|
globalMemoryConfig,
|
|
571
|
-
enableAutoExtraction = true
|
|
571
|
+
enableAutoExtraction = true,
|
|
572
|
+
// External storage options
|
|
573
|
+
useExternalStorage = false,
|
|
574
|
+
onLoadSessions,
|
|
575
|
+
onCreateSession,
|
|
576
|
+
onLoadSession,
|
|
577
|
+
onDeleteSession: onDeleteSessionCallback,
|
|
578
|
+
onUpdateSessionTitle,
|
|
579
|
+
onSaveMessages
|
|
572
580
|
} = options;
|
|
573
581
|
const [sessions, setSessions] = (0, import_react3.useState)([]);
|
|
574
582
|
const [currentSessionId, setCurrentSessionId] = (0, import_react3.useState)(null);
|
|
@@ -586,6 +594,8 @@ var useChatUI = (options) => {
|
|
|
586
594
|
...initialPersonalization
|
|
587
595
|
});
|
|
588
596
|
const [activeAlternatives, setActiveAlternatives] = (0, import_react3.useState)({});
|
|
597
|
+
const [isSessionsLoading, setIsSessionsLoading] = (0, import_react3.useState)(false);
|
|
598
|
+
const [isSessionLoading, setIsSessionLoading] = (0, import_react3.useState)(false);
|
|
589
599
|
const abortControllerRef = (0, import_react3.useRef)(null);
|
|
590
600
|
const memoryOptions = (0, import_react3.useMemo)(
|
|
591
601
|
() => ({
|
|
@@ -609,6 +619,36 @@ var useChatUI = (options) => {
|
|
|
609
619
|
const compressionState = currentSession?.compressionState || null;
|
|
610
620
|
(0, import_react3.useEffect)(() => {
|
|
611
621
|
if (typeof window === "undefined") return;
|
|
622
|
+
if (useExternalStorage && onLoadSessions) {
|
|
623
|
+
setIsSessionsLoading(true);
|
|
624
|
+
onLoadSessions().then((sessionList) => {
|
|
625
|
+
const sessionsWithoutMessages = sessionList.map((s) => ({
|
|
626
|
+
id: s.id,
|
|
627
|
+
title: s.title,
|
|
628
|
+
messages: [],
|
|
629
|
+
// 메시지는 세션 선택 시 로드
|
|
630
|
+
model: initialModel || models[0]?.id || "",
|
|
631
|
+
createdAt: Date.now(),
|
|
632
|
+
updatedAt: Date.now()
|
|
633
|
+
}));
|
|
634
|
+
setSessions(sessionsWithoutMessages);
|
|
635
|
+
if (sessionsWithoutMessages.length > 0) {
|
|
636
|
+
setCurrentSessionId(sessionsWithoutMessages[0].id);
|
|
637
|
+
}
|
|
638
|
+
}).catch((error) => {
|
|
639
|
+
onError?.(error instanceof Error ? error : new Error("Failed to load sessions"));
|
|
640
|
+
}).finally(() => {
|
|
641
|
+
setIsSessionsLoading(false);
|
|
642
|
+
});
|
|
643
|
+
const savedPersonalization2 = localStorage.getItem(`${storageKey}_personalization`);
|
|
644
|
+
if (savedPersonalization2) {
|
|
645
|
+
try {
|
|
646
|
+
setPersonalization(JSON.parse(savedPersonalization2));
|
|
647
|
+
} catch {
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
612
652
|
const saved = localStorage.getItem(storageKey);
|
|
613
653
|
if (saved) {
|
|
614
654
|
try {
|
|
@@ -628,13 +668,14 @@ var useChatUI = (options) => {
|
|
|
628
668
|
} catch {
|
|
629
669
|
}
|
|
630
670
|
}
|
|
631
|
-
}, [storageKey]);
|
|
671
|
+
}, [storageKey, useExternalStorage, onLoadSessions, initialModel, models, onError]);
|
|
632
672
|
(0, import_react3.useEffect)(() => {
|
|
633
673
|
if (typeof window === "undefined") return;
|
|
674
|
+
if (useExternalStorage) return;
|
|
634
675
|
if (sessions.length > 0) {
|
|
635
676
|
localStorage.setItem(storageKey, JSON.stringify(sessions));
|
|
636
677
|
}
|
|
637
|
-
}, [sessions, storageKey]);
|
|
678
|
+
}, [sessions, storageKey, useExternalStorage]);
|
|
638
679
|
(0, import_react3.useEffect)(() => {
|
|
639
680
|
if (typeof window === "undefined") return;
|
|
640
681
|
localStorage.setItem(`${storageKey}_personalization`, JSON.stringify(personalization));
|
|
@@ -787,7 +828,29 @@ ${newConversation}
|
|
|
787
828
|
const estimateTokens = (0, import_react3.useCallback)((messages2) => {
|
|
788
829
|
return messages2.reduce((sum, m) => sum + Math.ceil(m.content.length / 4), 0);
|
|
789
830
|
}, []);
|
|
790
|
-
const newSession = (0, import_react3.useCallback)(() => {
|
|
831
|
+
const newSession = (0, import_react3.useCallback)(async () => {
|
|
832
|
+
if (useExternalStorage && onCreateSession) {
|
|
833
|
+
setIsSessionLoading(true);
|
|
834
|
+
try {
|
|
835
|
+
const created = await onCreateSession();
|
|
836
|
+
const now2 = Date.now();
|
|
837
|
+
const newSess2 = {
|
|
838
|
+
id: created.id,
|
|
839
|
+
title: created.title,
|
|
840
|
+
messages: [],
|
|
841
|
+
model: selectedModel,
|
|
842
|
+
createdAt: now2,
|
|
843
|
+
updatedAt: now2
|
|
844
|
+
};
|
|
845
|
+
setSessions((prev) => [newSess2, ...prev]);
|
|
846
|
+
setCurrentSessionId(newSess2.id);
|
|
847
|
+
} catch (error) {
|
|
848
|
+
onError?.(error instanceof Error ? error : new Error("Failed to create session"));
|
|
849
|
+
} finally {
|
|
850
|
+
setIsSessionLoading(false);
|
|
851
|
+
}
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
791
854
|
const now = Date.now();
|
|
792
855
|
const newSess = {
|
|
793
856
|
id: generateId("session"),
|
|
@@ -799,15 +862,60 @@ ${newConversation}
|
|
|
799
862
|
};
|
|
800
863
|
setSessions((prev) => [newSess, ...prev]);
|
|
801
864
|
setCurrentSessionId(newSess.id);
|
|
802
|
-
}, [selectedModel]);
|
|
803
|
-
const selectSession = (0, import_react3.useCallback)((id) => {
|
|
865
|
+
}, [selectedModel, useExternalStorage, onCreateSession, onError]);
|
|
866
|
+
const selectSession = (0, import_react3.useCallback)(async (id) => {
|
|
867
|
+
if (useExternalStorage && onLoadSession) {
|
|
868
|
+
setIsSessionLoading(true);
|
|
869
|
+
try {
|
|
870
|
+
const sessionDetail = await onLoadSession(id);
|
|
871
|
+
const loadedMessages = sessionDetail.messages.map((m, idx) => ({
|
|
872
|
+
id: m.id || generateId("msg"),
|
|
873
|
+
role: typeof m.role === "string" ? m.role.toLowerCase() : m.role,
|
|
874
|
+
content: m.content,
|
|
875
|
+
timestamp: m.timestamp || Date.now() - (sessionDetail.messages.length - idx) * 1e3,
|
|
876
|
+
model: m.model,
|
|
877
|
+
alternatives: m.alternatives,
|
|
878
|
+
sources: m.sources
|
|
879
|
+
}));
|
|
880
|
+
setSessions(
|
|
881
|
+
(prev) => prev.map(
|
|
882
|
+
(s) => s.id === id ? { ...s, title: sessionDetail.title, messages: loadedMessages, updatedAt: Date.now() } : s
|
|
883
|
+
)
|
|
884
|
+
);
|
|
885
|
+
setCurrentSessionId(id);
|
|
886
|
+
const existingSession = sessions.find((s) => s.id === id);
|
|
887
|
+
if (existingSession) {
|
|
888
|
+
setSelectedModel(existingSession.model);
|
|
889
|
+
}
|
|
890
|
+
} catch (error) {
|
|
891
|
+
onError?.(error instanceof Error ? error : new Error("Failed to load session"));
|
|
892
|
+
} finally {
|
|
893
|
+
setIsSessionLoading(false);
|
|
894
|
+
}
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
804
897
|
const session = sessions.find((s) => s.id === id);
|
|
805
898
|
if (session) {
|
|
806
899
|
setCurrentSessionId(id);
|
|
807
900
|
setSelectedModel(session.model);
|
|
808
901
|
}
|
|
809
|
-
}, [sessions]);
|
|
810
|
-
const deleteSession = (0, import_react3.useCallback)((id) => {
|
|
902
|
+
}, [sessions, useExternalStorage, onLoadSession, onError]);
|
|
903
|
+
const deleteSession = (0, import_react3.useCallback)(async (id) => {
|
|
904
|
+
if (useExternalStorage && onDeleteSessionCallback) {
|
|
905
|
+
try {
|
|
906
|
+
await onDeleteSessionCallback(id);
|
|
907
|
+
setSessions((prev) => {
|
|
908
|
+
const filtered = prev.filter((s) => s.id !== id);
|
|
909
|
+
if (currentSessionId === id) {
|
|
910
|
+
setCurrentSessionId(filtered.length > 0 ? filtered[0].id : null);
|
|
911
|
+
}
|
|
912
|
+
return filtered;
|
|
913
|
+
});
|
|
914
|
+
} catch (error) {
|
|
915
|
+
onError?.(error instanceof Error ? error : new Error("Failed to delete session"));
|
|
916
|
+
}
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
811
919
|
setSessions((prev) => {
|
|
812
920
|
const filtered = prev.filter((s) => s.id !== id);
|
|
813
921
|
if (currentSessionId === id) {
|
|
@@ -818,16 +926,30 @@ ${newConversation}
|
|
|
818
926
|
}
|
|
819
927
|
return filtered;
|
|
820
928
|
});
|
|
821
|
-
}, [currentSessionId, storageKey]);
|
|
822
|
-
const renameSession = (0, import_react3.useCallback)((id, newTitle) => {
|
|
929
|
+
}, [currentSessionId, storageKey, useExternalStorage, onDeleteSessionCallback, onError]);
|
|
930
|
+
const renameSession = (0, import_react3.useCallback)(async (id, newTitle) => {
|
|
823
931
|
if (!newTitle.trim()) return;
|
|
932
|
+
if (useExternalStorage && onUpdateSessionTitle) {
|
|
933
|
+
try {
|
|
934
|
+
await onUpdateSessionTitle(id, newTitle.trim());
|
|
935
|
+
setSessions(
|
|
936
|
+
(prev) => prev.map(
|
|
937
|
+
(s) => s.id === id ? { ...s, title: newTitle.trim(), updatedAt: Date.now() } : s
|
|
938
|
+
)
|
|
939
|
+
);
|
|
940
|
+
onTitleChange?.(id, newTitle.trim());
|
|
941
|
+
} catch (error) {
|
|
942
|
+
onError?.(error instanceof Error ? error : new Error("Failed to update session title"));
|
|
943
|
+
}
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
824
946
|
setSessions(
|
|
825
947
|
(prev) => prev.map(
|
|
826
948
|
(s) => s.id === id ? { ...s, title: newTitle.trim(), updatedAt: Date.now() } : s
|
|
827
949
|
)
|
|
828
950
|
);
|
|
829
951
|
onTitleChange?.(id, newTitle.trim());
|
|
830
|
-
}, [onTitleChange]);
|
|
952
|
+
}, [onTitleChange, useExternalStorage, onUpdateSessionTitle, onError]);
|
|
831
953
|
const setModel = (0, import_react3.useCallback)((model) => {
|
|
832
954
|
setSelectedModel(model);
|
|
833
955
|
if (currentSessionId) {
|
|
@@ -1095,6 +1217,21 @@ ${contextSummary}` },
|
|
|
1095
1217
|
}
|
|
1096
1218
|
}
|
|
1097
1219
|
}
|
|
1220
|
+
if (useExternalStorage && onSaveMessages && capturedSessionId) {
|
|
1221
|
+
const updatedSession = sessions.find((s) => s.id === capturedSessionId);
|
|
1222
|
+
const assistantContent = updatedSession?.messages.find(
|
|
1223
|
+
(m) => m.id === assistantMessageId
|
|
1224
|
+
)?.content || "";
|
|
1225
|
+
if (assistantContent) {
|
|
1226
|
+
const messagesToSave = [
|
|
1227
|
+
{ role: "USER", message: finalContent },
|
|
1228
|
+
{ role: "ASSISTANT", message: assistantContent }
|
|
1229
|
+
];
|
|
1230
|
+
onSaveMessages(capturedSessionId, messagesToSave).catch((saveError) => {
|
|
1231
|
+
console.error("[useChatUI] Failed to save messages:", saveError);
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1098
1235
|
} catch (error) {
|
|
1099
1236
|
if (error instanceof Error && error.name === "AbortError") {
|
|
1100
1237
|
return;
|
|
@@ -1136,7 +1273,9 @@ ${contextSummary}` },
|
|
|
1136
1273
|
onSendMessage,
|
|
1137
1274
|
onError,
|
|
1138
1275
|
generateTitleCallback,
|
|
1139
|
-
onTitleChange
|
|
1276
|
+
onTitleChange,
|
|
1277
|
+
useExternalStorage,
|
|
1278
|
+
onSaveMessages
|
|
1140
1279
|
]);
|
|
1141
1280
|
const saveEdit = (0, import_react3.useCallback)(async (content) => {
|
|
1142
1281
|
if (!editingMessageId || !currentSession || !currentSessionId) return;
|
|
@@ -1389,7 +1528,18 @@ ${currentSession.contextSummary}` },
|
|
|
1389
1528
|
content: m.content
|
|
1390
1529
|
}));
|
|
1391
1530
|
await infoExtraction.extractInfo(recentMessages);
|
|
1392
|
-
}
|
|
1531
|
+
},
|
|
1532
|
+
// External Storage Loading States
|
|
1533
|
+
/**
|
|
1534
|
+
* @description 세션 목록 로딩 상태
|
|
1535
|
+
* @Todo vibecode - 외부 스토리지 사용 시
|
|
1536
|
+
*/
|
|
1537
|
+
isSessionsLoading,
|
|
1538
|
+
/**
|
|
1539
|
+
* @description 단일 세션 로딩 상태
|
|
1540
|
+
* @Todo vibecode - 외부 스토리지 사용 시
|
|
1541
|
+
*/
|
|
1542
|
+
isSessionLoading
|
|
1393
1543
|
};
|
|
1394
1544
|
};
|
|
1395
1545
|
|
|
@@ -5202,7 +5352,15 @@ var ChatUI = ({
|
|
|
5202
5352
|
onSessionChange,
|
|
5203
5353
|
onError,
|
|
5204
5354
|
onTitleChange,
|
|
5205
|
-
generateTitle: generateTitle2
|
|
5355
|
+
generateTitle: generateTitle2,
|
|
5356
|
+
// External Storage Props
|
|
5357
|
+
useExternalStorage = false,
|
|
5358
|
+
onLoadSessions,
|
|
5359
|
+
onCreateSession,
|
|
5360
|
+
onLoadSession,
|
|
5361
|
+
onDeleteSession,
|
|
5362
|
+
onUpdateSessionTitle,
|
|
5363
|
+
onSaveMessages
|
|
5206
5364
|
}) => {
|
|
5207
5365
|
import_react13.default.useEffect(() => {
|
|
5208
5366
|
injectStyles();
|
|
@@ -5221,7 +5379,15 @@ var ChatUI = ({
|
|
|
5221
5379
|
onSessionChange,
|
|
5222
5380
|
onError,
|
|
5223
5381
|
onTitleChange,
|
|
5224
|
-
generateTitle: generateTitle2
|
|
5382
|
+
generateTitle: generateTitle2,
|
|
5383
|
+
// External Storage Options
|
|
5384
|
+
useExternalStorage,
|
|
5385
|
+
onLoadSessions,
|
|
5386
|
+
onCreateSession,
|
|
5387
|
+
onLoadSession,
|
|
5388
|
+
onDeleteSession,
|
|
5389
|
+
onUpdateSessionTitle,
|
|
5390
|
+
onSaveMessages
|
|
5225
5391
|
};
|
|
5226
5392
|
const {
|
|
5227
5393
|
sessions,
|