flare-chat-core 0.2.0 → 0.2.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/README.md +28 -0
- package/docs/CAPABILITY-INVENTORY.md +42 -0
- package/docs/CHAT-CORE-BOUNDARY.md +47 -0
- package/docs/CORE-APP-REALIGNMENT-WORKLOAD-2026-04-18.md +86 -0
- package/docs/SSOT-CHAT-CORE-BOUNDARY.md +73 -0
- package/docs/SSOT-CHAT-CORE-DATAFLOW.md +97 -0
- package/index.html +12 -0
- package/package.json +24 -2
- package/src/adapters/index.js +6 -0
- package/src/adapters/message-api.adapter.js +59 -0
- package/src/adapters/session-api.adapter.js +133 -0
- package/src/adapters/session-message-api.http.js +161 -0
- package/src/adapters/session-message-api.js +34 -0
- package/src/adapters/session-message-api.normalize-source-record.test.mjs +180 -0
- package/src/adapters/session-message-api.normalizers.js +153 -0
- package/src/adapters/source-api.adapter.js +135 -0
- package/src/adapters/sse-client.js +244 -0
- package/src/adapters/sse-event-dispatcher.js +121 -0
- package/src/app/App.jsx +11 -0
- package/src/app/AppProviders.jsx +12 -0
- package/src/app/ChatWorkspaceScreen.jsx +33 -0
- package/src/app/WorkspaceLayout.jsx +125 -0
- package/src/app/components/AppCanvasPanel.jsx +64 -0
- package/src/app/components/TriggerThresholdPopoverContent.jsx +122 -0
- package/src/app/components/WorkspaceBodySection.jsx +109 -0
- package/src/app/components/WorkspaceMainPane.jsx +113 -0
- package/src/app/components/WorkspaceSessionPane.jsx +48 -0
- package/src/app/components/WorkspaceTopBarSection.jsx +65 -0
- package/src/app/core-chat-entry/ComposerSectionNode.jsx +241 -0
- package/src/app/core-chat-entry/attachmentSendRefs.js +154 -0
- package/src/app/core-chat-entry/attachmentSendRefs.test.mjs +101 -0
- package/src/app/core-chat-entry/composerActionRouter.js +26 -0
- package/src/app/core-chat-entry/constants.js +108 -0
- package/src/app/core-chat-entry/selectors.js +28 -0
- package/src/app/core-chat-entry/useAppActionErrorGuards.js +68 -0
- package/src/app/core-chat-entry/useChatCorePipelines.js +110 -0
- package/src/app/core-chat-entry/useComposerModeSuggestion.js +89 -0
- package/src/app/core-chat-entry/useDevCapabilityStatusNote.js +22 -0
- package/src/app/core-chat-entry/useProjectNameEditing.js +41 -0
- package/src/app/core-chat-entry/useProjectSourceUpload.js +341 -0
- package/src/app/core-chat-entry/useRealApiReadinessGate.js +103 -0
- package/src/app/core-chat-entry/useUnavailableActionError.js +29 -0
- package/src/app/core-chat-entry/useWorkspaceCanvasController.jsx +177 -0
- package/src/app/core-chat-entry/useWorkspaceCanvasProjection.jsx +171 -0
- package/src/app/core-chat-entry/useWorkspaceComposerController.jsx +199 -0
- package/src/app/core-chat-entry/useWorkspaceController.jsx +226 -0
- package/src/app/core-chat-entry/useWorkspacePanels.js +55 -0
- package/src/app/hooks/useComposerAttachmentSync.js +223 -0
- package/src/app/hooks/useComposerChooserHandlers.js +52 -0
- package/src/app/hooks/useSendWithContextRefs.js +140 -0
- package/src/app/hooks/useSendWithContextRefs.test.mjs +29 -0
- package/src/app/hooks/useUserThresholdProfile.js +121 -0
- package/src/app/index.js +1 -0
- package/src/app/selectors/assistantTextSelector.js +73 -0
- package/src/app/selectors/canvasEvidenceSummarySelector.js +28 -0
- package/src/app/selectors/canvasReportTemplateSelector.js +28 -0
- package/src/app/selectors/canvasTabsSelector.js +58 -0
- package/src/app/selectors/evidenceProjectionSelector.js +175 -0
- package/src/app/selectors/evidenceProjectionSelector.test.mjs +107 -0
- package/src/app/selectors/modeSuggestionSelector.js +50 -0
- package/src/chat-core/app/mockRuntime.js +291 -0
- package/src/chat-core/app/useAppStream.js +187 -0
- package/src/chat-core/app/useAppStream.refs.test.mjs +44 -0
- package/src/chat-core/app/useAppStream.request-body.test.mjs +116 -0
- package/src/chat-core/app/useCoreChatApp.js +115 -0
- package/src/chat-core/facade/useBasicConversationFacade.js +280 -0
- package/src/chat-core/index.js +9 -1
- package/src/chat-core/messages/buildTimelineItems.analysis-route.test.mjs +36 -0
- package/src/chat-core/messages/buildTimelineItems.js +172 -11
- package/src/chat-core/messages/buildTimelineItems.knowledge-citation.test.mjs +183 -0
- package/src/chat-core/messages/contextUsageDefaults.js +3 -0
- package/src/chat-core/messages/contextUsageViewModel.js +147 -0
- package/src/chat-core/messages/contextUsageViewModel.test.mjs +74 -0
- package/src/chat-core/messages/useContextUsageViewModel.js +41 -0
- package/src/chat-core/orchestration/useBasicSendHandler.js +55 -0
- package/src/chat-core/pipelines/build-action-request.js +46 -0
- package/src/chat-core/pipelines/build-stream-request.js +74 -0
- package/src/chat-core/pipelines/entity-extraction.js +159 -0
- package/src/chat-core/pipelines/preprocess-message.js +16 -0
- package/src/chat-core/pipelines/stream-persist-utils.js +32 -0
- package/src/chat-core/pipelines/transport/send-mock-stream.js +86 -0
- package/src/chat-core/pipelines/transport/send-real-stream.js +330 -0
- package/src/chat-core/pipelines/transport/send-real-stream.test.mjs +27 -0
- package/src/chat-core/pipelines/transport/send-sourcing-search.js +86 -0
- package/src/chat-core/pipelines/transport/send-sourcing-search.test.mjs +14 -0
- package/src/chat-core/pipelines/transport/sourcing-response-templates.js +55 -0
- package/src/chat-core/pipelines/transport/sourcing-search-api.js +155 -0
- package/src/chat-core/runtime/runtimeMode.js +69 -0
- package/src/chat-core/session/chatSessionActionTypes.js +24 -0
- package/src/chat-core/session/chatSessionReducer.js +352 -0
- package/src/chat-core/session/chatSessionReducer.streaming-done.test.mjs +39 -0
- package/src/chat-core/session/index.js +2 -0
- package/src/chat-core/session/sessionActionsMessages.js +44 -0
- package/src/chat-core/session/sessionActionsSessionCrud.js +131 -0
- package/src/chat-core/session/sessionActionsStreaming.js +80 -0
- package/src/chat-core/session/sessionActionsUiState.js +51 -0
- package/src/chat-core/session/useChatSessionReducer.js +67 -390
- package/src/chat-core/session/useSessionListController.js +67 -0
- package/src/chat-core/stream/sse-client.js +1 -142
- package/src/chat-core/stream/sse-event-dispatcher.js +1 -0
- package/src/chat-core/stream/sse-events.js +1 -598
- package/src/chat-core/stream/useSSEStream.js +1 -273
- package/src/chat-core/stream/useStreamSendController.js +46 -0
- package/src/contracts/context-ssot.js +47 -0
- package/src/contracts/index.js +1 -0
- package/src/contracts/sse-events/base-parsers.js +79 -0
- package/src/contracts/sse-events/domain-parsers.js +3 -0
- package/src/contracts/sse-events/internal-normalizers.js +143 -0
- package/src/contracts/sse-events/parsers-intake.js +235 -0
- package/src/contracts/sse-events/parsers-runtime.js +37 -0
- package/src/contracts/sse-events/parsers-sourcing.js +179 -0
- package/src/contracts/sse-events/patch-event-parser.js +121 -0
- package/src/contracts/sse-events/runtime-parsers.js +79 -0
- package/src/contracts/sse-events.js +4 -0
- package/src/index.js +5 -0
- package/src/main.jsx +28 -0
- package/src/orchestration/index.js +6 -0
- package/src/orchestration/useSSEStream.js +221 -0
- package/src/state/index.js +4 -0
- package/vite.config.js +36 -0
|
@@ -1,402 +1,69 @@
|
|
|
1
|
-
import { useReducer,
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const THINKING_TRACE_SET = 'THINKING_TRACE_SET';
|
|
13
|
-
const EXECUTION_TRACE_SET = 'EXECUTION_TRACE_SET';
|
|
14
|
-
const STREAMING_DONE = 'STREAMING_DONE';
|
|
15
|
-
const DOC_GENERATED = 'DOC_GENERATED';
|
|
16
|
-
const LEGEND_HINT_SHOWN = 'LEGEND_HINT_SHOWN';
|
|
17
|
-
const EXECUTION_CARD_UPSERTED = 'EXECUTION_CARD_UPSERTED';
|
|
18
|
-
const UI_CARDS_SET = 'UI_CARDS_SET';
|
|
19
|
-
const LAST_USER_MESSAGE_SET = 'LAST_USER_MESSAGE_SET';
|
|
20
|
-
const UI_CARD_UPDATED = 'UI_CARD_UPDATED';
|
|
21
|
-
const INSTANCE_PROFILE_SET = 'INSTANCE_PROFILE_SET';
|
|
22
|
-
|
|
23
|
-
const initialStreaming = {
|
|
24
|
-
content: '',
|
|
25
|
-
agentStatus: null,
|
|
26
|
-
thinkingTrace: '',
|
|
27
|
-
executionTrace: null,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export const initialState = {
|
|
31
|
-
sessionId: null,
|
|
32
|
-
sessionTitle: '',
|
|
33
|
-
sessionStatus: 'active',
|
|
34
|
-
instanceProfile: null,
|
|
35
|
-
messages: [],
|
|
36
|
-
executionCards: [],
|
|
37
|
-
uiCards: [],
|
|
38
|
-
lastUserMessage: '',
|
|
39
|
-
generatedDoc: null,
|
|
40
|
-
streaming: initialStreaming,
|
|
41
|
-
sessionError: null,
|
|
42
|
-
legendHintsShown: {
|
|
43
|
-
knowledge_base: false,
|
|
44
|
-
ai: false,
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
function reducer(state, { type, payload }) {
|
|
49
|
-
switch (type) {
|
|
50
|
-
case SESSION_LOADED: {
|
|
51
|
-
const { sessionId, title, status, messages } = payload;
|
|
52
|
-
return {
|
|
53
|
-
...state,
|
|
54
|
-
sessionId,
|
|
55
|
-
sessionTitle: title,
|
|
56
|
-
sessionStatus: status || 'active',
|
|
57
|
-
instanceProfile: state.instanceProfile,
|
|
58
|
-
messages,
|
|
59
|
-
executionCards: [],
|
|
60
|
-
uiCards: [],
|
|
61
|
-
lastUserMessage: '',
|
|
62
|
-
generatedDoc: null,
|
|
63
|
-
streaming: initialStreaming,
|
|
64
|
-
legendHintsShown: initialState.legendHintsShown,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
case SESSION_RESET:
|
|
69
|
-
return { ...initialState, instanceProfile: state.instanceProfile };
|
|
70
|
-
|
|
71
|
-
case SESSION_ERROR:
|
|
72
|
-
return { ...state, sessionError: payload };
|
|
73
|
-
|
|
74
|
-
case TITLE_UPDATED:
|
|
75
|
-
return { ...state, sessionTitle: payload };
|
|
76
|
-
|
|
77
|
-
case MESSAGES_REFRESHED:
|
|
78
|
-
return { ...state, messages: payload };
|
|
79
|
-
|
|
80
|
-
case MESSAGE_APPENDED:
|
|
81
|
-
return {
|
|
82
|
-
...state,
|
|
83
|
-
messages: [...state.messages, payload],
|
|
84
|
-
lastUserMessage: payload.role === 'user' ? payload.content : state.lastUserMessage,
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
case LAST_USER_MESSAGE_SET:
|
|
88
|
-
return { ...state, lastUserMessage: payload };
|
|
89
|
-
|
|
90
|
-
case UI_CARDS_SET:
|
|
91
|
-
return { ...state, uiCards: payload };
|
|
92
|
-
|
|
93
|
-
case UI_CARD_UPDATED: {
|
|
94
|
-
const nextCard = payload;
|
|
95
|
-
return {
|
|
96
|
-
...state,
|
|
97
|
-
uiCards: state.uiCards.map((card) =>
|
|
98
|
-
card.id === nextCard.id ? { ...card, ...nextCard } : card
|
|
99
|
-
),
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
case INSTANCE_PROFILE_SET:
|
|
104
|
-
return { ...state, instanceProfile: payload };
|
|
105
|
-
|
|
106
|
-
case EXECUTION_CARD_UPSERTED: {
|
|
107
|
-
const nextCard = payload;
|
|
108
|
-
const cardKey = nextCard.step_id || `${nextCard.agent}-${nextCard.label}-${nextCard.stage}`;
|
|
109
|
-
const existingIndex = state.executionCards.findIndex((card) => {
|
|
110
|
-
const existingKey = card.step_id || `${card.agent}-${card.label}-${card.stage}`;
|
|
111
|
-
return existingKey === cardKey;
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
const updatedCards = existingIndex >= 0
|
|
115
|
-
? state.executionCards.map((card, index) =>
|
|
116
|
-
index === existingIndex ? { ...card, ...nextCard } : card
|
|
117
|
-
)
|
|
118
|
-
: [...state.executionCards, nextCard];
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
...state,
|
|
122
|
-
executionCards: updatedCards.slice(-6),
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
case STREAMING_RESET:
|
|
127
|
-
return { ...state, streaming: initialStreaming };
|
|
128
|
-
|
|
129
|
-
case STREAMING_CHUNK:
|
|
130
|
-
return {
|
|
131
|
-
...state,
|
|
132
|
-
streaming: {
|
|
133
|
-
...state.streaming,
|
|
134
|
-
content: state.streaming.content + payload,
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
case AGENT_STATUS_SET:
|
|
139
|
-
return {
|
|
140
|
-
...state,
|
|
141
|
-
streaming: { ...state.streaming, agentStatus: payload },
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
case THINKING_TRACE_SET:
|
|
145
|
-
return {
|
|
146
|
-
...state,
|
|
147
|
-
streaming: { ...state.streaming, thinkingTrace: payload },
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
case EXECUTION_TRACE_SET:
|
|
151
|
-
return {
|
|
152
|
-
...state,
|
|
153
|
-
streaming: {
|
|
154
|
-
...state.streaming,
|
|
155
|
-
executionTrace: {
|
|
156
|
-
...state.streaming.executionTrace,
|
|
157
|
-
...payload,
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
case STREAMING_DONE: {
|
|
163
|
-
const newMessages = payload
|
|
164
|
-
? [
|
|
165
|
-
...state.messages,
|
|
166
|
-
{
|
|
167
|
-
message_id: `temp-assistant-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
168
|
-
role: 'assistant',
|
|
169
|
-
content: payload,
|
|
170
|
-
created_at: new Date().toISOString(),
|
|
171
|
-
},
|
|
172
|
-
]
|
|
173
|
-
: state.messages;
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
...state,
|
|
177
|
-
messages: newMessages,
|
|
178
|
-
streaming: initialStreaming,
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
case DOC_GENERATED:
|
|
183
|
-
return { ...state, generatedDoc: payload };
|
|
184
|
-
|
|
185
|
-
case LEGEND_HINT_SHOWN:
|
|
186
|
-
return {
|
|
187
|
-
...state,
|
|
188
|
-
legendHintsShown: {
|
|
189
|
-
...state.legendHintsShown,
|
|
190
|
-
[payload]: true,
|
|
191
|
-
},
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
default:
|
|
195
|
-
return state;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function requireAPI(api, name) {
|
|
200
|
-
if (!api) {
|
|
201
|
-
throw new Error(`${name} is required`);
|
|
202
|
-
}
|
|
203
|
-
return api;
|
|
204
|
-
}
|
|
1
|
+
import { useReducer, useMemo } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
initialState,
|
|
4
|
+
reducer,
|
|
5
|
+
} from './chatSessionReducer.js';
|
|
6
|
+
import { useSessionCrudActions } from './sessionActionsSessionCrud.js';
|
|
7
|
+
import { useMessageActions } from './sessionActionsMessages.js';
|
|
8
|
+
import { useStreamingActions } from './sessionActionsStreaming.js';
|
|
9
|
+
import { useUiStateActions } from './sessionActionsUiState.js';
|
|
10
|
+
|
|
11
|
+
export { initialState } from './chatSessionReducer.js';
|
|
205
12
|
|
|
206
13
|
export default function useChatSessionReducer(deps = {}) {
|
|
207
14
|
const { sessionAPI, messageAPI } = deps;
|
|
208
15
|
const [state, dispatch] = useReducer(reducer, initialState);
|
|
209
16
|
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
...extraPayload
|
|
256
|
-
} = {}) => {
|
|
257
|
-
const sessionApi = requireAPI(sessionAPI, 'sessionAPI');
|
|
258
|
-
const messageApi = requireAPI(messageAPI, 'messageAPI');
|
|
259
|
-
|
|
260
|
-
let sessionId;
|
|
261
|
-
let title;
|
|
262
|
-
|
|
263
|
-
if (forceNew) {
|
|
264
|
-
title = defaultTitle;
|
|
265
|
-
const response = await sessionApi.create({ function_type, title, ...extraPayload });
|
|
266
|
-
sessionId = response.sessionId;
|
|
267
|
-
} else {
|
|
268
|
-
const sessionList = await sessionApi.list({
|
|
269
|
-
function_type,
|
|
270
|
-
status: 'active',
|
|
271
|
-
page: 1,
|
|
272
|
-
page_size: 1,
|
|
273
|
-
...extraPayload,
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
if (sessionList.sessions.length > 0) {
|
|
277
|
-
sessionId = sessionList.sessions[0].sessionId;
|
|
278
|
-
title = sessionList.sessions[0].title;
|
|
279
|
-
} else {
|
|
280
|
-
title = defaultTitle;
|
|
281
|
-
const response = await sessionApi.create({ function_type, title, ...extraPayload });
|
|
282
|
-
sessionId = response.sessionId;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const [msgs, detail] = await Promise.all([
|
|
287
|
-
messageApi.list(sessionId),
|
|
288
|
-
sessionApi.get(sessionId),
|
|
289
|
-
]);
|
|
290
|
-
|
|
291
|
-
dispatch({
|
|
292
|
-
type: SESSION_LOADED,
|
|
293
|
-
payload: {
|
|
294
|
-
sessionId,
|
|
295
|
-
title,
|
|
296
|
-
status: detail.status || 'active',
|
|
297
|
-
messages: msgs.messages,
|
|
298
|
-
},
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
return sessionId;
|
|
302
|
-
}, [sessionAPI, messageAPI]);
|
|
303
|
-
|
|
304
|
-
const resetSession = useCallback(() => {
|
|
305
|
-
dispatch({ type: SESSION_RESET });
|
|
306
|
-
}, []);
|
|
307
|
-
|
|
308
|
-
const updateTitle = useCallback(async (sessionId, title) => {
|
|
309
|
-
const sessionApi = requireAPI(sessionAPI, 'sessionAPI');
|
|
310
|
-
await sessionApi.update(sessionId, { title });
|
|
311
|
-
dispatch({ type: TITLE_UPDATED, payload: title });
|
|
312
|
-
}, [sessionAPI]);
|
|
313
|
-
|
|
314
|
-
const refreshMessages = useCallback(async (sessionId) => {
|
|
315
|
-
const messageApi = requireAPI(messageAPI, 'messageAPI');
|
|
316
|
-
const response = await messageApi.list(sessionId);
|
|
317
|
-
const messagesWithIds = response.messages.map((msg, index) => ({
|
|
318
|
-
...msg,
|
|
319
|
-
message_id: msg.message_id || `fallback-${Date.now()}-${index}`,
|
|
320
|
-
}));
|
|
321
|
-
dispatch({ type: MESSAGES_REFRESHED, payload: messagesWithIds });
|
|
322
|
-
}, [messageAPI]);
|
|
323
|
-
|
|
324
|
-
const appendUserMessage = useCallback((content, metadata = {}) => {
|
|
325
|
-
const payloadMeta = (
|
|
326
|
-
metadata
|
|
327
|
-
&& typeof metadata === 'object'
|
|
328
|
-
&& !Array.isArray(metadata)
|
|
329
|
-
)
|
|
330
|
-
? metadata
|
|
331
|
-
: {};
|
|
332
|
-
dispatch({
|
|
333
|
-
type: MESSAGE_APPENDED,
|
|
334
|
-
payload: {
|
|
335
|
-
message_id: `temp-user-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
336
|
-
role: 'user',
|
|
337
|
-
content,
|
|
338
|
-
created_at: new Date().toISOString(),
|
|
339
|
-
...payloadMeta,
|
|
340
|
-
},
|
|
341
|
-
});
|
|
342
|
-
}, []);
|
|
343
|
-
|
|
344
|
-
const setUICards = useCallback((cards) => {
|
|
345
|
-
dispatch({ type: UI_CARDS_SET, payload: Array.isArray(cards) ? cards : [] });
|
|
346
|
-
}, []);
|
|
347
|
-
|
|
348
|
-
const updateUICard = useCallback((card) => {
|
|
349
|
-
dispatch({ type: UI_CARD_UPDATED, payload: card });
|
|
350
|
-
}, []);
|
|
351
|
-
|
|
352
|
-
const setLastUserMessage = useCallback((content) => {
|
|
353
|
-
dispatch({ type: LAST_USER_MESSAGE_SET, payload: content || '' });
|
|
354
|
-
}, []);
|
|
355
|
-
|
|
356
|
-
const resetStreaming = useCallback(() => {
|
|
357
|
-
dispatch({ type: STREAMING_RESET });
|
|
358
|
-
}, []);
|
|
359
|
-
|
|
360
|
-
const appendStreamChunk = useCallback((chunk) => {
|
|
361
|
-
dispatch({ type: STREAMING_CHUNK, payload: chunk });
|
|
362
|
-
}, []);
|
|
363
|
-
|
|
364
|
-
const setAgentStatus = useCallback((agentStatus) => {
|
|
365
|
-
dispatch({ type: AGENT_STATUS_SET, payload: agentStatus });
|
|
366
|
-
}, []);
|
|
367
|
-
|
|
368
|
-
const setThinkingTrace = useCallback((trace) => {
|
|
369
|
-
dispatch({ type: THINKING_TRACE_SET, payload: trace });
|
|
370
|
-
}, []);
|
|
371
|
-
|
|
372
|
-
const setExecutionTrace = useCallback((executionTrace) => {
|
|
373
|
-
dispatch({ type: EXECUTION_TRACE_SET, payload: executionTrace });
|
|
374
|
-
dispatch({ type: EXECUTION_CARD_UPSERTED, payload: executionTrace });
|
|
375
|
-
}, []);
|
|
376
|
-
|
|
377
|
-
const completeStreaming = useCallback((finalContent) => {
|
|
378
|
-
dispatch({ type: STREAMING_DONE, payload: finalContent });
|
|
379
|
-
}, []);
|
|
380
|
-
|
|
381
|
-
const setGeneratedDoc = useCallback((doc) => {
|
|
382
|
-
dispatch({ type: DOC_GENERATED, payload: doc });
|
|
383
|
-
}, []);
|
|
384
|
-
|
|
385
|
-
const markLegendHintShown = useCallback((sourceType) => {
|
|
386
|
-
dispatch({ type: LEGEND_HINT_SHOWN, payload: sourceType });
|
|
387
|
-
}, []);
|
|
388
|
-
|
|
389
|
-
const setSessionError = useCallback((error) => {
|
|
390
|
-
dispatch({ type: SESSION_ERROR, payload: error });
|
|
391
|
-
}, []);
|
|
392
|
-
|
|
393
|
-
const setInstanceProfile = useCallback((profile) => {
|
|
394
|
-
dispatch({ type: INSTANCE_PROFILE_SET, payload: profile || null });
|
|
395
|
-
}, []);
|
|
17
|
+
const sessionCrudActions = useSessionCrudActions({
|
|
18
|
+
dispatch,
|
|
19
|
+
sessionAPI,
|
|
20
|
+
messageAPI,
|
|
21
|
+
});
|
|
22
|
+
const messageActions = useMessageActions({
|
|
23
|
+
dispatch,
|
|
24
|
+
messageAPI,
|
|
25
|
+
});
|
|
26
|
+
const streamingActions = useStreamingActions({ dispatch });
|
|
27
|
+
const uiStateActions = useUiStateActions({ dispatch });
|
|
28
|
+
|
|
29
|
+
const {
|
|
30
|
+
loadSession,
|
|
31
|
+
createSession,
|
|
32
|
+
createOrLoadSession,
|
|
33
|
+
resetSession,
|
|
34
|
+
updateTitle,
|
|
35
|
+
promoteSession,
|
|
36
|
+
} = sessionCrudActions;
|
|
37
|
+
const {
|
|
38
|
+
refreshMessages,
|
|
39
|
+
appendUserMessage,
|
|
40
|
+
} = messageActions;
|
|
41
|
+
const {
|
|
42
|
+
resetStreaming,
|
|
43
|
+
appendStreamChunk,
|
|
44
|
+
replaceStreamContent,
|
|
45
|
+
setAgentStatus,
|
|
46
|
+
setThinkingTrace,
|
|
47
|
+
setExecutionTrace,
|
|
48
|
+
setKnowledgeSearch,
|
|
49
|
+
setSourcingCandidates,
|
|
50
|
+
setKnowledgeCitation,
|
|
51
|
+
completeStreaming,
|
|
52
|
+
} = streamingActions;
|
|
53
|
+
const {
|
|
54
|
+
setUICards,
|
|
55
|
+
updateUICard,
|
|
56
|
+
setLastUserMessage,
|
|
57
|
+
setGeneratedDoc,
|
|
58
|
+
markLegendHintShown,
|
|
59
|
+
setSessionError,
|
|
60
|
+
setInstanceProfile,
|
|
61
|
+
} = uiStateActions;
|
|
396
62
|
|
|
397
63
|
return useMemo(() => ({
|
|
398
64
|
sessionId: state.sessionId,
|
|
399
65
|
sessionTitle: state.sessionTitle,
|
|
66
|
+
sessionDetail: state.sessionDetail,
|
|
400
67
|
instanceProfile: state.instanceProfile,
|
|
401
68
|
messages: state.messages,
|
|
402
69
|
executionCards: state.executionCards,
|
|
@@ -411,6 +78,7 @@ export default function useChatSessionReducer(deps = {}) {
|
|
|
411
78
|
createOrLoadSession,
|
|
412
79
|
resetSession,
|
|
413
80
|
updateTitle,
|
|
81
|
+
promoteSession,
|
|
414
82
|
refreshMessages,
|
|
415
83
|
setSessionError,
|
|
416
84
|
appendUserMessage,
|
|
@@ -419,9 +87,13 @@ export default function useChatSessionReducer(deps = {}) {
|
|
|
419
87
|
setLastUserMessage,
|
|
420
88
|
resetStreaming,
|
|
421
89
|
appendStreamChunk,
|
|
90
|
+
replaceStreamContent,
|
|
422
91
|
setAgentStatus,
|
|
423
92
|
setThinkingTrace,
|
|
424
93
|
setExecutionTrace,
|
|
94
|
+
setKnowledgeSearch,
|
|
95
|
+
setSourcingCandidates,
|
|
96
|
+
setKnowledgeCitation,
|
|
425
97
|
completeStreaming,
|
|
426
98
|
setGeneratedDoc,
|
|
427
99
|
setInstanceProfile,
|
|
@@ -433,6 +105,7 @@ export default function useChatSessionReducer(deps = {}) {
|
|
|
433
105
|
createOrLoadSession,
|
|
434
106
|
resetSession,
|
|
435
107
|
updateTitle,
|
|
108
|
+
promoteSession,
|
|
436
109
|
refreshMessages,
|
|
437
110
|
setSessionError,
|
|
438
111
|
appendUserMessage,
|
|
@@ -441,9 +114,13 @@ export default function useChatSessionReducer(deps = {}) {
|
|
|
441
114
|
setLastUserMessage,
|
|
442
115
|
resetStreaming,
|
|
443
116
|
appendStreamChunk,
|
|
117
|
+
replaceStreamContent,
|
|
444
118
|
setAgentStatus,
|
|
445
119
|
setThinkingTrace,
|
|
446
120
|
setExecutionTrace,
|
|
121
|
+
setKnowledgeSearch,
|
|
122
|
+
setSourcingCandidates,
|
|
123
|
+
setKnowledgeCitation,
|
|
447
124
|
completeStreaming,
|
|
448
125
|
setGeneratedDoc,
|
|
449
126
|
setInstanceProfile,
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Session list lifecycle controller.
|
|
5
|
+
*
|
|
6
|
+
* @param {object} params - Hook params.
|
|
7
|
+
* @param {object} params.sessionAPI - Session API provider.
|
|
8
|
+
* @param {object} params.listParams - Session list query params.
|
|
9
|
+
* @param {Function} params.onStarted - Optional started callback.
|
|
10
|
+
* @param {Function} params.onSucceeded - Optional succeeded callback.
|
|
11
|
+
* @param {Function} params.onFailed - Optional failed callback.
|
|
12
|
+
* @returns {{sessions: Array<object>, loading: boolean, error: any, loadSessionList: Function}}
|
|
13
|
+
*/
|
|
14
|
+
export default function useSessionListController({
|
|
15
|
+
sessionAPI,
|
|
16
|
+
listParams = {},
|
|
17
|
+
onStarted,
|
|
18
|
+
onSucceeded,
|
|
19
|
+
onFailed,
|
|
20
|
+
} = {}) {
|
|
21
|
+
const [sessions, setSessions] = useState([]);
|
|
22
|
+
const [loading, setLoading] = useState(false);
|
|
23
|
+
const [error, setError] = useState(null);
|
|
24
|
+
const [hasLoaded, setHasLoaded] = useState(false);
|
|
25
|
+
|
|
26
|
+
const loadSessionList = useCallback(async (options = {}) => {
|
|
27
|
+
if (!sessionAPI?.list) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
const background = options?.background === true;
|
|
31
|
+
const shouldShowLoading = !background && !hasLoaded;
|
|
32
|
+
|
|
33
|
+
onStarted?.();
|
|
34
|
+
if (shouldShowLoading) {
|
|
35
|
+
setLoading(true);
|
|
36
|
+
}
|
|
37
|
+
if (!background) {
|
|
38
|
+
setError(null);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const response = await sessionAPI.list(listParams);
|
|
43
|
+
const resolvedSessions = Array.isArray(response?.sessions) ? response.sessions : [];
|
|
44
|
+
setSessions(resolvedSessions);
|
|
45
|
+
setHasLoaded(true);
|
|
46
|
+
onSucceeded?.(resolvedSessions);
|
|
47
|
+
return resolvedSessions;
|
|
48
|
+
} catch (nextError) {
|
|
49
|
+
setError(nextError);
|
|
50
|
+
onFailed?.(nextError);
|
|
51
|
+
throw nextError;
|
|
52
|
+
} finally {
|
|
53
|
+
if (shouldShowLoading) {
|
|
54
|
+
setLoading(false);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}, [hasLoaded, listParams, onFailed, onStarted, onSucceeded, sessionAPI]);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
sessions,
|
|
61
|
+
loading,
|
|
62
|
+
error,
|
|
63
|
+
loadSessionList,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { useSessionListController };
|
|
@@ -1,142 +1 @@
|
|
|
1
|
-
|
|
2
|
-
const DEFAULT_ENDPOINT = '/api/v1/chat/stream';
|
|
3
|
-
|
|
4
|
-
function joinUrl(baseUrl, endpoint) {
|
|
5
|
-
if (!baseUrl) {
|
|
6
|
-
return endpoint;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
if (!endpoint) {
|
|
10
|
-
return baseUrl;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
return `${baseUrl.replace(/\/+$/, '')}/${endpoint.replace(/^\/+/, '')}`;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export class SSEClient {
|
|
17
|
-
constructor({ endpoint = DEFAULT_ENDPOINT, baseUrl = API_BASE_URL } = {}) {
|
|
18
|
-
this.abortController = null;
|
|
19
|
-
this.endpoint = endpoint;
|
|
20
|
-
this.baseUrl = baseUrl;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async sendMessage(sessionOrParams, contentArg, onEventArg, onCompleteArg, onErrorArg, optionsArg = {}) {
|
|
24
|
-
const isObjectCall = typeof sessionOrParams === 'object' && sessionOrParams !== null;
|
|
25
|
-
const params = isObjectCall
|
|
26
|
-
? sessionOrParams
|
|
27
|
-
: {
|
|
28
|
-
sessionId: sessionOrParams,
|
|
29
|
-
content: contentArg,
|
|
30
|
-
enabledCapabilities: optionsArg.enabledCapabilities || [],
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const sessionId = params.sessionId;
|
|
34
|
-
const content = params.content;
|
|
35
|
-
const enabledCapabilities = Array.isArray(params.enabledCapabilities) ? params.enabledCapabilities : [];
|
|
36
|
-
const modeKey = typeof params.modeKey === 'string' ? params.modeKey.trim() : '';
|
|
37
|
-
const manualModeKey = typeof params.manualModeKey === 'string' ? params.manualModeKey.trim() : '';
|
|
38
|
-
const currentModeKey = typeof params.currentModeKey === 'string' ? params.currentModeKey.trim() : '';
|
|
39
|
-
const payloadExtra = (
|
|
40
|
-
params.payloadExtra
|
|
41
|
-
&& typeof params.payloadExtra === 'object'
|
|
42
|
-
&& !Array.isArray(params.payloadExtra)
|
|
43
|
-
)
|
|
44
|
-
? params.payloadExtra
|
|
45
|
-
: {};
|
|
46
|
-
const onEvent = isObjectCall ? contentArg : onEventArg;
|
|
47
|
-
const onComplete = isObjectCall ? onEventArg : onCompleteArg;
|
|
48
|
-
const onError = isObjectCall ? onCompleteArg : onErrorArg;
|
|
49
|
-
const url = params.url || joinUrl(params.baseUrl ?? this.baseUrl, params.endpoint || this.endpoint);
|
|
50
|
-
|
|
51
|
-
this.abortController = new AbortController();
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const response = await fetch(url, {
|
|
55
|
-
method: 'POST',
|
|
56
|
-
headers: {
|
|
57
|
-
'Content-Type': 'application/json',
|
|
58
|
-
},
|
|
59
|
-
body: JSON.stringify({
|
|
60
|
-
message: content,
|
|
61
|
-
session_id: sessionId,
|
|
62
|
-
enabled_capabilities: enabledCapabilities,
|
|
63
|
-
...(modeKey ? { mode: modeKey } : {}),
|
|
64
|
-
...(manualModeKey ? { manual_mode: manualModeKey } : {}),
|
|
65
|
-
...(currentModeKey ? { current_mode: currentModeKey } : {}),
|
|
66
|
-
payload: {
|
|
67
|
-
message: content,
|
|
68
|
-
...(modeKey ? { mode: modeKey } : {}),
|
|
69
|
-
...(manualModeKey ? { manual_mode: manualModeKey } : {}),
|
|
70
|
-
...(currentModeKey ? { current_mode: currentModeKey } : {}),
|
|
71
|
-
...payloadExtra,
|
|
72
|
-
},
|
|
73
|
-
}),
|
|
74
|
-
signal: this.abortController.signal,
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
if (!response.ok) {
|
|
78
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const reader = response.body.getReader();
|
|
82
|
-
const decoder = new TextDecoder();
|
|
83
|
-
let buffer = '';
|
|
84
|
-
let currentEvent = '';
|
|
85
|
-
|
|
86
|
-
while (true) {
|
|
87
|
-
const { done, value } = await reader.read();
|
|
88
|
-
if (done) break;
|
|
89
|
-
|
|
90
|
-
buffer += decoder.decode(value, { stream: true });
|
|
91
|
-
const lines = buffer.split('\n');
|
|
92
|
-
buffer = lines.pop() || '';
|
|
93
|
-
|
|
94
|
-
for (const line of lines) {
|
|
95
|
-
if (line.startsWith('event:')) {
|
|
96
|
-
currentEvent = line.substring(6).trim();
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (!line.startsWith('data:')) {
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const data = line.substring(5).trim();
|
|
105
|
-
if (!data || !currentEvent) {
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
const eventData = JSON.parse(data);
|
|
111
|
-
onEvent?.({
|
|
112
|
-
type: currentEvent,
|
|
113
|
-
data: eventData,
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
if (currentEvent === 'complete') {
|
|
117
|
-
onComplete?.();
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
} catch (error) {
|
|
121
|
-
console.error('SSE 数据解析失败:', error, 'data:', data);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
onComplete?.();
|
|
127
|
-
} catch (error) {
|
|
128
|
-
if (error?.name === 'AbortError') {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
onError?.(error instanceof Error ? error.message : '发送消息失败');
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
abort() {
|
|
137
|
-
if (this.abortController) {
|
|
138
|
-
this.abortController.abort();
|
|
139
|
-
this.abortController = null;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
1
|
+
export { SSEClient } from '../../adapters/sse-client.js';
|