@ww_nero/mini-cli 1.0.81 → 1.0.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -40
- package/package.json +38 -38
- package/src/chat.js +1150 -1150
- package/src/config.js +342 -342
- package/src/index.js +39 -39
- package/src/llm.js +147 -147
- package/src/prompt/tool.js +18 -18
- package/src/request.js +338 -328
- package/src/tools/bash.js +144 -144
- package/src/tools/edit.js +137 -137
- package/src/tools/index.js +74 -74
- package/src/tools/mcp.js +503 -503
- package/src/tools/read.js +44 -44
- package/src/tools/skills.js +49 -49
- package/src/tools/todos.js +90 -90
- package/src/tools/write.js +66 -66
- package/src/utils/cliOptions.js +9 -9
- package/src/utils/git.js +89 -89
- package/src/utils/history.js +181 -181
- package/src/utils/model.js +131 -131
- package/src/utils/output.js +75 -75
- package/src/utils/renderer.js +102 -102
- package/src/utils/settings.js +77 -77
- package/src/utils/skills.js +250 -250
- package/src/utils/think.js +214 -214
package/src/request.js
CHANGED
|
@@ -1,328 +1,338 @@
|
|
|
1
|
-
const fetch = require('node-fetch');
|
|
2
|
-
const { createParser } = require('eventsource-parser');
|
|
3
|
-
const { COMPACT_SUMMARY_PROMPT } = require('./config');
|
|
4
|
-
|
|
5
|
-
const joinUrl = (base, pathname) => {
|
|
6
|
-
const normalizedBase = String(base || '').replace(/\/$/, '');
|
|
7
|
-
const normalizedPath = pathname.startsWith('/') ? pathname : `/${pathname}`;
|
|
8
|
-
return `${normalizedBase}${normalizedPath}`;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const createAbortError = () => {
|
|
12
|
-
if (typeof DOMException === 'function') {
|
|
13
|
-
return new DOMException('Aborted', 'AbortError');
|
|
14
|
-
}
|
|
15
|
-
const error = new Error('Aborted');
|
|
16
|
-
error.name = 'AbortError';
|
|
17
|
-
return error;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const createFetchOptions = (endpoint, body, abortController) => ({
|
|
21
|
-
method: 'POST',
|
|
22
|
-
headers: {
|
|
23
|
-
Authorization: `Bearer ${endpoint.key}`,
|
|
24
|
-
'Content-Type': 'application/json'
|
|
25
|
-
},
|
|
26
|
-
body: JSON.stringify(body),
|
|
27
|
-
signal: abortController.signal
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const makeRequestWithRetry = async (endpoint, requestBody, abortController, options = {}) => {
|
|
31
|
-
const { maxRetries = 5, retryDelay = 3000, onRetry = null } = options;
|
|
32
|
-
const url = joinUrl(endpoint.baseUrl, '/chat/completions');
|
|
33
|
-
|
|
34
|
-
const attemptRequest = async () => {
|
|
35
|
-
try {
|
|
36
|
-
const response = await fetch(url, createFetchOptions(endpoint, requestBody, abortController));
|
|
37
|
-
if (!response.ok) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
throw
|
|
44
|
-
}
|
|
45
|
-
return { response: null
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
abortController.signal.removeEventListener('abort', handleAbort);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (msg.
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
parts.push(`[${
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
1
|
+
const fetch = require('node-fetch');
|
|
2
|
+
const { createParser } = require('eventsource-parser');
|
|
3
|
+
const { COMPACT_SUMMARY_PROMPT } = require('./config');
|
|
4
|
+
|
|
5
|
+
const joinUrl = (base, pathname) => {
|
|
6
|
+
const normalizedBase = String(base || '').replace(/\/$/, '');
|
|
7
|
+
const normalizedPath = pathname.startsWith('/') ? pathname : `/${pathname}`;
|
|
8
|
+
return `${normalizedBase}${normalizedPath}`;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const createAbortError = () => {
|
|
12
|
+
if (typeof DOMException === 'function') {
|
|
13
|
+
return new DOMException('Aborted', 'AbortError');
|
|
14
|
+
}
|
|
15
|
+
const error = new Error('Aborted');
|
|
16
|
+
error.name = 'AbortError';
|
|
17
|
+
return error;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const createFetchOptions = (endpoint, body, abortController) => ({
|
|
21
|
+
method: 'POST',
|
|
22
|
+
headers: {
|
|
23
|
+
Authorization: `Bearer ${endpoint.key}`,
|
|
24
|
+
'Content-Type': 'application/json'
|
|
25
|
+
},
|
|
26
|
+
body: JSON.stringify(body),
|
|
27
|
+
signal: abortController.signal
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const makeRequestWithRetry = async (endpoint, requestBody, abortController, options = {}) => {
|
|
31
|
+
const { maxRetries = 5, retryDelay = 3000, onRetry = null } = options;
|
|
32
|
+
const url = joinUrl(endpoint.baseUrl, '/chat/completions');
|
|
33
|
+
|
|
34
|
+
const attemptRequest = async () => {
|
|
35
|
+
try {
|
|
36
|
+
const response = await fetch(url, createFetchOptions(endpoint, requestBody, abortController));
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
let detail = '';
|
|
39
|
+
try {
|
|
40
|
+
const body = await response.text();
|
|
41
|
+
detail = body ? `: ${body}` : '';
|
|
42
|
+
} catch (_) {}
|
|
43
|
+
throw new Error(`HTTP ${response.status}${detail}`);
|
|
44
|
+
}
|
|
45
|
+
return { response, error: null };
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (abortController.signal.aborted) {
|
|
48
|
+
throw createAbortError();
|
|
49
|
+
}
|
|
50
|
+
return { response: null, error };
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
let attempt = 0;
|
|
55
|
+
let result = await attemptRequest();
|
|
56
|
+
|
|
57
|
+
while (!result.response && attempt < maxRetries) {
|
|
58
|
+
attempt += 1;
|
|
59
|
+
if (typeof onRetry === 'function') {
|
|
60
|
+
onRetry(attempt, result.error);
|
|
61
|
+
}
|
|
62
|
+
await new Promise(resolve => {
|
|
63
|
+
const handleAbort = () => {
|
|
64
|
+
abortController.signal.removeEventListener('abort', handleAbort);
|
|
65
|
+
clearTimeout(timeout);
|
|
66
|
+
resolve();
|
|
67
|
+
};
|
|
68
|
+
const timeout = setTimeout(() => {
|
|
69
|
+
abortController.signal.removeEventListener('abort', handleAbort);
|
|
70
|
+
resolve();
|
|
71
|
+
}, retryDelay);
|
|
72
|
+
abortController.signal.addEventListener('abort', handleAbort);
|
|
73
|
+
});
|
|
74
|
+
if (abortController.signal.aborted) {
|
|
75
|
+
throw createAbortError();
|
|
76
|
+
}
|
|
77
|
+
result = await attemptRequest();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!result.response) {
|
|
81
|
+
throw result.error;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return result.response;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const processStreamResponse = (response, options = {}) => {
|
|
88
|
+
const {
|
|
89
|
+
onContent = null,
|
|
90
|
+
onReasoningContent = null,
|
|
91
|
+
onComplete = null,
|
|
92
|
+
abortController = null,
|
|
93
|
+
includeToolCalls = false
|
|
94
|
+
} = options;
|
|
95
|
+
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
let abortHandler = null;
|
|
98
|
+
const cleanupAbortListener = () => {
|
|
99
|
+
if (abortController && abortHandler) {
|
|
100
|
+
abortController.signal.removeEventListener('abort', abortHandler);
|
|
101
|
+
abortHandler = null;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const rejectWithAbort = () => {
|
|
106
|
+
cleanupAbortListener();
|
|
107
|
+
if (response.body && typeof response.body.destroy === 'function') {
|
|
108
|
+
response.body.destroy();
|
|
109
|
+
}
|
|
110
|
+
reject(createAbortError());
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const checkAbort = () => {
|
|
114
|
+
if (abortController && abortController.signal.aborted) {
|
|
115
|
+
rejectWithAbort();
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
let fullContent = '';
|
|
123
|
+
let toolCalls = [];
|
|
124
|
+
let currentMessage = null;
|
|
125
|
+
let fullReasoningContent = '';
|
|
126
|
+
let usage = null;
|
|
127
|
+
const decoder = new TextDecoder('utf-8', { fatal: false });
|
|
128
|
+
|
|
129
|
+
const handleEvent = (event) => {
|
|
130
|
+
if (checkAbort()) return;
|
|
131
|
+
if (!event || event.type !== 'event') {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (!event.data || event.data === '[DONE]') {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const payload = JSON.parse(event.data);
|
|
139
|
+
if (payload.usage && typeof payload.usage === 'object') {
|
|
140
|
+
usage = payload.usage;
|
|
141
|
+
}
|
|
142
|
+
const choice = payload.choices && payload.choices[0];
|
|
143
|
+
if (!choice || !choice.delta) return;
|
|
144
|
+
const delta = choice.delta;
|
|
145
|
+
const contentChunk = delta.content || '';
|
|
146
|
+
if (contentChunk) {
|
|
147
|
+
fullContent += contentChunk;
|
|
148
|
+
if (onContent) {
|
|
149
|
+
onContent(contentChunk, fullContent);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const reasoningChunk = delta.reasoning_content || '';
|
|
154
|
+
if (reasoningChunk) {
|
|
155
|
+
fullReasoningContent += reasoningChunk;
|
|
156
|
+
if (onReasoningContent) {
|
|
157
|
+
onReasoningContent(reasoningChunk, fullReasoningContent);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (includeToolCalls && Array.isArray(delta.tool_calls)) {
|
|
162
|
+
delta.tool_calls.forEach((call, index) => {
|
|
163
|
+
if (!toolCalls[index]) {
|
|
164
|
+
toolCalls[index] = {
|
|
165
|
+
id: call.id || '',
|
|
166
|
+
type: call.type || 'function',
|
|
167
|
+
function: {
|
|
168
|
+
name: (call.function && call.function.name) || '',
|
|
169
|
+
arguments: ''
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (call.function && typeof call.function.arguments === 'string') {
|
|
175
|
+
toolCalls[index].function.arguments += call.function.arguments;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (includeToolCalls) {
|
|
181
|
+
currentMessage = {
|
|
182
|
+
role: 'assistant',
|
|
183
|
+
content: fullContent || null,
|
|
184
|
+
...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),
|
|
185
|
+
...(fullReasoningContent ? { reasoning_content: fullReasoningContent } : {})
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.warn('SSE parse error:', error.message);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const parser = createParser(handleEvent);
|
|
194
|
+
|
|
195
|
+
const safeFeed = (text) => {
|
|
196
|
+
try {
|
|
197
|
+
parser.feed(text);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.warn('SSE error:', error.message);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
response.body.on('data', chunk => {
|
|
204
|
+
if (checkAbort()) return;
|
|
205
|
+
const text = decoder.decode(chunk, { stream: true });
|
|
206
|
+
safeFeed(text);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
response.body.on('end', () => {
|
|
210
|
+
if (checkAbort()) return;
|
|
211
|
+
cleanupAbortListener();
|
|
212
|
+
const rest = decoder.decode();
|
|
213
|
+
if (rest) {
|
|
214
|
+
safeFeed(rest);
|
|
215
|
+
}
|
|
216
|
+
onComplete && onComplete();
|
|
217
|
+
if (includeToolCalls) {
|
|
218
|
+
const filteredToolCalls = toolCalls.filter(Boolean);
|
|
219
|
+
const assistantMessage = currentMessage || {
|
|
220
|
+
role: 'assistant',
|
|
221
|
+
content: fullContent || null,
|
|
222
|
+
...(filteredToolCalls.length > 0 ? { tool_calls: filteredToolCalls } : {})
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
if (fullReasoningContent && !assistantMessage.reasoning_content) {
|
|
226
|
+
assistantMessage.reasoning_content = fullReasoningContent;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
resolve({
|
|
230
|
+
content: fullContent,
|
|
231
|
+
toolCalls: filteredToolCalls,
|
|
232
|
+
message: assistantMessage,
|
|
233
|
+
reasoningContent: fullReasoningContent,
|
|
234
|
+
usage
|
|
235
|
+
});
|
|
236
|
+
} else {
|
|
237
|
+
resolve({
|
|
238
|
+
content: fullContent,
|
|
239
|
+
reasoningContent: fullReasoningContent,
|
|
240
|
+
usage
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
response.body.on('error', (error) => {
|
|
246
|
+
cleanupAbortListener();
|
|
247
|
+
reject(error);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
if (abortController) {
|
|
251
|
+
abortHandler = () => {
|
|
252
|
+
rejectWithAbort();
|
|
253
|
+
};
|
|
254
|
+
abortController.signal.addEventListener('abort', abortHandler);
|
|
255
|
+
}
|
|
256
|
+
} catch (error) {
|
|
257
|
+
reject(error);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const buildConversationForSummary = (messages) => {
|
|
263
|
+
const parts = [];
|
|
264
|
+
for (const msg of messages) {
|
|
265
|
+
if (msg.role === 'system') continue;
|
|
266
|
+
|
|
267
|
+
const role = msg.role === 'user' ? '用户' : msg.role === 'assistant' ? '助手' : '工具';
|
|
268
|
+
let content = msg.content || '';
|
|
269
|
+
|
|
270
|
+
if (msg.tool_call_id) {
|
|
271
|
+
parts.push(`[工具返回 ${msg.tool_call_id}]: ${content}`);
|
|
272
|
+
} else if (msg.tool_calls && msg.tool_calls.length > 0) {
|
|
273
|
+
const toolNames = msg.tool_calls.map(tc => tc.function?.name || '未知工具').join(', ');
|
|
274
|
+
parts.push(`[${role}]: ${content || ''}\n[调用工具: ${toolNames}]`);
|
|
275
|
+
} else {
|
|
276
|
+
parts.push(`[${role}]: ${content}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return parts.join('\n\n');
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
const performCompactSummary = async (endpoint, messages, abortController) => {
|
|
283
|
+
const conversationText = buildConversationForSummary(messages);
|
|
284
|
+
|
|
285
|
+
const summaryMessages = [
|
|
286
|
+
{
|
|
287
|
+
role: 'system',
|
|
288
|
+
content: COMPACT_SUMMARY_PROMPT
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
role: 'user',
|
|
292
|
+
content: `以下是需要总结的对话内容:\n\n${conversationText}`
|
|
293
|
+
}
|
|
294
|
+
];
|
|
295
|
+
|
|
296
|
+
const requestBody = {
|
|
297
|
+
stream: false,
|
|
298
|
+
messages: summaryMessages,
|
|
299
|
+
model: endpoint.model
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const url = joinUrl(endpoint.baseUrl, '/chat/completions');
|
|
303
|
+
|
|
304
|
+
try {
|
|
305
|
+
const response = await fetch(url, createFetchOptions(endpoint, requestBody, abortController));
|
|
306
|
+
|
|
307
|
+
if (!response.ok) {
|
|
308
|
+
let detail = '';
|
|
309
|
+
try {
|
|
310
|
+
const body = await response.text();
|
|
311
|
+
detail = body ? `: ${body}` : '';
|
|
312
|
+
} catch (_) {}
|
|
313
|
+
throw new Error(`HTTP ${response.status}${detail}`);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const data = await response.json();
|
|
317
|
+
const choice = data.choices && data.choices[0];
|
|
318
|
+
|
|
319
|
+
if (choice && choice.message && choice.message.content) {
|
|
320
|
+
return choice.message.content;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
throw new Error('无法获取总结内容');
|
|
324
|
+
} catch (error) {
|
|
325
|
+
if (error.name === 'AbortError') {
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
328
|
+
console.error('Compact 总结请求失败:', error);
|
|
329
|
+
throw error;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
module.exports = {
|
|
334
|
+
makeRequestWithRetry,
|
|
335
|
+
processStreamResponse,
|
|
336
|
+
buildConversationForSummary,
|
|
337
|
+
performCompactSummary
|
|
338
|
+
};
|