flare-chat-core 0.2.2 → 0.2.4

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.
Files changed (122) hide show
  1. package/dist/index.js +6343 -0
  2. package/package.json +19 -22
  3. package/docs/CAPABILITY-INVENTORY.md +0 -42
  4. package/docs/CHAT-CORE-BOUNDARY.md +0 -47
  5. package/docs/CORE-APP-REALIGNMENT-WORKLOAD-2026-04-18.md +0 -86
  6. package/docs/SSOT-CHAT-CORE-BOUNDARY.md +0 -73
  7. package/docs/SSOT-CHAT-CORE-DATAFLOW.md +0 -97
  8. package/index.html +0 -12
  9. package/scripts/check.sh +0 -15
  10. package/src/adapters/index.js +0 -6
  11. package/src/adapters/message-api.adapter.js +0 -59
  12. package/src/adapters/session-api.adapter.js +0 -133
  13. package/src/adapters/session-message-api.http.js +0 -161
  14. package/src/adapters/session-message-api.js +0 -34
  15. package/src/adapters/session-message-api.normalize-source-record.test.mjs +0 -180
  16. package/src/adapters/session-message-api.normalizers.js +0 -153
  17. package/src/adapters/source-api.adapter.js +0 -135
  18. package/src/adapters/sse-client.js +0 -244
  19. package/src/adapters/sse-event-dispatcher.js +0 -121
  20. package/src/app/App.jsx +0 -11
  21. package/src/app/AppProviders.jsx +0 -12
  22. package/src/app/ChatWorkspaceScreen.jsx +0 -33
  23. package/src/app/WorkspaceLayout.jsx +0 -125
  24. package/src/app/components/AppCanvasPanel.jsx +0 -64
  25. package/src/app/components/TriggerThresholdPopoverContent.jsx +0 -122
  26. package/src/app/components/WorkspaceBodySection.jsx +0 -109
  27. package/src/app/components/WorkspaceMainPane.jsx +0 -113
  28. package/src/app/components/WorkspaceSessionPane.jsx +0 -48
  29. package/src/app/components/WorkspaceTopBarSection.jsx +0 -65
  30. package/src/app/core-chat-entry/ComposerSectionNode.jsx +0 -241
  31. package/src/app/core-chat-entry/attachmentSendRefs.js +0 -154
  32. package/src/app/core-chat-entry/attachmentSendRefs.test.mjs +0 -101
  33. package/src/app/core-chat-entry/composerActionRouter.js +0 -26
  34. package/src/app/core-chat-entry/constants.js +0 -108
  35. package/src/app/core-chat-entry/selectors.js +0 -28
  36. package/src/app/core-chat-entry/useAppActionErrorGuards.js +0 -68
  37. package/src/app/core-chat-entry/useChatCorePipelines.js +0 -110
  38. package/src/app/core-chat-entry/useComposerModeSuggestion.js +0 -89
  39. package/src/app/core-chat-entry/useDevCapabilityStatusNote.js +0 -22
  40. package/src/app/core-chat-entry/useProjectNameEditing.js +0 -41
  41. package/src/app/core-chat-entry/useProjectSourceUpload.js +0 -341
  42. package/src/app/core-chat-entry/useRealApiReadinessGate.js +0 -103
  43. package/src/app/core-chat-entry/useUnavailableActionError.js +0 -29
  44. package/src/app/core-chat-entry/useWorkspaceCanvasController.jsx +0 -177
  45. package/src/app/core-chat-entry/useWorkspaceCanvasProjection.jsx +0 -171
  46. package/src/app/core-chat-entry/useWorkspaceComposerController.jsx +0 -199
  47. package/src/app/core-chat-entry/useWorkspaceController.jsx +0 -226
  48. package/src/app/core-chat-entry/useWorkspacePanels.js +0 -55
  49. package/src/app/hooks/useComposerAttachmentSync.js +0 -223
  50. package/src/app/hooks/useComposerChooserHandlers.js +0 -52
  51. package/src/app/hooks/useSendWithContextRefs.js +0 -140
  52. package/src/app/hooks/useSendWithContextRefs.test.mjs +0 -29
  53. package/src/app/hooks/useUserThresholdProfile.js +0 -121
  54. package/src/app/index.js +0 -1
  55. package/src/app/selectors/assistantTextSelector.js +0 -73
  56. package/src/app/selectors/canvasEvidenceSummarySelector.js +0 -28
  57. package/src/app/selectors/canvasReportTemplateSelector.js +0 -28
  58. package/src/app/selectors/canvasTabsSelector.js +0 -58
  59. package/src/app/selectors/evidenceProjectionSelector.js +0 -175
  60. package/src/app/selectors/evidenceProjectionSelector.test.mjs +0 -107
  61. package/src/app/selectors/modeSuggestionSelector.js +0 -50
  62. package/src/chat-core/app/mockRuntime.js +0 -291
  63. package/src/chat-core/app/useAppStream.js +0 -187
  64. package/src/chat-core/app/useAppStream.refs.test.mjs +0 -44
  65. package/src/chat-core/app/useAppStream.request-body.test.mjs +0 -116
  66. package/src/chat-core/app/useCoreChatApp.js +0 -115
  67. package/src/chat-core/facade/useBasicConversationFacade.js +0 -280
  68. package/src/chat-core/index.js +0 -14
  69. package/src/chat-core/input/useChatInput.js +0 -103
  70. package/src/chat-core/messages/buildTimelineItems.analysis-route.test.mjs +0 -36
  71. package/src/chat-core/messages/buildTimelineItems.js +0 -233
  72. package/src/chat-core/messages/buildTimelineItems.knowledge-citation.test.mjs +0 -183
  73. package/src/chat-core/messages/contextUsageDefaults.js +0 -3
  74. package/src/chat-core/messages/contextUsageViewModel.js +0 -147
  75. package/src/chat-core/messages/contextUsageViewModel.test.mjs +0 -74
  76. package/src/chat-core/messages/useContextUsageViewModel.js +0 -41
  77. package/src/chat-core/orchestration/useBasicSendHandler.js +0 -55
  78. package/src/chat-core/pipelines/build-action-request.js +0 -46
  79. package/src/chat-core/pipelines/build-stream-request.js +0 -74
  80. package/src/chat-core/pipelines/entity-extraction.js +0 -159
  81. package/src/chat-core/pipelines/preprocess-message.js +0 -16
  82. package/src/chat-core/pipelines/stream-persist-utils.js +0 -32
  83. package/src/chat-core/pipelines/transport/send-mock-stream.js +0 -86
  84. package/src/chat-core/pipelines/transport/send-real-stream.js +0 -330
  85. package/src/chat-core/pipelines/transport/send-real-stream.test.mjs +0 -27
  86. package/src/chat-core/pipelines/transport/send-sourcing-search.js +0 -86
  87. package/src/chat-core/pipelines/transport/send-sourcing-search.test.mjs +0 -14
  88. package/src/chat-core/pipelines/transport/sourcing-response-templates.js +0 -55
  89. package/src/chat-core/pipelines/transport/sourcing-search-api.js +0 -155
  90. package/src/chat-core/runtime/runtimeMode.js +0 -69
  91. package/src/chat-core/session/chatSessionActionTypes.js +0 -24
  92. package/src/chat-core/session/chatSessionReducer.js +0 -352
  93. package/src/chat-core/session/chatSessionReducer.streaming-done.test.mjs +0 -39
  94. package/src/chat-core/session/index.js +0 -2
  95. package/src/chat-core/session/sessionActionsMessages.js +0 -44
  96. package/src/chat-core/session/sessionActionsSessionCrud.js +0 -131
  97. package/src/chat-core/session/sessionActionsStreaming.js +0 -80
  98. package/src/chat-core/session/sessionActionsUiState.js +0 -51
  99. package/src/chat-core/session/useChatSessionReducer.js +0 -131
  100. package/src/chat-core/session/useSessionListController.js +0 -67
  101. package/src/chat-core/stream/sse-client.js +0 -1
  102. package/src/chat-core/stream/sse-event-dispatcher.js +0 -1
  103. package/src/chat-core/stream/sse-events.js +0 -1
  104. package/src/chat-core/stream/useSSEStream.js +0 -1
  105. package/src/chat-core/stream/useStreamSendController.js +0 -46
  106. package/src/contracts/context-ssot.js +0 -47
  107. package/src/contracts/index.js +0 -1
  108. package/src/contracts/sse-events/base-parsers.js +0 -79
  109. package/src/contracts/sse-events/domain-parsers.js +0 -3
  110. package/src/contracts/sse-events/internal-normalizers.js +0 -143
  111. package/src/contracts/sse-events/parsers-intake.js +0 -235
  112. package/src/contracts/sse-events/parsers-runtime.js +0 -37
  113. package/src/contracts/sse-events/parsers-sourcing.js +0 -179
  114. package/src/contracts/sse-events/patch-event-parser.js +0 -121
  115. package/src/contracts/sse-events/runtime-parsers.js +0 -79
  116. package/src/contracts/sse-events.js +0 -4
  117. package/src/index.js +0 -6
  118. package/src/main.jsx +0 -28
  119. package/src/orchestration/index.js +0 -6
  120. package/src/orchestration/useSSEStream.js +0 -221
  121. package/src/state/index.js +0 -4
  122. package/vite.config.js +0 -36
