flare-chat-core 0.2.3 → 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 -190
  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 -156
  27. package/src/app/components/WorkspaceMainPane.jsx +0 -121
  28. package/src/app/components/WorkspaceSessionPane.jsx +0 -70
  29. package/src/app/components/WorkspaceTopBarSection.jsx +0 -71
  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 -201
  72. package/src/chat-core/messages/buildTimelineItems.knowledge-citation.test.mjs +0 -182
  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,226 +0,0 @@
1
- import React, { useMemo, useState } from 'react';
2
- import {
3
- CHAT_UI_THEME_TOKENS,
4
- DEFAULT_CHAT_UI_THEME,
5
- KnowledgeHubDrawerContent,
6
- } from 'flare-chat-ui';
7
- import { useChatCorePipelines } from './useChatCorePipelines.js';
8
- import {
9
- RESOLVED_UI_LABELS,
10
- STARTER_SCENARIOS,
11
- } from './constants.js';
12
- import {
13
- selectActiveSession,
14
- selectProjectItems,
15
- } from './selectors.js';
16
- import { useProjectSourceUpload } from './useProjectSourceUpload.js';
17
- import { useProjectNameEditing } from './useProjectNameEditing.js';
18
- import { useWorkspacePanels } from './useWorkspacePanels.js';
19
- import { useWorkspaceComposerController } from './useWorkspaceComposerController.jsx';
20
- import { useWorkspaceCanvasController } from './useWorkspaceCanvasController.jsx';
21
-
22
- export function useWorkspaceController({
23
- functionType = 'chat_component_debug',
24
- defaultSessionTitle = '调试会话',
25
- projectId = 'project_demo_001',
26
- userId = 'user_demo_001',
27
- backendMode = 'real',
28
- kernelBaseUrl = 'http://127.0.0.1:18002',
29
- apiBaseUrl = '',
30
- apiToken = '',
31
- defaultProjectName = '本地演示项目',
32
- sessionAPI,
33
- messageAPI,
34
- } = {}) {
35
- const {
36
- apiReadinessGate,
37
- blockedApiError,
38
- realApi,
39
- viewModel,
40
- } = useChatCorePipelines({
41
- functionType,
42
- defaultSessionTitle,
43
- projectId,
44
- userId,
45
- backendMode,
46
- kernelBaseUrl,
47
- apiBaseUrl,
48
- apiToken,
49
- sessionAPI,
50
- messageAPI,
51
- });
52
-
53
- const themeTokens = CHAT_UI_THEME_TOKENS[DEFAULT_CHAT_UI_THEME];
54
- const [activeWorkspaceTab, setActiveWorkspaceTab] = useState('chats');
55
- const [showAllScenarios, setShowAllScenarios] = useState(false);
56
- const {
57
- knowledgeHubPopoverOpen,
58
- setKnowledgeHubPopoverOpen,
59
- showCanvasPanel,
60
- setShowCanvasPanel,
61
- canvasActiveTabKey,
62
- setCanvasActiveTabKey,
63
- handleOpenKnowledgeHub,
64
- handleToggleWorkspacePanel,
65
- } = useWorkspacePanels();
66
- const [canvasFullscreenOpen, setCanvasFullscreenOpen] = useState(false);
67
- const {
68
- projectNameEditing,
69
- projectNameDraft,
70
- setProjectNameDraft,
71
- projectDisplayName,
72
- handleProjectNameStartEdit,
73
- handleProjectNameCancel,
74
- handleProjectNameSave,
75
- } = useProjectNameEditing({ defaultProjectName });
76
-
77
- const {
78
- fileInputRef: sourceFileInputRef,
79
- sourceActionError,
80
- sourceItems,
81
- sourceRemovingId,
82
- sourceSyncLoading,
83
- sourceUploadLoading,
84
- sourceUploadProgressItems,
85
- handleOpenSourcePicker,
86
- handleSourceFileChange,
87
- handleRemoveSource,
88
- handleRetrySource,
89
- handleViewSourceDetail,
90
- } = useProjectSourceUpload({
91
- projectId,
92
- userId,
93
- sourceAPI: realApi?.sourceAPI || null,
94
- shouldLoadSources: activeWorkspaceTab === 'sources' || knowledgeHubPopoverOpen,
95
- });
96
-
97
- const sessions = Array.isArray(viewModel.sessionList.sessions) ? viewModel.sessionList.sessions : [];
98
- const activeSessionId = viewModel.sessionList.activeSessionId;
99
- const activeSession = selectActiveSession(sessions, activeSessionId);
100
- const projectItems = selectProjectItems({
101
- projectId,
102
- projectDisplayName,
103
- sessions,
104
- });
105
- const projectSlot = projectItems[0] || null;
106
- const hasProject = Boolean(projectSlot?.name && String(projectSlot.name).trim());
107
-
108
- const visibleScenarios = useMemo(() => (
109
- showAllScenarios ? STARTER_SCENARIOS : STARTER_SCENARIOS.slice(0, 2)
110
- ), [showAllScenarios]);
111
-
112
- const handleProjectNameStartEditForSlot = () => {
113
- handleProjectNameStartEdit(projectSlot);
114
- };
115
-
116
- const {
117
- actionGuards,
118
- inputState,
119
- composerNode,
120
- composerModeKey,
121
- languageGuardEnabled,
122
- friendlyToneEnabled,
123
- evidenceStrictMode,
124
- evidenceHitScoreThreshold,
125
- } = useWorkspaceComposerController({
126
- userId,
127
- projectId,
128
- activeSessionId,
129
- realApi,
130
- viewModel,
131
- apiReadinessGate,
132
- blockedApiError,
133
- sourceItems,
134
- activeWorkspaceTab,
135
- setActiveWorkspaceTab,
136
- setShowCanvasPanel,
137
- setCanvasActiveTabKey,
138
- themeTokens,
139
- });
140
-
141
- const {
142
- canvasPanelNode,
143
- renderedTimelineItems,
144
- handleUICardAction,
145
- generativeRegistry,
146
- } = useWorkspaceCanvasController({
147
- apiBaseUrl,
148
- projectId,
149
- userId,
150
- activeSessionId,
151
- timeline: viewModel.timeline,
152
- composerModeKey,
153
- evidenceStrictMode,
154
- evidenceHitScoreThreshold,
155
- showCanvasPanel,
156
- setShowCanvasPanel,
157
- canvasActiveTabKey,
158
- setCanvasActiveTabKey,
159
- canvasFullscreenOpen,
160
- setCanvasFullscreenOpen,
161
- inputState,
162
- themeTokens,
163
- languageGuardEnabled,
164
- friendlyToneEnabled,
165
- });
166
-
167
- const knowledgeHubPopoverContent = (
168
- <KnowledgeHubDrawerContent
169
- resolvedUILabels={RESOLVED_UI_LABELS}
170
- sourceUploadLoading={sourceUploadLoading}
171
- sourceSyncLoading={sourceSyncLoading}
172
- sourceActionError={sourceActionError}
173
- sourceItems={sourceItems}
174
- sourceUploadProgressItems={sourceUploadProgressItems}
175
- handleOpenSourcePicker={handleOpenSourcePicker}
176
- handleRemoveSource={handleRemoveSource}
177
- handleViewSourceDetail={handleViewSourceDetail}
178
- />
179
- );
180
-
181
- return {
182
- themeTokens,
183
- viewModel,
184
- actionGuards,
185
- apiReadinessGate,
186
- hasProject,
187
- projectItems,
188
- projectSlot,
189
- activeSession,
190
- sessions,
191
- projectNameEditing,
192
- projectNameDraft,
193
- setProjectNameDraft,
194
- handleProjectNameSave,
195
- handleProjectNameCancel,
196
- projectDisplayName,
197
- handleProjectNameStartEdit: handleProjectNameStartEditForSlot,
198
- activeWorkspaceTab,
199
- setActiveWorkspaceTab,
200
- knowledgeHubPopoverContent,
201
- knowledgeHubPopoverOpen,
202
- setKnowledgeHubPopoverOpen,
203
- handleOpenKnowledgeHub,
204
- showCanvasPanel,
205
- handleToggleWorkspacePanel,
206
- composerNode,
207
- handleOpenSourcePicker,
208
- handleRemoveSource,
209
- handleRetrySource,
210
- handleViewSourceDetail,
211
- sourceActionError,
212
- sourceItems,
213
- sourceRemovingId,
214
- sourceSyncLoading,
215
- sourceUploadLoading,
216
- canvasPanelNode,
217
- renderedTimelineItems,
218
- handleUICardAction,
219
- generativeRegistry,
220
- showAllScenarios,
221
- setShowAllScenarios,
222
- visibleScenarios,
223
- sourceFileInputRef,
224
- handleSourceFileChange,
225
- };
226
- }
@@ -1,55 +0,0 @@
1
- import { useCallback, useEffect, useState } from 'react';
2
-
3
- export function useWorkspacePanels() {
4
- const [knowledgeHubPopoverOpen, setKnowledgeHubPopoverOpen] = useState(false);
5
- const [showCanvasPanel, setShowCanvasPanel] = useState(false);
6
- const [canvasActiveTabKey, setCanvasActiveTabKey] = useState('requirement');
7
- const [canvasFocusEvidenceAnchorId, setCanvasFocusEvidenceAnchorId] = useState('');
8
-
9
- const handleOpenKnowledgeHub = useCallback(() => {
10
- setKnowledgeHubPopoverOpen(true);
11
- }, []);
12
-
13
- const handleToggleWorkspacePanel = useCallback(() => {
14
- setShowCanvasPanel((prev) => !prev);
15
- }, []);
16
-
17
- const handleUICardAction = useCallback((action) => {
18
- const actionType = String(action?.type || action?.action_key || '').trim();
19
- if (actionType === 'open_evidence_citation') {
20
- const citationId = String(action?.citation_id || '').trim();
21
- setShowCanvasPanel(true);
22
- setCanvasActiveTabKey('evidence');
23
- setCanvasFocusEvidenceAnchorId(citationId ? `retrieval-evidence-${citationId}` : '');
24
- return;
25
- }
26
- if (actionType.includes('canvas') || actionType.includes('evidence') || actionType.includes('sourcing')) {
27
- setShowCanvasPanel(true);
28
- setCanvasActiveTabKey(actionType.includes('sourcing') ? 'sourcing' : 'evidence');
29
- }
30
- }, []);
31
-
32
- useEffect(() => {
33
- if (!canvasFocusEvidenceAnchorId) {
34
- return;
35
- }
36
- const timer = setTimeout(() => setCanvasFocusEvidenceAnchorId(''), 1600);
37
- return () => clearTimeout(timer);
38
- }, [canvasFocusEvidenceAnchorId]);
39
-
40
- return {
41
- knowledgeHubPopoverOpen,
42
- setKnowledgeHubPopoverOpen,
43
- showCanvasPanel,
44
- setShowCanvasPanel,
45
- canvasActiveTabKey,
46
- setCanvasActiveTabKey,
47
- canvasFocusEvidenceAnchorId,
48
- setCanvasFocusEvidenceAnchorId,
49
- handleOpenKnowledgeHub,
50
- handleToggleWorkspacePanel,
51
- handleUICardAction,
52
- };
53
- }
54
-
55
- export default useWorkspacePanels;
@@ -1,223 +0,0 @@
1
- import { useCallback, useEffect, useMemo, useRef } from 'react';
2
- import {
3
- collectReadyAttachmentRefs,
4
- hasNonReadyComposerAttachments,
5
- } from '../core-chat-entry/attachmentSendRefs.js';
6
-
7
- export function useComposerAttachmentSync({
8
- composerFileList,
9
- onSyncFileList,
10
- realSourceApi,
11
- projectId,
12
- userId,
13
- activeSessionId,
14
- } = {}) {
15
- const composerFileListRef = useRef(Array.isArray(composerFileList) ? composerFileList : []);
16
- const attachmentUploadTaskRef = useRef(new Set());
17
- const attachmentPollingTaskRef = useRef(new Set());
18
-
19
- useEffect(() => {
20
- composerFileListRef.current = Array.isArray(composerFileList) ? composerFileList : [];
21
- }, [composerFileList]);
22
-
23
- const syncComposerFilePatch = useCallback((uid, patch = {}) => {
24
- const normalizedUid = String(uid || '').trim();
25
- if (!normalizedUid || typeof onSyncFileList !== 'function') {
26
- return;
27
- }
28
- const currentList = Array.isArray(composerFileListRef.current)
29
- ? composerFileListRef.current
30
- : [];
31
- let changed = false;
32
- const nextList = currentList.map((item) => {
33
- if (String(item?.uid || '').trim() !== normalizedUid) {
34
- return item;
35
- }
36
- changed = true;
37
- return {
38
- ...item,
39
- ...patch,
40
- };
41
- });
42
- if (!changed) {
43
- return;
44
- }
45
- composerFileListRef.current = nextList;
46
- onSyncFileList(nextList);
47
- }, [onSyncFileList]);
48
-
49
- const pollComposerAttachmentStatus = useCallback((sourceId, fileUid) => {
50
- const normalizedSourceId = String(sourceId || '').trim();
51
- const normalizedFileUid = String(fileUid || '').trim();
52
- if (
53
- !normalizedSourceId
54
- || !normalizedFileUid
55
- || attachmentPollingTaskRef.current.has(normalizedSourceId)
56
- || typeof realSourceApi?.listProjectSources !== 'function'
57
- ) {
58
- return;
59
- }
60
-
61
- attachmentPollingTaskRef.current.add(normalizedSourceId);
62
- const wait = (ms) => new Promise((resolve) => {
63
- setTimeout(resolve, ms);
64
- });
65
- const maxPollCount = 80;
66
- const pollIntervalMs = 1500;
67
-
68
- void (async () => {
69
- let pollCount = 0;
70
- while (pollCount < maxPollCount) {
71
- pollCount += 1;
72
- const fileExists = composerFileListRef.current.some((item) => String(item?.uid || '').trim() === normalizedFileUid);
73
- if (!fileExists) {
74
- return;
75
- }
76
- try {
77
- const response = await realSourceApi.listProjectSources(projectId, activeSessionId || undefined);
78
- const sources = Array.isArray(response?.sources) ? response.sources : [];
79
- const matchedSource = sources.find((item) => {
80
- const currentSourceId = String(item?.source_id || item?.id || '').trim();
81
- return currentSourceId === normalizedSourceId;
82
- });
83
- if (matchedSource) {
84
- const nextStatus = String(matchedSource.status || '').trim().toLowerCase() || 'uploaded';
85
- syncComposerFilePatch(normalizedFileUid, {
86
- source_id: normalizedSourceId,
87
- status: nextStatus,
88
- error_message: String(matchedSource?.error_message || ''),
89
- });
90
- if (nextStatus === 'ready' || nextStatus.startsWith('failed') || nextStatus === 'error') {
91
- return;
92
- }
93
- }
94
- } catch (error) {
95
- syncComposerFilePatch(normalizedFileUid, {
96
- error_message: String(error?.message || ''),
97
- });
98
- }
99
- await wait(pollIntervalMs);
100
- }
101
- syncComposerFilePatch(normalizedFileUid, {
102
- status: 'failed',
103
- error_message: '文件处理超时,请重试',
104
- });
105
- })().finally(() => {
106
- attachmentPollingTaskRef.current.delete(normalizedSourceId);
107
- });
108
- }, [activeSessionId, projectId, realSourceApi, syncComposerFilePatch]);
109
-
110
- useEffect(() => {
111
- if (typeof realSourceApi?.uploadSourceFile !== 'function') {
112
- return;
113
- }
114
-
115
- const currentFileList = Array.isArray(composerFileList) ? composerFileList : [];
116
- currentFileList.forEach((item) => {
117
- const fileUid = String(item?.uid || '').trim();
118
- if (!fileUid || attachmentUploadTaskRef.current.has(fileUid)) {
119
- return;
120
- }
121
- const sourceId = String(item?.source_id || '').trim();
122
- if (sourceId) {
123
- const status = String(item?.status || '').trim().toLowerCase();
124
- if (status && status !== 'ready') {
125
- pollComposerAttachmentStatus(sourceId, fileUid);
126
- }
127
- return;
128
- }
129
- const status = String(item?.status || '').trim().toLowerCase();
130
- if (status.startsWith('failed')) {
131
- return;
132
- }
133
- const uploadableFile = item?.originFileObj || null;
134
- if (!(uploadableFile instanceof File)) {
135
- syncComposerFilePatch(fileUid, {
136
- status: 'failed',
137
- error_message: '缺少可上传文件',
138
- });
139
- return;
140
- }
141
-
142
- attachmentUploadTaskRef.current.add(fileUid);
143
- syncComposerFilePatch(fileUid, {
144
- status: 'uploading',
145
- error_message: '',
146
- });
147
-
148
- void (async () => {
149
- try {
150
- const uploaded = await realSourceApi.uploadSourceFile({
151
- project_id: projectId,
152
- user_id: userId,
153
- session_id: activeSessionId || null,
154
- file: uploadableFile,
155
- filename: item?.name || uploadableFile?.name || 'upload.bin',
156
- });
157
- const uploadedSourceId = String(uploaded?.source_id || uploaded?.id || '').trim();
158
- if (!uploadedSourceId) {
159
- syncComposerFilePatch(fileUid, {
160
- status: 'failed',
161
- error_message: '上传成功但未返回 source_id',
162
- });
163
- return;
164
- }
165
- const uploadedStatus = String(uploaded?.status || 'uploaded').trim().toLowerCase() || 'uploaded';
166
- syncComposerFilePatch(fileUid, {
167
- ...uploaded,
168
- source_id: uploadedSourceId,
169
- status: uploadedStatus,
170
- error_message: String(uploaded?.error_message || ''),
171
- });
172
- if (uploadedStatus === 'ready' || uploadedStatus.startsWith('failed') || uploadedStatus === 'error') {
173
- return;
174
- }
175
- pollComposerAttachmentStatus(uploadedSourceId, fileUid);
176
- } catch (error) {
177
- syncComposerFilePatch(fileUid, {
178
- status: 'failed',
179
- error_message: String(error?.message || '上传失败'),
180
- });
181
- } finally {
182
- attachmentUploadTaskRef.current.delete(fileUid);
183
- }
184
- })();
185
- });
186
- }, [
187
- activeSessionId,
188
- composerFileList,
189
- pollComposerAttachmentStatus,
190
- projectId,
191
- userId,
192
- realSourceApi,
193
- syncComposerFilePatch,
194
- ]);
195
-
196
- const readyAttachmentRefs = useMemo(
197
- () => collectReadyAttachmentRefs(composerFileList),
198
- [composerFileList],
199
- );
200
- const hasNonReadyAttachments = useMemo(
201
- () => hasNonReadyComposerAttachments(composerFileList),
202
- [composerFileList],
203
- );
204
- const syncErrors = useMemo(() => {
205
- const list = Array.isArray(composerFileList) ? composerFileList : [];
206
- return list
207
- .map((item) => ({
208
- uid: String(item?.uid || '').trim(),
209
- source_id: String(item?.source_id || '').trim(),
210
- error_message: String(item?.error_message || '').trim(),
211
- }))
212
- .filter((item) => item.error_message);
213
- }, [composerFileList]);
214
-
215
- return {
216
- composerFileListRef,
217
- hasNonReadyAttachments,
218
- readyAttachmentRefs,
219
- syncErrors,
220
- };
221
- }
222
-
223
- export default useComposerAttachmentSync;
@@ -1,52 +0,0 @@
1
- import { useCallback } from 'react';
2
-
3
- function buildDraftRoundKey({ activeSessionId, composerValue }) {
4
- return `${String(activeSessionId || 'draft')}:${String(composerValue || '').trim()}`;
5
- }
6
-
7
- export function useComposerChooserHandlers({
8
- setComposerModeKey,
9
- setSourcingChooser,
10
- setShowCanvasPanel,
11
- setCanvasActiveTabKey,
12
- setSourcingChooserDismissDraftKey,
13
- activeSessionId,
14
- composerValue,
15
- } = {}) {
16
- const onComposerActionSelect = useCallback((choice) => {
17
- const actionKey = String(choice?.action?.action_key || '').trim();
18
- const draftRoundKey = buildDraftRoundKey({ activeSessionId, composerValue });
19
- if (actionKey === 'open_sourcing') {
20
- setComposerModeKey('intelligent_sourcing');
21
- setSourcingChooser(null);
22
- setShowCanvasPanel(true);
23
- setCanvasActiveTabKey('sourcing');
24
- return;
25
- }
26
- if (actionKey === 'dismiss_sourcing_once') {
27
- setSourcingChooser(null);
28
- setSourcingChooserDismissDraftKey(draftRoundKey);
29
- }
30
- }, [
31
- activeSessionId,
32
- composerValue,
33
- setCanvasActiveTabKey,
34
- setComposerModeKey,
35
- setShowCanvasPanel,
36
- setSourcingChooser,
37
- setSourcingChooserDismissDraftKey,
38
- ]);
39
-
40
- const onComposerChooserDismiss = useCallback(() => {
41
- const draftRoundKey = buildDraftRoundKey({ activeSessionId, composerValue });
42
- setSourcingChooser(null);
43
- setSourcingChooserDismissDraftKey(draftRoundKey);
44
- }, [activeSessionId, composerValue, setSourcingChooser, setSourcingChooserDismissDraftKey]);
45
-
46
- return {
47
- onComposerActionSelect,
48
- onComposerChooserDismiss,
49
- };
50
- }
51
-
52
- export default useComposerChooserHandlers;
@@ -1,140 +0,0 @@
1
- import { useCallback } from 'react';
2
- import { detectSourcingIntent } from '../../chat-core/pipelines/preprocess-message.js';
3
- import {
4
- collectSourceIdStrings,
5
- mergeSourceReferenceLists,
6
- } from '../core-chat-entry/attachmentSendRefs.js';
7
-
8
- function normalizeText(value) {
9
- return String(value || '').trim();
10
- }
11
-
12
- export function isSourcingRefreshCommand(value) {
13
- const normalized = normalizeText(value);
14
- if (!normalized) {
15
- return false;
16
- }
17
- if (normalized === '继续' || normalized === '继续寻源') {
18
- return true;
19
- }
20
- return normalized.includes('换一批') || normalized.includes('再来一批');
21
- }
22
-
23
- export function resolveSourcingPayloadExtra({
24
- streamOptions,
25
- isSourcingMode,
26
- currentInput,
27
- latestSourcingQuery,
28
- latestSourcingResultIds,
29
- }) {
30
- const payloadExtra = (
31
- streamOptions?.payloadExtra
32
- && typeof streamOptions.payloadExtra === 'object'
33
- && !Array.isArray(streamOptions.payloadExtra)
34
- ) ? { ...streamOptions.payloadExtra } : {};
35
- if (!isSourcingMode) {
36
- return payloadExtra;
37
- }
38
-
39
- const normalizedInput = normalizeText(currentInput);
40
- const normalizedLatestQuery = normalizeText(latestSourcingQuery);
41
- if (normalizedLatestQuery) {
42
- const hasSourcingIntentInInput = detectSourcingIntent(normalizedInput);
43
- if (!hasSourcingIntentInInput || isSourcingRefreshCommand(normalizedInput)) {
44
- payloadExtra.sourcing_query = normalizedLatestQuery;
45
- }
46
- }
47
- if (isSourcingRefreshCommand(normalizedInput)) {
48
- payloadExtra.append_mode = true;
49
- payloadExtra.lock_existing_order = true;
50
- payloadExtra.base_result_ids = Array.isArray(latestSourcingResultIds)
51
- ? latestSourcingResultIds.map((item) => normalizeText(item)).filter(Boolean)
52
- : [];
53
- }
54
- return payloadExtra;
55
- }
56
-
57
- export function useSendWithContextRefs({
58
- composerModeKey,
59
- setSourcingChooser,
60
- sourceItems,
61
- composer,
62
- activeWorkspaceTab,
63
- setActiveWorkspaceTab,
64
- composerFileListRef,
65
- hasNonReadyAttachments,
66
- readyAttachmentRefs,
67
- latestSourcingQuery = '',
68
- latestSourcingResultIds = [],
69
- } = {}) {
70
- return useCallback(async (options = {}) => {
71
- const streamOptions = (
72
- options?.streamOptions
73
- && typeof options.streamOptions === 'object'
74
- && !Array.isArray(options.streamOptions)
75
- ) ? { ...options.streamOptions } : {};
76
- const normalizedModeKey = String(streamOptions.modeKey || composerModeKey || '').trim();
77
- const isSourcingMode = normalizedModeKey === 'intelligent_sourcing';
78
- const currentInput = String(composer?.value || '').trim();
79
-
80
- if (hasNonReadyAttachments) {
81
- return false;
82
- }
83
-
84
- setSourcingChooser(null);
85
- const currentComposerFileList = Array.isArray(composerFileListRef?.current)
86
- ? composerFileListRef.current
87
- : [];
88
- const projectReadyRefs = Array.isArray(sourceItems)
89
- ? sourceItems.filter((item) => String(item?.status || '').trim().toLowerCase() === 'ready')
90
- : [];
91
- const sourcingDefaultRefs = isSourcingMode ? mergeSourceReferenceLists(projectReadyRefs) : [];
92
- const mergedRefs = mergeSourceReferenceLists(
93
- options?.context_refs,
94
- options?.knowledge_refs,
95
- readyAttachmentRefs,
96
- sourcingDefaultRefs,
97
- );
98
- const mergedKnowledgeRefIds = collectSourceIdStrings(
99
- options?.context_refs,
100
- options?.knowledge_refs,
101
- readyAttachmentRefs,
102
- sourcingDefaultRefs,
103
- );
104
- streamOptions.payloadExtra = resolveSourcingPayloadExtra({
105
- streamOptions,
106
- isSourcingMode,
107
- currentInput,
108
- latestSourcingQuery,
109
- latestSourcingResultIds,
110
- });
111
-
112
- const sendResult = await composer.onSend({
113
- ...options,
114
- ...(currentInput ? { content: currentInput } : {}),
115
- streamOptions,
116
- context_refs: mergedRefs,
117
- knowledge_refs: mergedKnowledgeRefIds,
118
- attachedSessionFilesSnapshot: currentComposerFileList,
119
- });
120
-
121
- if (sendResult === true && activeWorkspaceTab === 'sources') {
122
- setActiveWorkspaceTab('chats');
123
- }
124
- return sendResult;
125
- }, [
126
- activeWorkspaceTab,
127
- composer,
128
- composerFileListRef,
129
- composerModeKey,
130
- hasNonReadyAttachments,
131
- readyAttachmentRefs,
132
- setActiveWorkspaceTab,
133
- setSourcingChooser,
134
- sourceItems,
135
- latestSourcingQuery,
136
- latestSourcingResultIds,
137
- ]);
138
- }
139
-
140
- export default useSendWithContextRefs;