snow-ai 0.2.14 → 0.2.16
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/dist/api/anthropic.d.ts +1 -1
- package/dist/api/anthropic.js +52 -76
- package/dist/api/chat.d.ts +4 -4
- package/dist/api/chat.js +32 -17
- package/dist/api/gemini.d.ts +1 -1
- package/dist/api/gemini.js +20 -13
- package/dist/api/responses.d.ts +5 -5
- package/dist/api/responses.js +29 -27
- package/dist/app.js +4 -1
- package/dist/hooks/useClipboard.d.ts +4 -0
- package/dist/hooks/useClipboard.js +120 -0
- package/dist/hooks/useCommandHandler.d.ts +26 -0
- package/dist/hooks/useCommandHandler.js +158 -0
- package/dist/hooks/useCommandPanel.d.ts +16 -0
- package/dist/hooks/useCommandPanel.js +53 -0
- package/dist/hooks/useConversation.d.ts +9 -1
- package/dist/hooks/useConversation.js +152 -58
- package/dist/hooks/useFilePicker.d.ts +17 -0
- package/dist/hooks/useFilePicker.js +91 -0
- package/dist/hooks/useHistoryNavigation.d.ts +21 -0
- package/dist/hooks/useHistoryNavigation.js +50 -0
- package/dist/hooks/useInputBuffer.d.ts +6 -0
- package/dist/hooks/useInputBuffer.js +29 -0
- package/dist/hooks/useKeyboardInput.d.ts +51 -0
- package/dist/hooks/useKeyboardInput.js +272 -0
- package/dist/hooks/useSnapshotState.d.ts +12 -0
- package/dist/hooks/useSnapshotState.js +28 -0
- package/dist/hooks/useStreamingState.d.ts +24 -0
- package/dist/hooks/useStreamingState.js +96 -0
- package/dist/hooks/useVSCodeState.d.ts +8 -0
- package/dist/hooks/useVSCodeState.js +63 -0
- package/dist/mcp/filesystem.d.ts +25 -9
- package/dist/mcp/filesystem.js +56 -51
- package/dist/mcp/todo.js +4 -8
- package/dist/ui/components/ChatInput.js +68 -557
- package/dist/ui/components/DiffViewer.js +57 -30
- package/dist/ui/components/FileList.js +70 -26
- package/dist/ui/components/MessageList.d.ts +6 -0
- package/dist/ui/components/MessageList.js +47 -15
- package/dist/ui/components/ShimmerText.d.ts +9 -0
- package/dist/ui/components/ShimmerText.js +30 -0
- package/dist/ui/components/TodoTree.d.ts +1 -1
- package/dist/ui/components/TodoTree.js +0 -4
- package/dist/ui/components/ToolConfirmation.js +14 -6
- package/dist/ui/pages/ChatScreen.js +159 -359
- package/dist/ui/pages/CustomHeadersScreen.d.ts +6 -0
- package/dist/ui/pages/CustomHeadersScreen.js +104 -0
- package/dist/ui/pages/WelcomeScreen.js +5 -0
- package/dist/utils/apiConfig.d.ts +10 -0
- package/dist/utils/apiConfig.js +51 -0
- package/dist/utils/incrementalSnapshot.d.ts +8 -0
- package/dist/utils/incrementalSnapshot.js +63 -0
- package/dist/utils/mcpToolsManager.js +8 -3
- package/dist/utils/retryUtils.d.ts +22 -0
- package/dist/utils/retryUtils.js +180 -0
- package/dist/utils/sessionConverter.js +80 -17
- package/dist/utils/sessionManager.js +35 -4
- package/dist/utils/textUtils.d.ts +4 -0
- package/dist/utils/textUtils.js +19 -0
- package/dist/utils/todoPreprocessor.d.ts +1 -1
- package/dist/utils/todoPreprocessor.js +0 -1
- package/dist/utils/vscodeConnection.d.ts +8 -0
- package/dist/utils/vscodeConnection.js +44 -0
- package/package.json +1 -13
- package/readme.md +6 -2
- package/dist/mcp/multiLanguageASTParser.d.ts +0 -67
- package/dist/mcp/multiLanguageASTParser.js +0 -360
package/dist/api/anthropic.d.ts
CHANGED
|
@@ -33,4 +33,4 @@ export declare function resetAnthropicClient(): void;
|
|
|
33
33
|
/**
|
|
34
34
|
* Create streaming chat completion using Anthropic API
|
|
35
35
|
*/
|
|
36
|
-
export declare function createStreamingAnthropicCompletion(options: AnthropicOptions, abortSignal?: AbortSignal): AsyncGenerator<AnthropicStreamChunk, void, unknown>;
|
|
36
|
+
export declare function createStreamingAnthropicCompletion(options: AnthropicOptions, abortSignal?: AbortSignal, onRetry?: (error: Error, attempt: number, nextDelay: number) => void): AsyncGenerator<AnthropicStreamChunk, void, unknown>;
|
package/dist/api/anthropic.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import Anthropic from '@anthropic-ai/sdk';
|
|
2
2
|
import { createHash, randomUUID } from 'crypto';
|
|
3
|
-
import { getOpenAiConfig, getCustomSystemPrompt } from '../utils/apiConfig.js';
|
|
3
|
+
import { getOpenAiConfig, getCustomSystemPrompt, getCustomHeaders } from '../utils/apiConfig.js';
|
|
4
4
|
import { SYSTEM_PROMPT } from './systemPrompt.js';
|
|
5
|
+
import { withRetryGenerator } from '../utils/retryUtils.js';
|
|
5
6
|
let anthropicClient = null;
|
|
6
7
|
function getAnthropicClient() {
|
|
7
8
|
if (!anthropicClient) {
|
|
@@ -12,17 +13,26 @@ function getAnthropicClient() {
|
|
|
12
13
|
const clientConfig = {
|
|
13
14
|
apiKey: config.apiKey,
|
|
14
15
|
};
|
|
15
|
-
// Support custom baseUrl for proxy servers
|
|
16
16
|
if (config.baseUrl && config.baseUrl !== 'https://api.openai.com/v1') {
|
|
17
17
|
clientConfig.baseURL = config.baseUrl;
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
if (config.anthropicBeta) {
|
|
21
|
-
clientConfig.defaultQuery = { beta: 'true' };
|
|
22
|
-
}
|
|
23
|
-
// Add Authorization header for enhanced compatibility
|
|
19
|
+
const customHeaders = getCustomHeaders();
|
|
24
20
|
clientConfig.defaultHeaders = {
|
|
25
21
|
'Authorization': `Bearer ${config.apiKey}`,
|
|
22
|
+
//'anthropic-version': '2024-09-24',
|
|
23
|
+
...customHeaders
|
|
24
|
+
};
|
|
25
|
+
// if (config.anthropicBeta) {
|
|
26
|
+
// clientConfig.defaultHeaders['anthropic-beta'] = 'prompt-caching-2024-07-31';
|
|
27
|
+
// }
|
|
28
|
+
// Intercept fetch to add beta parameter to URL
|
|
29
|
+
const originalFetch = clientConfig.fetch || globalThis.fetch;
|
|
30
|
+
clientConfig.fetch = async (url, init) => {
|
|
31
|
+
let finalUrl = url;
|
|
32
|
+
if (config.anthropicBeta && typeof url === 'string' && !url.includes('?beta=')) {
|
|
33
|
+
finalUrl = url + (url.includes('?') ? '&beta=true' : '?beta=true');
|
|
34
|
+
}
|
|
35
|
+
return originalFetch(finalUrl, init);
|
|
26
36
|
};
|
|
27
37
|
anthropicClient = new Anthropic(clientConfig);
|
|
28
38
|
}
|
|
@@ -63,7 +73,6 @@ function convertToolsToAnthropic(tools) {
|
|
|
63
73
|
}
|
|
64
74
|
throw new Error('Invalid tool format');
|
|
65
75
|
});
|
|
66
|
-
// Add cache_control to the last tool for prompt caching
|
|
67
76
|
if (convertedTools.length > 0) {
|
|
68
77
|
const lastTool = convertedTools[convertedTools.length - 1];
|
|
69
78
|
lastTool.cache_control = { type: 'ephemeral' };
|
|
@@ -73,23 +82,17 @@ function convertToolsToAnthropic(tools) {
|
|
|
73
82
|
/**
|
|
74
83
|
* Convert our ChatMessage format to Anthropic's message format
|
|
75
84
|
* Adds cache_control to system prompt and last user message for prompt caching
|
|
76
|
-
* Logic:
|
|
77
|
-
* 1. If custom system prompt exists: use custom as system, prepend default as first user message
|
|
78
|
-
* 2. If no custom system prompt: use default as system
|
|
79
85
|
*/
|
|
80
86
|
function convertToAnthropicMessages(messages) {
|
|
81
87
|
const customSystemPrompt = getCustomSystemPrompt();
|
|
82
88
|
let systemContent;
|
|
83
89
|
const anthropicMessages = [];
|
|
84
90
|
for (const msg of messages) {
|
|
85
|
-
// Extract system message
|
|
86
91
|
if (msg.role === 'system') {
|
|
87
92
|
systemContent = msg.content;
|
|
88
93
|
continue;
|
|
89
94
|
}
|
|
90
|
-
// Handle tool result messages
|
|
91
95
|
if (msg.role === 'tool' && msg.tool_call_id) {
|
|
92
|
-
// Anthropic expects tool results as user messages with tool_result content
|
|
93
96
|
anthropicMessages.push({
|
|
94
97
|
role: 'user',
|
|
95
98
|
content: [{
|
|
@@ -100,19 +103,15 @@ function convertToAnthropicMessages(messages) {
|
|
|
100
103
|
});
|
|
101
104
|
continue;
|
|
102
105
|
}
|
|
103
|
-
// Handle user messages with images
|
|
104
106
|
if (msg.role === 'user' && msg.images && msg.images.length > 0) {
|
|
105
107
|
const content = [];
|
|
106
|
-
// Add text content
|
|
107
108
|
if (msg.content) {
|
|
108
109
|
content.push({
|
|
109
110
|
type: 'text',
|
|
110
111
|
text: msg.content
|
|
111
112
|
});
|
|
112
113
|
}
|
|
113
|
-
// Add images
|
|
114
114
|
for (const image of msg.images) {
|
|
115
|
-
// Extract base64 data and mime type
|
|
116
115
|
const base64Match = image.data.match(/^data:([^;]+);base64,(.+)$/);
|
|
117
116
|
if (base64Match) {
|
|
118
117
|
content.push({
|
|
@@ -131,17 +130,14 @@ function convertToAnthropicMessages(messages) {
|
|
|
131
130
|
});
|
|
132
131
|
continue;
|
|
133
132
|
}
|
|
134
|
-
// Handle assistant messages with tool calls
|
|
135
133
|
if (msg.role === 'assistant' && msg.tool_calls && msg.tool_calls.length > 0) {
|
|
136
134
|
const content = [];
|
|
137
|
-
// Add text content if present
|
|
138
135
|
if (msg.content) {
|
|
139
136
|
content.push({
|
|
140
137
|
type: 'text',
|
|
141
138
|
text: msg.content
|
|
142
139
|
});
|
|
143
140
|
}
|
|
144
|
-
// Add tool uses
|
|
145
141
|
for (const toolCall of msg.tool_calls) {
|
|
146
142
|
content.push({
|
|
147
143
|
type: 'tool_use',
|
|
@@ -156,7 +152,6 @@ function convertToAnthropicMessages(messages) {
|
|
|
156
152
|
});
|
|
157
153
|
continue;
|
|
158
154
|
}
|
|
159
|
-
// Handle regular text messages
|
|
160
155
|
if (msg.role === 'user' || msg.role === 'assistant') {
|
|
161
156
|
anthropicMessages.push({
|
|
162
157
|
role: msg.role,
|
|
@@ -164,25 +159,33 @@ function convertToAnthropicMessages(messages) {
|
|
|
164
159
|
});
|
|
165
160
|
}
|
|
166
161
|
}
|
|
167
|
-
// 如果配置了自定义系统提示词
|
|
168
162
|
if (customSystemPrompt) {
|
|
169
|
-
// 自定义系统提示词作为 system,默认系统提示词作为第一条用户消息
|
|
170
163
|
systemContent = customSystemPrompt;
|
|
171
164
|
anthropicMessages.unshift({
|
|
172
165
|
role: 'user',
|
|
173
|
-
content:
|
|
166
|
+
content: [{
|
|
167
|
+
type: 'text',
|
|
168
|
+
text: SYSTEM_PROMPT,
|
|
169
|
+
cache_control: { type: 'ephemeral' }
|
|
170
|
+
}]
|
|
174
171
|
});
|
|
175
172
|
}
|
|
176
173
|
else if (!systemContent) {
|
|
177
|
-
// 没有自定义系统提示词,默认系统提示词作为 system
|
|
178
174
|
systemContent = SYSTEM_PROMPT;
|
|
179
175
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
176
|
+
let lastUserMessageIndex = -1;
|
|
177
|
+
for (let i = anthropicMessages.length - 1; i >= 0; i--) {
|
|
178
|
+
if (anthropicMessages[i]?.role === 'user') {
|
|
179
|
+
if (customSystemPrompt && i === 0) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
lastUserMessageIndex = i;
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (lastUserMessageIndex >= 0) {
|
|
187
|
+
const lastMessage = anthropicMessages[lastUserMessageIndex];
|
|
184
188
|
if (lastMessage && lastMessage.role === 'user') {
|
|
185
|
-
// Convert content to array format if it's a string
|
|
186
189
|
if (typeof lastMessage.content === 'string') {
|
|
187
190
|
lastMessage.content = [{
|
|
188
191
|
type: 'text',
|
|
@@ -191,7 +194,6 @@ function convertToAnthropicMessages(messages) {
|
|
|
191
194
|
}];
|
|
192
195
|
}
|
|
193
196
|
else if (Array.isArray(lastMessage.content)) {
|
|
194
|
-
// Add cache_control to last content block
|
|
195
197
|
const lastContentIndex = lastMessage.content.length - 1;
|
|
196
198
|
if (lastContentIndex >= 0) {
|
|
197
199
|
const lastContent = lastMessage.content[lastContentIndex];
|
|
@@ -200,7 +202,6 @@ function convertToAnthropicMessages(messages) {
|
|
|
200
202
|
}
|
|
201
203
|
}
|
|
202
204
|
}
|
|
203
|
-
// Format system prompt with cache_control (only if we have a system prompt)
|
|
204
205
|
const system = systemContent ? [{
|
|
205
206
|
type: 'text',
|
|
206
207
|
text: systemContent,
|
|
@@ -211,14 +212,13 @@ function convertToAnthropicMessages(messages) {
|
|
|
211
212
|
/**
|
|
212
213
|
* Create streaming chat completion using Anthropic API
|
|
213
214
|
*/
|
|
214
|
-
export async function* createStreamingAnthropicCompletion(options, abortSignal) {
|
|
215
|
+
export async function* createStreamingAnthropicCompletion(options, abortSignal, onRetry) {
|
|
215
216
|
const client = getAnthropicClient();
|
|
216
|
-
|
|
217
|
+
yield* withRetryGenerator(async function* () {
|
|
217
218
|
const { system, messages } = convertToAnthropicMessages(options.messages);
|
|
218
|
-
// Generate user_id with session tracking if sessionId is provided
|
|
219
219
|
const sessionId = options.sessionId || randomUUID();
|
|
220
220
|
const userId = generateUserId(sessionId);
|
|
221
|
-
|
|
221
|
+
const customHeaders = getCustomHeaders();
|
|
222
222
|
const requestBody = {
|
|
223
223
|
model: options.model,
|
|
224
224
|
max_tokens: options.max_tokens || 4096,
|
|
@@ -231,33 +231,32 @@ export async function* createStreamingAnthropicCompletion(options, abortSignal)
|
|
|
231
231
|
},
|
|
232
232
|
stream: true
|
|
233
233
|
};
|
|
234
|
-
|
|
235
|
-
|
|
234
|
+
const stream = await client.messages.create(requestBody, {
|
|
235
|
+
headers: customHeaders
|
|
236
|
+
});
|
|
236
237
|
let contentBuffer = '';
|
|
237
238
|
let toolCallsBuffer = new Map();
|
|
238
239
|
let hasToolCalls = false;
|
|
239
240
|
let usageData;
|
|
240
|
-
let
|
|
241
|
+
let blockIndexToId = new Map();
|
|
241
242
|
for await (const event of stream) {
|
|
242
243
|
if (abortSignal?.aborted) {
|
|
243
244
|
return;
|
|
244
245
|
}
|
|
245
|
-
// Handle different event types
|
|
246
246
|
if (event.type === 'content_block_start') {
|
|
247
247
|
const block = event.content_block;
|
|
248
|
-
// Handle tool use blocks
|
|
249
248
|
if (block.type === 'tool_use') {
|
|
250
249
|
hasToolCalls = true;
|
|
251
|
-
|
|
250
|
+
const blockIndex = event.index;
|
|
251
|
+
blockIndexToId.set(blockIndex, block.id);
|
|
252
252
|
toolCallsBuffer.set(block.id, {
|
|
253
253
|
id: block.id,
|
|
254
254
|
type: 'function',
|
|
255
255
|
function: {
|
|
256
256
|
name: block.name,
|
|
257
|
-
arguments: '{}'
|
|
257
|
+
arguments: '{}'
|
|
258
258
|
}
|
|
259
259
|
});
|
|
260
|
-
// Yield delta for token counting
|
|
261
260
|
yield {
|
|
262
261
|
type: 'tool_call_delta',
|
|
263
262
|
delta: block.name
|
|
@@ -266,7 +265,6 @@ export async function* createStreamingAnthropicCompletion(options, abortSignal)
|
|
|
266
265
|
}
|
|
267
266
|
else if (event.type === 'content_block_delta') {
|
|
268
267
|
const delta = event.delta;
|
|
269
|
-
// Handle text content
|
|
270
268
|
if (delta.type === 'text_delta') {
|
|
271
269
|
const text = delta.text;
|
|
272
270
|
contentBuffer += text;
|
|
@@ -275,21 +273,19 @@ export async function* createStreamingAnthropicCompletion(options, abortSignal)
|
|
|
275
273
|
content: text
|
|
276
274
|
};
|
|
277
275
|
}
|
|
278
|
-
// Handle tool input deltas
|
|
279
276
|
if (delta.type === 'input_json_delta') {
|
|
280
277
|
const jsonDelta = delta.partial_json;
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
278
|
+
const blockIndex = event.index;
|
|
279
|
+
const toolId = blockIndexToId.get(blockIndex);
|
|
280
|
+
if (toolId) {
|
|
281
|
+
const toolCall = toolCallsBuffer.get(toolId);
|
|
284
282
|
if (toolCall) {
|
|
285
|
-
// If this is the first delta and arguments is still '{}', replace it
|
|
286
283
|
if (toolCall.function.arguments === '{}') {
|
|
287
284
|
toolCall.function.arguments = jsonDelta;
|
|
288
285
|
}
|
|
289
286
|
else {
|
|
290
287
|
toolCall.function.arguments += jsonDelta;
|
|
291
288
|
}
|
|
292
|
-
// Yield delta for token counting
|
|
293
289
|
yield {
|
|
294
290
|
type: 'tool_call_delta',
|
|
295
291
|
delta: jsonDelta
|
|
@@ -298,12 +294,7 @@ export async function* createStreamingAnthropicCompletion(options, abortSignal)
|
|
|
298
294
|
}
|
|
299
295
|
}
|
|
300
296
|
}
|
|
301
|
-
else if (event.type === 'content_block_stop') {
|
|
302
|
-
// Reset current tool use ID when block ends
|
|
303
|
-
currentToolUseId = null;
|
|
304
|
-
}
|
|
305
297
|
else if (event.type === 'message_start') {
|
|
306
|
-
// Capture initial usage data (including cache metrics)
|
|
307
298
|
if (event.message.usage) {
|
|
308
299
|
usageData = {
|
|
309
300
|
prompt_tokens: event.message.usage.input_tokens || 0,
|
|
@@ -315,7 +306,6 @@ export async function* createStreamingAnthropicCompletion(options, abortSignal)
|
|
|
315
306
|
}
|
|
316
307
|
}
|
|
317
308
|
else if (event.type === 'message_delta') {
|
|
318
|
-
// Update usage data with final token counts (including cache metrics)
|
|
319
309
|
if (event.usage) {
|
|
320
310
|
if (!usageData) {
|
|
321
311
|
usageData = {
|
|
@@ -326,7 +316,6 @@ export async function* createStreamingAnthropicCompletion(options, abortSignal)
|
|
|
326
316
|
}
|
|
327
317
|
usageData.completion_tokens = event.usage.output_tokens || 0;
|
|
328
318
|
usageData.total_tokens = usageData.prompt_tokens + usageData.completion_tokens;
|
|
329
|
-
// Update cache metrics if present
|
|
330
319
|
if (event.usage.cache_creation_input_tokens !== undefined) {
|
|
331
320
|
usageData.cache_creation_input_tokens = event.usage.cache_creation_input_tokens;
|
|
332
321
|
}
|
|
@@ -336,17 +325,12 @@ export async function* createStreamingAnthropicCompletion(options, abortSignal)
|
|
|
336
325
|
}
|
|
337
326
|
}
|
|
338
327
|
}
|
|
339
|
-
// Yield tool calls if any (only after stream completes)
|
|
340
328
|
if (hasToolCalls && toolCallsBuffer.size > 0) {
|
|
341
|
-
// Validate that all tool call arguments are complete valid JSON
|
|
342
329
|
const toolCalls = Array.from(toolCallsBuffer.values());
|
|
343
330
|
for (const toolCall of toolCalls) {
|
|
344
331
|
try {
|
|
345
|
-
// Validate JSON completeness
|
|
346
|
-
// Empty string should be treated as empty object
|
|
347
332
|
const args = toolCall.function.arguments.trim() || '{}';
|
|
348
333
|
JSON.parse(args);
|
|
349
|
-
// Update with normalized version
|
|
350
334
|
toolCall.function.arguments = args;
|
|
351
335
|
}
|
|
352
336
|
catch (e) {
|
|
@@ -359,25 +343,17 @@ export async function* createStreamingAnthropicCompletion(options, abortSignal)
|
|
|
359
343
|
tool_calls: toolCalls
|
|
360
344
|
};
|
|
361
345
|
}
|
|
362
|
-
// Yield usage information if available
|
|
363
346
|
if (usageData) {
|
|
364
347
|
yield {
|
|
365
348
|
type: 'usage',
|
|
366
349
|
usage: usageData
|
|
367
350
|
};
|
|
368
351
|
}
|
|
369
|
-
// Signal completion
|
|
370
352
|
yield {
|
|
371
353
|
type: 'done'
|
|
372
354
|
};
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
-
if (error instanceof Error) {
|
|
379
|
-
throw new Error(`Anthropic streaming completion failed: ${error.message}`);
|
|
380
|
-
}
|
|
381
|
-
throw new Error('Anthropic streaming completion failed: Unknown error');
|
|
382
|
-
}
|
|
355
|
+
}, {
|
|
356
|
+
abortSignal,
|
|
357
|
+
onRetry
|
|
358
|
+
});
|
|
383
359
|
}
|
package/dist/api/chat.d.ts
CHANGED
|
@@ -60,11 +60,11 @@ export declare function resetOpenAIClient(): void;
|
|
|
60
60
|
/**
|
|
61
61
|
* Create chat completion with automatic function calling support
|
|
62
62
|
*/
|
|
63
|
-
export declare function createChatCompletionWithTools(options: ChatCompletionOptions, maxToolRounds?: number): Promise<{
|
|
63
|
+
export declare function createChatCompletionWithTools(options: ChatCompletionOptions, maxToolRounds?: number, abortSignal?: AbortSignal, onRetry?: (error: Error, attempt: number, nextDelay: number) => void): Promise<{
|
|
64
64
|
content: string;
|
|
65
65
|
toolCalls: ToolCall[];
|
|
66
66
|
}>;
|
|
67
|
-
export declare function createChatCompletion(options: ChatCompletionOptions): Promise<string>;
|
|
67
|
+
export declare function createChatCompletion(options: ChatCompletionOptions, abortSignal?: AbortSignal, onRetry?: (error: Error, attempt: number, nextDelay: number) => void): Promise<string>;
|
|
68
68
|
export interface UsageInfo {
|
|
69
69
|
prompt_tokens: number;
|
|
70
70
|
completion_tokens: number;
|
|
@@ -74,7 +74,7 @@ export interface UsageInfo {
|
|
|
74
74
|
cached_tokens?: number;
|
|
75
75
|
}
|
|
76
76
|
export interface StreamChunk {
|
|
77
|
-
type: 'content' | 'tool_calls' | 'tool_call_delta' | 'reasoning_delta' | 'done' | 'usage';
|
|
77
|
+
type: 'content' | 'tool_calls' | 'tool_call_delta' | 'reasoning_delta' | 'reasoning_started' | 'done' | 'usage';
|
|
78
78
|
content?: string;
|
|
79
79
|
tool_calls?: Array<{
|
|
80
80
|
id: string;
|
|
@@ -91,5 +91,5 @@ export interface StreamChunk {
|
|
|
91
91
|
* Simple streaming chat completion - only handles OpenAI interaction
|
|
92
92
|
* Tool execution should be handled by the caller
|
|
93
93
|
*/
|
|
94
|
-
export declare function createStreamingChatCompletion(options: ChatCompletionOptions, abortSignal?: AbortSignal): AsyncGenerator<StreamChunk, void, unknown>;
|
|
94
|
+
export declare function createStreamingChatCompletion(options: ChatCompletionOptions, abortSignal?: AbortSignal, onRetry?: (error: Error, attempt: number, nextDelay: number) => void): AsyncGenerator<StreamChunk, void, unknown>;
|
|
95
95
|
export declare function validateChatOptions(options: ChatCompletionOptions): string[];
|
package/dist/api/chat.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import { getOpenAiConfig, getCustomSystemPrompt } from '../utils/apiConfig.js';
|
|
2
|
+
import { getOpenAiConfig, getCustomSystemPrompt, getCustomHeaders } from '../utils/apiConfig.js';
|
|
3
3
|
import { executeMCPTool } from '../utils/mcpToolsManager.js';
|
|
4
4
|
import { SYSTEM_PROMPT } from './systemPrompt.js';
|
|
5
|
+
import { withRetry, withRetryGenerator } from '../utils/retryUtils.js';
|
|
5
6
|
/**
|
|
6
7
|
* Convert our ChatMessage format to OpenAI's ChatCompletionMessageParam format
|
|
7
8
|
* Automatically prepends system prompt if not present
|
|
@@ -96,9 +97,14 @@ function getOpenAIClient() {
|
|
|
96
97
|
if (!config.apiKey || !config.baseUrl) {
|
|
97
98
|
throw new Error('OpenAI API configuration is incomplete. Please configure API settings first.');
|
|
98
99
|
}
|
|
100
|
+
// Get custom headers
|
|
101
|
+
const customHeaders = getCustomHeaders();
|
|
99
102
|
openaiClient = new OpenAI({
|
|
100
103
|
apiKey: config.apiKey,
|
|
101
104
|
baseURL: config.baseUrl,
|
|
105
|
+
defaultHeaders: {
|
|
106
|
+
...customHeaders
|
|
107
|
+
}
|
|
102
108
|
});
|
|
103
109
|
}
|
|
104
110
|
return openaiClient;
|
|
@@ -109,14 +115,14 @@ export function resetOpenAIClient() {
|
|
|
109
115
|
/**
|
|
110
116
|
* Create chat completion with automatic function calling support
|
|
111
117
|
*/
|
|
112
|
-
export async function createChatCompletionWithTools(options, maxToolRounds = 5) {
|
|
118
|
+
export async function createChatCompletionWithTools(options, maxToolRounds = 5, abortSignal, onRetry) {
|
|
113
119
|
const client = getOpenAIClient();
|
|
114
120
|
let messages = [...options.messages];
|
|
115
121
|
let allToolCalls = [];
|
|
116
122
|
let rounds = 0;
|
|
117
123
|
try {
|
|
118
124
|
while (rounds < maxToolRounds) {
|
|
119
|
-
const response = await client.chat.completions.create({
|
|
125
|
+
const response = await withRetry(() => client.chat.completions.create({
|
|
120
126
|
model: options.model,
|
|
121
127
|
messages: convertToOpenAIMessages(messages),
|
|
122
128
|
stream: false,
|
|
@@ -124,6 +130,9 @@ export async function createChatCompletionWithTools(options, maxToolRounds = 5)
|
|
|
124
130
|
max_tokens: options.max_tokens,
|
|
125
131
|
tools: options.tools,
|
|
126
132
|
tool_choice: options.tool_choice,
|
|
133
|
+
}), {
|
|
134
|
+
abortSignal,
|
|
135
|
+
onRetry
|
|
127
136
|
});
|
|
128
137
|
const message = response.choices[0]?.message;
|
|
129
138
|
if (!message) {
|
|
@@ -179,12 +188,12 @@ export async function createChatCompletionWithTools(options, maxToolRounds = 5)
|
|
|
179
188
|
throw new Error('Chat completion with tools failed: Unknown error');
|
|
180
189
|
}
|
|
181
190
|
}
|
|
182
|
-
export async function createChatCompletion(options) {
|
|
191
|
+
export async function createChatCompletion(options, abortSignal, onRetry) {
|
|
183
192
|
const client = getOpenAIClient();
|
|
184
193
|
let messages = [...options.messages];
|
|
185
194
|
try {
|
|
186
195
|
while (true) {
|
|
187
|
-
const response = await client.chat.completions.create({
|
|
196
|
+
const response = await withRetry(() => client.chat.completions.create({
|
|
188
197
|
model: options.model,
|
|
189
198
|
messages: convertToOpenAIMessages(messages),
|
|
190
199
|
stream: false,
|
|
@@ -192,6 +201,9 @@ export async function createChatCompletion(options) {
|
|
|
192
201
|
max_tokens: options.max_tokens,
|
|
193
202
|
tools: options.tools,
|
|
194
203
|
tool_choice: options.tool_choice,
|
|
204
|
+
}), {
|
|
205
|
+
abortSignal,
|
|
206
|
+
onRetry
|
|
195
207
|
});
|
|
196
208
|
const message = response.choices[0]?.message;
|
|
197
209
|
if (!message) {
|
|
@@ -246,9 +258,10 @@ export async function createChatCompletion(options) {
|
|
|
246
258
|
* Simple streaming chat completion - only handles OpenAI interaction
|
|
247
259
|
* Tool execution should be handled by the caller
|
|
248
260
|
*/
|
|
249
|
-
export async function* createStreamingChatCompletion(options, abortSignal) {
|
|
261
|
+
export async function* createStreamingChatCompletion(options, abortSignal, onRetry) {
|
|
250
262
|
const client = getOpenAIClient();
|
|
251
|
-
|
|
263
|
+
// 使用重试包装生成器
|
|
264
|
+
yield* withRetryGenerator(async function* () {
|
|
252
265
|
const stream = await client.chat.completions.create({
|
|
253
266
|
model: options.model,
|
|
254
267
|
messages: convertToOpenAIMessages(options.messages),
|
|
@@ -265,6 +278,7 @@ export async function* createStreamingChatCompletion(options, abortSignal) {
|
|
|
265
278
|
let toolCallsBuffer = {};
|
|
266
279
|
let hasToolCalls = false;
|
|
267
280
|
let usageData;
|
|
281
|
+
let reasoningStarted = false; // Track if reasoning has started
|
|
268
282
|
for await (const chunk of stream) {
|
|
269
283
|
if (abortSignal?.aborted) {
|
|
270
284
|
return;
|
|
@@ -298,6 +312,13 @@ export async function* createStreamingChatCompletion(options, abortSignal) {
|
|
|
298
312
|
// Note: reasoning_content is NOT included in the response, only counted for tokens
|
|
299
313
|
const reasoningContent = choice.delta?.reasoning_content;
|
|
300
314
|
if (reasoningContent) {
|
|
315
|
+
// Emit reasoning_started event on first reasoning content
|
|
316
|
+
if (!reasoningStarted) {
|
|
317
|
+
reasoningStarted = true;
|
|
318
|
+
yield {
|
|
319
|
+
type: 'reasoning_started'
|
|
320
|
+
};
|
|
321
|
+
}
|
|
301
322
|
yield {
|
|
302
323
|
type: 'reasoning_delta',
|
|
303
324
|
delta: reasoningContent
|
|
@@ -363,16 +384,10 @@ export async function* createStreamingChatCompletion(options, abortSignal) {
|
|
|
363
384
|
yield {
|
|
364
385
|
type: 'done'
|
|
365
386
|
};
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}
|
|
371
|
-
if (error instanceof Error) {
|
|
372
|
-
throw new Error(`Streaming chat completion failed: ${error.message}`);
|
|
373
|
-
}
|
|
374
|
-
throw new Error('Streaming chat completion failed: Unknown error');
|
|
375
|
-
}
|
|
387
|
+
}, {
|
|
388
|
+
abortSignal,
|
|
389
|
+
onRetry
|
|
390
|
+
});
|
|
376
391
|
}
|
|
377
392
|
export function validateChatOptions(options) {
|
|
378
393
|
const errors = [];
|
package/dist/api/gemini.d.ts
CHANGED
|
@@ -32,4 +32,4 @@ export declare function resetGeminiClient(): void;
|
|
|
32
32
|
/**
|
|
33
33
|
* Create streaming chat completion using Gemini API
|
|
34
34
|
*/
|
|
35
|
-
export declare function createStreamingGeminiCompletion(options: GeminiOptions, abortSignal?: AbortSignal): AsyncGenerator<GeminiStreamChunk, void, unknown>;
|
|
35
|
+
export declare function createStreamingGeminiCompletion(options: GeminiOptions, abortSignal?: AbortSignal, onRetry?: (error: Error, attempt: number, nextDelay: number) => void): AsyncGenerator<GeminiStreamChunk, void, unknown>;
|
package/dist/api/gemini.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { GoogleGenAI } from '@google/genai';
|
|
2
|
-
import { getOpenAiConfig, getCustomSystemPrompt } from '../utils/apiConfig.js';
|
|
2
|
+
import { getOpenAiConfig, getCustomSystemPrompt, getCustomHeaders } from '../utils/apiConfig.js';
|
|
3
3
|
import { SYSTEM_PROMPT } from './systemPrompt.js';
|
|
4
|
+
import { withRetryGenerator } from '../utils/retryUtils.js';
|
|
4
5
|
let geminiClient = null;
|
|
5
6
|
function getGeminiClient() {
|
|
6
7
|
if (!geminiClient) {
|
|
@@ -12,12 +13,23 @@ function getGeminiClient() {
|
|
|
12
13
|
const clientConfig = {
|
|
13
14
|
apiKey: config.apiKey
|
|
14
15
|
};
|
|
16
|
+
// Get custom headers
|
|
17
|
+
const customHeaders = getCustomHeaders();
|
|
15
18
|
// Support custom baseUrl and headers for proxy servers
|
|
16
19
|
if (config.baseUrl && config.baseUrl !== 'https://api.openai.com/v1') {
|
|
17
20
|
clientConfig.httpOptions = {
|
|
18
21
|
baseUrl: config.baseUrl,
|
|
19
22
|
headers: {
|
|
20
23
|
'x-goog-api-key': config.apiKey, // Gemini API requires this header
|
|
24
|
+
...customHeaders
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
else if (Object.keys(customHeaders).length > 0) {
|
|
29
|
+
// If using default base URL but have custom headers
|
|
30
|
+
clientConfig.httpOptions = {
|
|
31
|
+
headers: {
|
|
32
|
+
...customHeaders
|
|
21
33
|
}
|
|
22
34
|
};
|
|
23
35
|
}
|
|
@@ -200,9 +212,10 @@ function convertToGeminiMessages(messages) {
|
|
|
200
212
|
/**
|
|
201
213
|
* Create streaming chat completion using Gemini API
|
|
202
214
|
*/
|
|
203
|
-
export async function* createStreamingGeminiCompletion(options, abortSignal) {
|
|
215
|
+
export async function* createStreamingGeminiCompletion(options, abortSignal, onRetry) {
|
|
204
216
|
const client = getGeminiClient();
|
|
205
|
-
|
|
217
|
+
// 使用重试包装生成器
|
|
218
|
+
yield* withRetryGenerator(async function* () {
|
|
206
219
|
const { systemInstruction, contents } = convertToGeminiMessages(options.messages);
|
|
207
220
|
// Build request config
|
|
208
221
|
const requestConfig = {
|
|
@@ -297,14 +310,8 @@ export async function* createStreamingGeminiCompletion(options, abortSignal) {
|
|
|
297
310
|
yield {
|
|
298
311
|
type: 'done'
|
|
299
312
|
};
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
if (error instanceof Error) {
|
|
306
|
-
throw new Error(`Gemini streaming completion failed: ${error.message}`);
|
|
307
|
-
}
|
|
308
|
-
throw new Error('Gemini streaming completion failed: Unknown error');
|
|
309
|
-
}
|
|
313
|
+
}, {
|
|
314
|
+
abortSignal,
|
|
315
|
+
onRetry
|
|
316
|
+
});
|
|
310
317
|
}
|
package/dist/api/responses.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface UsageInfo {
|
|
|
31
31
|
cached_tokens?: number;
|
|
32
32
|
}
|
|
33
33
|
export interface ResponseStreamChunk {
|
|
34
|
-
type: 'content' | 'tool_calls' | 'tool_call_delta' | 'reasoning_delta' | 'done' | 'usage';
|
|
34
|
+
type: 'content' | 'tool_calls' | 'tool_call_delta' | 'reasoning_delta' | 'reasoning_started' | 'done' | 'usage';
|
|
35
35
|
content?: string;
|
|
36
36
|
tool_calls?: ToolCall[];
|
|
37
37
|
delta?: string;
|
|
@@ -39,17 +39,17 @@ export interface ResponseStreamChunk {
|
|
|
39
39
|
}
|
|
40
40
|
export declare function resetOpenAIClient(): void;
|
|
41
41
|
/**
|
|
42
|
-
* 使用 Responses API
|
|
42
|
+
* 使用 Responses API 创建响应(非流式,带自动工具调用)
|
|
43
43
|
*/
|
|
44
|
-
export declare function createResponse(options: ResponseOptions): Promise<string>;
|
|
44
|
+
export declare function createResponse(options: ResponseOptions, abortSignal?: AbortSignal, onRetry?: (error: Error, attempt: number, nextDelay: number) => void): Promise<string>;
|
|
45
45
|
/**
|
|
46
46
|
* 使用 Responses API 创建流式响应(带自动工具调用)
|
|
47
47
|
*/
|
|
48
|
-
export declare function createStreamingResponse(options: ResponseOptions, abortSignal?: AbortSignal): AsyncGenerator<ResponseStreamChunk, void, unknown>;
|
|
48
|
+
export declare function createStreamingResponse(options: ResponseOptions, abortSignal?: AbortSignal, onRetry?: (error: Error, attempt: number, nextDelay: number) => void): AsyncGenerator<ResponseStreamChunk, void, unknown>;
|
|
49
49
|
/**
|
|
50
50
|
* 使用 Responses API 创建响应(限制工具调用轮数)
|
|
51
51
|
*/
|
|
52
|
-
export declare function createResponseWithTools(options: ResponseOptions, maxToolRounds?: number): Promise<{
|
|
52
|
+
export declare function createResponseWithTools(options: ResponseOptions, maxToolRounds?: number, abortSignal?: AbortSignal, onRetry?: (error: Error, attempt: number, nextDelay: number) => void): Promise<{
|
|
53
53
|
content: string;
|
|
54
54
|
toolCalls: ToolCall[];
|
|
55
55
|
}>;
|