llmasaservice-ui 0.12.2 → 0.12.3

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/index.css CHANGED
@@ -395,6 +395,7 @@
395
395
  display: flex;
396
396
  align-items: center;
397
397
  justify-content: center;
398
+ margin-top: 4px;
398
399
  font-size: 10px;
399
400
  color: #666;
400
401
  opacity: 0.7;
@@ -720,3 +721,60 @@
720
721
  .dark-theme .tool-status {
721
722
  color: #999;
722
723
  }
724
+ .reasoning-section,
725
+ .searching-section {
726
+ margin: 1rem 0;
727
+ padding: 0.75rem;
728
+ border-radius: var(--border-radius);
729
+ border-left: 4px solid;
730
+ background-color: var(--reasoning-background-color, #f8f9fa);
731
+ }
732
+ .reasoning-section {
733
+ border-left-color: var(--reasoning-border-color, #007bff);
734
+ background-color: var(--reasoning-background-color, #f0f8ff);
735
+ }
736
+ .searching-section {
737
+ border-left-color: var(--searching-border-color, #28a745);
738
+ background-color: var(--searching-background-color, #f0fff0);
739
+ }
740
+ .reasoning-header,
741
+ .searching-header {
742
+ font-weight: 600;
743
+ font-size: 0.9rem;
744
+ margin-bottom: 0.5rem;
745
+ color: var(--prompt-text-color);
746
+ opacity: 0.8;
747
+ }
748
+ .reasoning-content,
749
+ .searching-content {
750
+ font-size: 0.85rem;
751
+ line-height: 1.4;
752
+ color: var(--prompt-text-color);
753
+ opacity: 0.9;
754
+ font-style: italic;
755
+ }
756
+ .dark-theme .reasoning-section {
757
+ background-color: var(--reasoning-background-color-dark, #1a1a2e);
758
+ border-left-color: var(--reasoning-border-color-dark, #4a9eff);
759
+ }
760
+ .dark-theme .searching-section {
761
+ background-color: var(--searching-background-color-dark, #1a2e1a);
762
+ border-left-color: var(--searching-border-color-dark, #4ade80);
763
+ }
764
+ .dark-theme .reasoning-header,
765
+ .dark-theme .searching-header,
766
+ .dark-theme .reasoning-content,
767
+ .dark-theme .searching-content {
768
+ color: var(--response-text-color);
769
+ }
770
+ .streaming-response {
771
+ animation: fadeIn 0.3s ease-in-out;
772
+ }
773
+ @keyframes fadeIn {
774
+ from {
775
+ opacity: 0;
776
+ }
777
+ to {
778
+ opacity: 1;
779
+ }
780
+ }
package/dist/index.js CHANGED
@@ -272,30 +272,35 @@ var ChatPanel = ({
272
272
  const [iframeUrl, setIframeUrl] = (0, import_react3.useState)(null);
273
273
  const responseAreaRef = (0, import_react3.useRef)(null);
274
274
  const extractLastThinkingTag = (text) => {
275
- var _a2, _b;
275
+ var _a2, _b, _c, _d;
276
+ console.log("extractLastThinkingTag called with:", (text == null ? void 0 : text.length) ? `${text.substring(0, 100)}...` : "empty");
276
277
  if (!text) return "Thinking";
277
- const reasoningRegex = /<reasoning>(.*?)<\/reasoning>/gi;
278
- const searchingRegex = /<searching>(.*?)<\/searching>/gi;
278
+ const reasoningRegex = /<reasoning>([\s\S]*?)<\/reasoning>/gi;
279
+ const searchingRegex = /<searching>([\s\S]*?)<\/searching>/gi;
279
280
  const allMatches = [];
280
281
  let reasoningMatch;
281
282
  while ((reasoningMatch = reasoningRegex.exec(text)) !== null) {
283
+ console.log("Found reasoning match:", ((_a2 = reasoningMatch[1]) == null ? void 0 : _a2.substring(0, 50)) + "...");
282
284
  allMatches.push({
283
- content: ((_a2 = reasoningMatch[1]) == null ? void 0 : _a2.trim()) || "",
285
+ content: ((_b = reasoningMatch[1]) == null ? void 0 : _b.trim()) || "",
284
286
  index: reasoningMatch.index,
285
287
  type: "reasoning"
286
288
  });
287
289
  }
288
290
  let searchingMatch;
289
291
  while ((searchingMatch = searchingRegex.exec(text)) !== null) {
292
+ console.log("Found searching match:", ((_c = searchingMatch[1]) == null ? void 0 : _c.substring(0, 50)) + "...");
290
293
  allMatches.push({
291
- content: ((_b = searchingMatch[1]) == null ? void 0 : _b.trim()) || "",
294
+ content: ((_d = searchingMatch[1]) == null ? void 0 : _d.trim()) || "",
292
295
  index: searchingMatch.index,
293
296
  type: "searching"
294
297
  });
295
298
  }
299
+ console.log("Total matches found:", allMatches.length);
296
300
  if (allMatches.length > 0) {
297
301
  const lastMatch = allMatches.sort((a, b) => b.index - a.index)[0];
298
302
  let content = (lastMatch == null ? void 0 : lastMatch.content) || "Thinking";
303
+ console.log("Last match content:", content == null ? void 0 : content.substring(0, 100));
299
304
  content = content.replace(/\*\*(.*?)\*\*/g, "$1");
300
305
  content = content.replace(/\*(.*?)\*/g, "$1");
301
306
  content = content.replace(/\n+/g, " ");
@@ -304,16 +309,46 @@ var ChatPanel = ({
304
309
  if (content.length > 80) {
305
310
  content = content.substring(0, 77) + "...";
306
311
  }
312
+ console.log("Final extracted content:", content);
307
313
  return content || "Thinking";
308
314
  }
315
+ console.log("No matches found, returning 'Thinking'");
309
316
  return "Thinking";
310
317
  };
311
- const cleanResponseContent = (text) => {
318
+ const processResponseContent = (text, isStreaming = false) => {
312
319
  if (!text) return "";
313
- let cleanText = text.replace(/<reasoning>[\s\S]*?<\/reasoning>/gi, "");
314
- cleanText = cleanText.replace(/<searching>[\s\S]*?<\/searching>/gi, "");
315
- cleanText = cleanText.replace(/\n\s*\n/g, "\n").trim();
316
- return cleanText;
320
+ let processedText = text;
321
+ processedText = processedText.replace(
322
+ /<reasoning>([\s\S]*?)<\/reasoning>/gi,
323
+ (match, content) => {
324
+ const trimmedContent = content.trim();
325
+ if (!trimmedContent) return "";
326
+ return `
327
+
328
+ <div class="reasoning-section">
329
+ <div class="reasoning-header">\u{1F914} Reasoning</div>
330
+ <div class="reasoning-content">${trimmedContent}</div>
331
+ </div>
332
+
333
+ `;
334
+ }
335
+ );
336
+ processedText = processedText.replace(
337
+ /<searching>([\s\S]*?)<\/searching>/gi,
338
+ (match, content) => {
339
+ const trimmedContent = content.trim();
340
+ if (!trimmedContent) return "";
341
+ return `
342
+
343
+ <div class="searching-section">
344
+ <div class="searching-header">\u{1F50D} Searching</div>
345
+ <div class="searching-content">${trimmedContent}</div>
346
+ </div>
347
+
348
+ `;
349
+ }
350
+ );
351
+ return processedText;
317
352
  };
318
353
  const getBrowserInfo = () => {
319
354
  try {
@@ -465,6 +500,16 @@ var ChatPanel = ({
465
500
  };
466
501
  })
467
502
  });
503
+ (0, import_react3.useEffect)(() => {
504
+ console.log("Streaming state changed:", {
505
+ isLoading,
506
+ idle,
507
+ responseLength: (response == null ? void 0 : response.length) || 0,
508
+ lastCallId,
509
+ hasResponse: !!response,
510
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
511
+ });
512
+ }, [isLoading, idle, response, lastCallId]);
468
513
  (0, import_react3.useEffect)(() => {
469
514
  setShowEmailPanel(customerEmailCaptureMode !== "HIDE");
470
515
  if (customerEmailCaptureMode === "REQUIRED") {
@@ -887,6 +932,14 @@ var ChatPanel = ({
887
932
  });
888
933
  (0, import_react3.useEffect)(() => {
889
934
  if (response && response.length > 0) {
935
+ console.log("Response updated:", {
936
+ length: response.length,
937
+ isLoading,
938
+ idle,
939
+ hasReasoningTags: response.includes("<reasoning>"),
940
+ hasSearchingTags: response.includes("<searching>"),
941
+ preview: response.substring(0, 200) + "..."
942
+ });
890
943
  setIsLoading(false);
891
944
  let newResponse = response;
892
945
  const toolRequests = [];
@@ -958,6 +1011,13 @@ var ChatPanel = ({
958
1011
  } else {
959
1012
  setPendingToolRequests([]);
960
1013
  }
1014
+ const cleanedContent = processResponseContent(newResponse);
1015
+ console.log("Processed content:", {
1016
+ originalLength: newResponse.length,
1017
+ processedLength: cleanedContent.length,
1018
+ wasContentRemoved: newResponse.length !== cleanedContent.length,
1019
+ processedPreview: cleanedContent.substring(0, 200) + "..."
1020
+ });
961
1021
  setHistory((prevHistory) => {
962
1022
  const existingEntry = prevHistory[lastKey != null ? lastKey : ""] || {
963
1023
  content: "",
@@ -966,7 +1026,7 @@ var ChatPanel = ({
966
1026
  return __spreadProps(__spreadValues({}, prevHistory), {
967
1027
  [lastKey != null ? lastKey : ""]: __spreadProps(__spreadValues({}, existingEntry), {
968
1028
  // This preserves toolCalls and toolResponses
969
- content: cleanResponseContent(newResponse),
1029
+ content: cleanedContent,
970
1030
  callId: lastCallId
971
1031
  })
972
1032
  });
@@ -1184,7 +1244,7 @@ var ChatPanel = ({
1184
1244
  setHistory((prevHistory) => {
1185
1245
  return __spreadProps(__spreadValues({}, prevHistory), {
1186
1246
  [lastKey != null ? lastKey : ""]: {
1187
- content: cleanResponseContent(response) + "\n\n(response cancelled)",
1247
+ content: processResponseContent(response) + "\n\n(response cancelled)",
1188
1248
  callId: lastCallId
1189
1249
  }
1190
1250
  });
@@ -1212,7 +1272,7 @@ var ChatPanel = ({
1212
1272
  messagesAndHistory.push({ role: "user", content: promptToSend });
1213
1273
  messagesAndHistory.push({
1214
1274
  role: "assistant",
1215
- content: cleanResponseContent(response2.content)
1275
+ content: processResponseContent(response2.content)
1216
1276
  });
1217
1277
  });
1218
1278
  let nextPromptToSend = suggestion != null ? suggestion : nextPrompt;
@@ -1579,7 +1639,19 @@ var ChatPanel = ({
1579
1639
  var _a2, _b;
1580
1640
  const isLastEntry = index === Object.keys(history).length - 1;
1581
1641
  const hasToolData = !!((((_a2 = historyEntry == null ? void 0 : historyEntry.toolCalls) == null ? void 0 : _a2.length) || 0) > 0 || (((_b = historyEntry == null ? void 0 : historyEntry.toolResponses) == null ? void 0 : _b.length) || 0) > 0);
1582
- return /* @__PURE__ */ import_react3.default.createElement("div", { className: "history-entry", key: index }, hideInitialPrompt && index === 0 ? null : /* @__PURE__ */ import_react3.default.createElement("div", { className: "prompt" }, formatPromptForDisplay(prompt)), /* @__PURE__ */ import_react3.default.createElement("div", { className: "response" }, index === Object.keys(history).length - 1 && isLoading && (!response || response.length < 50) ? /* @__PURE__ */ import_react3.default.createElement("div", { className: "loading-text" }, extractLastThinkingTag(response || ""), "\xA0", /* @__PURE__ */ import_react3.default.createElement("div", { className: "dot" }), /* @__PURE__ */ import_react3.default.createElement("div", { className: "dot" }), /* @__PURE__ */ import_react3.default.createElement("div", { className: "dot" })) : null, /* @__PURE__ */ import_react3.default.createElement(
1642
+ return /* @__PURE__ */ import_react3.default.createElement("div", { className: "history-entry", key: index }, hideInitialPrompt && index === 0 ? null : /* @__PURE__ */ import_react3.default.createElement("div", { className: "prompt" }, formatPromptForDisplay(prompt)), /* @__PURE__ */ import_react3.default.createElement("div", { className: "response" }, index === Object.keys(history).length - 1 && isLoading ? /* @__PURE__ */ import_react3.default.createElement("div", { className: "streaming-response" }, response && response.length > 0 ? /* @__PURE__ */ import_react3.default.createElement(
1643
+ import_react_markdown.default,
1644
+ {
1645
+ className: markdownClass,
1646
+ remarkPlugins: [import_remark_gfm.default],
1647
+ rehypePlugins: [import_rehype_raw.default],
1648
+ components: {
1649
+ /*a: CustomLink,*/
1650
+ code: CodeBlock
1651
+ }
1652
+ },
1653
+ processResponseContent(response, true)
1654
+ ) : /* @__PURE__ */ import_react3.default.createElement("div", { className: "loading-text" }, extractLastThinkingTag(response || ""), "\xA0", /* @__PURE__ */ import_react3.default.createElement("div", { className: "dot" }), /* @__PURE__ */ import_react3.default.createElement("div", { className: "dot" }), /* @__PURE__ */ import_react3.default.createElement("div", { className: "dot" }))) : /* @__PURE__ */ import_react3.default.createElement(
1583
1655
  import_react_markdown.default,
1584
1656
  {
1585
1657
  className: markdownClass,
package/dist/index.mjs CHANGED
@@ -244,30 +244,35 @@ var ChatPanel = ({
244
244
  const [iframeUrl, setIframeUrl] = useState2(null);
245
245
  const responseAreaRef = useRef(null);
246
246
  const extractLastThinkingTag = (text) => {
247
- var _a2, _b;
247
+ var _a2, _b, _c, _d;
248
+ console.log("extractLastThinkingTag called with:", (text == null ? void 0 : text.length) ? `${text.substring(0, 100)}...` : "empty");
248
249
  if (!text) return "Thinking";
249
- const reasoningRegex = /<reasoning>(.*?)<\/reasoning>/gi;
250
- const searchingRegex = /<searching>(.*?)<\/searching>/gi;
250
+ const reasoningRegex = /<reasoning>([\s\S]*?)<\/reasoning>/gi;
251
+ const searchingRegex = /<searching>([\s\S]*?)<\/searching>/gi;
251
252
  const allMatches = [];
252
253
  let reasoningMatch;
253
254
  while ((reasoningMatch = reasoningRegex.exec(text)) !== null) {
255
+ console.log("Found reasoning match:", ((_a2 = reasoningMatch[1]) == null ? void 0 : _a2.substring(0, 50)) + "...");
254
256
  allMatches.push({
255
- content: ((_a2 = reasoningMatch[1]) == null ? void 0 : _a2.trim()) || "",
257
+ content: ((_b = reasoningMatch[1]) == null ? void 0 : _b.trim()) || "",
256
258
  index: reasoningMatch.index,
257
259
  type: "reasoning"
258
260
  });
259
261
  }
260
262
  let searchingMatch;
261
263
  while ((searchingMatch = searchingRegex.exec(text)) !== null) {
264
+ console.log("Found searching match:", ((_c = searchingMatch[1]) == null ? void 0 : _c.substring(0, 50)) + "...");
262
265
  allMatches.push({
263
- content: ((_b = searchingMatch[1]) == null ? void 0 : _b.trim()) || "",
266
+ content: ((_d = searchingMatch[1]) == null ? void 0 : _d.trim()) || "",
264
267
  index: searchingMatch.index,
265
268
  type: "searching"
266
269
  });
267
270
  }
271
+ console.log("Total matches found:", allMatches.length);
268
272
  if (allMatches.length > 0) {
269
273
  const lastMatch = allMatches.sort((a, b) => b.index - a.index)[0];
270
274
  let content = (lastMatch == null ? void 0 : lastMatch.content) || "Thinking";
275
+ console.log("Last match content:", content == null ? void 0 : content.substring(0, 100));
271
276
  content = content.replace(/\*\*(.*?)\*\*/g, "$1");
272
277
  content = content.replace(/\*(.*?)\*/g, "$1");
273
278
  content = content.replace(/\n+/g, " ");
@@ -276,16 +281,46 @@ var ChatPanel = ({
276
281
  if (content.length > 80) {
277
282
  content = content.substring(0, 77) + "...";
278
283
  }
284
+ console.log("Final extracted content:", content);
279
285
  return content || "Thinking";
280
286
  }
287
+ console.log("No matches found, returning 'Thinking'");
281
288
  return "Thinking";
282
289
  };
283
- const cleanResponseContent = (text) => {
290
+ const processResponseContent = (text, isStreaming = false) => {
284
291
  if (!text) return "";
285
- let cleanText = text.replace(/<reasoning>[\s\S]*?<\/reasoning>/gi, "");
286
- cleanText = cleanText.replace(/<searching>[\s\S]*?<\/searching>/gi, "");
287
- cleanText = cleanText.replace(/\n\s*\n/g, "\n").trim();
288
- return cleanText;
292
+ let processedText = text;
293
+ processedText = processedText.replace(
294
+ /<reasoning>([\s\S]*?)<\/reasoning>/gi,
295
+ (match, content) => {
296
+ const trimmedContent = content.trim();
297
+ if (!trimmedContent) return "";
298
+ return `
299
+
300
+ <div class="reasoning-section">
301
+ <div class="reasoning-header">\u{1F914} Reasoning</div>
302
+ <div class="reasoning-content">${trimmedContent}</div>
303
+ </div>
304
+
305
+ `;
306
+ }
307
+ );
308
+ processedText = processedText.replace(
309
+ /<searching>([\s\S]*?)<\/searching>/gi,
310
+ (match, content) => {
311
+ const trimmedContent = content.trim();
312
+ if (!trimmedContent) return "";
313
+ return `
314
+
315
+ <div class="searching-section">
316
+ <div class="searching-header">\u{1F50D} Searching</div>
317
+ <div class="searching-content">${trimmedContent}</div>
318
+ </div>
319
+
320
+ `;
321
+ }
322
+ );
323
+ return processedText;
289
324
  };
290
325
  const getBrowserInfo = () => {
291
326
  try {
@@ -437,6 +472,16 @@ var ChatPanel = ({
437
472
  };
438
473
  })
439
474
  });
475
+ useEffect2(() => {
476
+ console.log("Streaming state changed:", {
477
+ isLoading,
478
+ idle,
479
+ responseLength: (response == null ? void 0 : response.length) || 0,
480
+ lastCallId,
481
+ hasResponse: !!response,
482
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
483
+ });
484
+ }, [isLoading, idle, response, lastCallId]);
440
485
  useEffect2(() => {
441
486
  setShowEmailPanel(customerEmailCaptureMode !== "HIDE");
442
487
  if (customerEmailCaptureMode === "REQUIRED") {
@@ -859,6 +904,14 @@ var ChatPanel = ({
859
904
  });
860
905
  useEffect2(() => {
861
906
  if (response && response.length > 0) {
907
+ console.log("Response updated:", {
908
+ length: response.length,
909
+ isLoading,
910
+ idle,
911
+ hasReasoningTags: response.includes("<reasoning>"),
912
+ hasSearchingTags: response.includes("<searching>"),
913
+ preview: response.substring(0, 200) + "..."
914
+ });
862
915
  setIsLoading(false);
863
916
  let newResponse = response;
864
917
  const toolRequests = [];
@@ -930,6 +983,13 @@ var ChatPanel = ({
930
983
  } else {
931
984
  setPendingToolRequests([]);
932
985
  }
986
+ const cleanedContent = processResponseContent(newResponse);
987
+ console.log("Processed content:", {
988
+ originalLength: newResponse.length,
989
+ processedLength: cleanedContent.length,
990
+ wasContentRemoved: newResponse.length !== cleanedContent.length,
991
+ processedPreview: cleanedContent.substring(0, 200) + "..."
992
+ });
933
993
  setHistory((prevHistory) => {
934
994
  const existingEntry = prevHistory[lastKey != null ? lastKey : ""] || {
935
995
  content: "",
@@ -938,7 +998,7 @@ var ChatPanel = ({
938
998
  return __spreadProps(__spreadValues({}, prevHistory), {
939
999
  [lastKey != null ? lastKey : ""]: __spreadProps(__spreadValues({}, existingEntry), {
940
1000
  // This preserves toolCalls and toolResponses
941
- content: cleanResponseContent(newResponse),
1001
+ content: cleanedContent,
942
1002
  callId: lastCallId
943
1003
  })
944
1004
  });
@@ -1156,7 +1216,7 @@ var ChatPanel = ({
1156
1216
  setHistory((prevHistory) => {
1157
1217
  return __spreadProps(__spreadValues({}, prevHistory), {
1158
1218
  [lastKey != null ? lastKey : ""]: {
1159
- content: cleanResponseContent(response) + "\n\n(response cancelled)",
1219
+ content: processResponseContent(response) + "\n\n(response cancelled)",
1160
1220
  callId: lastCallId
1161
1221
  }
1162
1222
  });
@@ -1184,7 +1244,7 @@ var ChatPanel = ({
1184
1244
  messagesAndHistory.push({ role: "user", content: promptToSend });
1185
1245
  messagesAndHistory.push({
1186
1246
  role: "assistant",
1187
- content: cleanResponseContent(response2.content)
1247
+ content: processResponseContent(response2.content)
1188
1248
  });
1189
1249
  });
1190
1250
  let nextPromptToSend = suggestion != null ? suggestion : nextPrompt;
@@ -1551,7 +1611,19 @@ var ChatPanel = ({
1551
1611
  var _a2, _b;
1552
1612
  const isLastEntry = index === Object.keys(history).length - 1;
1553
1613
  const hasToolData = !!((((_a2 = historyEntry == null ? void 0 : historyEntry.toolCalls) == null ? void 0 : _a2.length) || 0) > 0 || (((_b = historyEntry == null ? void 0 : historyEntry.toolResponses) == null ? void 0 : _b.length) || 0) > 0);
1554
- return /* @__PURE__ */ React3.createElement("div", { className: "history-entry", key: index }, hideInitialPrompt && index === 0 ? null : /* @__PURE__ */ React3.createElement("div", { className: "prompt" }, formatPromptForDisplay(prompt)), /* @__PURE__ */ React3.createElement("div", { className: "response" }, index === Object.keys(history).length - 1 && isLoading && (!response || response.length < 50) ? /* @__PURE__ */ React3.createElement("div", { className: "loading-text" }, extractLastThinkingTag(response || ""), "\xA0", /* @__PURE__ */ React3.createElement("div", { className: "dot" }), /* @__PURE__ */ React3.createElement("div", { className: "dot" }), /* @__PURE__ */ React3.createElement("div", { className: "dot" })) : null, /* @__PURE__ */ React3.createElement(
1614
+ return /* @__PURE__ */ React3.createElement("div", { className: "history-entry", key: index }, hideInitialPrompt && index === 0 ? null : /* @__PURE__ */ React3.createElement("div", { className: "prompt" }, formatPromptForDisplay(prompt)), /* @__PURE__ */ React3.createElement("div", { className: "response" }, index === Object.keys(history).length - 1 && isLoading ? /* @__PURE__ */ React3.createElement("div", { className: "streaming-response" }, response && response.length > 0 ? /* @__PURE__ */ React3.createElement(
1615
+ ReactMarkdown,
1616
+ {
1617
+ className: markdownClass,
1618
+ remarkPlugins: [remarkGfm],
1619
+ rehypePlugins: [rehypeRaw],
1620
+ components: {
1621
+ /*a: CustomLink,*/
1622
+ code: CodeBlock
1623
+ }
1624
+ },
1625
+ processResponseContent(response, true)
1626
+ ) : /* @__PURE__ */ React3.createElement("div", { className: "loading-text" }, extractLastThinkingTag(response || ""), "\xA0", /* @__PURE__ */ React3.createElement("div", { className: "dot" }), /* @__PURE__ */ React3.createElement("div", { className: "dot" }), /* @__PURE__ */ React3.createElement("div", { className: "dot" }))) : /* @__PURE__ */ React3.createElement(
1555
1627
  ReactMarkdown,
1556
1628
  {
1557
1629
  className: markdownClass,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llmasaservice-ui",
3
- "version": "0.12.2",
3
+ "version": "0.12.3",
4
4
  "description": "Prebuilt UI components for LLMAsAService.io",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
package/src/ChatPanel.css CHANGED
@@ -490,6 +490,7 @@
490
490
  display: flex;
491
491
  align-items: center;
492
492
  justify-content: center;
493
+ margin-top: 4px;
493
494
  font-size: 10px;
494
495
  color: #666;
495
496
  opacity: 0.7;
@@ -885,4 +886,72 @@
885
886
  /* Dark theme support */
886
887
  .dark-theme .tool-status {
887
888
  color: #999;
889
+ }
890
+
891
+ /* Reasoning and Searching Sections */
892
+ .reasoning-section, .searching-section {
893
+ margin: 1rem 0;
894
+ padding: 0.75rem;
895
+ border-radius: var(--border-radius);
896
+ border-left: 4px solid;
897
+ background-color: var(--reasoning-background-color, #f8f9fa);
898
+ }
899
+
900
+ .reasoning-section {
901
+ border-left-color: var(--reasoning-border-color, #007bff);
902
+ background-color: var(--reasoning-background-color, #f0f8ff);
903
+ }
904
+
905
+ .searching-section {
906
+ border-left-color: var(--searching-border-color, #28a745);
907
+ background-color: var(--searching-background-color, #f0fff0);
908
+ }
909
+
910
+ .reasoning-header, .searching-header {
911
+ font-weight: 600;
912
+ font-size: 0.9rem;
913
+ margin-bottom: 0.5rem;
914
+ color: var(--prompt-text-color);
915
+ opacity: 0.8;
916
+ }
917
+
918
+ .reasoning-content, .searching-content {
919
+ font-size: 0.85rem;
920
+ line-height: 1.4;
921
+ color: var(--prompt-text-color);
922
+ opacity: 0.9;
923
+ font-style: italic;
924
+ }
925
+
926
+ /* Dark theme support for reasoning/searching */
927
+ .dark-theme .reasoning-section {
928
+ background-color: var(--reasoning-background-color-dark, #1a1a2e);
929
+ border-left-color: var(--reasoning-border-color-dark, #4a9eff);
930
+ }
931
+
932
+ .dark-theme .searching-section {
933
+ background-color: var(--searching-background-color-dark, #1a2e1a);
934
+ border-left-color: var(--searching-border-color-dark, #4ade80);
935
+ }
936
+
937
+ .dark-theme .reasoning-header,
938
+ .dark-theme .searching-header,
939
+ .dark-theme .reasoning-content,
940
+ .dark-theme .searching-content {
941
+ color: var(--response-text-color);
942
+ }
943
+
944
+ /* Streaming response styling */
945
+ .streaming-response {
946
+ /* Add a subtle animation to indicate streaming */
947
+ animation: fadeIn 0.3s ease-in-out;
948
+ }
949
+
950
+ @keyframes fadeIn {
951
+ from {
952
+ opacity: 0;
953
+ }
954
+ to {
955
+ opacity: 1;
956
+ }
888
957
  }
package/src/ChatPanel.tsx CHANGED
@@ -232,17 +232,20 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
232
232
  const responseAreaRef = useRef(null);
233
233
  // Extract the last reasoning or searching tag from the response
234
234
  const extractLastThinkingTag = (text: string): string => {
235
+ console.log("extractLastThinkingTag called with:", text?.length ? `${text.substring(0, 100)}...` : "empty");
236
+
235
237
  if (!text) return "Thinking";
236
238
 
237
- // Find all reasoning and searching tags using exec method
238
- const reasoningRegex = /<reasoning>(.*?)<\/reasoning>/gi;
239
- const searchingRegex = /<searching>(.*?)<\/searching>/gi;
239
+ // Find all reasoning and searching tags using exec method (with global and multiline flags)
240
+ const reasoningRegex = /<reasoning>([\s\S]*?)<\/reasoning>/gi;
241
+ const searchingRegex = /<searching>([\s\S]*?)<\/searching>/gi;
240
242
 
241
243
  const allMatches: Array<{ content: string; index: number; type: string }> = [];
242
244
 
243
245
  // Find all reasoning matches
244
246
  let reasoningMatch;
245
247
  while ((reasoningMatch = reasoningRegex.exec(text)) !== null) {
248
+ console.log("Found reasoning match:", reasoningMatch[1]?.substring(0, 50) + "...");
246
249
  allMatches.push({
247
250
  content: reasoningMatch[1]?.trim() || "",
248
251
  index: reasoningMatch.index,
@@ -253,6 +256,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
253
256
  // Find all searching matches
254
257
  let searchingMatch;
255
258
  while ((searchingMatch = searchingRegex.exec(text)) !== null) {
259
+ console.log("Found searching match:", searchingMatch[1]?.substring(0, 50) + "...");
256
260
  allMatches.push({
257
261
  content: searchingMatch[1]?.trim() || "",
258
262
  index: searchingMatch.index,
@@ -260,11 +264,15 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
260
264
  });
261
265
  }
262
266
 
267
+ console.log("Total matches found:", allMatches.length);
268
+
263
269
  // Sort by position and get the last one
264
270
  if (allMatches.length > 0) {
265
271
  const lastMatch = allMatches.sort((a, b) => b.index - a.index)[0];
266
272
  let content = lastMatch?.content || "Thinking";
267
273
 
274
+ console.log("Last match content:", content?.substring(0, 100));
275
+
268
276
  // Clean up the content - remove markdown formatting and limit length
269
277
  content = content.replace(/\*\*(.*?)\*\*/g, '$1'); // Remove bold
270
278
  content = content.replace(/\*(.*?)\*/g, '$1'); // Remove italics
@@ -277,26 +285,49 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
277
285
  content = content.substring(0, 77) + '...';
278
286
  }
279
287
 
288
+ console.log("Final extracted content:", content);
280
289
  return content || "Thinking";
281
290
  }
282
291
 
292
+ console.log("No matches found, returning 'Thinking'");
283
293
  return "Thinking";
284
294
  };
285
295
 
286
- // Remove reasoning and searching tags from the response content
287
- const cleanResponseContent = (text: string): string => {
296
+ // Process response content to show reasoning/searching in a styled format
297
+ const processResponseContent = (text: string, isStreaming: boolean = false): string => {
288
298
  if (!text) return "";
289
299
 
290
- // Remove reasoning tags and their content (with multiline support)
291
- let cleanText = text.replace(/<reasoning>[\s\S]*?<\/reasoning>/gi, '');
300
+ let processedText = text;
292
301
 
293
- // Remove searching tags and their content (with multiline support)
294
- cleanText = cleanText.replace(/<searching>[\s\S]*?<\/searching>/gi, '');
302
+ // Replace reasoning tags with styled content
303
+ processedText = processedText.replace(
304
+ /<reasoning>([\s\S]*?)<\/reasoning>/gi,
305
+ (match, content) => {
306
+ const trimmedContent = content.trim();
307
+ if (!trimmedContent) return '';
308
+
309
+ return `\n\n<div class="reasoning-section">
310
+ <div class="reasoning-header">🤔 Reasoning</div>
311
+ <div class="reasoning-content">${trimmedContent}</div>
312
+ </div>\n\n`;
313
+ }
314
+ );
295
315
 
296
- // Clean up any extra whitespace that might be left
297
- cleanText = cleanText.replace(/\n\s*\n/g, '\n').trim();
316
+ // Replace searching tags with styled content
317
+ processedText = processedText.replace(
318
+ /<searching>([\s\S]*?)<\/searching>/gi,
319
+ (match, content) => {
320
+ const trimmedContent = content.trim();
321
+ if (!trimmedContent) return '';
322
+
323
+ return `\n\n<div class="searching-section">
324
+ <div class="searching-header">🔍 Searching</div>
325
+ <div class="searching-content">${trimmedContent}</div>
326
+ </div>\n\n`;
327
+ }
328
+ );
298
329
 
299
- return cleanText;
330
+ return processedText;
300
331
  };
301
332
 
302
333
  const getBrowserInfo = () => {
@@ -477,6 +508,18 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
477
508
  }) as [],
478
509
  });
479
510
 
511
+ // Add logging for streaming states
512
+ useEffect(() => {
513
+ console.log("Streaming state changed:", {
514
+ isLoading,
515
+ idle,
516
+ responseLength: response?.length || 0,
517
+ lastCallId,
518
+ hasResponse: !!response,
519
+ timestamp: new Date().toISOString()
520
+ });
521
+ }, [isLoading, idle, response, lastCallId]);
522
+
480
523
  useEffect(() => {
481
524
  setShowEmailPanel(customerEmailCaptureMode !== "HIDE");
482
525
 
@@ -998,6 +1041,15 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
998
1041
 
999
1042
  useEffect(() => {
1000
1043
  if (response && response.length > 0) {
1044
+ console.log("Response updated:", {
1045
+ length: response.length,
1046
+ isLoading,
1047
+ idle,
1048
+ hasReasoningTags: response.includes('<reasoning>'),
1049
+ hasSearchingTags: response.includes('<searching>'),
1050
+ preview: response.substring(0, 200) + "..."
1051
+ });
1052
+
1001
1053
  setIsLoading(false);
1002
1054
 
1003
1055
  let newResponse = response;
@@ -1088,6 +1140,14 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
1088
1140
  setPendingToolRequests([]);
1089
1141
  }
1090
1142
 
1143
+ const cleanedContent = processResponseContent(newResponse);
1144
+ console.log("Processed content:", {
1145
+ originalLength: newResponse.length,
1146
+ processedLength: cleanedContent.length,
1147
+ wasContentRemoved: newResponse.length !== cleanedContent.length,
1148
+ processedPreview: cleanedContent.substring(0, 200) + "..."
1149
+ });
1150
+
1091
1151
  setHistory((prevHistory) => {
1092
1152
  // Get any existing tool data from the previous state
1093
1153
  const existingEntry = prevHistory[lastKey ?? ""] || {
@@ -1099,7 +1159,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
1099
1159
  ...prevHistory,
1100
1160
  [lastKey ?? ""]: {
1101
1161
  ...existingEntry, // This preserves toolCalls and toolResponses
1102
- content: cleanResponseContent(newResponse),
1162
+ content: cleanedContent,
1103
1163
  callId: lastCallId,
1104
1164
  },
1105
1165
  };
@@ -1374,7 +1434,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
1374
1434
  return {
1375
1435
  ...prevHistory,
1376
1436
  [lastKey ?? ""]: {
1377
- content: cleanResponseContent(response) + "\n\n(response cancelled)",
1437
+ content: processResponseContent(response) + "\n\n(response cancelled)",
1378
1438
  callId: lastCallId,
1379
1439
  },
1380
1440
  };
@@ -1410,7 +1470,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
1410
1470
  messagesAndHistory.push({ role: "user", content: promptToSend });
1411
1471
  messagesAndHistory.push({
1412
1472
  role: "assistant",
1413
- content: cleanResponseContent(response.content),
1473
+ content: processResponseContent(response.content),
1414
1474
  });
1415
1475
  });
1416
1476
 
@@ -1885,22 +1945,37 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
1885
1945
  )}
1886
1946
 
1887
1947
  <div className="response">
1888
- {index === Object.keys(history).length - 1 && isLoading && (!response || response.length < 50) ? (
1889
- <div className="loading-text">
1890
- {extractLastThinkingTag(response || "")}&nbsp;
1891
- <div className="dot"></div>
1892
- <div className="dot"></div>
1893
- <div className="dot"></div>
1948
+ {/* Show streaming response with reasoning/searching tags */}
1949
+ {index === Object.keys(history).length - 1 && isLoading ? (
1950
+ <div className="streaming-response">
1951
+ {response && response.length > 0 ? (
1952
+ <ReactMarkdown
1953
+ className={markdownClass}
1954
+ remarkPlugins={[remarkGfm]}
1955
+ rehypePlugins={[rehypeRaw]}
1956
+ components={{ /*a: CustomLink,*/ code: CodeBlock }}
1957
+ >
1958
+ {processResponseContent(response, true)}
1959
+ </ReactMarkdown>
1960
+ ) : (
1961
+ <div className="loading-text">
1962
+ {extractLastThinkingTag(response || "")}&nbsp;
1963
+ <div className="dot"></div>
1964
+ <div className="dot"></div>
1965
+ <div className="dot"></div>
1966
+ </div>
1967
+ )}
1894
1968
  </div>
1895
- ) : null}
1896
- <ReactMarkdown
1897
- className={markdownClass}
1898
- remarkPlugins={[remarkGfm]}
1899
- rehypePlugins={[rehypeRaw]}
1900
- components={{ /*a: CustomLink,*/ code: CodeBlock }}
1901
- >
1902
- {historyEntry.content}
1903
- </ReactMarkdown>
1969
+ ) : (
1970
+ <ReactMarkdown
1971
+ className={markdownClass}
1972
+ remarkPlugins={[remarkGfm]}
1973
+ rehypePlugins={[rehypeRaw]}
1974
+ components={{ /*a: CustomLink,*/ code: CodeBlock }}
1975
+ >
1976
+ {historyEntry.content}
1977
+ </ReactMarkdown>
1978
+ )}
1904
1979
 
1905
1980
  {isLastEntry && pendingToolRequests.length > 0 && (
1906
1981
  <div className="approve-tools-panel">
File without changes
@@ -0,0 +1,125 @@
1
+ // Test file to verify tag extraction functions
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ // Mock test data
6
+ const testTexts = [
7
+ "This is a simple response without any tags.",
8
+ "<reasoning>I need to think about this problem step by step.</reasoning>Here's my answer.",
9
+ "Some text <searching>Looking for information about cats</searching> more text",
10
+ "<reasoning>First, I'll analyze the problem.</reasoning>Then I'll search. <searching>Finding relevant data</searching>Final answer here.",
11
+ "Multiple <reasoning>First reasoning</reasoning> and <reasoning>Second reasoning</reasoning> tags.",
12
+ "Incomplete <reasoning>Missing closing tag...",
13
+ "<reasoning>\nMultiline reasoning\nwith several lines\nof content\n</reasoning>Response text here.",
14
+ "Mixed <reasoning>Some thinking</reasoning> and <searching>Some searching</searching> with final answer."
15
+ ];
16
+
17
+ // Copy the extraction function from the React component
18
+ function extractLastThinkingTag(text) {
19
+ console.log("extractLastThinkingTag called with:", text?.length ? `${text.substring(0, 100)}...` : "empty");
20
+
21
+ if (!text) return "Thinking";
22
+
23
+ // Find all reasoning and searching tags using exec method (with global and multiline flags)
24
+ const reasoningRegex = /<reasoning>([\s\S]*?)<\/reasoning>/gi;
25
+ const searchingRegex = /<searching>([\s\S]*?)<\/searching>/gi;
26
+
27
+ const allMatches = [];
28
+
29
+ // Find all reasoning matches
30
+ let reasoningMatch;
31
+ while ((reasoningMatch = reasoningRegex.exec(text)) !== null) {
32
+ console.log("Found reasoning match:", reasoningMatch[1]?.substring(0, 50) + "...");
33
+ allMatches.push({
34
+ content: reasoningMatch[1]?.trim() || "",
35
+ index: reasoningMatch.index,
36
+ type: 'reasoning'
37
+ });
38
+ }
39
+
40
+ // Find all searching matches
41
+ let searchingMatch;
42
+ while ((searchingMatch = searchingRegex.exec(text)) !== null) {
43
+ console.log("Found searching match:", searchingMatch[1]?.substring(0, 50) + "...");
44
+ allMatches.push({
45
+ content: searchingMatch[1]?.trim() || "",
46
+ index: searchingMatch.index,
47
+ type: 'searching'
48
+ });
49
+ }
50
+
51
+ console.log("Total matches found:", allMatches.length);
52
+
53
+ // Sort by position and get the last one
54
+ if (allMatches.length > 0) {
55
+ const lastMatch = allMatches.sort((a, b) => b.index - a.index)[0];
56
+ let content = lastMatch?.content || "Thinking";
57
+
58
+ console.log("Last match content:", content?.substring(0, 100));
59
+
60
+ // Clean up the content - remove markdown formatting and limit length
61
+ content = content.replace(/\*\*(.*?)\*\*/g, '$1'); // Remove bold
62
+ content = content.replace(/\*(.*?)\*/g, '$1'); // Remove italics
63
+ content = content.replace(/\n+/g, ' '); // Replace newlines with spaces
64
+ content = content.replace(/\s+/g, ' '); // Normalize whitespace
65
+ content = content.trim();
66
+
67
+ // Limit length to keep UI clean
68
+ if (content.length > 80) {
69
+ content = content.substring(0, 77) + '...';
70
+ }
71
+
72
+ console.log("Final extracted content:", content);
73
+ return content || "Thinking";
74
+ }
75
+
76
+ console.log("No matches found, returning 'Thinking'");
77
+ return "Thinking";
78
+ }
79
+
80
+ // Copy the processing function from the React component
81
+ function processResponseContent(text, isStreaming = false) {
82
+ if (!text) return "";
83
+
84
+ let processedText = text;
85
+
86
+ // Replace reasoning tags with styled content
87
+ processedText = processedText.replace(
88
+ /<reasoning>([\s\S]*?)<\/reasoning>/gi,
89
+ (match, content) => {
90
+ const trimmedContent = content.trim();
91
+ if (!trimmedContent) return '';
92
+
93
+ return `\n\n<div class="reasoning-section">
94
+ <div class="reasoning-header">🤔 Reasoning</div>
95
+ <div class="reasoning-content">${trimmedContent}</div>
96
+ </div>\n\n`;
97
+ }
98
+ );
99
+
100
+ // Replace searching tags with styled content
101
+ processedText = processedText.replace(
102
+ /<searching>([\s\S]*?)<\/searching>/gi,
103
+ (match, content) => {
104
+ const trimmedContent = content.trim();
105
+ if (!trimmedContent) return '';
106
+
107
+ return `\n\n<div class="searching-section">
108
+ <div class="searching-header">🔍 Searching</div>
109
+ <div class="searching-content">${trimmedContent}</div>
110
+ </div>\n\n`;
111
+ }
112
+ );
113
+
114
+ return processedText;
115
+ }
116
+
117
+ console.log("Testing tag extraction and processing functions...\n");
118
+
119
+ testTexts.forEach((text, index) => {
120
+ console.log(`\n=== Test ${index + 1} ===`);
121
+ console.log("Input:", text);
122
+ console.log("Extracted tag:", extractLastThinkingTag(text));
123
+ console.log("Processed content:", processResponseContent(text));
124
+ console.log("---");
125
+ });
File without changes
File without changes