@nextclaw/ui 0.6.1 → 0.6.2
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/CHANGELOG.md +6 -0
- package/dist/assets/{ChannelsList-CkCpHSto.js → ChannelsList-Bga6n85j.js} +1 -1
- package/dist/assets/ChatPage-B-Yk3kkv.js +32 -0
- package/dist/assets/{DocBrowser-B5Aqiz6W.js → DocBrowser-dv57PRp5.js} +1 -1
- package/dist/assets/{MarketplacePage-BIi0bBdW.js → MarketplacePage-j6p73Hjo.js} +1 -1
- package/dist/assets/{ModelConfig-BTFiEAxQ.js → ModelConfig-BiKSDp5h.js} +1 -1
- package/dist/assets/{ProvidersList-cdk1d-G_.js → ProvidersList-B7ZfRUkD.js} +1 -1
- package/dist/assets/{RuntimeConfig-CFqFsXmR.js → RuntimeConfig-Bpt9UNb6.js} +1 -1
- package/dist/assets/{SecretsConfig-CIKasCek.js → SecretsConfig-Ds00G-_O.js} +2 -2
- package/dist/assets/{SessionsConfig-mnCLFtbo.js → SessionsConfig-Mjet4opU.js} +1 -1
- package/dist/assets/{card-C1BUfR85.js → card-C7JJ5BGA.js} +1 -1
- package/dist/assets/index-BiJ2xs5X.css +1 -0
- package/dist/assets/{index-Dxas8MJ9.js → index-Cb9xiqC5.js} +2 -2
- package/dist/assets/{label-CwWfYbuj.js → label-DHJKdaUl.js} +1 -1
- package/dist/assets/{logos-DDyjHSEU.js → logos-fPO_amyL.js} +1 -1
- package/dist/assets/{page-layout-DKTRKcHL.js → page-layout-CF0JQsWW.js} +1 -1
- package/dist/assets/{switch-Bi3yeYiC.js → switch-C1hgy-fE.js} +1 -1
- package/dist/assets/{tabs-custom-HZFNZrc0.js → tabs-custom-OyoLf5ZM.js} +1 -1
- package/dist/assets/useConfig-D_G46zbo.js +6 -0
- package/dist/assets/{useConfirmDialog-DwD21HlD.js → useConfirmDialog-_0u6i3cI.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/api/config.ts +77 -12
- package/src/api/types.ts +24 -0
- package/src/components/chat/ChatConversationPanel.tsx +5 -20
- package/src/components/chat/ChatPage.tsx +49 -8
- package/src/components/chat/useChatStreamController.ts +192 -115
- package/src/hooks/useConfig.ts +29 -0
- package/src/hooks/useWebSocket.ts +21 -0
- package/dist/assets/ChatPage-DM4XNsrW.js +0 -32
- package/dist/assets/index-P4YzN9iS.css +0 -1
- package/dist/assets/useConfig-CgzVQTZl.js +0 -6
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import type { Dispatch, MutableRefObject, SetStateAction } from 'react';
|
|
3
|
-
import type { SessionEventView } from '@/api/types';
|
|
4
|
-
import { sendChatTurnStream, stopChatTurn } from '@/api/config';
|
|
3
|
+
import type { ChatRunView, SessionEventView } from '@/api/types';
|
|
4
|
+
import { sendChatTurnStream, stopChatTurn, streamChatRun } from '@/api/config';
|
|
5
5
|
|
|
6
6
|
type PendingChatMessage = {
|
|
7
7
|
id: number;
|
|
@@ -17,7 +17,7 @@ type PendingChatMessage = {
|
|
|
17
17
|
type ActiveRunState = {
|
|
18
18
|
localRunId: number;
|
|
19
19
|
sessionKey: string;
|
|
20
|
-
agentId
|
|
20
|
+
agentId?: string;
|
|
21
21
|
requestAbortController: AbortController;
|
|
22
22
|
backendRunId?: string;
|
|
23
23
|
backendStopSupported: boolean;
|
|
@@ -141,62 +141,86 @@ async function refetchIfSessionVisible(params: {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
function upsertStreamingEvent(
|
|
145
|
+
setStreamingSessionEvents: Dispatch<SetStateAction<SessionEventView[]>>,
|
|
146
|
+
event: SessionEventView
|
|
147
|
+
) {
|
|
148
|
+
setStreamingSessionEvents((prev) => {
|
|
149
|
+
const next = [...prev];
|
|
150
|
+
const hit = next.findIndex((streamEvent) => streamEvent.seq === event.seq);
|
|
151
|
+
if (hit >= 0) {
|
|
152
|
+
next[hit] = event;
|
|
153
|
+
} else {
|
|
154
|
+
next.push(event);
|
|
155
|
+
}
|
|
156
|
+
return next;
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
type ExecuteStreamRunParams = {
|
|
146
161
|
runId: number;
|
|
147
162
|
runIdRef: MutableRefObject<number>;
|
|
148
163
|
activeRunRef: MutableRefObject<ActiveRunState | null>;
|
|
149
|
-
nextOptimisticUserSeq: number;
|
|
150
164
|
selectedSessionKeyRef: MutableRefObject<string | null>;
|
|
151
165
|
setSelectedSessionKey: Dispatch<SetStateAction<string | null>>;
|
|
152
166
|
setDraft: Dispatch<SetStateAction<string>>;
|
|
153
167
|
refetchSessions: () => Promise<unknown>;
|
|
154
168
|
refetchHistory: () => Promise<unknown>;
|
|
155
169
|
restoreDraftOnError?: boolean;
|
|
170
|
+
sourceSessionKey: string;
|
|
171
|
+
sourceAgentId?: string;
|
|
172
|
+
sourceMessage?: string;
|
|
173
|
+
sourceStopSupported?: boolean;
|
|
174
|
+
sourceStopReason?: string;
|
|
175
|
+
optimisticUserEvent: SessionEventView | null;
|
|
176
|
+
openStream: (params: {
|
|
177
|
+
signal: AbortSignal;
|
|
178
|
+
onReady: (event: { runId?: string; stopSupported?: boolean; stopReason?: string; sessionKey: string }) => void;
|
|
179
|
+
onDelta: (event: { delta: string }) => void;
|
|
180
|
+
onSessionEvent: (event: { data: SessionEventView }) => void;
|
|
181
|
+
}) => Promise<{ sessionKey: string }>;
|
|
156
182
|
setters: StreamSetters;
|
|
157
|
-
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
async function executeStreamRun(params: ExecuteStreamRunParams): Promise<void> {
|
|
158
186
|
const {
|
|
159
|
-
item,
|
|
160
187
|
runId,
|
|
161
188
|
runIdRef,
|
|
162
189
|
activeRunRef,
|
|
163
|
-
nextOptimisticUserSeq,
|
|
164
190
|
selectedSessionKeyRef,
|
|
165
191
|
setSelectedSessionKey,
|
|
166
192
|
setDraft,
|
|
167
193
|
refetchSessions,
|
|
168
194
|
refetchHistory,
|
|
169
195
|
restoreDraftOnError,
|
|
196
|
+
sourceSessionKey,
|
|
197
|
+
sourceAgentId,
|
|
198
|
+
sourceMessage,
|
|
199
|
+
sourceStopSupported,
|
|
200
|
+
sourceStopReason,
|
|
201
|
+
optimisticUserEvent,
|
|
202
|
+
openStream,
|
|
170
203
|
setters
|
|
171
204
|
} = params;
|
|
172
205
|
|
|
173
206
|
const requestAbortController = new AbortController();
|
|
174
207
|
activeRunRef.current = {
|
|
175
208
|
localRunId: runId,
|
|
176
|
-
sessionKey:
|
|
177
|
-
agentId:
|
|
209
|
+
sessionKey: sourceSessionKey,
|
|
210
|
+
...(sourceAgentId ? { agentId: sourceAgentId } : {}),
|
|
178
211
|
requestAbortController,
|
|
179
|
-
backendStopSupported: Boolean(
|
|
180
|
-
...(
|
|
212
|
+
backendStopSupported: Boolean(sourceStopSupported),
|
|
213
|
+
...(sourceStopReason ? { backendStopReason: sourceStopReason } : {})
|
|
181
214
|
};
|
|
182
215
|
|
|
183
216
|
setters.setStreamingSessionEvents([]);
|
|
184
217
|
setters.setStreamingAssistantText('');
|
|
185
218
|
setters.setStreamingAssistantTimestamp(null);
|
|
186
|
-
setters.setOptimisticUserEvent(
|
|
187
|
-
seq: nextOptimisticUserSeq,
|
|
188
|
-
type: 'message.user.optimistic',
|
|
189
|
-
timestamp: new Date().toISOString(),
|
|
190
|
-
message: {
|
|
191
|
-
role: 'user',
|
|
192
|
-
content: item.message,
|
|
193
|
-
timestamp: new Date().toISOString()
|
|
194
|
-
}
|
|
195
|
-
});
|
|
219
|
+
setters.setOptimisticUserEvent(optimisticUserEvent);
|
|
196
220
|
setters.setIsSending(true);
|
|
197
221
|
setters.setIsAwaitingAssistantOutput(true);
|
|
198
222
|
setters.setCanStopCurrentRun(false);
|
|
199
|
-
setters.setStopDisabledReason(
|
|
223
|
+
setters.setStopDisabledReason(sourceStopSupported ? '__preparing__' : sourceStopReason ?? null);
|
|
200
224
|
setters.setLastSendError(null);
|
|
201
225
|
|
|
202
226
|
let streamText = '';
|
|
@@ -205,96 +229,69 @@ async function executeSendRun(params: {
|
|
|
205
229
|
const streamTimestamp = new Date().toISOString();
|
|
206
230
|
setters.setStreamingAssistantTimestamp(streamTimestamp);
|
|
207
231
|
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
{
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
: {}),
|
|
222
|
-
channel: 'ui',
|
|
223
|
-
chatId: 'web-ui'
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
signal: requestAbortController.signal,
|
|
227
|
-
onReady: (event) => {
|
|
228
|
-
if (runId !== runIdRef.current) {
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
const activeRun = activeRunRef.current;
|
|
232
|
-
if (activeRun && activeRun.localRunId === runId) {
|
|
233
|
-
activeRun.backendRunId = event.runId?.trim() || undefined;
|
|
234
|
-
if (typeof event.stopSupported === 'boolean') {
|
|
235
|
-
activeRun.backendStopSupported = event.stopSupported;
|
|
236
|
-
}
|
|
237
|
-
if (typeof event.stopReason === 'string' && event.stopReason.trim().length > 0) {
|
|
238
|
-
activeRun.backendStopReason = event.stopReason.trim();
|
|
239
|
-
}
|
|
240
|
-
const canStopNow = Boolean(activeRun.backendStopSupported && activeRun.backendRunId);
|
|
241
|
-
setters.setCanStopCurrentRun(canStopNow);
|
|
242
|
-
setters.setStopDisabledReason(
|
|
243
|
-
canStopNow
|
|
244
|
-
? null
|
|
245
|
-
: activeRun.backendStopReason ?? (activeRun.backendStopSupported ? '__preparing__' : null)
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
if (event.sessionKey) {
|
|
249
|
-
setSelectedSessionKey((prev) => (prev === event.sessionKey ? prev : event.sessionKey));
|
|
232
|
+
const result = await openStream({
|
|
233
|
+
signal: requestAbortController.signal,
|
|
234
|
+
onReady: (event) => {
|
|
235
|
+
if (runId !== runIdRef.current) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const activeRun = activeRunRef.current;
|
|
239
|
+
if (activeRun && activeRun.localRunId === runId) {
|
|
240
|
+
activeRun.backendRunId = event.runId?.trim() || undefined;
|
|
241
|
+
if (typeof event.stopSupported === 'boolean') {
|
|
242
|
+
activeRun.backendStopSupported = event.stopSupported;
|
|
250
243
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if (runId !== runIdRef.current) {
|
|
254
|
-
return;
|
|
244
|
+
if (typeof event.stopReason === 'string' && event.stopReason.trim().length > 0) {
|
|
245
|
+
activeRun.backendStopReason = event.stopReason.trim();
|
|
255
246
|
}
|
|
256
|
-
|
|
257
|
-
setters.
|
|
247
|
+
const canStopNow = Boolean(activeRun.backendStopSupported && activeRun.backendRunId);
|
|
248
|
+
setters.setCanStopCurrentRun(canStopNow);
|
|
249
|
+
setters.setStopDisabledReason(
|
|
250
|
+
canStopNow
|
|
251
|
+
? null
|
|
252
|
+
: activeRun.backendStopReason ?? (activeRun.backendStopSupported ? '__preparing__' : null)
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
if (event.sessionKey) {
|
|
256
|
+
setSelectedSessionKey((prev) => (prev === event.sessionKey ? prev : event.sessionKey));
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
onDelta: (event) => {
|
|
260
|
+
if (runId !== runIdRef.current) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
streamText += event.delta;
|
|
264
|
+
setters.setStreamingAssistantText(streamText);
|
|
265
|
+
setters.setIsAwaitingAssistantOutput(false);
|
|
266
|
+
},
|
|
267
|
+
onSessionEvent: (event) => {
|
|
268
|
+
if (runId !== runIdRef.current) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (event.data.message?.role === 'user') {
|
|
272
|
+
setters.setOptimisticUserEvent(null);
|
|
273
|
+
}
|
|
274
|
+
upsertStreamingEvent(setters.setStreamingSessionEvents, event.data);
|
|
275
|
+
if (event.data.message?.role === 'assistant') {
|
|
276
|
+
hasAssistantSessionEvent = true;
|
|
277
|
+
streamText = '';
|
|
278
|
+
setters.setStreamingAssistantText('');
|
|
258
279
|
setters.setIsAwaitingAssistantOutput(false);
|
|
259
|
-
},
|
|
260
|
-
onSessionEvent: (event) => {
|
|
261
|
-
if (runId !== runIdRef.current) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
if (event.data.message?.role === 'user') {
|
|
265
|
-
setters.setOptimisticUserEvent(null);
|
|
266
|
-
}
|
|
267
|
-
setters.setStreamingSessionEvents((prev) => {
|
|
268
|
-
const next = [...prev];
|
|
269
|
-
const hit = next.findIndex((streamEvent) => streamEvent.seq === event.data.seq);
|
|
270
|
-
if (hit >= 0) {
|
|
271
|
-
next[hit] = event.data;
|
|
272
|
-
} else {
|
|
273
|
-
next.push(event.data);
|
|
274
|
-
}
|
|
275
|
-
return next;
|
|
276
|
-
});
|
|
277
|
-
if (event.data.message?.role === 'assistant') {
|
|
278
|
-
hasAssistantSessionEvent = true;
|
|
279
|
-
streamText = '';
|
|
280
|
-
setters.setStreamingAssistantText('');
|
|
281
|
-
setters.setIsAwaitingAssistantOutput(false);
|
|
282
|
-
}
|
|
283
280
|
}
|
|
284
281
|
}
|
|
285
|
-
);
|
|
282
|
+
});
|
|
286
283
|
if (runId !== runIdRef.current) {
|
|
287
284
|
return;
|
|
288
285
|
}
|
|
289
286
|
setters.setOptimisticUserEvent(null);
|
|
290
|
-
if (result.sessionKey !==
|
|
287
|
+
if (result.sessionKey !== sourceSessionKey) {
|
|
291
288
|
setSelectedSessionKey(result.sessionKey);
|
|
292
289
|
}
|
|
293
290
|
|
|
294
291
|
const localAssistantText = !hasAssistantSessionEvent ? streamText.trim() : '';
|
|
295
292
|
await refetchIfSessionVisible({
|
|
296
293
|
selectedSessionKeyRef,
|
|
297
|
-
currentSessionKey:
|
|
294
|
+
currentSessionKey: sourceSessionKey,
|
|
298
295
|
resultSessionKey: result.sessionKey,
|
|
299
296
|
refetchSessions,
|
|
300
297
|
refetchHistory
|
|
@@ -317,23 +314,14 @@ async function executeSendRun(params: {
|
|
|
317
314
|
const wasAborted = requestAbortController.signal.aborted || isAbortLikeError(error);
|
|
318
315
|
runIdRef.current += 1;
|
|
319
316
|
if (wasAborted) {
|
|
320
|
-
|
|
321
|
-
setters.setOptimisticUserEvent(null);
|
|
322
|
-
setters.setStreamingAssistantText('');
|
|
323
|
-
setters.setStreamingAssistantTimestamp(null);
|
|
324
|
-
setters.setIsSending(false);
|
|
325
|
-
setters.setIsAwaitingAssistantOutput(false);
|
|
326
|
-
setters.setCanStopCurrentRun(false);
|
|
327
|
-
setters.setStopDisabledReason(null);
|
|
328
|
-
setters.setLastSendError(null);
|
|
317
|
+
clearStreamingState(setters);
|
|
329
318
|
activeRunRef.current = null;
|
|
330
319
|
await refetchIfSessionVisible({
|
|
331
320
|
selectedSessionKeyRef,
|
|
332
|
-
currentSessionKey:
|
|
321
|
+
currentSessionKey: sourceSessionKey,
|
|
333
322
|
refetchSessions,
|
|
334
323
|
refetchHistory
|
|
335
324
|
});
|
|
336
|
-
setters.setStreamingSessionEvents(localAssistantText ? [buildLocalAssistantEvent(localAssistantText)] : []);
|
|
337
325
|
return;
|
|
338
326
|
}
|
|
339
327
|
|
|
@@ -343,7 +331,7 @@ async function executeSendRun(params: {
|
|
|
343
331
|
setters.setStreamingSessionEvents([buildLocalAssistantEvent(sendError, 'message.assistant.error.local')]);
|
|
344
332
|
activeRunRef.current = null;
|
|
345
333
|
if (restoreDraftOnError) {
|
|
346
|
-
setDraft((prev) => (prev.trim().length === 0 ?
|
|
334
|
+
setDraft((prev) => (prev.trim().length === 0 && sourceMessage ? sourceMessage : prev));
|
|
347
335
|
}
|
|
348
336
|
}
|
|
349
337
|
}
|
|
@@ -394,18 +382,51 @@ export function useChatStreamController(params: UseChatStreamControllerParams) {
|
|
|
394
382
|
async (item: PendingChatMessage, options?: { restoreDraftOnError?: boolean }) => {
|
|
395
383
|
setLastSendError(null);
|
|
396
384
|
streamRunIdRef.current += 1;
|
|
397
|
-
|
|
398
|
-
|
|
385
|
+
const requestedSkills = normalizeRequestedSkills(item.requestedSkills);
|
|
386
|
+
await executeStreamRun({
|
|
399
387
|
runId: streamRunIdRef.current,
|
|
400
388
|
runIdRef: streamRunIdRef,
|
|
401
389
|
activeRunRef,
|
|
402
|
-
nextOptimisticUserSeq: params.nextOptimisticUserSeq,
|
|
403
390
|
selectedSessionKeyRef: params.selectedSessionKeyRef,
|
|
404
391
|
setSelectedSessionKey: params.setSelectedSessionKey,
|
|
405
392
|
setDraft: params.setDraft,
|
|
406
393
|
refetchSessions: params.refetchSessions,
|
|
407
394
|
refetchHistory: params.refetchHistory,
|
|
408
395
|
restoreDraftOnError: options?.restoreDraftOnError,
|
|
396
|
+
sourceSessionKey: item.sessionKey,
|
|
397
|
+
sourceAgentId: item.agentId,
|
|
398
|
+
sourceMessage: item.message,
|
|
399
|
+
sourceStopSupported: item.stopSupported,
|
|
400
|
+
sourceStopReason: item.stopReason,
|
|
401
|
+
optimisticUserEvent: {
|
|
402
|
+
seq: params.nextOptimisticUserSeq,
|
|
403
|
+
type: 'message.user.optimistic',
|
|
404
|
+
timestamp: new Date().toISOString(),
|
|
405
|
+
message: {
|
|
406
|
+
role: 'user',
|
|
407
|
+
content: item.message,
|
|
408
|
+
timestamp: new Date().toISOString()
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
openStream: ({ signal, onReady, onDelta, onSessionEvent }) =>
|
|
412
|
+
sendChatTurnStream(
|
|
413
|
+
{
|
|
414
|
+
message: item.message,
|
|
415
|
+
sessionKey: item.sessionKey,
|
|
416
|
+
agentId: item.agentId,
|
|
417
|
+
...(item.model ? { model: item.model } : {}),
|
|
418
|
+
...(requestedSkills.length > 0
|
|
419
|
+
? {
|
|
420
|
+
metadata: {
|
|
421
|
+
requested_skills: requestedSkills
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
: {}),
|
|
425
|
+
channel: 'ui',
|
|
426
|
+
chatId: 'web-ui'
|
|
427
|
+
},
|
|
428
|
+
{ signal, onReady, onDelta, onSessionEvent }
|
|
429
|
+
),
|
|
409
430
|
setters: {
|
|
410
431
|
setOptimisticUserEvent,
|
|
411
432
|
setStreamingSessionEvents,
|
|
@@ -422,6 +443,60 @@ export function useChatStreamController(params: UseChatStreamControllerParams) {
|
|
|
422
443
|
[params]
|
|
423
444
|
);
|
|
424
445
|
|
|
446
|
+
const resumeRun = useCallback(
|
|
447
|
+
async (run: ChatRunView) => {
|
|
448
|
+
const runId = run.runId?.trim();
|
|
449
|
+
const sessionKey = run.sessionKey?.trim();
|
|
450
|
+
if (!runId || !sessionKey) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
const active = activeRunRef.current;
|
|
454
|
+
if (active?.backendRunId === runId) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
if (isSending && active) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
setLastSendError(null);
|
|
462
|
+
streamRunIdRef.current += 1;
|
|
463
|
+
await executeStreamRun({
|
|
464
|
+
runId: streamRunIdRef.current,
|
|
465
|
+
runIdRef: streamRunIdRef,
|
|
466
|
+
activeRunRef,
|
|
467
|
+
selectedSessionKeyRef: params.selectedSessionKeyRef,
|
|
468
|
+
setSelectedSessionKey: params.setSelectedSessionKey,
|
|
469
|
+
setDraft: params.setDraft,
|
|
470
|
+
refetchSessions: params.refetchSessions,
|
|
471
|
+
refetchHistory: params.refetchHistory,
|
|
472
|
+
sourceSessionKey: sessionKey,
|
|
473
|
+
sourceAgentId: run.agentId,
|
|
474
|
+
sourceStopSupported: run.stopSupported,
|
|
475
|
+
sourceStopReason: run.stopReason,
|
|
476
|
+
optimisticUserEvent: null,
|
|
477
|
+
openStream: ({ signal, onReady, onDelta, onSessionEvent }) =>
|
|
478
|
+
streamChatRun(
|
|
479
|
+
{
|
|
480
|
+
runId
|
|
481
|
+
},
|
|
482
|
+
{ signal, onReady, onDelta, onSessionEvent }
|
|
483
|
+
),
|
|
484
|
+
setters: {
|
|
485
|
+
setOptimisticUserEvent,
|
|
486
|
+
setStreamingSessionEvents,
|
|
487
|
+
setStreamingAssistantText,
|
|
488
|
+
setStreamingAssistantTimestamp,
|
|
489
|
+
setIsSending,
|
|
490
|
+
setIsAwaitingAssistantOutput,
|
|
491
|
+
setCanStopCurrentRun,
|
|
492
|
+
setStopDisabledReason,
|
|
493
|
+
setLastSendError
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
},
|
|
497
|
+
[isSending, params]
|
|
498
|
+
);
|
|
499
|
+
|
|
425
500
|
useEffect(() => {
|
|
426
501
|
if (isSending || queuedMessages.length === 0) {
|
|
427
502
|
return;
|
|
@@ -472,7 +547,7 @@ export function useChatStreamController(params: UseChatStreamControllerParams) {
|
|
|
472
547
|
await stopChatTurn({
|
|
473
548
|
runId: activeRun.backendRunId,
|
|
474
549
|
sessionKey: activeRun.sessionKey,
|
|
475
|
-
agentId: activeRun.agentId
|
|
550
|
+
...(activeRun.agentId ? { agentId: activeRun.agentId } : {})
|
|
476
551
|
});
|
|
477
552
|
} catch {
|
|
478
553
|
// Keep local abort as fallback even if stop API fails.
|
|
@@ -492,7 +567,9 @@ export function useChatStreamController(params: UseChatStreamControllerParams) {
|
|
|
492
567
|
canStopCurrentRun,
|
|
493
568
|
stopDisabledReason,
|
|
494
569
|
lastSendError,
|
|
570
|
+
activeBackendRunId: activeRunRef.current?.backendRunId ?? null,
|
|
495
571
|
sendMessage,
|
|
572
|
+
resumeRun,
|
|
496
573
|
stopCurrentRun,
|
|
497
574
|
resetStreamState
|
|
498
575
|
};
|
package/src/hooks/useConfig.ts
CHANGED
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
updateSession,
|
|
18
18
|
deleteSession,
|
|
19
19
|
sendChatTurn,
|
|
20
|
+
fetchChatRun,
|
|
21
|
+
fetchChatRuns,
|
|
20
22
|
fetchChatCapabilities,
|
|
21
23
|
fetchCronJobs,
|
|
22
24
|
deleteCronJob,
|
|
@@ -260,6 +262,33 @@ export function useChatCapabilities(params?: { sessionKey?: string | null; agent
|
|
|
260
262
|
});
|
|
261
263
|
}
|
|
262
264
|
|
|
265
|
+
export function useChatRuns(params?: { sessionKey?: string | null; states?: Array<'queued' | 'running' | 'completed' | 'failed' | 'aborted'>; limit?: number }) {
|
|
266
|
+
const sessionKey = params?.sessionKey?.trim() || undefined;
|
|
267
|
+
const states = Array.isArray(params?.states) && params.states.length > 0 ? params.states : undefined;
|
|
268
|
+
return useQuery({
|
|
269
|
+
queryKey: ['chat-runs', sessionKey ?? null, states ?? null, params?.limit ?? null],
|
|
270
|
+
queryFn: () => fetchChatRuns({
|
|
271
|
+
...(sessionKey ? { sessionKey } : {}),
|
|
272
|
+
...(states ? { states } : {}),
|
|
273
|
+
...(typeof params?.limit === 'number' ? { limit: params.limit } : {})
|
|
274
|
+
}),
|
|
275
|
+
enabled: Boolean(sessionKey) || Boolean(states),
|
|
276
|
+
staleTime: 5_000,
|
|
277
|
+
retry: false
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export function useChatRun(runId: string | null) {
|
|
282
|
+
const normalizedRunId = runId?.trim() || null;
|
|
283
|
+
return useQuery({
|
|
284
|
+
queryKey: ['chat-run', normalizedRunId],
|
|
285
|
+
queryFn: () => fetchChatRun(normalizedRunId as string),
|
|
286
|
+
enabled: Boolean(normalizedRunId),
|
|
287
|
+
staleTime: 5_000,
|
|
288
|
+
retry: false
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
263
292
|
export function useCronJobs(params: { all?: boolean } = { all: true }) {
|
|
264
293
|
return useQuery({
|
|
265
294
|
queryKey: ['cron', params],
|
|
@@ -49,6 +49,27 @@ export function useWebSocket(queryClient?: QueryClient) {
|
|
|
49
49
|
}
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
+
client.on('run.updated', (event) => {
|
|
53
|
+
if (event.type !== 'run.updated') {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (!queryClient) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const sessionKey = event.payload.run.sessionKey;
|
|
60
|
+
const runId = event.payload.run.runId;
|
|
61
|
+
queryClient.invalidateQueries({ queryKey: ['chat-runs'] });
|
|
62
|
+
if (sessionKey) {
|
|
63
|
+
queryClient.invalidateQueries({ queryKey: ['sessions'] });
|
|
64
|
+
queryClient.invalidateQueries({ queryKey: ['session-history', sessionKey] });
|
|
65
|
+
} else {
|
|
66
|
+
queryClient.invalidateQueries({ queryKey: ['session-history'] });
|
|
67
|
+
}
|
|
68
|
+
if (runId) {
|
|
69
|
+
queryClient.invalidateQueries({ queryKey: ['chat-run', runId] });
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
52
73
|
client.on('error', (event) => {
|
|
53
74
|
if (event.type === 'error') {
|
|
54
75
|
console.error('WebSocket error:', event.payload.message);
|