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,22 +0,0 @@
1
- import { useMemo } from 'react';
2
-
3
- export function useDevCapabilityStatusNote({
4
- isDev = false,
5
- mode = '',
6
- labels = {},
7
- } = {}) {
8
- return useMemo(() => {
9
- if (!isDev) {
10
- return '';
11
- }
12
- const normalizedMode = String(mode || '').trim().toLowerCase();
13
- if (normalizedMode === 'persistent') {
14
- return String(labels.persistent || '开发态:资料能力已接入持久化链路');
15
- }
16
- if (normalizedMode === 'local_fallback') {
17
- return String(labels.localFallback || '开发态:资料能力当前为本地回退模式');
18
- }
19
- return String(labels.unknown || '开发态:资料能力状态未知');
20
- }, [isDev, labels.localFallback, labels.persistent, labels.unknown, mode]);
21
- }
22
-
@@ -1,41 +0,0 @@
1
- import { useState } from 'react';
2
-
3
- export function useProjectNameEditing({ defaultProjectName } = {}) {
4
- const initialName = String(defaultProjectName || '').trim() || '本地演示项目';
5
- const [projectNameEditing, setProjectNameEditing] = useState(false);
6
- const [projectNameDraft, setProjectNameDraft] = useState(initialName);
7
- const [projectDisplayName, setProjectDisplayName] = useState(initialName);
8
-
9
- const handleProjectNameStartEdit = (projectSlot) => {
10
- if (!projectSlot?.key) {
11
- return;
12
- }
13
- setProjectNameDraft(String(projectDisplayName || '').trim());
14
- setProjectNameEditing(true);
15
- };
16
-
17
- const handleProjectNameCancel = () => {
18
- setProjectNameEditing(false);
19
- setProjectNameDraft('');
20
- };
21
-
22
- const handleProjectNameSave = () => {
23
- const nextName = String(projectNameDraft || '').trim();
24
- if (nextName) {
25
- setProjectDisplayName(nextName);
26
- }
27
- handleProjectNameCancel();
28
- };
29
-
30
- return {
31
- projectNameEditing,
32
- projectNameDraft,
33
- setProjectNameDraft,
34
- projectDisplayName,
35
- handleProjectNameStartEdit,
36
- handleProjectNameCancel,
37
- handleProjectNameSave,
38
- };
39
- }
40
-
41
- export default useProjectNameEditing;
@@ -1,341 +0,0 @@
1
- import { useCallback, useEffect, useRef, useState } from 'react';
2
-
3
- const projectSourceListSingleflight = new Map();
4
- const SOURCE_STATUS_TERMINAL = new Set(['ready', 'failed', 'error']);
5
-
6
- function normalizeError(error, fallbackMessage) {
7
- if (error instanceof Error) {
8
- return error;
9
- }
10
- return new Error(String(error || fallbackMessage));
11
- }
12
-
13
- function buildSourceItem(file, index, scopedProjectId) {
14
- return {
15
- id: `project-source-${Date.now()}-${index}`,
16
- project_id: scopedProjectId,
17
- message_id: null,
18
- scope: 'project',
19
- source: 'local_upload',
20
- filename: String(file?.name || '').trim() || `资料 ${index + 1}`,
21
- mime_type: String(file?.type || '').trim(),
22
- size: Number.isFinite(file?.size) ? file.size : null,
23
- created_at: new Date().toISOString(),
24
- status: 'ready',
25
- error_message: '',
26
- persisted_to_project: false,
27
- };
28
- }
29
-
30
- function buildSourceFileSignature(item = {}) {
31
- return `${item.filename || ''}::${item.size || ''}::${item.mime_type || ''}`;
32
- }
33
-
34
- function parseSourceList(response) {
35
- if (Array.isArray(response?.sources)) {
36
- return response.sources;
37
- }
38
- if (Array.isArray(response?.data?.sources)) {
39
- return response.data.sources;
40
- }
41
- return [];
42
- }
43
-
44
- function normalizeSourceStatus(status) {
45
- return String(status || '').trim().toLowerCase();
46
- }
47
-
48
- function hasPendingSourceProcessing(items = []) {
49
- if (!Array.isArray(items) || items.length === 0) {
50
- return false;
51
- }
52
- return items.some((item) => !SOURCE_STATUS_TERMINAL.has(normalizeSourceStatus(item?.status)));
53
- }
54
-
55
- function createUploadProgressItem(file, index) {
56
- return {
57
- id: `upload-progress-${Date.now()}-${index}`,
58
- filename: String(file?.name || '').trim() || `资料 ${index + 1}`,
59
- progress: 0,
60
- stage: 'reading',
61
- error_message: '',
62
- };
63
- }
64
-
65
- function readFileWithProgress(file, onProgress) {
66
- return new Promise((resolve, reject) => {
67
- const reader = new FileReader();
68
- reader.onprogress = (event) => {
69
- if (!event.lengthComputable) {
70
- return;
71
- }
72
- const percent = (event.loaded / event.total) * 80;
73
- onProgress(percent);
74
- };
75
- reader.onload = () => {
76
- onProgress(80);
77
- resolve();
78
- };
79
- reader.onerror = () => {
80
- reject(reader.error || new Error('读取文件失败'));
81
- };
82
- reader.readAsArrayBuffer(file);
83
- });
84
- }
85
-
86
- export function useProjectSourceUpload({
87
- projectId = '',
88
- userId = '',
89
- sourceAPI = null,
90
- shouldLoadSources = true,
91
- } = {}) {
92
- const fileInputRef = useRef(null);
93
- const [sourceItems, setSourceItems] = useState([]);
94
- const [sourceActionError, setSourceActionError] = useState(null);
95
- const [sourceUploadLoading, setSourceUploadLoading] = useState(false);
96
- const [sourceUploadProgressItems, setSourceUploadProgressItems] = useState([]);
97
- const [sourceSyncLoading, setSourceSyncLoading] = useState(false);
98
- const [sourceRemovingId, setSourceRemovingId] = useState('');
99
- const [sourceEndpointUnavailable, setSourceEndpointUnavailable] = useState(false);
100
-
101
- const canUsePersistentSourceAPI = (
102
- typeof sourceAPI?.listProjectSources === 'function'
103
- && typeof sourceAPI?.uploadSourceFile === 'function'
104
- && typeof sourceAPI?.deleteProjectSource === 'function'
105
- && !sourceEndpointUnavailable
106
- );
107
-
108
- const loadProjectSources = useCallback(async ({ silent = false } = {}) => {
109
- const scopedProjectId = String(projectId || '').trim();
110
- if (!canUsePersistentSourceAPI || !scopedProjectId) {
111
- return;
112
- }
113
- if (!silent) {
114
- setSourceSyncLoading(true);
115
- setSourceActionError(null);
116
- }
117
- try {
118
- const requestKey = scopedProjectId;
119
- let pendingRequest = projectSourceListSingleflight.get(requestKey);
120
- if (!pendingRequest) {
121
- pendingRequest = sourceAPI.listProjectSources(scopedProjectId)
122
- .finally(() => {
123
- projectSourceListSingleflight.delete(requestKey);
124
- });
125
- projectSourceListSingleflight.set(requestKey, pendingRequest);
126
- }
127
- const response = await pendingRequest;
128
- setSourceItems(parseSourceList(response));
129
- setSourceEndpointUnavailable(false);
130
- } catch (error) {
131
- if (Number(error?.status) === 404) {
132
- setSourceEndpointUnavailable(true);
133
- if (!silent) {
134
- setSourceActionError(null);
135
- }
136
- return;
137
- }
138
- if (!silent) {
139
- setSourceActionError(normalizeError(error, '加载资料失败'));
140
- }
141
- } finally {
142
- if (!silent) {
143
- setSourceSyncLoading(false);
144
- }
145
- }
146
- }, [canUsePersistentSourceAPI, projectId, sourceAPI]);
147
-
148
- useEffect(() => {
149
- if (!shouldLoadSources) {
150
- return;
151
- }
152
- loadProjectSources();
153
- }, [loadProjectSources, shouldLoadSources]);
154
-
155
- useEffect(() => {
156
- if (!shouldLoadSources) {
157
- return undefined;
158
- }
159
- if (!canUsePersistentSourceAPI) {
160
- return undefined;
161
- }
162
- if (!hasPendingSourceProcessing(sourceItems)) {
163
- return undefined;
164
- }
165
- const timer = setInterval(() => {
166
- void loadProjectSources({ silent: true });
167
- }, 1500);
168
- return () => {
169
- clearInterval(timer);
170
- };
171
- }, [canUsePersistentSourceAPI, loadProjectSources, shouldLoadSources, sourceItems]);
172
-
173
- const appendLocalSourceFiles = useCallback((nativeFiles = []) => {
174
- setSourceItems((prev) => {
175
- const existingSignatures = new Set(prev.map((item) => buildSourceFileSignature(item)));
176
- const nextItems = nativeFiles
177
- .map((file, index) => buildSourceItem(file, index, projectId))
178
- .filter((item) => {
179
- const signature = buildSourceFileSignature(item);
180
- if (existingSignatures.has(signature)) {
181
- return false;
182
- }
183
- existingSignatures.add(signature);
184
- return true;
185
- });
186
- return [...nextItems, ...prev];
187
- });
188
- }, [projectId]);
189
-
190
- const uploadSourceFiles = useCallback(async (nativeFiles = []) => {
191
- const scopedProjectId = String(projectId || '').trim();
192
- if (nativeFiles.length === 0) {
193
- return;
194
- }
195
- setSourceUploadLoading(true);
196
- setSourceActionError(null);
197
- const progressItems = nativeFiles.map((file, index) => createUploadProgressItem(file, index));
198
- setSourceUploadProgressItems((prev) => [...progressItems, ...prev]);
199
- const updateProgressItem = (itemId, patch = {}) => {
200
- setSourceUploadProgressItems((prev) => prev.map((item) => (item.id === itemId ? { ...item, ...patch } : item)));
201
- };
202
- try {
203
- if (canUsePersistentSourceAPI && scopedProjectId) {
204
- for (let index = 0; index < nativeFiles.length; index += 1) {
205
- const file = nativeFiles[index];
206
- const progressItem = progressItems[index];
207
- await readFileWithProgress(file, (percent) => {
208
- updateProgressItem(progressItem.id, {
209
- stage: 'reading',
210
- progress: percent,
211
- });
212
- });
213
- updateProgressItem(progressItem.id, {
214
- stage: 'uploading',
215
- progress: 80,
216
- });
217
- await sourceAPI.uploadSourceFile({
218
- project_id: scopedProjectId,
219
- user_id: String(userId || '').trim() || null,
220
- file,
221
- filename: file.name,
222
- onProgress: (percent) => {
223
- const safePercent = Number.isFinite(percent) ? percent : 0;
224
- const mappedPercent = 80 + (Math.max(0, Math.min(100, safePercent)) * 0.2);
225
- updateProgressItem(progressItem.id, {
226
- stage: 'uploading',
227
- progress: mappedPercent,
228
- });
229
- },
230
- });
231
- updateProgressItem(progressItem.id, {
232
- stage: 'done',
233
- progress: 100,
234
- });
235
- }
236
- await loadProjectSources();
237
- return;
238
- }
239
- for (let index = 0; index < nativeFiles.length; index += 1) {
240
- const file = nativeFiles[index];
241
- const progressItem = progressItems[index];
242
- await readFileWithProgress(file, (percent) => {
243
- updateProgressItem(progressItem.id, {
244
- stage: 'reading',
245
- progress: percent,
246
- });
247
- });
248
- updateProgressItem(progressItem.id, {
249
- stage: 'uploading',
250
- progress: 95,
251
- });
252
- appendLocalSourceFiles([file]);
253
- updateProgressItem(progressItem.id, {
254
- stage: 'done',
255
- progress: 100,
256
- });
257
- }
258
- } catch (error) {
259
- const errorMessage = String(error?.message || '添加资料失败');
260
- setSourceUploadProgressItems((prev) => prev.map((item) => (item.stage === 'done'
261
- ? item
262
- : {
263
- ...item,
264
- stage: 'error',
265
- error_message: errorMessage,
266
- })));
267
- setSourceActionError(normalizeError(error, '添加资料失败'));
268
- } finally {
269
- setSourceUploadLoading(false);
270
- }
271
- }, [appendLocalSourceFiles, canUsePersistentSourceAPI, loadProjectSources, projectId, sourceAPI, userId]);
272
-
273
- const handleOpenSourcePicker = useCallback((nativeFiles = []) => {
274
- if (sourceUploadLoading) {
275
- return;
276
- }
277
- if (Array.isArray(nativeFiles) && nativeFiles.length > 0) {
278
- void uploadSourceFiles(nativeFiles);
279
- return;
280
- }
281
- setSourceActionError(null);
282
- fileInputRef.current?.click();
283
- }, [sourceUploadLoading, uploadSourceFiles]);
284
-
285
- const handleSourceFileChange = useCallback((event) => {
286
- const nativeFiles = Array.from(event?.target?.files || []);
287
- event.target.value = '';
288
- void uploadSourceFiles(nativeFiles);
289
- }, [uploadSourceFiles]);
290
-
291
- const handleRemoveSource = useCallback(async (sourceId) => {
292
- const scopedProjectId = String(projectId || '').trim();
293
- const normalizedSourceId = String(sourceId || '').trim();
294
- if (!normalizedSourceId) {
295
- return;
296
- }
297
- setSourceActionError(null);
298
- setSourceRemovingId(normalizedSourceId);
299
- try {
300
- if (canUsePersistentSourceAPI && scopedProjectId) {
301
- await sourceAPI.deleteProjectSource(scopedProjectId, normalizedSourceId);
302
- await loadProjectSources();
303
- } else {
304
- setSourceItems((prev) => prev.filter((item) => {
305
- const currentSourceId = String(item?.source_id || item?.id || '').trim();
306
- return currentSourceId !== normalizedSourceId;
307
- }));
308
- }
309
- } catch (error) {
310
- setSourceActionError(normalizeError(error, '删除资料失败'));
311
- } finally {
312
- setSourceRemovingId('');
313
- }
314
- }, [canUsePersistentSourceAPI, loadProjectSources, projectId, sourceAPI]);
315
-
316
- const handleRetrySource = useCallback(() => {
317
- if (canUsePersistentSourceAPI) {
318
- loadProjectSources();
319
- return;
320
- }
321
- setSourceActionError(null);
322
- }, [canUsePersistentSourceAPI, loadProjectSources]);
323
-
324
- const handleViewSourceDetail = useCallback(() => {}, []);
325
-
326
- return {
327
- fileInputRef,
328
- sourceCapabilityMode: canUsePersistentSourceAPI ? 'persistent' : 'local_fallback',
329
- sourceActionError,
330
- sourceItems,
331
- sourceRemovingId,
332
- sourceSyncLoading,
333
- sourceUploadLoading,
334
- sourceUploadProgressItems,
335
- handleOpenSourcePicker,
336
- handleSourceFileChange,
337
- handleRemoveSource,
338
- handleRetrySource,
339
- handleViewSourceDetail,
340
- };
341
- }
@@ -1,103 +0,0 @@
1
- import { useEffect, useMemo, useState } from 'react';
2
-
3
- function joinUrl(baseUrl = '', path = '') {
4
- if (!baseUrl) {
5
- return path || '';
6
- }
7
- if (!path) {
8
- return baseUrl;
9
- }
10
- return `${baseUrl.replace(/\/+$/, '')}/${path.replace(/^\/+/, '')}`;
11
- }
12
-
13
- function createGateError({ message, status = 0, method = 'GET', url = '' }) {
14
- const error = new Error(message);
15
- error.status = status;
16
- error.method = method;
17
- error.url = url;
18
- return error;
19
- }
20
-
21
- export function useRealApiReadinessGate({
22
- enabled = false,
23
- apiBaseUrl = '',
24
- apiToken = '',
25
- } = {}) {
26
- const [checking, setChecking] = useState(false);
27
- const [ready, setReady] = useState(false);
28
- const [error, setError] = useState(null);
29
-
30
- const normalizedApiBaseUrl = String(apiBaseUrl || '').trim();
31
-
32
- useEffect(() => {
33
- let cancelled = false;
34
-
35
- const run = async () => {
36
- if (!enabled) {
37
- setChecking(false);
38
- setReady(true);
39
- setError(null);
40
- return;
41
- }
42
-
43
- if (!normalizedApiBaseUrl) {
44
- setChecking(false);
45
- setReady(false);
46
- setError(createGateError({
47
- message: 'real 模式要求配置 VITE_FLARE_CHAT_API_BASE_URL',
48
- }));
49
- return;
50
- }
51
-
52
- setChecking(true);
53
- setReady(false);
54
- setError(null);
55
-
56
- const url = joinUrl(normalizedApiBaseUrl, '/chat/sessions?page=1&page_size=1');
57
- const headers = new Headers();
58
- if (apiToken) {
59
- headers.set('Authorization', `Bearer ${String(apiToken || '').trim()}`);
60
- }
61
-
62
- try {
63
- const response = await fetch(url, {
64
- method: 'GET',
65
- headers,
66
- });
67
- if (!response.ok) {
68
- throw createGateError({
69
- message: `API 预检失败: HTTP ${response.status} ${response.statusText || ''}`.trim(),
70
- status: response.status,
71
- method: 'GET',
72
- url,
73
- });
74
- }
75
- if (!cancelled) {
76
- setReady(true);
77
- }
78
- } catch (nextError) {
79
- if (!cancelled) {
80
- setReady(false);
81
- setError(nextError);
82
- }
83
- } finally {
84
- if (!cancelled) {
85
- setChecking(false);
86
- }
87
- }
88
- };
89
-
90
- run();
91
-
92
- return () => {
93
- cancelled = true;
94
- };
95
- }, [apiToken, enabled, normalizedApiBaseUrl]);
96
-
97
- return useMemo(() => ({
98
- checking,
99
- ready,
100
- error,
101
- blocked: enabled && Boolean(error),
102
- }), [checking, enabled, error, ready]);
103
- }
@@ -1,29 +0,0 @@
1
- import { useCallback, useState } from 'react';
2
-
3
- function createUnavailableError(message, fallbackMessage) {
4
- if (message instanceof Error) {
5
- return message;
6
- }
7
- return new Error(String(message || fallbackMessage));
8
- }
9
-
10
- export function useUnavailableActionError({
11
- fallbackMessage = '当前能力尚未接入。',
12
- } = {}) {
13
- const [error, setError] = useState(null);
14
-
15
- const raiseUnavailableError = useCallback((message) => {
16
- setError(createUnavailableError(message, fallbackMessage));
17
- }, [fallbackMessage]);
18
-
19
- const clearUnavailableError = useCallback(() => {
20
- setError(null);
21
- }, []);
22
-
23
- return {
24
- error,
25
- raiseUnavailableError,
26
- clearUnavailableError,
27
- };
28
- }
29
-