llmasaservice-ui 0.12.13 → 0.12.14
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 +66 -0
- package/dist/index.js +50 -36
- package/dist/index.mjs +50 -36
- package/package.json +1 -1
- package/src/ChatPanel.css +78 -0
- package/src/ChatPanel.tsx +93 -60
package/dist/index.css
CHANGED
|
@@ -778,3 +778,69 @@
|
|
|
778
778
|
opacity: 1;
|
|
779
779
|
}
|
|
780
780
|
}
|
|
781
|
+
.thinking-block-container {
|
|
782
|
+
margin-bottom: var(--spacing-medium);
|
|
783
|
+
}
|
|
784
|
+
.thinking-section {
|
|
785
|
+
border: 1px solid var(--input-border-color);
|
|
786
|
+
border-radius: var(--border-radius);
|
|
787
|
+
padding: var(--spacing-medium);
|
|
788
|
+
background-color: var(--input-background-color);
|
|
789
|
+
margin-bottom: var(--spacing-small);
|
|
790
|
+
}
|
|
791
|
+
.thinking-header {
|
|
792
|
+
display: flex;
|
|
793
|
+
justify-content: space-between;
|
|
794
|
+
align-items: center;
|
|
795
|
+
font-weight: bold;
|
|
796
|
+
color: var(--title-text-color);
|
|
797
|
+
margin-bottom: var(--spacing-small);
|
|
798
|
+
font-size: 0.9rem;
|
|
799
|
+
}
|
|
800
|
+
.thinking-navigation {
|
|
801
|
+
display: flex;
|
|
802
|
+
align-items: center;
|
|
803
|
+
gap: var(--spacing-small);
|
|
804
|
+
}
|
|
805
|
+
.thinking-nav-btn {
|
|
806
|
+
background: var(--button-background-color);
|
|
807
|
+
border: 1px solid var(--button-border-color);
|
|
808
|
+
border-radius: 4px;
|
|
809
|
+
padding: 2px 6px;
|
|
810
|
+
cursor: pointer;
|
|
811
|
+
font-size: 12px;
|
|
812
|
+
line-height: 1;
|
|
813
|
+
min-width: 20px;
|
|
814
|
+
height: 20px;
|
|
815
|
+
display: flex;
|
|
816
|
+
align-items: center;
|
|
817
|
+
justify-content: center;
|
|
818
|
+
}
|
|
819
|
+
.thinking-nav-btn:hover:not(:disabled) {
|
|
820
|
+
background: var(--button-background-color-hover);
|
|
821
|
+
color: var(--button-text-color-hover);
|
|
822
|
+
}
|
|
823
|
+
.thinking-nav-btn:disabled {
|
|
824
|
+
background: var(--button-disabled-background-color);
|
|
825
|
+
color: var(--button-disabled-color);
|
|
826
|
+
cursor: not-allowed;
|
|
827
|
+
}
|
|
828
|
+
.thinking-counter {
|
|
829
|
+
font-size: 11px;
|
|
830
|
+
color: var(--title-text-color);
|
|
831
|
+
font-weight: normal;
|
|
832
|
+
min-width: 30px;
|
|
833
|
+
text-align: center;
|
|
834
|
+
}
|
|
835
|
+
.thinking-content {
|
|
836
|
+
color: var(--input-text-color);
|
|
837
|
+
font-size: 0.85rem;
|
|
838
|
+
line-height: 1.4;
|
|
839
|
+
white-space: pre-wrap;
|
|
840
|
+
}
|
|
841
|
+
.reasoning-section {
|
|
842
|
+
border-left: 3px solid #4a90e2;
|
|
843
|
+
}
|
|
844
|
+
.searching-section {
|
|
845
|
+
border-left: 3px solid #7b68ee;
|
|
846
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -254,8 +254,8 @@ var ChatPanel = ({
|
|
|
254
254
|
[]
|
|
255
255
|
);
|
|
256
256
|
const [alwaysApprovedTools, setAlwaysApprovedTools] = (0, import_react3.useState)([]);
|
|
257
|
-
const [
|
|
258
|
-
const [
|
|
257
|
+
const [thinkingBlocks, setThinkingBlocks] = (0, import_react3.useState)([]);
|
|
258
|
+
const [currentThinkingIndex, setCurrentThinkingIndex] = (0, import_react3.useState)(0);
|
|
259
259
|
(0, import_react3.useEffect)(() => {
|
|
260
260
|
const stored = localStorage.getItem("alwaysApprovedTools");
|
|
261
261
|
if (stored) setAlwaysApprovedTools(JSON.parse(stored));
|
|
@@ -281,12 +281,9 @@ var ChatPanel = ({
|
|
|
281
281
|
var _a2, _b;
|
|
282
282
|
if (!text) return {
|
|
283
283
|
cleanedText: "",
|
|
284
|
-
|
|
285
|
-
searchingBlocks: [],
|
|
284
|
+
thinkingBlocks: [],
|
|
286
285
|
lastThinkingContent: "Thinking"
|
|
287
286
|
};
|
|
288
|
-
const newReasoningBlocks = [];
|
|
289
|
-
const newSearchingBlocks = [];
|
|
290
287
|
const allMatches = [];
|
|
291
288
|
let cleanedText = text;
|
|
292
289
|
const reasoningRegex = new RegExp(THINKING_PATTERNS.reasoning.source, "gi");
|
|
@@ -294,7 +291,6 @@ var ChatPanel = ({
|
|
|
294
291
|
while ((reasoningMatch = reasoningRegex.exec(text)) !== null) {
|
|
295
292
|
const content = (_a2 = reasoningMatch[1]) == null ? void 0 : _a2.trim();
|
|
296
293
|
if (content) {
|
|
297
|
-
newReasoningBlocks.push(content);
|
|
298
294
|
allMatches.push({
|
|
299
295
|
content,
|
|
300
296
|
index: reasoningMatch.index,
|
|
@@ -307,7 +303,6 @@ var ChatPanel = ({
|
|
|
307
303
|
while ((searchingMatch = searchingRegex.exec(text)) !== null) {
|
|
308
304
|
const content = (_b = searchingMatch[1]) == null ? void 0 : _b.trim();
|
|
309
305
|
if (content) {
|
|
310
|
-
newSearchingBlocks.push(content);
|
|
311
306
|
allMatches.push({
|
|
312
307
|
content,
|
|
313
308
|
index: searchingMatch.index,
|
|
@@ -315,12 +310,13 @@ var ChatPanel = ({
|
|
|
315
310
|
});
|
|
316
311
|
}
|
|
317
312
|
}
|
|
313
|
+
const thinkingBlocks2 = allMatches.sort((a, b) => a.index - b.index);
|
|
318
314
|
cleanedText = cleanedText.replace(THINKING_PATTERNS.reasoning, "");
|
|
319
315
|
cleanedText = cleanedText.replace(THINKING_PATTERNS.searching, "");
|
|
320
316
|
let lastThinkingContent = "Thinking";
|
|
321
|
-
if (
|
|
322
|
-
const
|
|
323
|
-
let content = (
|
|
317
|
+
if (thinkingBlocks2.length > 0) {
|
|
318
|
+
const lastBlock = thinkingBlocks2[thinkingBlocks2.length - 1];
|
|
319
|
+
let content = (lastBlock == null ? void 0 : lastBlock.content) || "Thinking";
|
|
324
320
|
content = content.replace(/\*\*(.*?)\*\*/g, "$1");
|
|
325
321
|
content = content.replace(/\*(.*?)\*/g, "$1");
|
|
326
322
|
content = content.replace(/\n+/g, " ");
|
|
@@ -333,24 +329,33 @@ var ChatPanel = ({
|
|
|
333
329
|
}
|
|
334
330
|
return {
|
|
335
331
|
cleanedText: cleanedText.trim(),
|
|
336
|
-
|
|
337
|
-
searchingBlocks: newSearchingBlocks,
|
|
332
|
+
thinkingBlocks: thinkingBlocks2,
|
|
338
333
|
lastThinkingContent
|
|
339
334
|
};
|
|
340
335
|
};
|
|
341
336
|
const renderThinkingBlocks = () => {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
337
|
+
if (thinkingBlocks.length === 0) return null;
|
|
338
|
+
const currentBlock = thinkingBlocks[currentThinkingIndex];
|
|
339
|
+
if (!currentBlock) return null;
|
|
340
|
+
const icon = currentBlock.type === "reasoning" ? "\u{1F914}" : "\u{1F50D}";
|
|
341
|
+
const title2 = currentBlock.type === "reasoning" ? "Reasoning" : "Searching";
|
|
342
|
+
return /* @__PURE__ */ import_react3.default.createElement("div", { className: "thinking-block-container" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: `thinking-section ${currentBlock.type}-section` }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "thinking-header" }, icon, " ", title2, thinkingBlocks.length > 1 && /* @__PURE__ */ import_react3.default.createElement("div", { className: "thinking-navigation" }, /* @__PURE__ */ import_react3.default.createElement(
|
|
343
|
+
"button",
|
|
344
|
+
{
|
|
345
|
+
onClick: () => setCurrentThinkingIndex(Math.max(0, currentThinkingIndex - 1)),
|
|
346
|
+
disabled: currentThinkingIndex === 0,
|
|
347
|
+
className: "thinking-nav-btn"
|
|
348
|
+
},
|
|
349
|
+
"\u2190"
|
|
350
|
+
), /* @__PURE__ */ import_react3.default.createElement("span", { className: "thinking-counter" }, currentThinkingIndex + 1, " / ", thinkingBlocks.length), /* @__PURE__ */ import_react3.default.createElement(
|
|
351
|
+
"button",
|
|
352
|
+
{
|
|
353
|
+
onClick: () => setCurrentThinkingIndex(Math.min(thinkingBlocks.length - 1, currentThinkingIndex + 1)),
|
|
354
|
+
disabled: currentThinkingIndex === thinkingBlocks.length - 1,
|
|
355
|
+
className: "thinking-nav-btn"
|
|
356
|
+
},
|
|
357
|
+
"\u2192"
|
|
358
|
+
))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "thinking-content" }, currentBlock.content)));
|
|
354
359
|
};
|
|
355
360
|
const getBrowserInfo = () => {
|
|
356
361
|
try {
|
|
@@ -943,9 +948,9 @@ var ChatPanel = ({
|
|
|
943
948
|
preview: response.substring(0, 200) + "..."
|
|
944
949
|
});
|
|
945
950
|
setIsLoading(false);
|
|
946
|
-
const { cleanedText,
|
|
947
|
-
|
|
948
|
-
|
|
951
|
+
const { cleanedText, thinkingBlocks: newThinkingBlocks } = processThinkingTags(response);
|
|
952
|
+
setThinkingBlocks(newThinkingBlocks);
|
|
953
|
+
setCurrentThinkingIndex(Math.max(0, newThinkingBlocks.length - 1));
|
|
949
954
|
let newResponse = cleanedText;
|
|
950
955
|
const toolRequests = [];
|
|
951
956
|
if (allActions && allActions.length > 0) {
|
|
@@ -1055,8 +1060,8 @@ var ChatPanel = ({
|
|
|
1055
1060
|
if (initialPrompt && initialPrompt !== "") {
|
|
1056
1061
|
if (initialPrompt !== lastPrompt) {
|
|
1057
1062
|
setIsLoading(true);
|
|
1058
|
-
|
|
1059
|
-
|
|
1063
|
+
setThinkingBlocks([]);
|
|
1064
|
+
setCurrentThinkingIndex(0);
|
|
1060
1065
|
ensureConversation().then((convId) => {
|
|
1061
1066
|
if (lastController) stop(lastController);
|
|
1062
1067
|
const controller = new AbortController();
|
|
@@ -1235,8 +1240,8 @@ var ChatPanel = ({
|
|
|
1235
1240
|
}, [currentCustomer, project_id, agent, publicAPIUrl, emailInput]);
|
|
1236
1241
|
const continueChat = (suggestion) => {
|
|
1237
1242
|
console.log("continueChat", suggestion);
|
|
1238
|
-
|
|
1239
|
-
|
|
1243
|
+
setThinkingBlocks([]);
|
|
1244
|
+
setCurrentThinkingIndex(0);
|
|
1240
1245
|
if (emailInput && isEmailAddress(emailInput) && !emailInputSet) {
|
|
1241
1246
|
const newId = (currentCustomer == null ? void 0 : currentCustomer.customer_id) && currentCustomer.customer_id !== "" && currentCustomer.customer_id !== (currentCustomer == null ? void 0 : currentCustomer.customer_user_email) ? currentCustomer.customer_id : emailInput;
|
|
1242
1247
|
setEmailInputSet(true);
|
|
@@ -1649,8 +1654,17 @@ var ChatPanel = ({
|
|
|
1649
1654
|
var _a2, _b;
|
|
1650
1655
|
const isLastEntry = index === Object.keys(history).length - 1;
|
|
1651
1656
|
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);
|
|
1652
|
-
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" },
|
|
1653
|
-
const { cleanedText
|
|
1657
|
+
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" }, (() => {
|
|
1658
|
+
const { cleanedText } = processThinkingTags(response || "");
|
|
1659
|
+
if (thinkingBlocks.length > 0) {
|
|
1660
|
+
return renderThinkingBlocks();
|
|
1661
|
+
}
|
|
1662
|
+
if (!cleanedText || cleanedText.length === 0) {
|
|
1663
|
+
return /* @__PURE__ */ import_react3.default.createElement("div", { className: "thinking-block-container" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "thinking-section" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "thinking-header" }, "\u{1F914} Thinking"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "thinking-content" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "loading-text" }, "Thinking...\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" })))));
|
|
1664
|
+
}
|
|
1665
|
+
return null;
|
|
1666
|
+
})(), (() => {
|
|
1667
|
+
const { cleanedText } = processThinkingTags(response || "");
|
|
1654
1668
|
return cleanedText && cleanedText.length > 0 ? /* @__PURE__ */ import_react3.default.createElement(
|
|
1655
1669
|
import_react_markdown.default,
|
|
1656
1670
|
{
|
|
@@ -1663,8 +1677,8 @@ var ChatPanel = ({
|
|
|
1663
1677
|
}
|
|
1664
1678
|
},
|
|
1665
1679
|
cleanedText
|
|
1666
|
-
) :
|
|
1667
|
-
})()) : /* @__PURE__ */ import_react3.default.createElement("div", null, isLastEntry &&
|
|
1680
|
+
) : null;
|
|
1681
|
+
})()) : /* @__PURE__ */ import_react3.default.createElement("div", null, isLastEntry && thinkingBlocks.length > 0 && renderThinkingBlocks(), /* @__PURE__ */ import_react3.default.createElement(
|
|
1668
1682
|
import_react_markdown.default,
|
|
1669
1683
|
{
|
|
1670
1684
|
className: markdownClass,
|
package/dist/index.mjs
CHANGED
|
@@ -226,8 +226,8 @@ var ChatPanel = ({
|
|
|
226
226
|
[]
|
|
227
227
|
);
|
|
228
228
|
const [alwaysApprovedTools, setAlwaysApprovedTools] = useState2([]);
|
|
229
|
-
const [
|
|
230
|
-
const [
|
|
229
|
+
const [thinkingBlocks, setThinkingBlocks] = useState2([]);
|
|
230
|
+
const [currentThinkingIndex, setCurrentThinkingIndex] = useState2(0);
|
|
231
231
|
useEffect2(() => {
|
|
232
232
|
const stored = localStorage.getItem("alwaysApprovedTools");
|
|
233
233
|
if (stored) setAlwaysApprovedTools(JSON.parse(stored));
|
|
@@ -253,12 +253,9 @@ var ChatPanel = ({
|
|
|
253
253
|
var _a2, _b;
|
|
254
254
|
if (!text) return {
|
|
255
255
|
cleanedText: "",
|
|
256
|
-
|
|
257
|
-
searchingBlocks: [],
|
|
256
|
+
thinkingBlocks: [],
|
|
258
257
|
lastThinkingContent: "Thinking"
|
|
259
258
|
};
|
|
260
|
-
const newReasoningBlocks = [];
|
|
261
|
-
const newSearchingBlocks = [];
|
|
262
259
|
const allMatches = [];
|
|
263
260
|
let cleanedText = text;
|
|
264
261
|
const reasoningRegex = new RegExp(THINKING_PATTERNS.reasoning.source, "gi");
|
|
@@ -266,7 +263,6 @@ var ChatPanel = ({
|
|
|
266
263
|
while ((reasoningMatch = reasoningRegex.exec(text)) !== null) {
|
|
267
264
|
const content = (_a2 = reasoningMatch[1]) == null ? void 0 : _a2.trim();
|
|
268
265
|
if (content) {
|
|
269
|
-
newReasoningBlocks.push(content);
|
|
270
266
|
allMatches.push({
|
|
271
267
|
content,
|
|
272
268
|
index: reasoningMatch.index,
|
|
@@ -279,7 +275,6 @@ var ChatPanel = ({
|
|
|
279
275
|
while ((searchingMatch = searchingRegex.exec(text)) !== null) {
|
|
280
276
|
const content = (_b = searchingMatch[1]) == null ? void 0 : _b.trim();
|
|
281
277
|
if (content) {
|
|
282
|
-
newSearchingBlocks.push(content);
|
|
283
278
|
allMatches.push({
|
|
284
279
|
content,
|
|
285
280
|
index: searchingMatch.index,
|
|
@@ -287,12 +282,13 @@ var ChatPanel = ({
|
|
|
287
282
|
});
|
|
288
283
|
}
|
|
289
284
|
}
|
|
285
|
+
const thinkingBlocks2 = allMatches.sort((a, b) => a.index - b.index);
|
|
290
286
|
cleanedText = cleanedText.replace(THINKING_PATTERNS.reasoning, "");
|
|
291
287
|
cleanedText = cleanedText.replace(THINKING_PATTERNS.searching, "");
|
|
292
288
|
let lastThinkingContent = "Thinking";
|
|
293
|
-
if (
|
|
294
|
-
const
|
|
295
|
-
let content = (
|
|
289
|
+
if (thinkingBlocks2.length > 0) {
|
|
290
|
+
const lastBlock = thinkingBlocks2[thinkingBlocks2.length - 1];
|
|
291
|
+
let content = (lastBlock == null ? void 0 : lastBlock.content) || "Thinking";
|
|
296
292
|
content = content.replace(/\*\*(.*?)\*\*/g, "$1");
|
|
297
293
|
content = content.replace(/\*(.*?)\*/g, "$1");
|
|
298
294
|
content = content.replace(/\n+/g, " ");
|
|
@@ -305,24 +301,33 @@ var ChatPanel = ({
|
|
|
305
301
|
}
|
|
306
302
|
return {
|
|
307
303
|
cleanedText: cleanedText.trim(),
|
|
308
|
-
|
|
309
|
-
searchingBlocks: newSearchingBlocks,
|
|
304
|
+
thinkingBlocks: thinkingBlocks2,
|
|
310
305
|
lastThinkingContent
|
|
311
306
|
};
|
|
312
307
|
};
|
|
313
308
|
const renderThinkingBlocks = () => {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
309
|
+
if (thinkingBlocks.length === 0) return null;
|
|
310
|
+
const currentBlock = thinkingBlocks[currentThinkingIndex];
|
|
311
|
+
if (!currentBlock) return null;
|
|
312
|
+
const icon = currentBlock.type === "reasoning" ? "\u{1F914}" : "\u{1F50D}";
|
|
313
|
+
const title2 = currentBlock.type === "reasoning" ? "Reasoning" : "Searching";
|
|
314
|
+
return /* @__PURE__ */ React3.createElement("div", { className: "thinking-block-container" }, /* @__PURE__ */ React3.createElement("div", { className: `thinking-section ${currentBlock.type}-section` }, /* @__PURE__ */ React3.createElement("div", { className: "thinking-header" }, icon, " ", title2, thinkingBlocks.length > 1 && /* @__PURE__ */ React3.createElement("div", { className: "thinking-navigation" }, /* @__PURE__ */ React3.createElement(
|
|
315
|
+
"button",
|
|
316
|
+
{
|
|
317
|
+
onClick: () => setCurrentThinkingIndex(Math.max(0, currentThinkingIndex - 1)),
|
|
318
|
+
disabled: currentThinkingIndex === 0,
|
|
319
|
+
className: "thinking-nav-btn"
|
|
320
|
+
},
|
|
321
|
+
"\u2190"
|
|
322
|
+
), /* @__PURE__ */ React3.createElement("span", { className: "thinking-counter" }, currentThinkingIndex + 1, " / ", thinkingBlocks.length), /* @__PURE__ */ React3.createElement(
|
|
323
|
+
"button",
|
|
324
|
+
{
|
|
325
|
+
onClick: () => setCurrentThinkingIndex(Math.min(thinkingBlocks.length - 1, currentThinkingIndex + 1)),
|
|
326
|
+
disabled: currentThinkingIndex === thinkingBlocks.length - 1,
|
|
327
|
+
className: "thinking-nav-btn"
|
|
328
|
+
},
|
|
329
|
+
"\u2192"
|
|
330
|
+
))), /* @__PURE__ */ React3.createElement("div", { className: "thinking-content" }, currentBlock.content)));
|
|
326
331
|
};
|
|
327
332
|
const getBrowserInfo = () => {
|
|
328
333
|
try {
|
|
@@ -915,9 +920,9 @@ var ChatPanel = ({
|
|
|
915
920
|
preview: response.substring(0, 200) + "..."
|
|
916
921
|
});
|
|
917
922
|
setIsLoading(false);
|
|
918
|
-
const { cleanedText,
|
|
919
|
-
|
|
920
|
-
|
|
923
|
+
const { cleanedText, thinkingBlocks: newThinkingBlocks } = processThinkingTags(response);
|
|
924
|
+
setThinkingBlocks(newThinkingBlocks);
|
|
925
|
+
setCurrentThinkingIndex(Math.max(0, newThinkingBlocks.length - 1));
|
|
921
926
|
let newResponse = cleanedText;
|
|
922
927
|
const toolRequests = [];
|
|
923
928
|
if (allActions && allActions.length > 0) {
|
|
@@ -1027,8 +1032,8 @@ var ChatPanel = ({
|
|
|
1027
1032
|
if (initialPrompt && initialPrompt !== "") {
|
|
1028
1033
|
if (initialPrompt !== lastPrompt) {
|
|
1029
1034
|
setIsLoading(true);
|
|
1030
|
-
|
|
1031
|
-
|
|
1035
|
+
setThinkingBlocks([]);
|
|
1036
|
+
setCurrentThinkingIndex(0);
|
|
1032
1037
|
ensureConversation().then((convId) => {
|
|
1033
1038
|
if (lastController) stop(lastController);
|
|
1034
1039
|
const controller = new AbortController();
|
|
@@ -1207,8 +1212,8 @@ var ChatPanel = ({
|
|
|
1207
1212
|
}, [currentCustomer, project_id, agent, publicAPIUrl, emailInput]);
|
|
1208
1213
|
const continueChat = (suggestion) => {
|
|
1209
1214
|
console.log("continueChat", suggestion);
|
|
1210
|
-
|
|
1211
|
-
|
|
1215
|
+
setThinkingBlocks([]);
|
|
1216
|
+
setCurrentThinkingIndex(0);
|
|
1212
1217
|
if (emailInput && isEmailAddress(emailInput) && !emailInputSet) {
|
|
1213
1218
|
const newId = (currentCustomer == null ? void 0 : currentCustomer.customer_id) && currentCustomer.customer_id !== "" && currentCustomer.customer_id !== (currentCustomer == null ? void 0 : currentCustomer.customer_user_email) ? currentCustomer.customer_id : emailInput;
|
|
1214
1219
|
setEmailInputSet(true);
|
|
@@ -1621,8 +1626,17 @@ var ChatPanel = ({
|
|
|
1621
1626
|
var _a2, _b;
|
|
1622
1627
|
const isLastEntry = index === Object.keys(history).length - 1;
|
|
1623
1628
|
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);
|
|
1624
|
-
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" },
|
|
1625
|
-
const { cleanedText
|
|
1629
|
+
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" }, (() => {
|
|
1630
|
+
const { cleanedText } = processThinkingTags(response || "");
|
|
1631
|
+
if (thinkingBlocks.length > 0) {
|
|
1632
|
+
return renderThinkingBlocks();
|
|
1633
|
+
}
|
|
1634
|
+
if (!cleanedText || cleanedText.length === 0) {
|
|
1635
|
+
return /* @__PURE__ */ React3.createElement("div", { className: "thinking-block-container" }, /* @__PURE__ */ React3.createElement("div", { className: "thinking-section" }, /* @__PURE__ */ React3.createElement("div", { className: "thinking-header" }, "\u{1F914} Thinking"), /* @__PURE__ */ React3.createElement("div", { className: "thinking-content" }, /* @__PURE__ */ React3.createElement("div", { className: "loading-text" }, "Thinking...\xA0", /* @__PURE__ */ React3.createElement("div", { className: "dot" }), /* @__PURE__ */ React3.createElement("div", { className: "dot" }), /* @__PURE__ */ React3.createElement("div", { className: "dot" })))));
|
|
1636
|
+
}
|
|
1637
|
+
return null;
|
|
1638
|
+
})(), (() => {
|
|
1639
|
+
const { cleanedText } = processThinkingTags(response || "");
|
|
1626
1640
|
return cleanedText && cleanedText.length > 0 ? /* @__PURE__ */ React3.createElement(
|
|
1627
1641
|
ReactMarkdown,
|
|
1628
1642
|
{
|
|
@@ -1635,8 +1649,8 @@ var ChatPanel = ({
|
|
|
1635
1649
|
}
|
|
1636
1650
|
},
|
|
1637
1651
|
cleanedText
|
|
1638
|
-
) :
|
|
1639
|
-
})()) : /* @__PURE__ */ React3.createElement("div", null, isLastEntry &&
|
|
1652
|
+
) : null;
|
|
1653
|
+
})()) : /* @__PURE__ */ React3.createElement("div", null, isLastEntry && thinkingBlocks.length > 0 && renderThinkingBlocks(), /* @__PURE__ */ React3.createElement(
|
|
1640
1654
|
ReactMarkdown,
|
|
1641
1655
|
{
|
|
1642
1656
|
className: markdownClass,
|
package/package.json
CHANGED
package/src/ChatPanel.css
CHANGED
|
@@ -954,4 +954,82 @@
|
|
|
954
954
|
to {
|
|
955
955
|
opacity: 1;
|
|
956
956
|
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
/* Thinking Block Styles */
|
|
960
|
+
.thinking-block-container {
|
|
961
|
+
margin-bottom: var(--spacing-medium);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
.thinking-section {
|
|
965
|
+
border: 1px solid var(--input-border-color);
|
|
966
|
+
border-radius: var(--border-radius);
|
|
967
|
+
padding: var(--spacing-medium);
|
|
968
|
+
background-color: var(--input-background-color);
|
|
969
|
+
margin-bottom: var(--spacing-small);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
.thinking-header {
|
|
973
|
+
display: flex;
|
|
974
|
+
justify-content: space-between;
|
|
975
|
+
align-items: center;
|
|
976
|
+
font-weight: bold;
|
|
977
|
+
color: var(--title-text-color);
|
|
978
|
+
margin-bottom: var(--spacing-small);
|
|
979
|
+
font-size: 0.9rem;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
.thinking-navigation {
|
|
983
|
+
display: flex;
|
|
984
|
+
align-items: center;
|
|
985
|
+
gap: var(--spacing-small);
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
.thinking-nav-btn {
|
|
989
|
+
background: var(--button-background-color);
|
|
990
|
+
border: 1px solid var(--button-border-color);
|
|
991
|
+
border-radius: 4px;
|
|
992
|
+
padding: 2px 6px;
|
|
993
|
+
cursor: pointer;
|
|
994
|
+
font-size: 12px;
|
|
995
|
+
line-height: 1;
|
|
996
|
+
min-width: 20px;
|
|
997
|
+
height: 20px;
|
|
998
|
+
display: flex;
|
|
999
|
+
align-items: center;
|
|
1000
|
+
justify-content: center;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
.thinking-nav-btn:hover:not(:disabled) {
|
|
1004
|
+
background: var(--button-background-color-hover);
|
|
1005
|
+
color: var(--button-text-color-hover);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
.thinking-nav-btn:disabled {
|
|
1009
|
+
background: var(--button-disabled-background-color);
|
|
1010
|
+
color: var(--button-disabled-color);
|
|
1011
|
+
cursor: not-allowed;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
.thinking-counter {
|
|
1015
|
+
font-size: 11px;
|
|
1016
|
+
color: var(--title-text-color);
|
|
1017
|
+
font-weight: normal;
|
|
1018
|
+
min-width: 30px;
|
|
1019
|
+
text-align: center;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
.thinking-content {
|
|
1023
|
+
color: var(--input-text-color);
|
|
1024
|
+
font-size: 0.85rem;
|
|
1025
|
+
line-height: 1.4;
|
|
1026
|
+
white-space: pre-wrap;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
.reasoning-section {
|
|
1030
|
+
border-left: 3px solid #4a90e2;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
.searching-section {
|
|
1034
|
+
border-left: 3px solid #7b68ee;
|
|
957
1035
|
}
|
package/src/ChatPanel.tsx
CHANGED
|
@@ -208,9 +208,9 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
208
208
|
);
|
|
209
209
|
const [alwaysApprovedTools, setAlwaysApprovedTools] = useState<string[]>([]);
|
|
210
210
|
|
|
211
|
-
// State for tracking
|
|
212
|
-
const [
|
|
213
|
-
const [
|
|
211
|
+
// State for tracking thinking content and navigation
|
|
212
|
+
const [thinkingBlocks, setThinkingBlocks] = useState<Array<{ type: 'reasoning' | 'searching'; content: string; index: number }>>([]);
|
|
213
|
+
const [currentThinkingIndex, setCurrentThinkingIndex] = useState(0);
|
|
214
214
|
|
|
215
215
|
// load “always” approvals
|
|
216
216
|
useEffect(() => {
|
|
@@ -241,23 +241,19 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
241
241
|
searching: /<searching>([\s\S]*?)<\/searching>/gi
|
|
242
242
|
} as const;
|
|
243
243
|
|
|
244
|
-
// Single function to extract thinking blocks
|
|
244
|
+
// Single function to extract thinking blocks in order
|
|
245
245
|
const processThinkingTags = (text: string): {
|
|
246
246
|
cleanedText: string;
|
|
247
|
-
|
|
248
|
-
searchingBlocks: string[];
|
|
247
|
+
thinkingBlocks: Array<{ type: 'reasoning' | 'searching'; content: string; index: number }>;
|
|
249
248
|
lastThinkingContent: string;
|
|
250
249
|
} => {
|
|
251
250
|
if (!text) return {
|
|
252
251
|
cleanedText: "",
|
|
253
|
-
|
|
254
|
-
searchingBlocks: [],
|
|
252
|
+
thinkingBlocks: [],
|
|
255
253
|
lastThinkingContent: "Thinking"
|
|
256
254
|
};
|
|
257
255
|
|
|
258
|
-
const
|
|
259
|
-
const newSearchingBlocks: string[] = [];
|
|
260
|
-
const allMatches: Array<{ content: string; index: number; type: string }> = [];
|
|
256
|
+
const allMatches: Array<{ content: string; index: number; type: 'reasoning' | 'searching' }> = [];
|
|
261
257
|
let cleanedText = text;
|
|
262
258
|
|
|
263
259
|
// Process reasoning blocks
|
|
@@ -266,7 +262,6 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
266
262
|
while ((reasoningMatch = reasoningRegex.exec(text)) !== null) {
|
|
267
263
|
const content = reasoningMatch[1]?.trim();
|
|
268
264
|
if (content) {
|
|
269
|
-
newReasoningBlocks.push(content);
|
|
270
265
|
allMatches.push({
|
|
271
266
|
content,
|
|
272
267
|
index: reasoningMatch.index,
|
|
@@ -281,7 +276,6 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
281
276
|
while ((searchingMatch = searchingRegex.exec(text)) !== null) {
|
|
282
277
|
const content = searchingMatch[1]?.trim();
|
|
283
278
|
if (content) {
|
|
284
|
-
newSearchingBlocks.push(content);
|
|
285
279
|
allMatches.push({
|
|
286
280
|
content,
|
|
287
281
|
index: searchingMatch.index,
|
|
@@ -290,15 +284,18 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
290
284
|
}
|
|
291
285
|
}
|
|
292
286
|
|
|
287
|
+
// Sort by index to preserve original order
|
|
288
|
+
const thinkingBlocks = allMatches.sort((a, b) => a.index - b.index);
|
|
289
|
+
|
|
293
290
|
// Clean the text
|
|
294
291
|
cleanedText = cleanedText.replace(THINKING_PATTERNS.reasoning, '');
|
|
295
292
|
cleanedText = cleanedText.replace(THINKING_PATTERNS.searching, '');
|
|
296
293
|
|
|
297
294
|
// Get last thinking content
|
|
298
295
|
let lastThinkingContent = "Thinking";
|
|
299
|
-
if (
|
|
300
|
-
const
|
|
301
|
-
let content =
|
|
296
|
+
if (thinkingBlocks.length > 0) {
|
|
297
|
+
const lastBlock = thinkingBlocks[thinkingBlocks.length - 1];
|
|
298
|
+
let content = lastBlock?.content || "Thinking";
|
|
302
299
|
|
|
303
300
|
// Clean up the content for display
|
|
304
301
|
content = content.replace(/\*\*(.*?)\*\*/g, '$1'); // Remove bold
|
|
@@ -317,38 +314,52 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
317
314
|
|
|
318
315
|
return {
|
|
319
316
|
cleanedText: cleanedText.trim(),
|
|
320
|
-
|
|
321
|
-
searchingBlocks: newSearchingBlocks,
|
|
317
|
+
thinkingBlocks,
|
|
322
318
|
lastThinkingContent
|
|
323
319
|
};
|
|
324
320
|
};
|
|
325
321
|
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
const blocks: JSX.Element[] = [];
|
|
322
|
+
// Render thinking blocks with navigation
|
|
323
|
+
const renderThinkingBlocks = (): JSX.Element | null => {
|
|
324
|
+
if (thinkingBlocks.length === 0) return null;
|
|
330
325
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
blocks.push(
|
|
334
|
-
<div key={`reasoning-${index}`} className="reasoning-section">
|
|
335
|
-
<div className="reasoning-header">🤔 Reasoning</div>
|
|
336
|
-
<div className="reasoning-content">{content}</div>
|
|
337
|
-
</div>
|
|
338
|
-
);
|
|
339
|
-
});
|
|
326
|
+
const currentBlock = thinkingBlocks[currentThinkingIndex];
|
|
327
|
+
if (!currentBlock) return null;
|
|
340
328
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
blocks.push(
|
|
344
|
-
<div key={`searching-${index}`} className="searching-section">
|
|
345
|
-
<div className="searching-header">🔍 Searching</div>
|
|
346
|
-
<div className="searching-content">{content}</div>
|
|
347
|
-
</div>
|
|
348
|
-
);
|
|
349
|
-
});
|
|
329
|
+
const icon = currentBlock.type === 'reasoning' ? '🤔' : '🔍';
|
|
330
|
+
const title = currentBlock.type === 'reasoning' ? 'Reasoning' : 'Searching';
|
|
350
331
|
|
|
351
|
-
return
|
|
332
|
+
return (
|
|
333
|
+
<div className="thinking-block-container">
|
|
334
|
+
<div className={`thinking-section ${currentBlock.type}-section`}>
|
|
335
|
+
<div className="thinking-header">
|
|
336
|
+
{icon} {title}
|
|
337
|
+
{thinkingBlocks.length > 1 && (
|
|
338
|
+
<div className="thinking-navigation">
|
|
339
|
+
<button
|
|
340
|
+
onClick={() => setCurrentThinkingIndex(Math.max(0, currentThinkingIndex - 1))}
|
|
341
|
+
disabled={currentThinkingIndex === 0}
|
|
342
|
+
className="thinking-nav-btn"
|
|
343
|
+
>
|
|
344
|
+
←
|
|
345
|
+
</button>
|
|
346
|
+
<span className="thinking-counter">
|
|
347
|
+
{currentThinkingIndex + 1} / {thinkingBlocks.length}
|
|
348
|
+
</span>
|
|
349
|
+
<button
|
|
350
|
+
onClick={() => setCurrentThinkingIndex(Math.min(thinkingBlocks.length - 1, currentThinkingIndex + 1))}
|
|
351
|
+
disabled={currentThinkingIndex === thinkingBlocks.length - 1}
|
|
352
|
+
className="thinking-nav-btn"
|
|
353
|
+
>
|
|
354
|
+
→
|
|
355
|
+
</button>
|
|
356
|
+
</div>
|
|
357
|
+
)}
|
|
358
|
+
</div>
|
|
359
|
+
<div className="thinking-content">{currentBlock.content}</div>
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
);
|
|
352
363
|
};
|
|
353
364
|
|
|
354
365
|
const getBrowserInfo = () => {
|
|
@@ -1074,11 +1085,12 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
1074
1085
|
setIsLoading(false);
|
|
1075
1086
|
|
|
1076
1087
|
// Process thinking tags and get cleaned response
|
|
1077
|
-
const { cleanedText,
|
|
1088
|
+
const { cleanedText, thinkingBlocks: newThinkingBlocks } = processThinkingTags(response);
|
|
1078
1089
|
|
|
1079
1090
|
// Replace the blocks entirely (don't append) to avoid duplicates during streaming
|
|
1080
|
-
|
|
1081
|
-
|
|
1091
|
+
setThinkingBlocks(newThinkingBlocks);
|
|
1092
|
+
// Always show the latest (last) thinking block
|
|
1093
|
+
setCurrentThinkingIndex(Math.max(0, newThinkingBlocks.length - 1));
|
|
1082
1094
|
|
|
1083
1095
|
let newResponse = cleanedText;
|
|
1084
1096
|
|
|
@@ -1217,8 +1229,8 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
1217
1229
|
setIsLoading(true);
|
|
1218
1230
|
|
|
1219
1231
|
// Clear thinking blocks for new response
|
|
1220
|
-
|
|
1221
|
-
|
|
1232
|
+
setThinkingBlocks([]);
|
|
1233
|
+
setCurrentThinkingIndex(0);
|
|
1222
1234
|
|
|
1223
1235
|
ensureConversation().then((convId) => {
|
|
1224
1236
|
if (lastController) stop(lastController);
|
|
@@ -1441,8 +1453,8 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
1441
1453
|
console.log("continueChat", suggestion);
|
|
1442
1454
|
|
|
1443
1455
|
// Clear thinking blocks for new response
|
|
1444
|
-
|
|
1445
|
-
|
|
1456
|
+
setThinkingBlocks([]);
|
|
1457
|
+
setCurrentThinkingIndex(0);
|
|
1446
1458
|
|
|
1447
1459
|
// Auto-set email if valid before proceeding
|
|
1448
1460
|
if (emailInput && isEmailAddress(emailInput) && !emailInputSet) {
|
|
@@ -1986,12 +1998,40 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
1986
1998
|
{/* Show streaming response with thinking blocks displayed separately */}
|
|
1987
1999
|
{index === Object.keys(history).length - 1 && isLoading ? (
|
|
1988
2000
|
<div className="streaming-response">
|
|
1989
|
-
{/* Display
|
|
1990
|
-
{
|
|
2001
|
+
{/* Display current thinking block or thinking message */}
|
|
2002
|
+
{(() => {
|
|
2003
|
+
const { cleanedText } = processThinkingTags(response || "");
|
|
2004
|
+
|
|
2005
|
+
// If we have thinking blocks, show the current one
|
|
2006
|
+
if (thinkingBlocks.length > 0) {
|
|
2007
|
+
return renderThinkingBlocks();
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
// If no thinking blocks yet but no main content, show generic thinking
|
|
2011
|
+
if (!cleanedText || cleanedText.length === 0) {
|
|
2012
|
+
return (
|
|
2013
|
+
<div className="thinking-block-container">
|
|
2014
|
+
<div className="thinking-section">
|
|
2015
|
+
<div className="thinking-header">🤔 Thinking</div>
|
|
2016
|
+
<div className="thinking-content">
|
|
2017
|
+
<div className="loading-text">
|
|
2018
|
+
Thinking...
|
|
2019
|
+
<div className="dot"></div>
|
|
2020
|
+
<div className="dot"></div>
|
|
2021
|
+
<div className="dot"></div>
|
|
2022
|
+
</div>
|
|
2023
|
+
</div>
|
|
2024
|
+
</div>
|
|
2025
|
+
</div>
|
|
2026
|
+
);
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
return null;
|
|
2030
|
+
})()}
|
|
1991
2031
|
|
|
1992
2032
|
{/* Display the main content (cleaned of thinking tags) */}
|
|
1993
2033
|
{(() => {
|
|
1994
|
-
const { cleanedText
|
|
2034
|
+
const { cleanedText } = processThinkingTags(response || "");
|
|
1995
2035
|
return cleanedText && cleanedText.length > 0 ? (
|
|
1996
2036
|
<ReactMarkdown
|
|
1997
2037
|
className={markdownClass}
|
|
@@ -2001,20 +2041,13 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
2001
2041
|
>
|
|
2002
2042
|
{cleanedText}
|
|
2003
2043
|
</ReactMarkdown>
|
|
2004
|
-
) :
|
|
2005
|
-
<div className="loading-text">
|
|
2006
|
-
{lastThinkingContent}
|
|
2007
|
-
<div className="dot"></div>
|
|
2008
|
-
<div className="dot"></div>
|
|
2009
|
-
<div className="dot"></div>
|
|
2010
|
-
</div>
|
|
2011
|
-
);
|
|
2044
|
+
) : null;
|
|
2012
2045
|
})()}
|
|
2013
2046
|
</div>
|
|
2014
2047
|
) : (
|
|
2015
2048
|
<div>
|
|
2016
2049
|
{/* For completed responses, show stored thinking blocks if this is the last entry */}
|
|
2017
|
-
{isLastEntry &&
|
|
2050
|
+
{isLastEntry && thinkingBlocks.length > 0 && renderThinkingBlocks()}
|
|
2018
2051
|
|
|
2019
2052
|
{/* Show the main content */}
|
|
2020
2053
|
<ReactMarkdown
|