@paymanai/payman-ask-sdk 4.0.8 → 4.0.10
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.js +102 -25
- package/dist/index.mjs +102 -25
- package/package.json +2 -2
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1521,6 +1521,7 @@ var RAG_IMAGE_PATH_REGEX2 = /^(?:https?:\/\/[^/\s]+)?\/api\/rag\/chunks\/[^"'\s]
|
|
|
1521
1521
|
function isUnresolvedRagImageSource2(src) {
|
|
1522
1522
|
return RAG_IMAGE_PATH_REGEX2.test(src);
|
|
1523
1523
|
}
|
|
1524
|
+
var loadedImageCache = /* @__PURE__ */ new Set();
|
|
1524
1525
|
var frameStyle = {
|
|
1525
1526
|
width: "min(100%, 32rem)",
|
|
1526
1527
|
maxWidth: "100%"
|
|
@@ -1539,24 +1540,36 @@ function MarkdownImageV2({
|
|
|
1539
1540
|
isResolving = false,
|
|
1540
1541
|
onImageClick
|
|
1541
1542
|
}) {
|
|
1542
|
-
const
|
|
1543
|
-
const [
|
|
1543
|
+
const cachedAlready = src ? loadedImageCache.has(src) : false;
|
|
1544
|
+
const [isVisible, setIsVisible] = react.useState(cachedAlready);
|
|
1545
|
+
const [isLoaded, setIsLoaded] = react.useState(cachedAlready);
|
|
1544
1546
|
const [hasError, setHasError] = react.useState(false);
|
|
1545
1547
|
const [retryCount, setRetryCount] = react.useState(0);
|
|
1546
1548
|
const sentinelRef = react.useRef(null);
|
|
1549
|
+
const prevLoadedRef = react.useRef(cachedAlready);
|
|
1547
1550
|
const isUnresolvedRag = react.useMemo(
|
|
1548
1551
|
() => src ? isUnresolvedRagImageSource2(src) : false,
|
|
1549
1552
|
[src]
|
|
1550
1553
|
);
|
|
1551
1554
|
const isResolvingRag = Boolean(isResolving && isUnresolvedRag);
|
|
1552
1555
|
react.useEffect(() => {
|
|
1553
|
-
|
|
1556
|
+
if (src && loadedImageCache.has(src)) {
|
|
1557
|
+
setIsLoaded(true);
|
|
1558
|
+
setIsVisible(true);
|
|
1559
|
+
prevLoadedRef.current = true;
|
|
1560
|
+
}
|
|
1554
1561
|
setHasError(false);
|
|
1555
1562
|
setRetryCount(0);
|
|
1556
1563
|
}, [src]);
|
|
1557
1564
|
react.useEffect(() => {
|
|
1558
1565
|
const el = sentinelRef.current;
|
|
1559
1566
|
if (!el || !src) return;
|
|
1567
|
+
const rect = el.getBoundingClientRect();
|
|
1568
|
+
const alreadyNear = rect.bottom >= -200 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) + 200;
|
|
1569
|
+
if (alreadyNear) {
|
|
1570
|
+
setIsVisible(true);
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1560
1573
|
const observer = new IntersectionObserver(
|
|
1561
1574
|
([entry]) => {
|
|
1562
1575
|
if (entry.isIntersecting) {
|
|
@@ -1570,7 +1583,7 @@ function MarkdownImageV2({
|
|
|
1570
1583
|
return () => observer.disconnect();
|
|
1571
1584
|
}, [src]);
|
|
1572
1585
|
if (!src) return null;
|
|
1573
|
-
if (isResolvingRag) {
|
|
1586
|
+
if (isResolvingRag && !isLoaded) {
|
|
1574
1587
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1575
1588
|
"span",
|
|
1576
1589
|
{
|
|
@@ -1645,10 +1658,13 @@ function MarkdownImageV2({
|
|
|
1645
1658
|
opacity: isLoaded ? 1 : 0
|
|
1646
1659
|
},
|
|
1647
1660
|
onLoad: () => {
|
|
1661
|
+
if (src) loadedImageCache.add(src);
|
|
1662
|
+
prevLoadedRef.current = true;
|
|
1648
1663
|
setHasError(false);
|
|
1649
1664
|
setIsLoaded(true);
|
|
1650
1665
|
},
|
|
1651
1666
|
onError: () => {
|
|
1667
|
+
prevLoadedRef.current = false;
|
|
1652
1668
|
setIsLoaded(false);
|
|
1653
1669
|
setHasError(true);
|
|
1654
1670
|
}
|
|
@@ -1666,7 +1682,7 @@ function MarkdownImageV2({
|
|
|
1666
1682
|
}
|
|
1667
1683
|
) });
|
|
1668
1684
|
}
|
|
1669
|
-
function buildComponents(onImageClick,
|
|
1685
|
+
function buildComponents(onImageClick, isResolvingRef) {
|
|
1670
1686
|
return {
|
|
1671
1687
|
p: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { children }),
|
|
1672
1688
|
code: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("code", { children }),
|
|
@@ -1687,7 +1703,7 @@ function buildComponents(onImageClick, isResolvingImages) {
|
|
|
1687
1703
|
{
|
|
1688
1704
|
src: typeof src === "string" ? src : void 0,
|
|
1689
1705
|
alt: typeof alt === "string" ? alt : void 0,
|
|
1690
|
-
isResolving:
|
|
1706
|
+
isResolving: isResolvingRef?.current,
|
|
1691
1707
|
onImageClick
|
|
1692
1708
|
}
|
|
1693
1709
|
),
|
|
@@ -1704,9 +1720,11 @@ function MarkdownRendererV2({
|
|
|
1704
1720
|
isResolvingImages,
|
|
1705
1721
|
onImageClick
|
|
1706
1722
|
}) {
|
|
1723
|
+
const isResolvingRef = react.useRef(isResolvingImages);
|
|
1724
|
+
isResolvingRef.current = isResolvingImages;
|
|
1707
1725
|
const components = react.useMemo(
|
|
1708
|
-
() => buildComponents(onImageClick,
|
|
1709
|
-
[onImageClick
|
|
1726
|
+
() => buildComponents(onImageClick, isResolvingRef),
|
|
1727
|
+
[onImageClick]
|
|
1710
1728
|
);
|
|
1711
1729
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1712
1730
|
"div",
|
|
@@ -1978,6 +1996,7 @@ var RESPONSE_SPEED = {
|
|
|
1978
1996
|
newline: [4, 6],
|
|
1979
1997
|
idle: 15
|
|
1980
1998
|
};
|
|
1999
|
+
var MARKDOWN_IMAGE_REGEX = /^!\[[^\]]*\]\([^)]*\)/;
|
|
1981
2000
|
function charDelay(char, speed, multiplier) {
|
|
1982
2001
|
const raw = (() => {
|
|
1983
2002
|
if (char === "*") return speed.fast;
|
|
@@ -2017,6 +2036,10 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
|
|
|
2017
2036
|
displayedRef.current = initialDisplayedRef.current;
|
|
2018
2037
|
setDisplayedText(initialDisplayedRef.current);
|
|
2019
2038
|
}
|
|
2039
|
+
if (displayedRef.current && !targetRef.current.startsWith(displayedRef.current)) {
|
|
2040
|
+
displayedRef.current = targetRef.current;
|
|
2041
|
+
setDisplayedText(targetRef.current);
|
|
2042
|
+
}
|
|
2020
2043
|
if (runningRef.current) return;
|
|
2021
2044
|
runningRef.current = true;
|
|
2022
2045
|
const tick = () => {
|
|
@@ -2024,8 +2047,22 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
|
|
|
2024
2047
|
runningRef.current = false;
|
|
2025
2048
|
return;
|
|
2026
2049
|
}
|
|
2050
|
+
if (displayedRef.current && !targetRef.current.startsWith(displayedRef.current)) {
|
|
2051
|
+
displayedRef.current = targetRef.current;
|
|
2052
|
+
setDisplayedText(targetRef.current);
|
|
2053
|
+
timerRef.current = setTimeout(tick, speed.idle / multiplier);
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2027
2056
|
if (displayedRef.current.length < targetRef.current.length) {
|
|
2028
|
-
const
|
|
2057
|
+
const remaining = targetRef.current.slice(displayedRef.current.length);
|
|
2058
|
+
const imgMatch = MARKDOWN_IMAGE_REGEX.exec(remaining);
|
|
2059
|
+
if (imgMatch) {
|
|
2060
|
+
displayedRef.current += imgMatch[0];
|
|
2061
|
+
setDisplayedText(displayedRef.current);
|
|
2062
|
+
timerRef.current = setTimeout(tick, 0);
|
|
2063
|
+
return;
|
|
2064
|
+
}
|
|
2065
|
+
const nextChar = remaining[0];
|
|
2029
2066
|
displayedRef.current += nextChar;
|
|
2030
2067
|
setDisplayedText(displayedRef.current);
|
|
2031
2068
|
const delay = charDelay(nextChar, speed, multiplier);
|
|
@@ -2049,6 +2086,23 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
|
|
|
2049
2086
|
isTyping
|
|
2050
2087
|
};
|
|
2051
2088
|
}
|
|
2089
|
+
function stripIncompleteImageToken(text) {
|
|
2090
|
+
const lastBang = text.lastIndexOf("![");
|
|
2091
|
+
if (lastBang === -1) return text;
|
|
2092
|
+
const after = text.slice(lastBang);
|
|
2093
|
+
if (/^!\[[^\]]*\]\([^)]*\)/.test(after)) return text;
|
|
2094
|
+
return text.slice(0, lastBang);
|
|
2095
|
+
}
|
|
2096
|
+
function getFeedbackState(message) {
|
|
2097
|
+
const feedback = message.feedback;
|
|
2098
|
+
if (feedback === "up" || feedback === "down") return feedback;
|
|
2099
|
+
if (feedback && typeof feedback === "object") {
|
|
2100
|
+
const value = feedback.feedback;
|
|
2101
|
+
if (!value) return null;
|
|
2102
|
+
return value === "POSITIVE" ? "up" : "down";
|
|
2103
|
+
}
|
|
2104
|
+
return null;
|
|
2105
|
+
}
|
|
2052
2106
|
function AssistantMessageV2({
|
|
2053
2107
|
message,
|
|
2054
2108
|
onImageClick,
|
|
@@ -2058,21 +2112,11 @@ function AssistantMessageV2({
|
|
|
2058
2112
|
typingSpeed = 4
|
|
2059
2113
|
}) {
|
|
2060
2114
|
const [copied, setCopied] = react.useState(false);
|
|
2061
|
-
const [activeFeedback, setActiveFeedback] = react.useState(
|
|
2115
|
+
const [activeFeedback, setActiveFeedback] = react.useState(
|
|
2116
|
+
() => getFeedbackState(message)
|
|
2117
|
+
);
|
|
2062
2118
|
const [reasonModalOpen, setReasonModalOpen] = react.useState(false);
|
|
2063
2119
|
const canSubmitFeedback = !!onSubmitFeedback && !!message.executionId;
|
|
2064
|
-
const handlePositiveFeedback = () => {
|
|
2065
|
-
if (!canSubmitFeedback || activeFeedback === "up") return;
|
|
2066
|
-
const previous = activeFeedback;
|
|
2067
|
-
setActiveFeedback("up");
|
|
2068
|
-
Promise.resolve(
|
|
2069
|
-
onSubmitFeedback?.({
|
|
2070
|
-
messageId: message.id,
|
|
2071
|
-
executionId: message.executionId,
|
|
2072
|
-
feedback: "POSITIVE"
|
|
2073
|
-
})
|
|
2074
|
-
).catch(() => setActiveFeedback(previous));
|
|
2075
|
-
};
|
|
2076
2120
|
const [toast, setToast] = react.useState(null);
|
|
2077
2121
|
const copyResetTimerRef = react.useRef(null);
|
|
2078
2122
|
const toastTimerRef = react.useRef(null);
|
|
@@ -2080,8 +2124,12 @@ function AssistantMessageV2({
|
|
|
2080
2124
|
const showTraceAction = (actions?.trace ?? true) && !!onExecutionTraceClick;
|
|
2081
2125
|
const showThumbsUp = actions?.thumbsUp ?? true;
|
|
2082
2126
|
const showThumbsDown = actions?.thumbsDown ?? true;
|
|
2127
|
+
const hydratedFeedback = message.feedback;
|
|
2083
2128
|
const hasEverStreamed = react.useRef(!!message.isStreaming);
|
|
2084
2129
|
if (message.isStreaming) hasEverStreamed.current = true;
|
|
2130
|
+
react.useEffect(() => {
|
|
2131
|
+
setActiveFeedback(getFeedbackState(message));
|
|
2132
|
+
}, [hydratedFeedback, message.id]);
|
|
2085
2133
|
react.useEffect(() => {
|
|
2086
2134
|
return () => {
|
|
2087
2135
|
if (copyResetTimerRef.current) clearTimeout(copyResetTimerRef.current);
|
|
@@ -2091,7 +2139,8 @@ function AssistantMessageV2({
|
|
|
2091
2139
|
const rawResponseContent = (() => {
|
|
2092
2140
|
const raw = message.isStreaming ? message.streamingContent || message.content : message.content;
|
|
2093
2141
|
if (!raw) return "";
|
|
2094
|
-
|
|
2142
|
+
const normalized = raw.replace(/\\n/g, "\n");
|
|
2143
|
+
return message.isStreaming ? stripIncompleteImageToken(normalized) : normalized;
|
|
2095
2144
|
})();
|
|
2096
2145
|
const isThinkingStreaming = !!message.isStreaming && !rawResponseContent && !message.isError;
|
|
2097
2146
|
const responseTypingEnabled = hasEverStreamed.current && Boolean(rawResponseContent) && !message.isError;
|
|
@@ -2147,6 +2196,21 @@ function AssistantMessageV2({
|
|
|
2147
2196
|
setToast({ label, tone });
|
|
2148
2197
|
toastTimerRef.current = setTimeout(() => setToast(null), 1800);
|
|
2149
2198
|
};
|
|
2199
|
+
const handlePositiveFeedback = () => {
|
|
2200
|
+
if (!canSubmitFeedback || activeFeedback === "up") return;
|
|
2201
|
+
const previous = activeFeedback;
|
|
2202
|
+
setActiveFeedback("up");
|
|
2203
|
+
Promise.resolve(
|
|
2204
|
+
onSubmitFeedback?.({
|
|
2205
|
+
messageId: message.id,
|
|
2206
|
+
executionId: message.executionId,
|
|
2207
|
+
feedback: "POSITIVE"
|
|
2208
|
+
})
|
|
2209
|
+
).then(() => showToast("Thank you for your feedback", "success")).catch(() => {
|
|
2210
|
+
setActiveFeedback(previous);
|
|
2211
|
+
showToast("Could not send feedback", "error");
|
|
2212
|
+
});
|
|
2213
|
+
};
|
|
2150
2214
|
const handleCopy = async () => {
|
|
2151
2215
|
try {
|
|
2152
2216
|
if (!navigator.clipboard?.writeText) {
|
|
@@ -2293,7 +2357,13 @@ function AssistantMessageV2({
|
|
|
2293
2357
|
activeFeedback === "up" && "payman-v2-assistant-msg-action-btn-active"
|
|
2294
2358
|
),
|
|
2295
2359
|
"aria-label": "Good response",
|
|
2296
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2360
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2361
|
+
lucideReact.ThumbsUp,
|
|
2362
|
+
{
|
|
2363
|
+
fill: activeFeedback === "up" ? "currentColor" : "none",
|
|
2364
|
+
style: { width: 15, height: 15 }
|
|
2365
|
+
}
|
|
2366
|
+
)
|
|
2297
2367
|
}
|
|
2298
2368
|
) }),
|
|
2299
2369
|
showThumbsDown && canSubmitFeedback && /* @__PURE__ */ jsxRuntime.jsx(ActionTooltipV2, { label: "Bad response", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2305,7 +2375,13 @@ function AssistantMessageV2({
|
|
|
2305
2375
|
activeFeedback === "down" && "payman-v2-assistant-msg-action-btn-active"
|
|
2306
2376
|
),
|
|
2307
2377
|
"aria-label": "Bad response",
|
|
2308
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2378
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2379
|
+
lucideReact.ThumbsDown,
|
|
2380
|
+
{
|
|
2381
|
+
fill: activeFeedback === "down" ? "currentColor" : "none",
|
|
2382
|
+
style: { width: 15, height: 15 }
|
|
2383
|
+
}
|
|
2384
|
+
)
|
|
2309
2385
|
}
|
|
2310
2386
|
) }),
|
|
2311
2387
|
showTraceAction && /* @__PURE__ */ jsxRuntime.jsx(ActionTooltipV2, { label: "Trace", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2332,6 +2408,7 @@ function AssistantMessageV2({
|
|
|
2332
2408
|
details
|
|
2333
2409
|
});
|
|
2334
2410
|
setActiveFeedback("down");
|
|
2411
|
+
showToast("Thank you for your feedback", "success");
|
|
2335
2412
|
}
|
|
2336
2413
|
}
|
|
2337
2414
|
)
|
package/dist/index.mjs
CHANGED
|
@@ -1495,6 +1495,7 @@ var RAG_IMAGE_PATH_REGEX2 = /^(?:https?:\/\/[^/\s]+)?\/api\/rag\/chunks\/[^"'\s]
|
|
|
1495
1495
|
function isUnresolvedRagImageSource2(src) {
|
|
1496
1496
|
return RAG_IMAGE_PATH_REGEX2.test(src);
|
|
1497
1497
|
}
|
|
1498
|
+
var loadedImageCache = /* @__PURE__ */ new Set();
|
|
1498
1499
|
var frameStyle = {
|
|
1499
1500
|
width: "min(100%, 32rem)",
|
|
1500
1501
|
maxWidth: "100%"
|
|
@@ -1513,24 +1514,36 @@ function MarkdownImageV2({
|
|
|
1513
1514
|
isResolving = false,
|
|
1514
1515
|
onImageClick
|
|
1515
1516
|
}) {
|
|
1516
|
-
const
|
|
1517
|
-
const [
|
|
1517
|
+
const cachedAlready = src ? loadedImageCache.has(src) : false;
|
|
1518
|
+
const [isVisible, setIsVisible] = useState(cachedAlready);
|
|
1519
|
+
const [isLoaded, setIsLoaded] = useState(cachedAlready);
|
|
1518
1520
|
const [hasError, setHasError] = useState(false);
|
|
1519
1521
|
const [retryCount, setRetryCount] = useState(0);
|
|
1520
1522
|
const sentinelRef = useRef(null);
|
|
1523
|
+
const prevLoadedRef = useRef(cachedAlready);
|
|
1521
1524
|
const isUnresolvedRag = useMemo(
|
|
1522
1525
|
() => src ? isUnresolvedRagImageSource2(src) : false,
|
|
1523
1526
|
[src]
|
|
1524
1527
|
);
|
|
1525
1528
|
const isResolvingRag = Boolean(isResolving && isUnresolvedRag);
|
|
1526
1529
|
useEffect(() => {
|
|
1527
|
-
|
|
1530
|
+
if (src && loadedImageCache.has(src)) {
|
|
1531
|
+
setIsLoaded(true);
|
|
1532
|
+
setIsVisible(true);
|
|
1533
|
+
prevLoadedRef.current = true;
|
|
1534
|
+
}
|
|
1528
1535
|
setHasError(false);
|
|
1529
1536
|
setRetryCount(0);
|
|
1530
1537
|
}, [src]);
|
|
1531
1538
|
useEffect(() => {
|
|
1532
1539
|
const el = sentinelRef.current;
|
|
1533
1540
|
if (!el || !src) return;
|
|
1541
|
+
const rect = el.getBoundingClientRect();
|
|
1542
|
+
const alreadyNear = rect.bottom >= -200 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) + 200;
|
|
1543
|
+
if (alreadyNear) {
|
|
1544
|
+
setIsVisible(true);
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1534
1547
|
const observer = new IntersectionObserver(
|
|
1535
1548
|
([entry]) => {
|
|
1536
1549
|
if (entry.isIntersecting) {
|
|
@@ -1544,7 +1557,7 @@ function MarkdownImageV2({
|
|
|
1544
1557
|
return () => observer.disconnect();
|
|
1545
1558
|
}, [src]);
|
|
1546
1559
|
if (!src) return null;
|
|
1547
|
-
if (isResolvingRag) {
|
|
1560
|
+
if (isResolvingRag && !isLoaded) {
|
|
1548
1561
|
return /* @__PURE__ */ jsx(
|
|
1549
1562
|
"span",
|
|
1550
1563
|
{
|
|
@@ -1619,10 +1632,13 @@ function MarkdownImageV2({
|
|
|
1619
1632
|
opacity: isLoaded ? 1 : 0
|
|
1620
1633
|
},
|
|
1621
1634
|
onLoad: () => {
|
|
1635
|
+
if (src) loadedImageCache.add(src);
|
|
1636
|
+
prevLoadedRef.current = true;
|
|
1622
1637
|
setHasError(false);
|
|
1623
1638
|
setIsLoaded(true);
|
|
1624
1639
|
},
|
|
1625
1640
|
onError: () => {
|
|
1641
|
+
prevLoadedRef.current = false;
|
|
1626
1642
|
setIsLoaded(false);
|
|
1627
1643
|
setHasError(true);
|
|
1628
1644
|
}
|
|
@@ -1640,7 +1656,7 @@ function MarkdownImageV2({
|
|
|
1640
1656
|
}
|
|
1641
1657
|
) });
|
|
1642
1658
|
}
|
|
1643
|
-
function buildComponents(onImageClick,
|
|
1659
|
+
function buildComponents(onImageClick, isResolvingRef) {
|
|
1644
1660
|
return {
|
|
1645
1661
|
p: ({ children }) => /* @__PURE__ */ jsx("p", { children }),
|
|
1646
1662
|
code: ({ children }) => /* @__PURE__ */ jsx("code", { children }),
|
|
@@ -1661,7 +1677,7 @@ function buildComponents(onImageClick, isResolvingImages) {
|
|
|
1661
1677
|
{
|
|
1662
1678
|
src: typeof src === "string" ? src : void 0,
|
|
1663
1679
|
alt: typeof alt === "string" ? alt : void 0,
|
|
1664
|
-
isResolving:
|
|
1680
|
+
isResolving: isResolvingRef?.current,
|
|
1665
1681
|
onImageClick
|
|
1666
1682
|
}
|
|
1667
1683
|
),
|
|
@@ -1678,9 +1694,11 @@ function MarkdownRendererV2({
|
|
|
1678
1694
|
isResolvingImages,
|
|
1679
1695
|
onImageClick
|
|
1680
1696
|
}) {
|
|
1697
|
+
const isResolvingRef = useRef(isResolvingImages);
|
|
1698
|
+
isResolvingRef.current = isResolvingImages;
|
|
1681
1699
|
const components = useMemo(
|
|
1682
|
-
() => buildComponents(onImageClick,
|
|
1683
|
-
[onImageClick
|
|
1700
|
+
() => buildComponents(onImageClick, isResolvingRef),
|
|
1701
|
+
[onImageClick]
|
|
1684
1702
|
);
|
|
1685
1703
|
return /* @__PURE__ */ jsx(
|
|
1686
1704
|
"div",
|
|
@@ -1952,6 +1970,7 @@ var RESPONSE_SPEED = {
|
|
|
1952
1970
|
newline: [4, 6],
|
|
1953
1971
|
idle: 15
|
|
1954
1972
|
};
|
|
1973
|
+
var MARKDOWN_IMAGE_REGEX = /^!\[[^\]]*\]\([^)]*\)/;
|
|
1955
1974
|
function charDelay(char, speed, multiplier) {
|
|
1956
1975
|
const raw = (() => {
|
|
1957
1976
|
if (char === "*") return speed.fast;
|
|
@@ -1991,6 +2010,10 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
|
|
|
1991
2010
|
displayedRef.current = initialDisplayedRef.current;
|
|
1992
2011
|
setDisplayedText(initialDisplayedRef.current);
|
|
1993
2012
|
}
|
|
2013
|
+
if (displayedRef.current && !targetRef.current.startsWith(displayedRef.current)) {
|
|
2014
|
+
displayedRef.current = targetRef.current;
|
|
2015
|
+
setDisplayedText(targetRef.current);
|
|
2016
|
+
}
|
|
1994
2017
|
if (runningRef.current) return;
|
|
1995
2018
|
runningRef.current = true;
|
|
1996
2019
|
const tick = () => {
|
|
@@ -1998,8 +2021,22 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
|
|
|
1998
2021
|
runningRef.current = false;
|
|
1999
2022
|
return;
|
|
2000
2023
|
}
|
|
2024
|
+
if (displayedRef.current && !targetRef.current.startsWith(displayedRef.current)) {
|
|
2025
|
+
displayedRef.current = targetRef.current;
|
|
2026
|
+
setDisplayedText(targetRef.current);
|
|
2027
|
+
timerRef.current = setTimeout(tick, speed.idle / multiplier);
|
|
2028
|
+
return;
|
|
2029
|
+
}
|
|
2001
2030
|
if (displayedRef.current.length < targetRef.current.length) {
|
|
2002
|
-
const
|
|
2031
|
+
const remaining = targetRef.current.slice(displayedRef.current.length);
|
|
2032
|
+
const imgMatch = MARKDOWN_IMAGE_REGEX.exec(remaining);
|
|
2033
|
+
if (imgMatch) {
|
|
2034
|
+
displayedRef.current += imgMatch[0];
|
|
2035
|
+
setDisplayedText(displayedRef.current);
|
|
2036
|
+
timerRef.current = setTimeout(tick, 0);
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2039
|
+
const nextChar = remaining[0];
|
|
2003
2040
|
displayedRef.current += nextChar;
|
|
2004
2041
|
setDisplayedText(displayedRef.current);
|
|
2005
2042
|
const delay = charDelay(nextChar, speed, multiplier);
|
|
@@ -2023,6 +2060,23 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
|
|
|
2023
2060
|
isTyping
|
|
2024
2061
|
};
|
|
2025
2062
|
}
|
|
2063
|
+
function stripIncompleteImageToken(text) {
|
|
2064
|
+
const lastBang = text.lastIndexOf("![");
|
|
2065
|
+
if (lastBang === -1) return text;
|
|
2066
|
+
const after = text.slice(lastBang);
|
|
2067
|
+
if (/^!\[[^\]]*\]\([^)]*\)/.test(after)) return text;
|
|
2068
|
+
return text.slice(0, lastBang);
|
|
2069
|
+
}
|
|
2070
|
+
function getFeedbackState(message) {
|
|
2071
|
+
const feedback = message.feedback;
|
|
2072
|
+
if (feedback === "up" || feedback === "down") return feedback;
|
|
2073
|
+
if (feedback && typeof feedback === "object") {
|
|
2074
|
+
const value = feedback.feedback;
|
|
2075
|
+
if (!value) return null;
|
|
2076
|
+
return value === "POSITIVE" ? "up" : "down";
|
|
2077
|
+
}
|
|
2078
|
+
return null;
|
|
2079
|
+
}
|
|
2026
2080
|
function AssistantMessageV2({
|
|
2027
2081
|
message,
|
|
2028
2082
|
onImageClick,
|
|
@@ -2032,21 +2086,11 @@ function AssistantMessageV2({
|
|
|
2032
2086
|
typingSpeed = 4
|
|
2033
2087
|
}) {
|
|
2034
2088
|
const [copied, setCopied] = useState(false);
|
|
2035
|
-
const [activeFeedback, setActiveFeedback] = useState(
|
|
2089
|
+
const [activeFeedback, setActiveFeedback] = useState(
|
|
2090
|
+
() => getFeedbackState(message)
|
|
2091
|
+
);
|
|
2036
2092
|
const [reasonModalOpen, setReasonModalOpen] = useState(false);
|
|
2037
2093
|
const canSubmitFeedback = !!onSubmitFeedback && !!message.executionId;
|
|
2038
|
-
const handlePositiveFeedback = () => {
|
|
2039
|
-
if (!canSubmitFeedback || activeFeedback === "up") return;
|
|
2040
|
-
const previous = activeFeedback;
|
|
2041
|
-
setActiveFeedback("up");
|
|
2042
|
-
Promise.resolve(
|
|
2043
|
-
onSubmitFeedback?.({
|
|
2044
|
-
messageId: message.id,
|
|
2045
|
-
executionId: message.executionId,
|
|
2046
|
-
feedback: "POSITIVE"
|
|
2047
|
-
})
|
|
2048
|
-
).catch(() => setActiveFeedback(previous));
|
|
2049
|
-
};
|
|
2050
2094
|
const [toast, setToast] = useState(null);
|
|
2051
2095
|
const copyResetTimerRef = useRef(null);
|
|
2052
2096
|
const toastTimerRef = useRef(null);
|
|
@@ -2054,8 +2098,12 @@ function AssistantMessageV2({
|
|
|
2054
2098
|
const showTraceAction = (actions?.trace ?? true) && !!onExecutionTraceClick;
|
|
2055
2099
|
const showThumbsUp = actions?.thumbsUp ?? true;
|
|
2056
2100
|
const showThumbsDown = actions?.thumbsDown ?? true;
|
|
2101
|
+
const hydratedFeedback = message.feedback;
|
|
2057
2102
|
const hasEverStreamed = useRef(!!message.isStreaming);
|
|
2058
2103
|
if (message.isStreaming) hasEverStreamed.current = true;
|
|
2104
|
+
useEffect(() => {
|
|
2105
|
+
setActiveFeedback(getFeedbackState(message));
|
|
2106
|
+
}, [hydratedFeedback, message.id]);
|
|
2059
2107
|
useEffect(() => {
|
|
2060
2108
|
return () => {
|
|
2061
2109
|
if (copyResetTimerRef.current) clearTimeout(copyResetTimerRef.current);
|
|
@@ -2065,7 +2113,8 @@ function AssistantMessageV2({
|
|
|
2065
2113
|
const rawResponseContent = (() => {
|
|
2066
2114
|
const raw = message.isStreaming ? message.streamingContent || message.content : message.content;
|
|
2067
2115
|
if (!raw) return "";
|
|
2068
|
-
|
|
2116
|
+
const normalized = raw.replace(/\\n/g, "\n");
|
|
2117
|
+
return message.isStreaming ? stripIncompleteImageToken(normalized) : normalized;
|
|
2069
2118
|
})();
|
|
2070
2119
|
const isThinkingStreaming = !!message.isStreaming && !rawResponseContent && !message.isError;
|
|
2071
2120
|
const responseTypingEnabled = hasEverStreamed.current && Boolean(rawResponseContent) && !message.isError;
|
|
@@ -2121,6 +2170,21 @@ function AssistantMessageV2({
|
|
|
2121
2170
|
setToast({ label, tone });
|
|
2122
2171
|
toastTimerRef.current = setTimeout(() => setToast(null), 1800);
|
|
2123
2172
|
};
|
|
2173
|
+
const handlePositiveFeedback = () => {
|
|
2174
|
+
if (!canSubmitFeedback || activeFeedback === "up") return;
|
|
2175
|
+
const previous = activeFeedback;
|
|
2176
|
+
setActiveFeedback("up");
|
|
2177
|
+
Promise.resolve(
|
|
2178
|
+
onSubmitFeedback?.({
|
|
2179
|
+
messageId: message.id,
|
|
2180
|
+
executionId: message.executionId,
|
|
2181
|
+
feedback: "POSITIVE"
|
|
2182
|
+
})
|
|
2183
|
+
).then(() => showToast("Thank you for your feedback", "success")).catch(() => {
|
|
2184
|
+
setActiveFeedback(previous);
|
|
2185
|
+
showToast("Could not send feedback", "error");
|
|
2186
|
+
});
|
|
2187
|
+
};
|
|
2124
2188
|
const handleCopy = async () => {
|
|
2125
2189
|
try {
|
|
2126
2190
|
if (!navigator.clipboard?.writeText) {
|
|
@@ -2267,7 +2331,13 @@ function AssistantMessageV2({
|
|
|
2267
2331
|
activeFeedback === "up" && "payman-v2-assistant-msg-action-btn-active"
|
|
2268
2332
|
),
|
|
2269
2333
|
"aria-label": "Good response",
|
|
2270
|
-
children: /* @__PURE__ */ jsx(
|
|
2334
|
+
children: /* @__PURE__ */ jsx(
|
|
2335
|
+
ThumbsUp,
|
|
2336
|
+
{
|
|
2337
|
+
fill: activeFeedback === "up" ? "currentColor" : "none",
|
|
2338
|
+
style: { width: 15, height: 15 }
|
|
2339
|
+
}
|
|
2340
|
+
)
|
|
2271
2341
|
}
|
|
2272
2342
|
) }),
|
|
2273
2343
|
showThumbsDown && canSubmitFeedback && /* @__PURE__ */ jsx(ActionTooltipV2, { label: "Bad response", children: /* @__PURE__ */ jsx(
|
|
@@ -2279,7 +2349,13 @@ function AssistantMessageV2({
|
|
|
2279
2349
|
activeFeedback === "down" && "payman-v2-assistant-msg-action-btn-active"
|
|
2280
2350
|
),
|
|
2281
2351
|
"aria-label": "Bad response",
|
|
2282
|
-
children: /* @__PURE__ */ jsx(
|
|
2352
|
+
children: /* @__PURE__ */ jsx(
|
|
2353
|
+
ThumbsDown,
|
|
2354
|
+
{
|
|
2355
|
+
fill: activeFeedback === "down" ? "currentColor" : "none",
|
|
2356
|
+
style: { width: 15, height: 15 }
|
|
2357
|
+
}
|
|
2358
|
+
)
|
|
2283
2359
|
}
|
|
2284
2360
|
) }),
|
|
2285
2361
|
showTraceAction && /* @__PURE__ */ jsx(ActionTooltipV2, { label: "Trace", children: /* @__PURE__ */ jsx(
|
|
@@ -2306,6 +2382,7 @@ function AssistantMessageV2({
|
|
|
2306
2382
|
details
|
|
2307
2383
|
});
|
|
2308
2384
|
setActiveFeedback("down");
|
|
2385
|
+
showToast("Thank you for your feedback", "success");
|
|
2309
2386
|
}
|
|
2310
2387
|
}
|
|
2311
2388
|
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paymanai/payman-ask-sdk",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.10",
|
|
4
4
|
"description": "Reusable web chat SDK for Payman K2 Agents with streaming support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@lottiefiles/dotlottie-react": "^0.19.0",
|
|
45
|
-
"@paymanai/payman-typescript-ask-sdk": "
|
|
45
|
+
"@paymanai/payman-typescript-ask-sdk": "/Users/ibraheem/Desktop/Work/payman/sdks/payman-sdk-typescript",
|
|
46
46
|
"@sentry/react": "^10.46.0",
|
|
47
47
|
"clsx": "^2.1.1",
|
|
48
48
|
"remark-breaks": "^4.0.0",
|