@paymanai/payman-ask-sdk 4.0.19 → 4.0.21

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
@@ -709,7 +709,8 @@ function buildRequestBody(config, userMessage, sessionId, options) {
709
709
  sessionAttributes,
710
710
  analysisMode: options?.analysisMode,
711
711
  locale: resolveLocale(config.locale),
712
- timezone: resolveTimezone(config.timezone)
712
+ timezone: resolveTimezone(config.timezone),
713
+ ...options?.attachments?.length ? { attachments: options.attachments } : {}
713
714
  };
714
715
  }
715
716
  function resolveLocale(configured) {
@@ -754,6 +755,7 @@ function buildResolveImagesUrl(config) {
754
755
  const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
755
756
  return `${config.api.baseUrl}${basePath}/resolve-image-urls`;
756
757
  }
758
+ var NGROK_SKIP_BROWSER_WARNING = "ngrok-skip-browser-warning";
757
759
  function buildRequestHeaders(config) {
758
760
  const headers = {
759
761
  ...config.api.headers
@@ -761,6 +763,9 @@ function buildRequestHeaders(config) {
761
763
  if (config.api.authToken) {
762
764
  headers.Authorization = `Bearer ${config.api.authToken}`;
763
765
  }
766
+ if (!headers[NGROK_SKIP_BROWSER_WARNING]) {
767
+ headers[NGROK_SKIP_BROWSER_WARNING] = "true";
768
+ }
764
769
  return headers;
765
770
  }
766
771
  var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
@@ -1041,6 +1046,81 @@ function createCancelledMessageUpdate(steps, currentMessage) {
1041
1046
  currentMessage: currentMessage || "Thinking..."
1042
1047
  };
1043
1048
  }
1049
+ var DEFAULT_SIGNED_URL_ENDPOINT = "/api/files/signed-url";
1050
+ function fileExtension(filename) {
1051
+ const ext = filename.split(".").pop()?.toLowerCase().replace(/^\./, "");
1052
+ return ext && ext.length > 0 ? ext : "bin";
1053
+ }
1054
+ function resolveMimeType(file) {
1055
+ if (file.type && file.type.trim().length > 0) return file.type;
1056
+ const ext = fileExtension(file.name);
1057
+ const byExt = {
1058
+ pdf: "application/pdf",
1059
+ png: "image/png",
1060
+ jpg: "image/jpeg",
1061
+ jpeg: "image/jpeg",
1062
+ gif: "image/gif",
1063
+ webp: "image/webp"
1064
+ };
1065
+ return byExt[ext] ?? "application/octet-stream";
1066
+ }
1067
+ function buildSignedUrlEndpoint(config, ext) {
1068
+ const endpoint = config.api.signedUrlEndpoint || DEFAULT_SIGNED_URL_ENDPOINT;
1069
+ const queryParams = new URLSearchParams({ extn: ext.replace(/^\./, "") });
1070
+ return `${config.api.baseUrl}${endpoint}?${queryParams.toString()}`;
1071
+ }
1072
+ async function requestSignedUrl(config, ext, signal) {
1073
+ const url = buildSignedUrlEndpoint(config, ext);
1074
+ const headers = buildRequestHeaders(config);
1075
+ const response = await fetch(url, {
1076
+ method: "GET",
1077
+ headers,
1078
+ signal
1079
+ });
1080
+ if (!response.ok) {
1081
+ const errorText = await response.text().catch(() => "");
1082
+ throw new Error(
1083
+ errorText ? `Failed to get upload URL (${response.status}): ${errorText}` : `Failed to get upload URL (${response.status})`
1084
+ );
1085
+ }
1086
+ const data = await response.json();
1087
+ if (!data.key || !data.url) {
1088
+ throw new Error("Signed URL response missing key or url");
1089
+ }
1090
+ return { key: data.key, url: data.url };
1091
+ }
1092
+ async function uploadToSignedUrl(signedUrl, file, mimeType, signal) {
1093
+ const response = await fetch(signedUrl, {
1094
+ method: "PUT",
1095
+ headers: {
1096
+ "Content-Type": mimeType,
1097
+ "x-ms-blob-type": "BlockBlob"
1098
+ },
1099
+ body: file,
1100
+ signal
1101
+ });
1102
+ if (!response.ok) {
1103
+ const errorText = await response.text().catch(() => "");
1104
+ throw new Error(
1105
+ errorText ? `Failed to upload file (${response.status}): ${errorText}` : `Failed to upload file (${response.status})`
1106
+ );
1107
+ }
1108
+ }
1109
+ async function uploadAttachment(config, file, signal) {
1110
+ const ext = fileExtension(file.name);
1111
+ const mimeType = resolveMimeType(file);
1112
+ const { key, url } = await requestSignedUrl(config, ext, signal);
1113
+ await uploadToSignedUrl(url, file, mimeType, signal);
1114
+ return {
1115
+ tempKey: key,
1116
+ filename: file.name,
1117
+ mimeType
1118
+ };
1119
+ }
1120
+ async function uploadAttachments(config, files, signal) {
1121
+ const uploads = files.map((file) => uploadAttachment(config, file, signal));
1122
+ return Promise.all(uploads);
1123
+ }
1044
1124
  var UserActionStaleError = class extends Error {
1045
1125
  constructor(userActionId, message = "User action is no longer actionable") {
1046
1126
  super(message);
@@ -1077,9 +1157,6 @@ async function cancelUserAction(config, userActionId) {
1077
1157
  async function resendUserAction(config, userActionId) {
1078
1158
  return sendUserActionRequest(config, userActionId, "resend");
1079
1159
  }
1080
- async function expireUserAction(config, userActionId) {
1081
- return sendUserActionRequest(config, userActionId, "expired");
1082
- }
1083
1160
  var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
1084
1161
  function upsertPrompt(prompts, req) {
1085
1162
  const active = { ...req, status: "pending" };
@@ -1108,12 +1185,32 @@ function getStoredOrInitialMessages(config) {
1108
1185
  function getSessionIdFromMessages(messages) {
1109
1186
  return messages.find((message) => message.sessionId)?.sessionId;
1110
1187
  }
1188
+ var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set(["jpg", "jpeg", "png", "gif", "webp", "avif"]);
1189
+ function attachmentKindFromFile(file) {
1190
+ const ext = file.name.split(".").pop()?.toLowerCase() ?? "";
1191
+ if (IMAGE_EXTENSIONS.has(ext) || file.type.startsWith("image/")) return "image";
1192
+ return "file";
1193
+ }
1194
+ function buildMessageAttachments(files) {
1195
+ return files.map((file, index) => {
1196
+ const kind = attachmentKindFromFile(file);
1197
+ return {
1198
+ id: `att-${Date.now()}-${index}`,
1199
+ filename: file.name,
1200
+ mimeType: file.type || "application/octet-stream",
1201
+ // Blob URL for all local files so PDFs/docs stay previewable after send.
1202
+ previewUrl: URL.createObjectURL(file),
1203
+ kind
1204
+ };
1205
+ });
1206
+ }
1111
1207
  function useChatV2(config, callbacks = {}) {
1112
1208
  const [messages, setMessages] = useState(() => getStoredOrInitialMessages(config));
1113
1209
  const [isWaitingForResponse, setIsWaitingForResponse] = useState(() => {
1114
1210
  if (!config.userId) return false;
1115
1211
  return activeStreamStore.get(config.userId)?.isWaiting ?? false;
1116
1212
  });
1213
+ const [isUploadingAttachments, setIsUploadingAttachments] = useState(false);
1117
1214
  const sessionIdRef = useRef(
1118
1215
  getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
1119
1216
  );
@@ -1188,21 +1285,45 @@ function useChatV2(config, callbacks = {}) {
1188
1285
  );
1189
1286
  const sendMessage = useCallback(
1190
1287
  async (userMessage, options) => {
1191
- if (!userMessage.trim()) return;
1288
+ const trimmedMessage = userMessage.trim();
1289
+ const files = options?.files ?? [];
1290
+ const hasPreuploadedAttachments = (options?.attachments?.length ?? 0) > 0;
1291
+ if (!trimmedMessage && files.length === 0 && !hasPreuploadedAttachments) return;
1292
+ let streamAttachments = options?.attachments;
1293
+ if (!streamAttachments && files.length > 0) {
1294
+ setIsUploadingAttachments(true);
1295
+ try {
1296
+ streamAttachments = await uploadAttachments(configRef.current, files);
1297
+ } catch (error) {
1298
+ if (error.name !== "AbortError") {
1299
+ callbacksRef.current.onError?.(error);
1300
+ }
1301
+ throw error;
1302
+ } finally {
1303
+ setIsUploadingAttachments(false);
1304
+ }
1305
+ }
1192
1306
  if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
1193
1307
  sessionIdRef.current = generateId();
1194
1308
  callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
1195
1309
  }
1310
+ const messageAttachments = files.length > 0 ? buildMessageAttachments(files) : streamAttachments?.length ? streamAttachments.map((attachment, index) => ({
1311
+ id: `att-${Date.now()}-${index}`,
1312
+ filename: attachment.filename,
1313
+ mimeType: attachment.mimeType,
1314
+ kind: attachment.mimeType.startsWith("image/") ? "image" : "file"
1315
+ })) : void 0;
1196
1316
  const userMessageId = `user-${Date.now()}`;
1197
1317
  const userMsg = {
1198
1318
  id: userMessageId,
1199
1319
  sessionId: sessionIdRef.current,
1200
1320
  role: "user",
1201
- content: userMessage,
1202
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1321
+ content: trimmedMessage,
1322
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1323
+ attachments: messageAttachments
1203
1324
  };
1204
1325
  setMessages((prev) => [...prev, userMsg]);
1205
- callbacksRef.current.onMessageSent?.(userMessage);
1326
+ callbacksRef.current.onMessageSent?.(trimmedMessage);
1206
1327
  setIsWaitingForResponse(true);
1207
1328
  callbacksRef.current.onStreamStart?.();
1208
1329
  const streamingId = `assistant-${Date.now()}`;
@@ -1229,11 +1350,14 @@ function useChatV2(config, callbacks = {}) {
1229
1350
  activeStreamStore.start(userId, abortController, initialMessages);
1230
1351
  }
1231
1352
  const newSessionId = await startStream(
1232
- userMessage,
1353
+ trimmedMessage,
1233
1354
  streamingId,
1234
1355
  sessionIdRef.current,
1235
1356
  abortController,
1236
- options
1357
+ {
1358
+ analysisMode: options?.analysisMode,
1359
+ attachments: streamAttachments
1360
+ }
1237
1361
  );
1238
1362
  const finalStreamUserId = streamUserIdRef.current ?? userId;
1239
1363
  if (finalStreamUserId) {
@@ -1371,19 +1495,6 @@ function useChatV2(config, callbacks = {}) {
1371
1495
  },
1372
1496
  [setPromptStatus]
1373
1497
  );
1374
- const expireUserAction2 = useCallback(
1375
- async (userActionId) => {
1376
- setPromptStatus(userActionId, "expired");
1377
- try {
1378
- await expireUserAction(configRef.current, userActionId);
1379
- } catch (error) {
1380
- if (error instanceof UserActionStaleError) return;
1381
- callbacksRef.current.onError?.(error);
1382
- throw error;
1383
- }
1384
- },
1385
- [setPromptStatus]
1386
- );
1387
1498
  const dismissNotification = useCallback((id) => {
1388
1499
  setUserActionState((prev) => ({
1389
1500
  ...prev,
@@ -1424,6 +1535,18 @@ function useChatV2(config, callbacks = {}) {
1424
1535
  setMessages(config.initialMessages);
1425
1536
  sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? config.initialSessionId;
1426
1537
  }, [config.initialMessages, config.initialSessionId, config.userId]);
1538
+ const hydratedSessionIdRef = useRef(void 0);
1539
+ useEffect(() => {
1540
+ if (config.userId) return;
1541
+ if (!config.initialMessages?.length) return;
1542
+ const sessionId = config.initialSessionId;
1543
+ if (hydratedSessionIdRef.current === sessionId && messagesRef.current.length > 0) {
1544
+ return;
1545
+ }
1546
+ hydratedSessionIdRef.current = sessionId;
1547
+ setMessages(config.initialMessages);
1548
+ sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? sessionId;
1549
+ }, [config.initialMessages, config.initialSessionId, config.userId]);
1427
1550
  useEffect(() => {
1428
1551
  const prevUserId = prevUserIdRef.current;
1429
1552
  prevUserIdRef.current = config.userId;
@@ -1458,12 +1581,12 @@ function useChatV2(config, callbacks = {}) {
1458
1581
  getSessionId,
1459
1582
  getMessages,
1460
1583
  isWaitingForResponse,
1584
+ isUploadingAttachments,
1461
1585
  sessionId: sessionIdRef.current,
1462
1586
  userActionState,
1463
1587
  submitUserAction: submitUserAction2,
1464
1588
  cancelUserAction: cancelUserAction2,
1465
1589
  resendUserAction: resendUserAction2,
1466
- expireUserAction: expireUserAction2,
1467
1590
  dismissNotification
1468
1591
  };
1469
1592
  }
@@ -6113,7 +6236,7 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6113
6236
  const submitUserAction2 = chat.submitUserAction ?? NOOP_ASYNC;
6114
6237
  const cancelUserAction2 = chat.cancelUserAction ?? NOOP_ASYNC;
6115
6238
  const resendUserAction2 = chat.resendUserAction ?? NOOP_ASYNC;
6116
- const expireUserAction2 = chat.expireUserAction ?? NOOP_ASYNC;
6239
+ const expireUserAction = chat.expireUserAction ?? NOOP_ASYNC;
6117
6240
  const dismissNotification = chat.dismissNotification ?? NOOP;
6118
6241
  const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
6119
6242
  const {
@@ -6514,7 +6637,7 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6514
6637
  onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
6515
6638
  onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
6516
6639
  onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
6517
- onExpireUserAction: isUserActionSupported ? expireUserAction2 : void 0,
6640
+ onExpireUserAction: isUserActionSupported ? expireUserAction : void 0,
6518
6641
  onDismissNotification: dismissNotification,
6519
6642
  onSubmitFeedback: handleSubmitFeedback
6520
6643
  }
@@ -6598,6 +6721,6 @@ var PaymanChat = forwardRef(
6598
6721
  }
6599
6722
  );
6600
6723
 
6601
- export { PaymanChat, PaymanChatContext, UserActionStaleError, cancelUserAction, captureSentryError, cn, expireUserAction, formatDate, resendUserAction, submitUserAction, useChatV2, usePaymanChat, useVoice };
6724
+ export { PaymanChat, PaymanChatContext, UserActionStaleError, cancelUserAction, captureSentryError, cn, formatDate, resendUserAction, submitUserAction, useChatV2, usePaymanChat, useVoice };
6602
6725
  //# sourceMappingURL=index.mjs.map
6603
6726
  //# sourceMappingURL=index.mjs.map