@paymanai/payman-ask-sdk 4.0.6 → 4.0.7

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.mjs CHANGED
@@ -5,7 +5,7 @@ import { createContext, forwardRef, useRef, useState, useCallback, useImperative
5
5
  import { clsx } from 'clsx';
6
6
  import { twMerge } from 'tailwind-merge';
7
7
  import * as Sentry from '@sentry/react';
8
- import { ArrowDown, Pencil, X, RotateCcw, Telescope, Zap, Plus, ImagePlus, Paperclip, Mic, ArrowUp, Check, AlertCircle, Copy, WifiOff, ThumbsUp, ThumbsDown, Binoculars, ShieldCheck, Download, Loader2, User, Clock, Sparkles, ImageOff, Eye, ChevronDown, ChevronRight } from 'lucide-react';
8
+ import { ArrowDown, Pencil, X, RotateCcw, Telescope, Zap, Plus, ImagePlus, Paperclip, Mic, ArrowUp, Check, AlertCircle, Copy, WifiOff, ThumbsUp, ThumbsDown, Binoculars, ShieldCheck, Download, Loader2, ChevronDown, User, Clock, Sparkles, ImageOff, Eye, ChevronRight } from 'lucide-react';
9
9
  import ReactMarkdown from 'react-markdown';
10
10
  import remarkGfm from 'remark-gfm';
11
11
  import { createPortal } from 'react-dom';
@@ -232,28 +232,38 @@ var NEGATIVE_FEEDBACK_REASONS = [
232
232
  { value: "REPORT_CONTENT", label: "Report content" },
233
233
  { value: "OTHER", label: "Other" }
234
234
  ];
235
- async function submitAskFeedback({
235
+ var DEFAULT_STREAM_ENDPOINT = "/api/playground/ask/stream";
236
+ async function submitFeedback({
236
237
  baseUrl,
238
+ streamEndpoint,
237
239
  headers,
240
+ authToken,
241
+ stage,
242
+ stageQueryParam,
238
243
  executionId,
239
244
  feedback,
240
245
  details,
241
246
  signal
242
247
  }) {
243
248
  const base = baseUrl.replace(/\/+$/, "");
244
- const resp = await fetch(
245
- `${base}/api/ask/executions/${encodeURIComponent(executionId)}/feedback`,
246
- {
247
- method: "POST",
248
- headers: {
249
- "Content-Type": "application/json",
250
- Accept: "application/json",
251
- ...headers ?? {}
252
- },
253
- body: JSON.stringify(details ? { feedback, details } : { feedback }),
254
- signal
255
- }
256
- );
249
+ const endpointPath = (streamEndpoint || DEFAULT_STREAM_ENDPOINT).split("?")[0].replace(/\/+$/, "");
250
+ const basePath = endpointPath.endsWith("/stream") ? endpointPath.slice(0, -"/stream".length) : endpointPath;
251
+ const query = new URLSearchParams();
252
+ if (stage) query.set(stageQueryParam ?? "stage", stage);
253
+ const qs = query.toString() ? `?${query.toString()}` : "";
254
+ const url = `${base}${basePath}/executions/${encodeURIComponent(executionId)}/feedback${qs}`;
255
+ const requestHeaders = {
256
+ "Content-Type": "application/json",
257
+ Accept: "application/json",
258
+ ...headers ?? {}
259
+ };
260
+ if (authToken) requestHeaders.Authorization = `Bearer ${authToken}`;
261
+ const resp = await fetch(url, {
262
+ method: "POST",
263
+ headers: requestHeaders,
264
+ body: JSON.stringify(details ? { feedback, details } : { feedback }),
265
+ signal
266
+ });
257
267
  if (!resp.ok) {
258
268
  throw new Error(`Feedback failed: ${resp.status} ${resp.statusText}`);
259
269
  }
@@ -1732,19 +1742,26 @@ function FeedbackReasonModal({
1732
1742
  const [details, setDetails] = useState("");
1733
1743
  const [submitting, setSubmitting] = useState(false);
1734
1744
  const [error, setError] = useState(null);
1745
+ const [reasonOpen, setReasonOpen] = useState(false);
1735
1746
  useEffect(() => {
1736
1747
  if (open) {
1737
1748
  setReason(NEGATIVE_FEEDBACK_REASONS[0].value);
1738
1749
  setDetails("");
1739
1750
  setError(null);
1740
1751
  setSubmitting(false);
1752
+ setReasonOpen(false);
1741
1753
  }
1742
1754
  }, [open]);
1743
1755
  const handleKeyDown = useCallback(
1744
1756
  (event) => {
1745
- if (event.key === "Escape") onClose();
1757
+ if (event.key !== "Escape") return;
1758
+ if (reasonOpen) {
1759
+ setReasonOpen(false);
1760
+ return;
1761
+ }
1762
+ onClose();
1746
1763
  },
1747
- [onClose]
1764
+ [onClose, reasonOpen]
1748
1765
  );
1749
1766
  useEffect(() => {
1750
1767
  if (!open || typeof document === "undefined") return;
@@ -1759,6 +1776,7 @@ function FeedbackReasonModal({
1759
1776
  const handleSubmit = async () => {
1760
1777
  setSubmitting(true);
1761
1778
  setError(null);
1779
+ setReasonOpen(false);
1762
1780
  try {
1763
1781
  await onSubmit(reason, details.trim() ? details.trim() : void 0);
1764
1782
  onClose();
@@ -1767,6 +1785,7 @@ function FeedbackReasonModal({
1767
1785
  setSubmitting(false);
1768
1786
  }
1769
1787
  };
1788
+ const selectedReason = NEGATIVE_FEEDBACK_REASONS.find((item) => item.value === reason) ?? NEGATIVE_FEEDBACK_REASONS[0];
1770
1789
  return /* @__PURE__ */ jsx(AnimatePresence, { children: open ? /* @__PURE__ */ jsx(
1771
1790
  motion.div,
1772
1791
  {
@@ -1790,6 +1809,17 @@ function FeedbackReasonModal({
1790
1809
  "aria-modal": "true",
1791
1810
  "aria-labelledby": "payman-v2-feedback-title",
1792
1811
  children: [
1812
+ /* @__PURE__ */ jsx(
1813
+ "button",
1814
+ {
1815
+ type: "button",
1816
+ onClick: onClose,
1817
+ disabled: submitting,
1818
+ className: "payman-v2-feedback-modal-close",
1819
+ "aria-label": "Close feedback modal",
1820
+ children: /* @__PURE__ */ jsx(X, { size: 18, strokeWidth: 2 })
1821
+ }
1822
+ ),
1793
1823
  /* @__PURE__ */ jsx(
1794
1824
  "h2",
1795
1825
  {
@@ -1806,15 +1836,73 @@ function FeedbackReasonModal({
1806
1836
  children: "What was the issue?"
1807
1837
  }
1808
1838
  ),
1809
- /* @__PURE__ */ jsx(
1810
- "select",
1839
+ /* @__PURE__ */ jsxs(
1840
+ "div",
1811
1841
  {
1812
1842
  id: "payman-v2-feedback-reason",
1813
- className: "payman-v2-feedback-modal-select",
1814
- value: reason,
1815
- onChange: (e) => setReason(e.target.value),
1816
- disabled: submitting,
1817
- children: NEGATIVE_FEEDBACK_REASONS.map((r) => /* @__PURE__ */ jsx("option", { value: r.value, children: r.label }, r.value))
1843
+ className: "payman-v2-feedback-modal-reason",
1844
+ onBlur: (event) => {
1845
+ const nextTarget = event.relatedTarget;
1846
+ if (!nextTarget || !event.currentTarget.contains(nextTarget)) {
1847
+ setReasonOpen(false);
1848
+ }
1849
+ },
1850
+ children: [
1851
+ /* @__PURE__ */ jsxs(
1852
+ "button",
1853
+ {
1854
+ type: "button",
1855
+ className: "payman-v2-feedback-modal-reason-trigger",
1856
+ "aria-haspopup": "listbox",
1857
+ "aria-expanded": reasonOpen,
1858
+ "aria-controls": "payman-v2-feedback-reason-options",
1859
+ disabled: submitting,
1860
+ onClick: () => setReasonOpen((current) => !current),
1861
+ children: [
1862
+ /* @__PURE__ */ jsx("span", { children: selectedReason.label }),
1863
+ /* @__PURE__ */ jsx(
1864
+ ChevronDown,
1865
+ {
1866
+ size: 17,
1867
+ strokeWidth: 2,
1868
+ className: reasonOpen ? "payman-v2-feedback-modal-reason-chevron payman-v2-feedback-modal-reason-chevron-open" : "payman-v2-feedback-modal-reason-chevron"
1869
+ }
1870
+ )
1871
+ ]
1872
+ }
1873
+ ),
1874
+ reasonOpen ? /* @__PURE__ */ jsx(
1875
+ "div",
1876
+ {
1877
+ id: "payman-v2-feedback-reason-options",
1878
+ className: "payman-v2-feedback-modal-reason-menu",
1879
+ role: "listbox",
1880
+ "aria-label": "Feedback issue",
1881
+ tabIndex: -1,
1882
+ children: NEGATIVE_FEEDBACK_REASONS.map((item) => {
1883
+ const isSelected = item.value === reason;
1884
+ return /* @__PURE__ */ jsxs(
1885
+ "button",
1886
+ {
1887
+ type: "button",
1888
+ className: isSelected ? "payman-v2-feedback-modal-reason-option payman-v2-feedback-modal-reason-option-selected" : "payman-v2-feedback-modal-reason-option",
1889
+ role: "option",
1890
+ "aria-selected": isSelected,
1891
+ onClick: () => {
1892
+ setReason(item.value);
1893
+ setReasonOpen(false);
1894
+ },
1895
+ children: [
1896
+ /* @__PURE__ */ jsx("span", { children: item.label }),
1897
+ isSelected ? /* @__PURE__ */ jsx(Check, { size: 16, strokeWidth: 2 }) : null
1898
+ ]
1899
+ },
1900
+ item.value
1901
+ );
1902
+ })
1903
+ }
1904
+ ) : null
1905
+ ]
1818
1906
  }
1819
1907
  ),
1820
1908
  /* @__PURE__ */ jsx(
@@ -1988,34 +2076,6 @@ function AssistantMessageV2({
1988
2076
  void 0,
1989
2077
  typingSpeed
1990
2078
  );
1991
- const elapsedMs = (() => {
1992
- const fromServer = message.totalElapsedMs;
1993
- if (typeof fromServer === "number" && Number.isFinite(fromServer) && fromServer > 0) {
1994
- return fromServer;
1995
- }
1996
- const steps = message.steps;
1997
- if (!steps || steps.length === 0) return void 0;
1998
- let earliest = Number.POSITIVE_INFINITY;
1999
- let latest = Number.NEGATIVE_INFINITY;
2000
- for (const s of steps) {
2001
- if (!s.timestamp) continue;
2002
- if (s.timestamp < earliest) earliest = s.timestamp;
2003
- const end = s.timestamp + (s.elapsedMs ?? 0);
2004
- if (end > latest) latest = end;
2005
- }
2006
- if (!Number.isFinite(earliest) || !Number.isFinite(latest)) {
2007
- const total = steps.reduce((sum, s) => sum + (s.elapsedMs || 0), 0);
2008
- return total > 0 ? total : void 0;
2009
- }
2010
- const diff = latest - earliest;
2011
- return diff > 0 ? diff : void 0;
2012
- })();
2013
- function formatElapsed(ms) {
2014
- if (ms === void 0) return void 0;
2015
- if (ms < 1e3) return `${Math.round(ms)}ms`;
2016
- return `${(ms / 1e3).toFixed(1)}s`;
2017
- }
2018
- const totalElapsedLabel = formatElapsed(elapsedMs);
2019
2079
  const stickyLabel = (() => {
2020
2080
  const steps = message.steps;
2021
2081
  if (!steps || steps.length === 0) return void 0;
@@ -2230,8 +2290,7 @@ function AssistantMessageV2({
2230
2290
  "aria-label": "Trace response",
2231
2291
  children: /* @__PURE__ */ jsx(Binoculars, { style: { width: 16, height: 16 } })
2232
2292
  }
2233
- ) }),
2234
- totalElapsedLabel && /* @__PURE__ */ jsx("span", { className: "payman-v2-assistant-msg-elapsed", children: totalElapsedLabel })
2293
+ ) })
2235
2294
  ] })
2236
2295
  ] }),
2237
2296
  /* @__PURE__ */ jsx(
@@ -3950,9 +4009,13 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
3950
4009
  if (!executionId) {
3951
4010
  throw new Error("Cannot submit feedback before the response completes");
3952
4011
  }
3953
- await submitAskFeedback({
4012
+ await submitFeedback({
3954
4013
  baseUrl: config.api.baseUrl,
4014
+ streamEndpoint: config.api.streamEndpoint,
3955
4015
  headers: config.api.headers,
4016
+ authToken: config.api.authToken,
4017
+ stage: config.stage,
4018
+ stageQueryParam: config.stageQueryParam,
3956
4019
  executionId,
3957
4020
  feedback,
3958
4021
  details
@@ -3962,7 +4025,15 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
3962
4025
  feedback: feedback === "POSITIVE" ? "up" : "down"
3963
4026
  });
3964
4027
  },
3965
- [config.api.baseUrl, config.api.headers, onMessageFeedback]
4028
+ [
4029
+ config.api.baseUrl,
4030
+ config.api.streamEndpoint,
4031
+ config.api.headers,
4032
+ config.api.authToken,
4033
+ config.stage,
4034
+ config.stageQueryParam,
4035
+ onMessageFeedback
4036
+ ]
3966
4037
  );
3967
4038
  const onExecutionTraceClick = useMemo(() => {
3968
4039
  if (!config.debug) return rawOnExecutionTraceClick;