@gendive/chatllm 0.17.19 → 0.17.20

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.
@@ -483,7 +483,7 @@ var parseExtractionResult = (text) => {
483
483
  function useInfoExtraction(options) {
484
484
  const [isExtracting, setIsExtracting] = (0, import_react2.useState)(false);
485
485
  const [lastExtraction, setLastExtraction] = (0, import_react2.useState)(null);
486
- const { apiEndpoint, model, minConfidence = 0.8, globalMemory } = options;
486
+ const { apiEndpoint, model, minConfidence = 0.8, globalMemory, onCallLLM } = options;
487
487
  const extractInfo = (0, import_react2.useCallback)(
488
488
  async (messages) => {
489
489
  if (messages.length === 0) {
@@ -492,41 +492,44 @@ function useInfoExtraction(options) {
492
492
  setIsExtracting(true);
493
493
  try {
494
494
  const prompt = buildExtractionPrompt(messages);
495
- const response = await fetch(apiEndpoint, {
496
- method: "POST",
497
- headers: { "Content-Type": "application/json" },
498
- body: JSON.stringify({
499
- messages: [{ role: "user", content: prompt }],
500
- model: model || "default"
501
- })
502
- });
503
- if (!response.ok) {
504
- throw new Error(`API error: ${response.status}`);
505
- }
506
- const reader = response.body?.getReader();
507
- if (!reader) {
508
- return [];
509
- }
510
- const decoder = new TextDecoder();
511
- let buffer = "";
512
- let fullResponse = "";
513
- while (true) {
514
- const { done, value } = await reader.read();
515
- if (done) break;
516
- buffer += decoder.decode(value, { stream: true });
517
- const lines = buffer.split("\n");
518
- buffer = lines.pop() || "";
519
- for (const line of lines) {
520
- if (line.startsWith("data: ")) {
521
- const data = line.slice(6);
522
- if (data === "[DONE]") continue;
523
- try {
524
- const parsed = JSON.parse(data);
525
- {
495
+ let fullResponse;
496
+ if (onCallLLM) {
497
+ fullResponse = await onCallLLM(prompt, model || "default");
498
+ } else {
499
+ const response = await fetch(apiEndpoint, {
500
+ method: "POST",
501
+ headers: { "Content-Type": "application/json" },
502
+ body: JSON.stringify({
503
+ messages: [{ role: "user", content: prompt }],
504
+ model: model || "default"
505
+ })
506
+ });
507
+ if (!response.ok) {
508
+ throw new Error(`API error: ${response.status}`);
509
+ }
510
+ const reader = response.body?.getReader();
511
+ if (!reader) {
512
+ return [];
513
+ }
514
+ const decoder = new TextDecoder();
515
+ let buffer = "";
516
+ fullResponse = "";
517
+ while (true) {
518
+ const { done, value } = await reader.read();
519
+ if (done) break;
520
+ buffer += decoder.decode(value, { stream: true });
521
+ const lines = buffer.split("\n");
522
+ buffer = lines.pop() || "";
523
+ for (const line of lines) {
524
+ if (line.startsWith("data: ")) {
525
+ const data = line.slice(6);
526
+ if (data === "[DONE]") continue;
527
+ try {
528
+ const parsed = JSON.parse(data);
526
529
  const chunk = parsed.content ?? parsed.text ?? "";
527
530
  if (chunk) fullResponse += chunk;
531
+ } catch {
528
532
  }
529
- } catch {
530
533
  }
531
534
  }
532
535
  }
@@ -553,7 +556,7 @@ function useInfoExtraction(options) {
553
556
  setIsExtracting(false);
554
557
  }
555
558
  },
556
- [apiEndpoint, model, minConfidence, globalMemory]
559
+ [apiEndpoint, model, minConfidence, globalMemory, onCallLLM]
557
560
  );
558
561
  return {
559
562
  extractInfo,
@@ -1609,6 +1612,33 @@ var removeSessionCache = (storageKey, sessionId) => {
1609
1612
  };
1610
1613
 
1611
1614
  // src/react/hooks/useChatUI.ts
1615
+ var parseSSEResponse = async (response) => {
1616
+ const reader = response.body?.getReader();
1617
+ if (!reader) return "";
1618
+ const decoder = new TextDecoder();
1619
+ let buffer = "";
1620
+ let result = "";
1621
+ while (true) {
1622
+ const { done, value } = await reader.read();
1623
+ if (done) break;
1624
+ buffer += decoder.decode(value, { stream: true });
1625
+ const lines = buffer.split("\n");
1626
+ buffer = lines.pop() || "";
1627
+ for (const line of lines) {
1628
+ if (line.startsWith("data: ")) {
1629
+ const data = line.slice(6);
1630
+ if (data === "[DONE]") continue;
1631
+ try {
1632
+ const parsed = JSON.parse(data);
1633
+ const chunk = parsed.content ?? parsed.text ?? "";
1634
+ if (chunk) result += chunk;
1635
+ } catch {
1636
+ }
1637
+ }
1638
+ }
1639
+ }
1640
+ return result;
1641
+ };
1612
1642
  var DEFAULT_STORAGE_KEY = "chatllm_sessions";
1613
1643
  var DEFAULT_COMPRESSION_THRESHOLD = 20;
1614
1644
  var DEFAULT_KEEP_RECENT = 6;
@@ -1814,11 +1844,37 @@ var useChatUI = (options) => {
1814
1844
  );
1815
1845
  const projectMemoryRaw = useGlobalMemory(projectMemoryOptions);
1816
1846
  const projectMemory = enableProjects ? projectMemoryRaw : null;
1847
+ const callInternalLLM = (0, import_react5.useCallback)(async (prompt, model) => {
1848
+ if (onSendMessageRef.current) {
1849
+ const modelConfig = models.find((m) => m.id === model);
1850
+ const provider = modelConfig?.provider || "ollama";
1851
+ const result = await onSendMessageRef.current({
1852
+ messages: [{ role: "user", content: prompt }],
1853
+ model,
1854
+ provider,
1855
+ apiKey
1856
+ });
1857
+ if (typeof result === "string") return result;
1858
+ if (typeof result === "object" && "content" in result) return result.content;
1859
+ return parseSSEResponse(new Response(result));
1860
+ }
1861
+ const response = await fetch(apiEndpoint, {
1862
+ method: "POST",
1863
+ headers: { "Content-Type": "application/json" },
1864
+ body: JSON.stringify({
1865
+ messages: [{ role: "user", content: prompt }],
1866
+ model
1867
+ })
1868
+ });
1869
+ if (!response.ok) return "";
1870
+ return parseSSEResponse(response);
1871
+ }, [apiEndpoint, apiKey, models]);
1817
1872
  const infoExtraction = useInfoExtraction({
1818
1873
  apiEndpoint,
1819
1874
  model: selectedModel,
1820
1875
  minConfidence: 0.8,
1821
- globalMemory
1876
+ globalMemory,
1877
+ onCallLLM: callInternalLLM
1822
1878
  });
1823
1879
  const currentSession = sessions.find((s) => s.id === currentSessionId) || null;
1824
1880
  const messages = currentSession?.messages.filter((m) => !m.hidden) || [];
@@ -2016,46 +2072,11 @@ ${conversationText}
2016
2072
 
2017
2073
  \uC694\uC57D:`;
2018
2074
  try {
2019
- const response = await fetch(apiEndpoint, {
2020
- method: "POST",
2021
- headers: { "Content-Type": "application/json" },
2022
- body: JSON.stringify({
2023
- messages: [{ role: "user", content: summaryPrompt }],
2024
- model
2025
- })
2026
- });
2027
- if (!response.ok) return "";
2028
- const reader = response.body?.getReader();
2029
- if (!reader) return "";
2030
- const decoder = new TextDecoder();
2031
- let buffer = "";
2032
- let summary = "";
2033
- while (true) {
2034
- const { done, value } = await reader.read();
2035
- if (done) break;
2036
- buffer += decoder.decode(value, { stream: true });
2037
- const lines = buffer.split("\n");
2038
- buffer = lines.pop() || "";
2039
- for (const line of lines) {
2040
- if (line.startsWith("data: ")) {
2041
- const data = line.slice(6);
2042
- if (data === "[DONE]") continue;
2043
- try {
2044
- const parsed = JSON.parse(data);
2045
- {
2046
- const chunk = parsed.content ?? parsed.text ?? "";
2047
- if (chunk) summary += chunk;
2048
- }
2049
- } catch {
2050
- }
2051
- }
2052
- }
2053
- }
2054
- return summary;
2075
+ return await callInternalLLM(summaryPrompt, model);
2055
2076
  } catch {
2056
2077
  return "";
2057
2078
  }
2058
- }, [apiEndpoint]);
2079
+ }, [callInternalLLM]);
2059
2080
  const incrementalCompressContext = (0, import_react5.useCallback)(
2060
2081
  async (existingSummary, newMessages, model) => {
2061
2082
  const newConversation = newMessages.map((m) => `${m.role === "user" ? "\uC0AC\uC6A9\uC790" : "AI"}: ${m.content}`).join("\n\n");
@@ -2075,47 +2096,13 @@ ${newConversation}
2075
2096
 
2076
2097
  \uD1B5\uD569 \uC694\uC57D:`;
2077
2098
  try {
2078
- const response = await fetch(apiEndpoint, {
2079
- method: "POST",
2080
- headers: { "Content-Type": "application/json" },
2081
- body: JSON.stringify({
2082
- messages: [{ role: "user", content: mergePrompt }],
2083
- model
2084
- })
2085
- });
2086
- if (!response.ok) return existingSummary;
2087
- const reader = response.body?.getReader();
2088
- if (!reader) return existingSummary;
2089
- const decoder = new TextDecoder();
2090
- let buffer = "";
2091
- let summary = "";
2092
- while (true) {
2093
- const { done, value } = await reader.read();
2094
- if (done) break;
2095
- buffer += decoder.decode(value, { stream: true });
2096
- const lines = buffer.split("\n");
2097
- buffer = lines.pop() || "";
2098
- for (const line of lines) {
2099
- if (line.startsWith("data: ")) {
2100
- const data = line.slice(6);
2101
- if (data === "[DONE]") continue;
2102
- try {
2103
- const parsed = JSON.parse(data);
2104
- {
2105
- const chunk = parsed.content ?? parsed.text ?? "";
2106
- if (chunk) summary += chunk;
2107
- }
2108
- } catch {
2109
- }
2110
- }
2111
- }
2112
- }
2099
+ const summary = await callInternalLLM(mergePrompt, model);
2113
2100
  return summary || existingSummary;
2114
2101
  } catch {
2115
2102
  return existingSummary;
2116
2103
  }
2117
2104
  },
2118
- [apiEndpoint]
2105
+ [callInternalLLM]
2119
2106
  );
2120
2107
  const estimateTokens = (0, import_react5.useCallback)((messages2) => {
2121
2108
  return messages2.reduce((sum, m) => sum + Math.ceil(m.content.length / 4), 0);