@huyooo/ai-chat-core 0.2.13 → 0.2.14

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/index.js CHANGED
@@ -1,126 +1,175 @@
1
1
  import { GoogleGenAI } from '@google/genai';
2
+ import { createGateway, streamText, tool as tool$1 } from 'ai';
3
+ import { z } from 'zod';
2
4
 
3
5
  // src/agent.ts
4
6
 
5
- // src/constants.ts
6
- var DEFAULT_ARK_URL = "https://ark.cn-beijing.volces.com/api/v3";
7
- var DEFAULT_QWEN_URL = "https://dashscope.aliyuncs.com/api/v1";
8
- var DEFAULT_QWEN_NATIVE_URL = "https://dashscope.aliyuncs.com/api/v1";
9
- var DEFAULT_OPENROUTER_URL = "https://openrouter.ai/api/v1";
10
- var DOUBAO_SEED_1_8 = {
11
- id: "doubao-seed-1-8-251215"};
12
- var DOUBAO_SEED_1_6 = {
13
- id: "doubao-seed-1-6-250615",
14
- displayName: "\u8C46\u5305 Seed 1.6",
15
- provider: "\u8C46\u5305"
7
+ // src/providers/model-registry.ts
8
+ var DOUBAO_FAMILY = {
9
+ id: "doubao",
10
+ displayName: "\u8C46\u5305",
11
+ supportsVision: true,
12
+ supportsThinking: true,
13
+ thinkingFormat: "reasoning",
14
+ supportsNativeSearch: true,
15
+ searchStrategy: "native_web_search",
16
+ toolCallFormat: "responses",
17
+ defaultMaxTokens: 32768
16
18
  };
17
- var DOUBAO_SEED_1_6_LATEST = {
18
- id: "doubao-seed-1-6-251015"};
19
- var DOUBAO_SEED_1_6_FLASH = {
20
- id: "doubao-seed-1-6-flash-250617"};
21
- var DEEPSEEK_V3 = {
22
- id: "deepseek-v3-1-terminus",
23
- displayName: "DeepSeek V3",
24
- provider: "DeepSeek"
19
+ var DEEPSEEK_FAMILY = {
20
+ id: "deepseek",
21
+ displayName: "DeepSeek",
22
+ supportsVision: false,
23
+ supportsThinking: true,
24
+ thinkingFormat: "reasoning",
25
+ supportsNativeSearch: true,
26
+ searchStrategy: "native_web_search",
27
+ toolCallFormat: "responses",
28
+ defaultMaxTokens: 32768
25
29
  };
26
- var QWEN_MAX = { id: "qwen-max"};
27
- var QWEN_MAX_LATEST = { id: "qwen-max-latest"};
28
- var QWEN_PLUS = { id: "qwen-plus"};
29
- var QWEN_PLUS_LATEST = { id: "qwen-plus-latest"};
30
- var QWEN_TURBO = { id: "qwen-turbo"};
31
- var QWEN_TURBO_LATEST = { id: "qwen-turbo-latest"};
32
- var QWEN3_235B = { id: "qwen3-235b-a22b"};
33
- var QWEN3_MAX_PREVIEW = {
34
- id: "qwen3-max-preview",
35
- displayName: "\u901A\u4E49\u5343\u95EE 3 Max",
36
- provider: "\u901A\u4E49\u5343\u95EE"
37
- };
38
- var GEMINI_2_5_FLASH = { id: "gemini-2.5-flash-preview-05-20"};
39
- var GEMINI_2_5_PRO = { id: "gemini-2.5-pro-preview-05-06"};
40
- var GEMINI_2_0_FLASH = { id: "gemini-2.0-flash"};
41
- var GEMINI_2_0_FLASH_LITE = { id: "gemini-2.0-flash-lite"};
42
- var GEMINI_3_PRO = {
43
- id: "gemini-3-pro-preview",
44
- displayName: "Gemini 3 Pro",
45
- provider: "Gemini"
46
- };
47
- var GEMINI_IMAGE_MODEL = "gemini-2.0-flash";
48
- var GEMINI_IMAGE_GEN_MODEL = "gemini-3-pro-image-preview";
49
- var OR_GEMINI_2_5_FLASH = {
50
- id: "google/gemini-2.5-flash-preview"};
51
- var OR_GEMINI_2_5_PRO = {
52
- id: "google/gemini-2.5-pro-preview"};
53
- var OR_GEMINI_2_0_FLASH = {
54
- id: "google/gemini-2.0-flash-001"};
55
- var OR_GEMINI_3_PRO = {
56
- id: "google/gemini-3-pro-preview",
57
- displayName: "Gemini 3 Pro",
58
- isOpenRouter: true,
59
- provider: "Gemini"
30
+ var QWEN_FAMILY = {
31
+ id: "qwen",
32
+ displayName: "\u901A\u4E49\u5343\u95EE",
33
+ supportsVision: false,
34
+ supportsThinking: true,
35
+ thinkingFormat: "thinking_enabled",
36
+ supportsNativeSearch: false,
37
+ // 关闭原生搜索,使用 Tavily
38
+ searchStrategy: "tool_based",
39
+ toolCallFormat: "openai",
40
+ defaultMaxTokens: 32768
60
41
  };
61
- var OR_GPT_5_2 = {
62
- id: "openai/gpt-5.2",
63
- displayName: "GPT-5.2",
64
- isOpenRouter: true,
65
- provider: "OpenAI"
42
+ var GEMINI_FAMILY = {
43
+ id: "gemini",
44
+ displayName: "Gemini",
45
+ supportsVision: true,
46
+ supportsThinking: true,
47
+ thinkingFormat: "thought_signature",
48
+ supportsNativeSearch: false,
49
+ // googleSearch 与其他工具冲突,统一使用 Tavily
50
+ searchStrategy: "tool_based",
51
+ toolCallFormat: "gemini",
52
+ defaultMaxTokens: 65536,
53
+ requiresSpecialHandling: ["thought_signature"]
66
54
  };
67
- var OR_CLAUDE_OPUS_4_5 = {
68
- id: "anthropic/claude-opus-4.5",
69
- displayName: "Claude Opus 4.5",
70
- isOpenRouter: true,
71
- provider: "Anthropic"
55
+ var GPT_FAMILY = {
56
+ id: "gpt",
57
+ displayName: "GPT",
58
+ supportsVision: true,
59
+ supportsThinking: true,
60
+ // GPT-5.x 支持 reasoning
61
+ thinkingFormat: "reasoning",
62
+ supportsNativeSearch: false,
63
+ searchStrategy: "tool_based",
64
+ toolCallFormat: "openai",
65
+ defaultMaxTokens: 128e3
72
66
  };
73
- var OR_QWEN3_MAX = {
74
- id: "qwen/qwen3-max",
75
- displayName: "\u901A\u4E49\u5343\u95EE Max",
76
- isOpenRouter: true,
77
- provider: "\u901A\u4E49\u5343\u95EE"
67
+ var CLAUDE_FAMILY = {
68
+ id: "claude",
69
+ displayName: "Claude",
70
+ supportsVision: true,
71
+ supportsThinking: true,
72
+ // 通过 Vercel AI SDK 支持 extended thinking
73
+ thinkingFormat: "reasoning",
74
+ supportsNativeSearch: false,
75
+ searchStrategy: "tool_based",
76
+ toolCallFormat: "openai",
77
+ defaultMaxTokens: 2e5
78
78
  };
79
- var OR_DEEPSEEK_R1 = {
80
- id: "deepseek/deepseek-r1"};
81
- var OR_DEEPSEEK_CHAT_V3 = {
82
- id: "deepseek/deepseek-chat-v3-0324"};
83
- var OR_DEEPSEEK_V3_2 = {
84
- id: "deepseek/deepseek-v3.2",
85
- displayName: "DeepSeek V3.2",
86
- isOpenRouter: true,
87
- provider: "DeepSeek"
79
+ var MODEL_FAMILIES = {
80
+ doubao: DOUBAO_FAMILY,
81
+ deepseek: DEEPSEEK_FAMILY,
82
+ qwen: QWEN_FAMILY,
83
+ gemini: GEMINI_FAMILY,
84
+ gpt: GPT_FAMILY,
85
+ claude: CLAUDE_FAMILY
88
86
  };
89
- var DISPLAY_MODELS = [
90
- // 原生模型
91
- DOUBAO_SEED_1_6,
92
- DEEPSEEK_V3,
93
- QWEN3_MAX_PREVIEW,
94
- GEMINI_3_PRO,
95
- // OpenRouter 模型
96
- OR_GPT_5_2,
97
- OR_CLAUDE_OPUS_4_5,
98
- OR_GEMINI_3_PRO,
99
- OR_QWEN3_MAX,
100
- OR_DEEPSEEK_V3_2
87
+ var MODEL_REGISTRY = [
88
+ // 豆包(价格为输入<=32k档,输出价格取决于输出长度)
89
+ { id: "doubao-seed-1-6-250615", displayName: "\u8C46\u5305 Seed 1.6", family: "doubao", protocol: "ark", visible: true, supportsVision: true, contextWindow: "256K", pricing: ["\u8F93\u5165 0.8 \u5143/\u767E\u4E07tokens", "\u8F93\u51FA 2-8 \u5143/\u767E\u4E07tokens"] },
90
+ { id: "doubao-seed-1-8-251215", displayName: "\u8C46\u5305 Seed 1.8", family: "doubao", protocol: "ark", contextWindow: "256K", pricing: ["\u8F93\u5165 0.8 \u5143/\u767E\u4E07tokens", "\u8F93\u51FA 2-8 \u5143/\u767E\u4E07tokens"] },
91
+ // DeepSeek(价格为输入<=32k档)
92
+ { id: "deepseek-v3-2-251201", displayName: "DeepSeek V3.2", family: "deepseek", protocol: "deepseek", visible: true, supportsVision: false, contextWindow: "128K", pricing: ["\u8F93\u5165 2 \u5143/\u767E\u4E07tokens", "\u8F93\u51FA 3 \u5143/\u767E\u4E07tokens"] },
93
+ // 通义千问
94
+ { id: "qwen3-vl-plus", displayName: "\u901A\u4E49\u5343\u95EE 3 VL", family: "qwen", protocol: "qwen", visible: true, supportsVision: true, contextWindow: "128K", pricing: ["\u8F93\u5165 1 \u5143/\u767E\u4E07tokens", "\u8F93\u51FA 10 \u5143/\u767E\u4E07tokens"] },
95
+ // Gemini
96
+ { id: "gemini-3-pro-preview", displayName: "Gemini 3 Pro", family: "gemini", protocol: "gemini", visible: true, supportsVision: true, contextWindow: "1M", pricing: ["\u8F93\u5165 1.25 \u5143/\u767E\u4E07tokens", "\u8F93\u51FA 10 \u5143/\u767E\u4E07tokens"] },
97
+ { id: "gemini-2.5-flash-preview-05-20", displayName: "Gemini 2.5 Flash", family: "gemini", protocol: "gemini", contextWindow: "1M", pricing: ["\u8F93\u5165 0.15 \u5143/\u767E\u4E07tokens", "\u8F93\u51FA 0.6 \u5143/\u767E\u4E07tokens"] },
98
+ { id: "gemini-2.5-pro-preview-05-06", displayName: "Gemini 2.5 Pro", family: "gemini", protocol: "gemini", contextWindow: "1M", pricing: ["\u8F93\u5165 1.25 \u5143/\u767E\u4E07tokens", "\u8F93\u51FA 10 \u5143/\u767E\u4E07tokens"] },
99
+ // GPT(OpenRouter,美元价格按约7.2汇率换算)
100
+ { id: "openai/gpt-5.2", displayName: "GPT-5.2", family: "gpt", protocol: "openai", visible: true, supportsVision: true, contextWindow: "400K", pricing: ["\u8F93\u5165 12.6 \u5143/\u767E\u4E07tokens", "\u8F93\u51FA 100.8 \u5143/\u767E\u4E07tokens"] },
101
+ // Claude(Vercel AI SDK,美元价格按约7.2汇率换算)
102
+ { id: "anthropic/claude-opus-4.5", displayName: "Claude Opus 4.5", family: "claude", protocol: "anthropic", visible: true, supportsVision: true, contextWindow: "200K", pricing: ["\u8F93\u5165 36 \u5143/\u767E\u4E07tokens", "\u8F93\u51FA 180 \u5143/\u767E\u4E07tokens"] }
101
103
  ];
102
- var DEFAULT_MODEL = DOUBAO_SEED_1_6.id;
103
- var PLAN_MODE_PROMPT = "\u5206\u6790\u9700\u6C42\u5E76\u5236\u5B9A\u6267\u884C\u8BA1\u5212\uFF0C\u4EE5 Markdown \u683C\u5F0F\u8F93\u51FA\u6B65\u9AA4\uFF0C\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u540E\u518D\u6267\u884C\u3002";
104
- var AGENT_MODE_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684 AI \u52A9\u624B\u3002\u8BF7\u9075\u5FAA\u4EE5\u4E0B\u89C4\u5219\uFF1A
105
- - \u8FD4\u56DE\u7684\u5185\u5BB9\u5C3D\u91CF\u4E0D\u8981\u4F7F\u7528\u8868\u60C5\u7B26\u53F7\uFF08emoji\uFF09\uFF0C\u9664\u975E\u7528\u6237\u8981\u6C42\u4F7F\u7528
106
- - \u4F7F\u7528 Markdown \u683C\u5F0F\u7EC4\u7EC7\u5185\u5BB9
107
- - \u4EE3\u7801\u5757\u4F7F\u7528\u6B63\u786E\u7684\u8BED\u8A00\u6807\u8BC6`;
104
+ function getModelEntry(modelId) {
105
+ return MODEL_REGISTRY.find((m) => m.id === modelId || modelId.includes(m.id));
106
+ }
107
+ function getModelFamily(modelId) {
108
+ const entry = getModelEntry(modelId);
109
+ if (!entry) return void 0;
110
+ return MODEL_FAMILIES[entry.family];
111
+ }
112
+ function getModelProtocol(modelId) {
113
+ const entry = getModelEntry(modelId);
114
+ return entry?.protocol;
115
+ }
116
+ function getVisibleModels() {
117
+ return MODEL_REGISTRY.filter((m) => m.visible);
118
+ }
119
+ function getModelsByFamily(familyId) {
120
+ return MODEL_REGISTRY.filter((m) => m.family === familyId);
121
+ }
122
+ function getModelsByProtocol(protocol) {
123
+ return MODEL_REGISTRY.filter((m) => m.protocol === protocol);
124
+ }
125
+ function modelSupportsThinking(modelId) {
126
+ const family = getModelFamily(modelId);
127
+ return family?.supportsThinking ?? false;
128
+ }
129
+ function modelSupportsNativeSearch(modelId) {
130
+ const family = getModelFamily(modelId);
131
+ return family?.supportsNativeSearch ?? false;
132
+ }
133
+ function modelSupportsVision(modelId) {
134
+ const entry = getModelEntry(modelId);
135
+ if (!entry) return false;
136
+ if (typeof entry.supportsVision === "boolean") return entry.supportsVision;
137
+ const family = getModelFamily(modelId);
138
+ return family?.supportsVision ?? false;
139
+ }
140
+ function getModelSearchStrategy(modelId) {
141
+ const family = getModelFamily(modelId);
142
+ return family?.searchStrategy ?? "tool_based";
143
+ }
108
144
 
109
145
  // src/types.ts
110
- var MODELS = DISPLAY_MODELS.map((m) => ({
111
- modelId: m.id,
112
- displayName: m.displayName,
113
- group: m.isOpenRouter ? "OpenRouter" : "\u539F\u751F\u6A21\u578B",
114
- // 后端决定分组
115
- isOpenRouter: m.isOpenRouter,
116
- provider: m.provider
117
- }));
118
- function getNativeModels() {
119
- return MODELS.filter((m) => !m.isOpenRouter);
120
- }
121
- function getOpenRouterModels() {
122
- return MODELS.filter((m) => m.isOpenRouter);
123
- }
146
+ function buildModelFeatures(entry) {
147
+ const family = getModelFamily(entry.id);
148
+ const supportsVision = modelSupportsVision(entry.id);
149
+ const supportsThinking = family?.supportsThinking ?? false;
150
+ const supportsNativeSearch = family?.supportsNativeSearch ?? false;
151
+ const features = [];
152
+ if (supportsVision) features.push("\u591A\u6A21\u6001");
153
+ if (supportsThinking) features.push("\u6DF1\u5EA6\u601D\u8003");
154
+ if (entry.contextWindow) features.push(`\u957F\u4E0A\u4E0B\u6587\uFF08${entry.contextWindow}\uFF09`);
155
+ if (supportsNativeSearch) features.push("\u8054\u7F51\u641C\u7D22");
156
+ return features;
157
+ }
158
+ var MODELS = getVisibleModels().map((entry) => {
159
+ const family = getModelFamily(entry.id);
160
+ const supportsThinking = family?.supportsThinking ?? false;
161
+ const supportsVision = modelSupportsVision(entry.id);
162
+ return {
163
+ modelId: entry.id,
164
+ displayName: entry.displayName,
165
+ supportsThinking,
166
+ supportsVision,
167
+ tooltip: {
168
+ features: buildModelFeatures(entry),
169
+ cost: entry.pricing
170
+ }
171
+ };
172
+ });
124
173
  function getModelByModelId(modelId) {
125
174
  return MODELS.find((m) => m.modelId === modelId);
126
175
  }
@@ -201,7 +250,7 @@ function createToolCallStart(id, name, args) {
201
250
  data: { id, name, args, startedAt: Date.now() }
202
251
  };
203
252
  }
204
- function createToolCallResult(id, name, result, success, startedAt, error, sideEffects) {
253
+ function createToolCallResult(id, name, result, success, startedAt, error, sideEffects, resultType) {
205
254
  const endedAt = Date.now();
206
255
  return {
207
256
  type: "tool_call_result",
@@ -213,7 +262,20 @@ function createToolCallResult(id, name, result, success, startedAt, error, sideE
213
262
  error,
214
263
  endedAt,
215
264
  duration: endedAt - startedAt,
216
- sideEffects
265
+ sideEffects,
266
+ resultType
267
+ }
268
+ };
269
+ }
270
+ function createToolCallOutput(id, name, stream, chunk) {
271
+ return {
272
+ type: "tool_call_output",
273
+ data: {
274
+ id,
275
+ name,
276
+ stream,
277
+ chunk,
278
+ at: Date.now()
217
279
  }
218
280
  };
219
281
  }
@@ -381,7 +443,7 @@ function isDangerousCommand(command) {
381
443
  }
382
444
  function createDefaultToolExecutor(defaultCwd = process.cwd()) {
383
445
  return {
384
- async executeCommand(command, cwd, signal) {
446
+ async executeCommand(command, cwd, signal, hooks) {
385
447
  if (signal?.aborted) {
386
448
  return { success: false, error: "\u64CD\u4F5C\u5DF2\u53D6\u6D88" };
387
449
  }
@@ -394,35 +456,70 @@ function createDefaultToolExecutor(defaultCwd = process.cwd()) {
394
456
  let stdout = "";
395
457
  let stderr = "";
396
458
  let killed = false;
459
+ let killTimer = null;
397
460
  const child = spawn("sh", ["-c", command], {
398
461
  cwd: cwd || defaultCwd,
399
- env: process.env
462
+ env: process.env,
463
+ // 关键:让 sh 及其子进程处于同一进程组,便于取消/超时时“一锅端”
464
+ // macOS/Linux: 可用负 PID kill 进程组;Windows 会忽略 detached 的语义
465
+ detached: process.platform !== "win32"
400
466
  });
401
- const timeout = setTimeout(() => {
402
- if (!killed) {
403
- killed = true;
404
- child.kill("SIGTERM");
405
- resolve({ success: false, error: "\u547D\u4EE4\u6267\u884C\u8D85\u65F6\uFF0830\u79D2\uFF09" });
467
+ const killProcessTree = (signalName) => {
468
+ if (killed) return;
469
+ killed = true;
470
+ try {
471
+ if (process.platform !== "win32" && typeof child.pid === "number") {
472
+ process.kill(-child.pid, signalName);
473
+ } else {
474
+ child.kill(signalName);
475
+ }
476
+ } catch {
477
+ try {
478
+ child.kill(signalName);
479
+ } catch {
480
+ }
406
481
  }
482
+ if (killTimer) clearTimeout(killTimer);
483
+ killTimer = setTimeout(() => {
484
+ try {
485
+ if (process.platform !== "win32" && typeof child.pid === "number") {
486
+ process.kill(-child.pid, "SIGKILL");
487
+ } else {
488
+ child.kill("SIGKILL");
489
+ }
490
+ } catch {
491
+ }
492
+ }, 1500);
493
+ };
494
+ const timeout = setTimeout(() => {
495
+ if (killed) return;
496
+ killProcessTree("SIGTERM");
497
+ resolve({ success: false, error: "\u547D\u4EE4\u6267\u884C\u8D85\u65F6\uFF0830\u79D2\uFF09" });
407
498
  }, 3e4);
408
499
  const abortHandler = () => {
409
- if (!killed) {
410
- killed = true;
411
- child.kill("SIGTERM");
412
- clearTimeout(timeout);
413
- resolve({ success: false, error: "\u64CD\u4F5C\u5DF2\u53D6\u6D88" });
414
- }
500
+ if (killed) return;
501
+ killProcessTree("SIGTERM");
502
+ clearTimeout(timeout);
503
+ resolve({ success: false, error: "\u64CD\u4F5C\u5DF2\u53D6\u6D88" });
415
504
  };
416
505
  signal?.addEventListener("abort", abortHandler);
417
506
  child.stdout?.on("data", (data) => {
418
- stdout += data.toString();
507
+ const chunk = data.toString();
508
+ stdout += chunk;
509
+ hooks?.onStdout?.(chunk);
419
510
  });
420
511
  child.stderr?.on("data", (data) => {
421
- stderr += data.toString();
512
+ const chunk = data.toString();
513
+ stderr += chunk;
514
+ hooks?.onStderr?.(chunk);
422
515
  });
423
516
  child.on("close", (code) => {
424
517
  clearTimeout(timeout);
425
518
  signal?.removeEventListener("abort", abortHandler);
519
+ if (killTimer) {
520
+ clearTimeout(killTimer);
521
+ killTimer = null;
522
+ }
426
523
  if (killed) return;
427
524
  const output = stdout.trim();
428
525
  const errOutput = stderr.trim();
@@ -443,6 +540,10 @@ function createDefaultToolExecutor(defaultCwd = process.cwd()) {
443
540
  child.on("error", (err) => {
444
541
  clearTimeout(timeout);
445
542
  signal?.removeEventListener("abort", abortHandler);
543
+ if (killTimer) {
544
+ clearTimeout(killTimer);
545
+ killTimer = null;
546
+ }
446
547
  if (!killed) {
447
548
  resolve({ success: false, error: err.message });
448
549
  }
@@ -455,95 +556,40 @@ function createDefaultToolExecutor(defaultCwd = process.cwd()) {
455
556
  };
456
557
  }
457
558
 
559
+ // src/constants.ts
560
+ var DEFAULT_ARK_URL = "https://ark.cn-beijing.volces.com/api/v3";
561
+ var DEFAULT_QWEN_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1";
562
+ var DEFAULT_OPENROUTER_URL = "https://openrouter.ai/api/v1";
563
+ var DOUBAO_SEED_1_6 = {
564
+ id: "doubao-seed-1-6-250615"};
565
+ var GEMINI_IMAGE_MODEL = "gemini-2.0-flash";
566
+ var GEMINI_IMAGE_GEN_MODEL = "gemini-3-pro-image-preview";
567
+ var DEFAULT_MODEL = DOUBAO_SEED_1_6.id;
568
+ var AGENT_MODE_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684 AI \u52A9\u624B\u3002\u8BF7\u9075\u5FAA\u4EE5\u4E0B\u89C4\u5219\uFF1A
569
+ - \u8FD4\u56DE\u7684\u5185\u5BB9\u5C3D\u91CF\u4E0D\u8981\u4F7F\u7528\u8868\u60C5\u7B26\u53F7\uFF08emoji\uFF09\uFF0C\u9664\u975E\u7528\u6237\u8981\u6C42\u4F7F\u7528
570
+ - \u4F7F\u7528 Markdown \u683C\u5F0F\u7EC4\u7EC7\u5185\u5BB9
571
+ - \u4EE3\u7801\u5757\u4F7F\u7528\u6B63\u786E\u7684\u8BED\u8A00\u6807\u8BC6`;
572
+
458
573
  // src/router.ts
459
- var MODEL_ROUTES = [
460
- // === 高优先级:OpenRouter 格式(包含 /)===
461
- // 所有包含 / 的模型名都路由到 OpenRouter
462
- // 例如:qwen/qwen3-max, anthropic/claude-opus-4.5, openai/gpt-5.2
463
- {
464
- type: "contains",
465
- pattern: "/",
466
- provider: "openrouter",
467
- priority: 100,
468
- description: "OpenRouter \u683C\u5F0F\u6A21\u578B\uFF08vendor/model\uFF09"
469
- },
470
- // === 中优先级:原生 Provider 前缀匹配 ===
471
- {
472
- type: "prefix",
473
- pattern: "qwen",
474
- provider: "qwen",
475
- priority: 50,
476
- description: "Qwen (DashScope) \u539F\u751F\u6A21\u578B"
477
- },
478
- {
479
- type: "prefix",
480
- pattern: "gemini",
481
- provider: "gemini",
482
- priority: 50,
483
- description: "Gemini (Google AI) \u539F\u751F\u6A21\u578B"
484
- },
485
- // === 低优先级:ARK 火山引擎模型 ===
486
- {
487
- type: "prefix",
488
- pattern: "doubao",
489
- provider: "ark",
490
- priority: 30,
491
- description: "\u8C46\u5305\u6A21\u578B\uFF08\u706B\u5C71\u5F15\u64CE\uFF09"
492
- },
493
- {
494
- type: "prefix",
495
- pattern: "deepseek",
496
- provider: "ark",
497
- priority: 30,
498
- description: "DeepSeek \u6A21\u578B\uFF08\u706B\u5C71\u5F15\u64CE\uFF09"
499
- }
500
- ];
501
574
  var DEFAULT_PROVIDER = "ark";
502
- var sortedRulesCache = null;
503
- function getSortedRules() {
504
- if (!sortedRulesCache) {
505
- sortedRulesCache = [...MODEL_ROUTES].sort((a, b) => b.priority - a.priority);
506
- }
507
- return sortedRulesCache;
508
- }
509
- function matchRule(model, rule) {
510
- const lowerPattern = rule.pattern.toLowerCase();
511
- switch (rule.type) {
512
- case "exact":
513
- return model === lowerPattern;
514
- case "prefix":
515
- return model.startsWith(lowerPattern);
516
- case "contains":
517
- return model.includes(lowerPattern);
518
- case "regex":
519
- return new RegExp(rule.pattern, "i").test(model);
520
- default:
521
- return false;
522
- }
523
- }
524
575
  function routeModelToProvider(model) {
525
576
  return routeModelWithDetails(model).provider;
526
577
  }
527
578
  function routeModelWithDetails(model) {
528
- const lowerModel = model.toLowerCase();
529
- const sortedRules = getSortedRules();
530
- for (const rule of sortedRules) {
531
- if (matchRule(lowerModel, rule)) {
532
- return {
533
- provider: rule.provider,
534
- matchedRule: rule,
535
- isDefault: false
536
- };
537
- }
579
+ const registryEntry = getModelEntry(model);
580
+ if (registryEntry) {
581
+ return {
582
+ provider: registryEntry.protocol,
583
+ registryEntry,
584
+ familyConfig: getModelFamily(model),
585
+ isDefault: false
586
+ };
538
587
  }
539
588
  return {
540
589
  provider: DEFAULT_PROVIDER,
541
590
  isDefault: true
542
591
  };
543
592
  }
544
- function getRouteRules() {
545
- return MODEL_ROUTES;
546
- }
547
593
  function getDefaultProvider() {
548
594
  return DEFAULT_PROVIDER;
549
595
  }
@@ -551,7 +597,199 @@ function isModelForProvider(model, provider) {
551
597
  return routeModelToProvider(model) === provider;
552
598
  }
553
599
 
600
+ // src/utils/debug-logger.ts
601
+ var debugEnabled = false;
602
+ var logFilePath = null;
603
+ var logBuffer = [];
604
+ var flushTimer = null;
605
+ var fsModule = null;
606
+ var fsLoadAttempted = false;
607
+ function getFs() {
608
+ if (fsModule !== null) return fsModule;
609
+ if (fsLoadAttempted) return null;
610
+ fsLoadAttempted = true;
611
+ try {
612
+ if (typeof globalThis.require === "function") {
613
+ fsModule = globalThis.require("fs");
614
+ return fsModule;
615
+ }
616
+ const proc = process;
617
+ if (typeof proc !== "undefined" && proc.mainModule?.require) {
618
+ fsModule = proc.mainModule.require("fs");
619
+ return fsModule;
620
+ }
621
+ try {
622
+ const dynamicRequire = new Function("moduleName", "return require(moduleName)");
623
+ fsModule = dynamicRequire("fs");
624
+ return fsModule;
625
+ } catch {
626
+ }
627
+ return null;
628
+ } catch {
629
+ return null;
630
+ }
631
+ }
632
+ function flushToFile() {
633
+ if (!logFilePath || logBuffer.length === 0) return;
634
+ const fs = getFs();
635
+ if (!fs) return;
636
+ try {
637
+ const lines = logBuffer.map((entry) => JSON.stringify(entry)).join("\n") + "\n";
638
+ fs.appendFileSync(logFilePath, lines, "utf-8");
639
+ logBuffer = [];
640
+ } catch (err) {
641
+ console.error("[DebugLogger] \u5199\u5165\u65E5\u5FD7\u6587\u4EF6\u5931\u8D25:", err);
642
+ }
643
+ }
644
+ function scheduleFlush() {
645
+ if (flushTimer) return;
646
+ flushTimer = setTimeout(() => {
647
+ flushTimer = null;
648
+ flushToFile();
649
+ }, 100);
650
+ }
651
+ function createEntry(level, module, message, data) {
652
+ return {
653
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
654
+ level,
655
+ module,
656
+ message,
657
+ ...data !== void 0 ? { data } : {}
658
+ };
659
+ }
660
+ function log(level, module, message, data) {
661
+ if (!debugEnabled) return;
662
+ const entry = createEntry(level, module, message, data);
663
+ const prefix = `[${entry.timestamp.slice(11, 23)}] [${module}]`;
664
+ const consoleMethod = level === "error" ? console.error : level === "warn" ? console.warn : console.log;
665
+ if (data !== void 0) {
666
+ consoleMethod(`${prefix} ${message}`, typeof data === "object" ? JSON.stringify(data) : data);
667
+ } else {
668
+ consoleMethod(`${prefix} ${message}`);
669
+ }
670
+ if (logFilePath) {
671
+ logBuffer.push(entry);
672
+ scheduleFlush();
673
+ }
674
+ }
675
+ var DebugLogger = {
676
+ /**
677
+ * 启用/禁用调试日志
678
+ */
679
+ enable(enabled = true) {
680
+ debugEnabled = enabled;
681
+ },
682
+ /**
683
+ * 检查是否启用
684
+ */
685
+ isEnabled() {
686
+ return debugEnabled;
687
+ },
688
+ /**
689
+ * 直接设置 fs 模块(用于 Electron 主进程等打包环境)
690
+ */
691
+ setFsModule(fs) {
692
+ fsModule = fs;
693
+ },
694
+ /**
695
+ * 设置日志文件路径(仅 Node.js 环境有效)
696
+ * @param path 日志文件路径,null 表示禁用文件输出
697
+ */
698
+ setLogFile(path) {
699
+ flushToFile();
700
+ logFilePath = path;
701
+ if (path) {
702
+ const fs = getFs();
703
+ if (fs) {
704
+ try {
705
+ const header = {
706
+ type: "session_start",
707
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
708
+ version: "1.0"
709
+ };
710
+ fs.writeFileSync(path, JSON.stringify(header) + "\n", "utf-8");
711
+ console.log("[DebugLogger] \u65E5\u5FD7\u6587\u4EF6\u521B\u5EFA\u6210\u529F:", path);
712
+ } catch (err) {
713
+ console.error("[DebugLogger] \u521B\u5EFA\u65E5\u5FD7\u6587\u4EF6\u5931\u8D25:", err);
714
+ logFilePath = null;
715
+ }
716
+ } else {
717
+ console.warn("[DebugLogger] \u65E0\u6CD5\u52A0\u8F7D fs \u6A21\u5757\uFF0C\u6587\u4EF6\u65E5\u5FD7\u4E0D\u53EF\u7528");
718
+ logFilePath = null;
719
+ }
720
+ }
721
+ },
722
+ /**
723
+ * 获取日志文件路径
724
+ */
725
+ getLogFile() {
726
+ return logFilePath;
727
+ },
728
+ /**
729
+ * 立即刷新日志到文件
730
+ */
731
+ flush() {
732
+ if (flushTimer) {
733
+ clearTimeout(flushTimer);
734
+ flushTimer = null;
735
+ }
736
+ flushToFile();
737
+ },
738
+ /**
739
+ * 创建模块专用的 logger
740
+ */
741
+ module(moduleName) {
742
+ return {
743
+ debug: (message, data) => log("debug", moduleName, message, data),
744
+ info: (message, data) => log("info", moduleName, message, data),
745
+ warn: (message, data) => log("warn", moduleName, message, data),
746
+ error: (message, data) => log("error", moduleName, message, data)
747
+ };
748
+ },
749
+ // 快捷方法
750
+ debug: (module, message, data) => log("debug", module, message, data),
751
+ info: (module, message, data) => log("info", module, message, data),
752
+ warn: (module, message, data) => log("warn", module, message, data),
753
+ error: (module, message, data) => log("error", module, message, data)
754
+ };
755
+ try {
756
+ if (typeof process !== "undefined" && process.env) {
757
+ if (process.env.AI_CHAT_DEBUG === "true") {
758
+ debugEnabled = true;
759
+ }
760
+ if (process.env.AI_CHAT_DEBUG_FILE) {
761
+ DebugLogger.setLogFile(process.env.AI_CHAT_DEBUG_FILE);
762
+ }
763
+ }
764
+ } catch {
765
+ }
766
+
554
767
  // src/providers/orchestrator.ts
768
+ var logger = DebugLogger.module("Orchestrator");
769
+ function normalizeSearchResults(items) {
770
+ const byUrl = /* @__PURE__ */ new Map();
771
+ for (const it of items) {
772
+ const url = String(it?.url ?? "").trim();
773
+ if (!url) continue;
774
+ const snippet = String(it?.snippet ?? "").trim();
775
+ let title = String(it?.title ?? "").trim();
776
+ if (!title) {
777
+ try {
778
+ title = new URL(url).hostname;
779
+ } catch {
780
+ title = url;
781
+ }
782
+ }
783
+ const prev = byUrl.get(url);
784
+ if (!prev) {
785
+ byUrl.set(url, { title, url, snippet });
786
+ continue;
787
+ }
788
+ if (!prev.snippet && snippet) prev.snippet = snippet;
789
+ if (!prev.title && title) prev.title = title;
790
+ }
791
+ return Array.from(byUrl.values());
792
+ }
555
793
  var DEFAULT_MAX_ITERATIONS = 10;
556
794
  var ChatOrchestrator = class {
557
795
  config;
@@ -575,37 +813,49 @@ var ChatOrchestrator = class {
575
813
  const messages = this.buildMessages(context, message);
576
814
  let iterations = 0;
577
815
  let finalText = "";
578
- let thinkingText = "";
579
- let thinkingStartedAt = 0;
580
- let thinkingComplete = !options.enableThinking;
581
816
  let searchResults = [];
582
817
  let searchStartedAt = 0;
583
818
  let searchStarted = false;
584
- if (options.enableThinking) {
585
- thinkingStartedAt = Date.now();
586
- yield createThinkingStart();
587
- }
819
+ let searchEnded = false;
588
820
  while (iterations < maxIterations) {
589
821
  if (context.signal.aborted) {
590
822
  yield createAbort("\u8BF7\u6C42\u5DF2\u53D6\u6D88");
591
823
  return;
592
824
  }
593
825
  iterations++;
826
+ logger.info(`======= \u7B2C ${iterations} \u8F6E\u5F00\u59CB =======`);
827
+ let thinkingText = "";
828
+ let thinkingStartedAt = 0;
829
+ let thinkingStarted = false;
830
+ let thinkingComplete = false;
831
+ const chunkCounts = {};
832
+ let textStartedInOrchestrator = false;
833
+ const logChunk = (type) => {
834
+ chunkCounts[type] = (chunkCounts[type] || 0) + 1;
835
+ };
594
836
  try {
595
837
  const pendingToolCalls = [];
596
838
  let hasToolCalls = false;
839
+ const enableThinkingThisRound = options.enableThinking === true;
840
+ const enableSearchThisRound = options.enableSearch === true;
841
+ logger.debug("\u8C03\u7528 adapter.streamOnce", { enableThinking: enableThinkingThisRound, enableSearch: enableSearchThisRound });
597
842
  const stream = adapter.streamOnce(messages, context.tools, {
598
843
  model: options.model,
599
- enableThinking: options.enableThinking && iterations === 1,
600
- // 只在第一轮启用思考
601
- enableSearch: options.enableSearch && iterations === 1,
844
+ enableThinking: enableThinkingThisRound,
845
+ enableSearch: enableSearchThisRound,
602
846
  signal: context.signal
603
847
  });
604
848
  for await (const chunk of stream) {
849
+ logChunk(chunk.type);
605
850
  switch (chunk.type) {
606
851
  case "text":
607
852
  if (chunk.text) {
608
- if (!thinkingComplete) {
853
+ if (!textStartedInOrchestrator) {
854
+ textStartedInOrchestrator = true;
855
+ logger.debug("\u9996\u6B21\u6536\u5230 text", { thinkingStarted, thinkingComplete });
856
+ }
857
+ if (enableThinkingThisRound && thinkingStarted && !thinkingComplete) {
858
+ logger.debug("text \u89E6\u53D1 thinking_end");
609
859
  thinkingComplete = true;
610
860
  yield createThinkingEnd(thinkingStartedAt);
611
861
  }
@@ -614,45 +864,80 @@ var ChatOrchestrator = class {
614
864
  }
615
865
  break;
616
866
  case "thinking":
867
+ if (!enableThinkingThisRound) break;
617
868
  if (chunk.thinking) {
618
- thinkingText += chunk.thinking;
619
- yield createThinkingDelta(chunk.thinking);
869
+ if (thinkingComplete) {
870
+ logger.warn("\u26A0\uFE0F thinkingComplete=true \u4F46\u6536\u5230 thinking", chunk.thinking.slice(0, 30));
871
+ logger.warn("\u5F53\u524D\u72B6\u6001", { textStarted: textStartedInOrchestrator, chunkCounts });
872
+ } else if (textStartedInOrchestrator) {
873
+ logger.warn("\u26A0\uFE0F text \u5F00\u59CB\u540E\u6536\u5230 thinking", chunk.thinking.slice(0, 30));
874
+ } else {
875
+ let delta = chunk.thinking;
876
+ if (!thinkingStarted) {
877
+ delta = delta.replace(/^(?:\r?\n)+/, "");
878
+ if (!delta) break;
879
+ }
880
+ if (!thinkingStarted) {
881
+ thinkingStarted = true;
882
+ thinkingStartedAt = Date.now();
883
+ logger.debug("\u53D1\u9001 thinking_start");
884
+ yield createThinkingStart();
885
+ }
886
+ thinkingText += delta;
887
+ yield createThinkingDelta(delta);
888
+ }
620
889
  }
621
890
  break;
622
891
  case "thinking_done":
623
- if (!thinkingComplete) {
892
+ if (!enableThinkingThisRound) break;
893
+ logger.info("\u6536\u5230 thinking_done", { thinkingStarted, thinkingComplete });
894
+ if (thinkingStarted && !thinkingComplete) {
624
895
  thinkingComplete = true;
896
+ logger.debug("\u53D1\u9001 thinking_end");
625
897
  yield createThinkingEnd(thinkingStartedAt);
898
+ } else if (!thinkingStarted) {
899
+ logger.warn("\u26A0\uFE0F \u6536\u5230 thinking_done \u4F46 thinkingStarted=false");
626
900
  }
627
901
  break;
628
902
  case "tool_call":
629
903
  if (chunk.toolCall) {
904
+ if (!context.tools || context.tools.length === 0) {
905
+ logger.warn("\u6536\u5230 tool_call \u4F46\u5F53\u524D\u672A\u6CE8\u5165\u5DE5\u5177\uFF0C\u5DF2\u5FFD\u7565", { toolName: chunk.toolCall.name });
906
+ break;
907
+ }
630
908
  pendingToolCalls.push(chunk.toolCall);
631
909
  hasToolCalls = true;
632
910
  }
633
911
  break;
634
912
  case "search_result":
913
+ if (!enableSearchThisRound) break;
635
914
  if (chunk.searchResults) {
636
915
  if (!searchStarted) {
637
916
  searchStarted = true;
638
917
  searchStartedAt = Date.now();
639
918
  yield createSearchStart(message);
640
919
  }
641
- searchResults = chunk.searchResults;
920
+ searchResults = normalizeSearchResults(chunk.searchResults);
642
921
  yield createSearchResult(searchResults, searchStartedAt);
643
922
  }
644
923
  break;
645
924
  case "done":
925
+ logger.info("\u6536\u5230 done", { finishReason: chunk.finishReason });
926
+ logger.info(`\u7B2C ${iterations} \u8F6E chunk \u7EDF\u8BA1`, chunkCounts);
646
927
  if (chunk.finishReason === "tool_calls") {
647
928
  hasToolCalls = true;
648
929
  }
649
930
  break;
650
931
  case "error":
932
+ logger.error("\u6536\u5230 error", chunk.error);
651
933
  yield createApiError(chunk.error ?? "\u672A\u77E5\u9519\u8BEF");
652
934
  return;
653
935
  }
654
936
  }
655
- if (!thinkingComplete) {
937
+ logger.info(`\u7B2C ${iterations} \u8F6E for-await \u5FAA\u73AF\u7ED3\u675F`);
938
+ logger.debug("\u72B6\u6001", { thinkingStarted, thinkingComplete });
939
+ if (enableThinkingThisRound && thinkingStarted && !thinkingComplete) {
940
+ logger.debug("\u8865\u53D1 thinking_end");
656
941
  thinkingComplete = true;
657
942
  yield createThinkingEnd(thinkingStartedAt);
658
943
  }
@@ -676,14 +961,20 @@ var ChatOrchestrator = class {
676
961
  });
677
962
  continue;
678
963
  }
964
+ if (toolCall.name === "web_search") {
965
+ const q = typeof args.query === "string" ? args.query : "";
966
+ if (!q.trim()) {
967
+ args.query = message;
968
+ }
969
+ }
679
970
  const autoRunConfig = this.config.getAutoRunConfig ? await this.config.getAutoRunConfig() : options.autoRunConfig || this.config.autoRunConfig;
680
- console.log("[Orchestrator] \u68C0\u67E5\u5DE5\u5177\u6279\u51C6:", {
971
+ logger.debug("\u68C0\u67E5\u5DE5\u5177\u6279\u51C6", {
681
972
  toolName: toolCall.name,
682
973
  autoRunConfigMode: autoRunConfig?.mode,
683
974
  hasCallback: !!this.config.onToolApprovalRequest
684
975
  });
685
976
  if (autoRunConfig?.mode === "manual" && this.config.onToolApprovalRequest) {
686
- console.log("[Orchestrator] \u53D1\u9001\u5DE5\u5177\u6279\u51C6\u8BF7\u6C42:", toolCall.name);
977
+ logger.info("\u53D1\u9001\u5DE5\u5177\u6279\u51C6\u8BF7\u6C42", toolCall.name);
687
978
  const approvalRequest = {
688
979
  type: "tool_approval_request",
689
980
  data: {
@@ -701,7 +992,18 @@ var ChatOrchestrator = class {
701
992
  });
702
993
  if (!approved) {
703
994
  const result2 = JSON.stringify({ skipped: true, message: "\u7528\u6237\u8DF3\u8FC7\u4E86\u6B64\u5DE5\u5177" });
704
- yield createToolCallResult(toolCall.id, toolCall.name, result2, false, toolStartedAt);
995
+ if (toolCall.name === "web_search" && enableSearchThisRound) {
996
+ if (!searchStarted) {
997
+ searchStarted = true;
998
+ searchStartedAt = Date.now();
999
+ const q = typeof args.query === "string" ? args.query : message;
1000
+ yield createSearchStart(q);
1001
+ }
1002
+ searchEnded = true;
1003
+ yield createSearchEnd(false, searchStartedAt, "\u7528\u6237\u8DF3\u8FC7\u4E86 web_search");
1004
+ } else {
1005
+ yield createToolCallResult(toolCall.id, toolCall.name, result2, false, toolStartedAt);
1006
+ }
705
1007
  messages.push({
706
1008
  role: "tool",
707
1009
  content: result2,
@@ -711,12 +1013,61 @@ var ChatOrchestrator = class {
711
1013
  continue;
712
1014
  }
713
1015
  }
714
- yield createToolCallStart(toolCall.id, toolCall.name, args);
1016
+ const isWebSearchTool = toolCall.name === "web_search";
1017
+ if (!isWebSearchTool) {
1018
+ yield createToolCallStart(toolCall.id, toolCall.name, args);
1019
+ } else if (enableSearchThisRound) {
1020
+ if (!searchStarted) {
1021
+ searchStarted = true;
1022
+ searchStartedAt = Date.now();
1023
+ const q = typeof args.query === "string" ? args.query : message;
1024
+ yield createSearchStart(q);
1025
+ }
1026
+ }
715
1027
  let result;
716
1028
  let dynamicSideEffects;
717
1029
  let success = true;
718
1030
  try {
719
- const executeResult = await this.config.executeTool(toolCall.name, args, context.signal);
1031
+ const eventQueue = [];
1032
+ let wake = null;
1033
+ const notify = () => wake?.();
1034
+ const pushEvent = (ev) => {
1035
+ eventQueue.push(ev);
1036
+ notify();
1037
+ };
1038
+ let toolDone = false;
1039
+ let toolValue;
1040
+ let toolError;
1041
+ const hooks = {
1042
+ toolCallId: toolCall.id,
1043
+ toolName: toolCall.name,
1044
+ onStdout: (chunk) => {
1045
+ if (chunk) pushEvent(createToolCallOutput(toolCall.id, toolCall.name, "stdout", chunk));
1046
+ },
1047
+ onStderr: (chunk) => {
1048
+ if (chunk) pushEvent(createToolCallOutput(toolCall.id, toolCall.name, "stderr", chunk));
1049
+ }
1050
+ };
1051
+ this.config.executeTool(toolCall.name, args, context.signal, hooks).then((v) => {
1052
+ toolValue = v;
1053
+ toolDone = true;
1054
+ notify();
1055
+ }).catch((e) => {
1056
+ toolError = e;
1057
+ toolDone = true;
1058
+ notify();
1059
+ });
1060
+ while (!toolDone || eventQueue.length > 0) {
1061
+ while (eventQueue.length > 0) {
1062
+ const ev = eventQueue.shift();
1063
+ if (ev) yield ev;
1064
+ }
1065
+ if (toolDone) break;
1066
+ await new Promise((r) => wake = r);
1067
+ wake = null;
1068
+ }
1069
+ if (toolError) throw toolError;
1070
+ const executeResult = toolValue;
720
1071
  if (typeof executeResult === "string") {
721
1072
  result = executeResult;
722
1073
  } else {
@@ -728,9 +1079,37 @@ var ChatOrchestrator = class {
728
1079
  const errorMessage = error instanceof Error ? error.message : String(error);
729
1080
  result = `\u9519\u8BEF: ${errorMessage}`;
730
1081
  }
731
- const tool2 = this.config.tools?.get(toolCall.name);
732
- const sideEffects = dynamicSideEffects ?? tool2?.sideEffects;
733
- yield createToolCallResult(toolCall.id, toolCall.name, result, success, toolStartedAt, void 0, sideEffects);
1082
+ const tool3 = this.config.tools?.get(toolCall.name);
1083
+ const sideEffects = dynamicSideEffects ?? tool3?.sideEffects;
1084
+ const resultType = success ? tool3?.resultType : void 0;
1085
+ if (!isWebSearchTool) {
1086
+ yield createToolCallResult(toolCall.id, toolCall.name, result, success, toolStartedAt, void 0, sideEffects, resultType);
1087
+ } else if (enableSearchThisRound) {
1088
+ let parsedResults = [];
1089
+ let parseError;
1090
+ try {
1091
+ const obj = JSON.parse(result || "{}");
1092
+ const arr = Array.isArray(obj?.results) ? obj.results : [];
1093
+ parsedResults = arr.map((r) => ({
1094
+ title: typeof r?.title === "string" ? r.title : "",
1095
+ url: typeof r?.url === "string" ? r.url : "",
1096
+ snippet: typeof r?.snippet === "string" ? r.snippet : ""
1097
+ })).filter((r) => !!r.url);
1098
+ if (typeof obj?.error === "string" && obj.error) {
1099
+ parseError = obj.error;
1100
+ success = false;
1101
+ }
1102
+ } catch {
1103
+ parseError = "web_search \u8FD4\u56DE\u7ED3\u679C\u89E3\u6790\u5931\u8D25";
1104
+ success = false;
1105
+ }
1106
+ if (parsedResults.length > 0) {
1107
+ searchResults = normalizeSearchResults(parsedResults);
1108
+ yield createSearchResult(searchResults, searchStartedAt);
1109
+ }
1110
+ searchEnded = true;
1111
+ yield createSearchEnd(success, searchStartedAt, parseError);
1112
+ }
734
1113
  messages.push({
735
1114
  role: "tool",
736
1115
  content: result,
@@ -756,7 +1135,7 @@ var ChatOrchestrator = class {
756
1135
  return;
757
1136
  }
758
1137
  }
759
- if (searchStarted) {
1138
+ if (searchStarted && !searchEnded) {
760
1139
  yield createSearchEnd(true, searchStartedAt);
761
1140
  }
762
1141
  const duration = Date.now() - startedAt;
@@ -782,7 +1161,9 @@ var ChatOrchestrator = class {
782
1161
  standardMsg.toolCalls = msg.tool_calls.map((tc) => ({
783
1162
  id: tc.id,
784
1163
  name: tc.function.name,
785
- arguments: tc.function.arguments
1164
+ arguments: tc.function.arguments,
1165
+ // 保留 thought_signature(Gemini 模型需要)
1166
+ thought_signature: tc.thought_signature
786
1167
  }));
787
1168
  }
788
1169
  messages.push(standardMsg);
@@ -799,59 +1180,28 @@ function createOrchestrator(config) {
799
1180
  return new ChatOrchestrator(config);
800
1181
  }
801
1182
 
802
- // src/providers/adapters/ark.ts
803
- var ARK_MODELS = [
804
- DOUBAO_SEED_1_8.id,
805
- DOUBAO_SEED_1_6.id,
806
- DOUBAO_SEED_1_6_LATEST.id,
807
- DOUBAO_SEED_1_6_FLASH.id,
808
- "doubao-1-5-pro-32k-250115",
809
- "doubao-1-5-pro-256k-250115",
810
- "doubao-1-5-lite-32k-250115"
811
- ];
812
- var ArkAdapter = class {
1183
+ // src/providers/protocols/ark.ts
1184
+ var logger2 = DebugLogger.module("ArkProtocol");
1185
+ var ArkProtocol = class {
813
1186
  name = "ark";
814
- supportedModels = ARK_MODELS;
815
1187
  apiKey;
816
1188
  apiUrl;
817
1189
  constructor(config) {
818
1190
  this.apiKey = config.apiKey;
819
1191
  this.apiUrl = config.apiUrl ?? DEFAULT_ARK_URL;
820
1192
  }
821
- supportsModel(model) {
822
- return this.supportedModels.some((m) => model.includes(m) || m.includes(model));
823
- }
824
1193
  /**
825
- * 单次流式调用
1194
+ * 发送请求并返回原始事件流
826
1195
  */
827
- async *streamOnce(messages, tools2, options) {
828
- const input = this.convertMessages(messages);
829
- const requestBody = {
1196
+ async *stream(messages, tools2, options) {
1197
+ const requestBody = this.buildRequestBody(messages, tools2, options);
1198
+ logger2.debug("\u53D1\u9001 ARK \u8BF7\u6C42", {
1199
+ url: `${this.apiUrl}/responses`,
830
1200
  model: options.model,
831
- stream: true,
832
- max_output_tokens: 32768,
833
- input
834
- };
835
- const apiTools = [];
836
- if (options.enableSearch) {
837
- apiTools.push({ type: "web_search", max_keyword: 5, limit: 20 });
838
- }
839
- if (tools2.length > 0) {
840
- for (const t of tools2) {
841
- apiTools.push({
842
- type: "function",
843
- name: t.name,
844
- description: t.description,
845
- parameters: t.parameters
846
- });
847
- }
848
- }
849
- if (apiTools.length > 0) {
850
- requestBody.tools = apiTools;
851
- }
852
- if (options.enableThinking) {
853
- requestBody.thinking = { type: "enabled" };
854
- }
1201
+ enableSearch: options.enableSearch,
1202
+ enableThinking: options.enableThinking,
1203
+ toolsCount: tools2.length
1204
+ });
855
1205
  const response = await fetch(`${this.apiUrl}/responses`, {
856
1206
  method: "POST",
857
1207
  headers: {
@@ -863,6 +1213,7 @@ var ArkAdapter = class {
863
1213
  });
864
1214
  if (!response.ok) {
865
1215
  const errorText = await response.text();
1216
+ logger2.error("ARK API \u9519\u8BEF", { status: response.status, body: errorText.slice(0, 500) });
866
1217
  yield { type: "error", error: `ARK API \u9519\u8BEF: ${response.status} - ${errorText}` };
867
1218
  return;
868
1219
  }
@@ -871,10 +1222,41 @@ var ArkAdapter = class {
871
1222
  yield { type: "error", error: "\u65E0\u6CD5\u83B7\u53D6\u54CD\u5E94\u6D41" };
872
1223
  return;
873
1224
  }
874
- yield* this.parseStream(reader);
1225
+ yield* this.parseSSE(reader);
1226
+ }
1227
+ /**
1228
+ * 构建请求体
1229
+ */
1230
+ buildRequestBody(messages, tools2, options) {
1231
+ const input = this.convertMessages(messages);
1232
+ const body = {
1233
+ model: options.model,
1234
+ stream: true,
1235
+ max_output_tokens: options.familyConfig.defaultMaxTokens ?? 32768,
1236
+ input
1237
+ };
1238
+ const apiTools = [];
1239
+ if (options.enableSearch) {
1240
+ apiTools.push({ type: "web_search", max_keyword: 5, limit: 20 });
1241
+ }
1242
+ for (const t of tools2) {
1243
+ apiTools.push({
1244
+ type: "function",
1245
+ name: t.name,
1246
+ description: t.description,
1247
+ parameters: t.parameters
1248
+ });
1249
+ }
1250
+ if (apiTools.length > 0) {
1251
+ body.tools = apiTools;
1252
+ }
1253
+ if (options.enableThinking) {
1254
+ body.thinking = { type: "enabled" };
1255
+ }
1256
+ return body;
875
1257
  }
876
1258
  /**
877
- * 转换标准消息为 ARK Responses API 格式
1259
+ * 转换消息格式
878
1260
  */
879
1261
  convertMessages(messages) {
880
1262
  const input = [];
@@ -884,7 +1266,8 @@ var ArkAdapter = class {
884
1266
  input.push({ role: "system", content: msg.content });
885
1267
  break;
886
1268
  case "user": {
887
- const content = [{ type: "input_text", text: msg.content }];
1269
+ const textContent = msg.content || (msg.images?.length ? "\u8BF7\u5206\u6790\u8FD9\u5F20\u56FE\u7247" : "");
1270
+ const content = [{ type: "input_text", text: textContent }];
888
1271
  if (msg.images?.length) {
889
1272
  for (const img of msg.images) {
890
1273
  content.push({
@@ -925,14 +1308,17 @@ var ArkAdapter = class {
925
1308
  return input;
926
1309
  }
927
1310
  /**
928
- * 解析 ARK 流式响应
1311
+ * 解析 SSE
929
1312
  */
930
- async *parseStream(reader) {
1313
+ async *parseSSE(reader) {
931
1314
  const decoder = new TextDecoder();
932
1315
  let buffer = "";
933
1316
  const pendingToolCalls = /* @__PURE__ */ new Map();
934
1317
  let currentFunctionCallId = null;
935
1318
  const searchResults = [];
1319
+ let streamDone = false;
1320
+ let thinkingDone = false;
1321
+ let textStarted = false;
936
1322
  while (true) {
937
1323
  const { done, value } = await reader.read();
938
1324
  if (done) break;
@@ -940,12 +1326,14 @@ var ArkAdapter = class {
940
1326
  const lines = buffer.split("\n");
941
1327
  buffer = lines.pop() || "";
942
1328
  for (const line of lines) {
1329
+ if (streamDone) continue;
943
1330
  if (!line.startsWith("data:")) continue;
944
1331
  const data = line.slice(5).trim();
945
1332
  if (data === "[DONE]") {
1333
+ streamDone = true;
946
1334
  if (pendingToolCalls.size > 0) {
947
1335
  for (const tc of pendingToolCalls.values()) {
948
- yield { type: "tool_call", toolCall: tc };
1336
+ yield { type: "tool_call_done", toolCall: tc };
949
1337
  }
950
1338
  yield { type: "done", finishReason: "tool_calls" };
951
1339
  } else {
@@ -958,18 +1346,14 @@ var ArkAdapter = class {
958
1346
  switch (event.type) {
959
1347
  case "response.output_item.added": {
960
1348
  const item = event.item;
961
- if (item?.type === "function_call") {
962
- const callId = item.call_id;
963
- if (!callId) {
964
- console.warn("[ARK] function_call \u7F3A\u5C11 call_id");
965
- break;
966
- }
967
- currentFunctionCallId = callId;
968
- pendingToolCalls.set(callId, {
969
- id: callId,
1349
+ if (item?.type === "function_call" && item.call_id) {
1350
+ currentFunctionCallId = item.call_id;
1351
+ pendingToolCalls.set(item.call_id, {
1352
+ id: item.call_id,
970
1353
  name: item.name || "",
971
1354
  arguments: item.arguments || ""
972
1355
  });
1356
+ yield { type: "tool_call_start", toolCall: { id: item.call_id, name: item.name || "" } };
973
1357
  }
974
1358
  break;
975
1359
  }
@@ -978,6 +1362,7 @@ var ArkAdapter = class {
978
1362
  const call = pendingToolCalls.get(currentFunctionCallId);
979
1363
  if (call) {
980
1364
  call.arguments += event.delta || "";
1365
+ yield { type: "tool_call_delta", toolCall: { id: currentFunctionCallId, arguments: event.delta || "" } };
981
1366
  }
982
1367
  }
983
1368
  break;
@@ -986,10 +1371,9 @@ var ArkAdapter = class {
986
1371
  case "response.output_item.done": {
987
1372
  const item = event.item;
988
1373
  if (item?.type === "function_call" && item.call_id) {
989
- const callId = item.call_id;
990
- const existing = pendingToolCalls.get(callId);
991
- pendingToolCalls.set(callId, {
992
- id: callId,
1374
+ const existing = pendingToolCalls.get(item.call_id);
1375
+ pendingToolCalls.set(item.call_id, {
1376
+ id: item.call_id,
993
1377
  name: item.name || existing?.name || "",
994
1378
  arguments: item.arguments || existing?.arguments || "{}"
995
1379
  });
@@ -998,8 +1382,7 @@ var ArkAdapter = class {
998
1382
  }
999
1383
  case "response.output_text.annotation.added": {
1000
1384
  const annotation = event.annotation;
1001
- const isUrlCitation = annotation?.type === "url_citation" || annotation?.type === "citation" || annotation?.url;
1002
- if (isUrlCitation && annotation?.url) {
1385
+ if (annotation?.url) {
1003
1386
  const exists = searchResults.some((r) => r.url === annotation.url);
1004
1387
  if (!exists) {
1005
1388
  searchResults.push({
@@ -1014,181 +1397,176 @@ var ArkAdapter = class {
1014
1397
  }
1015
1398
  case "response.output_text.delta":
1016
1399
  if (event.delta) {
1017
- yield { type: "text", text: event.delta };
1400
+ if (!textStarted) {
1401
+ textStarted = true;
1402
+ if (!thinkingDone) {
1403
+ thinkingDone = true;
1404
+ yield { type: "thinking_done" };
1405
+ }
1406
+ }
1407
+ yield { type: "text_delta", delta: event.delta };
1018
1408
  }
1019
1409
  break;
1020
1410
  case "response.reasoning_summary_text.delta":
1021
- if (event.delta) {
1022
- yield { type: "thinking", thinking: event.delta };
1411
+ if (event.delta && !thinkingDone) {
1412
+ yield { type: "thinking_delta", delta: event.delta };
1023
1413
  }
1024
1414
  break;
1025
- case "response.reasoning_summary_text.done":
1026
- yield { type: "thinking_done" };
1027
- break;
1028
1415
  }
1029
1416
  } catch {
1030
1417
  }
1031
1418
  }
1032
1419
  }
1033
- if (pendingToolCalls.size > 0) {
1034
- for (const tc of pendingToolCalls.values()) {
1035
- yield { type: "tool_call", toolCall: tc };
1420
+ if (!streamDone) {
1421
+ if (pendingToolCalls.size > 0) {
1422
+ for (const tc of pendingToolCalls.values()) {
1423
+ yield { type: "tool_call_done", toolCall: tc };
1424
+ }
1425
+ yield { type: "done", finishReason: "tool_calls" };
1426
+ } else {
1427
+ yield { type: "done", finishReason: "stop" };
1036
1428
  }
1037
- yield { type: "done", finishReason: "tool_calls" };
1038
- } else {
1039
- yield { type: "done", finishReason: "stop" };
1040
1429
  }
1041
1430
  }
1042
1431
  };
1043
- function createArkAdapter(config) {
1044
- return new ArkAdapter(config);
1432
+ function createArkProtocol(config) {
1433
+ return new ArkProtocol(config);
1045
1434
  }
1046
1435
 
1047
- // src/providers/adapters/openrouter.ts
1048
- var OPENROUTER_MODELS = [
1049
- OR_GEMINI_2_5_FLASH.id,
1050
- OR_GEMINI_2_5_PRO.id,
1051
- OR_GEMINI_2_0_FLASH.id,
1052
- "anthropic/claude-sonnet-4",
1053
- "anthropic/claude-3.5-sonnet",
1054
- "openai/gpt-4o",
1055
- "openai/gpt-4o-mini",
1056
- OR_DEEPSEEK_R1.id,
1057
- OR_DEEPSEEK_CHAT_V3.id
1058
- ];
1059
- var OpenRouterAdapter = class {
1060
- name = "openrouter";
1061
- supportedModels = OPENROUTER_MODELS;
1436
+ // src/providers/protocols/deepseek.ts
1437
+ var logger3 = DebugLogger.module("DeepSeekProtocol");
1438
+ var DeepSeekProtocol = class {
1439
+ name = "deepseek";
1062
1440
  apiKey;
1063
1441
  apiUrl;
1064
1442
  constructor(config) {
1065
1443
  this.apiKey = config.apiKey;
1066
- this.apiUrl = config.apiUrl ?? DEFAULT_OPENROUTER_URL;
1444
+ this.apiUrl = config.apiUrl ?? DEFAULT_ARK_URL;
1067
1445
  }
1068
- supportsModel(model) {
1069
- return this.supportedModels.some((m) => model.includes(m) || m.includes(model));
1446
+ async *stream(messages, tools2, options) {
1447
+ const requestBody = this.buildRequestBody(messages, tools2, options);
1448
+ const url = `${this.apiUrl}/responses`;
1449
+ logger3.debug("\u53D1\u9001 DeepSeek \u8BF7\u6C42", {
1450
+ url,
1451
+ model: options.model,
1452
+ enableSearch: options.enableSearch,
1453
+ enableThinking: options.enableThinking,
1454
+ toolsCount: tools2.length
1455
+ });
1456
+ const response = await fetch(url, {
1457
+ method: "POST",
1458
+ headers: {
1459
+ "Authorization": `Bearer ${this.apiKey}`,
1460
+ "Content-Type": "application/json"
1461
+ },
1462
+ body: JSON.stringify(requestBody),
1463
+ signal: options.signal
1464
+ });
1465
+ if (!response.ok) {
1466
+ const errorText = await response.text();
1467
+ logger3.error("DeepSeek API \u9519\u8BEF", { status: response.status, body: errorText.slice(0, 500) });
1468
+ yield { type: "error", error: `DeepSeek API \u9519\u8BEF: ${response.status} - ${errorText}` };
1469
+ return;
1470
+ }
1471
+ const reader = response.body?.getReader();
1472
+ if (!reader) {
1473
+ yield { type: "error", error: "\u65E0\u6CD5\u83B7\u53D6\u54CD\u5E94\u6D41" };
1474
+ return;
1475
+ }
1476
+ yield* this.parseSSE(reader, options.enableThinking);
1070
1477
  }
1071
1478
  /**
1072
- * 单次流式调用
1479
+ * 构建请求体(DeepSeek 专用)
1073
1480
  */
1074
- async *streamOnce(messages, tools2, options) {
1075
- const apiMessages = this.convertMessages(messages);
1076
- const requestBody = {
1481
+ buildRequestBody(messages, tools2, options) {
1482
+ const input = this.convertMessages(messages);
1483
+ const body = {
1077
1484
  model: options.model,
1078
- messages: apiMessages,
1079
1485
  stream: true,
1080
- max_tokens: 32768
1486
+ max_output_tokens: options.familyConfig.defaultMaxTokens ?? 32768,
1487
+ input
1081
1488
  };
1082
- if (tools2.length > 0) {
1083
- requestBody.tools = tools2.map((t) => ({
1084
- type: "function",
1085
- function: {
1086
- name: t.name,
1087
- description: t.description,
1088
- parameters: t.parameters
1089
- }
1090
- }));
1091
- }
1092
- if (options.model.includes("gemini") && options.enableThinking) {
1093
- requestBody.reasoning = { effort: "high" };
1094
- }
1095
- if (options.model.includes("claude") && options.enableThinking) {
1096
- requestBody.reasoning = { effort: "high", exclude: false };
1097
- }
1489
+ const apiTools = [];
1098
1490
  if (options.enableSearch) {
1099
- const isSmallContext = options.model.includes("deepseek") || options.model.includes("qwen");
1100
- const maxResults = isSmallContext ? 2 : 3;
1101
- requestBody.plugins = [{ id: "web", max_results: maxResults }];
1491
+ apiTools.push({ type: "web_search", max_keyword: 5, limit: 20 });
1102
1492
  }
1103
- const response = await fetch(`${this.apiUrl}/chat/completions`, {
1104
- method: "POST",
1105
- headers: {
1106
- "Authorization": `Bearer ${this.apiKey}`,
1107
- "Content-Type": "application/json",
1108
- "HTTP-Referer": "https://ai-chat.local",
1109
- "X-Title": "AI Chat"
1110
- },
1111
- body: JSON.stringify(requestBody),
1112
- signal: options.signal
1113
- });
1114
- if (!response.ok) {
1115
- const errorText = await response.text();
1116
- yield { type: "error", error: `OpenRouter API \u9519\u8BEF: ${response.status} - ${errorText}` };
1117
- return;
1493
+ for (const t of tools2) {
1494
+ apiTools.push({
1495
+ type: "function",
1496
+ name: t.name,
1497
+ description: t.description,
1498
+ parameters: t.parameters
1499
+ });
1118
1500
  }
1119
- const reader = response.body?.getReader();
1120
- if (!reader) {
1121
- yield { type: "error", error: "\u65E0\u6CD5\u83B7\u53D6\u54CD\u5E94\u6D41" };
1122
- return;
1501
+ if (apiTools.length > 0) {
1502
+ body.tools = apiTools;
1503
+ }
1504
+ if (options.enableThinking) {
1505
+ body.thinking = { type: "enabled" };
1123
1506
  }
1124
- yield* this.parseStream(reader);
1507
+ return body;
1125
1508
  }
1126
- /**
1127
- * 转换标准消息为 OpenAI 格式
1128
- */
1129
1509
  convertMessages(messages) {
1130
- const apiMessages = [];
1510
+ const input = [];
1131
1511
  for (const msg of messages) {
1132
1512
  switch (msg.role) {
1133
1513
  case "system":
1134
- apiMessages.push({ role: "system", content: msg.content });
1514
+ input.push({ role: "system", content: msg.content });
1135
1515
  break;
1136
1516
  case "user": {
1517
+ const textContent = msg.content || (msg.images?.length ? "\u8BF7\u5206\u6790\u8FD9\u5F20\u56FE\u7247" : "");
1518
+ const content = [{ type: "input_text", text: textContent }];
1137
1519
  if (msg.images?.length) {
1138
- const content = [{ type: "text", text: msg.content }];
1139
1520
  for (const img of msg.images) {
1140
1521
  content.push({
1141
- type: "image_url",
1142
- image_url: { url: img.startsWith("data:") ? img : `data:image/jpeg;base64,${img}` }
1522
+ type: "input_image",
1523
+ image_url: img.startsWith("data:") ? img : `data:image/jpeg;base64,${img}`
1143
1524
  });
1144
1525
  }
1145
- apiMessages.push({ role: "user", content });
1146
- } else {
1147
- apiMessages.push({ role: "user", content: msg.content });
1148
1526
  }
1527
+ input.push({ role: "user", content });
1149
1528
  break;
1150
1529
  }
1151
1530
  case "assistant":
1152
1531
  if (msg.toolCalls?.length) {
1153
- apiMessages.push({
1154
- role: "assistant",
1155
- content: msg.content || null,
1156
- tool_calls: msg.toolCalls.map((tc) => ({
1157
- id: tc.id,
1158
- type: "function",
1159
- function: {
1160
- name: tc.name,
1161
- arguments: tc.arguments
1162
- }
1163
- }))
1164
- });
1532
+ for (const tc of msg.toolCalls) {
1533
+ input.push({
1534
+ type: "function_call",
1535
+ call_id: tc.id,
1536
+ name: tc.name,
1537
+ arguments: tc.arguments
1538
+ });
1539
+ }
1165
1540
  } else {
1166
- apiMessages.push({ role: "assistant", content: msg.content });
1541
+ input.push({
1542
+ role: "developer",
1543
+ content: `[\u4E0A\u4E00\u8F6EAI\u56DE\u590D]: ${msg.content}`
1544
+ });
1167
1545
  }
1168
1546
  break;
1169
1547
  case "tool":
1170
- apiMessages.push({
1171
- role: "tool",
1172
- tool_call_id: msg.toolCallId,
1173
- content: msg.content
1548
+ input.push({
1549
+ type: "function_call_output",
1550
+ call_id: msg.toolCallId,
1551
+ output: msg.content
1174
1552
  });
1175
1553
  break;
1176
1554
  }
1177
1555
  }
1178
- return apiMessages;
1556
+ return input;
1179
1557
  }
1180
1558
  /**
1181
- * 解析 OpenAI 兼容流式响应
1559
+ * 解析 SSE 流(DeepSeek 专用)
1182
1560
  */
1183
- async *parseStream(reader) {
1561
+ async *parseSSE(reader, enableThinking) {
1184
1562
  const decoder = new TextDecoder();
1185
1563
  let buffer = "";
1186
- const toolCallsMap = /* @__PURE__ */ new Map();
1187
- let toolCallCounter = 0;
1188
- let finishReason = null;
1564
+ const pendingToolCalls = /* @__PURE__ */ new Map();
1565
+ let currentFunctionCallId = null;
1189
1566
  const searchResults = [];
1190
- let hasThinking = false;
1567
+ let streamDone = false;
1191
1568
  let thinkingDone = false;
1569
+ let textStarted = false;
1192
1570
  while (true) {
1193
1571
  const { done, value } = await reader.read();
1194
1572
  if (done) break;
@@ -1196,17 +1574,14 @@ var OpenRouterAdapter = class {
1196
1574
  const lines = buffer.split("\n");
1197
1575
  buffer = lines.pop() || "";
1198
1576
  for (const line of lines) {
1577
+ if (streamDone) continue;
1199
1578
  if (!line.startsWith("data:")) continue;
1200
1579
  const data = line.slice(5).trim();
1201
1580
  if (data === "[DONE]") {
1202
- if (searchResults.length > 0) {
1203
- yield { type: "search_result", searchResults: [...searchResults] };
1204
- }
1205
- if (toolCallsMap.size > 0) {
1206
- for (const tc of toolCallsMap.values()) {
1207
- if (tc.name) {
1208
- yield { type: "tool_call", toolCall: tc };
1209
- }
1581
+ streamDone = true;
1582
+ if (pendingToolCalls.size > 0) {
1583
+ for (const tc of pendingToolCalls.values()) {
1584
+ yield { type: "tool_call_done", toolCall: tc };
1210
1585
  }
1211
1586
  yield { type: "done", finishReason: "tool_calls" };
1212
1587
  } else {
@@ -1215,159 +1590,133 @@ var OpenRouterAdapter = class {
1215
1590
  return;
1216
1591
  }
1217
1592
  try {
1218
- const json = JSON.parse(data);
1219
- const choice = json.choices?.[0];
1220
- const annotations = json.annotations;
1221
- if (annotations?.length) {
1222
- for (const annotation of annotations) {
1223
- if (annotation.type === "url_citation" && annotation.url_citation?.url) {
1224
- const exists = searchResults.some((r) => r.url === annotation.url_citation.url);
1593
+ const event = JSON.parse(data);
1594
+ switch (event.type) {
1595
+ case "response.output_item.added": {
1596
+ const item = event.item;
1597
+ if (item?.type === "function_call" && item.call_id) {
1598
+ currentFunctionCallId = item.call_id;
1599
+ pendingToolCalls.set(item.call_id, {
1600
+ id: item.call_id,
1601
+ name: item.name || "",
1602
+ arguments: item.arguments || ""
1603
+ });
1604
+ yield { type: "tool_call_start", toolCall: { id: item.call_id, name: item.name || "" } };
1605
+ }
1606
+ break;
1607
+ }
1608
+ case "response.function_call_arguments.delta": {
1609
+ if (currentFunctionCallId) {
1610
+ const call = pendingToolCalls.get(currentFunctionCallId);
1611
+ if (call) {
1612
+ call.arguments += event.delta || "";
1613
+ yield { type: "tool_call_delta", toolCall: { id: currentFunctionCallId, arguments: event.delta || "" } };
1614
+ }
1615
+ }
1616
+ break;
1617
+ }
1618
+ case "response.function_call_arguments.done":
1619
+ case "response.output_item.done": {
1620
+ const item = event.item;
1621
+ if (item?.type === "function_call" && item.call_id) {
1622
+ const existing = pendingToolCalls.get(item.call_id);
1623
+ pendingToolCalls.set(item.call_id, {
1624
+ id: item.call_id,
1625
+ name: item.name || existing?.name || "",
1626
+ arguments: item.arguments || existing?.arguments || "{}"
1627
+ });
1628
+ }
1629
+ break;
1630
+ }
1631
+ case "response.output_text.annotation.added": {
1632
+ const annotation = event.annotation;
1633
+ if (annotation?.url) {
1634
+ const exists = searchResults.some((r) => r.url === annotation.url);
1225
1635
  if (!exists) {
1226
1636
  searchResults.push({
1227
- url: annotation.url_citation.url,
1228
- title: annotation.url_citation.title || "",
1229
- snippet: annotation.url_citation.content || ""
1637
+ title: annotation.title || annotation.text || "",
1638
+ url: annotation.url,
1639
+ snippet: annotation.summary || annotation.snippet || ""
1230
1640
  });
1231
1641
  yield { type: "search_result", searchResults: [...searchResults] };
1232
1642
  }
1233
1643
  }
1644
+ break;
1234
1645
  }
1235
- }
1236
- if (!choice) continue;
1237
- const delta = choice.delta || {};
1238
- finishReason = choice.finish_reason;
1239
- if (delta.reasoning) {
1240
- hasThinking = true;
1241
- yield { type: "thinking", thinking: delta.reasoning };
1242
- }
1243
- if (delta.content) {
1244
- if (hasThinking && !thinkingDone) {
1245
- thinkingDone = true;
1246
- yield { type: "thinking_done" };
1247
- }
1248
- yield { type: "text", text: delta.content };
1249
- }
1250
- if (delta.tool_calls?.length) {
1251
- for (const tc of delta.tool_calls) {
1252
- const index = tc.index;
1253
- const existing = toolCallsMap.get(index);
1254
- if (existing) {
1255
- if (tc.function?.arguments) {
1256
- existing.arguments += tc.function.arguments;
1257
- }
1258
- if (tc.function?.name) {
1259
- existing.name = tc.function.name;
1260
- }
1261
- if (tc.id) {
1262
- existing.id = tc.id;
1646
+ case "response.output_text.delta":
1647
+ if (event.delta) {
1648
+ if (!textStarted) {
1649
+ textStarted = true;
1650
+ if (enableThinking && !thinkingDone) {
1651
+ thinkingDone = true;
1652
+ yield { type: "thinking_done" };
1653
+ }
1263
1654
  }
1264
- } else {
1265
- toolCallsMap.set(index, {
1266
- id: tc.id || `call_${Date.now()}_${toolCallCounter++}`,
1267
- name: tc.function?.name || "",
1268
- arguments: tc.function?.arguments || ""
1269
- });
1655
+ yield { type: "text_delta", delta: event.delta };
1270
1656
  }
1271
- }
1272
- }
1273
- if (finishReason === "tool_calls") {
1274
- for (const tc of toolCallsMap.values()) {
1275
- if (tc.name) {
1276
- yield { type: "tool_call", toolCall: tc };
1657
+ break;
1658
+ // DeepSeek 支持 reasoning(深度思考)
1659
+ case "response.reasoning_summary_text.delta":
1660
+ if (enableThinking && event.delta && !thinkingDone) {
1661
+ yield { type: "thinking_delta", delta: event.delta };
1277
1662
  }
1278
- }
1279
- yield { type: "done", finishReason: "tool_calls" };
1280
- return;
1663
+ break;
1281
1664
  }
1282
1665
  } catch {
1283
1666
  }
1284
1667
  }
1285
1668
  }
1286
- if (searchResults.length > 0) {
1287
- yield { type: "search_result", searchResults: [...searchResults] };
1288
- }
1289
- if (toolCallsMap.size > 0) {
1290
- for (const tc of toolCallsMap.values()) {
1291
- if (tc.name) {
1292
- yield { type: "tool_call", toolCall: tc };
1669
+ if (!streamDone) {
1670
+ if (pendingToolCalls.size > 0) {
1671
+ for (const tc of pendingToolCalls.values()) {
1672
+ yield { type: "tool_call_done", toolCall: tc };
1293
1673
  }
1674
+ yield { type: "done", finishReason: "tool_calls" };
1675
+ } else {
1676
+ yield { type: "done", finishReason: "stop" };
1294
1677
  }
1295
- yield { type: "done", finishReason: "tool_calls" };
1296
- } else {
1297
- yield { type: "done", finishReason: "stop" };
1298
1678
  }
1299
1679
  }
1300
1680
  };
1301
- function createOpenRouterAdapter(config) {
1302
- return new OpenRouterAdapter(config);
1681
+ function createDeepSeekProtocol(config) {
1682
+ return new DeepSeekProtocol(config);
1303
1683
  }
1304
1684
 
1305
- // src/providers/adapters/qwen.ts
1306
- var QWEN_MODELS = [
1307
- QWEN_MAX.id,
1308
- QWEN_MAX_LATEST.id,
1309
- QWEN_PLUS.id,
1310
- QWEN_PLUS_LATEST.id,
1311
- QWEN_TURBO.id,
1312
- QWEN_TURBO_LATEST.id,
1313
- QWEN3_235B.id
1314
- ];
1315
- var QwenAdapter = class {
1685
+ // src/providers/protocols/qwen.ts
1686
+ var logger4 = DebugLogger.module("QwenProtocol");
1687
+ var DEFAULT_QWEN_COMPATIBLE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1";
1688
+ var QwenProtocol = class {
1316
1689
  name = "qwen";
1317
- supportedModels = QWEN_MODELS;
1318
1690
  apiKey;
1319
1691
  apiUrl;
1320
1692
  constructor(config) {
1321
1693
  this.apiKey = config.apiKey;
1322
- this.apiUrl = config.apiUrl ?? DEFAULT_QWEN_URL;
1323
- }
1324
- supportsModel(model) {
1325
- return this.supportedModels.some((m) => model.includes(m) || m.includes(model));
1694
+ this.apiUrl = config.apiUrl ?? DEFAULT_QWEN_COMPATIBLE_URL;
1326
1695
  }
1327
1696
  /**
1328
- * 单次流式调用
1697
+ * 发送请求并返回原始事件流
1329
1698
  */
1330
- async *streamOnce(messages, tools2, options) {
1331
- const apiMessages = this.convertMessages(messages);
1332
- const requestBody = {
1699
+ async *stream(messages, tools2, options) {
1700
+ const requestBody = this.buildRequestBody(messages, tools2, options);
1701
+ const url = `${this.apiUrl}/chat/completions`;
1702
+ logger4.debug("\u53D1\u9001 Qwen \u8BF7\u6C42\uFF08\u517C\u5BB9\u6A21\u5F0F\uFF09", {
1703
+ url,
1333
1704
  model: options.model,
1334
- input: { messages: apiMessages },
1335
- parameters: {
1336
- result_format: "message",
1337
- incremental_output: true,
1338
- enable_search: options.enableSearch,
1339
- // 深度思考支持
1340
- ...options.enableThinking && {
1341
- enable_thinking: true,
1342
- thinking_budget: 38400
1343
- }
1344
- }
1345
- };
1346
- if (tools2.length > 0) {
1347
- requestBody.parameters = {
1348
- ...requestBody.parameters,
1349
- tools: tools2.map((t) => ({
1350
- type: "function",
1351
- function: {
1352
- name: t.name,
1353
- description: t.description,
1354
- parameters: t.parameters
1355
- }
1356
- }))
1357
- };
1358
- }
1359
- const response = await fetch(`${this.apiUrl}/services/aigc/text-generation/generation`, {
1705
+ enableThinking: options.enableThinking,
1706
+ toolsCount: tools2.length
1707
+ });
1708
+ const response = await fetch(url, {
1360
1709
  method: "POST",
1361
1710
  headers: {
1362
1711
  "Authorization": `Bearer ${this.apiKey}`,
1363
- "Content-Type": "application/json",
1364
- "X-DashScope-SSE": "enable"
1712
+ "Content-Type": "application/json"
1365
1713
  },
1366
1714
  body: JSON.stringify(requestBody),
1367
1715
  signal: options.signal
1368
1716
  });
1369
1717
  if (!response.ok) {
1370
1718
  const errorText = await response.text();
1719
+ logger4.error("Qwen API \u9519\u8BEF", { status: response.status, body: errorText.slice(0, 500) });
1371
1720
  yield { type: "error", error: `Qwen API \u9519\u8BEF: ${response.status} - ${errorText}` };
1372
1721
  return;
1373
1722
  }
@@ -1376,53 +1725,77 @@ var QwenAdapter = class {
1376
1725
  yield { type: "error", error: "\u65E0\u6CD5\u83B7\u53D6\u54CD\u5E94\u6D41" };
1377
1726
  return;
1378
1727
  }
1379
- yield* this.parseStream(reader);
1728
+ yield* this.parseSSE(reader, options.enableThinking);
1729
+ }
1730
+ /**
1731
+ * 构建请求体(OpenAI 格式)
1732
+ */
1733
+ buildRequestBody(messages, tools2, options) {
1734
+ const convertedMessages = this.convertMessages(messages);
1735
+ const body = {
1736
+ model: options.model,
1737
+ messages: convertedMessages,
1738
+ stream: true
1739
+ };
1740
+ if (options.enableThinking) {
1741
+ body.enable_thinking = true;
1742
+ body.thinking_budget = 38400;
1743
+ }
1744
+ if (tools2.length > 0) {
1745
+ body.tools = tools2.map((t) => ({
1746
+ type: "function",
1747
+ function: {
1748
+ name: t.name,
1749
+ description: t.description,
1750
+ parameters: t.parameters
1751
+ }
1752
+ }));
1753
+ }
1754
+ return body;
1380
1755
  }
1381
1756
  /**
1382
- * 转换标准消息为 Qwen 格式
1757
+ * 转换消息格式(OpenAI 标准格式)
1383
1758
  */
1384
1759
  convertMessages(messages) {
1385
- const apiMessages = [];
1760
+ const result = [];
1386
1761
  for (const msg of messages) {
1387
1762
  switch (msg.role) {
1388
1763
  case "system":
1389
- apiMessages.push({ role: "system", content: msg.content });
1764
+ result.push({ role: "system", content: msg.content });
1390
1765
  break;
1391
1766
  case "user": {
1767
+ const textContent = msg.content || (msg.images?.length ? "\u8BF7\u5206\u6790\u8FD9\u5F20\u56FE\u7247" : "");
1392
1768
  if (msg.images?.length) {
1393
- const content = [{ type: "text", text: msg.content }];
1769
+ const content = [{ type: "text", text: textContent }];
1394
1770
  for (const img of msg.images) {
1395
1771
  content.push({
1396
1772
  type: "image_url",
1397
1773
  image_url: { url: img.startsWith("data:") ? img : `data:image/jpeg;base64,${img}` }
1398
1774
  });
1399
1775
  }
1400
- apiMessages.push({ role: "user", content });
1776
+ result.push({ role: "user", content });
1401
1777
  } else {
1402
- apiMessages.push({ role: "user", content: msg.content });
1778
+ result.push({ role: "user", content: textContent });
1403
1779
  }
1404
1780
  break;
1405
1781
  }
1406
1782
  case "assistant":
1407
1783
  if (msg.toolCalls?.length) {
1408
- apiMessages.push({
1784
+ result.push({
1409
1785
  role: "assistant",
1410
- content: msg.content || "",
1786
+ content: msg.content || null,
1411
1787
  tool_calls: msg.toolCalls.map((tc) => ({
1412
1788
  id: tc.id,
1413
1789
  type: "function",
1414
- function: {
1415
- name: tc.name,
1416
- arguments: tc.arguments
1417
- }
1790
+ function: { name: tc.name, arguments: tc.arguments }
1418
1791
  }))
1419
1792
  });
1420
1793
  } else {
1421
- apiMessages.push({ role: "assistant", content: msg.content });
1794
+ result.push({ role: "assistant", content: msg.content });
1422
1795
  }
1423
1796
  break;
1424
1797
  case "tool":
1425
- apiMessages.push({
1798
+ result.push({
1426
1799
  role: "tool",
1427
1800
  tool_call_id: msg.toolCallId,
1428
1801
  content: msg.content
@@ -1430,18 +1803,16 @@ var QwenAdapter = class {
1430
1803
  break;
1431
1804
  }
1432
1805
  }
1433
- return apiMessages;
1806
+ return result;
1434
1807
  }
1435
1808
  /**
1436
- * 解析 Qwen 流式响应
1809
+ * 解析 SSE 流(OpenAI 格式)
1437
1810
  */
1438
- async *parseStream(reader) {
1811
+ async *parseSSE(reader, enableThinking) {
1439
1812
  const decoder = new TextDecoder();
1440
1813
  let buffer = "";
1441
1814
  const toolCallsMap = /* @__PURE__ */ new Map();
1442
- let toolCallCounter = 0;
1443
- const searchResults = [];
1444
- let hasThinking = false;
1815
+ let textStarted = false;
1445
1816
  let thinkingDone = false;
1446
1817
  while (true) {
1447
1818
  const { done, value } = await reader.read();
@@ -1452,42 +1823,41 @@ var QwenAdapter = class {
1452
1823
  for (const line of lines) {
1453
1824
  if (!line.startsWith("data:")) continue;
1454
1825
  const data = line.slice(5).trim();
1455
- if (!data || data === "[DONE]") continue;
1826
+ if (!data || data === "[DONE]") {
1827
+ if (data === "[DONE]") {
1828
+ const toolCalls2 = Array.from(toolCallsMap.values());
1829
+ if (toolCalls2.length > 0) {
1830
+ for (const tc of toolCalls2) {
1831
+ yield { type: "tool_call_done", toolCall: tc };
1832
+ }
1833
+ yield { type: "done", finishReason: "tool_calls" };
1834
+ } else {
1835
+ yield { type: "done", finishReason: "stop" };
1836
+ }
1837
+ return;
1838
+ }
1839
+ continue;
1840
+ }
1456
1841
  try {
1457
1842
  const json = JSON.parse(data);
1458
- const output = json.output;
1459
- if (!output) continue;
1460
- const choice = output.choices?.[0];
1461
- const message = choice?.message || {};
1462
- const finishReason = choice?.finish_reason;
1463
- if (message.reasoning_content) {
1464
- hasThinking = true;
1465
- yield { type: "thinking", thinking: message.reasoning_content };
1843
+ const choice = json.choices?.[0];
1844
+ if (!choice) continue;
1845
+ const delta = choice.delta;
1846
+ if (!delta) continue;
1847
+ if (enableThinking && delta.reasoning_content && !thinkingDone) {
1848
+ yield { type: "thinking_delta", delta: delta.reasoning_content };
1466
1849
  }
1467
- if (message.content) {
1468
- if (!thinkingDone && hasThinking) {
1850
+ if (delta.content) {
1851
+ if (enableThinking && !textStarted && !thinkingDone) {
1469
1852
  thinkingDone = true;
1470
1853
  yield { type: "thinking_done" };
1471
1854
  }
1472
- yield { type: "text", text: message.content };
1855
+ textStarted = true;
1856
+ yield { type: "text_delta", delta: delta.content };
1473
1857
  }
1474
- const searchInfo = json.output?.search_info;
1475
- if (searchInfo?.search_results?.length) {
1476
- for (const result of searchInfo.search_results) {
1477
- const exists = searchResults.some((r) => r.url === result.url);
1478
- if (!exists) {
1479
- searchResults.push({
1480
- title: result.title || "",
1481
- url: result.url || "",
1482
- snippet: result.snippet || ""
1483
- });
1484
- }
1485
- }
1486
- yield { type: "search_result", searchResults: [...searchResults] };
1487
- }
1488
- if (message.tool_calls?.length) {
1489
- for (const tc of message.tool_calls) {
1490
- const index = toolCallsMap.size;
1858
+ if (delta.tool_calls?.length) {
1859
+ for (const tc of delta.tool_calls) {
1860
+ const index = tc.index ?? 0;
1491
1861
  const existing = toolCallsMap.get(index);
1492
1862
  if (existing) {
1493
1863
  if (tc.function?.arguments) {
@@ -1495,21 +1865,26 @@ var QwenAdapter = class {
1495
1865
  }
1496
1866
  } else {
1497
1867
  toolCallsMap.set(index, {
1498
- id: tc.id || `call_${Date.now()}_${toolCallCounter++}`,
1499
- name: tc.function.name,
1500
- arguments: tc.function.arguments
1868
+ id: tc.id || `call_${index}`,
1869
+ name: tc.function?.name || "",
1870
+ arguments: tc.function?.arguments || ""
1501
1871
  });
1872
+ yield {
1873
+ type: "tool_call_start",
1874
+ toolCall: { id: tc.id || `call_${index}`, name: tc.function?.name || "" }
1875
+ };
1502
1876
  }
1503
1877
  }
1504
1878
  }
1505
- if (finishReason === "stop" || finishReason === "length" || finishReason === "tool_calls") {
1506
- if (toolCallsMap.size > 0) {
1507
- for (const tc of toolCallsMap.values()) {
1508
- yield { type: "tool_call", toolCall: tc };
1879
+ if (choice.finish_reason) {
1880
+ const toolCalls2 = Array.from(toolCallsMap.values());
1881
+ if (toolCalls2.length > 0) {
1882
+ for (const tc of toolCalls2) {
1883
+ yield { type: "tool_call_done", toolCall: tc };
1509
1884
  }
1510
1885
  yield { type: "done", finishReason: "tool_calls" };
1511
1886
  } else {
1512
- yield { type: "done", finishReason: "stop" };
1887
+ yield { type: "done", finishReason: choice.finish_reason };
1513
1888
  }
1514
1889
  return;
1515
1890
  }
@@ -1517,9 +1892,10 @@ var QwenAdapter = class {
1517
1892
  }
1518
1893
  }
1519
1894
  }
1520
- if (toolCallsMap.size > 0) {
1521
- for (const tc of toolCallsMap.values()) {
1522
- yield { type: "tool_call", toolCall: tc };
1895
+ const toolCalls = Array.from(toolCallsMap.values());
1896
+ if (toolCalls.length > 0) {
1897
+ for (const tc of toolCalls) {
1898
+ yield { type: "tool_call_done", toolCall: tc };
1523
1899
  }
1524
1900
  yield { type: "done", finishReason: "tool_calls" };
1525
1901
  } else {
@@ -1527,201 +1903,1034 @@ var QwenAdapter = class {
1527
1903
  }
1528
1904
  }
1529
1905
  };
1530
- function createQwenAdapter(config) {
1531
- return new QwenAdapter(config);
1906
+ function createQwenProtocol(config) {
1907
+ return new QwenProtocol(config);
1532
1908
  }
1533
1909
 
1534
- // src/providers/adapters/gemini.ts
1535
- var GEMINI_MODELS = [
1536
- GEMINI_2_5_FLASH.id,
1537
- GEMINI_2_5_PRO.id,
1538
- GEMINI_2_0_FLASH.id,
1539
- GEMINI_2_0_FLASH_LITE.id,
1540
- GEMINI_3_PRO.id
1541
- ];
1542
- var GeminiAdapter = class {
1910
+ // src/providers/protocols/gemini.ts
1911
+ var logger5 = DebugLogger.module("GeminiProtocol");
1912
+ var DEFAULT_GEMINI_URL = "https://generativelanguage.googleapis.com/v1beta";
1913
+ var GeminiProtocol = class {
1543
1914
  name = "gemini";
1544
- supportedModels = GEMINI_MODELS;
1545
- client = null;
1546
1915
  apiKey;
1916
+ apiUrl;
1547
1917
  constructor(config) {
1548
1918
  this.apiKey = config.apiKey;
1919
+ this.apiUrl = config.apiUrl ?? DEFAULT_GEMINI_URL;
1549
1920
  }
1550
- /** 懒加载客户端 */
1551
- async getClient() {
1552
- if (!this.client) {
1553
- const { GoogleGenAI: GoogleGenAI2 } = await import('@google/genai');
1554
- this.client = new GoogleGenAI2({ apiKey: this.apiKey });
1921
+ /**
1922
+ * 发送请求并返回原始事件流
1923
+ */
1924
+ async *stream(messages, tools2, options) {
1925
+ const requestBody = this.buildRequestBody(messages, tools2, options);
1926
+ const url = `${this.apiUrl}/models/${options.model}:streamGenerateContent?key=${this.apiKey}&alt=sse`;
1927
+ logger5.debug("\u53D1\u9001 Gemini \u8BF7\u6C42", {
1928
+ url: url.replace(this.apiKey, "***"),
1929
+ model: options.model,
1930
+ enableSearch: options.enableSearch,
1931
+ enableThinking: options.enableThinking,
1932
+ toolsCount: tools2.length
1933
+ });
1934
+ const response = await fetch(url, {
1935
+ method: "POST",
1936
+ headers: { "Content-Type": "application/json" },
1937
+ body: JSON.stringify(requestBody),
1938
+ signal: options.signal
1939
+ });
1940
+ if (!response.ok) {
1941
+ const errorText = await response.text();
1942
+ logger5.error("Gemini API \u9519\u8BEF", { status: response.status, body: errorText.slice(0, 500) });
1943
+ yield { type: "error", error: `Gemini API \u9519\u8BEF: ${response.status} - ${errorText}` };
1944
+ return;
1555
1945
  }
1556
- return this.client;
1557
- }
1558
- supportsModel(model) {
1559
- return this.supportedModels.some((m) => model.includes(m) || m.includes(model)) || model.startsWith("gemini-");
1946
+ const reader = response.body?.getReader();
1947
+ if (!reader) {
1948
+ yield { type: "error", error: "\u65E0\u6CD5\u83B7\u53D6\u54CD\u5E94\u6D41" };
1949
+ return;
1950
+ }
1951
+ yield* this.parseSSE(reader);
1560
1952
  }
1561
1953
  /**
1562
- * 单次流式调用
1954
+ * 构建请求体
1563
1955
  */
1564
- async *streamOnce(messages, tools2, options) {
1565
- const client = await this.getClient();
1566
- const geminiMessages = this.convertMessages(messages);
1567
- const generateConfig = {};
1568
- const toolsConfig = [];
1956
+ buildRequestBody(messages, tools2, options) {
1957
+ const { systemInstruction, contents } = this.convertMessages(messages);
1958
+ const body = {
1959
+ contents,
1960
+ generationConfig: {
1961
+ maxOutputTokens: options.familyConfig.defaultMaxTokens ?? 65536
1962
+ }
1963
+ };
1964
+ if (systemInstruction) {
1965
+ body.systemInstruction = systemInstruction;
1966
+ }
1967
+ if (options.enableThinking) {
1968
+ body.generationConfig.thinkingConfig = {
1969
+ thinkingBudget: 24576,
1970
+ includeThoughts: true
1971
+ };
1972
+ }
1973
+ const geminiTools = [];
1569
1974
  if (tools2.length > 0) {
1570
- toolsConfig.push({
1975
+ geminiTools.push({
1571
1976
  functionDeclarations: tools2.map((t) => ({
1572
1977
  name: t.name,
1573
1978
  description: t.description,
1574
1979
  parameters: t.parameters
1575
1980
  }))
1576
1981
  });
1982
+ } else if (options.enableSearch) {
1983
+ geminiTools.push({ googleSearch: {} });
1577
1984
  }
1578
- if (options.enableSearch) {
1579
- toolsConfig.push({ googleSearch: {} });
1580
- }
1581
- if (toolsConfig.length > 0) {
1582
- generateConfig.tools = toolsConfig;
1985
+ if (geminiTools.length > 0) {
1986
+ body.tools = geminiTools;
1583
1987
  }
1584
- if (options.enableThinking) {
1585
- generateConfig.thinkingConfig = {
1586
- thinkingLevel: "high",
1587
- includeThoughts: true
1588
- };
1589
- }
1590
- try {
1591
- const streamResponse = await client.models.generateContentStream({
1592
- model: options.model,
1593
- contents: geminiMessages,
1594
- config: generateConfig
1595
- });
1596
- const pendingToolCalls = [];
1597
- let toolCallCounter = 0;
1598
- let hasThinking = false;
1599
- let thinkingDone = false;
1600
- for await (const chunk of streamResponse) {
1601
- if (options.signal.aborted) {
1602
- yield { type: "error", error: "\u8BF7\u6C42\u5DF2\u53D6\u6D88" };
1603
- return;
1988
+ return body;
1989
+ }
1990
+ /**
1991
+ * 转换消息格式
1992
+ */
1993
+ convertMessages(messages) {
1994
+ let systemInstruction;
1995
+ const contents = [];
1996
+ for (const msg of messages) {
1997
+ switch (msg.role) {
1998
+ case "system":
1999
+ systemInstruction = { parts: [{ text: msg.content }] };
2000
+ break;
2001
+ case "user": {
2002
+ const textContent = msg.content || (msg.images?.length ? "\u8BF7\u5206\u6790\u8FD9\u5F20\u56FE\u7247" : "");
2003
+ const parts = [{ text: textContent }];
2004
+ if (msg.images?.length) {
2005
+ for (const img of msg.images) {
2006
+ if (img.startsWith("data:")) {
2007
+ const match = img.match(/^data:([^;]+);base64,(.+)$/);
2008
+ if (match) {
2009
+ parts.push({
2010
+ inlineData: { mimeType: match[1], data: match[2] }
2011
+ });
2012
+ }
2013
+ } else {
2014
+ parts.push({
2015
+ inlineData: { mimeType: "image/jpeg", data: img }
2016
+ });
2017
+ }
2018
+ }
2019
+ }
2020
+ contents.push({ role: "user", parts });
2021
+ break;
1604
2022
  }
1605
- const candidate = chunk.candidates?.[0];
1606
- if (!candidate) continue;
1607
- const parts = candidate.content?.parts || [];
1608
- for (const part of parts) {
1609
- if (part.thought && part.text) {
1610
- hasThinking = true;
1611
- yield { type: "thinking", thinking: part.text };
1612
- continue;
2023
+ case "assistant":
2024
+ if (msg.toolCalls?.length) {
2025
+ const parts = [];
2026
+ for (const tc of msg.toolCalls) {
2027
+ const funcPart = {
2028
+ functionCall: {
2029
+ name: tc.name,
2030
+ args: JSON.parse(tc.arguments || "{}")
2031
+ }
2032
+ };
2033
+ if (tc.thoughtSignature) {
2034
+ funcPart.thoughtSignature = tc.thoughtSignature;
2035
+ }
2036
+ parts.push(funcPart);
2037
+ }
2038
+ contents.push({ role: "model", parts });
2039
+ } else {
2040
+ contents.push({ role: "model", parts: [{ text: msg.content }] });
1613
2041
  }
1614
- if (part.text && !part.thought) {
1615
- if (hasThinking && !thinkingDone) {
1616
- thinkingDone = true;
1617
- yield { type: "thinking_done" };
2042
+ break;
2043
+ case "tool":
2044
+ contents.push({
2045
+ role: "user",
2046
+ parts: [{
2047
+ functionResponse: {
2048
+ name: msg.toolName || "unknown",
2049
+ response: { result: msg.content }
2050
+ }
2051
+ }]
2052
+ });
2053
+ break;
2054
+ }
2055
+ }
2056
+ return { systemInstruction, contents };
2057
+ }
2058
+ /**
2059
+ * 解析 SSE 流
2060
+ */
2061
+ async *parseSSE(reader) {
2062
+ const decoder = new TextDecoder();
2063
+ let buffer = "";
2064
+ const pendingToolCalls = /* @__PURE__ */ new Map();
2065
+ let textStarted = false;
2066
+ let toolCallIndex = 0;
2067
+ while (true) {
2068
+ const { done, value } = await reader.read();
2069
+ if (done) break;
2070
+ buffer += decoder.decode(value, { stream: true });
2071
+ const lines = buffer.split("\n");
2072
+ buffer = lines.pop() || "";
2073
+ for (const line of lines) {
2074
+ if (!line.startsWith("data:")) continue;
2075
+ const data = line.slice(5).trim();
2076
+ if (!data) continue;
2077
+ try {
2078
+ const json = JSON.parse(data);
2079
+ const candidate = json.candidates?.[0];
2080
+ if (!candidate?.content?.parts) continue;
2081
+ for (const part of candidate.content.parts) {
2082
+ if (part.text) {
2083
+ if (part.thought === true) {
2084
+ yield { type: "thinking_delta", delta: part.text };
2085
+ } else {
2086
+ if (!textStarted) {
2087
+ textStarted = true;
2088
+ yield { type: "thinking_done" };
2089
+ }
2090
+ yield { type: "text_delta", delta: part.text };
2091
+ }
2092
+ }
2093
+ if (part.functionCall) {
2094
+ const callId = `gemini-${toolCallIndex++}`;
2095
+ const toolCall = {
2096
+ id: callId,
2097
+ name: part.functionCall.name,
2098
+ arguments: JSON.stringify(part.functionCall.args || {})
2099
+ };
2100
+ if (part.thoughtSignature) {
2101
+ toolCall.thoughtSignature = part.thoughtSignature;
2102
+ }
2103
+ pendingToolCalls.set(callId, toolCall);
2104
+ yield { type: "tool_call_start", toolCall: { id: callId, name: toolCall.name } };
2105
+ yield { type: "tool_call_done", toolCall };
2106
+ }
2107
+ }
2108
+ const groundingMeta = candidate.groundingMetadata;
2109
+ if (groundingMeta?.groundingChunks?.length) {
2110
+ const searchResults = [];
2111
+ for (const chunk of groundingMeta.groundingChunks) {
2112
+ if (chunk.web?.uri) {
2113
+ searchResults.push({
2114
+ title: chunk.web.title || "",
2115
+ url: chunk.web.uri,
2116
+ snippet: ""
2117
+ });
2118
+ }
2119
+ }
2120
+ if (searchResults.length > 0) {
2121
+ yield { type: "search_result", searchResults };
2122
+ }
2123
+ }
2124
+ if (candidate.finishReason) {
2125
+ if (pendingToolCalls.size > 0) {
2126
+ yield { type: "done", finishReason: "tool_calls" };
2127
+ } else {
2128
+ yield { type: "done", finishReason: "stop" };
2129
+ }
2130
+ return;
2131
+ }
2132
+ } catch {
2133
+ }
2134
+ }
2135
+ }
2136
+ if (pendingToolCalls.size > 0) {
2137
+ yield { type: "done", finishReason: "tool_calls" };
2138
+ } else {
2139
+ yield { type: "done", finishReason: "stop" };
2140
+ }
2141
+ }
2142
+ };
2143
+ function createGeminiProtocol(config) {
2144
+ return new GeminiProtocol(config);
2145
+ }
2146
+
2147
+ // src/providers/protocols/openai.ts
2148
+ var logger6 = DebugLogger.module("OpenAIProtocol");
2149
+ var OpenAIProtocol = class {
2150
+ name = "openai";
2151
+ apiKey;
2152
+ apiUrl;
2153
+ constructor(config) {
2154
+ this.apiKey = config.apiKey;
2155
+ this.apiUrl = config.apiUrl ?? DEFAULT_OPENROUTER_URL;
2156
+ }
2157
+ async *stream(messages, tools2, options) {
2158
+ const requestBody = this.buildRequestBody(messages, tools2, options);
2159
+ const url = `${this.apiUrl}/responses`;
2160
+ logger6.debug("\u53D1\u9001 OpenAI \u8BF7\u6C42", {
2161
+ url,
2162
+ model: options.model,
2163
+ toolsCount: tools2.length
2164
+ });
2165
+ const response = await fetch(url, {
2166
+ method: "POST",
2167
+ headers: {
2168
+ "Authorization": `Bearer ${this.apiKey}`,
2169
+ "Content-Type": "application/json",
2170
+ "HTTP-Referer": "https://ai-chat.local",
2171
+ "X-Title": "AI Chat"
2172
+ },
2173
+ body: JSON.stringify(requestBody),
2174
+ signal: options.signal
2175
+ });
2176
+ if (!response.ok) {
2177
+ const errorText = await response.text();
2178
+ logger6.error("OpenAI API \u9519\u8BEF", { status: response.status, body: errorText.slice(0, 500) });
2179
+ yield { type: "error", error: `OpenAI API \u9519\u8BEF: ${response.status} - ${errorText}` };
2180
+ return;
2181
+ }
2182
+ const reader = response.body?.getReader();
2183
+ if (!reader) {
2184
+ yield { type: "error", error: "\u65E0\u6CD5\u83B7\u53D6\u54CD\u5E94\u6D41" };
2185
+ return;
2186
+ }
2187
+ yield* this.parseSSE(reader, options.enableThinking);
2188
+ }
2189
+ /**
2190
+ * 构建请求体(OpenAI/GPT 专用)
2191
+ */
2192
+ buildRequestBody(messages, tools2, options) {
2193
+ const input = this.convertMessages(messages);
2194
+ const body = {
2195
+ model: options.model,
2196
+ stream: true,
2197
+ input
2198
+ };
2199
+ if (options.enableThinking) {
2200
+ body.reasoning = { effort: "high" };
2201
+ }
2202
+ if (tools2.length > 0) {
2203
+ body.tools = tools2.map((t) => ({
2204
+ type: "function",
2205
+ name: t.name,
2206
+ description: t.description,
2207
+ parameters: t.parameters
2208
+ }));
2209
+ }
2210
+ return body;
2211
+ }
2212
+ convertMessages(messages) {
2213
+ const input = [];
2214
+ for (const msg of messages) {
2215
+ switch (msg.role) {
2216
+ case "system":
2217
+ input.push({ role: "system", content: msg.content });
2218
+ break;
2219
+ case "user": {
2220
+ const textContent = msg.content || (msg.images?.length ? "\u8BF7\u5206\u6790\u8FD9\u5F20\u56FE\u7247" : "");
2221
+ const content = [{ type: "input_text", text: textContent }];
2222
+ if (msg.images?.length) {
2223
+ for (const img of msg.images) {
2224
+ content.push({
2225
+ type: "input_image",
2226
+ image_url: img.startsWith("data:") ? img : `data:image/jpeg;base64,${img}`,
2227
+ detail: "auto"
2228
+ });
1618
2229
  }
1619
- yield { type: "text", text: part.text };
1620
2230
  }
1621
- if (part.functionCall && part.functionCall.name) {
1622
- pendingToolCalls.push({
1623
- id: `call_${Date.now()}_${toolCallCounter++}`,
1624
- name: part.functionCall.name,
1625
- arguments: JSON.stringify(part.functionCall.args || {})
2231
+ input.push({ role: "user", content });
2232
+ break;
2233
+ }
2234
+ case "assistant":
2235
+ if (msg.toolCalls?.length) {
2236
+ for (const tc of msg.toolCalls) {
2237
+ input.push({
2238
+ type: "function_call",
2239
+ call_id: tc.id,
2240
+ name: tc.name,
2241
+ arguments: tc.arguments
2242
+ });
2243
+ }
2244
+ } else {
2245
+ input.push({
2246
+ role: "developer",
2247
+ content: `[\u4E0A\u4E00\u8F6EAI\u56DE\u590D]: ${msg.content}`
1626
2248
  });
1627
2249
  }
2250
+ break;
2251
+ case "tool":
2252
+ input.push({
2253
+ type: "function_call_output",
2254
+ call_id: msg.toolCallId,
2255
+ output: msg.content
2256
+ });
2257
+ break;
2258
+ }
2259
+ }
2260
+ return input;
2261
+ }
2262
+ /**
2263
+ * 解析 SSE 流(OpenAI/GPT 专用)
2264
+ */
2265
+ async *parseSSE(reader, enableThinking) {
2266
+ const decoder = new TextDecoder();
2267
+ let buffer = "";
2268
+ const pendingToolCalls = /* @__PURE__ */ new Map();
2269
+ let currentFunctionCallId = null;
2270
+ const searchResults = [];
2271
+ let streamDone = false;
2272
+ let thinkingDone = false;
2273
+ let textStarted = false;
2274
+ while (true) {
2275
+ const { done, value } = await reader.read();
2276
+ if (done) break;
2277
+ buffer += decoder.decode(value, { stream: true });
2278
+ const lines = buffer.split("\n");
2279
+ buffer = lines.pop() || "";
2280
+ for (const line of lines) {
2281
+ if (streamDone) continue;
2282
+ if (!line.startsWith("data:")) continue;
2283
+ const data = line.slice(5).trim();
2284
+ if (data === "[DONE]") {
2285
+ streamDone = true;
2286
+ if (pendingToolCalls.size > 0) {
2287
+ for (const tc of pendingToolCalls.values()) {
2288
+ yield { type: "tool_call_done", toolCall: tc };
2289
+ }
2290
+ yield { type: "done", finishReason: "tool_calls" };
2291
+ } else {
2292
+ yield { type: "done", finishReason: "stop" };
2293
+ }
2294
+ return;
2295
+ }
2296
+ try {
2297
+ const event = JSON.parse(data);
2298
+ logger6.debug("SSE \u4E8B\u4EF6", { type: event.type, event: JSON.stringify(event).slice(0, 200) });
2299
+ switch (event.type) {
2300
+ // ========== Responses API 格式 ==========
2301
+ case "response.output_item.added": {
2302
+ const item = event.item;
2303
+ const callId = item?.call_id || item?.id;
2304
+ if (item?.type === "function_call" && callId) {
2305
+ currentFunctionCallId = callId;
2306
+ let args = item.arguments || item.input || "";
2307
+ if (typeof args === "object") {
2308
+ args = JSON.stringify(args);
2309
+ }
2310
+ pendingToolCalls.set(callId, {
2311
+ id: callId,
2312
+ name: item.name || item.function?.name || "",
2313
+ arguments: args
2314
+ });
2315
+ logger6.debug("\u5DE5\u5177\u8C03\u7528\u5F00\u59CB", { id: callId, name: item.name, args: args.slice(0, 100) });
2316
+ yield { type: "tool_call_start", toolCall: { id: callId, name: item.name || item.function?.name || "" } };
2317
+ }
2318
+ break;
2319
+ }
2320
+ case "response.function_call_arguments.delta": {
2321
+ const delta = event.delta;
2322
+ if (currentFunctionCallId && delta) {
2323
+ const call = pendingToolCalls.get(currentFunctionCallId);
2324
+ if (call) {
2325
+ call.arguments += delta;
2326
+ yield { type: "tool_call_delta", toolCall: { id: currentFunctionCallId, arguments: delta } };
2327
+ }
2328
+ }
2329
+ break;
2330
+ }
2331
+ case "response.function_call_arguments.done": {
2332
+ const callId = event.item_id || event.call_id || currentFunctionCallId;
2333
+ if (callId) {
2334
+ const existing = pendingToolCalls.get(callId);
2335
+ if (existing) {
2336
+ if (event.arguments) {
2337
+ existing.arguments = event.arguments;
2338
+ }
2339
+ try {
2340
+ JSON.parse(existing.arguments);
2341
+ } catch {
2342
+ logger6.warn("\u5DE5\u5177\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF0C\u4F7F\u7528\u7A7A\u5BF9\u8C61", { args: existing.arguments.slice(0, 100) });
2343
+ existing.arguments = "{}";
2344
+ }
2345
+ }
2346
+ }
2347
+ break;
2348
+ }
2349
+ case "response.output_item.done": {
2350
+ const item = event.item;
2351
+ const callId = item?.call_id || item?.id;
2352
+ if (item?.type === "function_call" && callId) {
2353
+ const existing = pendingToolCalls.get(callId);
2354
+ let args = item.arguments || item.input || existing?.arguments || "{}";
2355
+ if (typeof args === "object") {
2356
+ args = JSON.stringify(args);
2357
+ }
2358
+ pendingToolCalls.set(callId, {
2359
+ id: callId,
2360
+ name: item.name || item.function?.name || existing?.name || "",
2361
+ arguments: args
2362
+ });
2363
+ logger6.debug("\u5DE5\u5177\u8C03\u7528\u5B8C\u6210", { id: callId, name: item.name, args: args.slice(0, 100) });
2364
+ }
2365
+ break;
2366
+ }
2367
+ case "response.output_text.annotation.added": {
2368
+ const annotation = event.annotation;
2369
+ if (annotation?.url) {
2370
+ const exists = searchResults.some((r) => r.url === annotation.url);
2371
+ if (!exists) {
2372
+ searchResults.push({
2373
+ title: annotation.title || annotation.text || "",
2374
+ url: annotation.url,
2375
+ snippet: annotation.summary || annotation.snippet || ""
2376
+ });
2377
+ yield { type: "search_result", searchResults: [...searchResults] };
2378
+ }
2379
+ }
2380
+ break;
2381
+ }
2382
+ case "response.output_text.delta":
2383
+ if (event.delta) {
2384
+ if (!textStarted) {
2385
+ textStarted = true;
2386
+ if (enableThinking && !thinkingDone) {
2387
+ thinkingDone = true;
2388
+ yield { type: "thinking_done" };
2389
+ }
2390
+ }
2391
+ yield { type: "text_delta", delta: event.delta };
2392
+ }
2393
+ break;
2394
+ // GPT-5.x 支持 reasoning
2395
+ case "response.reasoning_summary_text.delta":
2396
+ if (enableThinking && event.delta && !thinkingDone) {
2397
+ yield { type: "thinking_delta", delta: event.delta };
2398
+ }
2399
+ break;
2400
+ // response.completed 事件(包含完整的工具调用参数)
2401
+ case "response.completed": {
2402
+ const response = event.response;
2403
+ if (response?.output?.length) {
2404
+ for (const item of response.output) {
2405
+ if (item.type === "function_call" && item.call_id) {
2406
+ const existing = pendingToolCalls.get(item.call_id);
2407
+ let args = item.arguments || existing?.arguments || "{}";
2408
+ if (typeof args === "object") {
2409
+ args = JSON.stringify(args);
2410
+ }
2411
+ pendingToolCalls.set(item.call_id, {
2412
+ id: item.call_id,
2413
+ name: item.name || existing?.name || "",
2414
+ arguments: args
2415
+ });
2416
+ logger6.debug("response.completed: \u66F4\u65B0\u5DE5\u5177\u53C2\u6570", { id: item.call_id, args: args.slice(0, 100) });
2417
+ }
2418
+ }
2419
+ }
2420
+ streamDone = true;
2421
+ if (pendingToolCalls.size > 0) {
2422
+ for (const tc of pendingToolCalls.values()) {
2423
+ logger6.debug("response.completed: \u53D1\u51FA tool_call_done", tc);
2424
+ yield { type: "tool_call_done", toolCall: tc };
2425
+ }
2426
+ yield { type: "done", finishReason: "tool_calls" };
2427
+ } else {
2428
+ yield { type: "done", finishReason: "stop" };
2429
+ }
2430
+ return;
2431
+ }
2432
+ }
2433
+ } catch (e) {
2434
+ logger6.warn("SSE \u89E3\u6790\u9519\u8BEF", { line: line.slice(0, 100), error: String(e) });
1628
2435
  }
1629
2436
  }
1630
- if (pendingToolCalls.length > 0) {
1631
- for (const tc of pendingToolCalls) {
1632
- yield { type: "tool_call", toolCall: tc };
2437
+ }
2438
+ if (!streamDone) {
2439
+ logger6.debug("\u6D41\u7ED3\u675F\uFF0C\u6267\u884C\u515C\u5E95\u903B\u8F91", { pendingToolCalls: pendingToolCalls.size });
2440
+ if (pendingToolCalls.size > 0) {
2441
+ for (const tc of pendingToolCalls.values()) {
2442
+ yield { type: "tool_call_done", toolCall: tc };
1633
2443
  }
1634
2444
  yield { type: "done", finishReason: "tool_calls" };
1635
2445
  } else {
1636
2446
  yield { type: "done", finishReason: "stop" };
1637
2447
  }
1638
- } catch (error) {
1639
- const errorStr = String(error);
1640
- if (errorStr.includes("User location is not supported") || errorStr.includes("FAILED_PRECONDITION")) {
1641
- yield {
1642
- type: "error",
1643
- error: "Gemini API \u4E0D\u652F\u6301\u5F53\u524D\u5730\u533A\uFF08\u4E2D\u56FD\u5927\u9646\u65E0\u6CD5\u8BBF\u95EE\uFF09\u3002\u8BF7\u4F7F\u7528\u5176\u4ED6\u6A21\u578B\u3002"
2448
+ }
2449
+ }
2450
+ };
2451
+ function createOpenAIProtocol(config) {
2452
+ return new OpenAIProtocol(config);
2453
+ }
2454
+ var logger7 = DebugLogger.module("AnthropicProtocol");
2455
+ var AnthropicProtocol = class {
2456
+ name = "anthropic";
2457
+ gateway;
2458
+ constructor(config) {
2459
+ this.gateway = createGateway({
2460
+ apiKey: config.apiKey
2461
+ });
2462
+ }
2463
+ async *stream(messages, tools2, options) {
2464
+ logger7.debug("\u4F7F\u7528 Vercel AI Gateway \u8C03\u7528 Claude", {
2465
+ model: options.model,
2466
+ enableThinking: options.enableThinking,
2467
+ toolsCount: tools2.length
2468
+ });
2469
+ try {
2470
+ const sdkMessages = this.convertMessages(messages);
2471
+ const streamParams = {
2472
+ model: this.gateway(options.model),
2473
+ messages: sdkMessages,
2474
+ maxOutputTokens: options.familyConfig.defaultMaxTokens,
2475
+ abortSignal: options.signal
2476
+ };
2477
+ if (tools2.length > 0) {
2478
+ streamParams.tools = this.convertTools(tools2);
2479
+ }
2480
+ if (options.enableThinking) {
2481
+ streamParams.providerOptions = {
2482
+ anthropic: {
2483
+ thinking: {
2484
+ type: "enabled",
2485
+ budgetTokens: 1e4
2486
+ }
2487
+ }
1644
2488
  };
1645
- } else {
1646
- yield { type: "error", error: errorStr };
1647
2489
  }
2490
+ logger7.debug("\u53D1\u9001\u8BF7\u6C42\u5230 Vercel AI Gateway", {
2491
+ model: options.model,
2492
+ messagesCount: sdkMessages.length,
2493
+ toolsCount: tools2.length,
2494
+ enableThinking: options.enableThinking
2495
+ });
2496
+ const result = streamText(streamParams);
2497
+ yield* this.parseStream(result);
2498
+ } catch (error) {
2499
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
2500
+ logger7.error("Vercel AI Gateway \u9519\u8BEF", { error: errorMessage });
2501
+ yield { type: "error", error: errorMessage };
1648
2502
  }
1649
2503
  }
1650
2504
  /**
1651
- * 转换标准消息为 Gemini 格式
2505
+ * 转换消息格式(ProtocolMessage AI SDK CoreMessage)
2506
+ * 简化版:主要处理文本消息,工具调用通过历史上下文传递
1652
2507
  */
1653
2508
  convertMessages(messages) {
1654
- const geminiMessages = [];
2509
+ const result = [];
1655
2510
  for (const msg of messages) {
1656
2511
  switch (msg.role) {
1657
2512
  case "system":
1658
- geminiMessages.push(
1659
- { role: "user", parts: [{ text: msg.content }] },
1660
- { role: "model", parts: [{ text: "OK" }] }
1661
- );
2513
+ result.push({ role: "system", content: msg.content });
1662
2514
  break;
1663
2515
  case "user": {
1664
- const parts = [{ text: msg.content }];
2516
+ const textContent = msg.content || (msg.images?.length ? "\u8BF7\u5206\u6790\u8FD9\u5F20\u56FE\u7247" : "");
1665
2517
  if (msg.images?.length) {
2518
+ const content = [
2519
+ { type: "text", text: textContent }
2520
+ ];
1666
2521
  for (const img of msg.images) {
1667
- const base64 = img.startsWith("data:") ? img.split(",")[1] : img;
1668
- parts.push({
1669
- inlineData: {
1670
- mimeType: "image/jpeg",
1671
- data: base64
1672
- }
2522
+ content.push({
2523
+ type: "image",
2524
+ image: img.startsWith("data:") ? img : `data:image/jpeg;base64,${img}`
1673
2525
  });
1674
2526
  }
2527
+ result.push({ role: "user", content });
2528
+ } else {
2529
+ result.push({ role: "user", content: textContent });
1675
2530
  }
1676
- geminiMessages.push({ role: "user", parts });
1677
2531
  break;
1678
2532
  }
1679
2533
  case "assistant":
1680
- if (msg.toolCalls?.length) {
1681
- const parts = [];
1682
- if (msg.content) {
1683
- parts.push({ text: msg.content });
1684
- }
1685
- for (const tc of msg.toolCalls) {
1686
- parts.push({
1687
- functionCall: {
1688
- name: tc.name,
1689
- args: JSON.parse(tc.arguments || "{}")
1690
- }
1691
- });
1692
- }
1693
- geminiMessages.push({ role: "model", parts });
1694
- } else {
1695
- geminiMessages.push({
1696
- role: "model",
1697
- parts: [{ text: msg.content }]
1698
- });
2534
+ if (msg.content) {
2535
+ result.push({ role: "assistant", content: msg.content });
1699
2536
  }
1700
2537
  break;
1701
2538
  case "tool":
1702
- geminiMessages.push({
2539
+ result.push({
1703
2540
  role: "user",
1704
- parts: [{
1705
- functionResponse: {
1706
- name: msg.toolName || "unknown_tool",
1707
- response: { result: msg.content }
1708
- }
1709
- }]
2541
+ content: `[\u5DE5\u5177 ${msg.toolName || "unknown"} \u8FD4\u56DE\u7ED3\u679C]: ${msg.content}`
1710
2542
  });
1711
2543
  break;
1712
2544
  }
1713
2545
  }
1714
- return geminiMessages;
2546
+ return result;
2547
+ }
2548
+ /**
2549
+ * 转换工具格式(ProtocolToolDefinition → AI SDK tools)
2550
+ * 注意:Vercel Gateway 对工具格式有特殊要求,这里使用标准格式
2551
+ */
2552
+ convertTools(tools2) {
2553
+ const result = {};
2554
+ for (const t of tools2) {
2555
+ const zodSchema = this.jsonSchemaToZod(t.parameters);
2556
+ result[t.name] = tool$1({
2557
+ description: t.description,
2558
+ inputSchema: zodSchema
2559
+ });
2560
+ }
2561
+ return result;
2562
+ }
2563
+ /**
2564
+ * 将 JSON Schema 转换为 Zod Schema
2565
+ */
2566
+ jsonSchemaToZod(schema) {
2567
+ const shape = {};
2568
+ for (const [key, prop] of Object.entries(schema.properties)) {
2569
+ let zodType;
2570
+ switch (prop.type) {
2571
+ case "string":
2572
+ zodType = prop.enum ? z.enum(prop.enum) : z.string();
2573
+ break;
2574
+ case "number":
2575
+ case "integer":
2576
+ zodType = z.number();
2577
+ break;
2578
+ case "boolean":
2579
+ zodType = z.boolean();
2580
+ break;
2581
+ case "array":
2582
+ zodType = z.array(z.string());
2583
+ break;
2584
+ default:
2585
+ zodType = z.unknown();
2586
+ }
2587
+ if (prop.description) {
2588
+ zodType = zodType.describe(prop.description);
2589
+ }
2590
+ if (!schema.required.includes(key)) {
2591
+ zodType = zodType.optional();
2592
+ }
2593
+ shape[key] = zodType;
2594
+ }
2595
+ return z.object(shape);
2596
+ }
2597
+ /**
2598
+ * 解析 AI SDK 流式响应
2599
+ */
2600
+ async *parseStream(result) {
2601
+ const toolCalls = /* @__PURE__ */ new Map();
2602
+ let hasThinking = false;
2603
+ let thinkingDone = false;
2604
+ try {
2605
+ for await (const part of result.fullStream) {
2606
+ switch (part.type) {
2607
+ case "reasoning-delta":
2608
+ if ("text" in part && part.text) {
2609
+ hasThinking = true;
2610
+ yield { type: "thinking_delta", delta: part.text };
2611
+ }
2612
+ break;
2613
+ case "reasoning-end":
2614
+ if (hasThinking && !thinkingDone) {
2615
+ thinkingDone = true;
2616
+ yield { type: "thinking_done" };
2617
+ }
2618
+ break;
2619
+ case "text-delta":
2620
+ if (hasThinking && !thinkingDone) {
2621
+ thinkingDone = true;
2622
+ yield { type: "thinking_done" };
2623
+ }
2624
+ if ("text" in part && part.text) {
2625
+ yield { type: "text_delta", delta: part.text };
2626
+ }
2627
+ break;
2628
+ case "tool-call":
2629
+ if ("toolCallId" in part && "toolName" in part) {
2630
+ const toolCallId = part.toolCallId;
2631
+ const toolName = part.toolName;
2632
+ const input = "input" in part ? part.input : null;
2633
+ const args = input ? typeof input === "string" ? input : JSON.stringify(input) : "{}";
2634
+ yield {
2635
+ type: "tool_call_start",
2636
+ toolCall: { id: toolCallId, name: toolName }
2637
+ };
2638
+ toolCalls.set(toolCallId, {
2639
+ id: toolCallId,
2640
+ name: toolName,
2641
+ arguments: args
2642
+ });
2643
+ yield {
2644
+ type: "tool_call_done",
2645
+ toolCall: {
2646
+ id: toolCallId,
2647
+ name: toolName,
2648
+ arguments: args
2649
+ }
2650
+ };
2651
+ }
2652
+ break;
2653
+ case "finish":
2654
+ const finishReason = toolCalls.size > 0 ? "tool_calls" : "finishReason" in part && part.finishReason === "stop" ? "stop" : "finishReason" in part && part.finishReason === "length" ? "length" : "stop";
2655
+ yield { type: "done", finishReason };
2656
+ return;
2657
+ }
2658
+ }
2659
+ yield { type: "done", finishReason: "stop" };
2660
+ } catch (error) {
2661
+ const errorMessage = error instanceof Error ? error.message : "Stream error";
2662
+ logger7.error("\u6D41\u5F0F\u5904\u7406\u9519\u8BEF", { error: errorMessage });
2663
+ yield { type: "error", error: errorMessage };
2664
+ }
2665
+ }
2666
+ };
2667
+ function createAnthropicProtocol(config) {
2668
+ return new AnthropicProtocol(config);
2669
+ }
2670
+
2671
+ // src/providers/unified-adapter.ts
2672
+ var logger8 = DebugLogger.module("UnifiedAdapter");
2673
+ var UnifiedAdapter = class {
2674
+ name = "unified";
2675
+ /** 支持的模型列表(从 Registry 获取) */
2676
+ get supportedModels() {
2677
+ return getVisibleModels().map((m) => m.id);
2678
+ }
2679
+ protocols = /* @__PURE__ */ new Map();
2680
+ constructor(config) {
2681
+ if (config.arkApiKey) {
2682
+ this.protocols.set("ark", createArkProtocol({
2683
+ apiKey: config.arkApiKey,
2684
+ apiUrl: config.arkApiUrl
2685
+ }));
2686
+ this.protocols.set("deepseek", createDeepSeekProtocol({
2687
+ apiKey: config.arkApiKey,
2688
+ apiUrl: config.arkApiUrl
2689
+ }));
2690
+ }
2691
+ if (config.qwenApiKey) {
2692
+ this.protocols.set("qwen", createQwenProtocol({
2693
+ apiKey: config.qwenApiKey,
2694
+ apiUrl: config.qwenApiUrl
2695
+ }));
2696
+ }
2697
+ if (config.geminiApiKey) {
2698
+ this.protocols.set("gemini", createGeminiProtocol({
2699
+ apiKey: config.geminiApiKey,
2700
+ apiUrl: config.geminiApiUrl
2701
+ }));
2702
+ }
2703
+ if (config.openrouterApiKey) {
2704
+ this.protocols.set("openai", createOpenAIProtocol({
2705
+ apiKey: config.openrouterApiKey,
2706
+ apiUrl: config.openrouterApiUrl
2707
+ }));
2708
+ }
2709
+ if (config.vercelApiKey) {
2710
+ this.protocols.set("anthropic", createAnthropicProtocol({
2711
+ apiKey: config.vercelApiKey
2712
+ }));
2713
+ }
2714
+ }
2715
+ /**
2716
+ * 检查是否支持指定模型
2717
+ */
2718
+ supportsModel(model) {
2719
+ const protocol = getModelProtocol(model);
2720
+ return protocol ? this.protocols.has(protocol) : false;
2721
+ }
2722
+ /**
2723
+ * 获取模型的家族配置
2724
+ */
2725
+ getModelFamilyConfig(model) {
2726
+ return getModelFamily(model);
2727
+ }
2728
+ /**
2729
+ * 实现 ProviderAdapter.streamOnce 接口
2730
+ *
2731
+ * @param messages 标准消息列表
2732
+ * @param tools 工具定义
2733
+ * @param options 调用选项
2734
+ * @returns 标准 StreamChunk 流
2735
+ */
2736
+ async *streamOnce(messages, tools2, options) {
2737
+ const { model, enableThinking = false, enableSearch = false, signal } = options;
2738
+ const protocolMessages = messages.map((m) => ({
2739
+ role: m.role,
2740
+ content: m.content,
2741
+ images: m.images,
2742
+ toolCalls: m.toolCalls?.map((tc) => ({
2743
+ id: tc.id,
2744
+ name: tc.name,
2745
+ arguments: tc.arguments,
2746
+ thoughtSignature: tc.thought_signature
2747
+ })),
2748
+ toolCallId: m.toolCallId,
2749
+ toolName: m.toolName
2750
+ }));
2751
+ const protocolTools = tools2;
2752
+ yield* this.stream(protocolMessages, protocolTools, {
2753
+ model,
2754
+ enableThinking,
2755
+ enableSearch,
2756
+ signal
2757
+ });
2758
+ }
2759
+ /**
2760
+ * 流式调用(内部方法)
2761
+ */
2762
+ async *stream(messages, tools2, options) {
2763
+ const { model, enableThinking = false, enableSearch = false, signal } = options;
2764
+ const protocolId = getModelProtocol(model);
2765
+ const familyConfig = getModelFamily(model);
2766
+ if (!protocolId) {
2767
+ yield { type: "error", error: `\u672A\u77E5\u6A21\u578B: ${model}` };
2768
+ return;
2769
+ }
2770
+ if (!familyConfig) {
2771
+ yield { type: "error", error: `\u6A21\u578B ${model} \u7F3A\u5C11\u5BB6\u65CF\u914D\u7F6E` };
2772
+ return;
2773
+ }
2774
+ const protocol = this.protocols.get(protocolId);
2775
+ if (!protocol) {
2776
+ yield { type: "error", error: `Protocol ${protocolId} \u672A\u914D\u7F6E` };
2777
+ return;
2778
+ }
2779
+ logger8.debug("\u5F00\u59CB\u6D41\u5F0F\u8C03\u7528", {
2780
+ model,
2781
+ protocol: protocolId,
2782
+ family: familyConfig.id,
2783
+ enableThinking,
2784
+ enableSearch
2785
+ });
2786
+ const rawStream = protocol.stream(messages, tools2, {
2787
+ model,
2788
+ familyConfig,
2789
+ enableThinking,
2790
+ enableSearch,
2791
+ signal
2792
+ });
2793
+ yield* this.transformEvents(rawStream, familyConfig, enableThinking, enableSearch);
2794
+ }
2795
+ /**
2796
+ * 将 RawEvent 转换为 StreamChunk
2797
+ *
2798
+ * 根据 FamilyConfig 处理行为差异
2799
+ */
2800
+ async *transformEvents(rawStream, familyConfig, enableThinking, enableSearch) {
2801
+ let thinkingStarted = false;
2802
+ for await (const event of rawStream) {
2803
+ switch (event.type) {
2804
+ // ========== Thinking 处理 ==========
2805
+ case "thinking_delta":
2806
+ if (enableThinking && familyConfig.supportsThinking && event.delta) {
2807
+ let delta = event.delta;
2808
+ if (!thinkingStarted) {
2809
+ delta = delta.replace(/^\n+/, "");
2810
+ if (delta) thinkingStarted = true;
2811
+ }
2812
+ if (delta) {
2813
+ yield { type: "thinking", thinking: delta };
2814
+ }
2815
+ }
2816
+ break;
2817
+ case "thinking_done":
2818
+ if (enableThinking && familyConfig.supportsThinking) {
2819
+ yield { type: "thinking_done" };
2820
+ }
2821
+ break;
2822
+ // ========== 文本处理 ==========
2823
+ case "text_delta":
2824
+ if (event.delta) {
2825
+ yield { type: "text", text: event.delta };
2826
+ }
2827
+ break;
2828
+ // ========== 工具调用处理 ==========
2829
+ case "tool_call_done":
2830
+ if (event.toolCall) {
2831
+ const toolCall = {
2832
+ id: event.toolCall.id || "",
2833
+ name: event.toolCall.name || "",
2834
+ arguments: event.toolCall.arguments || "{}"
2835
+ };
2836
+ if (event.toolCall.thoughtSignature) {
2837
+ toolCall.thought_signature = event.toolCall.thoughtSignature;
2838
+ }
2839
+ yield { type: "tool_call", toolCall };
2840
+ }
2841
+ break;
2842
+ // ========== 搜索结果处理 ==========
2843
+ case "search_result":
2844
+ if (enableSearch && event.searchResults) {
2845
+ const searchResults = event.searchResults.map((r) => ({
2846
+ title: r.title,
2847
+ url: r.url,
2848
+ snippet: r.snippet
2849
+ }));
2850
+ yield { type: "search_result", searchResults };
2851
+ }
2852
+ break;
2853
+ // ========== 完成处理 ==========
2854
+ case "done":
2855
+ yield {
2856
+ type: "done",
2857
+ finishReason: event.finishReason || "stop"
2858
+ };
2859
+ break;
2860
+ // ========== 错误处理 ==========
2861
+ case "error":
2862
+ yield { type: "error", error: event.error || "\u672A\u77E5\u9519\u8BEF" };
2863
+ break;
2864
+ }
2865
+ }
1715
2866
  }
1716
2867
  };
1717
- function createGeminiAdapter(config) {
1718
- return new GeminiAdapter(config);
2868
+ function createUnifiedAdapter(config) {
2869
+ return new UnifiedAdapter(config);
2870
+ }
2871
+
2872
+ // src/builtin-tools/web-search.ts
2873
+ function createWebSearchTool(tavilyApiKey) {
2874
+ return {
2875
+ name: "web_search",
2876
+ description: "\u8054\u7F51\u641C\u7D22\u5DE5\u5177\u3002\u8F93\u5165 query\uFF08\u641C\u7D22\u5173\u952E\u8BCD/\u95EE\u9898\uFF09\uFF0C\u8FD4\u56DE\u641C\u7D22\u7ED3\u679C\u5217\u8868\uFF08title/url/snippet\uFF09\u3002\u7528\u4E8E\u83B7\u53D6\u5B9E\u65F6\u4FE1\u606F\u4E0E\u53EF\u5F15\u7528\u6765\u6E90\u3002",
2877
+ parameters: {
2878
+ type: "object",
2879
+ properties: {
2880
+ query: { type: "string", description: "\u641C\u7D22\u5173\u952E\u8BCD\u6216\u95EE\u9898\uFF08\u5FC5\u586B\uFF09" },
2881
+ max_results: { type: "number", description: "\u6700\u5927\u8FD4\u56DE\u7ED3\u679C\u6570\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4 5\uFF09" }
2882
+ },
2883
+ required: ["query"]
2884
+ },
2885
+ // 结果类型:让前端/调试可识别(不强依赖)
2886
+ resultType: "search_results",
2887
+ execute: async (args, ctx) => {
2888
+ const query = typeof args.query === "string" ? args.query : "";
2889
+ const maxResults = typeof args.max_results === "number" && Number.isFinite(args.max_results) ? Math.max(1, Math.min(10, Math.floor(args.max_results))) : 5;
2890
+ if (!query.trim()) {
2891
+ return JSON.stringify({ query: "", results: [], error: "\u7F3A\u5C11 query" });
2892
+ }
2893
+ if (!tavilyApiKey) {
2894
+ return JSON.stringify({ query, results: [], error: "\u7F3A\u5C11 Tavily API Key" });
2895
+ }
2896
+ const resp = await fetch("https://api.tavily.com/search", {
2897
+ method: "POST",
2898
+ headers: {
2899
+ Authorization: `Bearer ${tavilyApiKey}`,
2900
+ "Content-Type": "application/json"
2901
+ },
2902
+ body: JSON.stringify({
2903
+ query,
2904
+ max_results: maxResults,
2905
+ search_depth: "basic",
2906
+ include_answer: false,
2907
+ include_raw_content: false
2908
+ }),
2909
+ signal: ctx.signal
2910
+ });
2911
+ if (!resp.ok) {
2912
+ const t = await resp.text().catch(() => "");
2913
+ return JSON.stringify({ query, results: [], error: `Tavily /search \u9519\u8BEF: ${resp.status} ${t}`.trim() });
2914
+ }
2915
+ const data = await resp.json().catch(() => null);
2916
+ const results = [];
2917
+ const arr = data && typeof data === "object" && Array.isArray(data.results) ? data.results : [];
2918
+ for (const r of arr) {
2919
+ const url = typeof r?.url === "string" ? r.url : "";
2920
+ if (!url) continue;
2921
+ const title = typeof r?.title === "string" ? r.title : "";
2922
+ const snippet = typeof r?.content === "string" ? r.content : "";
2923
+ results.push({ title, url, snippet });
2924
+ }
2925
+ return JSON.stringify({ query, results });
2926
+ }
2927
+ };
1719
2928
  }
1720
2929
 
1721
2930
  // src/agent.ts
1722
2931
  var HybridAgent = class {
1723
2932
  config;
1724
- adapters = /* @__PURE__ */ new Map();
2933
+ adapter;
1725
2934
  orchestrator;
1726
2935
  geminiClient;
1727
2936
  toolExecutor;
@@ -1735,76 +2944,58 @@ var HybridAgent = class {
1735
2944
  arkApiKey: config.arkApiKey,
1736
2945
  arkApiUrl: config.arkApiUrl || DEFAULT_ARK_URL,
1737
2946
  qwenApiKey: config.qwenApiKey || "",
1738
- qwenApiUrl: config.qwenApiUrl || DEFAULT_QWEN_NATIVE_URL,
2947
+ qwenApiUrl: config.qwenApiUrl || DEFAULT_QWEN_URL,
1739
2948
  openrouterApiKey: config.openrouterApiKey || "",
1740
2949
  openrouterApiUrl: config.openrouterApiUrl || DEFAULT_OPENROUTER_URL,
2950
+ vercelApiKey: config.vercelApiKey || "",
2951
+ tavilyApiKey: config.tavilyApiKey || "",
1741
2952
  geminiApiKey: config.geminiApiKey,
1742
- cwd: config.cwd || process.cwd(),
1743
- planPrompt: config.planPrompt || PLAN_MODE_PROMPT
2953
+ cwd: config.cwd || process.cwd()
1744
2954
  };
1745
2955
  this.geminiClient = new GoogleGenAI({ apiKey: this.config.geminiApiKey });
1746
2956
  this.toolExecutor = toolExecutor || createDefaultToolExecutor(this.config.cwd);
1747
2957
  this.toolConfig = config.tools;
1748
2958
  this.tools = /* @__PURE__ */ new Map();
1749
- this.initializeAdapters();
2959
+ this.adapter = new UnifiedAdapter({
2960
+ arkApiKey: this.config.arkApiKey,
2961
+ arkApiUrl: this.config.arkApiUrl,
2962
+ qwenApiKey: this.config.qwenApiKey,
2963
+ qwenApiUrl: this.config.qwenApiUrl,
2964
+ geminiApiKey: this.config.geminiApiKey,
2965
+ openrouterApiKey: this.config.openrouterApiKey,
2966
+ openrouterApiUrl: this.config.openrouterApiUrl,
2967
+ vercelApiKey: this.config.vercelApiKey
2968
+ });
1750
2969
  this.orchestrator = new ChatOrchestrator({
1751
2970
  maxIterations: 10,
1752
2971
  executeTool: this.executeTool.bind(this),
1753
2972
  tools: this.tools,
1754
- // 传入工具列表,用于获取 sideEffects
1755
- // autoRunConfig 在每次 chat 调用时通过 options 传递
1756
2973
  onToolApprovalRequest: config.onToolApprovalRequest,
1757
- // 传递工具批准回调
1758
2974
  getAutoRunConfig: config.getAutoRunConfig
1759
- // 传递动态获取配置回调
1760
2975
  });
1761
2976
  }
1762
2977
  /** 异步初始化工具(在第一次 chat 前调用) */
1763
2978
  async asyncInit() {
1764
2979
  if (this.toolConfig && this.tools.size === 0) {
1765
2980
  const resolvedTools = await resolveTools(this.toolConfig);
1766
- for (const tool2 of resolvedTools) {
1767
- this.tools.set(tool2.name, tool2);
2981
+ for (const tool3 of resolvedTools) {
2982
+ this.tools.set(tool3.name, tool3);
1768
2983
  }
1769
2984
  }
1770
- }
1771
- /** 初始化所有 Adapter */
1772
- initializeAdapters() {
1773
- if (this.config.arkApiKey) {
1774
- this.adapters.set("ark", new ArkAdapter({
1775
- apiKey: this.config.arkApiKey,
1776
- apiUrl: this.config.arkApiUrl
1777
- }));
1778
- }
1779
- if (this.config.qwenApiKey) {
1780
- this.adapters.set("qwen", new QwenAdapter({
1781
- apiKey: this.config.qwenApiKey,
1782
- apiUrl: this.config.qwenApiUrl
1783
- }));
1784
- }
1785
- if (this.config.geminiApiKey) {
1786
- this.adapters.set("gemini", new GeminiAdapter({
1787
- apiKey: this.config.geminiApiKey
1788
- }));
1789
- }
1790
- if (this.config.openrouterApiKey) {
1791
- this.adapters.set("openrouter", new OpenRouterAdapter({
1792
- apiKey: this.config.openrouterApiKey,
1793
- apiUrl: this.config.openrouterApiUrl
1794
- }));
2985
+ if (this.config.tavilyApiKey && !this.tools.has("web_search")) {
2986
+ this.tools.set("web_search", createWebSearchTool(this.config.tavilyApiKey));
1795
2987
  }
1796
2988
  }
2989
+ /** 获取模型的家族配置 */
2990
+ getModelFamilyConfig(model) {
2991
+ return getModelFamily(model);
2992
+ }
1797
2993
  /**
1798
- * 判断模型提供商
2994
+ * 判断模型提供商(兼容旧接口)
1799
2995
  */
1800
2996
  getModelProvider(model) {
1801
2997
  return routeModelToProvider(model);
1802
2998
  }
1803
- /** 获取 Adapter */
1804
- getAdapter(model) {
1805
- const providerName = this.getModelProvider(model);
1806
- return this.adapters.get(providerName);
1807
- }
1808
2999
  /**
1809
3000
  * 调试:获取模型路由信息
1810
3001
  */
@@ -1812,43 +3003,67 @@ var HybridAgent = class {
1812
3003
  const result = routeModelWithDetails(model);
1813
3004
  return {
1814
3005
  ...result,
1815
- available: this.adapters.has(result.provider)
3006
+ available: this.adapter.supportsModel(model),
3007
+ familyConfig: getModelFamily(model)
1816
3008
  };
1817
3009
  }
1818
3010
  /** 构建系统提示词 */
1819
3011
  buildSystemPrompt(options) {
1820
- if (options.mode === "plan") {
1821
- return this.config.planPrompt;
3012
+ const model = options.model || DEFAULT_MODEL;
3013
+ const familyConfig = getModelFamily(model);
3014
+ let prompt = AGENT_MODE_PROMPT;
3015
+ if (options.enableWebSearch && familyConfig && !modelSupportsNativeSearch(model)) {
3016
+ prompt += "\n\n\u3010\u8054\u7F51\u641C\u7D22\u3011\u5F53\u7528\u6237\u95EE\u9898\u9700\u8981\u5B9E\u65F6\u4FE1\u606F/\u6700\u65B0\u4E8B\u5B9E/\u53EF\u5F15\u7528\u6765\u6E90\u65F6\uFF0C\u8BF7\u5148\u8C03\u7528 web_search \u5DE5\u5177\u83B7\u53D6\u7ED3\u679C\uFF0C\u7136\u540E\u57FA\u4E8E\u8FD4\u56DE\u7684 title/url/snippet \u4F5C\u7B54\uFF0C\u5E76\u5728\u56DE\u7B54\u4E2D\u7ED9\u51FA\u6765\u6E90\u94FE\u63A5\u3002";
1822
3017
  }
1823
- return AGENT_MODE_PROMPT;
3018
+ return prompt;
1824
3019
  }
1825
3020
  /** 获取默认深度思考模式 */
1826
3021
  getDefaultThinkingMode() {
1827
3022
  return "disabled";
1828
3023
  }
1829
3024
  /** 创建工具执行上下文 */
1830
- createToolContext(signal) {
3025
+ createToolContext(signal, hooks) {
1831
3026
  return {
1832
3027
  cwd: this.config.cwd,
1833
3028
  geminiClient: this.geminiClient,
1834
- executeCommand: (command, cwd) => this.toolExecutor.executeCommand(command, cwd || this.config.cwd, signal),
3029
+ executeCommand: (command, cwd) => this.toolExecutor.executeCommand(command, cwd || this.config.cwd, signal, {
3030
+ onStdout: hooks?.onStdout,
3031
+ onStderr: hooks?.onStderr
3032
+ }),
1835
3033
  signal
1836
3034
  };
1837
3035
  }
1838
3036
  /** 执行工具 */
1839
- async executeTool(name, args, signal) {
1840
- const tool2 = this.tools.get(name);
1841
- if (!tool2) {
3037
+ async executeTool(name, args, signal, hooks) {
3038
+ const tool3 = this.tools.get(name);
3039
+ if (!tool3) {
1842
3040
  return `\u672A\u77E5\u5DE5\u5177: ${name}`;
1843
3041
  }
1844
- return await tool2.execute(args, this.createToolContext(signal));
3042
+ return await tool3.execute(args, this.createToolContext(signal, hooks));
1845
3043
  }
1846
3044
  /** 获取工具定义列表 */
1847
- getToolDefinitions() {
1848
- return Array.from(this.tools.values()).map((tool2) => ({
1849
- name: tool2.name,
1850
- description: tool2.description,
1851
- parameters: tool2.parameters
3045
+ getToolDefinitions(enabledTools, forceInclude) {
3046
+ const allTools = Array.from(this.tools.values());
3047
+ let selected = enabledTools !== void 0 ? allTools.filter((t) => enabledTools.includes(t.name)) : allTools;
3048
+ if (forceInclude?.length) {
3049
+ for (const name of forceInclude) {
3050
+ if (!selected.some((t) => t.name === name)) {
3051
+ const t = this.tools.get(name);
3052
+ if (t) selected = [...selected, t];
3053
+ }
3054
+ }
3055
+ }
3056
+ return selected.map((tool3) => ({
3057
+ name: tool3.name,
3058
+ description: tool3.description,
3059
+ parameters: tool3.parameters
3060
+ }));
3061
+ }
3062
+ /** 获取所有工具列表(用于设置面板) */
3063
+ getAllTools() {
3064
+ return Array.from(this.tools.values()).map((tool3) => ({
3065
+ name: tool3.name,
3066
+ description: tool3.description
1852
3067
  }));
1853
3068
  }
1854
3069
  /**
@@ -1861,15 +3076,24 @@ var HybridAgent = class {
1861
3076
  this.abortController = new AbortController();
1862
3077
  const signal = this.abortController.signal;
1863
3078
  const model = options.model || DEFAULT_MODEL;
1864
- const adapter = this.getAdapter(model);
1865
- if (!adapter) {
1866
- const providerName = this.getModelProvider(model);
1867
- yield createApiError(`\u7F3A\u5C11 ${providerName} Provider \u7684 API Key`, { code: "MISSING_API_KEY" });
3079
+ if (!this.adapter.supportsModel(model)) {
3080
+ const familyConfig2 = getModelFamily(model);
3081
+ if (!familyConfig2) {
3082
+ yield createApiError(`\u672A\u77E5\u6A21\u578B: ${model}`, { code: "MODEL_NOT_SUPPORTED" });
3083
+ } else {
3084
+ yield createApiError(`\u6A21\u578B ${model} \u7F3A\u5C11 API Key \u914D\u7F6E`, { code: "MISSING_API_KEY" });
3085
+ }
1868
3086
  return;
1869
3087
  }
1870
- const thinkingMode = options.thinkingMode ?? this.getDefaultThinkingMode();
3088
+ const familyConfig = getModelFamily(model);
3089
+ const rawThinkingMode = options.thinkingMode ?? this.getDefaultThinkingMode();
3090
+ const thinkingMode = rawThinkingMode === "enabled" ? "enabled" : "disabled";
1871
3091
  const systemPrompt = this.buildSystemPrompt(options);
1872
- const tools2 = options.mode === "ask" ? [] : this.getToolDefinitions();
3092
+ const isAskMode = options.mode === "ask";
3093
+ const enableSearch = !isAskMode && !!options.enableWebSearch;
3094
+ const forceInclude = enableSearch && familyConfig && !modelSupportsNativeSearch(model) ? ["web_search"] : void 0;
3095
+ const tools2 = isAskMode ? [] : this.getToolDefinitions(options.enabledTools, forceInclude);
3096
+ const enableThinking = !isAskMode && thinkingMode === "enabled" && (familyConfig?.supportsThinking ?? false);
1873
3097
  const history = options.history || [];
1874
3098
  const context = {
1875
3099
  systemPrompt,
@@ -1879,10 +3103,10 @@ var HybridAgent = class {
1879
3103
  images
1880
3104
  };
1881
3105
  try {
1882
- yield* this.orchestrator.chat(adapter, message, context, {
3106
+ yield* this.orchestrator.chat(this.adapter, message, context, {
1883
3107
  model,
1884
- enableThinking: thinkingMode === "enabled",
1885
- enableSearch: options.enableWebSearch,
3108
+ enableThinking,
3109
+ enableSearch,
1886
3110
  autoRunConfig: options.autoRunConfig
1887
3111
  });
1888
3112
  } finally {
@@ -2561,7 +3785,9 @@ var AMAP_KEY = "16924e39cfd78c645f0035c1bc290961";
2561
3785
  function getWeatherTool() {
2562
3786
  return tool({
2563
3787
  name: "get_weather",
2564
- description: "\u83B7\u53D6\u6307\u5B9A\u57CE\u5E02\u7684\u5B9E\u65F6\u5929\u6C14\u4FE1\u606F\u3002\u652F\u6301\u5168\u56FD 337 \u4E2A\u5730\u7EA7\u5E02\uFF0C\u5305\u62EC\uFF1A\u5317\u4EAC\u3001\u4E0A\u6D77\u3001\u5E7F\u5DDE\u3001\u6DF1\u5733\u3001\u676D\u5DDE\u3001\u6210\u90FD\u3001\u6B66\u6C49\u3001\u897F\u5B89\u3001\u5357\u4EAC\u3001\u82CF\u5DDE\u3001\u91CD\u5E86\u3001\u5929\u6D25\u7B49\u3002\u8FD4\u56DE\u6E29\u5EA6\u3001\u5929\u6C14\u72B6\u51B5\u3001\u6E7F\u5EA6\u3001\u98CE\u5411\u98CE\u529B\u7B49\u4FE1\u606F\u3002",
3788
+ description: '\u83B7\u53D6\u6307\u5B9A\u57CE\u5E02\u7684\u5B9E\u65F6\u5929\u6C14\u4FE1\u606F\u3002\u652F\u6301\u5168\u56FD 337 \u4E2A\u5730\u7EA7\u5E02\uFF0C\u5305\u62EC\uFF1A\u5317\u4EAC\u3001\u4E0A\u6D77\u3001\u5E7F\u5DDE\u3001\u6DF1\u5733\u3001\u676D\u5DDE\u3001\u6210\u90FD\u3001\u6B66\u6C49\u3001\u897F\u5B89\u3001\u5357\u4EAC\u3001\u82CF\u5DDE\u3001\u91CD\u5E86\u3001\u5929\u6D25\u7B49\u3002\u8FD4\u56DE\u6E29\u5EA6\u3001\u5929\u6C14\u72B6\u51B5\u3001\u6E7F\u5EA6\u3001\u98CE\u5411\u98CE\u529B\u7B49\u4FE1\u606F\u3002\n\n\u6CE8\u610F\uFF1A\u5DE5\u5177\u7ED3\u679C\u4F1A\u4EE5\u53EF\u89C6\u5316\u5361\u7247\u5F62\u5F0F\u81EA\u52A8\u663E\u793A\u7ED9\u7528\u6237\u3002\n- \u8BF7\u4E0D\u8981\u5728\u56DE\u590D\u4E2D\u7528\u5217\u8868/\u8868\u683C\u91CD\u590D\u5C55\u793A\u5361\u7247\u91CC\u5DF2\u6709\u5B57\u6BB5\uFF08\u57CE\u5E02\u3001\u6E29\u5EA6\u3001\u5929\u6C14\u3001\u6E7F\u5EA6\u3001\u98CE\u529B\u3001\u65F6\u95F4\u7B49\uFF09\u3002\n- \u4F60\u53EA\u9700\u8981\u7ED9\u51FA\u4E00\u53E5\u5230\u4E09\u53E5"\u7ED3\u8BBA/\u5EFA\u8BAE"\uFF08\u5982\u7A7F\u8863\u3001\u9632\u6652\u3001\u51FA\u884C\u63D0\u9192\uFF09\u3002\n- \u53EA\u6709\u7528\u6237\u660E\u786E\u8FFD\u95EE\u5177\u4F53\u6570\u503C\u65F6\uFF0C\u518D\u8865\u5145\u6570\u636E\u3002',
3789
+ // 结果类型:前端会生成 { type: 'weather', ...result } 的 Part
3790
+ resultType: "weather",
2565
3791
  parameters: {
2566
3792
  type: "object",
2567
3793
  properties: {
@@ -2841,6 +4067,6 @@ async function getDocumentSearchInstance(dataDir = "./.search-data", workspace)
2841
4067
  return await mod.searchPlugin({ dataDir, workspace });
2842
4068
  }
2843
4069
 
2844
- export { ARK_MODELS, ArkAdapter, ChatOrchestrator, DEFAULT_MODEL, GEMINI_MODELS, GeminiAdapter, HybridAgent, MODELS, OPENROUTER_MODELS, OpenRouterAdapter, QWEN_MODELS, QwenAdapter, analyzeImageTool, analyzeVideoTool, createAbort, createApiError, createArkAdapter, createDefaultToolExecutor, createDocumentSearchTools, createDone, createError, createGeminiAdapter, createImage, createOpenRouterAdapter, createOrchestrator, createParseError, createQwenAdapter, createRateLimitError, createSearchEnd, createSearchResult, createSearchStart, createStepEnd, createStepStart, createStreamStart, createTextDelta, createThinkingDelta, createThinkingEnd, createThinkingStart, createTimeoutError, createToolCallResult, createToolCallStart, createToolError, createVideo, executeCommandTool, generateImageTool, getCwdTool, getDefaultProvider, getDocumentSearchInstance, getModelByModelId, getNativeModels, getOpenRouterModels, getPlatformTool, getRouteRules, getWeatherTool, isAbortEvent, isErrorEvent, isMediaEvent, isModelForProvider, isRetryableError, isSearchEvent, isStatusEvent, isStepEvent, isTextEvent, isThinkingEvent, isToolEvent, navigateToDirectoryTool, openConfirmDialogTool, openFilePreviewTool, resolveTools, routeModelToProvider, routeModelWithDetails, showToastTool, tool, tools, uiActionTools };
4070
+ export { AnthropicProtocol, ArkProtocol, CLAUDE_FAMILY, ChatOrchestrator, DEEPSEEK_FAMILY, DEFAULT_MODEL, DOUBAO_FAMILY, DebugLogger, DeepSeekProtocol, GEMINI_FAMILY, GPT_FAMILY, GeminiProtocol, HybridAgent, MODELS, MODEL_FAMILIES, MODEL_REGISTRY, OpenAIProtocol, QWEN_FAMILY, QwenProtocol, UnifiedAdapter, analyzeImageTool, analyzeVideoTool, createAbort, createAnthropicProtocol, createApiError, createArkProtocol, createDeepSeekProtocol, createDefaultToolExecutor, createDocumentSearchTools, createDone, createError, createGeminiProtocol, createImage, createOpenAIProtocol, createOrchestrator, createParseError, createQwenProtocol, createRateLimitError, createSearchEnd, createSearchResult, createSearchStart, createStepEnd, createStepStart, createStreamStart, createTextDelta, createThinkingDelta, createThinkingEnd, createThinkingStart, createTimeoutError, createToolCallResult, createToolCallStart, createToolError, createUnifiedAdapter, createVideo, executeCommandTool, generateImageTool, getCwdTool, getDefaultProvider, getDocumentSearchInstance, getModelByModelId, getModelEntry, getModelFamily, getModelProtocol, getModelSearchStrategy, getModelsByFamily, getModelsByProtocol, getPlatformTool, getVisibleModels, getWeatherTool, isAbortEvent, isErrorEvent, isMediaEvent, isModelForProvider, isRetryableError, isSearchEvent, isStatusEvent, isStepEvent, isTextEvent, isThinkingEvent, isToolEvent, modelSupportsNativeSearch, modelSupportsThinking, navigateToDirectoryTool, openConfirmDialogTool, openFilePreviewTool, resolveTools, routeModelToProvider, routeModelWithDetails, showToastTool, tool, tools, uiActionTools };
2845
4071
  //# sourceMappingURL=index.js.map
2846
4072
  //# sourceMappingURL=index.js.map