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