@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/README.md +159 -0
- package/dist/index.d.mts +173 -0
- package/dist/index.d.ts +173 -0
- package/dist/index.js +600 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +596 -0
- package/dist/index.mjs.map +1 -0
- package/dist/index.native.js +624 -0
- package/dist/index.native.js.map +1 -0
- package/package.json +46 -0
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
|