@gendive/chatllm 0.6.3 → 0.6.5

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/README.md CHANGED
@@ -931,12 +931,11 @@ const response = await fetch('http://localhost:11434/api/chat', {
931
931
 
932
932
  ```typescript
933
933
  // DevDive Gateway API
934
- const response = await fetch(
935
934
  `https://prd-gtw.devdive.ai/model/text-generation/v1/${model}`,
936
935
  {
937
- method: 'POST',
938
- headers: {
939
- 'Content-Type': 'application/json',
936
+ const response = await fetch(
937
+ headers: {
938
+ 'Content-Type': 'application/json',
940
939
  'X-API-KEY': apiKey,
941
940
  },
942
941
  body: JSON.stringify({
@@ -948,5 +947,5 @@ const response = await fetch(
948
947
  }
949
948
  )
950
949
  ```
951
-
950
+ method: 'POST',
952
951
  ---
@@ -48,6 +48,26 @@ interface PersonalizationConfig {
48
48
  language: string;
49
49
  }
50
50
 
51
+ /**
52
+ * @description 마크다운 렌더러 컴포넌트
53
+ * 외부 의존성 없이 기본 마크다운 파싱 지원
54
+ */
55
+
56
+ interface ChoiceOption {
57
+ number: number;
58
+ text: string;
59
+ fullText: string;
60
+ }
61
+ interface MarkdownRendererProps {
62
+ /** 마크다운 텍스트 */
63
+ content: string;
64
+ /** 커스텀 클래스 */
65
+ className?: string;
66
+ /** 선택지 클릭 핸들러 */
67
+ onChoiceClick?: (choice: ChoiceOption) => void;
68
+ }
69
+ declare const MarkdownRenderer: React$1.FC<MarkdownRendererProps>;
70
+
51
71
  /**
52
72
  * @description React UI 컴포넌트 타입 정의
53
73
  */
@@ -211,6 +231,8 @@ interface MessageListProps {
211
231
  models?: ModelConfig[];
212
232
  copiedId: string | null;
213
233
  editingId: string | null;
234
+ /** 선택지 클릭 핸들러 */
235
+ onChoiceClick?: (choice: ChoiceOption) => void;
214
236
  }
215
237
  interface MessageBubbleProps {
216
238
  message: ChatMessage;
@@ -227,6 +249,7 @@ interface MessageBubbleProps {
227
249
  activeAlternativeIndex?: number;
228
250
  models?: ModelConfig[];
229
251
  alternatives?: AlternativeResponse[];
252
+ onChoiceClick?: (choice: ChoiceOption) => void;
230
253
  }
231
254
  interface InputProps {
232
255
  value: string;
@@ -454,19 +477,6 @@ interface MemoryPanelProps {
454
477
  }
455
478
  declare const MemoryPanel: React$1.FC<MemoryPanelProps>;
456
479
 
457
- /**
458
- * @description 마크다운 렌더러 컴포넌트
459
- * 외부 의존성 없이 기본 마크다운 파싱 지원
460
- */
461
-
462
- interface MarkdownRendererProps {
463
- /** 마크다운 텍스트 */
464
- content: string;
465
- /** 커스텀 클래스 */
466
- className?: string;
467
- }
468
- declare const MarkdownRenderer: React$1.FC<MarkdownRendererProps>;
469
-
470
480
  /**
471
481
  * @description 링크 칩 컴포넌트
472
482
  * 출처 링크를 칩 형태로 표시
@@ -48,6 +48,26 @@ interface PersonalizationConfig {
48
48
  language: string;
49
49
  }
50
50
 
51
+ /**
52
+ * @description 마크다운 렌더러 컴포넌트
53
+ * 외부 의존성 없이 기본 마크다운 파싱 지원
54
+ */
55
+
56
+ interface ChoiceOption {
57
+ number: number;
58
+ text: string;
59
+ fullText: string;
60
+ }
61
+ interface MarkdownRendererProps {
62
+ /** 마크다운 텍스트 */
63
+ content: string;
64
+ /** 커스텀 클래스 */
65
+ className?: string;
66
+ /** 선택지 클릭 핸들러 */
67
+ onChoiceClick?: (choice: ChoiceOption) => void;
68
+ }
69
+ declare const MarkdownRenderer: React$1.FC<MarkdownRendererProps>;
70
+
51
71
  /**
52
72
  * @description React UI 컴포넌트 타입 정의
53
73
  */
@@ -211,6 +231,8 @@ interface MessageListProps {
211
231
  models?: ModelConfig[];
212
232
  copiedId: string | null;
213
233
  editingId: string | null;
234
+ /** 선택지 클릭 핸들러 */
235
+ onChoiceClick?: (choice: ChoiceOption) => void;
214
236
  }
215
237
  interface MessageBubbleProps {
216
238
  message: ChatMessage;
@@ -227,6 +249,7 @@ interface MessageBubbleProps {
227
249
  activeAlternativeIndex?: number;
228
250
  models?: ModelConfig[];
229
251
  alternatives?: AlternativeResponse[];
252
+ onChoiceClick?: (choice: ChoiceOption) => void;
230
253
  }
231
254
  interface InputProps {
232
255
  value: string;
@@ -454,19 +477,6 @@ interface MemoryPanelProps {
454
477
  }
455
478
  declare const MemoryPanel: React$1.FC<MemoryPanelProps>;
456
479
 
457
- /**
458
- * @description 마크다운 렌더러 컴포넌트
459
- * 외부 의존성 없이 기본 마크다운 파싱 지원
460
- */
461
-
462
- interface MarkdownRendererProps {
463
- /** 마크다운 텍스트 */
464
- content: string;
465
- /** 커스텀 클래스 */
466
- className?: string;
467
- }
468
- declare const MarkdownRenderer: React$1.FC<MarkdownRendererProps>;
469
-
470
480
  /**
471
481
  * @description 링크 칩 컴포넌트
472
482
  * 출처 링크를 칩 형태로 표시
@@ -1821,7 +1821,7 @@ var LinkChip = ({
1821
1821
  var import_jsx_runtime6 = require("react/jsx-runtime");
1822
1822
  var IMAGE_REGEX = /!\[([^\]]*)\]\(([^)]+)\)/g;
1823
1823
  var LINK_REGEX = /(?<!!)\[([^\]]+)\]\(([^)]+)\)/g;
1824
- var SOURCE_LINKS_REGEX = /(\*{0,2}출처:?\*{0,2}\s*)?((?:\[`?[^\]]+`?\]\([^)]+\)\s*)+)/gi;
1824
+ var SOURCE_LINKS_REGEX = /(\*{0,2}출처:?\*{0,2}\s*)?((?:(?<!!)\[`?[^\]]+`?\]\([^)]+\)\s*)+)/gi;
1825
1825
  var CODE_BLOCK_REGEX = /```(\w*)\n?([\s\S]*?)```/g;
1826
1826
  var INLINE_CODE_REGEX = /`([^`]+)`/g;
1827
1827
  var BOLD_REGEX = /\*\*([^*]+)\*\*/g;
@@ -2066,6 +2066,96 @@ var CodeBlock = ({ language, code }) => {
2066
2066
  }
2067
2067
  );
2068
2068
  };
2069
+ var ChoiceButtons = ({ choices, onChoiceClick }) => {
2070
+ const [hoveredIndex, setHoveredIndex] = import_react5.default.useState(null);
2071
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2072
+ "div",
2073
+ {
2074
+ className: "chatllm-choices",
2075
+ style: {
2076
+ display: "flex",
2077
+ flexDirection: "column",
2078
+ gap: "8px",
2079
+ margin: "16px 0",
2080
+ padding: "12px",
2081
+ backgroundColor: "var(--chatllm-bg-secondary, #f9fafb)",
2082
+ borderRadius: "12px",
2083
+ border: "1px solid var(--chatllm-border-light, #e5e7eb)"
2084
+ },
2085
+ children: [
2086
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2087
+ "div",
2088
+ {
2089
+ style: {
2090
+ fontSize: "12px",
2091
+ fontWeight: 600,
2092
+ color: "var(--chatllm-text-muted, #6b7280)",
2093
+ marginBottom: "4px",
2094
+ textTransform: "uppercase",
2095
+ letterSpacing: "0.5px"
2096
+ },
2097
+ children: "\uC120\uD0DD\uD558\uC138\uC694"
2098
+ }
2099
+ ),
2100
+ choices.map((choice, index) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2101
+ "button",
2102
+ {
2103
+ onClick: () => onChoiceClick?.(choice),
2104
+ onMouseEnter: () => setHoveredIndex(index),
2105
+ onMouseLeave: () => setHoveredIndex(null),
2106
+ style: {
2107
+ display: "flex",
2108
+ alignItems: "center",
2109
+ gap: "12px",
2110
+ padding: "12px 16px",
2111
+ backgroundColor: hoveredIndex === index ? "var(--chatllm-primary-light, #dbeafe)" : "var(--chatllm-bg, #ffffff)",
2112
+ border: "1px solid var(--chatllm-border, #e5e7eb)",
2113
+ borderRadius: "8px",
2114
+ cursor: "pointer",
2115
+ textAlign: "left",
2116
+ transition: "all 0.2s ease",
2117
+ transform: hoveredIndex === index ? "translateX(4px)" : "translateX(0)"
2118
+ },
2119
+ children: [
2120
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2121
+ "span",
2122
+ {
2123
+ style: {
2124
+ display: "flex",
2125
+ alignItems: "center",
2126
+ justifyContent: "center",
2127
+ width: "28px",
2128
+ height: "28px",
2129
+ borderRadius: "50%",
2130
+ backgroundColor: hoveredIndex === index ? "var(--chatllm-primary, #3b82f6)" : "var(--chatllm-bg-tertiary, #f3f4f6)",
2131
+ color: hoveredIndex === index ? "#ffffff" : "var(--chatllm-text, #374151)",
2132
+ fontSize: "14px",
2133
+ fontWeight: 600,
2134
+ flexShrink: 0,
2135
+ transition: "all 0.2s ease"
2136
+ },
2137
+ children: choice.number
2138
+ }
2139
+ ),
2140
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2141
+ "span",
2142
+ {
2143
+ style: {
2144
+ fontSize: "14px",
2145
+ color: "var(--chatllm-text, #374151)",
2146
+ lineHeight: "1.5"
2147
+ },
2148
+ children: choice.text
2149
+ }
2150
+ )
2151
+ ]
2152
+ },
2153
+ choice.number
2154
+ ))
2155
+ ]
2156
+ }
2157
+ );
2158
+ };
2069
2159
  var SourceLinksSection = ({ links, label }) => {
2070
2160
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2071
2161
  "div",
@@ -2099,7 +2189,7 @@ var SourceLinksSection = ({ links, label }) => {
2099
2189
  }
2100
2190
  );
