@gendive/chatllm 0.9.0 → 0.10.1

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.
@@ -65,6 +65,16 @@ interface MarkdownRendererProps {
65
65
  className?: string;
66
66
  /** 선택지 클릭 핸들러 */
67
67
  onChoiceClick?: (choice: ChoiceOption) => void;
68
+ /**
69
+ * @description Thinking 블록 표시 여부
70
+ * @Todo vibecode - AI 추론 과정 표시 (기본: true)
71
+ */
72
+ showThinking?: boolean;
73
+ /**
74
+ * @description Thinking 블록 기본 펼침 상태
75
+ * @Todo vibecode - true면 펼쳐진 상태로 표시 (기본: false)
76
+ */
77
+ thinkingDefaultOpen?: boolean;
68
78
  }
69
79
  declare const MarkdownRenderer: React$1.FC<MarkdownRendererProps>;
70
80
 
@@ -289,11 +299,18 @@ interface ChatUIProps {
289
299
  /**
290
300
  * @description 세션 상세 로드 콜백 (메시지 포함)
291
301
  * @Todo vibecode - 세션 선택 시 호출 (lazy loading)
302
+ * API 응답 형식: { role: 'USER'|'ASSISTANT', message: string }
292
303
  */
293
304
  onLoadSession?: (sessionId: string) => Promise<{
294
305
  id: string;
295
306
  title: string;
296
- messages: ChatMessage[];
307
+ messages: Array<{
308
+ id?: string;
309
+ role: 'USER' | 'ASSISTANT' | 'user' | 'assistant';
310
+ message?: string;
311
+ content?: string;
312
+ created_at?: string;
313
+ }>;
297
314
  memory_data?: object;
298
315
  }>;
299
316
  /**
@@ -314,6 +331,16 @@ interface ChatUIProps {
314
331
  role: 'USER' | 'ASSISTANT';
315
332
  message: string;
316
333
  }[]) => Promise<void>;
334
+ /**
335
+ * @description Thinking 블록 표시 여부
336
+ * @Todo vibecode - AI 추론 과정 표시 (기본: true)
337
+ */
338
+ showThinking?: boolean;
339
+ /**
340
+ * @description Thinking 블록 기본 펼침 상태
341
+ * @Todo vibecode - true면 펼쳐진 상태로 표시 (기본: false)
342
+ */
343
+ thinkingDefaultOpen?: boolean;
317
344
  }
