@paymanai/payman-typescript-ask-sdk 1.0.0

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.
@@ -0,0 +1,624 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ // src/hooks/useChat.ts
6
+
7
+ // src/utils/generateId.ts
8
+ function generateId() {
9
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
10
+ const r = Math.random() * 16 | 0;
11
+ const v = c === "x" ? r : r & 3 | 8;
12
+ return v.toString(16);
13
+ });
14
+ }
15
+
16
+ // src/utils/streamingClient.native.ts
17
+ function parseJSONBuffer(buffer) {
18
+ const events = [];
19
+ let braceCount = 0;
20
+ let startIndex = 0;
21
+ let inString = false;
22
+ let escapeNext = false;
23
+ let lastParsedIndex = -1;
24
+ for (let i = 0; i < buffer.length; i++) {
25
+ const char = buffer[i];
26
+ if (escapeNext) {
27
+ escapeNext = false;
28
+ continue;
29
+ }
30
+ if (char === "\\") {
31
+ escapeNext = true;
32
+ continue;
33
+ }
34
+ if (char === '"' && !escapeNext) {
35
+ inString = !inString;
36
+ continue;
37
+ }
38
+ if (inString) {
39
+ continue;
40
+ }
41
+ if (char === "{") {
42
+ if (braceCount === 0) {
43
+ startIndex = i;
44
+ }
45
+ braceCount++;
46
+ } else if (char === "}") {
47
+ braceCount--;
48
+ if (braceCount === 0) {
49
+ const jsonStr = buffer.substring(startIndex, i + 1);
50
+ try {
51
+ const parsed = JSON.parse(jsonStr);
52
+ const event = parsed;
53
+ events.push(event);
54
+ lastParsedIndex = i;
55
+ } catch (err) {
56
+ console.error("Failed to parse JSON event:", jsonStr, err);
57
+ }
58
+ }
59
+ }
60
+ }
61
+ const remaining = lastParsedIndex >= 0 ? buffer.substring(lastParsedIndex + 1) : buffer;
62
+ return { events, remaining };
63
+ }
64
+ async function streamWorkflowEvents(url, body, headers, options = {}) {
65
+ const { signal, onEvent, onError, onComplete } = options;
66
+ return new Promise((resolve, reject) => {
67
+ const xhr = new XMLHttpRequest();
68
+ let buffer = "";
69
+ let lastProcessedIndex = 0;
70
+ const abortHandler = () => {
71
+ xhr.abort();
72
+ resolve();
73
+ };
74
+ if (signal) {
75
+ signal.addEventListener("abort", abortHandler);
76
+ }
77
+ xhr.onprogress = () => {
78
+ if (xhr.readyState === 3 || xhr.readyState === 4) {
79
+ const newData = xhr.responseText.substring(lastProcessedIndex);
80
+ lastProcessedIndex = xhr.responseText.length;
81
+ if (newData) {
82
+ buffer += newData;
83
+ const { events, remaining } = parseJSONBuffer(buffer);
84
+ for (const event of events) {
85
+ onEvent?.(event);
86
+ }
87
+ buffer = remaining;
88
+ }
89
+ }
90
+ };
91
+ xhr.onload = () => {
92
+ if (signal) {
93
+ signal.removeEventListener("abort", abortHandler);
94
+ }
95
+ if (xhr.status >= 200 && xhr.status < 300) {
96
+ if (buffer.trim()) {
97
+ const { events } = parseJSONBuffer(buffer);
98
+ for (const event of events) {
99
+ onEvent?.(event);
100
+ }
101
+ }
102
+ onComplete?.();
103
+ resolve();
104
+ } else {
105
+ const error = new Error(`HTTP ${xhr.status}: ${xhr.responseText}`);
106
+ onError?.(error);
107
+ reject(error);
108
+ }
109
+ };
110
+ xhr.onerror = () => {
111
+ if (signal) {
112
+ signal.removeEventListener("abort", abortHandler);
113
+ }
114
+ const error = new Error("Network error");
115
+ onError?.(error);
116
+ reject(error);
117
+ };
118
+ xhr.onabort = () => {
119
+ if (signal) {
120
+ signal.removeEventListener("abort", abortHandler);
121
+ }
122
+ resolve();
123
+ };
124
+ xhr.open("POST", url, true);
125
+ xhr.setRequestHeader("Content-Type", "application/json");
126
+ xhr.setRequestHeader(
127
+ "Accept",
128
+ "application/x-ndjson, text/event-stream, text/plain, */*"
129
+ );
130
+ for (const [key, value] of Object.entries(headers)) {
131
+ xhr.setRequestHeader(key, value);
132
+ }
133
+ xhr.send(JSON.stringify(body));
134
+ });
135
+ }
136
+
137
+ // src/utils/eventProcessor.ts
138
+ function getEventMessage(event) {
139
+ if (event.message?.trim()) {
140
+ return event.message;
141
+ }
142
+ if (event.errorMessage?.trim()) {
143
+ return event.errorMessage;
144
+ }
145
+ const eventType = event.eventType;
146
+ switch (eventType) {
147
+ case "STARTED":
148
+ case "WORKFLOW_STARTED":
149
+ return "Starting workflow...";
150
+ case "ORCHESTRATOR_THINKING":
151
+ return "Planning execution strategy...";
152
+ case "ORCHESTRATOR_COMPLETED":
153
+ return "Planning complete";
154
+ case "INTENT_STARTED":
155
+ return event.workerName ? `${event.workerName} started` : "Processing intent...";
156
+ case "INTENT_PROGRESS":
157
+ return event.workerName ? `${event.workerName} in progress` : "Thinking...";
158
+ case "INTENT_COMPLETED":
159
+ return event.workerName ? `${event.workerName} completed` : "Intent completed";
160
+ case "AGGREGATOR_THINKING":
161
+ return "Combining results...";
162
+ case "AGGREGATOR_COMPLETED":
163
+ return "Results combined";
164
+ case "COMPLETED":
165
+ case "WORKFLOW_COMPLETED":
166
+ return "Workflow completed successfully";
167
+ case "ERROR":
168
+ case "WORKFLOW_ERROR":
169
+ case "INTENT_ERROR":
170
+ return event.errorMessage || "An error occurred";
171
+ default:
172
+ return eventType;
173
+ }
174
+ }
175
+ function extractResponseContent(response) {
176
+ if (typeof response === "string") {
177
+ return response;
178
+ }
179
+ if (typeof response === "object" && response !== null) {
180
+ const resp = response;
181
+ if ("text" in resp && typeof resp.text === "string") {
182
+ return resp.text;
183
+ }
184
+ if ("content" in resp && typeof resp.content === "string") {
185
+ return resp.content;
186
+ }
187
+ if ("message" in resp && typeof resp.message === "string") {
188
+ return resp.message;
189
+ }
190
+ if ("answer" in resp && typeof resp.answer === "string") {
191
+ return resp.answer;
192
+ }
193
+ return JSON.stringify(response);
194
+ }
195
+ return "";
196
+ }
197
+ function processStreamEvent(event, state) {
198
+ const eventType = event.eventType;
199
+ const message = getEventMessage(event);
200
+ if ((eventType === "COMPLETED" || eventType === "WORKFLOW_COMPLETED") && event.response !== void 0) {
201
+ const content = extractResponseContent(event.response);
202
+ if (content) {
203
+ state.accumulatedContent = content;
204
+ state.finalData = event.response;
205
+ }
206
+ }
207
+ if (eventType === "STARTED" || eventType === "WORKFLOW_STARTED") ; else if (eventType === "COMPLETED" || eventType === "WORKFLOW_COMPLETED") {
208
+ state.steps.forEach((step) => {
209
+ if (step.status === "in_progress") {
210
+ step.status = "completed";
211
+ }
212
+ });
213
+ } else if (eventType === "INTENT_ERROR") {
214
+ state.hasError = true;
215
+ state.errorMessage = message || event.errorMessage || "An error occurred";
216
+ const intentStep = state.steps.find(
217
+ (s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress"
218
+ );
219
+ if (intentStep) {
220
+ intentStep.status = "error";
221
+ }
222
+ } else if (eventType === "ERROR" || eventType === "WORKFLOW_ERROR") {
223
+ state.hasError = true;
224
+ state.errorMessage = message || event.errorMessage || "An error occurred";
225
+ } else if (eventType === "ORCHESTRATOR_COMPLETED") {
226
+ const orchestratorStep = state.steps.find(
227
+ (s) => s.eventType === "ORCHESTRATOR_THINKING" && s.status === "in_progress"
228
+ );
229
+ if (orchestratorStep) {
230
+ orchestratorStep.status = "completed";
231
+ if (event.elapsedMs) {
232
+ orchestratorStep.elapsedMs = event.elapsedMs;
233
+ }
234
+ if (orchestratorStep.id === state.currentExecutingStepId) {
235
+ state.currentExecutingStepId = void 0;
236
+ }
237
+ }
238
+ } else if (eventType === "INTENT_COMPLETED") {
239
+ const intentStep = state.steps.find(
240
+ (s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress"
241
+ );
242
+ if (intentStep) {
243
+ intentStep.status = "completed";
244
+ if (event.elapsedMs) {
245
+ intentStep.elapsedMs = event.elapsedMs;
246
+ }
247
+ if (intentStep.id === state.currentExecutingStepId) {
248
+ state.currentExecutingStepId = void 0;
249
+ }
250
+ }
251
+ } else if (eventType === "AGGREGATOR_COMPLETED") {
252
+ const aggregatorStep = state.steps.find(
253
+ (s) => s.eventType === "AGGREGATOR_THINKING" && s.status === "in_progress"
254
+ );
255
+ if (aggregatorStep) {
256
+ aggregatorStep.status = "completed";
257
+ if (event.elapsedMs) {
258
+ aggregatorStep.elapsedMs = event.elapsedMs;
259
+ }
260
+ if (aggregatorStep.id === state.currentExecutingStepId) {
261
+ state.currentExecutingStepId = void 0;
262
+ }
263
+ }
264
+ } else if (eventType === "ORCHESTRATOR_THINKING" || eventType === "INTENT_STARTED" || eventType === "INTENT_PROGRESS" || eventType === "AGGREGATOR_THINKING") {
265
+ if (eventType === "INTENT_PROGRESS") {
266
+ const intentStep = state.steps.find(
267
+ (s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress"
268
+ );
269
+ if (intentStep) {
270
+ intentStep.message = message;
271
+ state.currentExecutingStepId = intentStep.id;
272
+ } else {
273
+ const stepId = `step-${state.stepCounter++}`;
274
+ state.steps.push({
275
+ id: stepId,
276
+ eventType: "INTENT_STARTED",
277
+ message,
278
+ status: "in_progress",
279
+ timestamp: Date.now(),
280
+ elapsedMs: event.elapsedMs
281
+ });
282
+ state.currentExecutingStepId = stepId;
283
+ }
284
+ } else {
285
+ const stepId = `step-${state.stepCounter++}`;
286
+ state.steps.push({
287
+ id: stepId,
288
+ eventType,
289
+ message,
290
+ status: "in_progress",
291
+ timestamp: Date.now(),
292
+ elapsedMs: event.elapsedMs
293
+ });
294
+ state.currentExecutingStepId = stepId;
295
+ }
296
+ }
297
+ return state;
298
+ }
299
+
300
+ // src/utils/messageStateManager.ts
301
+ function createStreamingMessageUpdate(state) {
302
+ const hasCompletedContent = state.accumulatedContent && state.finalData !== void 0;
303
+ return {
304
+ streamingContent: state.hasError ? `Oops, something went wrong. Please try again.
305
+
306
+ ${state.errorMessage}` : hasCompletedContent ? state.accumulatedContent : "",
307
+ content: state.hasError ? `Oops, something went wrong. Please try again.
308
+
309
+ ${state.errorMessage}` : "",
310
+ currentMessage: state.hasError ? void 0 : state.currentMessage,
311
+ streamProgress: state.hasError ? "error" : "processing",
312
+ isError: state.hasError,
313
+ errorDetails: state.hasError ? state.errorMessage : void 0,
314
+ executionId: state.executionId,
315
+ sessionId: state.sessionId,
316
+ steps: state.hasError ? [] : [...state.steps],
317
+ // Don't show steps on error
318
+ currentExecutingStepId: state.hasError ? void 0 : state.currentExecutingStepId,
319
+ isCancelled: false
320
+ };
321
+ }
322
+ function createErrorMessageUpdate(error, state) {
323
+ const isAborted = error.name === "AbortError";
324
+ return {
325
+ isStreaming: false,
326
+ streamProgress: isAborted ? "processing" : "error",
327
+ isError: !isAborted,
328
+ isCancelled: isAborted,
329
+ errorDetails: isAborted ? void 0 : error.message,
330
+ content: isAborted ? state.accumulatedContent || "" : state.accumulatedContent || `Oops, something went wrong. Please try again.
331
+
332
+ ${error.message}`,
333
+ // Preserve currentMessage when cancelled so UI can show it
334
+ currentMessage: isAborted ? state.currentMessage || "Thinking..." : void 0,
335
+ steps: [...state.steps].map((step) => {
336
+ if (step.status === "in_progress" && isAborted) {
337
+ return { ...step, status: "pending" };
338
+ }
339
+ return step;
340
+ }),
341
+ currentExecutingStepId: void 0
342
+ };
343
+ }
344
+ function createFinalMessage(streamingId, state) {
345
+ return {
346
+ id: streamingId,
347
+ sessionId: state.sessionId,
348
+ role: "assistant",
349
+ content: state.hasError ? `Oops, something went wrong. Please try again.
350
+
351
+ ${state.errorMessage}` : state.accumulatedContent || "",
352
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
353
+ isStreaming: false,
354
+ streamProgress: state.hasError ? "error" : "completed",
355
+ isError: state.hasError,
356
+ errorDetails: state.hasError ? state.errorMessage : void 0,
357
+ executionId: state.executionId,
358
+ tracingData: state.finalData,
359
+ steps: state.hasError ? [] : [...state.steps],
360
+ // Don't show steps on error
361
+ isCancelled: false,
362
+ currentExecutingStepId: void 0
363
+ };
364
+ }
365
+ function createCancelledMessageUpdate(steps, currentMessage) {
366
+ const updatedSteps = steps.map((step) => {
367
+ if (step.status === "in_progress") {
368
+ return { ...step, status: "pending" };
369
+ }
370
+ return step;
371
+ });
372
+ return {
373
+ isStreaming: false,
374
+ isCancelled: true,
375
+ steps: updatedSteps,
376
+ currentExecutingStepId: void 0,
377
+ // Preserve currentMessage so UI can show it with X icon
378
+ currentMessage: currentMessage || "Thinking..."
379
+ };
380
+ }
381
+
382
+ // src/utils/requestBuilder.ts
383
+ function buildRequestBody(config, userMessage, sessionId) {
384
+ const sessionOwner = config.sessionParams;
385
+ const sessionAttributes = sessionOwner?.attributes && Object.keys(sessionOwner.attributes).length > 0 ? sessionOwner.attributes : void 0;
386
+ return {
387
+ workflowName: config.workflowName,
388
+ userInput: userMessage,
389
+ sessionId,
390
+ sessionOwnerId: sessionOwner?.id || "",
391
+ sessionOwnerLabel: sessionOwner?.name || "",
392
+ sessionAttributes,
393
+ options: {
394
+ clientTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone
395
+ }
396
+ };
397
+ }
398
+ function buildStreamingUrl(config) {
399
+ const endpoint = config.api.streamEndpoint || "/api/workflows/ask/stream";
400
+ const stage = config.stage || "DEV";
401
+ const version = config.workflowVersion || 1;
402
+ return `${config.api.baseUrl}${endpoint}?stage=${stage}&workflowVersion=${version}`;
403
+ }
404
+ function buildRequestHeaders(config) {
405
+ const headers = {
406
+ ...config.api.headers
407
+ };
408
+ if (config.api.authToken) {
409
+ headers.Authorization = `Bearer ${config.api.authToken}`;
410
+ }
411
+ return headers;
412
+ }
413
+ function useStreamManager(config, callbacks, setMessages, setIsWaitingForResponse) {
414
+ const abortControllerRef = react.useRef(null);
415
+ const startStream = react.useCallback(
416
+ async (userMessage, streamingId, sessionId) => {
417
+ abortControllerRef.current?.abort();
418
+ const abortController = new AbortController();
419
+ abortControllerRef.current = abortController;
420
+ const state = {
421
+ accumulatedContent: "",
422
+ executionId: void 0,
423
+ currentSessionId: void 0,
424
+ finalData: void 0,
425
+ steps: [],
426
+ stepCounter: 0,
427
+ currentExecutingStepId: void 0,
428
+ hasError: false,
429
+ errorMessage: ""
430
+ };
431
+ const requestBody = buildRequestBody(config, userMessage, sessionId);
432
+ const url = buildStreamingUrl(config);
433
+ const headers = buildRequestHeaders(config);
434
+ try {
435
+ await streamWorkflowEvents(url, requestBody, headers, {
436
+ signal: abortController.signal,
437
+ onEvent: (event) => {
438
+ if (abortController.signal.aborted) {
439
+ return;
440
+ }
441
+ if (event.executionId) state.executionId = event.executionId;
442
+ if (event.sessionId) state.currentSessionId = event.sessionId;
443
+ processStreamEvent(event, state);
444
+ const currentMessage = event.message?.trim() || event.errorMessage?.trim();
445
+ setMessages(
446
+ (prev) => prev.map(
447
+ (msg) => msg.id === streamingId ? {
448
+ ...msg,
449
+ ...createStreamingMessageUpdate({
450
+ ...state,
451
+ currentMessage
452
+ })
453
+ } : msg
454
+ )
455
+ );
456
+ },
457
+ onError: (error) => {
458
+ setIsWaitingForResponse(false);
459
+ if (error.name !== "AbortError") {
460
+ callbacks.onError?.(error);
461
+ }
462
+ setMessages(
463
+ (prev) => prev.map(
464
+ (msg) => msg.id === streamingId ? {
465
+ ...msg,
466
+ ...createErrorMessageUpdate(error, state)
467
+ } : msg
468
+ )
469
+ );
470
+ },
471
+ onComplete: () => {
472
+ setIsWaitingForResponse(false);
473
+ if (state.currentSessionId && state.currentSessionId !== sessionId) {
474
+ callbacks.onSessionIdChange?.(state.currentSessionId);
475
+ }
476
+ const finalMessage = createFinalMessage(streamingId, {
477
+ ...state,
478
+ sessionId: state.currentSessionId || sessionId
479
+ });
480
+ setMessages(
481
+ (prev) => prev.map(
482
+ (msg) => msg.id === streamingId ? finalMessage : msg
483
+ )
484
+ );
485
+ callbacks.onStreamComplete?.(finalMessage);
486
+ }
487
+ });
488
+ return state.currentSessionId;
489
+ } catch (error) {
490
+ setIsWaitingForResponse(false);
491
+ if (error.name !== "AbortError") {
492
+ callbacks.onError?.(error);
493
+ }
494
+ setMessages(
495
+ (prev) => prev.map(
496
+ (msg) => msg.id === streamingId ? {
497
+ ...msg,
498
+ ...createErrorMessageUpdate(error, state)
499
+ } : msg
500
+ )
501
+ );
502
+ return state.currentSessionId;
503
+ }
504
+ },
505
+ [config, callbacks, setMessages, setIsWaitingForResponse]
506
+ );
507
+ const cancelStream = react.useCallback(() => {
508
+ abortControllerRef.current?.abort();
509
+ }, []);
510
+ return {
511
+ startStream,
512
+ cancelStream,
513
+ abortControllerRef
514
+ };
515
+ }
516
+
517
+ // src/hooks/useChat.ts
518
+ function useChat(config, callbacks = {}) {
519
+ const [messages, setMessages] = react.useState([]);
520
+ const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(false);
521
+ const sessionIdRef = react.useRef(void 0);
522
+ const { startStream, cancelStream: cancelStreamManager, abortControllerRef } = useStreamManager(
523
+ config,
524
+ callbacks,
525
+ setMessages,
526
+ setIsWaitingForResponse
527
+ );
528
+ const sendMessage = react.useCallback(
529
+ async (userMessage) => {
530
+ if (!userMessage.trim()) return;
531
+ if (!sessionIdRef.current && config.autoGenerateSessionId !== false) {
532
+ sessionIdRef.current = generateId();
533
+ callbacks.onSessionIdChange?.(sessionIdRef.current);
534
+ }
535
+ const userMessageId = `user-${Date.now()}`;
536
+ const userMsg = {
537
+ id: userMessageId,
538
+ sessionId: sessionIdRef.current,
539
+ role: "user",
540
+ content: userMessage,
541
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
542
+ };
543
+ setMessages((prev) => [...prev, userMsg]);
544
+ callbacks.onMessageSent?.(userMessage);
545
+ setIsWaitingForResponse(true);
546
+ callbacks.onStreamStart?.();
547
+ const streamingId = `assistant-${Date.now()}`;
548
+ const streamingMsg = {
549
+ id: streamingId,
550
+ sessionId: sessionIdRef.current,
551
+ role: "assistant",
552
+ content: "",
553
+ streamingContent: "",
554
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
555
+ isStreaming: true,
556
+ streamProgress: "started",
557
+ steps: [],
558
+ currentExecutingStepId: void 0,
559
+ isCancelled: false,
560
+ currentMessage: void 0
561
+ };
562
+ setMessages((prev) => [...prev, streamingMsg]);
563
+ const newSessionId = await startStream(
564
+ userMessage,
565
+ streamingId,
566
+ sessionIdRef.current
567
+ );
568
+ if (newSessionId && newSessionId !== sessionIdRef.current) {
569
+ sessionIdRef.current = newSessionId;
570
+ }
571
+ },
572
+ [config, callbacks, startStream, setMessages, setIsWaitingForResponse]
573
+ );
574
+ const clearMessages = react.useCallback(() => {
575
+ setMessages([]);
576
+ }, []);
577
+ const cancelStream = react.useCallback(() => {
578
+ cancelStreamManager();
579
+ setIsWaitingForResponse(false);
580
+ setMessages(
581
+ (prev) => prev.map((msg) => {
582
+ if (msg.isStreaming) {
583
+ return {
584
+ ...msg,
585
+ ...createCancelledMessageUpdate(
586
+ msg.steps || [],
587
+ msg.currentMessage
588
+ )
589
+ };
590
+ }
591
+ return msg;
592
+ })
593
+ );
594
+ }, [cancelStreamManager]);
595
+ const resetSession = react.useCallback(() => {
596
+ setMessages([]);
597
+ sessionIdRef.current = void 0;
598
+ abortControllerRef.current?.abort();
599
+ setIsWaitingForResponse(false);
600
+ }, []);
601
+ const getSessionId = react.useCallback(() => {
602
+ return sessionIdRef.current;
603
+ }, []);
604
+ const getMessages = react.useCallback(() => {
605
+ return messages;
606
+ }, [messages]);
607
+ return {
608
+ messages,
609
+ sendMessage,
610
+ clearMessages,
611
+ cancelStream,
612
+ resetSession,
613
+ getSessionId,
614
+ getMessages,
615
+ isWaitingForResponse,
616
+ sessionId: sessionIdRef.current
617
+ };
618
+ }
619
+
620
+ exports.generateId = generateId;
621
+ exports.streamWorkflowEvents = streamWorkflowEvents;
622
+ exports.useChat = useChat;
623
+ //# sourceMappingURL=index.native.js.map
624
+ //# sourceMappingURL=index.native.js.map