2101
2191
  };
2102
- var MarkdownRenderer = ({ content, className }) => {
2192
+ var MarkdownRenderer = ({ content, className, onChoiceClick }) => {
2103
2193
  const rendered = (0, import_react5.useMemo)(() => {
2104
2194
  const elements = [];
2105
2195
  let processedContent = content;
@@ -2121,6 +2211,7 @@ var MarkdownRenderer = ({ content, className }) => {
2121
2211
  let currentList = null;
2122
2212
  let blockquoteLines = [];
2123
2213
  let tableLines = [];
2214
+ let choiceItems = [];
2124
2215
  const flushTable = () => {
2125
2216
  if (tableLines.length >= 2) {
2126
2217
  const headerLine = tableLines[0];
@@ -2149,6 +2240,24 @@ var MarkdownRenderer = ({ content, className }) => {
2149
2240
  }
2150
2241
  tableLines = [];
2151
2242
  };
2243
+ const flushChoices = () => {
2244
+ if (choiceItems.length >= 2 && onChoiceClick) {
2245
+ elements.push(
2246
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2247
+ ChoiceButtons,
2248
+ {
2249
+ choices: choiceItems,
2250
+ onChoiceClick
2251
+ },
2252
+ `choices-${elements.length}`
2253
+ )
2254
+ );
2255
+ choiceItems = [];
2256
+ return true;
2257
+ }
2258
+ choiceItems = [];
2259
+ return false;
2260
+ };
2152
2261
  const flushList = () => {
2153
2262
  if (currentList) {
2154
2263
  if (currentList.type === "ul") {
@@ -2156,9 +2265,11 @@ var MarkdownRenderer = ({ content, className }) => {
2156
2265
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { style: { margin: "8px 0", paddingLeft: "24px" }, children: currentList.items }, `ul-${elements.length}`)
2157
2266
  );
2158
2267
  } else {
2159
- elements.push(
2160
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ol", { style: { margin: "8px 0", paddingLeft: "24px" }, children: currentList.items }, `ol-${elements.length}`)
2161
- );
2268
+ if (!flushChoices()) {
2269
+ elements.push(
2270
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ol", { style: { margin: "8px 0", paddingLeft: "24px" }, children: currentList.items }, `ol-${elements.length}`)
2271
+ );
2272
+ }
2162
2273
  }
2163
2274
  currentList = null;
2164
2275
  }
@@ -2287,15 +2398,24 @@ var MarkdownRenderer = ({ content, className }) => {
2287
2398
  );
2288
2399
  return;
2289
2400
  }
2290
- const olMatch = line.match(/^(\d+)\.\s+(.+)$/);
2401
+ const olMatch = line.match(/^(\d+)[.)]\s+(.+)$/);
2291
2402
  if (olMatch) {
2292
2403
  flushBlockquote();
2293
2404
  if (!currentList || currentList.type !== "ol") {
2294
2405
  flushList();
2295
2406
  currentList = { type: "ol", items: [] };
2296
2407
  }
2408
+ const itemNumber = parseInt(olMatch[1]);
2409
+ const itemText = olMatch[2];
2410
+ if (itemText.length <= 100) {
2411
+ choiceItems.push({
2412
+ number: itemNumber,
2413
+ text: itemText,
2414
+ fullText: line
2415
+ });
2416
+ }
2297
2417
  currentList.items.push(
2298
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { style: { margin: "4px 0" }, children: parseInlineElements(olMatch[2], `li-${lineIndex}`) }, `li-${lineIndex}`)
2418
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { style: { margin: "4px 0" }, children: parseInlineElements(itemText, `li-${lineIndex}`) }, `li-${lineIndex}`)
2299
2419
  );
2300
2420
  return;
2301
2421
  }
@@ -2312,7 +2432,7 @@ var MarkdownRenderer = ({ content, className }) => {
2312
2432
  flushBlockquote();
2313
2433
  flushTable();
2314
2434
  return elements;
2315
- }, [content]);
2435
+ }, [content, onChoiceClick]);
2316
2436
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2317
2437
  "div",
