@paymanai/payman-ask-sdk 4.0.6 → 4.0.8

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 CHANGED
@@ -258,28 +258,38 @@ var NEGATIVE_FEEDBACK_REASONS = [
258
258
  { value: "REPORT_CONTENT", label: "Report content" },
259
259
  { value: "OTHER", label: "Other" }
260
260
  ];
261
- async function submitAskFeedback({
261
+ var DEFAULT_STREAM_ENDPOINT = "/api/playground/ask/stream";
262
+ async function submitFeedback({
262
263
  baseUrl,
264
+ streamEndpoint,
263
265
  headers,
266
+ authToken,
267
+ stage,
268
+ stageQueryParam,
264
269
  executionId,
265
270
  feedback,
266
271
  details,
267
272
  signal
268
273
  }) {
269
274
  const base = baseUrl.replace(/\/+$/, "");
270
- const resp = await fetch(
271
- `${base}/api/ask/executions/${encodeURIComponent(executionId)}/feedback`,
272
- {
273
- method: "POST",
274
- headers: {
275
- "Content-Type": "application/json",
276
- Accept: "application/json",
277
- ...headers ?? {}
278
- },
279
- body: JSON.stringify(details ? { feedback, details } : { feedback }),
280
- signal
281
- }
282
- );
275
+ const endpointPath = (streamEndpoint || DEFAULT_STREAM_ENDPOINT).split("?")[0].replace(/\/+$/, "");
276
+ const basePath = endpointPath.endsWith("/stream") ? endpointPath.slice(0, -"/stream".length) : endpointPath;
277
+ const query = new URLSearchParams();
278
+ if (stage) query.set(stageQueryParam ?? "stage", stage);
279
+ const qs = query.toString() ? `?${query.toString()}` : "";
280
+ const url = `${base}${basePath}/executions/${encodeURIComponent(executionId)}/feedback${qs}`;
281
+ const requestHeaders = {
282
+ "Content-Type": "application/json",
283
+ Accept: "application/json",
284
+ ...headers ?? {}
285
+ };
286
+ if (authToken) requestHeaders.Authorization = `Bearer ${authToken}`;
287
+ const resp = await fetch(url, {
288
+ method: "POST",
289
+ headers: requestHeaders,
290
+ body: JSON.stringify(details ? { feedback, details } : { feedback }),
291
+ signal
292
+ });
283
293
  if (!resp.ok) {
284
294
  throw new Error(`Feedback failed: ${resp.status} ${resp.statusText}`);
285
295
  }
@@ -1758,19 +1768,26 @@ function FeedbackReasonModal({
1758
1768
  const [details, setDetails] = react.useState("");
1759
1769
  const [submitting, setSubmitting] = react.useState(false);
1760
1770
  const [error, setError] = react.useState(null);
1771
+ const [reasonOpen, setReasonOpen] = react.useState(false);
1761
1772
  react.useEffect(() => {
1762
1773
  if (open) {
1763
1774
  setReason(NEGATIVE_FEEDBACK_REASONS[0].value);
1764
1775
  setDetails("");
1765
1776
  setError(null);
1766
1777
  setSubmitting(false);
1778
+ setReasonOpen(false);
1767
1779
  }
1768
1780
  }, [open]);
1769
1781
  const handleKeyDown = react.useCallback(
1770
1782
  (event) => {
1771
- if (event.key === "Escape") onClose();
1783
+ if (event.key !== "Escape") return;
1784
+ if (reasonOpen) {
1785
+ setReasonOpen(false);
1786
+ return;
1787
+ }
1788
+ onClose();
1772
1789
  },
1773
- [onClose]
1790
+ [onClose, reasonOpen]
1774
1791
  );
1775
1792
  react.useEffect(() => {
1776
1793
  if (!open || typeof document === "undefined") return;
@@ -1785,6 +1802,7 @@ function FeedbackReasonModal({
1785
1802
  const handleSubmit = async () => {
1786
1803
  setSubmitting(true);
1787
1804
  setError(null);
1805
+ setReasonOpen(false);
1788
1806
  try {
1789
1807
  await onSubmit(reason, details.trim() ? details.trim() : void 0);
1790
1808
  onClose();
@@ -1793,6 +1811,7 @@ function FeedbackReasonModal({
1793
1811
  setSubmitting(false);
1794
1812
  }
1795
1813
  };
1814
+ const selectedReason = NEGATIVE_FEEDBACK_REASONS.find((item) => item.value === reason) ?? NEGATIVE_FEEDBACK_REASONS[0];
1796
1815
  return /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: open ? /* @__PURE__ */ jsxRuntime.jsx(
1797
1816
  framerMotion.motion.div,
1798
1817
  {
@@ -1816,6 +1835,17 @@ function FeedbackReasonModal({
1816
1835
  "aria-modal": "true",
1817
1836
  "aria-labelledby": "payman-v2-feedback-title",
1818
1837
  children: [
1838
+ /* @__PURE__ */ jsxRuntime.jsx(
1839
+ "button",
1840
+ {
1841
+ type: "button",
1842
+ onClick: onClose,
1843
+ disabled: submitting,
1844
+ className: "payman-v2-feedback-modal-close",
1845
+ "aria-label": "Close feedback modal",
1846
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 18, strokeWidth: 2 })
1847
+ }
1848
+ ),
1819
1849
  /* @__PURE__ */ jsxRuntime.jsx(
1820
1850
  "h2",
1821
1851
  {
@@ -1832,15 +1862,73 @@ function FeedbackReasonModal({
1832
1862
  children: "What was the issue?"
1833
1863
  }
1834
1864
  ),
1835
- /* @__PURE__ */ jsxRuntime.jsx(
1836
- "select",
1865
+ /* @__PURE__ */ jsxRuntime.jsxs(
1866
+ "div",
1837
1867
  {
1838
1868
  id: "payman-v2-feedback-reason",
1839
- className: "payman-v2-feedback-modal-select",
1840
- value: reason,
1841
- onChange: (e) => setReason(e.target.value),
1842
- disabled: submitting,
1843
- children: NEGATIVE_FEEDBACK_REASONS.map((r) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: r.value, children: r.label }, r.value))
1869
+ className: "payman-v2-feedback-modal-reason",
1870
+ onBlur: (event) => {
1871
+ const nextTarget = event.relatedTarget;
1872
+ if (!nextTarget || !event.currentTarget.contains(nextTarget)) {
1873
+ setReasonOpen(false);
1874
+ }
1875
+ },
1876
+ children: [
1877
+ /* @__PURE__ */ jsxRuntime.jsxs(
1878
+ "button",
1879
+ {
1880
+ type: "button",
1881
+ className: "payman-v2-feedback-modal-reason-trigger",
1882
+ "aria-haspopup": "listbox",
1883
+ "aria-expanded": reasonOpen,
1884
+ "aria-controls": "payman-v2-feedback-reason-options",
1885
+ disabled: submitting,
1886
+ onClick: () => setReasonOpen((current) => !current),
1887
+ children: [
1888
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: selectedReason.label }),
1889
+ /* @__PURE__ */ jsxRuntime.jsx(
1890
+ lucideReact.ChevronDown,
1891
+ {
1892
+ size: 17,
1893
+ strokeWidth: 2,
1894
+ className: reasonOpen ? "payman-v2-feedback-modal-reason-chevron payman-v2-feedback-modal-reason-chevron-open" : "payman-v2-feedback-modal-reason-chevron"
1895
+ }
1896
+ )
1897
+ ]
1898
+ }
1899
+ ),
1900
+ reasonOpen ? /* @__PURE__ */ jsxRuntime.jsx(
1901
+ "div",
1902
+ {
1903
+ id: "payman-v2-feedback-reason-options",
1904
+ className: "payman-v2-feedback-modal-reason-menu",
1905
+ role: "listbox",
1906
+ "aria-label": "Feedback issue",
1907
+ tabIndex: -1,
1908
+ children: NEGATIVE_FEEDBACK_REASONS.map((item) => {
1909
+ const isSelected = item.value === reason;
1910
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1911
+ "button",
1912
+ {
1913
+ type: "button",
1914
+ className: isSelected ? "payman-v2-feedback-modal-reason-option payman-v2-feedback-modal-reason-option-selected" : "payman-v2-feedback-modal-reason-option",
1915
+ role: "option",
1916
+ "aria-selected": isSelected,
1917
+ onClick: () => {
1918
+ setReason(item.value);
1919
+ setReasonOpen(false);
1920
+ },
1921
+ children: [
1922
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: item.label }),
1923
+ isSelected ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { size: 16, strokeWidth: 2 }) : null
1924
+ ]
1925
+ },
1926
+ item.value
1927
+ );
1928
+ })
1929
+ }
1930
+ ) : null
1931
+ ]
1844
1932
  }
1845
1933
  ),
1846
1934
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2014,34 +2102,6 @@ function AssistantMessageV2({
2014
2102
  void 0,
2015
2103
  typingSpeed
2016
2104
  );
2017
- const elapsedMs = (() => {
2018
- const fromServer = message.totalElapsedMs;
2019
- if (typeof fromServer === "number" && Number.isFinite(fromServer) && fromServer > 0) {
2020
- return fromServer;
2021
- }
2022
- const steps = message.steps;
2023
- if (!steps || steps.length === 0) return void 0;
2024
- let earliest = Number.POSITIVE_INFINITY;
2025
- let latest = Number.NEGATIVE_INFINITY;
2026
- for (const s of steps) {
2027
- if (!s.timestamp) continue;
2028
- if (s.timestamp < earliest) earliest = s.timestamp;
2029
- const end = s.timestamp + (s.elapsedMs ?? 0);
2030
- if (end > latest) latest = end;
2031
- }
2032
- if (!Number.isFinite(earliest) || !Number.isFinite(latest)) {
2033
- const total = steps.reduce((sum, s) => sum + (s.elapsedMs || 0), 0);
2034
- return total > 0 ? total : void 0;
2035
- }
2036
- const diff = latest - earliest;
2037
- return diff > 0 ? diff : void 0;
2038
- })();
2039
- function formatElapsed(ms) {
2040
- if (ms === void 0) return void 0;
2041
- if (ms < 1e3) return `${Math.round(ms)}ms`;
2042
- return `${(ms / 1e3).toFixed(1)}s`;
2043
- }
2044
- const totalElapsedLabel = formatElapsed(elapsedMs);
2045
2105
  const stickyLabel = (() => {
2046
2106
  const steps = message.steps;
2047
2107
  if (!steps || steps.length === 0) return void 0;
@@ -2256,8 +2316,7 @@ function AssistantMessageV2({
2256
2316
  "aria-label": "Trace response",
2257
2317
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Binoculars, { style: { width: 16, height: 16 } })
2258
2318
  }
2259
- ) }),
2260
- totalElapsedLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-assistant-msg-elapsed", children: totalElapsedLabel })
2319
+ ) })
2261
2320
  ] })
2262
2321
  ] }),
