@personaai/react 0.3.5 → 0.5.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.cjs +244 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -4
- package/dist/index.d.ts +70 -4
- package/dist/index.js +242 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -22,6 +22,8 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
PersonaProvider: () => PersonaProvider,
|
|
24
24
|
VERSION: () => VERSION,
|
|
25
|
+
openSSEStream: () => openSSEStream,
|
|
26
|
+
supportsStreamingFetch: () => supportsStreamingFetch,
|
|
25
27
|
useAgents: () => useAgents,
|
|
26
28
|
useChat: () => useChat,
|
|
27
29
|
useConnection: () => useConnection,
|
|
@@ -77,6 +79,181 @@ function usePersonaContext() {
|
|
|
77
79
|
|
|
78
80
|
// src/hooks/useChat.ts
|
|
79
81
|
var import_react2 = require("react");
|
|
82
|
+
|
|
83
|
+
// src/streaming.ts
|
|
84
|
+
function supportsStreamingFetch() {
|
|
85
|
+
if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return typeof fetch !== "undefined" && typeof ReadableStream !== "undefined";
|
|
89
|
+
}
|
|
90
|
+
function fetchReader(response, controller) {
|
|
91
|
+
const reader = response.body.getReader();
|
|
92
|
+
const decoder = new TextDecoder("utf-8");
|
|
93
|
+
return {
|
|
94
|
+
async read() {
|
|
95
|
+
const { done, value } = await reader.read();
|
|
96
|
+
if (done) return { done: true };
|
|
97
|
+
return { done: false, value: decoder.decode(value, { stream: true }) };
|
|
98
|
+
},
|
|
99
|
+
cancel() {
|
|
100
|
+
controller.abort();
|
|
101
|
+
void reader.cancel().catch(() => {
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function xhrStream(opts) {
|
|
107
|
+
return new Promise((resolveStream, rejectStream) => {
|
|
108
|
+
const xhr = new XMLHttpRequest();
|
|
109
|
+
xhr.open("POST", opts.url);
|
|
110
|
+
for (const [key, val] of Object.entries(opts.headers)) xhr.setRequestHeader(key, val);
|
|
111
|
+
let consumed = 0;
|
|
112
|
+
let finished = false;
|
|
113
|
+
let failure = null;
|
|
114
|
+
const queue = [];
|
|
115
|
+
let waiting = null;
|
|
116
|
+
let waitingReject = null;
|
|
117
|
+
let headersResolved = false;
|
|
118
|
+
const pump = () => {
|
|
119
|
+
const text = xhr.responseText;
|
|
120
|
+
if (text.length <= consumed) return;
|
|
121
|
+
const chunk = text.slice(consumed);
|
|
122
|
+
consumed = text.length;
|
|
123
|
+
if (waiting) {
|
|
124
|
+
const resolve = waiting;
|
|
125
|
+
waiting = null;
|
|
126
|
+
waitingReject = null;
|
|
127
|
+
resolve({ done: false, value: chunk });
|
|
128
|
+
} else {
|
|
129
|
+
queue.push(chunk);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
const finish = (err) => {
|
|
133
|
+
if (finished) return;
|
|
134
|
+
finished = true;
|
|
135
|
+
failure = err ?? null;
|
|
136
|
+
if (waiting) {
|
|
137
|
+
const resolve = waiting;
|
|
138
|
+
const reject = waitingReject;
|
|
139
|
+
waiting = null;
|
|
140
|
+
waitingReject = null;
|
|
141
|
+
if (err && reject) reject(err);
|
|
142
|
+
else resolve({ done: true });
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
const reader = {
|
|
146
|
+
read() {
|
|
147
|
+
if (queue.length > 0) {
|
|
148
|
+
return Promise.resolve({ done: false, value: queue.shift() });
|
|
149
|
+
}
|
|
150
|
+
if (finished) {
|
|
151
|
+
return failure ? Promise.reject(failure) : Promise.resolve({ done: true });
|
|
152
|
+
}
|
|
153
|
+
return new Promise((resolve, reject) => {
|
|
154
|
+
waiting = resolve;
|
|
155
|
+
waitingReject = reject;
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
cancel() {
|
|
159
|
+
finish();
|
|
160
|
+
xhr.abort();
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const resolveHeaders = () => {
|
|
164
|
+
if (headersResolved) return;
|
|
165
|
+
headersResolved = true;
|
|
166
|
+
const ok = xhr.status >= 200 && xhr.status < 300;
|
|
167
|
+
resolveStream({
|
|
168
|
+
status: xhr.status,
|
|
169
|
+
ok,
|
|
170
|
+
getHeader: (name) => xhr.getResponseHeader(name),
|
|
171
|
+
reader
|
|
172
|
+
});
|
|
173
|
+
};
|
|
174
|
+
xhr.onreadystatechange = () => {
|
|
175
|
+
if (xhr.readyState === 2) {
|
|
176
|
+
resolveHeaders();
|
|
177
|
+
} else if (xhr.readyState === 3) {
|
|
178
|
+
resolveHeaders();
|
|
179
|
+
pump();
|
|
180
|
+
} else if (xhr.readyState === 4) {
|
|
181
|
+
resolveHeaders();
|
|
182
|
+
pump();
|
|
183
|
+
finish();
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
xhr.onerror = () => {
|
|
187
|
+
const err = new Error("Network request failed");
|
|
188
|
+
if (!headersResolved) rejectStream(err);
|
|
189
|
+
finish(err);
|
|
190
|
+
};
|
|
191
|
+
xhr.ontimeout = () => {
|
|
192
|
+
const err = new Error("Network request timed out");
|
|
193
|
+
if (!headersResolved) rejectStream(err);
|
|
194
|
+
finish(err);
|
|
195
|
+
};
|
|
196
|
+
if (opts.signal) {
|
|
197
|
+
if (opts.signal.aborted) {
|
|
198
|
+
xhr.abort();
|
|
199
|
+
finish();
|
|
200
|
+
} else {
|
|
201
|
+
opts.signal.addEventListener("abort", () => {
|
|
202
|
+
finish();
|
|
203
|
+
xhr.abort();
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
xhr.send(opts.body);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
async function openSSEStream(opts) {
|
|
211
|
+
if (!supportsStreamingFetch()) {
|
|
212
|
+
return xhrStream(opts);
|
|
213
|
+
}
|
|
214
|
+
const controller = new AbortController();
|
|
215
|
+
if (opts.signal) {
|
|
216
|
+
if (opts.signal.aborted) controller.abort();
|
|
217
|
+
else opts.signal.addEventListener("abort", () => controller.abort());
|
|
218
|
+
}
|
|
219
|
+
const response = await fetch(opts.url, {
|
|
220
|
+
method: "POST",
|
|
221
|
+
headers: opts.headers,
|
|
222
|
+
body: opts.body,
|
|
223
|
+
signal: controller.signal
|
|
224
|
+
});
|
|
225
|
+
if (!response.ok) {
|
|
226
|
+
const errorText = await response.text().catch(() => "Stream failed");
|
|
227
|
+
return {
|
|
228
|
+
status: response.status,
|
|
229
|
+
ok: false,
|
|
230
|
+
errorText,
|
|
231
|
+
getHeader: (name) => response.headers.get(name),
|
|
232
|
+
reader: { read: async () => ({ done: true }), cancel: () => controller.abort() }
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (!response.body) {
|
|
236
|
+
const text = await response.text();
|
|
237
|
+
let handed = false;
|
|
238
|
+
return {
|
|
239
|
+
status: response.status,
|
|
240
|
+
ok: true,
|
|
241
|
+
getHeader: (name) => response.headers.get(name),
|
|
242
|
+
reader: {
|
|
243
|
+
read: async () => handed ? { done: true } : (handed = true, { done: false, value: text }),
|
|
244
|
+
cancel: () => controller.abort()
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
status: response.status,
|
|
250
|
+
ok: true,
|
|
251
|
+
getHeader: (name) => response.headers.get(name),
|
|
252
|
+
reader: fetchReader(response, controller)
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// src/hooks/useChat.ts
|
|
80
257
|
function isErrorToolContent(content) {
|
|
81
258
|
if (typeof content !== "string" || !content.trim().startsWith("{")) return false;
|
|
82
259
|
try {
|
|
@@ -143,7 +320,7 @@ function normalizePendingInterrupt(pending) {
|
|
|
143
320
|
return null;
|
|
144
321
|
}
|
|
145
322
|
function useChat(options = {}) {
|
|
146
|
-
const { defaultAgentId, fetchWithAuth } = usePersonaContext();
|
|
323
|
+
const { defaultAgentId, fetchWithAuth, baseUrl, getAuthToken } = usePersonaContext();
|
|
147
324
|
const agentId = options.agentId || defaultAgentId;
|
|
148
325
|
const threadId = options.threadId;
|
|
149
326
|
const [messages, setMessages] = (0, import_react2.useState)(options.initialMessages || []);
|
|
@@ -250,16 +427,25 @@ function useChat(options = {}) {
|
|
|
250
427
|
setInterrupt(null);
|
|
251
428
|
const controller = new AbortController();
|
|
252
429
|
abortControllerRef.current = controller;
|
|
430
|
+
const finalizeReasoning = () => {
|
|
431
|
+
setMessages(
|
|
432
|
+
(prev) => prev.map(
|
|
433
|
+
(m) => m.role === "reasoning" && m.isStreaming ? { ...m, isStreaming: false } : m
|
|
434
|
+
)
|
|
435
|
+
);
|
|
436
|
+
};
|
|
253
437
|
try {
|
|
254
|
-
const payloadMessages = nextMessages.map((m) => ({
|
|
438
|
+
const payloadMessages = nextMessages.filter((m) => m.role !== "reasoning").map((m) => ({
|
|
255
439
|
role: m.role === "assistant" ? "assistant" : "user",
|
|
256
440
|
content: m.content
|
|
257
441
|
}));
|
|
258
442
|
const resolvedThreadId = await (overrideOptions?.threadId ?? options.threadId);
|
|
259
|
-
const
|
|
260
|
-
|
|
443
|
+
const token = getAuthToken ? await getAuthToken() : null;
|
|
444
|
+
const stream = await openSSEStream({
|
|
445
|
+
url: `${baseUrl}/chat`,
|
|
261
446
|
headers: {
|
|
262
|
-
"Content-Type": "application/json"
|
|
447
|
+
"Content-Type": "application/json",
|
|
448
|
+
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
263
449
|
},
|
|
264
450
|
body: JSON.stringify({
|
|
265
451
|
agentId: targetAgentId,
|
|
@@ -269,19 +455,15 @@ function useChat(options = {}) {
|
|
|
269
455
|
}),
|
|
270
456
|
signal: controller.signal
|
|
271
457
|
});
|
|
272
|
-
if (!
|
|
273
|
-
|
|
274
|
-
throw new Error(`Chat error (${response.status}): ${errText}`);
|
|
275
|
-
}
|
|
276
|
-
if (!response.body) {
|
|
277
|
-
throw new Error("No response body received for streaming.");
|
|
458
|
+
if (!stream.ok) {
|
|
459
|
+
throw new Error(`Chat error (${stream.status}): ${stream.errorText ?? "Stream failed"}`);
|
|
278
460
|
}
|
|
279
|
-
const reader =
|
|
280
|
-
const decoder = new TextDecoder("utf-8");
|
|
461
|
+
const reader = stream.reader;
|
|
281
462
|
let buffer = "";
|
|
282
463
|
let accumulatedText = "";
|
|
283
|
-
let accumulatedReasoning = "";
|
|
284
464
|
const toolCallsMap = /* @__PURE__ */ new Map();
|
|
465
|
+
let activeReasoningId = null;
|
|
466
|
+
const reasoningById = /* @__PURE__ */ new Map();
|
|
285
467
|
const patchAssistant = (patch) => {
|
|
286
468
|
setMessages(
|
|
287
469
|
(prev) => prev.map(
|
|
@@ -289,10 +471,26 @@ function useChat(options = {}) {
|
|
|
289
471
|
)
|
|
290
472
|
);
|
|
291
473
|
};
|
|
474
|
+
const insertReasoningMessage = (id) => {
|
|
475
|
+
const msg = {
|
|
476
|
+
id,
|
|
477
|
+
role: "reasoning",
|
|
478
|
+
content: "",
|
|
479
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
480
|
+
isStreaming: true
|
|
481
|
+
};
|
|
482
|
+
setMessages((prev) => {
|
|
483
|
+
const idx = prev.findIndex((m) => m.id === assistantMessageId);
|
|
484
|
+
if (idx === -1) return [...prev, msg];
|
|
485
|
+
const next = [...prev];
|
|
486
|
+
next.splice(idx, 0, msg);
|
|
487
|
+
return next;
|
|
488
|
+
});
|
|
489
|
+
};
|
|
292
490
|
while (true) {
|
|
293
491
|
const { done, value } = await reader.read();
|
|
294
492
|
if (done) break;
|
|
295
|
-
buffer +=
|
|
493
|
+
buffer += value ?? "";
|
|
296
494
|
const lines = buffer.split("\n");
|
|
297
495
|
buffer = lines.pop() ?? "";
|
|
298
496
|
for (const line of lines) {
|
|
@@ -332,11 +530,33 @@ function useChat(options = {}) {
|
|
|
332
530
|
} else if (event.type === "STATE_SNAPSHOT") {
|
|
333
531
|
setFiles(normalizeWorkspaceFiles(event.snapshot.files));
|
|
334
532
|
setTodos(event.snapshot.todos);
|
|
533
|
+
} else if (event.type === "REASONING_MESSAGE_START" && event.messageId) {
|
|
534
|
+
activeReasoningId = event.messageId;
|
|
535
|
+
reasoningById.set(event.messageId, { content: "" });
|
|
536
|
+
insertReasoningMessage(event.messageId);
|
|
335
537
|
} else if (event.type === "REASONING_MESSAGE_CONTENT") {
|
|
336
|
-
|
|
337
|
-
|
|
538
|
+
let rid = event.messageId || activeReasoningId || "";
|
|
539
|
+
if (!rid || !reasoningById.has(rid)) {
|
|
540
|
+
rid = rid || `reasoning-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
541
|
+
activeReasoningId = rid;
|
|
542
|
+
reasoningById.set(rid, { content: "" });
|
|
543
|
+
insertReasoningMessage(rid);
|
|
544
|
+
}
|
|
545
|
+
const entry = reasoningById.get(rid);
|
|
546
|
+
entry.content += event.delta;
|
|
547
|
+
setMessages(
|
|
548
|
+
(prev) => prev.map(
|
|
549
|
+
(m) => m.id === rid ? { ...m, content: entry.content, isStreaming: true } : m
|
|
550
|
+
)
|
|
551
|
+
);
|
|
338
552
|
} else if (event.type === "REASONING_END") {
|
|
339
|
-
|
|
553
|
+
const rid = activeReasoningId;
|
|
554
|
+
activeReasoningId = null;
|
|
555
|
+
if (rid) {
|
|
556
|
+
setMessages(
|
|
557
|
+
(prev) => prev.map((m) => m.id === rid ? { ...m, isStreaming: false } : m)
|
|
558
|
+
);
|
|
559
|
+
}
|
|
340
560
|
} else if (event.type === "CUSTOM") {
|
|
341
561
|
if (event.name === "hitl_request") {
|
|
342
562
|
const value2 = event.value;
|
|
@@ -372,8 +592,7 @@ function useChat(options = {}) {
|
|
|
372
592
|
content: accumulatedText,
|
|
373
593
|
createdAt: /* @__PURE__ */ new Date(),
|
|
374
594
|
isStreaming: false,
|
|
375
|
-
toolCalls: Array.from(toolCallsMap.values())
|
|
376
|
-
...accumulatedReasoning ? { reasoning: accumulatedReasoning, isReasoning: false } : {}
|
|
595
|
+
toolCalls: Array.from(toolCallsMap.values())
|
|
377
596
|
};
|
|
378
597
|
setMessages(
|
|
379
598
|
(prev) => prev.map((msg) => msg.id === assistantMessageId ? finalMessage : msg)
|
|
@@ -403,9 +622,10 @@ function useChat(options = {}) {
|
|
|
403
622
|
} finally {
|
|
404
623
|
setIsStreaming(false);
|
|
405
624
|
abortControllerRef.current = null;
|
|
625
|
+
finalizeReasoning();
|
|
406
626
|
}
|
|
407
627
|
},
|
|
408
|
-
[agentId,
|
|
628
|
+
[agentId, baseUrl, getAuthToken, input, isStreaming, messages, options]
|
|
409
629
|
);
|
|
410
630
|
const handleInputChange = (0, import_react2.useCallback)((e) => {
|
|
411
631
|
setInput(e.target.value);
|
|
@@ -891,11 +1111,13 @@ function useMcpConnections(options = {}) {
|
|
|
891
1111
|
}
|
|
892
1112
|
|
|
893
1113
|
// src/index.ts
|
|
894
|
-
var VERSION = "0.
|
|
1114
|
+
var VERSION = "0.4.0";
|
|
895
1115
|
// Annotate the CommonJS export names for ESM import in node:
|
|
896
1116
|
0 && (module.exports = {
|
|
897
1117
|
PersonaProvider,
|
|
898
1118
|
VERSION,
|
|
1119
|
+
openSSEStream,
|
|
1120
|
+
supportsStreamingFetch,
|
|
899
1121
|
useAgents,
|
|
900
1122
|
useChat,
|
|
901
1123
|
useConnection,
|