flare-chat-core 0.2.3 → 0.2.5

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 +6372 -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,161 +0,0 @@
1
- export function ensureFetchImpl(fetchImpl) {
2
- const resolvedFetch = fetchImpl ?? globalThis.fetch;
3
- if (typeof resolvedFetch !== 'function') {
4
- throw new Error('fetch implementation is required');
5
- }
6
- return resolvedFetch;
7
- }
8
-
9
- export function joinUrl(baseUrl = '', path = '') {
10
- if (!baseUrl) {
11
- return path || '';
12
- }
13
- if (!path) {
14
- return baseUrl;
15
- }
16
- return `${baseUrl.replace(/\/+$/, '')}/${path.replace(/^\/+/, '')}`;
17
- }
18
-
19
- export function buildHeaders({ headers, token }) {
20
- const resolvedHeaders = new Headers(headers || {});
21
- if (token) {
22
- resolvedHeaders.set('Authorization', `Bearer ${token}`);
23
- }
24
- return resolvedHeaders;
25
- }
26
-
27
- export async function parseJsonResponse(response) {
28
- const text = await response.text();
29
- if (!text) {
30
- return {};
31
- }
32
- try {
33
- return JSON.parse(text);
34
- } catch {
35
- return {};
36
- }
37
- }
38
-
39
- export function createHttpError({ status, statusText, method, url, payload }) {
40
- const payloadMessage = String(payload?.message || payload?.detail || '').trim();
41
- const message = payloadMessage || String(statusText || 'request failed');
42
- const error = new Error(`HTTP ${status}: ${message}`);
43
- error.status = status;
44
- error.method = method;
45
- error.url = url;
46
- error.payload = payload;
47
- return error;
48
- }
49
-
50
- export async function requestFormWithUploadProgress({
51
- url,
52
- method = 'POST',
53
- formData,
54
- headers,
55
- signal,
56
- onProgress,
57
- }) {
58
- return new Promise((resolve, reject) => {
59
- const xhr = new XMLHttpRequest();
60
- xhr.open(method, url, true);
61
- headers.forEach((value, key) => {
62
- if (String(key).toLowerCase() === 'content-type') {
63
- return;
64
- }
65
- xhr.setRequestHeader(key, value);
66
- });
67
- if (typeof onProgress === 'function') {
68
- xhr.upload.onprogress = (event) => {
69
- if (!event.lengthComputable) {
70
- return;
71
- }
72
- const percent = (event.loaded / event.total) * 100;
73
- onProgress(percent);
74
- };
75
- }
76
- xhr.onload = () => {
77
- let payload = {};
78
- const rawText = String(xhr.responseText || '').trim();
79
- if (rawText) {
80
- try {
81
- payload = JSON.parse(rawText);
82
- } catch {
83
- payload = {};
84
- }
85
- }
86
- if (xhr.status >= 200 && xhr.status < 300) {
87
- resolve(payload);
88
- return;
89
- }
90
- reject(createHttpError({
91
- status: xhr.status,
92
- statusText: xhr.statusText,
93
- method,
94
- url,
95
- payload,
96
- }));
97
- };
98
- xhr.onerror = () => {
99
- reject(new Error('Network request failed'));
100
- };
101
- xhr.onabort = () => {
102
- reject(new Error('Request aborted'));
103
- };
104
- if (signal) {
105
- signal.addEventListener('abort', () => xhr.abort(), { once: true });
106
- }
107
- xhr.send(formData);
108
- });
109
- }
110
-
111
- export async function requestJson(path, { method = 'GET', body, options }) {
112
- const fetchImpl = ensureFetchImpl(options.fetchImpl);
113
- const url = joinUrl(options.baseUrl, path);
114
- const resolvedHeaders = buildHeaders(options);
115
- resolvedHeaders.set('Content-Type', 'application/json');
116
- const response = await fetchImpl(url, {
117
- method,
118
- headers: resolvedHeaders,
119
- body: body ? JSON.stringify(body) : undefined,
120
- signal: options.signal,
121
- });
122
- const payload = await parseJsonResponse(response);
123
- if (!response.ok) {
124
- throw createHttpError({
125
- status: response.status,
126
- statusText: response.statusText,
127
- method,
128
- url,
129
- payload,
130
- });
131
- }
132
- return payload;
133
- }
134
-
135
- export async function requestJsonWithRouteFallback({
136
- buildPath,
137
- method = 'GET',
138
- body,
139
- options,
140
- routeMode,
141
- setRouteMode,
142
- }) {
143
- const primaryMode = routeMode === 'chat' ? 'chat' : 'root';
144
- const fallbackMode = primaryMode === 'chat' ? 'root' : 'chat';
145
- try {
146
- return await requestJson(buildPath(primaryMode), { method, body, options });
147
- } catch (error) {
148
- const statusCode = Number(error?.status);
149
- const errorMessage = String(error?.message || '').trim().toLowerCase();
150
- const isRouteNotFound = statusCode === 404;
151
- const isNetworkLikeFailure = !Number.isFinite(statusCode)
152
- && (errorMessage.includes('failed to fetch') || errorMessage.includes('network request failed'));
153
- if (!isRouteNotFound && !isNetworkLikeFailure) {
154
- throw error;
155
- }
156
- const payloadPath = buildPath(fallbackMode);
157
- const payload = await requestJson(payloadPath, { method, body, options });
158
- setRouteMode(fallbackMode);
159
- return payload;
160
- }
161
- }
@@ -1,34 +0,0 @@
1
- import { createSessionAPI } from './session-api.adapter.js';
2
- import { createMessageAPI } from './message-api.adapter.js';
3
- import { createSourceAPI } from './source-api.adapter.js';
4
-
5
- export function createSessionMessageAPI({
6
- baseUrl = '',
7
- token = '',
8
- headers,
9
- fetchImpl,
10
- signal,
11
- } = {}) {
12
- const requestOptions = {
13
- baseUrl,
14
- token: String(token || '').trim(),
15
- headers,
16
- fetchImpl,
17
- signal,
18
- };
19
-
20
- const routeState = {
21
- sourceRouteMode: 'root',
22
- fileRouteMode: 'root',
23
- sessionCollectionRouteMode: 'chat',
24
- sessionDetailRouteMode: 'chat',
25
- sessionMessageRouteMode: 'chat',
26
- messageCollectionRouteMode: 'chat',
27
- };
28
-
29
- return {
30
- sessionAPI: createSessionAPI(requestOptions, routeState),
31
- messageAPI: createMessageAPI(requestOptions, routeState),
32
- sourceAPI: createSourceAPI(requestOptions, routeState),
33
- };
34
- }
@@ -1,180 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { createSessionMessageAPI } from './session-message-api.js';
4
-
5
- function buildJsonResponse(payload, status = 200) {
6
- return new Response(JSON.stringify(payload), {
7
- status,
8
- headers: { 'Content-Type': 'application/json' },
9
- });
10
- }
11
-
12
- function buildFetchWithSources(sources) {
13
- return async () => buildJsonResponse({ sources });
14
- }
15
-
16
- function buildFetchWithMessages(messages) {
17
- return async () => buildJsonResponse({ messages });
18
- }
19
-
20
- test('normalizeSourceRecord keeps source_id and error_code from server payload', async () => {
21
- const api = createSessionMessageAPI({
22
- fetchImpl: buildFetchWithSources([
23
- {
24
- id: 'row-1',
25
- source_id: 'src-1',
26
- status: 'failed',
27
- error_code: 'ingest_failed',
28
- error_message: 'parse failed',
29
- },
30
- ]),
31
- });
32
-
33
- const response = await api.sourceAPI.listProjectSources('project-1');
34
- assert.equal(response.sources[0].id, 'row-1');
35
- assert.equal(response.sources[0].source_id, 'src-1');
36
- assert.equal(response.sources[0].status, 'failed');
37
- assert.equal(response.sources[0].error_code, 'ingest_failed');
38
- assert.equal(response.sources[0].error_message, 'parse failed');
39
- });
40
-
41
- test('normalizeSourceRecord falls back source_id to resolved id', async () => {
42
- const api = createSessionMessageAPI({
43
- fetchImpl: buildFetchWithSources([
44
- {
45
- id: 'row-2',
46
- },
47
- ]),
48
- });
49
-
50
- const response = await api.sourceAPI.listProjectSources('project-2');
51
- assert.equal(response.sources[0].id, 'row-2');
52
- assert.equal(response.sources[0].source_id, 'row-2');
53
- assert.equal(response.sources[0].error_code, null);
54
- });
55
-
56
- test('normalizeSourceRecord keeps explicit server status and only defaults when missing', async () => {
57
- const apiWithExplicitEmptyStatus = createSessionMessageAPI({
58
- fetchImpl: buildFetchWithSources([
59
- {
60
- id: 'row-3',
61
- status: '',
62
- },
63
- ]),
64
- });
65
- const explicitStatusResponse = await apiWithExplicitEmptyStatus.sourceAPI.listProjectSources('project-3');
66
- assert.equal(explicitStatusResponse.sources[0].status, '');
67
-
68
- const apiWithMissingStatus = createSessionMessageAPI({
69
- fetchImpl: buildFetchWithSources([
70
- {
71
- id: 'row-4',
72
- },
73
- ]),
74
- });
75
- const missingStatusResponse = await apiWithMissingStatus.sourceAPI.listProjectSources('project-4');
76
- assert.equal(missingStatusResponse.sources[0].status, 'ready');
77
- });
78
-
79
- test('normalizeSourceRecord keeps explicit null status from server payload', async () => {
80
- const api = createSessionMessageAPI({
81
- fetchImpl: buildFetchWithSources([
82
- {
83
- id: 'row-5',
84
- status: null,
85
- },
86
- ]),
87
- });
88
-
89
- const response = await api.sourceAPI.listProjectSources('project-5');
90
- assert.equal(response.sources[0].status, null);
91
- });
92
-
93
- test('listProjectSources appends session_id query when provided', async () => {
94
- let requestedUrl = '';
95
- const api = createSessionMessageAPI({
96
- fetchImpl: async (url) => {
97
- requestedUrl = String(url || '');
98
- return buildJsonResponse({ sources: [] });
99
- },
100
- });
101
-
102
- await api.sourceAPI.listProjectSources('project-6', 'sess-6');
103
- assert.match(requestedUrl, /\/projects\/project-6\/sources\?session_id=sess-6$/);
104
- });
105
-
106
- test('normalizeMessageRecord keeps attachments and refs for session replay', async () => {
107
- const api = createSessionMessageAPI({
108
- fetchImpl: buildFetchWithMessages([
109
- {
110
- message_id: 'm-1',
111
- session_id: 's-1',
112
- role: 'user',
113
- content: '总结附件',
114
- attachments: [
115
- {
116
- source_id: 'src-1',
117
- filename: '附件一.pdf',
118
- mime_type: 'application/pdf',
119
- },
120
- ],
121
- context_refs: [{ source_id: 'src-1' }],
122
- knowledge_refs: ['src-2'],
123
- },
124
- ]),
125
- });
126
-
127
- const response = await api.messageAPI.list('s-1');
128
- assert.equal(response.messages[0].attachments.length, 2);
129
- assert.equal(response.messages[0].attachments[0].filename, '附件一.pdf');
130
- assert.equal(response.messages[0].attachments[1].source_id, 'src-2');
131
- assert.deepEqual(response.messages[0].context_refs, [{ source_id: 'src-1' }]);
132
- assert.deepEqual(response.messages[0].knowledge_refs, [{ source_id: 'src-2' }]);
133
- });
134
-
135
- test('normalizeMessageRecord keeps sourcing_candidates for canvas sourcing panel projection', async () => {
136
- const api = createSessionMessageAPI({
137
- fetchImpl: buildFetchWithMessages([
138
- {
139
- message_id: 'm-sourcing-1',
140
- session_id: 's-sourcing-1',
141
- role: 'assistant',
142
- content: '已完成寻源',
143
- sourcing_candidates: {
144
- run_id: 'run-1',
145
- candidates: [
146
- { id: 'c-1', title: '供应商A' },
147
- ],
148
- },
149
- },
150
- ]),
151
- });
152
-
153
- const response = await api.messageAPI.list('s-sourcing-1');
154
- assert.equal(response.messages[0].sourcing_candidates.run_id, 'run-1');
155
- assert.equal(response.messages[0].sourcing_candidates.candidates[0].id, 'c-1');
156
- });
157
-
158
- test('uploadSourceFile includes user_id in form data when provided', async () => {
159
- let capturedUserId = '';
160
- const api = createSessionMessageAPI({
161
- fetchImpl: async (_url, init = {}) => {
162
- const formData = init?.body;
163
- capturedUserId = String(formData?.get?.('user_id') || '');
164
- return buildJsonResponse({
165
- data: {
166
- source_id: 'src-user-test',
167
- status: 'uploaded',
168
- },
169
- });
170
- },
171
- });
172
-
173
- await api.sourceAPI.uploadSourceFile({
174
- project_id: 'project-uid-test',
175
- user_id: 'user_demo_001',
176
- file: new File(['hello'], 'probe.txt', { type: 'text/plain' }),
177
- });
178
-
179
- assert.equal(capturedUserId, 'user_demo_001');
180
- });
@@ -1,153 +0,0 @@
1
- export function normalizeSessionRecord(record = {}) {
2
- return {
3
- sessionId: String(record.session_id || record.sessionId || '').trim(),
4
- project_id: record.project_id ?? null,
5
- title: String(record.title || '').trim(),
6
- preview: String(record.preview || '').trim(),
7
- title_source: String(record.title_source || '').trim(),
8
- status: String(record.status || 'active').trim(),
9
- user_id: record.user_id ?? null,
10
- function_type: record.function_type ?? null,
11
- createdAt: record.created_at || record.createdAt || '',
12
- updatedAt: record.updated_at || record.updatedAt || '',
13
- };
14
- }
15
-
16
- export function normalizeMessageRefList(value) {
17
- if (!Array.isArray(value)) {
18
- return [];
19
- }
20
- const refs = [];
21
- const seen = new Set();
22
- value.forEach((item) => {
23
- const sourceId = String(
24
- typeof item === 'string'
25
- ? item
26
- : item?.source_id || item?.id || ''
27
- ).trim();
28
- if (!sourceId || seen.has(sourceId)) {
29
- return;
30
- }
31
- seen.add(sourceId);
32
- refs.push({ source_id: sourceId });
33
- });
34
- return refs;
35
- }
36
-
37
- export function normalizeMessageAttachmentList(value, contextRefs = [], knowledgeRefs = []) {
38
- const base = Array.isArray(value) ? value : [];
39
- const attachments = [];
40
- const seen = new Set();
41
- base.forEach((item) => {
42
- const sourceId = String(item?.source_id || item?.id || '').trim();
43
- const filename = String(item?.filename || item?.name || '').trim();
44
- const mimeType = String(item?.mime_type || item?.type || '').trim();
45
- if (!sourceId && !filename) {
46
- return;
47
- }
48
- if (sourceId && seen.has(sourceId)) {
49
- return;
50
- }
51
- if (sourceId) {
52
- seen.add(sourceId);
53
- }
54
- attachments.push({
55
- source_id: sourceId || '',
56
- filename: filename || (sourceId ? sourceId : '未命名附件'),
57
- mime_type: mimeType || '',
58
- status: String(item?.status || 'ready').trim() || 'ready',
59
- scope: String(item?.scope || 'session').trim() || 'session',
60
- });
61
- });
62
- [...contextRefs, ...knowledgeRefs].forEach((item) => {
63
- const sourceId = String(item?.source_id || '').trim();
64
- if (!sourceId || seen.has(sourceId)) {
65
- return;
66
- }
67
- seen.add(sourceId);
68
- attachments.push({
69
- source_id: sourceId,
70
- filename: sourceId,
71
- mime_type: '',
72
- status: 'ready',
73
- scope: 'session',
74
- });
75
- });
76
- return attachments;
77
- }
78
-
79
- export function normalizeMessageRecord(record = {}) {
80
- const contextRefs = normalizeMessageRefList(record.context_refs);
81
- const knowledgeRefs = normalizeMessageRefList(record.knowledge_refs);
82
- const agentStatus = (
83
- record.agent_status
84
- && typeof record.agent_status === 'object'
85
- && !Array.isArray(record.agent_status)
86
- ) ? { ...record.agent_status } : null;
87
- const executionTrace = (
88
- record.execution_trace
89
- && typeof record.execution_trace === 'object'
90
- && !Array.isArray(record.execution_trace)
91
- ) ? { ...record.execution_trace } : null;
92
- const knowledgeSearch = (
93
- record.knowledge_search
94
- && typeof record.knowledge_search === 'object'
95
- && !Array.isArray(record.knowledge_search)
96
- ) ? { ...record.knowledge_search } : null;
97
- const knowledgeCitation = (
98
- record.knowledge_citation
99
- && typeof record.knowledge_citation === 'object'
100
- && !Array.isArray(record.knowledge_citation)
101
- ) ? { ...record.knowledge_citation } : null;
102
- const sourcingCandidates = (
103
- record.sourcing_candidates
104
- && typeof record.sourcing_candidates === 'object'
105
- && !Array.isArray(record.sourcing_candidates)
106
- ) ? { ...record.sourcing_candidates } : null;
107
- const contextUsage = (
108
- record.context_usage
109
- && typeof record.context_usage === 'object'
110
- && !Array.isArray(record.context_usage)
111
- ) ? { ...record.context_usage } : null;
112
- return {
113
- message_id: String(record.message_id || '').trim(),
114
- session_id: String(record.session_id || '').trim(),
115
- role: String(record.role || '').trim(),
116
- content: String(record.content || ''),
117
- attachments: normalizeMessageAttachmentList(record.attachments, contextRefs, knowledgeRefs),
118
- context_refs: contextRefs,
119
- knowledge_refs: knowledgeRefs,
120
- agent_status: agentStatus,
121
- thinking_trace: String(record.thinking_trace || ''),
122
- execution_trace: executionTrace,
123
- knowledge_search: knowledgeSearch,
124
- sourcing_candidates: sourcingCandidates,
125
- knowledge_citation: knowledgeCitation,
126
- context_usage: contextUsage,
127
- created_at: record.created_at || '',
128
- };
129
- }
130
-
131
- export function normalizeSourceRecord(record = {}) {
132
- const resolvedId = String(record.id || record.source_id || record.uid || '').trim();
133
- const hasStatus = Object.prototype.hasOwnProperty.call(record, 'status');
134
- const resolvedStatus = hasStatus ? record.status : 'ready';
135
- return {
136
- id: resolvedId,
137
- source_id: String(record.source_id || resolvedId).trim(),
138
- project_id: record.project_id ?? null,
139
- message_id: record.message_id ?? null,
140
- scope: String(record.scope || 'project').trim() || 'project',
141
- source: String(record.source || 'local_upload').trim() || 'local_upload',
142
- filename: String(record.filename || record.file_name || record.name || '').trim(),
143
- mime_type: String(record.mime_type || record.type || '').trim(),
144
- size: Number.isFinite(record.size) ? record.size : null,
145
- created_at: record.created_at || '',
146
- status: resolvedStatus,
147
- error_code: record.error_code === undefined || record.error_code === null
148
- ? null
149
- : String(record.error_code).trim(),
150
- error_message: String(record.error_message || '').trim(),
151
- persisted_to_project: record.persisted_to_project === true,
152
- };
153
- }
@@ -1,135 +0,0 @@
1
- import { normalizeSourceRecord } from './session-message-api.normalizers.js';
2
- import {
3
- buildHeaders,
4
- createHttpError,
5
- ensureFetchImpl,
6
- joinUrl,
7
- parseJsonResponse,
8
- requestFormWithUploadProgress,
9
- requestJsonWithRouteFallback,
10
- } from './session-message-api.http.js';
11
-
12
- export function createSourceAPI(requestOptions, routeState) {
13
- return {
14
- async listProjectSources(projectId, sessionId = '') {
15
- const resolvedProjectId = String(projectId || '').trim();
16
- if (!resolvedProjectId) {
17
- throw new Error('projectId is required');
18
- }
19
- const encodedProjectId = encodeURIComponent(resolvedProjectId);
20
- const resolvedSessionId = String(sessionId || '').trim();
21
- const query = resolvedSessionId
22
- ? `?session_id=${encodeURIComponent(resolvedSessionId)}`
23
- : '';
24
- const response = await requestJsonWithRouteFallback({
25
- buildPath: (mode) => (mode === 'chat'
26
- ? `/chat/projects/${encodedProjectId}/sources${query}`
27
- : `/projects/${encodedProjectId}/sources${query}`),
28
- method: 'GET',
29
- options: requestOptions,
30
- routeMode: routeState.sourceRouteMode,
31
- setRouteMode: (nextMode) => {
32
- routeState.sourceRouteMode = nextMode;
33
- },
34
- });
35
- const source = Array.isArray(response?.sources)
36
- ? response.sources
37
- : Array.isArray(response?.data?.sources)
38
- ? response.data.sources
39
- : [];
40
- return {
41
- sources: source.map(normalizeSourceRecord),
42
- };
43
- },
44
-
45
- async uploadSourceFile(payload = {}) {
46
- const resolvedProjectId = String(payload.project_id || '').trim();
47
- if (!resolvedProjectId) {
48
- throw new Error('project_id is required');
49
- }
50
- if (!(payload.file instanceof File)) {
51
- throw new Error('file is required');
52
- }
53
- const formData = new FormData();
54
- formData.append('project_id', resolvedProjectId);
55
- const resolvedUserId = String(payload.user_id || '').trim();
56
- if (resolvedUserId) {
57
- formData.append('user_id', resolvedUserId);
58
- }
59
- if (payload.session_id !== undefined && payload.session_id !== null) {
60
- formData.append('session_id', String(payload.session_id));
61
- }
62
- formData.append('file', payload.file, payload.filename || payload.file.name || 'upload.bin');
63
-
64
- const fetchImpl = ensureFetchImpl(requestOptions.fetchImpl);
65
- const onProgress = typeof payload.onProgress === 'function' ? payload.onProgress : null;
66
- const executeUpload = async (mode) => {
67
- const path = mode === 'chat' ? '/chat/files/upload' : '/files/upload';
68
- const url = joinUrl(requestOptions.baseUrl, path);
69
- if (onProgress) {
70
- return requestFormWithUploadProgress({
71
- url,
72
- method: 'POST',
73
- formData,
74
- headers: buildHeaders(requestOptions),
75
- signal: requestOptions.signal,
76
- onProgress,
77
- });
78
- }
79
- const response = await fetchImpl(url, {
80
- method: 'POST',
81
- headers: buildHeaders(requestOptions),
82
- body: formData,
83
- signal: requestOptions.signal,
84
- });
85
- const responsePayload = await parseJsonResponse(response);
86
- if (!response.ok) {
87
- throw createHttpError({
88
- status: response.status,
89
- statusText: response.statusText,
90
- method: 'POST',
91
- url,
92
- payload: responsePayload,
93
- });
94
- }
95
- return responsePayload;
96
- };
97
- let responsePayload;
98
- try {
99
- responsePayload = await executeUpload(routeState.fileRouteMode);
100
- } catch (error) {
101
- if (Number(error?.status) !== 404) {
102
- throw error;
103
- }
104
- const fallbackMode = routeState.fileRouteMode === 'chat' ? 'root' : 'chat';
105
- responsePayload = await executeUpload(fallbackMode);
106
- routeState.fileRouteMode = fallbackMode;
107
- }
108
- return normalizeSourceRecord(responsePayload?.data || responsePayload);
109
- },
110
-
111
- async deleteProjectSource(projectId, sourceId) {
112
- const resolvedProjectId = String(projectId || '').trim();
113
- const resolvedSourceId = String(sourceId || '').trim();
114
- if (!resolvedProjectId) {
115
- throw new Error('projectId is required');
116
- }
117
- if (!resolvedSourceId) {
118
- throw new Error('sourceId is required');
119
- }
120
- const encodedProjectId = encodeURIComponent(resolvedProjectId);
121
- const encodedSourceId = encodeURIComponent(resolvedSourceId);
122
- return requestJsonWithRouteFallback({
123
- buildPath: (mode) => (mode === 'chat'
124
- ? `/chat/projects/${encodedProjectId}/sources/${encodedSourceId}`
125
- : `/projects/${encodedProjectId}/sources/${encodedSourceId}`),
126
- method: 'DELETE',
127
- options: requestOptions,
128
- routeMode: routeState.sourceRouteMode,
129
- setRouteMode: (nextMode) => {
130
- routeState.sourceRouteMode = nextMode;
131
- },
132
- });
133
- },
134
- };
135
- }