@huyooo/ai-chat-core 0.2.19 → 0.2.21
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/events.d.ts +452 -0
- package/dist/events.js +1 -0
- package/dist/index.d.ts +202 -550
- package/dist/index.js +1 -1
- package/package.json +23 -4
- package/src/agent.ts +399 -0
- package/src/constants.ts +125 -0
- package/src/events.ts +797 -0
- package/src/index.ts +309 -0
- package/src/internal/update-plan.ts +2 -0
- package/src/internal/web-search.ts +78 -0
- package/src/mcp/client-manager.ts +301 -0
- package/src/mcp/index.ts +2 -0
- package/src/mcp/types.ts +43 -0
- package/src/providers/context-compressor.ts +149 -0
- package/src/providers/index.ts +120 -0
- package/src/providers/model-registry.ts +320 -0
- package/src/providers/orchestrator.ts +761 -0
- package/src/providers/protocols/anthropic.ts +406 -0
- package/src/providers/protocols/ark.ts +362 -0
- package/src/providers/protocols/deepseek.ts +344 -0
- package/src/providers/protocols/error-utils.ts +74 -0
- package/src/providers/protocols/gemini.ts +350 -0
- package/src/providers/protocols/index.ts +36 -0
- package/src/providers/protocols/openai.ts +420 -0
- package/src/providers/protocols/qwen.ts +326 -0
- package/src/providers/protocols/types.ts +189 -0
- package/src/providers/types.ts +272 -0
- package/src/providers/unified-adapter.ts +367 -0
- package/src/router.ts +72 -0
- package/src/test-utils/mock-sse.ts +32 -0
- package/src/tools.ts +162 -0
- package/src/types.ts +531 -0
- package/src/utils.ts +86 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Qwen Protocol(通义千问 OpenAI 兼容模式 API)
|
|
3
|
+
*
|
|
4
|
+
* 使用 OpenAI 兼容端点以支持工具调用:
|
|
5
|
+
* https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
Protocol,
|
|
10
|
+
ProtocolConfig,
|
|
11
|
+
ProtocolMessage,
|
|
12
|
+
ProtocolToolDefinition,
|
|
13
|
+
ProtocolRequestOptions,
|
|
14
|
+
RawEvent,
|
|
15
|
+
RawToolCall,
|
|
16
|
+
} from './types';
|
|
17
|
+
import { DebugLogger } from '../../utils';
|
|
18
|
+
import { friendlyHttpError } from './error-utils';
|
|
19
|
+
|
|
20
|
+
const logger = DebugLogger.module('QwenProtocol');
|
|
21
|
+
|
|
22
|
+
// OpenAI 兼容模式端点
|
|
23
|
+
const DEFAULT_QWEN_COMPATIBLE_URL = 'https://dashscope.aliyuncs.com/compatible-mode/v1';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Qwen Protocol 实现(OpenAI 兼容模式)
|
|
27
|
+
*/
|
|
28
|
+
export class QwenProtocol implements Protocol {
|
|
29
|
+
readonly name = 'qwen';
|
|
30
|
+
|
|
31
|
+
private apiKey: string;
|
|
32
|
+
private apiUrl: string;
|
|
33
|
+
|
|
34
|
+
constructor(config: ProtocolConfig) {
|
|
35
|
+
this.apiKey = config.apiKey;
|
|
36
|
+
// 使用兼容模式端点
|
|
37
|
+
this.apiUrl = config.apiUrl ?? DEFAULT_QWEN_COMPATIBLE_URL;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 发送请求并返回原始事件流
|
|
42
|
+
*/
|
|
43
|
+
async *stream(
|
|
44
|
+
messages: ProtocolMessage[],
|
|
45
|
+
tools: ProtocolToolDefinition[],
|
|
46
|
+
options: ProtocolRequestOptions
|
|
47
|
+
): AsyncGenerator<RawEvent> {
|
|
48
|
+
const requestBody = this.buildRequestBody(messages, tools, options);
|
|
49
|
+
const url = `${this.apiUrl}/chat/completions`;
|
|
50
|
+
|
|
51
|
+
logger.debug('发送 Qwen 请求(兼容模式)', {
|
|
52
|
+
url,
|
|
53
|
+
model: options.model,
|
|
54
|
+
enableThinking: options.enableThinking,
|
|
55
|
+
toolsCount: tools.length,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const response = await fetch(url, {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: {
|
|
61
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
62
|
+
'Content-Type': 'application/json',
|
|
63
|
+
},
|
|
64
|
+
body: JSON.stringify(requestBody),
|
|
65
|
+
signal: options.signal,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
const errorText = await response.text();
|
|
70
|
+
logger.error('Qwen API 错误', { status: response.status, body: errorText.slice(0, 500) });
|
|
71
|
+
yield { type: 'error', error: friendlyHttpError(response.status, errorText, 'Qwen') };
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const reader = response.body?.getReader();
|
|
76
|
+
if (!reader) {
|
|
77
|
+
yield { type: 'error', error: '无法获取响应流' };
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
yield* this.parseSSE(reader, options.enableThinking);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 构建请求体(OpenAI 格式)
|
|
86
|
+
*/
|
|
87
|
+
private buildRequestBody(
|
|
88
|
+
messages: ProtocolMessage[],
|
|
89
|
+
tools: ProtocolToolDefinition[],
|
|
90
|
+
options: ProtocolRequestOptions
|
|
91
|
+
): Record<string, unknown> {
|
|
92
|
+
const convertedMessages = this.convertMessages(messages);
|
|
93
|
+
|
|
94
|
+
const body: Record<string, unknown> = {
|
|
95
|
+
model: options.model,
|
|
96
|
+
messages: convertedMessages,
|
|
97
|
+
stream: true,
|
|
98
|
+
// 请求返回 Token 使用统计(Chat Completions 格式)
|
|
99
|
+
stream_options: { include_usage: true },
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// 启用 thinking(Qwen 直接在请求体中设置)
|
|
103
|
+
if (options.enableThinking) {
|
|
104
|
+
body.enable_thinking = true;
|
|
105
|
+
body.thinking_budget = 38400;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 注意:Qwen 搜索通过 Tavily web_search 工具实现(searchStrategy: tavily)
|
|
109
|
+
// 不使用原生 enable_search 参数,保持与其他 Provider 一致
|
|
110
|
+
|
|
111
|
+
// 添加工具
|
|
112
|
+
if (tools.length > 0) {
|
|
113
|
+
body.tools = tools.map(t => ({
|
|
114
|
+
type: 'function',
|
|
115
|
+
function: {
|
|
116
|
+
name: t.name,
|
|
117
|
+
description: t.description,
|
|
118
|
+
parameters: t.parameters,
|
|
119
|
+
},
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return body;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 转换消息格式(OpenAI 标准格式)
|
|
128
|
+
*/
|
|
129
|
+
private convertMessages(messages: ProtocolMessage[]): unknown[] {
|
|
130
|
+
const result: unknown[] = [];
|
|
131
|
+
|
|
132
|
+
for (const msg of messages) {
|
|
133
|
+
switch (msg.role) {
|
|
134
|
+
case 'system':
|
|
135
|
+
result.push({ role: 'system', content: msg.content });
|
|
136
|
+
break;
|
|
137
|
+
|
|
138
|
+
case 'user': {
|
|
139
|
+
// 当只有图片没有文字时提供默认提示
|
|
140
|
+
const textContent = msg.content || (msg.images?.length ? '请分析这张图片' : '');
|
|
141
|
+
if (msg.images?.length) {
|
|
142
|
+
const content: unknown[] = [{ type: 'text', text: textContent }];
|
|
143
|
+
for (const img of msg.images) {
|
|
144
|
+
content.push({
|
|
145
|
+
type: 'image_url',
|
|
146
|
+
image_url: { url: img.startsWith('data:') ? img : `data:image/jpeg;base64,${img}` },
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
result.push({ role: 'user', content });
|
|
150
|
+
} else {
|
|
151
|
+
result.push({ role: 'user', content: textContent });
|
|
152
|
+
}
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
case 'assistant':
|
|
157
|
+
if (msg.toolCalls?.length) {
|
|
158
|
+
result.push({
|
|
159
|
+
role: 'assistant',
|
|
160
|
+
content: msg.content || null,
|
|
161
|
+
tool_calls: msg.toolCalls.map(tc => ({
|
|
162
|
+
id: tc.id,
|
|
163
|
+
type: 'function',
|
|
164
|
+
function: { name: tc.name, arguments: tc.arguments },
|
|
165
|
+
})),
|
|
166
|
+
});
|
|
167
|
+
} else {
|
|
168
|
+
result.push({ role: 'assistant', content: msg.content });
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
|
|
172
|
+
case 'tool':
|
|
173
|
+
result.push({
|
|
174
|
+
role: 'tool',
|
|
175
|
+
tool_call_id: msg.toolCallId,
|
|
176
|
+
content: msg.content,
|
|
177
|
+
});
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 解析 SSE 流(OpenAI 格式)
|
|
187
|
+
*/
|
|
188
|
+
private async *parseSSE(
|
|
189
|
+
reader: ReadableStreamDefaultReader<Uint8Array>,
|
|
190
|
+
enableThinking: boolean
|
|
191
|
+
): AsyncGenerator<RawEvent> {
|
|
192
|
+
const decoder = new TextDecoder();
|
|
193
|
+
let buffer = '';
|
|
194
|
+
const toolCallsMap = new Map<number, RawToolCall>();
|
|
195
|
+
let textStarted = false;
|
|
196
|
+
let thinkingDone = false;
|
|
197
|
+
// Token 使用统计
|
|
198
|
+
let lastUsage: { promptTokens: number; completionTokens: number; totalTokens: number } | undefined;
|
|
199
|
+
|
|
200
|
+
while (true) {
|
|
201
|
+
const { done, value } = await reader.read();
|
|
202
|
+
if (done) break;
|
|
203
|
+
|
|
204
|
+
buffer += decoder.decode(value, { stream: true });
|
|
205
|
+
const lines = buffer.split('\n');
|
|
206
|
+
buffer = lines.pop() || '';
|
|
207
|
+
|
|
208
|
+
for (const line of lines) {
|
|
209
|
+
if (!line.startsWith('data:')) continue;
|
|
210
|
+
|
|
211
|
+
const data = line.slice(5).trim();
|
|
212
|
+
if (!data || data === '[DONE]') {
|
|
213
|
+
if (data === '[DONE]') {
|
|
214
|
+
// 输出工具调用
|
|
215
|
+
const toolCalls = Array.from(toolCallsMap.values());
|
|
216
|
+
if (toolCalls.length > 0) {
|
|
217
|
+
for (const tc of toolCalls) {
|
|
218
|
+
yield { type: 'tool_call_done', toolCall: tc };
|
|
219
|
+
}
|
|
220
|
+
yield { type: 'done', finishReason: 'tool_calls', usage: lastUsage };
|
|
221
|
+
} else {
|
|
222
|
+
yield { type: 'done', finishReason: 'stop', usage: lastUsage };
|
|
223
|
+
}
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
const json = JSON.parse(data);
|
|
231
|
+
|
|
232
|
+
// 提取 Token 使用统计(Chat Completions 格式)
|
|
233
|
+
if (json.usage) {
|
|
234
|
+
lastUsage = {
|
|
235
|
+
promptTokens: json.usage.prompt_tokens || 0,
|
|
236
|
+
completionTokens: json.usage.completion_tokens || 0,
|
|
237
|
+
totalTokens: json.usage.total_tokens || (json.usage.prompt_tokens || 0) + (json.usage.completion_tokens || 0),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const choice = json.choices?.[0];
|
|
242
|
+
if (!choice) continue;
|
|
243
|
+
|
|
244
|
+
const delta = choice.delta;
|
|
245
|
+
if (!delta) continue;
|
|
246
|
+
|
|
247
|
+
// 处理 reasoning_content(Qwen 的思考输出)
|
|
248
|
+
if (enableThinking && delta.reasoning_content && !thinkingDone) {
|
|
249
|
+
yield { type: 'thinking_delta', delta: delta.reasoning_content };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// 处理文本
|
|
253
|
+
if (delta.content) {
|
|
254
|
+
// 第一次收到 content 时,结束 thinking
|
|
255
|
+
if (enableThinking && !textStarted && !thinkingDone) {
|
|
256
|
+
thinkingDone = true;
|
|
257
|
+
yield { type: 'thinking_done' };
|
|
258
|
+
}
|
|
259
|
+
textStarted = true;
|
|
260
|
+
yield { type: 'text_delta', delta: delta.content };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// 处理工具调用
|
|
264
|
+
if (delta.tool_calls?.length) {
|
|
265
|
+
for (const tc of delta.tool_calls) {
|
|
266
|
+
const index = tc.index ?? 0;
|
|
267
|
+
const existing = toolCallsMap.get(index);
|
|
268
|
+
|
|
269
|
+
if (existing) {
|
|
270
|
+
// 累积 arguments
|
|
271
|
+
if (tc.function?.arguments) {
|
|
272
|
+
existing.arguments += tc.function.arguments;
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
// 新工具调用
|
|
276
|
+
toolCallsMap.set(index, {
|
|
277
|
+
id: tc.id || `call_${index}`,
|
|
278
|
+
name: tc.function?.name || '',
|
|
279
|
+
arguments: tc.function?.arguments || '',
|
|
280
|
+
});
|
|
281
|
+
yield {
|
|
282
|
+
type: 'tool_call_start',
|
|
283
|
+
toolCall: { id: tc.id || `call_${index}`, name: tc.function?.name || '' }
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// 检查 finish_reason
|
|
290
|
+
if (choice.finish_reason) {
|
|
291
|
+
const toolCalls = Array.from(toolCallsMap.values());
|
|
292
|
+
if (toolCalls.length > 0) {
|
|
293
|
+
for (const tc of toolCalls) {
|
|
294
|
+
yield { type: 'tool_call_done', toolCall: tc };
|
|
295
|
+
}
|
|
296
|
+
yield { type: 'done', finishReason: 'tool_calls', usage: lastUsage };
|
|
297
|
+
} else {
|
|
298
|
+
yield { type: 'done', finishReason: choice.finish_reason, usage: lastUsage };
|
|
299
|
+
}
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
} catch {
|
|
303
|
+
// 忽略解析错误
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// 兜底
|
|
309
|
+
const toolCalls = Array.from(toolCallsMap.values());
|
|
310
|
+
if (toolCalls.length > 0) {
|
|
311
|
+
for (const tc of toolCalls) {
|
|
312
|
+
yield { type: 'tool_call_done', toolCall: tc };
|
|
313
|
+
}
|
|
314
|
+
yield { type: 'done', finishReason: 'tool_calls', usage: lastUsage };
|
|
315
|
+
} else {
|
|
316
|
+
yield { type: 'done', finishReason: 'stop', usage: lastUsage };
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* 创建 Qwen Protocol
|
|
323
|
+
*/
|
|
324
|
+
export function createQwenProtocol(config: ProtocolConfig): QwenProtocol {
|
|
325
|
+
return new QwenProtocol(config);
|
|
326
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protocol Layer 类型定义
|
|
3
|
+
*
|
|
4
|
+
* Protocol 只负责:
|
|
5
|
+
* 1. HTTP 请求发送
|
|
6
|
+
* 2. SSE 流解析
|
|
7
|
+
* 3. 认证处理
|
|
8
|
+
*
|
|
9
|
+
* 不负责:
|
|
10
|
+
* - thinking 格式转换(由 Family 处理)
|
|
11
|
+
* - 搜索行为差异(由 Family 处理)
|
|
12
|
+
* - 工具调用格式差异(由 Family 处理)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { ModelFamilyConfig } from '../model-registry';
|
|
16
|
+
|
|
17
|
+
// ==================== 原始事件类型 ====================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Protocol 原始事件类型
|
|
21
|
+
*
|
|
22
|
+
* 这是 Protocol 层产出的"原始"事件,
|
|
23
|
+
* 后续由 FamilyBehavior 转换为标准 StreamChunk
|
|
24
|
+
*/
|
|
25
|
+
export type RawEventType =
|
|
26
|
+
| 'text_delta' // 文本增量
|
|
27
|
+
| 'thinking_delta' // 思考内容增量
|
|
28
|
+
| 'thinking_done' // 思考完成
|
|
29
|
+
| 'tool_call_start' // 工具调用开始
|
|
30
|
+
| 'tool_call_delta' // 工具调用参数增量
|
|
31
|
+
| 'tool_call_done' // 工具调用完成
|
|
32
|
+
| 'search_query' // 搜索查询
|
|
33
|
+
| 'search_result' // 搜索结果
|
|
34
|
+
| 'done' // 流结束
|
|
35
|
+
| 'error'; // 错误
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 原始工具调用
|
|
39
|
+
*/
|
|
40
|
+
export interface RawToolCall {
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
arguments: string;
|
|
44
|
+
/** Gemini 需要的 thoughtSignature(用于工具调用循环) */
|
|
45
|
+
thoughtSignature?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Protocol 消息中的工具调用
|
|
50
|
+
*/
|
|
51
|
+
export interface ProtocolToolCall {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
arguments: string;
|
|
55
|
+
/** Gemini 需要的 thoughtSignature */
|
|
56
|
+
thoughtSignature?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 原始搜索结果
|
|
61
|
+
*/
|
|
62
|
+
export interface RawSearchResult {
|
|
63
|
+
title: string;
|
|
64
|
+
url: string;
|
|
65
|
+
snippet: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Protocol 原始事件
|
|
70
|
+
*/
|
|
71
|
+
/** Token 使用统计(Protocol 层) */
|
|
72
|
+
export interface RawTokenUsage {
|
|
73
|
+
promptTokens?: number;
|
|
74
|
+
completionTokens?: number;
|
|
75
|
+
totalTokens?: number;
|
|
76
|
+
reasoningTokens?: number;
|
|
77
|
+
cachedTokens?: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface RawEvent {
|
|
81
|
+
type: RawEventType;
|
|
82
|
+
|
|
83
|
+
// text_delta / thinking_delta
|
|
84
|
+
delta?: string;
|
|
85
|
+
|
|
86
|
+
// tool_call_*
|
|
87
|
+
toolCall?: Partial<RawToolCall>;
|
|
88
|
+
|
|
89
|
+
// search_*
|
|
90
|
+
searchQuery?: string;
|
|
91
|
+
searchResults?: RawSearchResult[];
|
|
92
|
+
|
|
93
|
+
// done
|
|
94
|
+
finishReason?: 'stop' | 'tool_calls' | 'length' | 'error';
|
|
95
|
+
/** Token 使用统计(done 事件携带) */
|
|
96
|
+
usage?: RawTokenUsage;
|
|
97
|
+
|
|
98
|
+
// error
|
|
99
|
+
error?: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ==================== Protocol 请求类型 ====================
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Protocol 请求消息(标准化格式)
|
|
106
|
+
*/
|
|
107
|
+
export interface ProtocolMessage {
|
|
108
|
+
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
109
|
+
content: string;
|
|
110
|
+
|
|
111
|
+
/** 图片列表(base64 或 URL) */
|
|
112
|
+
images?: string[];
|
|
113
|
+
|
|
114
|
+
/** 工具调用(assistant 消息) */
|
|
115
|
+
toolCalls?: ProtocolToolCall[];
|
|
116
|
+
|
|
117
|
+
/** 工具调用 ID(tool 消息) */
|
|
118
|
+
toolCallId?: string;
|
|
119
|
+
|
|
120
|
+
/** 工具名称(tool 消息,Gemini 需要) */
|
|
121
|
+
toolName?: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Protocol 工具定义(简化格式)
|
|
126
|
+
*/
|
|
127
|
+
import type { JsonSchemaObject } from '../../types';
|
|
128
|
+
|
|
129
|
+
export interface ProtocolToolDefinition {
|
|
130
|
+
name: string;
|
|
131
|
+
description: string;
|
|
132
|
+
parameters: JsonSchemaObject;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Protocol 请求选项
|
|
137
|
+
*/
|
|
138
|
+
export interface ProtocolRequestOptions {
|
|
139
|
+
/** 模型 ID */
|
|
140
|
+
model: string;
|
|
141
|
+
/** 模型家族配置 */
|
|
142
|
+
familyConfig: ModelFamilyConfig;
|
|
143
|
+
/** 是否启用 thinking */
|
|
144
|
+
enableThinking: boolean;
|
|
145
|
+
/** 是否启用搜索 */
|
|
146
|
+
enableSearch: boolean;
|
|
147
|
+
/** 中断信号 */
|
|
148
|
+
signal: AbortSignal;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ==================== Protocol 接口 ====================
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Protocol 配置
|
|
155
|
+
*/
|
|
156
|
+
export interface ProtocolConfig {
|
|
157
|
+
apiKey: string;
|
|
158
|
+
apiUrl?: string;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Protocol 接口
|
|
163
|
+
*
|
|
164
|
+
* 只负责 HTTP/SSE 通信,不处理任何业务逻辑
|
|
165
|
+
*/
|
|
166
|
+
export interface Protocol {
|
|
167
|
+
/** 协议名称 */
|
|
168
|
+
readonly name: string;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 发送请求并返回原始事件流
|
|
172
|
+
*
|
|
173
|
+
* @param messages 消息列表
|
|
174
|
+
* @param tools 工具定义
|
|
175
|
+
* @param options 请求选项
|
|
176
|
+
* @returns 原始事件流
|
|
177
|
+
*/
|
|
178
|
+
stream(
|
|
179
|
+
messages: ProtocolMessage[],
|
|
180
|
+
tools: ProtocolToolDefinition[],
|
|
181
|
+
options: ProtocolRequestOptions
|
|
182
|
+
): AsyncGenerator<RawEvent>;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Protocol 工厂函数类型
|
|
187
|
+
*/
|
|
188
|
+
export type ProtocolFactory = (config: ProtocolConfig) => Protocol;
|
|
189
|
+
|