@@ -1,115 +0,0 @@
1
- import { useEffect, useMemo, useRef } from 'react';
2
- import { useBasicConversationFacade } from '../facade/useBasicConversationFacade.js';
3
-
4
- /**
5
- * Core app-level chat orchestration hook.
6
- * Owns state/actions mapping for UI component libraries.
7
- */
8
- export default function useCoreChatApp({
9
- sessionAPI,
10
- messageAPI,
11
- stream,
12
- functionType,
13
- defaultSessionTitle,
14
- listParams,
15
- createSessionPayload = {},
16
- } = {}) {
17
- const didInitialRefreshRef = useRef(false);
18
- const chat = useBasicConversationFacade({
19
- sessionAPI,
20
- messageAPI,
21
- stream,
22
- functionType,
23
- defaultSessionTitle,
24
- listParams,
25
- defaultSessionPayload: createSessionPayload,
26
- });
27
-
28
- useEffect(() => {
29
- if (didInitialRefreshRef.current) {
30
- return;
31
- }
32
- didInitialRefreshRef.current = true;
33
- chat.actions.refreshSessionList();
34
- }, [chat.actions.refreshSessionList]);
35
-
36
- const viewModel = useMemo(() => ({
37
- sessionList: {
38
- sessions: chat.state.sessions.items,
39
- error: chat.state.sessions.error,
40
- activeSessionId: chat.state.session.sessionId,
41
- onSelectSession: chat.actions.selectSession,
42
- onCreateSession: () => chat.actions.createSession(createSessionPayload),
43
- onCreateProject: () => chat.actions.createSession(createSessionPayload),
44
- onRenameSession: async (session, nextTitle) => {
45
- const sessionId = String(session?.sessionId || session?.session_id || session?.id || '').trim();
46
- const title = String(nextTitle || '').trim();
47
- if (!sessionId || !title) {
48
- return;
49
- }
50
- await chat.actions.updateTitle(sessionId, title);
51
- await chat.actions.refreshSessionList();
52
- },
53
- onArchiveSession: async (session) => {
54
- const sessionId = String(session?.sessionId || session?.session_id || session?.id || '').trim();
55
- if (!sessionId || !sessionAPI?.update) {
56
- return;
57
- }
58
- await sessionAPI.update(sessionId, { status: 'archived' });
59
- const latestSessions = await chat.actions.refreshSessionList();
60
- if (sessionId !== chat.state.session.sessionId) {
61
- return;
62
- }
63
- const nextActive = Array.isArray(latestSessions)
64
- ? latestSessions.find((item) => {
65
- const id = String(item?.sessionId || item?.session_id || item?.id || '').trim();
66
- const status = String(item?.status || 'active').trim();
67
- return Boolean(id) && status !== 'archived';
68
- })
69
- : null;
70
- const nextSessionId = String(
71
- nextActive?.sessionId
72
- || nextActive?.session_id
73
- || nextActive?.id
74
- || ''
75
- ).trim();
76
- if (nextSessionId) {
77
- await chat.actions.selectSession(nextSessionId);
78
- return;
79
- }
80
- chat.actions.resetSession();
81
- },
82
- projectItems: Array.isArray(chat.state.sessions.items) && chat.state.sessions.items.length > 0
83
- ? null
84
- : [],
85
- },
86
- timeline: {
87
- items: chat.state.timeline.items,
88
- loading: chat.state.composer.loading,
89
- },
90
- composer: {
91
- value: chat.state.composer.inputValue,
92
- loading: chat.state.composer.loading,
93
- error: chat.state.composer.error,
94
- fileList: chat.state.composer.fileList,
95
- hintText: chat.state.session.error
96
- ? String(chat.state.session.error?.message || chat.state.session.error)
97
- : '',
98
- onChange: (event) => chat.actions.setInputValue(event?.target?.value || ''),
99
- onSend: (options = {}) => chat.actions.send(options),
100
- onAttachFiles: (files) => chat.actions.attachFiles(files),
101
- onRemoveFile: (file) => chat.actions.removeFile(file),
102
- },
103
- toolbar: {
104
- onRefreshSessions: chat.actions.refreshSessionList,
105
- onCreateSession: () => chat.actions.createSession(createSessionPayload),
106
- },
107
- }), [chat.actions, chat.state.composer.error, chat.state.composer.fileList, chat.state.composer.inputValue, chat.state.composer.loading, chat.state.session.error, chat.state.session.sessionId, chat.state.sessions.error, chat.state.sessions.items, chat.state.timeline.items, createSessionPayload, sessionAPI]);
108
-
109
- return {
110
- chat,
111
- viewModel,
112
- };
113
- }
114
-
115
- export { useCoreChatApp };
@@ -1,280 +0,0 @@
1
- import { useCallback, useMemo } from 'react';
2
- import useChatSessionReducer from '../session/useChatSessionReducer.js';
3
- import { useChatInput } from '../input/useChatInput.js';
4
- import { useSSEStream } from '../stream/useSSEStream.js';
5
- import { buildTimelineItems } from '../messages/buildTimelineItems.js';
6
- import { useSessionListController } from '../session/useSessionListController.js';
7
- import { useStreamSendController } from '../stream/useStreamSendController.js';
8
- import { useBasicSendHandler } from '../orchestration/useBasicSendHandler.js';
9
-
10
- function normalizeRefs(value) {
11
- if (!Array.isArray(value)) {
12
- return null;
13
- }
14
- return value.filter((item) => item !== undefined && item !== null);
15
- }
16
-
17
- function resolvePromoteSessionPayload(raw = {}) {
18
- const source = (
19
- raw
20
- && typeof raw === 'object'
21
- && !Array.isArray(raw)
22
- ) ? raw : {};
23
- const sessionEnvelope = (
24
- source.session
25
- && typeof source.session === 'object'
26
- && !Array.isArray(source.session)
27
- ) ? source.session : {};
28
- const payloadEnvelope = (
29
- source.payload
30
- && typeof source.payload === 'object'
31
- && !Array.isArray(source.payload)
32
- ) ? source.payload : {};
33
- const payloadSessionEnvelope = (
34
- payloadEnvelope.session
35
- && typeof payloadEnvelope.session === 'object'
36
- && !Array.isArray(payloadEnvelope.session)
37
- ) ? payloadEnvelope.session : {};
38
- const sessionId = String(
39
- source.sessionId
40
- || sessionEnvelope.session_id
41
- || ''
42
- ).trim();
43
- const title = String(
44
- payloadSessionEnvelope.title
45
- || ''
46
- ).trim();
47
- return { sessionId, title };
48
- }
49
-
50
- /**
51
- * Basic conversation facade.
52
- *
53
- * Data flow:
54
- * 1) UI intent -> facade action
55
- * 2) facade preflight -> round execution
56
- * 3) stream event -> session state
57
- * 4) selector(buildTimelineItems) -> UI model
58
- */
59
- export default function useBasicConversationFacade({
60
- sessionAPI,
61
- messageAPI,
62
- stream,
63
- listParams = {},
64
- functionType = 'chat_component_debug',
65
- defaultSessionTitle = '新会话',
66
- defaultSessionPayload = {},
67
- } = {}) {
68
- const chatSession = useChatSessionReducer({ sessionAPI, messageAPI });
69
- const chatInput = useChatInput();
70
- const sseStream = useSSEStream(chatSession.sessionId);
71
- const runtimeStream = stream || sseStream;
72
-
73
- const sessionList = useSessionListController({
74
- sessionAPI,
75
- listParams,
76
- onFailed: chatSession.setSessionError,
77
- });
78
-
79
- const streamSend = useStreamSendController({
80
- stream: runtimeStream,
81
- onStarted: () => {
82
- chatSession.setSessionError(null);
83
- },
84
- onChunk: (chunk) => {
85
- chatSession.appendStreamChunk(chunk);
86
- },
87
- onFailed: (error) => {
88
- chatSession.setSessionError(error);
89
- },
90
- });
91
-
92
- const resolveSendPreflight = useCallback(async (options = {}) => {
93
- const content = String(options?.content ?? chatInput.inputValue ?? '').trim();
94
- if (!content) {
95
- return { blocked: true };
96
- }
97
-
98
- let sessionId = String(chatSession.sessionId || '').trim();
99
- let createdNewSession = false;
100
- if (!sessionId) {
101
- sessionId = await chatSession.createSession({
102
- function_type: functionType,
103
- title: defaultSessionTitle,
104
- ...defaultSessionPayload,
105
- });
106
- createdNewSession = true;
107
- }
108
-
109
- const streamOptions = (
110
- options.streamOptions
111
- && typeof options.streamOptions === 'object'
112
- && !Array.isArray(options.streamOptions)
113
- ) ? { ...options.streamOptions } : {};
114
- const contextRefs = normalizeRefs(options.context_refs);
115
- const knowledgeRefs = normalizeRefs(options.knowledge_refs);
116
- if (contextRefs && !Object.prototype.hasOwnProperty.call(streamOptions, 'context_refs')) {
117
- streamOptions.context_refs = contextRefs;
118
- }
119
- if (knowledgeRefs && !Object.prototype.hasOwnProperty.call(streamOptions, 'knowledge_refs')) {
120
- streamOptions.knowledge_refs = knowledgeRefs;
121
- }
122
-
123
- return {
124
- blocked: false,
125
- content,
126
- sessionId,
127
- createdNewSession,
128
- preserveInputOnError: options.preserveInputOnError === true,
129
- attachedSessionFilesSnapshot: Array.isArray(chatInput.fileList) ? chatInput.fileList : [],
130
- streamOptions,
131
- };
132
- }, [chatInput.fileList, chatInput.inputValue, chatSession, defaultSessionPayload, defaultSessionTitle, functionType]);
133
-
134
- const executeSendRound = useCallback(async ({ sendPreflight }) => {
135
- chatSession.appendUserMessage(sendPreflight.content);
136
- chatSession.setLastUserMessage(sendPreflight.content);
137
- chatSession.resetStreaming();
138
- chatInput.clearInput();
139
-
140
- await streamSend.executeStreamSend(
141
- sendPreflight.content,
142
- {
143
- onTextReplace: (content) => {
144
- chatSession.replaceStreamContent(content);
145
- },
146
- onAgentStatus: (agentStatus) => {
147
- chatSession.setAgentStatus(agentStatus);
148
- },
149
- onThinkingTrace: (trace) => {
150
- chatSession.setThinkingTrace(trace);
151
- },
152
- onExecutionTrace: (trace) => {
153
- chatSession.setExecutionTrace(trace);
154
- },
155
- onKnowledgeSearch: (searchPayload) => {
156
- chatSession.setKnowledgeSearch(searchPayload);
157
- },
158
- onSourcingCandidates: (sourcingPayload) => {
159
- chatSession.setSourcingCandidates(sourcingPayload);
160
- },
161
- onKnowledgeCitation: (citationPayload) => {
162
- chatSession.setKnowledgeCitation(citationPayload);
163
- },
164
- onPatchEvent: (patchEvent) => {
165
- const patchSession = resolvePromoteSessionPayload(patchEvent);
166
- if (!patchSession.sessionId) {
167
- return;
168
- }
169
- if (patchSession.sessionId !== chatSession.sessionId || patchSession.title) {
170
- chatSession.promoteSession({
171
- sessionId: patchSession.sessionId,
172
- ...(patchSession.title ? { title: patchSession.title } : {}),
173
- });
174
- }
175
- },
176
- onComplete: async (finalContent, doneMeta = {}) => {
177
- const doneSession = resolvePromoteSessionPayload(doneMeta);
178
- if (doneSession.sessionId && doneSession.sessionId !== chatSession.sessionId) {
179
- chatSession.promoteSession({ sessionId: doneSession.sessionId });
180
- }
181
-
182
- chatSession.completeStreaming(finalContent, {
183
- session_id: doneSession.sessionId || sendPreflight.sessionId,
184
- });
185
-
186
- if (sendPreflight.createdNewSession) {
187
- void sessionList.loadSessionList({ background: true });
188
- }
189
- },
190
- },
191
- {
192
- sessionIdOverride: sendPreflight.sessionId,
193
- ...sendPreflight.streamOptions,
194
- }
195
- );
196
- }, [chatInput, chatSession, sessionList, streamSend]);
197
-
198
- const handleSendFailureRecovery = useCallback(({ preserveInputOnError, content }) => {
199
- if (preserveInputOnError) {
200
- chatInput.handleInputChange({ target: { value: content } });
201
- }
202
- }, [chatInput]);
203
-
204
- const sendHandler = useBasicSendHandler({
205
- resolveSendPreflight,
206
- executeSendRound,
207
- handleSendFailureRecovery,
208
- emptyArray: [],
209
- });
210
-
211
- const createSession = useCallback(async (options = {}) => {
212
- const mergedOptions = {
213
- ...defaultSessionPayload,
214
- ...options,
215
- };
216
- const sessionId = await chatSession.createSession({
217
- function_type: functionType,
218
- title: String(mergedOptions?.title || defaultSessionTitle),
219
- project_id: mergedOptions?.project_id ?? null,
220
- user_id: mergedOptions?.user_id ?? null,
221
- });
222
- await sessionList.loadSessionList({ background: true });
223
- return sessionId;
224
- }, [chatSession, defaultSessionPayload, defaultSessionTitle, functionType, sessionList]);
225
-
226
- const selectSession = useCallback(async (sessionId) => {
227
- await chatSession.loadSession(sessionId);
228
- }, [chatSession]);
229
-
230
- const refreshSessionList = useCallback(async () => {
231
- return sessionList.loadSessionList({ background: true });
232
- }, [sessionList]);
233
-
234
- const timeline = useMemo(() => buildTimelineItems(chatSession, { loading: runtimeStream.loading }), [chatSession, runtimeStream.loading]);
235
-
236
- const state = useMemo(() => ({
237
- session: {
238
- sessionId: chatSession.sessionId,
239
- title: chatSession.sessionTitle,
240
- detail: chatSession.sessionDetail,
241
- error: chatSession.sessionError,
242
- },
243
- sessions: {
244
- items: sessionList.sessions,
245
- loading: sessionList.loading,
246
- error: sessionList.error,
247
- },
248
- composer: {
249
- inputValue: chatInput.inputValue,
250
- fileList: chatInput.fileList,
251
- isDragging: chatInput.isDragging,
252
- loading: runtimeStream.loading,
253
- error: runtimeStream.error,
254
- },
255
- timeline,
256
- }), [chatInput.fileList, chatInput.inputValue, chatInput.isDragging, chatSession.sessionDetail, chatSession.sessionError, chatSession.sessionId, chatSession.sessionTitle, runtimeStream.error, runtimeStream.loading, sessionList.error, sessionList.loading, sessionList.sessions, timeline]);
257
-
258
- const actions = useMemo(() => ({
259
- createSession,
260
- selectSession,
261
- refreshSessionList,
262
- resetSession: chatSession.resetSession,
263
- loadSession: chatSession.loadSession,
264
- updateTitle: chatSession.updateTitle,
265
- setInputValue: (value) => chatInput.handleInputChange({ target: { value } }),
266
- clearInput: chatInput.clearInput,
267
- attachFiles: chatInput.appendFiles,
268
- removeFile: chatInput.handleRemoveFile,
269
- send: sendHandler.handleSend,
270
- retry: runtimeStream.retry,
271
- abort: runtimeStream.abort,
272
- }), [chatInput, chatSession.loadSession, chatSession.resetSession, chatSession.updateTitle, createSession, refreshSessionList, runtimeStream.abort, runtimeStream.retry, selectSession, sendHandler.handleSend]);
273
-
274
- return {
275
- state,
276
- actions,
277
- };
278
- }
279
-
280
- export { useBasicConversationFacade };
@@ -1,14 +0,0 @@
1
- export { useChatSessionReducer, initialState as chatSessionInitialState } from './session/index.js';
2
- export { useChatInput } from './input/useChatInput.js';
3
- export { useSSEStream } from './stream/useSSEStream.js';
4
- export { SSEClient } from './stream/sse-client.js';
5
- export * from './stream/sse-events.js';
6
- export { buildTimelineItems } from './messages/buildTimelineItems.js';
7
- export { useSessionListController } from './session/index.js';
8
- export { useStreamSendController } from './stream/useStreamSendController.js';
9
- export { useBasicSendHandler } from './orchestration/useBasicSendHandler.js';
10
- export { useBasicConversationFacade } from './facade/useBasicConversationFacade.js';
11
- export { useCoreChatApp } from './app/useCoreChatApp.js';
12
- export { useAppStream } from './app/useAppStream.js';
13
- export { createMockRuntime } from './app/mockRuntime.js';
14
- export * from './runtime/runtimeMode.js';
@@ -1,103 +0,0 @@
1
- import { useState, useCallback } from 'react';
2
-
3
- function buildAttachmentFile(file, index = 0, status = 'ready') {
4
- return {
5
- uid: file?.uid || `${Date.now()}-${index}`,
6
- name: file?.name || `附件-${index + 1}`,
7
- size: Number.isFinite(file?.size) ? file.size : null,
8
- type: file?.type || '',
9
- originFileObj: file?.originFileObj || file || null,
10
- status: file?.status || status,
11
- error_message: file?.error_message || '',
12
- created_at: file?.created_at || new Date().toISOString(),
13
- };
14
- }
15
-
16
- export function useChatInput() {
17
- const [inputValue, setInputValue] = useState('');
18
- const [fileList, setFileList] = useState([]);
19
- const [isDragging, setIsDragging] = useState(false);
20
-
21
- const handleInputChange = useCallback((e) => {
22
- setInputValue(e.target.value);
23
- }, []);
24
-
25
- const handleFileChange = useCallback(({ fileList: newFileList }) => {
26
- setFileList(newFileList);
27
- }, []);
28
-
29
- const handleRemoveFile = useCallback((file) => {
30
- setFileList((prev) => prev.filter((f) => f.uid !== file.uid));
31
- }, []);
32
-
33
- const handleDrop = useCallback((e) => {
34
- e.preventDefault();
35
- e.stopPropagation();
36
- setIsDragging(false);
37
-
38
- const files = Array.from(e.dataTransfer.files);
39
- const newFiles = files.map((file, index) => buildAttachmentFile(file, index));
40
-
41
- setFileList((prev) => [...prev, ...newFiles]);
42
- return files.length;
43
- }, []);
44
-
45
- const appendFiles = useCallback((files = [], options = {}) => {
46
- const normalized = Array.isArray(files)
47
- ? files.map((file, index) => buildAttachmentFile(file, index, options.status || 'ready'))
48
- : [];
49
- if (normalized.length === 0) {
50
- return;
51
- }
52
- setFileList((prev) => [...prev, ...normalized]);
53
- }, []);
54
-
55
- const updateFileStatus = useCallback((uid, status, errorMessage = '') => {
56
- if (!uid) {
57
- return;
58
- }
59
- setFileList((prev) => prev.map((file) => (
60
- file.uid === uid
61
- ? {
62
- ...file,
63
- status: status || file.status,
64
- error_message: errorMessage || '',
65
- }
66
- : file
67
- )));
68
- }, []);
69
-
70
- const handleDragOver = useCallback((e) => {
71
- e.preventDefault();
72
- e.stopPropagation();
73
- setIsDragging(true);
74
- }, []);
75
-
76
- const handleDragLeave = useCallback((e) => {
77
- e.preventDefault();
78
- e.stopPropagation();
79
- setIsDragging(false);
80
- }, []);
81
-
82
- const clearInput = useCallback(() => {
83
- setInputValue('');
84
- setFileList([]);
85
- }, []);
86
-
87
- return {
88
- inputValue,
89
- fileList,
90
- isDragging,
91
- handleInputChange,
92
- handleFileChange,
93
- handleRemoveFile,
94
- appendFiles,
95
- updateFileStatus,
96
- handleDrop,
97
- handleDragOver,
98
- handleDragLeave,
99
- clearInput,
100
- };
101
- }
102
-
103
- export default useChatInput;
@@ -1,36 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
-
4
- import { buildTimelineItems } from './buildTimelineItems.js';
5
-
6
- test('buildTimelineItems attaches analysis route metadata on sourcing_candidates artifact', () => {
7
- const timeline = buildTimelineItems({
8
- messages: [
9
- {
10
- message_id: 'm1',
11
- role: 'user',
12
- content: '请分析这些供应商',
13
- created_at: '2026-04-21T00:00:00Z',
14
- },
15
- ],
16
- streaming: {
17
- content: '',
18
- sourcingCandidates: {
19
- run_id: 'sourcing-run-1',
20
- candidates: [{ supplier_id: 'sup-1' }],
21
- analysis_route: {
22
- analysis_type: 'sourcing',
23
- template_id: 'analysis_sourcing_default',
24
- reason_codes: ['ROUTE_SIGNAL_SOURCING_INTENT'],
25
- confidence: 0.82,
26
- },
27
- },
28
- roundKey: 'rk-1',
29
- },
30
- });
31
-
32
- const sourcingItem = timeline.items.find((item) => item.type === 'sourcing_candidates');
33
- assert.ok(sourcingItem);
34
- assert.equal(sourcingItem.analysisRoute.analysis_type, 'sourcing');
35
- assert.equal(sourcingItem.analysisRoute.template_id, 'analysis_sourcing_default');
36
- });