318
345
  interface SendMessageParams {
319
346
  messages: {
@@ -376,6 +403,16 @@ interface MessageListProps {
376
403
  editingId: string | null;
377
404
  /** 선택지 클릭 핸들러 */
378
405
  onChoiceClick?: (choice: ChoiceOption) => void;
406
+ /**
407
+ * @description Thinking 블록 표시 여부
408
+ * @Todo vibecode - AI 추론 과정 표시 (기본: true)
409
+ */
410
+ showThinking?: boolean;
411
+ /**
412
+ * @description Thinking 블록 기본 펼침 상태
413
+ * @Todo vibecode - true면 펼쳐진 상태로 표시 (기본: false)
414
+ */
415
+ thinkingDefaultOpen?: boolean;
379
416
  }
380
417
  interface MessageBubbleProps {
381
418
  message: ChatMessage;
@@ -393,6 +430,16 @@ interface MessageBubbleProps {
393
430
  models?: ModelConfig[];
394
431
  alternatives?: AlternativeResponse[];
395
432
  onChoiceClick?: (choice: ChoiceOption) => void;
433
+ /**
434
+ * @description Thinking 블록 표시 여부
435
+ * @Todo vibecode - AI 추론 과정 표시 (기본: true)
436
+ */
437
+ showThinking?: boolean;
438
+ /**
439
+ * @description Thinking 블록 기본 펼침 상태
440
+ * @Todo vibecode - true면 펼쳐진 상태로 표시 (기본: false)
441
+ */
442
+ thinkingDefaultOpen?: boolean;
396
443
  }
397
444
  interface InputProps {
398
445
  value: string;
@@ -572,11 +619,18 @@ interface UseChatUIOptions {
572
619
  /**
573
620
  * @description 세션 상세 로드 콜백 (메시지 포함)
574
621
  * @Todo vibecode - 세션 선택 시 호출 (lazy loading)
622
+ * API 응답 형식: { role: 'USER'|'ASSISTANT', message: string }
575
623
  */
576
624
  onLoadSession?: (sessionId: string) => Promise<{
577
625
  id: string;
578
626
  title: string;
579
- messages: ChatMessage[];
627
+ messages: Array<{
628
+ id?: string;
629
+ role: 'USER' | 'ASSISTANT' | 'user' | 'assistant';
630
+ message?: string;
631
+ content?: string;
632
+ created_at?: string;
633
+ }>;
580
634
  memory_data?: object;
581
635
  }>;
582
636
  /**
@@ -65,6 +65,16 @@ interface MarkdownRendererProps {
65
65
  className?: string;
66
66
  /** 선택지 클릭 핸들러 */
67
67
  onChoiceClick?: (choice: ChoiceOption) => void;
68
+ /**
69
+ * @description Thinking 블록 표시 여부
70
+ * @Todo vibecode - AI 추론 과정 표시 (기본: true)
71
+ */
72
+ showThinking?: boolean;
73
+ /**
74
+ * @description Thinking 블록 기본 펼침 상태
75
+ * @Todo vibecode - true면 펼쳐진 상태로 표시 (기본: false)
76
+ */
77
+ thinkingDefaultOpen?: boolean;
68
78
  }
69
79
  declare const MarkdownRenderer: React$1.FC<MarkdownRendererProps>;
70
80
 
@@ -289,11 +299,18 @@ interface ChatUIProps {
289
299
  /**
290
300
  * @description 세션 상세 로드 콜백 (메시지 포함)
291
301
  * @Todo vibecode - 세션 선택 시 호출 (lazy loading)
302
+ * API 응답 형식: { role: 'USER'|'ASSISTANT', message: string }
292
303
  */
293
304
  onLoadSession?: (sessionId: string) => Promise<{
294
305
  id: string;
295
306
  title: string;
296
- messages: ChatMessage[];
307
+ messages: Array<{
308
+ id?: string;
309
+ role: 'USER' | 'ASSISTANT' | 'user' | 'assistant';
310
+ message?: string;
311
+ content?: string;
312
+ created_at?: string;
313
+ }>;
297
314
  memory_data?: object;
298
315
  }>;
299
316
  /**
@@ -314,6 +331,16 @@ interface ChatUIProps {
314
331
  role: 'USER' | 'ASSISTANT';
315
332
  message: string;
316
333
  }[]) => Promise<void>;
334
+ /**
335
+ * @description Thinking 블록 표시 여부
336
+ * @Todo vibecode - AI 추론 과정 표시 (기본: true)
337
+ */
338
+ showThinking?: boolean;
339
+ /**
340
+ * @description Thinking 블록 기본 펼침 상태
341
+ * @Todo vibecode - true면 펼쳐진 상태로 표시 (기본: false)
342
+ */
343
+ thinkingDefaultOpen?: boolean;
317
344
  }
318
345
  interface SendMessageParams {
319
346
  messages: {
@@ -376,6 +403,16 @@ interface MessageListProps {
376
403
  editingId: string | null;
377
404
  /** 선택지 클릭 핸들러 */
378
405
  onChoiceClick?: (choice: ChoiceOption) => void;
406
+ /**
407
+ * @description Thinking 블록 표시 여부
408
+ * @Todo vibecode - AI 추론 과정 표시 (기본: true)
409
+ */
410
+ showThinking?: boolean;
411
+ /**
412
+ * @description Thinking 블록 기본 펼침 상태
413
+ * @Todo vibecode - true면 펼쳐진 상태로 표시 (기본: false)
414
+ */
415
+ thinkingDefaultOpen?: boolean;
379
416
  }
380
417
  interface MessageBubbleProps {
381
418
  message: ChatMessage;
@@ -393,6 +430,16 @@ interface MessageBubbleProps {
393
430
  models?: ModelConfig[];
394
431
  alternatives?: AlternativeResponse[];
395
432
  onChoiceClick?: (choice: ChoiceOption) => void;
433
+ /**
434
+ * @description Thinking 블록 표시 여부
435
+ * @Todo vibecode - AI 추론 과정 표시 (기본: true)
436
+ */
437
+ showThinking?: boolean;
438
+ /**
439
+ * @description Thinking 블록 기본 펼침 상태
440
+ * @Todo vibecode - true면 펼쳐진 상태로 표시 (기본: false)
441
+ */
442
+ thinkingDefaultOpen?: boolean;
396
443
  }
397
444
  interface InputProps {
398
445
  value: string;
@@ -572,11 +619,18 @@ interface UseChatUIOptions {
572
619
  /**
573
620
  * @description 세션 상세 로드 콜백 (메시지 포함)
574
621
  * @Todo vibecode - 세션 선택 시 호출 (lazy loading)
622
+ * API 응답 형식: { role: 'USER'|'ASSISTANT', message: string }
575
623
  */
576
624
  onLoadSession?: (sessionId: string) => Promise<{
577
625
  id: string;
578
626
  title: string;
579
- messages: ChatMessage[];
627
+ messages: Array<{
628
+ id?: string;
629
+ role: 'USER' | 'ASSISTANT' | 'user' | 'assistant';
630
+ message?: string;
631
+ content?: string;
632
+ created_at?: string;
633
+ }>;
580
634
  memory_data?: object;
581
635
  }>;
582
636
  /**
@@ -871,8 +871,9 @@ ${newConversation}
871
871
  const loadedMessages = sessionDetail.messages.map((m, idx) => ({
872
872
  id: m.id || generateId("msg"),
873
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,
874
+ // API는 message 필드, 내부는 content 필드 사용
875
+ content: m.content || m.message || "",
876
+ timestamp: m.created_at ? new Date(m.created_at).getTime() : Date.now() - (sessionDetail.messages.length - idx) * 1e3,
876
877
  model: m.model,
877
878
  alternatives: m.alternatives,
878
879
  sources: m.sources
@@ -2697,6 +2698,9 @@ var SOURCE_LINKS_REGEX = /(\*{0,2}출처:?\*{0,2}\s*)?((?:(?<!!)\[`?[^\]]+`?\]\(
2697
2698
  var INLINE_CHOICE_PATTERN = /\[(\d+)\]\s*([^\[\n]+?)(?=\s*\[\d+\]|$)/g;
2698
2699
  var INLINE_CHOICE_LINE_PATTERN = /^(.+?)?\s*(\[\d+\][^\[]+(?:\[\d+\][^\[]+)+)$/;
2699
2700
  var CODE_BLOCK_REGEX = /```(\w*)\n?([\s\S]*?)```/g;
2701
+ var THINKING_TAG_REGEX = /<thinking>([\s\S]*?)<\/thinking>/gi;
2702
+ var THINKING_CODEBLOCK_REGEX = /```thinking\n?([\s\S]*?)```/gi;
2703
+ var THINKING_TEXT_REGEX = /^Thinking:\s*\n([\s\S]*?)(?=\n\n|$)/gim;
2700
2704
  var INLINE_CODE_REGEX = /`([^`]+)`/g;
2701
2705
  var BOLD_REGEX = /\*\*([^*]+)\*\*/g;
2702
2706
  var ITALIC_REGEX = /(?<!\*)\*([^*]+)\*(?!\*)/g;
@@ -2874,6 +2878,80 @@ var MarkdownTable = ({ data }) => {
2874
2878
  }
2875
2879
  );
2876
2880
  };
2881
+ var ThinkingBlock = ({ content, defaultOpen = false }) => {
2882
+ const [isOpen, setIsOpen] = import_react8.default.useState(defaultOpen);
2883
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2884
+ "details",
2885
+ {
2886
+ open: isOpen,
2887
+ className: "chatllm-thinking",
2888
+ style: {
2889
+ margin: "12px 0",
2890
+ borderRadius: "8px",
2891
+ border: "1px solid var(--chatllm-border, #e5e7eb)",
2892
+ background: "var(--chatllm-bg-secondary, #f9fafb)",
2893
+ overflow: "hidden"
2894
+ },
2895
+ children: [
2896
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2897
+ "summary",
2898
+ {
2899
+ onClick: (e) => {
2900
+ e.preventDefault();
2901
+ setIsOpen(!isOpen);
2902
+ },
2903
+ style: {
2904
+ padding: "10px 14px",
2905
+ cursor: "pointer",
2906
+ fontSize: "13px",
2907
+ color: "var(--chatllm-text-secondary, #6b7280)",
2908
+ userSelect: "none",
2909
+ listStyle: "none",
2910
+ display: "flex",
2911
+ alignItems: "center",
2912
+ gap: "6px"
2913
+ },
2914
+ children: [
2915
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontSize: "14px" }, children: "\u{1F4AD}" }),
2916
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2917
+ "Thinking",
2918
+ isOpen ? "" : "..."
2919
+ ] }),
2920
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2921
+ "span",
2922
+ {
2923
+ style: {
2924
+ marginLeft: "auto",
2925
+ fontSize: "10px",
2926
+ transition: "transform 0.2s",
2927
+ transform: isOpen ? "rotate(180deg)" : "rotate(0deg)"
2928
+ },
2929
+ children: "\u25BC"
2930
+ }
2931
+ )
2932
+ ]
2933
+ }
2934
+ ),
2935
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2936
+ "div",
2937
+ {
2938
+ className: "chatllm-thinking-content",
2939
+ style: {
2940
+ padding: "12px 14px",
2941
+ borderTop: "1px solid var(--chatllm-border, #e5e7eb)",
2942
+ fontSize: "13px",
2943
+ color: "var(--chatllm-text-secondary, #6b7280)",
2944
+ whiteSpace: "pre-wrap",
2945
+ lineHeight: "1.6",
2946
+ background: "var(--chatllm-bg, #ffffff)"
2947
+ },
2948
+ children: content.trim()
2949
+ }
2950
+ )
2951
+ ]
2952
+ }
2953
+ );
2954
+ };
2877
2955
  var CodeBlock = ({ language, code }) => {
2878
2956
  const [copied, setCopied] = import_react8.default.useState(false);
2879
2957
  const handleCopy = async () => {
@@ -3298,10 +3376,35 @@ var SourceLinksSection = ({ links, label }) => {
3298
3376
  }
3299
3377
  );
3300
3378
  };