2318
2438
  {
@@ -2343,7 +2463,8 @@ var MessageBubble = ({
2343
2463
  alternatives,
2344
2464
  activeAlternativeIndex = 0,
2345
2465
  onAlternativeChange,
2346
- nextAssistantMessage
2466
+ nextAssistantMessage,
2467
+ onChoiceClick
2347
2468
  }) => {
2348
2469
  const [showActions, setShowActions] = (0, import_react6.useState)(false);
2349
2470
  const [showModelMenu, setShowModelMenu] = (0, import_react6.useState)(false);
@@ -2447,7 +2568,7 @@ var MessageBubble = ({
2447
2568
  children: [
2448
2569
  isAssistant ? (
2449
2570
  // AI 메시지는 마크다운 렌더링
2450
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MarkdownRenderer, { content: displayContent })
2571
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MarkdownRenderer, { content: displayContent, onChoiceClick })
2451
2572
  ) : (
2452
2573
  // 사용자 메시지는 일반 텍스트
2453
2574
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
@@ -2736,7 +2857,8 @@ var MessageList = ({
2736
2857
  activeAlternatives = {},
2737
2858
  models,
2738
2859
  copiedId,
2739
- editingId
2860
+ editingId,
2861
+ onChoiceClick
2740
2862
  }) => {
2741
2863
  const messagesEndRef = (0, import_react7.useRef)(null);
2742
2864
  const containerRef = (0, import_react7.useRef)(null);
@@ -2808,7 +2930,8 @@ var MessageList = ({
2808
2930
  onAskOtherModel: message.role === "user" && assistantForAlts && onAskOtherModel ? (targetModel) => onAskOtherModel(message.id, assistantForAlts.id, targetModel) : void 0,
2809
2931
  onAlternativeChange: message.role === "user" && assistantForAlts && onSetActiveAlternative ? (idx) => onSetActiveAlternative(assistantForAlts.id, idx) : message.role === "assistant" && onSetActiveAlternative ? (idx) => onSetActiveAlternative(message.id, idx) : void 0,
2810
2932
  models,
2811
- alternatives: message.role === "assistant" ? message.alternatives : assistantForAlts?.alternatives
2933
+ alternatives: message.role === "assistant" ? message.alternatives : assistantForAlts?.alternatives,
2934
+ onChoiceClick: message.role === "assistant" ? onChoiceClick : void 0
2812
2935
  },
2813
2936
  message.id
2814
2937
  );
@@ -4161,6 +4284,9 @@ var ChatUI = ({
4161
4284
  const handleSubmit = () => {
4162
4285
  sendMessage();
4163
4286
  };
4287
+ const handleChoiceClick = (choice) => {
4288
+ setInput(choice.text);
4289
+ };
4164
4290
  const [memoryPanelOpen, setMemoryPanelOpen] = (0, import_react10.useState)(false);
4165
4291
  const memoryItems = import_react10.default.useMemo(() => {
4166
4292
  const items = [];
@@ -4272,7 +4398,8 @@ var ChatUI = ({
4272
4398
  activeAlternatives,
4273
4399
  models: hookModels,
4274
4400
  copiedId: copiedMessageId,
4275
- editingId: editingMessageId
4401
+ editingId: editingMessageId,
4402
+ onChoiceClick: handleChoiceClick
4276
4403
  }
4277
4404
  ),
4278
4405
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
@@ -4295,7 +4422,7 @@ var ChatUI = ({
4295
4422
  ]
4296
4423
  }
4297
4424
  ),
4298
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4425
+ showSettings && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4299
4426
  MemoryPanel,
4300
4427
  {
4301
4428
  items: memoryItems,