2263
2322
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -3976,9 +4035,13 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
3976
4035
  if (!executionId) {
3977
4036
  throw new Error("Cannot submit feedback before the response completes");
3978
4037
  }
3979
- await submitAskFeedback({
4038
+ await submitFeedback({
3980
4039
  baseUrl: config.api.baseUrl,
4040
+ streamEndpoint: config.api.streamEndpoint,
3981
4041
  headers: config.api.headers,
4042
+ authToken: config.api.authToken,
4043
+ stage: config.stage,
4044
+ stageQueryParam: config.stageQueryParam,
3982
4045
  executionId,
3983
4046
  feedback,
3984
4047
  details
@@ -3988,7 +4051,15 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
3988
4051
  feedback: feedback === "POSITIVE" ? "up" : "down"
3989
4052
  });
3990
4053
  },
3991
- [config.api.baseUrl, config.api.headers, onMessageFeedback]
4054
+ [
4055
+ config.api.baseUrl,
4056
+ config.api.streamEndpoint,
4057
+ config.api.headers,
4058
+ config.api.authToken,
4059
+ config.stage,
4060
+ config.stageQueryParam,
4061
+ onMessageFeedback
4062
+ ]
3992
4063
  );
3993
4064
  const onExecutionTraceClick = react.useMemo(() => {
3994
4065
  if (!config.debug) return rawOnExecutionTraceClick;