@nextclaw/ui 0.6.10 → 0.6.12
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 +16 -0
- package/dist/assets/ChannelsList-DBDjwf-X.js +1 -0
- package/dist/assets/ChatPage-C18sGGk1.js +36 -0
- package/dist/assets/DocBrowser-ZOplDEMS.js +1 -0
- package/dist/assets/LogoBadge-2LMzEMwe.js +1 -0
- package/dist/assets/MarketplacePage-D4JHYcB5.js +49 -0
- package/dist/assets/ModelConfig-DZVvdLFq.js +1 -0
- package/dist/assets/ProvidersList-Dum31480.js +1 -0
- package/dist/assets/{RuntimeConfig-BO6s-ls-.js → RuntimeConfig-4sb3mpkd.js} +1 -1
- package/dist/assets/SearchConfig-B4u_MxRG.js +1 -0
- package/dist/assets/{SecretsConfig-mayFdxpM.js → SecretsConfig-BQXblZvb.js} +2 -2
- package/dist/assets/SessionsConfig-Jk29xjQU.js +2 -0
- package/dist/assets/{card-BP5YnL-G.js → card-BekAnCgX.js} +1 -1
- package/dist/assets/config-layout-BHnOoweL.js +1 -0
- package/dist/assets/index-BXwjfCEO.css +1 -0
- package/dist/assets/index-Dl6t70wA.js +8 -0
- package/dist/assets/{input-B1D2QX0O.js → input-MMn_Na9q.js} +1 -1
- package/dist/assets/{label-DW0j-fXA.js → label-Dg2ydpN0.js} +1 -1
- package/dist/assets/{page-layout-Ch-H9gD-.js → page-layout-7K0rcz0I.js} +1 -1
- package/dist/assets/session-run-status-CAdjSqeb.js +3 -0
- package/dist/assets/{switch-_cZHlGKB.js → switch-DnDMlDVu.js} +1 -1
- package/dist/assets/{tabs-custom-ARxqYYjG.js → tabs-custom-khLM8lWj.js} +1 -1
- package/dist/assets/{useConfirmDialog-BaU7nIat.js → useConfirmDialog-BYA1XnVU.js} +2 -2
- package/dist/assets/{vendor-C--HHaLf.js → vendor-d7E8OgNx.js} +84 -84
- package/dist/index.html +3 -3
- package/package.json +4 -2
- package/src/App.tsx +3 -2
- package/src/api/config.ts +212 -200
- package/src/api/types.ts +93 -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/SearchConfig.tsx +297 -0
- package/src/components/layout/Sidebar.tsx +6 -1
- package/src/hooks/useConfig.ts +48 -1
- package/src/hooks/useObservable.ts +20 -0
- package/src/lib/chat-message.ts +2 -202
- package/src/lib/chat-runtime-utils.ts +250 -0
- package/src/lib/i18n.ts +31 -0
- package/tsconfig.json +2 -1
- package/vite.config.ts +2 -1
- package/dist/assets/ChannelsList-TyMb5Mgz.js +0 -1
- package/dist/assets/ChatPage-CQerYqvy.js +0 -34
- package/dist/assets/DocBrowser-CNtrA0ps.js +0 -1
- package/dist/assets/LogoBadge-BLqiOM5D.js +0 -1
- package/dist/assets/MarketplacePage-CotZxxNe.js +0 -49
- package/dist/assets/ModelConfig-CCsQ8KFq.js +0 -1
- package/dist/assets/ProvidersList-BYYX5K_g.js +0 -1
- package/dist/assets/SessionsConfig-DAIczdBj.js +0 -2
- package/dist/assets/index-BUiahmWm.css +0 -1
- package/dist/assets/index-D6_5HaDl.js +0 -7
- package/dist/assets/session-run-status-BUYsQeWs.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-Dl6t70wA.js"></script>
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-d7E8OgNx.js">
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BXwjfCEO.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.12",
|
|
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
|
@@ -16,6 +16,7 @@ const queryClient = new QueryClient({
|
|
|
16
16
|
|
|
17
17
|
const ModelConfigPage = lazy(async () => ({ default: (await import('@/components/config/ModelConfig')).ModelConfig }));
|
|
18
18
|
const ChatPage = lazy(async () => ({ default: (await import('@/components/chat/ChatPage')).ChatPage }));
|
|
19
|
+
const SearchConfigPage = lazy(async () => ({ default: (await import('@/components/config/SearchConfig')).SearchConfig }));
|
|
19
20
|
const ProvidersListPage = lazy(async () => ({ default: (await import('@/components/config/ProvidersList')).ProvidersList }));
|
|
20
21
|
const ChannelsListPage = lazy(async () => ({ default: (await import('@/components/config/ChannelsList')).ChannelsList }));
|
|
21
22
|
const RuntimeConfigPage = lazy(async () => ({ default: (await import('@/components/config/RuntimeConfig')).RuntimeConfig }));
|
|
@@ -41,11 +42,11 @@ function AppContent() {
|
|
|
41
42
|
<Routes>
|
|
42
43
|
<Route path="/chat/skills" element={<Navigate to="/skills" replace />} />
|
|
43
44
|
<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>} />
|
|
45
|
+
<Route path="/chat/:sessionId?" element={<LazyRoute><ChatPage view="chat" /></LazyRoute>} />
|
|
46
46
|
<Route path="/skills" element={<LazyRoute><ChatPage view="skills" /></LazyRoute>} />
|
|
47
47
|
<Route path="/cron" element={<LazyRoute><ChatPage view="cron" /></LazyRoute>} />
|
|
48
48
|
<Route path="/model" element={<LazyRoute><ModelConfigPage /></LazyRoute>} />
|
|
49
|
+
<Route path="/search" element={<LazyRoute><SearchConfigPage /></LazyRoute>} />
|
|
49
50
|
<Route path="/providers" element={<LazyRoute><ProvidersListPage /></LazyRoute>} />
|
|
50
51
|
<Route path="/channels" element={<LazyRoute><ChannelsListPage /></LazyRoute>} />
|
|
51
52
|
<Route path="/runtime" element={<LazyRoute><RuntimeConfigPage /></LazyRoute>} />
|
package/src/api/config.ts
CHANGED
|
@@ -14,6 +14,8 @@ import type {
|
|
|
14
14
|
ProviderAuthPollRequest,
|
|
15
15
|
ProviderAuthPollResult,
|
|
16
16
|
ProviderAuthImportResult,
|
|
17
|
+
SearchConfigUpdate,
|
|
18
|
+
SearchConfigView,
|
|
17
19
|
ProviderCreateRequest,
|
|
18
20
|
ProviderCreateResult,
|
|
19
21
|
ProviderDeleteResult,
|
|
@@ -27,7 +29,12 @@ import type {
|
|
|
27
29
|
SessionPatchUpdate,
|
|
28
30
|
ChatTurnRequest,
|
|
29
31
|
ChatTurnView,
|
|
32
|
+
ChatTurnStreamDeltaEvent,
|
|
33
|
+
ChatTurnStreamErrorEvent,
|
|
34
|
+
ChatTurnStreamReadyEvent,
|
|
35
|
+
ChatTurnStreamSessionEvent,
|
|
30
36
|
ChatCapabilitiesView,
|
|
37
|
+
ChatSessionTypesView,
|
|
31
38
|
ChatTurnStopRequest,
|
|
32
39
|
ChatTurnStopResult,
|
|
33
40
|
ChatRunListView,
|
|
@@ -36,10 +43,7 @@ import type {
|
|
|
36
43
|
CronListView,
|
|
37
44
|
CronEnableRequest,
|
|
38
45
|
CronRunRequest,
|
|
39
|
-
CronActionResult
|
|
40
|
-
ChatTurnStreamReadyEvent,
|
|
41
|
-
ChatTurnStreamDeltaEvent,
|
|
42
|
-
ChatTurnStreamSessionEvent
|
|
46
|
+
CronActionResult
|
|
43
47
|
} from './types';
|
|
44
48
|
|
|
45
49
|
// GET /api/app/meta
|
|
@@ -89,6 +93,17 @@ export async function updateModel(data: {
|
|
|
89
93
|
return response.data;
|
|
90
94
|
}
|
|
91
95
|
|
|
96
|
+
// PUT /api/config/search
|
|
97
|
+
export async function updateSearch(
|
|
98
|
+
data: SearchConfigUpdate
|
|
99
|
+
): Promise<SearchConfigView> {
|
|
100
|
+
const response = await api.put<SearchConfigView>('/api/config/search', data);
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
throw new Error(response.error.message);
|
|
103
|
+
}
|
|
104
|
+
return response.data;
|
|
105
|
+
}
|
|
106
|
+
|
|
92
107
|
// PUT /api/config/providers/:provider
|
|
93
108
|
export async function updateProvider(
|
|
94
109
|
provider: string,
|
|
@@ -304,91 +319,24 @@ export async function sendChatTurn(data: ChatTurnRequest): Promise<ChatTurnView>
|
|
|
304
319
|
return response.data;
|
|
305
320
|
}
|
|
306
321
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (params?.sessionKey?.trim()) {
|
|
311
|
-
query.set('sessionKey', params.sessionKey.trim());
|
|
312
|
-
}
|
|
313
|
-
if (params?.agentId?.trim()) {
|
|
314
|
-
query.set('agentId', params.agentId.trim());
|
|
315
|
-
}
|
|
316
|
-
const suffix = query.toString();
|
|
317
|
-
const response = await api.get<ChatCapabilitiesView>(suffix ? `/api/chat/capabilities?${suffix}` : '/api/chat/capabilities');
|
|
318
|
-
if (!response.ok) {
|
|
319
|
-
throw new Error(response.error.message);
|
|
320
|
-
}
|
|
321
|
-
return response.data;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// POST /api/chat/turn/stop
|
|
325
|
-
export async function stopChatTurn(data: ChatTurnStopRequest): Promise<ChatTurnStopResult> {
|
|
326
|
-
const response = await api.post<ChatTurnStopResult>('/api/chat/turn/stop', data);
|
|
327
|
-
if (!response.ok) {
|
|
328
|
-
throw new Error(response.error.message);
|
|
329
|
-
}
|
|
330
|
-
return response.data;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// GET /api/chat/runs
|
|
334
|
-
export async function fetchChatRuns(params?: {
|
|
335
|
-
sessionKey?: string;
|
|
336
|
-
states?: ChatRunState[];
|
|
337
|
-
limit?: number;
|
|
338
|
-
}): Promise<ChatRunListView> {
|
|
339
|
-
const query = new URLSearchParams();
|
|
340
|
-
if (params?.sessionKey?.trim()) {
|
|
341
|
-
query.set('sessionKey', params.sessionKey.trim());
|
|
342
|
-
}
|
|
343
|
-
if (Array.isArray(params?.states) && params.states.length > 0) {
|
|
344
|
-
query.set('states', params.states.join(','));
|
|
345
|
-
}
|
|
346
|
-
if (typeof params?.limit === 'number' && Number.isFinite(params.limit)) {
|
|
347
|
-
query.set('limit', String(Math.max(0, Math.trunc(params.limit))));
|
|
348
|
-
}
|
|
349
|
-
const suffix = query.toString();
|
|
350
|
-
const response = await api.get<ChatRunListView>(suffix ? `/api/chat/runs?${suffix}` : '/api/chat/runs');
|
|
351
|
-
if (!response.ok) {
|
|
352
|
-
throw new Error(response.error.message);
|
|
353
|
-
}
|
|
354
|
-
return response.data;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// GET /api/chat/runs/:runId
|
|
358
|
-
export async function fetchChatRun(runId: string): Promise<ChatRunView> {
|
|
359
|
-
const response = await api.get<ChatRunView>(`/api/chat/runs/${encodeURIComponent(runId)}`);
|
|
360
|
-
if (!response.ok) {
|
|
361
|
-
throw new Error(response.error.message);
|
|
362
|
-
}
|
|
363
|
-
return response.data;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
type ChatTurnStreamOptions = {
|
|
367
|
-
signal?: AbortSignal;
|
|
368
|
-
onReady?: (event: ChatTurnStreamReadyEvent) => void;
|
|
369
|
-
onDelta?: (event: ChatTurnStreamDeltaEvent) => void;
|
|
370
|
-
onSessionEvent?: (event: ChatTurnStreamSessionEvent) => void;
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
type SseParsedEvent = {
|
|
374
|
-
event: string;
|
|
375
|
-
data: string;
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
function parseSseFrame(frame: string): SseParsedEvent | null {
|
|
379
|
-
const lines = frame.split(/\r?\n/);
|
|
380
|
-
let event = 'message';
|
|
322
|
+
function parseSseFrame(frame: string): { event: string; data: string } | null {
|
|
323
|
+
const lines = frame.split('\n');
|
|
324
|
+
let event = '';
|
|
381
325
|
const dataLines: string[] = [];
|
|
382
|
-
for (const
|
|
326
|
+
for (const raw of lines) {
|
|
327
|
+
const line = raw.trimEnd();
|
|
328
|
+
if (!line || line.startsWith(':')) {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
383
331
|
if (line.startsWith('event:')) {
|
|
384
|
-
event = line.slice(
|
|
332
|
+
event = line.slice(6).trim();
|
|
385
333
|
continue;
|
|
386
334
|
}
|
|
387
335
|
if (line.startsWith('data:')) {
|
|
388
|
-
dataLines.push(line.slice(
|
|
336
|
+
dataLines.push(line.slice(5).trimStart());
|
|
389
337
|
}
|
|
390
338
|
}
|
|
391
|
-
if (
|
|
339
|
+
if (!event) {
|
|
392
340
|
return null;
|
|
393
341
|
}
|
|
394
342
|
return {
|
|
@@ -397,178 +345,242 @@ function parseSseFrame(frame: string): SseParsedEvent | null {
|
|
|
397
345
|
};
|
|
398
346
|
}
|
|
399
347
|
|
|
400
|
-
async function
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
348
|
+
async function readSseStream(params: {
|
|
349
|
+
path: string;
|
|
350
|
+
method: 'GET' | 'POST';
|
|
351
|
+
body?: unknown;
|
|
352
|
+
signal?: AbortSignal;
|
|
353
|
+
onReady: (event: ChatTurnStreamReadyEvent) => void;
|
|
354
|
+
onDelta: (event: ChatTurnStreamDeltaEvent) => void;
|
|
355
|
+
onSessionEvent: (event: ChatTurnStreamSessionEvent) => void;
|
|
356
|
+
}): Promise<{ sessionKey: string; reply: string }> {
|
|
357
|
+
const response = await fetch(`${API_BASE}${params.path}`, {
|
|
358
|
+
method: params.method,
|
|
359
|
+
headers: {
|
|
360
|
+
'Content-Type': 'application/json',
|
|
361
|
+
Accept: 'text/event-stream'
|
|
362
|
+
},
|
|
363
|
+
...(params.body !== undefined ? { body: JSON.stringify(params.body) } : {}),
|
|
364
|
+
...(params.signal ? { signal: params.signal } : {})
|
|
365
|
+
});
|
|
366
|
+
|
|
404
367
|
if (!response.ok) {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
message = payload.error.message;
|
|
410
|
-
}
|
|
411
|
-
} catch {
|
|
412
|
-
const text = await response.text().catch(() => '');
|
|
413
|
-
if (text.trim()) {
|
|
414
|
-
message = text.trim();
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
throw new Error(message);
|
|
368
|
+
const text = await response.text();
|
|
369
|
+
const fallback = `HTTP ${response.status}`;
|
|
370
|
+
const trimmed = text.trim();
|
|
371
|
+
throw new Error(trimmed || fallback);
|
|
418
372
|
}
|
|
419
373
|
|
|
420
|
-
|
|
421
|
-
|
|
374
|
+
const reader = response.body?.getReader();
|
|
375
|
+
if (!reader) {
|
|
376
|
+
throw new Error('SSE response body unavailable');
|
|
422
377
|
}
|
|
423
378
|
|
|
424
|
-
const reader = response.body.getReader();
|
|
425
379
|
const decoder = new TextDecoder();
|
|
426
380
|
let buffer = '';
|
|
427
|
-
let
|
|
381
|
+
let finalResult: { sessionKey: string; reply: string } | null = null;
|
|
382
|
+
let readySessionKey: string | null = null;
|
|
428
383
|
|
|
429
|
-
const
|
|
384
|
+
const consumeFrame = (frame: string) => {
|
|
430
385
|
const parsed = parseSseFrame(frame);
|
|
431
386
|
if (!parsed) {
|
|
432
387
|
return;
|
|
433
388
|
}
|
|
434
|
-
|
|
389
|
+
|
|
390
|
+
let payload: unknown = undefined;
|
|
391
|
+
if (parsed.data) {
|
|
435
392
|
try {
|
|
436
|
-
|
|
437
|
-
sessionKey?: string;
|
|
438
|
-
requestedAt?: string;
|
|
439
|
-
runId?: string;
|
|
440
|
-
stopSupported?: boolean;
|
|
441
|
-
stopReason?: string;
|
|
442
|
-
};
|
|
443
|
-
options.onReady?.({
|
|
444
|
-
event: 'ready',
|
|
445
|
-
sessionKey: String(readyPayload.sessionKey ?? ''),
|
|
446
|
-
requestedAt: String(readyPayload.requestedAt ?? ''),
|
|
447
|
-
...(typeof readyPayload.runId === 'string' && readyPayload.runId.trim().length > 0
|
|
448
|
-
? { runId: readyPayload.runId.trim() }
|
|
449
|
-
: {}),
|
|
450
|
-
...(typeof readyPayload.stopSupported === 'boolean'
|
|
451
|
-
? { stopSupported: readyPayload.stopSupported }
|
|
452
|
-
: {}),
|
|
453
|
-
...(typeof readyPayload.stopReason === 'string' && readyPayload.stopReason.trim().length > 0
|
|
454
|
-
? { stopReason: readyPayload.stopReason.trim() }
|
|
455
|
-
: {})
|
|
456
|
-
});
|
|
393
|
+
payload = JSON.parse(parsed.data);
|
|
457
394
|
} catch {
|
|
458
|
-
|
|
395
|
+
payload = undefined;
|
|
459
396
|
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (parsed.event === 'ready') {
|
|
400
|
+
const ready = (payload ?? {}) as ChatTurnStreamReadyEvent;
|
|
401
|
+
readySessionKey = typeof ready.sessionKey === 'string' && ready.sessionKey.trim() ? ready.sessionKey : readySessionKey;
|
|
402
|
+
params.onReady(ready);
|
|
460
403
|
return;
|
|
461
404
|
}
|
|
405
|
+
|
|
462
406
|
if (parsed.event === 'delta') {
|
|
463
|
-
|
|
464
|
-
const deltaPayload = JSON.parse(parsed.data) as { delta?: string };
|
|
465
|
-
if (typeof deltaPayload.delta === 'string' && deltaPayload.delta.length > 0) {
|
|
466
|
-
options.onDelta?.({
|
|
467
|
-
event: 'delta',
|
|
468
|
-
delta: deltaPayload.delta
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
} catch {
|
|
472
|
-
// ignore malformed delta event payload
|
|
473
|
-
}
|
|
407
|
+
params.onDelta((payload ?? { delta: '' }) as ChatTurnStreamDeltaEvent);
|
|
474
408
|
return;
|
|
475
409
|
}
|
|
410
|
+
|
|
476
411
|
if (parsed.event === 'session_event') {
|
|
477
|
-
|
|
478
|
-
options.onSessionEvent?.({
|
|
479
|
-
event: 'session_event',
|
|
480
|
-
data: JSON.parse(parsed.data)
|
|
481
|
-
});
|
|
482
|
-
} catch {
|
|
483
|
-
// ignore malformed session_event payload
|
|
484
|
-
}
|
|
412
|
+
params.onSessionEvent({ data: payload as ChatTurnStreamSessionEvent['data'] });
|
|
485
413
|
return;
|
|
486
414
|
}
|
|
415
|
+
|
|
487
416
|
if (parsed.event === 'final') {
|
|
488
|
-
|
|
417
|
+
const result = payload as ChatTurnView;
|
|
418
|
+
finalResult = {
|
|
419
|
+
sessionKey: typeof result?.sessionKey === 'string' && result.sessionKey.trim()
|
|
420
|
+
? result.sessionKey
|
|
421
|
+
: (readySessionKey ?? ''),
|
|
422
|
+
reply: typeof result?.reply === 'string' ? result.reply : ''
|
|
423
|
+
};
|
|
489
424
|
return;
|
|
490
425
|
}
|
|
426
|
+
|
|
491
427
|
if (parsed.event === 'error') {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
throw new Error(typeof errPayload.message === 'string' && errPayload.message ? errPayload.message : 'chat stream failed');
|
|
495
|
-
} catch (error) {
|
|
496
|
-
if (error instanceof Error) {
|
|
497
|
-
throw error;
|
|
498
|
-
}
|
|
499
|
-
throw new Error('chat stream failed');
|
|
500
|
-
}
|
|
428
|
+
const errorPayload = (payload ?? {}) as ChatTurnStreamErrorEvent;
|
|
429
|
+
throw new Error((errorPayload.message ?? '').trim() || 'chat stream failed');
|
|
501
430
|
}
|
|
502
431
|
};
|
|
503
432
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
433
|
+
try {
|
|
434
|
+
let isReading = true;
|
|
435
|
+
while (isReading) {
|
|
436
|
+
const { value, done } = await reader.read();
|
|
437
|
+
if (done) {
|
|
438
|
+
isReading = false;
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
buffer += decoder.decode(value, { stream: true });
|
|
442
|
+
let boundary = buffer.indexOf('\n\n');
|
|
443
|
+
while (boundary !== -1) {
|
|
444
|
+
const frame = buffer.slice(0, boundary);
|
|
445
|
+
buffer = buffer.slice(boundary + 2);
|
|
446
|
+
consumeFrame(frame);
|
|
447
|
+
boundary = buffer.indexOf('\n\n');
|
|
519
448
|
}
|
|
520
|
-
boundary = buffer.indexOf('\n\n');
|
|
521
449
|
}
|
|
450
|
+
if (buffer.trim()) {
|
|
451
|
+
consumeFrame(buffer);
|
|
452
|
+
}
|
|
453
|
+
} finally {
|
|
454
|
+
reader.releaseLock();
|
|
522
455
|
}
|
|
523
456
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
handleFrame(trailing);
|
|
457
|
+
if (finalResult) {
|
|
458
|
+
return finalResult;
|
|
527
459
|
}
|
|
528
460
|
|
|
529
|
-
if (
|
|
530
|
-
|
|
461
|
+
if (readySessionKey) {
|
|
462
|
+
return { sessionKey: readySessionKey, reply: '' };
|
|
531
463
|
}
|
|
532
|
-
|
|
464
|
+
|
|
465
|
+
throw new Error('chat stream ended without final event');
|
|
533
466
|
}
|
|
534
467
|
|
|
468
|
+
// POST /api/chat/turn/stream
|
|
535
469
|
export async function sendChatTurnStream(
|
|
536
470
|
data: ChatTurnRequest,
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
471
|
+
params: {
|
|
472
|
+
signal?: AbortSignal;
|
|
473
|
+
onReady: (event: ChatTurnStreamReadyEvent) => void;
|
|
474
|
+
onDelta: (event: ChatTurnStreamDeltaEvent) => void;
|
|
475
|
+
onSessionEvent: (event: ChatTurnStreamSessionEvent) => void;
|
|
476
|
+
}
|
|
477
|
+
): Promise<{ sessionKey: string; reply: string }> {
|
|
478
|
+
return readSseStream({
|
|
479
|
+
path: '/api/chat/turn/stream',
|
|
540
480
|
method: 'POST',
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
signal: options.signal
|
|
481
|
+
body: data,
|
|
482
|
+
signal: params.signal,
|
|
483
|
+
onReady: params.onReady,
|
|
484
|
+
onDelta: params.onDelta,
|
|
485
|
+
onSessionEvent: params.onSessionEvent
|
|
547
486
|
});
|
|
548
|
-
return consumeChatTurnSseStream(response, options);
|
|
549
487
|
}
|
|
550
488
|
|
|
489
|
+
// GET /api/chat/runs/:runId/stream
|
|
551
490
|
export async function streamChatRun(
|
|
552
|
-
|
|
491
|
+
data: {
|
|
553
492
|
runId: string;
|
|
554
493
|
fromEventIndex?: number;
|
|
555
494
|
},
|
|
556
|
-
|
|
557
|
-
|
|
495
|
+
params: {
|
|
496
|
+
signal?: AbortSignal;
|
|
497
|
+
onReady: (event: ChatTurnStreamReadyEvent) => void;
|
|
498
|
+
onDelta: (event: ChatTurnStreamDeltaEvent) => void;
|
|
499
|
+
onSessionEvent: (event: ChatTurnStreamSessionEvent) => void;
|
|
500
|
+
}
|
|
501
|
+
): Promise<{ sessionKey: string; reply: string }> {
|
|
558
502
|
const query = new URLSearchParams();
|
|
559
|
-
if (typeof
|
|
560
|
-
query.set('fromEventIndex', String(Math.max(0, Math.trunc(
|
|
503
|
+
if (typeof data.fromEventIndex === 'number' && Number.isFinite(data.fromEventIndex)) {
|
|
504
|
+
query.set('fromEventIndex', String(Math.max(0, Math.trunc(data.fromEventIndex))));
|
|
561
505
|
}
|
|
562
506
|
const suffix = query.toString();
|
|
563
|
-
const
|
|
564
|
-
|
|
507
|
+
const path = `/api/chat/runs/${encodeURIComponent(data.runId)}/stream${suffix ? `?${suffix}` : ''}`;
|
|
508
|
+
return readSseStream({
|
|
509
|
+
path,
|
|
565
510
|
method: 'GET',
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
511
|
+
signal: params.signal,
|
|
512
|
+
onReady: params.onReady,
|
|
513
|
+
onDelta: params.onDelta,
|
|
514
|
+
onSessionEvent: params.onSessionEvent
|
|
570
515
|
});
|
|
571
|
-
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// GET /api/chat/capabilities
|
|
519
|
+
export async function fetchChatCapabilities(params?: { sessionKey?: string; agentId?: string }): Promise<ChatCapabilitiesView> {
|
|
520
|
+
const query = new URLSearchParams();
|
|
521
|
+
if (params?.sessionKey?.trim()) {
|
|
522
|
+
query.set('sessionKey', params.sessionKey.trim());
|
|
523
|
+
}
|
|
524
|
+
if (params?.agentId?.trim()) {
|
|
525
|
+
query.set('agentId', params.agentId.trim());
|
|
526
|
+
}
|
|
527
|
+
const suffix = query.toString();
|
|
528
|
+
const response = await api.get<ChatCapabilitiesView>(suffix ? `/api/chat/capabilities?${suffix}` : '/api/chat/capabilities');
|
|
529
|
+
if (!response.ok) {
|
|
530
|
+
throw new Error(response.error.message);
|
|
531
|
+
}
|
|
532
|
+
return response.data;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// GET /api/chat/session-types
|
|
536
|
+
export async function fetchChatSessionTypes(): Promise<ChatSessionTypesView> {
|
|
537
|
+
const response = await api.get<ChatSessionTypesView>('/api/chat/session-types');
|
|
538
|
+
if (!response.ok) {
|
|
539
|
+
throw new Error(response.error.message);
|
|
540
|
+
}
|
|
541
|
+
return response.data;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// POST /api/chat/turn/stop
|
|
545
|
+
export async function stopChatTurn(data: ChatTurnStopRequest): Promise<ChatTurnStopResult> {
|
|
546
|
+
const response = await api.post<ChatTurnStopResult>('/api/chat/turn/stop', data);
|
|
547
|
+
if (!response.ok) {
|
|
548
|
+
throw new Error(response.error.message);
|
|
549
|
+
}
|
|
550
|
+
return response.data;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// GET /api/chat/runs
|
|
554
|
+
export async function fetchChatRuns(params?: {
|
|
555
|
+
sessionKey?: string;
|
|
556
|
+
states?: ChatRunState[];
|
|
557
|
+
limit?: number;
|
|
558
|
+
}): Promise<ChatRunListView> {
|
|
559
|
+
const query = new URLSearchParams();
|
|
560
|
+
if (params?.sessionKey?.trim()) {
|
|
561
|
+
query.set('sessionKey', params.sessionKey.trim());
|
|
562
|
+
}
|
|
563
|
+
if (Array.isArray(params?.states) && params.states.length > 0) {
|
|
564
|
+
query.set('states', params.states.join(','));
|
|
565
|
+
}
|
|
566
|
+
if (typeof params?.limit === 'number' && Number.isFinite(params.limit)) {
|
|
567
|
+
query.set('limit', String(Math.max(0, Math.trunc(params.limit))));
|
|
568
|
+
}
|
|
569
|
+
const suffix = query.toString();
|
|
570
|
+
const response = await api.get<ChatRunListView>(suffix ? `/api/chat/runs?${suffix}` : '/api/chat/runs');
|
|
571
|
+
if (!response.ok) {
|
|
572
|
+
throw new Error(response.error.message);
|
|
573
|
+
}
|
|
574
|
+
return response.data;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// GET /api/chat/runs/:runId
|
|
578
|
+
export async function fetchChatRun(runId: string): Promise<ChatRunView> {
|
|
579
|
+
const response = await api.get<ChatRunView>(`/api/chat/runs/${encodeURIComponent(runId)}`);
|
|
580
|
+
if (!response.ok) {
|
|
581
|
+
throw new Error(response.error.message);
|
|
582
|
+
}
|
|
583
|
+
return response.data;
|
|
572
584
|
}
|
|
573
585
|
|
|
574
586
|
// GET /api/cron
|