3301
- var MarkdownRenderer = ({ content, className, onChoiceClick }) => {
3379
+ var MarkdownRenderer = ({
3380
+ content,
3381
+ className,
3382
+ onChoiceClick,
3383
+ showThinking = true,
3384
+ thinkingDefaultOpen = false
3385
+ }) => {
3302
3386
  const rendered = (0, import_react8.useMemo)(() => {
3303
3387
  const elements = [];
3304
3388
  let processedContent = content;
3389
+ const thinkingBlocks = [];
3390
+ if (showThinking) {
3391
+ processedContent = processedContent.replace(THINKING_TAG_REGEX, (_, thinkContent) => {
3392
+ thinkingBlocks.push(thinkContent);
3393
+ return `\xA7THINKING\xA7${thinkingBlocks.length - 1}\xA7/THINKING\xA7`;
3394
+ });
3395
+ processedContent = processedContent.replace(THINKING_CODEBLOCK_REGEX, (_, thinkContent) => {
3396
+ thinkingBlocks.push(thinkContent);
3397
+ return `\xA7THINKING\xA7${thinkingBlocks.length - 1}\xA7/THINKING\xA7`;
3398
+ });
3399
+ processedContent = processedContent.replace(THINKING_TEXT_REGEX, (_, thinkContent) => {
3400
+ thinkingBlocks.push(thinkContent);
3401
+ return `\xA7THINKING\xA7${thinkingBlocks.length - 1}\xA7/THINKING\xA7`;
3402
+ });
3403
+ } else {
3404
+ processedContent = processedContent.replace(THINKING_TAG_REGEX, "");
3405
+ processedContent = processedContent.replace(THINKING_CODEBLOCK_REGEX, "");
3406
+ processedContent = processedContent.replace(THINKING_TEXT_REGEX, "");
3407
+ }
3305
3408
  const codeBlocks = [];
3306
3409
  processedContent = processedContent.replace(CODE_BLOCK_REGEX, (_, lang, code) => {
3307
3410
  codeBlocks.push({ language: lang || "", code });
@@ -3409,6 +3512,23 @@ var MarkdownRenderer = ({ content, className, onChoiceClick }) => {
3409
3512
  }
3410
3513
  };
3411
3514
  lines.forEach((line, lineIndex) => {
3515
+ const thinkingMatch = line.match(/§THINKING§(\d+)§\/THINKING§/);
3516
+ if (thinkingMatch) {
3517
+ flushList();
3518
+ flushBlockquote();
3519
+ const index = parseInt(thinkingMatch[1]);
3520
+ elements.push(
3521
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3522
+ ThinkingBlock,
3523
+ {
3524
+ content: thinkingBlocks[index],
3525
+ defaultOpen: thinkingDefaultOpen
3526
+ },
3527
+ `thinking-${lineIndex}`
3528
+ )
3529
+ );
3530
+ return;
3531
+ }
3412
3532
  const codeBlockMatch = line.match(/§CODEBLOCK§(\d+)§\/CODEBLOCK§/);
3413
3533
  if (codeBlockMatch) {
3414
3534
  flushList();
@@ -3597,7 +3717,9 @@ var MessageBubble = ({
3597
3717
  activeAlternativeIndex = 0,
3598
3718
  onAlternativeChange,
3599
3719
  nextAssistantMessage,
3600
- onChoiceClick
3720
+ onChoiceClick,
3721
+ showThinking = true,
3722
+ thinkingDefaultOpen = false
3601
3723
  }) => {
3602
3724
  const [showActions, setShowActions] = (0, import_react9.useState)(false);
3603
3725
  const [showModelMenu, setShowModelMenu] = (0, import_react9.useState)(false);
@@ -3701,7 +3823,15 @@ var MessageBubble = ({
3701
3823
  children: [
3702
3824
  isAssistant ? (
3703
3825
  // AI 메시지는 마크다운 렌더링
3704
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MarkdownRenderer, { content: displayContent, onChoiceClick })
3826
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3827
+ MarkdownRenderer,
3828
+ {
3829
+ content: displayContent,
3830
+ onChoiceClick,
3831
+ showThinking,
3832
+ thinkingDefaultOpen
3833
+ }
3834
+ )
3705
3835
  ) : (
3706
3836
  // 사용자 메시지는 일반 텍스트
3707
3837
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
@@ -3991,7 +4121,9 @@ var MessageList = ({
3991
4121
  models,
3992
4122
  copiedId,
3993
4123
  editingId,
3994
- onChoiceClick
4124
+ onChoiceClick,
4125
+ showThinking = true,
4126
+ thinkingDefaultOpen = false
3995
4127
  }) => {
3996
4128
  const messagesEndRef = (0, import_react10.useRef)(null);
3997
4129
  const containerRef = (0, import_react10.useRef)(null);
@@ -4064,7 +4196,9 @@ var MessageList = ({
4064
4196
  onAlternativeChange: message.role === "user" && assistantForAlts && onSetActiveAlternative ? (idx) => onSetActiveAlternative(assistantForAlts.id, idx) : message.role === "assistant" && onSetActiveAlternative ? (idx) => onSetActiveAlternative(message.id, idx) : void 0,
4065
4197
  models,
4066
4198
  alternatives: message.role === "assistant" ? message.alternatives : assistantForAlts?.alternatives,
4067
- onChoiceClick: message.role === "assistant" ? onChoiceClick : void 0
4199
+ onChoiceClick: message.role === "assistant" ? onChoiceClick : void 0,
4200
+ showThinking,
4201
+ thinkingDefaultOpen
4068
4202
  },
4069
4203
  message.id
4070
4204
  );
@@ -5360,7 +5494,10 @@ var ChatUI = ({
5360
5494
  onLoadSession,
5361
5495
  onDeleteSession,
5362
5496
  onUpdateSessionTitle,
5363
- onSaveMessages
5497
+ onSaveMessages,
5498
+ // Thinking Block Props
5499
+ showThinking = true,
5500
+ thinkingDefaultOpen = false
5364
5501
  }) => {
5365
5502
  import_react13.default.useEffect(() => {
5366
5503
  injectStyles();
@@ -5556,7 +5693,9 @@ var ChatUI = ({
5556
5693
  models: hookModels,
5557
5694
  copiedId: copiedMessageId,
5558
5695
  editingId: editingMessageId,
5559
- onChoiceClick: handleChoiceClick
5696
+ onChoiceClick: handleChoiceClick,
5697
+ showThinking,
5698
+ thinkingDefaultOpen
5560
5699
  }
5561
5700
  ),
5562
5701
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(