@standardagents/react 0.10.1-dev.b8746e9 → 0.10.1-next.bbd142a
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 +312 -191
- package/dist/index.d.ts +170 -75
- package/dist/index.js +399 -237
- package/dist/index.js.map +1 -1
- package/package.json +1 -4
package/dist/index.js
CHANGED
|
@@ -1,10 +1,183 @@
|
|
|
1
1
|
import { createContext, useMemo, useEffect, useRef, useState, useCallback, useContext } from 'react';
|
|
2
|
-
import { FileUploadManager, AgentBuilderClient, messagesToFiles, transformToWorkblocks, ThreadConnectionManager } from '@standardagents/client';
|
|
3
|
-
export { AgentBuilderClient, FileUploadManager, ThreadConnectionManager, generatePendingFileId, isImageMimeType, messagesToFiles, parseAttachments, readFileAsDataUrl, transformToWorkblocks } from '@standardagents/client';
|
|
4
2
|
import { jsx } from 'react/jsx-runtime';
|
|
5
3
|
|
|
6
4
|
// src/context/AgentBuilderProvider.tsx
|
|
7
5
|
|
|
6
|
+
// src/services/client.ts
|
|
7
|
+
var AgentBuilderClient = class {
|
|
8
|
+
endpoint;
|
|
9
|
+
token;
|
|
10
|
+
constructor(endpoint) {
|
|
11
|
+
this.endpoint = endpoint.replace(/\/$/, "");
|
|
12
|
+
this.token = typeof localStorage !== "undefined" ? localStorage.getItem("agentbuilder_auth_token") : null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get thread metadata
|
|
16
|
+
*/
|
|
17
|
+
async getThread(id) {
|
|
18
|
+
const response = await fetch(`${this.endpoint}/threads/${id}`, {
|
|
19
|
+
method: "GET",
|
|
20
|
+
headers: this.getHeaders()
|
|
21
|
+
});
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
throw new Error(`Failed to get thread: ${response.statusText}`);
|
|
24
|
+
}
|
|
25
|
+
return response.json();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get messages from a thread with optional pagination and filtering
|
|
29
|
+
*/
|
|
30
|
+
async getMessages(id, options = {}) {
|
|
31
|
+
const params = new URLSearchParams();
|
|
32
|
+
if (options.limit !== void 0) params.set("limit", String(options.limit));
|
|
33
|
+
if (options.offset !== void 0) params.set("offset", String(options.offset));
|
|
34
|
+
if (options.depth !== void 0) params.set("depth", String(options.depth));
|
|
35
|
+
if (options.includeSilent !== void 0) params.set("includeSilent", String(options.includeSilent));
|
|
36
|
+
const queryString = params.toString();
|
|
37
|
+
const url = `${this.endpoint}/threads/${id}/messages${queryString ? `?${queryString}` : ""}`;
|
|
38
|
+
const response = await fetch(url, {
|
|
39
|
+
method: "GET",
|
|
40
|
+
headers: this.getHeaders()
|
|
41
|
+
});
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
throw new Error(`Failed to get messages: ${response.statusText}`);
|
|
44
|
+
}
|
|
45
|
+
const data = await response.json();
|
|
46
|
+
return data.messages || [];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Send a message to a thread
|
|
50
|
+
*/
|
|
51
|
+
async sendMessage(id, payload) {
|
|
52
|
+
const response = await fetch(`${this.endpoint}/threads/${id}/message`, {
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers: {
|
|
55
|
+
...this.getHeaders(),
|
|
56
|
+
"Content-Type": "application/json"
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify(payload)
|
|
59
|
+
});
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
throw new Error(`Failed to send message: ${response.statusText}`);
|
|
62
|
+
}
|
|
63
|
+
return response.json();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Stop execution of a thread
|
|
67
|
+
*/
|
|
68
|
+
async stopExecution(id) {
|
|
69
|
+
const response = await fetch(`${this.endpoint}/threads/${id}/stop`, {
|
|
70
|
+
method: "POST",
|
|
71
|
+
headers: this.getHeaders()
|
|
72
|
+
});
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
throw new Error(`Failed to stop execution: ${response.statusText}`);
|
|
75
|
+
}
|
|
76
|
+
await response.json();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Connect to message WebSocket for real-time message updates
|
|
80
|
+
*/
|
|
81
|
+
connectMessageWebSocket(id, callbacks = {}, options = {}) {
|
|
82
|
+
const params = new URLSearchParams();
|
|
83
|
+
if (this.token) params.set("token", this.token);
|
|
84
|
+
if (options.includeSilent !== void 0) params.set("includeSilent", String(options.includeSilent));
|
|
85
|
+
if (options.depth !== void 0) params.set("depth", String(options.depth));
|
|
86
|
+
const wsProtocol = this.endpoint.startsWith("https") ? "wss" : "ws";
|
|
87
|
+
const wsEndpoint = this.endpoint.replace(/^https?/, wsProtocol);
|
|
88
|
+
const url = `${wsEndpoint}/threads/${id}/stream?${params.toString()}`;
|
|
89
|
+
const ws = new WebSocket(url);
|
|
90
|
+
ws.onopen = () => {
|
|
91
|
+
callbacks.onOpen?.();
|
|
92
|
+
};
|
|
93
|
+
ws.onmessage = (event) => {
|
|
94
|
+
try {
|
|
95
|
+
if (typeof event.data === "string" && event.data === "pong") {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const data = JSON.parse(event.data);
|
|
99
|
+
switch (data.type) {
|
|
100
|
+
case "message_data":
|
|
101
|
+
callbacks.onMessage?.(data);
|
|
102
|
+
break;
|
|
103
|
+
case "message_chunk":
|
|
104
|
+
callbacks.onChunk?.(data);
|
|
105
|
+
break;
|
|
106
|
+
case "event":
|
|
107
|
+
callbacks.onEvent?.(data);
|
|
108
|
+
break;
|
|
109
|
+
case "error":
|
|
110
|
+
callbacks.onError?.(data);
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error("Failed to parse WebSocket message:", error);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
ws.onerror = (event) => {
|
|
118
|
+
console.error("WebSocket error:", event);
|
|
119
|
+
callbacks.onError?.({ type: "error", error: "WebSocket connection error" });
|
|
120
|
+
};
|
|
121
|
+
ws.onclose = (event) => {
|
|
122
|
+
console.log(`[AgentBuilderClient] Message WebSocket closed - code: ${event.code}, reason: ${event.reason || "none"}, wasClean: ${event.wasClean}`);
|
|
123
|
+
callbacks.onClose?.();
|
|
124
|
+
};
|
|
125
|
+
return ws;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Connect to log WebSocket for custom events
|
|
129
|
+
*/
|
|
130
|
+
connectLogWebSocket(id, callbacks = {}) {
|
|
131
|
+
const params = new URLSearchParams();
|
|
132
|
+
if (this.token) params.set("token", this.token);
|
|
133
|
+
const wsProtocol = this.endpoint.startsWith("https") ? "wss" : "ws";
|
|
134
|
+
const wsEndpoint = this.endpoint.replace(/^https?/, wsProtocol);
|
|
135
|
+
const url = `${wsEndpoint}/threads/${id}?${params.toString()}`;
|
|
136
|
+
const ws = new WebSocket(url);
|
|
137
|
+
ws.onopen = () => {
|
|
138
|
+
callbacks.onOpen?.();
|
|
139
|
+
};
|
|
140
|
+
ws.onmessage = (event) => {
|
|
141
|
+
try {
|
|
142
|
+
if (typeof event.data === "string" && event.data === "pong") {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const data = JSON.parse(event.data);
|
|
146
|
+
switch (data.type) {
|
|
147
|
+
case "log_data":
|
|
148
|
+
callbacks.onLog?.(data);
|
|
149
|
+
break;
|
|
150
|
+
case "custom":
|
|
151
|
+
callbacks.onCustom?.(data);
|
|
152
|
+
break;
|
|
153
|
+
case "stopped_by_user":
|
|
154
|
+
callbacks.onStopped?.(data);
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error("Failed to parse WebSocket message:", error);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
ws.onerror = (event) => {
|
|
162
|
+
console.error("WebSocket error:", event);
|
|
163
|
+
};
|
|
164
|
+
ws.onclose = () => {
|
|
165
|
+
callbacks.onClose?.();
|
|
166
|
+
};
|
|
167
|
+
return ws;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Get headers for HTTP requests
|
|
171
|
+
*/
|
|
172
|
+
getHeaders() {
|
|
173
|
+
const headers = {};
|
|
174
|
+
if (this.token) {
|
|
175
|
+
headers["Authorization"] = `Bearer ${this.token}`;
|
|
176
|
+
}
|
|
177
|
+
return headers;
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
8
181
|
// src/services/sendMessage.ts
|
|
9
182
|
var globalEndpoint = null;
|
|
10
183
|
function __setGlobalEndpoint(endpoint) {
|
|
@@ -23,7 +196,7 @@ async function sendMessage(id, payload) {
|
|
|
23
196
|
if (token) {
|
|
24
197
|
headers["Authorization"] = `Bearer ${token}`;
|
|
25
198
|
}
|
|
26
|
-
const response = await fetch(`${globalEndpoint}/threads/${id}/
|
|
199
|
+
const response = await fetch(`${globalEndpoint}/threads/${id}/message`, {
|
|
27
200
|
method: "POST",
|
|
28
201
|
headers,
|
|
29
202
|
body: JSON.stringify(payload)
|
|
@@ -93,18 +266,12 @@ function useAgentBuilderConfig() {
|
|
|
93
266
|
}
|
|
94
267
|
return context.config;
|
|
95
268
|
}
|
|
96
|
-
var
|
|
97
|
-
var StaticContext = createContext(null);
|
|
98
|
-
var MessagesContext = createContext(null);
|
|
99
|
-
var FilesContext = createContext(null);
|
|
100
|
-
var ConnectionContext = createContext(null);
|
|
101
|
-
createContext(null);
|
|
269
|
+
var ThreadContext = createContext(null);
|
|
102
270
|
function ThreadProvider({
|
|
103
271
|
threadId,
|
|
104
272
|
options = {},
|
|
105
273
|
preload = true,
|
|
106
274
|
live = true,
|
|
107
|
-
useWorkblocks = false,
|
|
108
275
|
depth = 0,
|
|
109
276
|
includeSilent = false,
|
|
110
277
|
endpoint: endpointOverride,
|
|
@@ -129,21 +296,13 @@ function ThreadProvider({
|
|
|
129
296
|
const [connectionStatus, setConnectionStatus] = useState(
|
|
130
297
|
live ? "connecting" : "disconnected"
|
|
131
298
|
);
|
|
132
|
-
const [pendingFiles, setPendingFiles] = useState([]);
|
|
133
|
-
const committedFiles = useMemo(() => {
|
|
134
|
-
return messagesToFiles(messages);
|
|
135
|
-
}, [messages]);
|
|
136
|
-
const files = useMemo(() => {
|
|
137
|
-
return [...pendingFiles, ...committedFiles];
|
|
138
|
-
}, [pendingFiles, committedFiles]);
|
|
139
|
-
const workblocks = useMemo(() => {
|
|
140
|
-
if (!useWorkblocks) {
|
|
141
|
-
return messages;
|
|
142
|
-
}
|
|
143
|
-
return transformToWorkblocks(messages);
|
|
144
|
-
}, [messages, useWorkblocks]);
|
|
145
299
|
const eventListenersRef = useRef(/* @__PURE__ */ new Map());
|
|
146
|
-
const
|
|
300
|
+
const wsRef = useRef(null);
|
|
301
|
+
const reconnectAttempts = useRef(0);
|
|
302
|
+
const reconnectTimeoutRef = useRef(null);
|
|
303
|
+
const heartbeatIntervalRef = useRef(null);
|
|
304
|
+
const isReconnectingRef = useRef(false);
|
|
305
|
+
const isMountedRef = useRef(true);
|
|
147
306
|
const subscribeToEvent = useCallback(
|
|
148
307
|
(eventType, listener) => {
|
|
149
308
|
if (!eventListenersRef.current.has(eventType)) {
|
|
@@ -174,84 +333,60 @@ function ThreadProvider({
|
|
|
174
333
|
});
|
|
175
334
|
}
|
|
176
335
|
}, []);
|
|
177
|
-
const
|
|
178
|
-
(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
clientRef.current,
|
|
187
|
-
(updates) => {
|
|
188
|
-
setPendingFiles(
|
|
189
|
-
(prev) => prev.map(
|
|
190
|
-
(f) => f.id === pending.id ? { ...f, ...updates } : f
|
|
191
|
-
)
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
).catch(() => {
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
[threadId]
|
|
199
|
-
);
|
|
200
|
-
const removeFile = useCallback((id) => {
|
|
201
|
-
setPendingFiles((prev) => prev.filter((f) => f.id !== id));
|
|
336
|
+
const clearTimers = useCallback(() => {
|
|
337
|
+
if (reconnectTimeoutRef.current) {
|
|
338
|
+
clearTimeout(reconnectTimeoutRef.current);
|
|
339
|
+
reconnectTimeoutRef.current = null;
|
|
340
|
+
}
|
|
341
|
+
if (heartbeatIntervalRef.current) {
|
|
342
|
+
clearInterval(heartbeatIntervalRef.current);
|
|
343
|
+
heartbeatIntervalRef.current = null;
|
|
344
|
+
}
|
|
202
345
|
}, []);
|
|
203
|
-
const
|
|
204
|
-
(
|
|
205
|
-
|
|
206
|
-
return clientRef.current.getFileUrl(threadId, file.path);
|
|
207
|
-
},
|
|
208
|
-
[threadId]
|
|
209
|
-
);
|
|
210
|
-
const getThumbnailUrl = useCallback(
|
|
211
|
-
(file) => {
|
|
212
|
-
if (!file.path) return "";
|
|
213
|
-
return clientRef.current.getThumbnailUrl(threadId, file.path);
|
|
214
|
-
},
|
|
215
|
-
[threadId]
|
|
216
|
-
);
|
|
217
|
-
const getPreviewUrl = useCallback(
|
|
218
|
-
(file) => {
|
|
219
|
-
if (!file.isImage) return null;
|
|
220
|
-
if (file.localPreviewUrl) return file.localPreviewUrl;
|
|
221
|
-
if (file.path) return clientRef.current.getThumbnailUrl(threadId, file.path);
|
|
222
|
-
return null;
|
|
223
|
-
},
|
|
224
|
-
[threadId]
|
|
225
|
-
);
|
|
226
|
-
const sendMessage2 = useCallback(
|
|
227
|
-
async (payload) => {
|
|
228
|
-
try {
|
|
229
|
-
return await clientRef.current.sendMessage(threadId, payload);
|
|
230
|
-
} catch (err) {
|
|
231
|
-
setError(err instanceof Error ? err : new Error(String(err)));
|
|
232
|
-
throw err;
|
|
233
|
-
}
|
|
234
|
-
},
|
|
235
|
-
[threadId]
|
|
236
|
-
);
|
|
237
|
-
const stopExecution = useCallback(async () => {
|
|
238
|
-
try {
|
|
239
|
-
await clientRef.current.stopExecution(threadId);
|
|
240
|
-
} catch (err) {
|
|
241
|
-
setError(err instanceof Error ? err : new Error(String(err)));
|
|
242
|
-
throw err;
|
|
346
|
+
const startHeartbeat = useCallback((ws) => {
|
|
347
|
+
if (heartbeatIntervalRef.current) {
|
|
348
|
+
clearInterval(heartbeatIntervalRef.current);
|
|
243
349
|
}
|
|
244
|
-
|
|
350
|
+
heartbeatIntervalRef.current = setInterval(() => {
|
|
351
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
352
|
+
ws.send("ping");
|
|
353
|
+
}
|
|
354
|
+
}, 3e4);
|
|
355
|
+
}, []);
|
|
245
356
|
useEffect(() => {
|
|
246
357
|
if (!live || !threadId) return;
|
|
358
|
+
console.log(
|
|
359
|
+
`[ThreadProvider] useEffect running - threadId: ${threadId}, live: ${live}`
|
|
360
|
+
);
|
|
361
|
+
connectWebSocket();
|
|
362
|
+
return () => {
|
|
363
|
+
console.log(
|
|
364
|
+
`[ThreadProvider] useEffect cleanup - threadId: ${threadId}, live: ${live}`
|
|
365
|
+
);
|
|
366
|
+
clearTimers();
|
|
367
|
+
if (wsRef.current) {
|
|
368
|
+
wsRef.current.close();
|
|
369
|
+
wsRef.current = null;
|
|
370
|
+
}
|
|
371
|
+
reconnectAttempts.current = 0;
|
|
372
|
+
isReconnectingRef.current = false;
|
|
373
|
+
};
|
|
374
|
+
}, [threadId, live]);
|
|
375
|
+
const connectWebSocket = useCallback(() => {
|
|
376
|
+
if (!live || !threadId || !isMountedRef.current) return;
|
|
247
377
|
console.log(`[ThreadProvider] Connecting WebSocket for thread ${threadId}`);
|
|
248
|
-
|
|
249
|
-
|
|
378
|
+
setConnectionStatus("connecting");
|
|
379
|
+
const ws = clientRef.current.connectMessageWebSocket(
|
|
250
380
|
threadId,
|
|
251
381
|
{
|
|
252
|
-
|
|
253
|
-
console.log(
|
|
254
|
-
|
|
382
|
+
onOpen: () => {
|
|
383
|
+
console.log(
|
|
384
|
+
`[ThreadProvider] WebSocket connected for thread ${threadId}`
|
|
385
|
+
);
|
|
386
|
+
setConnectionStatus("connected");
|
|
387
|
+
reconnectAttempts.current = 0;
|
|
388
|
+
isReconnectingRef.current = false;
|
|
389
|
+
startHeartbeat(ws);
|
|
255
390
|
},
|
|
256
391
|
onMessage: (event) => {
|
|
257
392
|
setMessages((prev) => {
|
|
@@ -282,6 +417,30 @@ function ThreadProvider({
|
|
|
282
417
|
onError: (event) => {
|
|
283
418
|
console.error("[ThreadProvider] WebSocket error:", event.error);
|
|
284
419
|
setError(new Error(event.error));
|
|
420
|
+
setConnectionStatus("disconnected");
|
|
421
|
+
},
|
|
422
|
+
onClose: () => {
|
|
423
|
+
console.log("[ThreadProvider] WebSocket closed");
|
|
424
|
+
clearTimers();
|
|
425
|
+
if (isMountedRef.current && !isReconnectingRef.current) {
|
|
426
|
+
isReconnectingRef.current = true;
|
|
427
|
+
setConnectionStatus("connecting");
|
|
428
|
+
const delay = Math.min(
|
|
429
|
+
1e3 * Math.pow(2, reconnectAttempts.current),
|
|
430
|
+
3e4
|
|
431
|
+
);
|
|
432
|
+
reconnectAttempts.current++;
|
|
433
|
+
console.log(
|
|
434
|
+
`[ThreadProvider] Reconnecting in ${delay}ms (attempt ${reconnectAttempts.current})`
|
|
435
|
+
);
|
|
436
|
+
reconnectTimeoutRef.current = setTimeout(() => {
|
|
437
|
+
if (isMountedRef.current) {
|
|
438
|
+
connectWebSocket();
|
|
439
|
+
}
|
|
440
|
+
}, delay);
|
|
441
|
+
} else {
|
|
442
|
+
setConnectionStatus("disconnected");
|
|
443
|
+
}
|
|
285
444
|
}
|
|
286
445
|
},
|
|
287
446
|
{
|
|
@@ -289,13 +448,16 @@ function ThreadProvider({
|
|
|
289
448
|
includeSilent
|
|
290
449
|
}
|
|
291
450
|
);
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
451
|
+
wsRef.current = ws;
|
|
452
|
+
}, [
|
|
453
|
+
threadId,
|
|
454
|
+
live,
|
|
455
|
+
depth,
|
|
456
|
+
includeSilent,
|
|
457
|
+
dispatchEvent,
|
|
458
|
+
startHeartbeat,
|
|
459
|
+
clearTimers
|
|
460
|
+
]);
|
|
299
461
|
useEffect(() => {
|
|
300
462
|
if (!preload || !threadId) return;
|
|
301
463
|
const fetchMessages = async () => {
|
|
@@ -319,158 +481,172 @@ function ThreadProvider({
|
|
|
319
481
|
};
|
|
320
482
|
fetchMessages();
|
|
321
483
|
}, [threadId, preload, depth, includeSilent]);
|
|
322
|
-
|
|
484
|
+
useEffect(() => {
|
|
485
|
+
isMountedRef.current = true;
|
|
486
|
+
return () => {
|
|
487
|
+
isMountedRef.current = false;
|
|
488
|
+
};
|
|
489
|
+
}, []);
|
|
490
|
+
const contextValue = useMemo(
|
|
323
491
|
() => ({
|
|
324
492
|
threadId,
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
493
|
+
messages,
|
|
494
|
+
loading,
|
|
495
|
+
error,
|
|
496
|
+
connectionStatus,
|
|
328
497
|
subscribeToEvent,
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
498
|
+
options: {
|
|
499
|
+
depth,
|
|
500
|
+
includeSilent,
|
|
501
|
+
...options
|
|
502
|
+
}
|
|
334
503
|
}),
|
|
335
504
|
[
|
|
336
505
|
threadId,
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
506
|
+
messages,
|
|
507
|
+
loading,
|
|
508
|
+
error,
|
|
509
|
+
connectionStatus,
|
|
510
|
+
subscribeToEvent,
|
|
340
511
|
depth,
|
|
341
512
|
includeSilent,
|
|
342
|
-
|
|
343
|
-
stopExecution,
|
|
344
|
-
subscribeToEvent,
|
|
345
|
-
addFiles,
|
|
346
|
-
removeFile,
|
|
347
|
-
getFileUrl,
|
|
348
|
-
getThumbnailUrl,
|
|
349
|
-
getPreviewUrl
|
|
513
|
+
options
|
|
350
514
|
]
|
|
351
515
|
);
|
|
352
|
-
|
|
353
|
-
() => ({
|
|
354
|
-
messages,
|
|
355
|
-
workblocks,
|
|
356
|
-
loading
|
|
357
|
-
}),
|
|
358
|
-
[messages, workblocks, loading]
|
|
359
|
-
);
|
|
360
|
-
const filesValue = useMemo(
|
|
361
|
-
() => ({
|
|
362
|
-
files
|
|
363
|
-
}),
|
|
364
|
-
[files]
|
|
365
|
-
);
|
|
366
|
-
const connectionValue = useMemo(
|
|
367
|
-
() => ({
|
|
368
|
-
connectionStatus,
|
|
369
|
-
error
|
|
370
|
-
}),
|
|
371
|
-
[connectionStatus, error]
|
|
372
|
-
);
|
|
373
|
-
return /* @__PURE__ */ jsx(StaticContext.Provider, { value: staticValue, children: /* @__PURE__ */ jsx(MessagesContext.Provider, { value: messagesValue, children: /* @__PURE__ */ jsx(FilesContext.Provider, { value: filesValue, children: /* @__PURE__ */ jsx(ConnectionContext.Provider, { value: connectionValue, children }) }) }) });
|
|
374
|
-
}
|
|
375
|
-
function useStaticContext() {
|
|
376
|
-
const context = useContext(StaticContext);
|
|
377
|
-
if (!context) {
|
|
378
|
-
throw new Error("useThread must be used within a ThreadProvider");
|
|
379
|
-
}
|
|
380
|
-
return context;
|
|
516
|
+
return /* @__PURE__ */ jsx(ThreadContext.Provider, { value: contextValue, children });
|
|
381
517
|
}
|
|
382
|
-
function
|
|
383
|
-
const context = useContext(
|
|
384
|
-
if (!context) {
|
|
385
|
-
throw new Error("useThread must be used within a ThreadProvider");
|
|
386
|
-
}
|
|
387
|
-
return context;
|
|
388
|
-
}
|
|
389
|
-
function useFilesContext() {
|
|
390
|
-
const context = useContext(FilesContext);
|
|
391
|
-
if (!context) {
|
|
392
|
-
throw new Error("useThread must be used within a ThreadProvider");
|
|
393
|
-
}
|
|
394
|
-
return context;
|
|
395
|
-
}
|
|
396
|
-
function useConnectionContext() {
|
|
397
|
-
const context = useContext(ConnectionContext);
|
|
518
|
+
function useThreadContext() {
|
|
519
|
+
const context = useContext(ThreadContext);
|
|
398
520
|
if (!context) {
|
|
399
|
-
throw new Error("
|
|
521
|
+
throw new Error("useThreadContext must be used within a ThreadProvider");
|
|
400
522
|
}
|
|
401
523
|
return context;
|
|
402
524
|
}
|
|
403
|
-
function useThreadContextInternal() {
|
|
404
|
-
const static_ = useStaticContext();
|
|
405
|
-
const messages_ = useMessagesContext();
|
|
406
|
-
const files_ = useFilesContext();
|
|
407
|
-
const connection_ = useConnectionContext();
|
|
408
|
-
return {
|
|
409
|
-
threadId: static_.threadId,
|
|
410
|
-
options: static_.options,
|
|
411
|
-
sendMessage: static_.sendMessage,
|
|
412
|
-
stopExecution: static_.stopExecution,
|
|
413
|
-
subscribeToEvent: static_.subscribeToEvent,
|
|
414
|
-
onEvent: static_.subscribeToEvent,
|
|
415
|
-
addFiles: static_.addFiles,
|
|
416
|
-
removeFile: static_.removeFile,
|
|
417
|
-
getFileUrl: static_.getFileUrl,
|
|
418
|
-
getThumbnailUrl: static_.getThumbnailUrl,
|
|
419
|
-
getPreviewUrl: static_.getPreviewUrl,
|
|
420
|
-
messages: messages_.messages,
|
|
421
|
-
workblocks: messages_.workblocks,
|
|
422
|
-
loading: messages_.loading,
|
|
423
|
-
isLoading: messages_.loading,
|
|
424
|
-
files: files_.files,
|
|
425
|
-
connectionStatus: connection_.connectionStatus,
|
|
426
|
-
status: connection_.connectionStatus,
|
|
427
|
-
error: connection_.error
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
var hasWarnedAboutUseThreadContext = false;
|
|
431
|
-
function useThreadContext() {
|
|
432
|
-
if (!hasWarnedAboutUseThreadContext && process.env.NODE_ENV !== "production") {
|
|
433
|
-
hasWarnedAboutUseThreadContext = true;
|
|
434
|
-
console.warn(
|
|
435
|
-
"[DEPRECATED] useThreadContext() is deprecated.\nUse: const { messages, sendMessage, ... } = useThread()"
|
|
436
|
-
);
|
|
437
|
-
}
|
|
438
|
-
return useThreadContextInternal();
|
|
439
|
-
}
|
|
440
525
|
function useThreadId() {
|
|
441
|
-
return
|
|
526
|
+
return useThreadContext().threadId;
|
|
442
527
|
}
|
|
443
528
|
|
|
444
|
-
// src/
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
529
|
+
// src/utils/workblocks.ts
|
|
530
|
+
function transformToWorkblocks(messages) {
|
|
531
|
+
if (messages.length === 0) {
|
|
532
|
+
return [];
|
|
533
|
+
}
|
|
534
|
+
const result = [];
|
|
535
|
+
let i = 0;
|
|
536
|
+
while (i < messages.length) {
|
|
537
|
+
const message = messages[i];
|
|
538
|
+
if (message.role === "assistant" && message.tool_calls) {
|
|
539
|
+
let toolCalls;
|
|
540
|
+
try {
|
|
541
|
+
toolCalls = JSON.parse(message.tool_calls);
|
|
542
|
+
} catch (error) {
|
|
543
|
+
result.push(message);
|
|
544
|
+
i++;
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
const workItems = [];
|
|
548
|
+
for (const toolCall of toolCalls) {
|
|
549
|
+
workItems.push({
|
|
550
|
+
id: toolCall.id || message.id,
|
|
551
|
+
type: "tool_call",
|
|
552
|
+
name: toolCall.function?.name,
|
|
553
|
+
content: toolCall.function?.arguments || null,
|
|
554
|
+
status: null,
|
|
555
|
+
// Will be updated below based on matching results
|
|
556
|
+
tool_call_id: toolCall.id
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
let j = i + 1;
|
|
560
|
+
while (j < messages.length && messages[j].role === "tool") {
|
|
561
|
+
const toolMessage = messages[j];
|
|
562
|
+
const resultStatus = toolMessage.tool_status || "pending";
|
|
563
|
+
workItems.push({
|
|
564
|
+
id: toolMessage.id,
|
|
565
|
+
type: "tool_result",
|
|
566
|
+
name: toolMessage.name || void 0,
|
|
567
|
+
content: toolMessage.content,
|
|
568
|
+
status: resultStatus,
|
|
569
|
+
tool_call_id: toolMessage.tool_call_id || void 0
|
|
570
|
+
});
|
|
571
|
+
j++;
|
|
572
|
+
}
|
|
573
|
+
for (const item of workItems) {
|
|
574
|
+
if (item.type === "tool_call" && item.tool_call_id) {
|
|
575
|
+
const matchingResult = workItems.find(
|
|
576
|
+
(wi) => wi.type === "tool_result" && wi.tool_call_id === item.tool_call_id
|
|
456
577
|
);
|
|
578
|
+
if (matchingResult) {
|
|
579
|
+
item.status = matchingResult.status;
|
|
580
|
+
} else {
|
|
581
|
+
item.status = "pending";
|
|
582
|
+
}
|
|
457
583
|
}
|
|
458
|
-
return target[prop];
|
|
459
584
|
}
|
|
460
|
-
|
|
585
|
+
let status = "completed";
|
|
586
|
+
if (message.status === "pending") {
|
|
587
|
+
status = "pending";
|
|
588
|
+
} else if (message.status === "failed") {
|
|
589
|
+
status = "failed";
|
|
590
|
+
}
|
|
591
|
+
const workblock = {
|
|
592
|
+
id: message.id,
|
|
593
|
+
type: "workblock",
|
|
594
|
+
content: message.content,
|
|
595
|
+
reasoning_content: message.reasoning_content,
|
|
596
|
+
workItems,
|
|
597
|
+
status,
|
|
598
|
+
created_at: message.created_at,
|
|
599
|
+
depth: message.depth
|
|
600
|
+
};
|
|
601
|
+
result.push(workblock);
|
|
602
|
+
i = j;
|
|
603
|
+
} else {
|
|
604
|
+
result.push(message);
|
|
605
|
+
i++;
|
|
606
|
+
}
|
|
461
607
|
}
|
|
462
|
-
return
|
|
608
|
+
return result;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// src/hooks/useThread.ts
|
|
612
|
+
function useThread(options = {}) {
|
|
613
|
+
const {
|
|
614
|
+
useWorkblocks = true
|
|
615
|
+
} = options;
|
|
616
|
+
const context = useContext(ThreadContext);
|
|
617
|
+
if (!context) {
|
|
618
|
+
throw new Error(
|
|
619
|
+
'useThread must be used within a ThreadProvider. Wrap your component with <ThreadProvider threadId="...">.'
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
const { messages } = context;
|
|
623
|
+
const transformedMessages = useMemo(() => {
|
|
624
|
+
return useWorkblocks ? transformToWorkblocks(messages) : messages;
|
|
625
|
+
}, [messages, useWorkblocks]);
|
|
626
|
+
return transformedMessages;
|
|
463
627
|
}
|
|
464
628
|
function onThreadEvent(type, callback) {
|
|
465
|
-
const
|
|
629
|
+
const context = useContext(ThreadContext);
|
|
630
|
+
if (!context) {
|
|
631
|
+
throw new Error(
|
|
632
|
+
'onThreadEvent must be used within a ThreadProvider. Wrap your component with <ThreadProvider threadId="...">.'
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
const { subscribeToEvent } = context;
|
|
466
636
|
useEffect(() => {
|
|
467
637
|
const unsubscribe = subscribeToEvent(type, callback);
|
|
468
638
|
return unsubscribe;
|
|
469
639
|
}, [subscribeToEvent, type, callback]);
|
|
470
640
|
}
|
|
471
641
|
function useThreadEvent(type) {
|
|
472
|
-
const
|
|
642
|
+
const context = useContext(ThreadContext);
|
|
473
643
|
const [eventData, setEventData] = useState(null);
|
|
644
|
+
if (!context) {
|
|
645
|
+
throw new Error(
|
|
646
|
+
'useThreadEvent must be used within a ThreadProvider. Wrap your component with <ThreadProvider threadId="...">.'
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
const { subscribeToEvent } = context;
|
|
474
650
|
useEffect(() => {
|
|
475
651
|
const unsubscribe = subscribeToEvent(type, (data) => {
|
|
476
652
|
setEventData(data);
|
|
@@ -479,14 +655,7 @@ function useThreadEvent(type) {
|
|
|
479
655
|
}, [subscribeToEvent, type]);
|
|
480
656
|
return eventData;
|
|
481
657
|
}
|
|
482
|
-
var hasWarned = false;
|
|
483
658
|
function useSendMessage() {
|
|
484
|
-
if (!hasWarned && process.env.NODE_ENV !== "production") {
|
|
485
|
-
hasWarned = true;
|
|
486
|
-
console.warn(
|
|
487
|
-
'[DEPRECATED] useSendMessage() is deprecated.\nUse: const { sendMessage } = useThread()\nThen: await sendMessage({ role: "user", content: "Hello!" })'
|
|
488
|
-
);
|
|
489
|
-
}
|
|
490
659
|
let context;
|
|
491
660
|
try {
|
|
492
661
|
context = useThreadContext();
|
|
@@ -501,14 +670,7 @@ function useSendMessage() {
|
|
|
501
670
|
[threadId]
|
|
502
671
|
);
|
|
503
672
|
}
|
|
504
|
-
var hasWarned2 = false;
|
|
505
673
|
function useStopThread() {
|
|
506
|
-
if (!hasWarned2 && process.env.NODE_ENV !== "production") {
|
|
507
|
-
hasWarned2 = true;
|
|
508
|
-
console.warn(
|
|
509
|
-
"[DEPRECATED] useStopThread() is deprecated.\nUse: const { stopExecution } = useThread()"
|
|
510
|
-
);
|
|
511
|
-
}
|
|
512
674
|
let context;
|
|
513
675
|
try {
|
|
514
676
|
context = useThreadContext();
|