@nextclaw/ui 0.6.9 → 0.6.11
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/.eslintrc.cjs +10 -0
- package/CHANGELOG.md +15 -0
- package/dist/assets/{ChannelsList-DACqpUYZ.js → ChannelsList-C49JQ-Zt.js} +1 -1
- package/dist/assets/ChatPage-DIx05c6s.js +36 -0
- package/dist/assets/{DocBrowser-D7mjKkGe.js → DocBrowser-CpOosDEI.js} +1 -1
- package/dist/assets/{LogoBadge-BlDT-g9R.js → LogoBadge-CL_8ZPXU.js} +1 -1
- package/dist/assets/MarketplacePage-BOzko5s9.js +49 -0
- package/dist/assets/{ModelConfig-DwRU5qrw.js → ModelConfig-BZ4ZfaQB.js} +1 -1
- package/dist/assets/ProvidersList-fPpJ5gl6.js +1 -0
- package/dist/assets/{RuntimeConfig-C7BRLGSC.js → RuntimeConfig-Dt9pLB9P.js} +1 -1
- package/dist/assets/{SecretsConfig-D5xZh7VF.js → SecretsConfig-C1PU0Yy8.js} +2 -2
- package/dist/assets/{SessionsConfig-ovpj_otA.js → SessionsConfig-EskBOofQ.js} +2 -2
- package/dist/assets/{card-Bf4CtrW8.js → card-C7Gtw2Vs.js} +1 -1
- package/dist/assets/index-Cn6_2To7.js +8 -0
- package/dist/assets/index-nEYGCJTC.css +1 -0
- package/dist/assets/{input-CaKJyoWZ.js → input-oBvxsnV9.js} +1 -1
- package/dist/assets/{label-BaXSWTKI.js → label-C7F8lMpQ.js} +1 -1
- package/dist/assets/{page-layout-DA6PFRtQ.js → page-layout-DO8BlScF.js} +1 -1
- package/dist/assets/session-run-status-Kg0FwAPn.js +3 -0
- package/dist/assets/{switch-Cvd5wZs-.js → switch-C6a5GyZB.js} +1 -1
- package/dist/assets/{tabs-custom-0PybLkXs.js → tabs-custom-BatFap5k.js} +1 -1
- package/dist/assets/{useConfirmDialog-DdtpSju1.js → useConfirmDialog-zJzVKMdu.js} +2 -2
- package/dist/assets/{vendor-C--HHaLf.js → vendor-TlME1INH.js} +84 -84
- package/dist/index.html +3 -3
- package/package.json +4 -2
- package/src/App.tsx +1 -2
- package/src/api/config.ts +205 -202
- package/src/api/types.ts +54 -24
- package/src/components/chat/ChatConversationPanel.tsx +102 -121
- package/src/components/chat/ChatPage.tsx +165 -437
- package/src/components/chat/ChatSidebar.tsx +30 -36
- package/src/components/chat/ChatThread.tsx +73 -131
- package/src/components/chat/chat-input/ChatInputBarView.tsx +82 -0
- package/src/components/chat/chat-input/ChatInputBottomToolbar.tsx +71 -0
- package/src/components/chat/chat-input/components/ChatInputModelStateHint.tsx +39 -0
- package/src/components/chat/chat-input/components/ChatInputSelectedSkillsSection.tsx +31 -0
- package/src/components/chat/chat-input/components/ChatInputSlashPanelSection.tsx +112 -0
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputAttachButton.tsx +24 -0
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputModelSelector.tsx +58 -0
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputSendControls.tsx +56 -0
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputSessionTypeSelector.tsx +40 -0
- package/src/components/chat/chat-input/useChatInputBarController.ts +313 -0
- package/src/components/chat/chat-input.types.ts +15 -0
- package/src/components/chat/chat-page-data.ts +121 -0
- package/src/components/chat/chat-page-runtime.ts +221 -0
- package/src/components/chat/chat-session-route.ts +59 -0
- package/src/components/chat/chat-stream/nextbot-parsers.ts +52 -0
- package/src/components/chat/chat-stream/nextbot-runtime-agent.ts +413 -0
- package/src/components/chat/chat-stream/stream-event-adapter.ts +98 -0
- package/src/components/chat/chat-stream/transport.ts +159 -0
- package/src/components/chat/chat-stream/types.ts +76 -0
- package/src/components/chat/managers/chat-input.manager.ts +142 -0
- package/src/components/chat/managers/chat-run-status.manager.ts +32 -0
- package/src/components/chat/managers/chat-session-list.manager.ts +77 -0
- package/src/components/chat/managers/chat-stream-actions.manager.ts +34 -0
- package/src/components/chat/managers/chat-thread.manager.ts +86 -0
- package/src/components/chat/managers/chat-ui.manager.ts +65 -0
- package/src/components/chat/presenter/chat-presenter-context.tsx +25 -0
- package/src/components/chat/presenter/chat.presenter.ts +32 -0
- package/src/components/chat/stores/chat-input.store.ts +62 -0
- package/src/components/chat/stores/chat-run-status.store.ts +30 -0
- package/src/components/chat/stores/chat-session-list.store.ts +34 -0
- package/src/components/chat/stores/chat-thread.store.ts +52 -0
- package/src/components/chat/useChatRuntimeController.ts +134 -0
- package/src/components/chat/useChatSessionTypeState.ts +148 -0
- package/src/components/common/MaskedInput.tsx +1 -1
- package/src/components/config/ProviderForm.tsx +221 -14
- package/src/hooks/useConfig.ts +33 -2
- package/src/hooks/useObservable.ts +20 -0
- package/src/hooks/useWebSocket.ts +23 -1
- package/src/lib/chat-message.ts +2 -202
- package/src/lib/chat-runtime-utils.ts +250 -0
- package/src/lib/i18n.ts +11 -0
- package/tsconfig.json +2 -1
- package/vite.config.ts +2 -1
- package/dist/assets/ChatPage-iji0RkTR.js +0 -34
- package/dist/assets/MarketplacePage-CZq3jVgg.js +0 -49
- package/dist/assets/ProvidersList-DFxN3pjx.js +0 -1
- package/dist/assets/index-C_DhisNo.css +0 -1
- package/dist/assets/index-dKTqKCJo.js +0 -7
- package/dist/assets/session-run-status-CllIZxNf.js +0 -5
- package/src/components/chat/ChatInputBar.tsx +0 -590
- package/src/components/chat/useChatStreamController.ts +0 -591
package/dist/index.html
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>NextClaw - 系统配置</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-Cn6_2To7.js"></script>
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-TlME1INH.js">
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/index-nEYGCJTC.css">
|
|
12
12
|
</head>
|
|
13
13
|
|
|
14
14
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/ui",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.11",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,10 +24,12 @@
|
|
|
24
24
|
"react-router-dom": "^7.13.0",
|
|
25
25
|
"rehype-sanitize": "^6.0.0",
|
|
26
26
|
"remark-gfm": "^4.0.1",
|
|
27
|
+
"rxjs": "^7.8.2",
|
|
27
28
|
"sonner": "^1.7.1",
|
|
28
29
|
"tailwind-merge": "^2.5.4",
|
|
29
30
|
"zod": "^3.23.8",
|
|
30
|
-
"zustand": "^5.0.2"
|
|
31
|
+
"zustand": "^5.0.2",
|
|
32
|
+
"@nextclaw/agent-chat": "0.1.1"
|
|
31
33
|
},
|
|
32
34
|
"devDependencies": {
|
|
33
35
|
"@types/react": "^18.3.12",
|
package/src/App.tsx
CHANGED
|
@@ -41,8 +41,7 @@ function AppContent() {
|
|
|
41
41
|
<Routes>
|
|
42
42
|
<Route path="/chat/skills" element={<Navigate to="/skills" replace />} />
|
|
43
43
|
<Route path="/chat/cron" element={<Navigate to="/cron" replace />} />
|
|
44
|
-
<Route path="/chat/:sessionId" element={<LazyRoute><ChatPage view="chat" /></LazyRoute>} />
|
|
45
|
-
<Route path="/chat" element={<LazyRoute><ChatPage view="chat" /></LazyRoute>} />
|
|
44
|
+
<Route path="/chat/:sessionId?" element={<LazyRoute><ChatPage view="chat" /></LazyRoute>} />
|
|
46
45
|
<Route path="/skills" element={<LazyRoute><ChatPage view="skills" /></LazyRoute>} />
|
|
47
46
|
<Route path="/cron" element={<LazyRoute><ChatPage view="cron" /></LazyRoute>} />
|
|
48
47
|
<Route path="/model" element={<LazyRoute><ModelConfigPage /></LazyRoute>} />
|
package/src/api/config.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
ProviderConfigUpdate,
|
|
10
10
|
ProviderConnectionTestRequest,
|
|
11
11
|
ProviderConnectionTestResult,
|
|
12
|
+
ProviderAuthStartRequest,
|
|
12
13
|
ProviderAuthStartResult,
|
|
13
14
|
ProviderAuthPollRequest,
|
|
14
15
|
ProviderAuthPollResult,
|
|
@@ -26,7 +27,12 @@ import type {
|
|
|
26
27
|
SessionPatchUpdate,
|
|
27
28
|
ChatTurnRequest,
|
|
28
29
|
ChatTurnView,
|
|
30
|
+
ChatTurnStreamDeltaEvent,
|
|
31
|
+
ChatTurnStreamErrorEvent,
|
|
32
|
+
ChatTurnStreamReadyEvent,
|
|
33
|
+
ChatTurnStreamSessionEvent,
|
|
29
34
|
ChatCapabilitiesView,
|
|
35
|
+
ChatSessionTypesView,
|
|
30
36
|
ChatTurnStopRequest,
|
|
31
37
|
ChatTurnStopResult,
|
|
32
38
|
ChatRunListView,
|
|
@@ -35,10 +41,7 @@ import type {
|
|
|
35
41
|
CronListView,
|
|
36
42
|
CronEnableRequest,
|
|
37
43
|
CronRunRequest,
|
|
38
|
-
CronActionResult
|
|
39
|
-
ChatTurnStreamReadyEvent,
|
|
40
|
-
ChatTurnStreamDeltaEvent,
|
|
41
|
-
ChatTurnStreamSessionEvent
|
|
44
|
+
CronActionResult
|
|
42
45
|
} from './types';
|
|
43
46
|
|
|
44
47
|
// GET /api/app/meta
|
|
@@ -144,10 +147,13 @@ export async function testProviderConnection(
|
|
|
144
147
|
}
|
|
145
148
|
|
|
146
149
|
// POST /api/config/providers/:provider/auth/start
|
|
147
|
-
export async function startProviderAuth(
|
|
150
|
+
export async function startProviderAuth(
|
|
151
|
+
provider: string,
|
|
152
|
+
data: ProviderAuthStartRequest = {}
|
|
153
|
+
): Promise<ProviderAuthStartResult> {
|
|
148
154
|
const response = await api.post<ProviderAuthStartResult>(
|
|
149
155
|
`/api/config/providers/${provider}/auth/start`,
|
|
150
|
-
|
|
156
|
+
data
|
|
151
157
|
);
|
|
152
158
|
if (!response.ok) {
|
|
153
159
|
throw new Error(response.error.message);
|
|
@@ -300,91 +306,24 @@ export async function sendChatTurn(data: ChatTurnRequest): Promise<ChatTurnView>
|
|
|
300
306
|
return response.data;
|
|
301
307
|
}
|
|
302
308
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
if (params?.sessionKey?.trim()) {
|
|
307
|
-
query.set('sessionKey', params.sessionKey.trim());
|
|
308
|
-
}
|
|
309
|
-
if (params?.agentId?.trim()) {
|
|
310
|
-
query.set('agentId', params.agentId.trim());
|
|
311
|
-
}
|
|
312
|
-
const suffix = query.toString();
|
|
313
|
-
const response = await api.get<ChatCapabilitiesView>(suffix ? `/api/chat/capabilities?${suffix}` : '/api/chat/capabilities');
|
|
314
|
-
if (!response.ok) {
|
|
315
|
-
throw new Error(response.error.message);
|
|
316
|
-
}
|
|
317
|
-
return response.data;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// POST /api/chat/turn/stop
|
|
321
|
-
export async function stopChatTurn(data: ChatTurnStopRequest): Promise<ChatTurnStopResult> {
|
|
322
|
-
const response = await api.post<ChatTurnStopResult>('/api/chat/turn/stop', data);
|
|
323
|
-
if (!response.ok) {
|
|
324
|
-
throw new Error(response.error.message);
|
|
325
|
-
}
|
|
326
|
-
return response.data;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// GET /api/chat/runs
|
|
330
|
-
export async function fetchChatRuns(params?: {
|
|
331
|
-
sessionKey?: string;
|
|
332
|
-
states?: ChatRunState[];
|
|
333
|
-
limit?: number;
|
|
334
|
-
}): Promise<ChatRunListView> {
|
|
335
|
-
const query = new URLSearchParams();
|
|
336
|
-
if (params?.sessionKey?.trim()) {
|
|
337
|
-
query.set('sessionKey', params.sessionKey.trim());
|
|
338
|
-
}
|
|
339
|
-
if (Array.isArray(params?.states) && params.states.length > 0) {
|
|
340
|
-
query.set('states', params.states.join(','));
|
|
341
|
-
}
|
|
342
|
-
if (typeof params?.limit === 'number' && Number.isFinite(params.limit)) {
|
|
343
|
-
query.set('limit', String(Math.max(0, Math.trunc(params.limit))));
|
|
344
|
-
}
|
|
345
|
-
const suffix = query.toString();
|
|
346
|
-
const response = await api.get<ChatRunListView>(suffix ? `/api/chat/runs?${suffix}` : '/api/chat/runs');
|
|
347
|
-
if (!response.ok) {
|
|
348
|
-
throw new Error(response.error.message);
|
|
349
|
-
}
|
|
350
|
-
return response.data;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// GET /api/chat/runs/:runId
|
|
354
|
-
export async function fetchChatRun(runId: string): Promise<ChatRunView> {
|
|
355
|
-
const response = await api.get<ChatRunView>(`/api/chat/runs/${encodeURIComponent(runId)}`);
|
|
356
|
-
if (!response.ok) {
|
|
357
|
-
throw new Error(response.error.message);
|
|
358
|
-
}
|
|
359
|
-
return response.data;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
type ChatTurnStreamOptions = {
|
|
363
|
-
signal?: AbortSignal;
|
|
364
|
-
onReady?: (event: ChatTurnStreamReadyEvent) => void;
|
|
365
|
-
onDelta?: (event: ChatTurnStreamDeltaEvent) => void;
|
|
366
|
-
onSessionEvent?: (event: ChatTurnStreamSessionEvent) => void;
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
type SseParsedEvent = {
|
|
370
|
-
event: string;
|
|
371
|
-
data: string;
|
|
372
|
-
};
|
|
373
|
-
|
|
374
|
-
function parseSseFrame(frame: string): SseParsedEvent | null {
|
|
375
|
-
const lines = frame.split(/\r?\n/);
|
|
376
|
-
let event = 'message';
|
|
309
|
+
function parseSseFrame(frame: string): { event: string; data: string } | null {
|
|
310
|
+
const lines = frame.split('\n');
|
|
311
|
+
let event = '';
|
|
377
312
|
const dataLines: string[] = [];
|
|
378
|
-
for (const
|
|
313
|
+
for (const raw of lines) {
|
|
314
|
+
const line = raw.trimEnd();
|
|
315
|
+
if (!line || line.startsWith(':')) {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
379
318
|
if (line.startsWith('event:')) {
|
|
380
|
-
event = line.slice(
|
|
319
|
+
event = line.slice(6).trim();
|
|
381
320
|
continue;
|
|
382
321
|
}
|
|
383
322
|
if (line.startsWith('data:')) {
|
|
384
|
-
dataLines.push(line.slice(
|
|
323
|
+
dataLines.push(line.slice(5).trimStart());
|
|
385
324
|
}
|
|
386
325
|
}
|
|
387
|
-
if (
|
|
326
|
+
if (!event) {
|
|
388
327
|
return null;
|
|
389
328
|
}
|
|
390
329
|
return {
|
|
@@ -393,178 +332,242 @@ function parseSseFrame(frame: string): SseParsedEvent | null {
|
|
|
393
332
|
};
|
|
394
333
|
}
|
|
395
334
|
|
|
396
|
-
async function
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
335
|
+
async function readSseStream(params: {
|
|
336
|
+
path: string;
|
|
337
|
+
method: 'GET' | 'POST';
|
|
338
|
+
body?: unknown;
|
|
339
|
+
signal?: AbortSignal;
|
|
340
|
+
onReady: (event: ChatTurnStreamReadyEvent) => void;
|
|
341
|
+
onDelta: (event: ChatTurnStreamDeltaEvent) => void;
|
|
342
|
+
onSessionEvent: (event: ChatTurnStreamSessionEvent) => void;
|
|
343
|
+
}): Promise<{ sessionKey: string; reply: string }> {
|
|
344
|
+
const response = await fetch(`${API_BASE}${params.path}`, {
|
|
345
|
+
method: params.method,
|
|
346
|
+
headers: {
|
|
347
|
+
'Content-Type': 'application/json',
|
|
348
|
+
Accept: 'text/event-stream'
|
|
349
|
+
},
|
|
350
|
+
...(params.body !== undefined ? { body: JSON.stringify(params.body) } : {}),
|
|
351
|
+
...(params.signal ? { signal: params.signal } : {})
|
|
352
|
+
});
|
|
353
|
+
|
|
400
354
|
if (!response.ok) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
message = payload.error.message;
|
|
406
|
-
}
|
|
407
|
-
} catch {
|
|
408
|
-
const text = await response.text().catch(() => '');
|
|
409
|
-
if (text.trim()) {
|
|
410
|
-
message = text.trim();
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
throw new Error(message);
|
|
355
|
+
const text = await response.text();
|
|
356
|
+
const fallback = `HTTP ${response.status}`;
|
|
357
|
+
const trimmed = text.trim();
|
|
358
|
+
throw new Error(trimmed || fallback);
|
|
414
359
|
}
|
|
415
360
|
|
|
416
|
-
|
|
417
|
-
|
|
361
|
+
const reader = response.body?.getReader();
|
|
362
|
+
if (!reader) {
|
|
363
|
+
throw new Error('SSE response body unavailable');
|
|
418
364
|
}
|
|
419
365
|
|
|
420
|
-
const reader = response.body.getReader();
|
|
421
366
|
const decoder = new TextDecoder();
|
|
422
367
|
let buffer = '';
|
|
423
|
-
let
|
|
368
|
+
let finalResult: { sessionKey: string; reply: string } | null = null;
|
|
369
|
+
let readySessionKey: string | null = null;
|
|
424
370
|
|
|
425
|
-
const
|
|
371
|
+
const consumeFrame = (frame: string) => {
|
|
426
372
|
const parsed = parseSseFrame(frame);
|
|
427
373
|
if (!parsed) {
|
|
428
374
|
return;
|
|
429
375
|
}
|
|
430
|
-
|
|
376
|
+
|
|
377
|
+
let payload: unknown = undefined;
|
|
378
|
+
if (parsed.data) {
|
|
431
379
|
try {
|
|
432
|
-
|
|
433
|
-
sessionKey?: string;
|
|
434
|
-
requestedAt?: string;
|
|
435
|
-
runId?: string;
|
|
436
|
-
stopSupported?: boolean;
|
|
437
|
-
stopReason?: string;
|
|
438
|
-
};
|
|
439
|
-
options.onReady?.({
|
|
440
|
-
event: 'ready',
|
|
441
|
-
sessionKey: String(readyPayload.sessionKey ?? ''),
|
|
442
|
-
requestedAt: String(readyPayload.requestedAt ?? ''),
|
|
443
|
-
...(typeof readyPayload.runId === 'string' && readyPayload.runId.trim().length > 0
|
|
444
|
-
? { runId: readyPayload.runId.trim() }
|
|
445
|
-
: {}),
|
|
446
|
-
...(typeof readyPayload.stopSupported === 'boolean'
|
|
447
|
-
? { stopSupported: readyPayload.stopSupported }
|
|
448
|
-
: {}),
|
|
449
|
-
...(typeof readyPayload.stopReason === 'string' && readyPayload.stopReason.trim().length > 0
|
|
450
|
-
? { stopReason: readyPayload.stopReason.trim() }
|
|
451
|
-
: {})
|
|
452
|
-
});
|
|
380
|
+
payload = JSON.parse(parsed.data);
|
|
453
381
|
} catch {
|
|
454
|
-
|
|
382
|
+
payload = undefined;
|
|
455
383
|
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (parsed.event === 'ready') {
|
|
387
|
+
const ready = (payload ?? {}) as ChatTurnStreamReadyEvent;
|
|
388
|
+
readySessionKey = typeof ready.sessionKey === 'string' && ready.sessionKey.trim() ? ready.sessionKey : readySessionKey;
|
|
389
|
+
params.onReady(ready);
|
|
456
390
|
return;
|
|
457
391
|
}
|
|
392
|
+
|
|
458
393
|
if (parsed.event === 'delta') {
|
|
459
|
-
|
|
460
|
-
const deltaPayload = JSON.parse(parsed.data) as { delta?: string };
|
|
461
|
-
if (typeof deltaPayload.delta === 'string' && deltaPayload.delta.length > 0) {
|
|
462
|
-
options.onDelta?.({
|
|
463
|
-
event: 'delta',
|
|
464
|
-
delta: deltaPayload.delta
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
} catch {
|
|
468
|
-
// ignore malformed delta event payload
|
|
469
|
-
}
|
|
394
|
+
params.onDelta((payload ?? { delta: '' }) as ChatTurnStreamDeltaEvent);
|
|
470
395
|
return;
|
|
471
396
|
}
|
|
397
|
+
|
|
472
398
|
if (parsed.event === 'session_event') {
|
|
473
|
-
|
|
474
|
-
options.onSessionEvent?.({
|
|
475
|
-
event: 'session_event',
|
|
476
|
-
data: JSON.parse(parsed.data)
|
|
477
|
-
});
|
|
478
|
-
} catch {
|
|
479
|
-
// ignore malformed session_event payload
|
|
480
|
-
}
|
|
399
|
+
params.onSessionEvent({ data: payload as ChatTurnStreamSessionEvent['data'] });
|
|
481
400
|
return;
|
|
482
401
|
}
|
|
402
|
+
|
|
483
403
|
if (parsed.event === 'final') {
|
|
484
|
-
|
|
404
|
+
const result = payload as ChatTurnView;
|
|
405
|
+
finalResult = {
|
|
406
|
+
sessionKey: typeof result?.sessionKey === 'string' && result.sessionKey.trim()
|
|
407
|
+
? result.sessionKey
|
|
408
|
+
: (readySessionKey ?? ''),
|
|
409
|
+
reply: typeof result?.reply === 'string' ? result.reply : ''
|
|
410
|
+
};
|
|
485
411
|
return;
|
|
486
412
|
}
|
|
413
|
+
|
|
487
414
|
if (parsed.event === 'error') {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
throw new Error(typeof errPayload.message === 'string' && errPayload.message ? errPayload.message : 'chat stream failed');
|
|
491
|
-
} catch (error) {
|
|
492
|
-
if (error instanceof Error) {
|
|
493
|
-
throw error;
|
|
494
|
-
}
|
|
495
|
-
throw new Error('chat stream failed');
|
|
496
|
-
}
|
|
415
|
+
const errorPayload = (payload ?? {}) as ChatTurnStreamErrorEvent;
|
|
416
|
+
throw new Error((errorPayload.message ?? '').trim() || 'chat stream failed');
|
|
497
417
|
}
|
|
498
418
|
};
|
|
499
419
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
buffer += decoder.decode(value, { stream: true });
|
|
508
|
-
|
|
509
|
-
let boundary = buffer.indexOf('\n\n');
|
|
510
|
-
while (boundary !== -1) {
|
|
511
|
-
const frame = buffer.slice(0, boundary).trim();
|
|
512
|
-
buffer = buffer.slice(boundary + 2);
|
|
513
|
-
if (frame) {
|
|
514
|
-
handleFrame(frame);
|
|
420
|
+
try {
|
|
421
|
+
let isReading = true;
|
|
422
|
+
while (isReading) {
|
|
423
|
+
const { value, done } = await reader.read();
|
|
424
|
+
if (done) {
|
|
425
|
+
isReading = false;
|
|
426
|
+
continue;
|
|
515
427
|
}
|
|
516
|
-
|
|
428
|
+
buffer += decoder.decode(value, { stream: true });
|
|
429
|
+
let boundary = buffer.indexOf('\n\n');
|
|
430
|
+
while (boundary !== -1) {
|
|
431
|
+
const frame = buffer.slice(0, boundary);
|
|
432
|
+
buffer = buffer.slice(boundary + 2);
|
|
433
|
+
consumeFrame(frame);
|
|
434
|
+
boundary = buffer.indexOf('\n\n');
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (buffer.trim()) {
|
|
438
|
+
consumeFrame(buffer);
|
|
517
439
|
}
|
|
440
|
+
} finally {
|
|
441
|
+
reader.releaseLock();
|
|
518
442
|
}
|
|
519
443
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
handleFrame(trailing);
|
|
444
|
+
if (finalResult) {
|
|
445
|
+
return finalResult;
|
|
523
446
|
}
|
|
524
447
|
|
|
525
|
-
if (
|
|
526
|
-
|
|
448
|
+
if (readySessionKey) {
|
|
449
|
+
return { sessionKey: readySessionKey, reply: '' };
|
|
527
450
|
}
|
|
528
|
-
|
|
451
|
+
|
|
452
|
+
throw new Error('chat stream ended without final event');
|
|
529
453
|
}
|
|
530
454
|
|
|
455
|
+
// POST /api/chat/turn/stream
|
|
531
456
|
export async function sendChatTurnStream(
|
|
532
457
|
data: ChatTurnRequest,
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
458
|
+
params: {
|
|
459
|
+
signal?: AbortSignal;
|
|
460
|
+
onReady: (event: ChatTurnStreamReadyEvent) => void;
|
|
461
|
+
onDelta: (event: ChatTurnStreamDeltaEvent) => void;
|
|
462
|
+
onSessionEvent: (event: ChatTurnStreamSessionEvent) => void;
|
|
463
|
+
}
|
|
464
|
+
): Promise<{ sessionKey: string; reply: string }> {
|
|
465
|
+
return readSseStream({
|
|
466
|
+
path: '/api/chat/turn/stream',
|
|
536
467
|
method: 'POST',
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
signal: options.signal
|
|
468
|
+
body: data,
|
|
469
|
+
signal: params.signal,
|
|
470
|
+
onReady: params.onReady,
|
|
471
|
+
onDelta: params.onDelta,
|
|
472
|
+
onSessionEvent: params.onSessionEvent
|
|
543
473
|
});
|
|
544
|
-
return consumeChatTurnSseStream(response, options);
|
|
545
474
|
}
|
|
546
475
|
|
|
476
|
+
// GET /api/chat/runs/:runId/stream
|
|
547
477
|
export async function streamChatRun(
|
|
548
|
-
|
|
478
|
+
data: {
|
|
549
479
|
runId: string;
|
|
550
480
|
fromEventIndex?: number;
|
|
551
481
|
},
|
|
552
|
-
|
|
553
|
-
|
|
482
|
+
params: {
|
|
483
|
+
signal?: AbortSignal;
|
|
484
|
+
onReady: (event: ChatTurnStreamReadyEvent) => void;
|
|
485
|
+
onDelta: (event: ChatTurnStreamDeltaEvent) => void;
|
|
486
|
+
onSessionEvent: (event: ChatTurnStreamSessionEvent) => void;
|
|
487
|
+
}
|
|
488
|
+
): Promise<{ sessionKey: string; reply: string }> {
|
|
554
489
|
const query = new URLSearchParams();
|
|
555
|
-
if (typeof
|
|
556
|
-
query.set('fromEventIndex', String(Math.max(0, Math.trunc(
|
|
490
|
+
if (typeof data.fromEventIndex === 'number' && Number.isFinite(data.fromEventIndex)) {
|
|
491
|
+
query.set('fromEventIndex', String(Math.max(0, Math.trunc(data.fromEventIndex))));
|
|
557
492
|
}
|
|
558
493
|
const suffix = query.toString();
|
|
559
|
-
const
|
|
560
|
-
|
|
494
|
+
const path = `/api/chat/runs/${encodeURIComponent(data.runId)}/stream${suffix ? `?${suffix}` : ''}`;
|
|
495
|
+
return readSseStream({
|
|
496
|
+
path,
|
|
561
497
|
method: 'GET',
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
498
|
+
signal: params.signal,
|
|
499
|
+
onReady: params.onReady,
|
|
500
|
+
onDelta: params.onDelta,
|
|
501
|
+
onSessionEvent: params.onSessionEvent
|
|
566
502
|
});
|
|
567
|
-
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// GET /api/chat/capabilities
|
|
506
|
+
export async function fetchChatCapabilities(params?: { sessionKey?: string; agentId?: string }): Promise<ChatCapabilitiesView> {
|
|
507
|
+
const query = new URLSearchParams();
|
|
508
|
+
if (params?.sessionKey?.trim()) {
|
|
509
|
+
query.set('sessionKey', params.sessionKey.trim());
|
|
510
|
+
}
|
|
511
|
+
if (params?.agentId?.trim()) {
|
|
512
|
+
query.set('agentId', params.agentId.trim());
|
|
513
|
+
}
|
|
514
|
+
const suffix = query.toString();
|
|
515
|
+
const response = await api.get<ChatCapabilitiesView>(suffix ? `/api/chat/capabilities?${suffix}` : '/api/chat/capabilities');
|
|
516
|
+
if (!response.ok) {
|
|
517
|
+
throw new Error(response.error.message);
|
|
518
|
+
}
|
|
519
|
+
return response.data;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// GET /api/chat/session-types
|
|
523
|
+
export async function fetchChatSessionTypes(): Promise<ChatSessionTypesView> {
|
|
524
|
+
const response = await api.get<ChatSessionTypesView>('/api/chat/session-types');
|
|
525
|
+
if (!response.ok) {
|
|
526
|
+
throw new Error(response.error.message);
|
|
527
|
+
}
|
|
528
|
+
return response.data;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// POST /api/chat/turn/stop
|
|
532
|
+
export async function stopChatTurn(data: ChatTurnStopRequest): Promise<ChatTurnStopResult> {
|
|
533
|
+
const response = await api.post<ChatTurnStopResult>('/api/chat/turn/stop', data);
|
|
534
|
+
if (!response.ok) {
|
|
535
|
+
throw new Error(response.error.message);
|
|
536
|
+
}
|
|
537
|
+
return response.data;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// GET /api/chat/runs
|
|
541
|
+
export async function fetchChatRuns(params?: {
|
|
542
|
+
sessionKey?: string;
|
|
543
|
+
states?: ChatRunState[];
|
|
544
|
+
limit?: number;
|
|
545
|
+
}): Promise<ChatRunListView> {
|
|
546
|
+
const query = new URLSearchParams();
|
|
547
|
+
if (params?.sessionKey?.trim()) {
|
|
548
|
+
query.set('sessionKey', params.sessionKey.trim());
|
|
549
|
+
}
|
|
550
|
+
if (Array.isArray(params?.states) && params.states.length > 0) {
|
|
551
|
+
query.set('states', params.states.join(','));
|
|
552
|
+
}
|
|
553
|
+
if (typeof params?.limit === 'number' && Number.isFinite(params.limit)) {
|
|
554
|
+
query.set('limit', String(Math.max(0, Math.trunc(params.limit))));
|
|
555
|
+
}
|
|
556
|
+
const suffix = query.toString();
|
|
557
|
+
const response = await api.get<ChatRunListView>(suffix ? `/api/chat/runs?${suffix}` : '/api/chat/runs');
|
|
558
|
+
if (!response.ok) {
|
|
559
|
+
throw new Error(response.error.message);
|
|
560
|
+
}
|
|
561
|
+
return response.data;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// GET /api/chat/runs/:runId
|
|
565
|
+
export async function fetchChatRun(runId: string): Promise<ChatRunView> {
|
|
566
|
+
const response = await api.get<ChatRunView>(`/api/chat/runs/${encodeURIComponent(runId)}`);
|
|
567
|
+
if (!response.ok) {
|
|
568
|
+
throw new Error(response.error.message);
|
|
569
|
+
}
|
|
570
|
+
return response.data;
|
|
568
571
|
}
|
|
569
572
|
|
|
570
573
|
// GET /api/cron
|