@huyooo/ai-chat-core 0.2.44 → 0.3.2
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/adapter/index.d.ts +11 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/model-adapter.d.ts +25 -0
- package/dist/adapter/model-adapter.d.ts.map +1 -0
- package/dist/adapter/model-options.d.ts +53 -0
- package/dist/adapter/model-options.d.ts.map +1 -0
- package/dist/adapter/types.d.ts +28 -0
- package/dist/adapter/types.d.ts.map +1 -0
- package/dist/chat-runtime.d.ts +96 -0
- package/dist/chat-runtime.d.ts.map +1 -0
- package/dist/constants.d.ts +12 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/events.d.ts +605 -1
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +1 -1
- package/dist/extension/index.d.ts +9 -0
- package/dist/extension/index.d.ts.map +1 -0
- package/dist/extension/types.d.ts +46 -0
- package/dist/extension/types.d.ts.map +1 -0
- package/dist/families/index.d.ts +11 -0
- package/dist/families/index.d.ts.map +1 -0
- package/dist/families/presets.d.ts +31 -0
- package/dist/families/presets.d.ts.map +1 -0
- package/dist/families/resolver.d.ts +11 -0
- package/dist/families/resolver.d.ts.map +1 -0
- package/dist/families/types.d.ts +29 -0
- package/dist/families/types.d.ts.map +1 -0
- package/dist/governance/command-safety.d.ts +34 -0
- package/dist/governance/command-safety.d.ts.map +1 -0
- package/dist/governance/governance.d.ts +19 -0
- package/dist/governance/governance.d.ts.map +1 -0
- package/dist/governance/index.d.ts +12 -0
- package/dist/governance/index.d.ts.map +1 -0
- package/dist/governance/types.d.ts +29 -0
- package/dist/governance/types.d.ts.map +1 -0
- package/dist/index.d.ts +72 -804
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -1
- package/dist/internal/management-args.d.ts +13 -0
- package/dist/internal/management-args.d.ts.map +1 -0
- package/dist/internal/management-results.d.ts +21 -0
- package/dist/internal/management-results.d.ts.map +1 -0
- package/dist/llm-config.d.ts +108 -0
- package/dist/llm-config.d.ts.map +1 -0
- package/dist/logger/core.d.ts +31 -0
- package/dist/logger/core.d.ts.map +1 -0
- package/dist/logger/index.d.ts +9 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/orchestrator/compression-handler.d.ts +29 -0
- package/dist/orchestrator/compression-handler.d.ts.map +1 -0
- package/dist/orchestrator/context-compressor.d.ts +51 -0
- package/dist/orchestrator/context-compressor.d.ts.map +1 -0
- package/dist/orchestrator/context-summarizer.d.ts +41 -0
- package/dist/orchestrator/context-summarizer.d.ts.map +1 -0
- package/dist/orchestrator/index.d.ts +12 -0
- package/dist/orchestrator/index.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator.d.ts +46 -0
- package/dist/orchestrator/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator/types.d.ts +58 -0
- package/dist/orchestrator/types.d.ts.map +1 -0
- package/dist/parts/index.d.ts +13 -0
- package/dist/parts/index.d.ts.map +1 -0
- package/dist/parts/registry.d.ts +11 -0
- package/dist/parts/registry.d.ts.map +1 -0
- package/dist/parts/summaries.d.ts +9 -0
- package/dist/parts/summaries.d.ts.map +1 -0
- package/dist/parts/types.d.ts +61 -0
- package/dist/parts/types.d.ts.map +1 -0
- package/dist/platform.d.ts +17 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +1 -0
- package/dist/protocols/anthropic.d.ts +20 -0
- package/dist/protocols/anthropic.d.ts.map +1 -0
- package/dist/protocols/ark.d.ts +36 -0
- package/dist/protocols/ark.d.ts.map +1 -0
- package/dist/protocols/deepseek.d.ts +24 -0
- package/dist/protocols/deepseek.d.ts.map +1 -0
- package/dist/protocols/error-utils.d.ts +14 -0
- package/dist/protocols/error-utils.d.ts.map +1 -0
- package/dist/protocols/gemini.d.ts +24 -0
- package/dist/protocols/gemini.d.ts.map +1 -0
- package/dist/protocols/glm.d.ts +20 -0
- package/dist/protocols/glm.d.ts.map +1 -0
- package/dist/protocols/grok.d.ts +20 -0
- package/dist/protocols/grok.d.ts.map +1 -0
- package/dist/protocols/index.d.ts +31 -0
- package/dist/protocols/index.d.ts.map +1 -0
- package/dist/protocols/minimax.d.ts +38 -0
- package/dist/protocols/minimax.d.ts.map +1 -0
- package/dist/protocols/moonshot.d.ts +20 -0
- package/dist/protocols/moonshot.d.ts.map +1 -0
- package/dist/protocols/openai-sse.d.ts +33 -0
- package/dist/protocols/openai-sse.d.ts.map +1 -0
- package/dist/protocols/openai.d.ts +19 -0
- package/dist/protocols/openai.d.ts.map +1 -0
- package/dist/protocols/qwen.d.ts +26 -0
- package/dist/protocols/qwen.d.ts.map +1 -0
- package/dist/protocols/responses-sse.d.ts +30 -0
- package/dist/protocols/responses-sse.d.ts.map +1 -0
- package/dist/protocols/sse-reader.d.ts +23 -0
- package/dist/protocols/sse-reader.d.ts.map +1 -0
- package/dist/protocols/tool-arguments.d.ts +8 -0
- package/dist/protocols/tool-arguments.d.ts.map +1 -0
- package/dist/protocols/types.d.ts +148 -0
- package/dist/protocols/types.d.ts.map +1 -0
- package/dist/protocols/vercel-gateway.d.ts +15 -0
- package/dist/protocols/vercel-gateway.d.ts.map +1 -0
- package/dist/runtime.d.ts +151 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +1 -0
- package/dist/skills/index.d.ts +14 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/management/admin.d.ts +10 -0
- package/dist/skills/management/admin.d.ts.map +1 -0
- package/dist/skills/management/index.d.ts +11 -0
- package/dist/skills/management/index.d.ts.map +1 -0
- package/dist/skills/management/inputs.d.ts +44 -0
- package/dist/skills/management/inputs.d.ts.map +1 -0
- package/dist/skills/management/operations.d.ts +78 -0
- package/dist/skills/management/operations.d.ts.map +1 -0
- package/dist/skills/management/types.d.ts +70 -0
- package/dist/skills/management/types.d.ts.map +1 -0
- package/dist/skills/registry.d.ts +37 -0
- package/dist/skills/registry.d.ts.map +1 -0
- package/dist/skills/summaries.d.ts +9 -0
- package/dist/skills/summaries.d.ts.map +1 -0
- package/dist/skills/types.d.ts +61 -0
- package/dist/skills/types.d.ts.map +1 -0
- package/dist/test-utils/mock-sse.d.ts +13 -0
- package/dist/test-utils/mock-sse.d.ts.map +1 -0
- package/dist/tool-manager/define-tool.d.ts +35 -0
- package/dist/tool-manager/define-tool.d.ts.map +1 -0
- package/dist/tool-manager/formats.d.ts +46 -0
- package/dist/tool-manager/formats.d.ts.map +1 -0
- package/dist/tool-manager/identity.d.ts +18 -0
- package/dist/tool-manager/identity.d.ts.map +1 -0
- package/dist/tool-manager/in-process-provider.d.ts +15 -0
- package/dist/tool-manager/in-process-provider.d.ts.map +1 -0
- package/dist/tool-manager/index.d.ts +18 -0
- package/dist/tool-manager/index.d.ts.map +1 -0
- package/dist/tool-manager/manager.d.ts +18 -0
- package/dist/tool-manager/manager.d.ts.map +1 -0
- package/dist/tool-manager/mcp-provider.d.ts +21 -0
- package/dist/tool-manager/mcp-provider.d.ts.map +1 -0
- package/dist/tool-manager/summaries.d.ts +39 -0
- package/dist/tool-manager/summaries.d.ts.map +1 -0
- package/dist/tool-manager/types.d.ts +314 -0
- package/dist/tool-manager/types.d.ts.map +1 -0
- package/dist/types.d.ts +663 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +26 -15
- package/src/adapter/index.ts +25 -0
- package/src/adapter/model-adapter.ts +196 -0
- package/src/adapter/model-options.ts +143 -0
- package/src/adapter/types.ts +41 -0
- package/src/chat-runtime.ts +515 -0
- package/src/constants.ts +9 -102
- package/src/events.ts +364 -150
- package/src/extension/index.ts +24 -0
- package/src/extension/types.ts +49 -0
- package/src/families/index.ts +28 -0
- package/src/families/presets.ts +124 -0
- package/src/families/resolver.ts +22 -0
- package/src/families/types.ts +55 -0
- package/src/governance/command-safety.ts +224 -0
- package/src/governance/governance.ts +125 -0
- package/src/governance/index.ts +38 -0
- package/src/governance/types.ts +44 -0
- package/src/index.ts +250 -145
- package/src/internal/management-args.ts +39 -0
- package/src/internal/management-results.ts +60 -0
- package/src/llm-config.ts +137 -0
- package/src/logger/core.ts +96 -0
- package/src/logger/index.ts +8 -0
- package/src/orchestrator/compression-handler.ts +137 -0
- package/src/{providers → orchestrator}/context-compressor.ts +79 -47
- package/src/orchestrator/context-summarizer.ts +123 -0
- package/src/orchestrator/index.ts +20 -0
- package/src/orchestrator/orchestrator.ts +1002 -0
- package/src/orchestrator/types.ts +70 -0
- package/src/parts/index.ts +20 -0
- package/src/parts/registry.ts +95 -0
- package/src/parts/summaries.ts +40 -0
- package/src/parts/types.ts +63 -0
- package/src/platform.ts +73 -0
- package/src/protocols/anthropic.ts +377 -0
- package/src/protocols/ark.ts +300 -0
- package/src/protocols/deepseek.ts +192 -0
- package/src/{providers/protocols → protocols}/error-utils.ts +17 -20
- package/src/protocols/gemini.ts +352 -0
- package/src/protocols/glm.ts +212 -0
- package/src/protocols/grok.ts +98 -0
- package/src/protocols/index.ts +48 -0
- package/src/protocols/minimax.ts +308 -0
- package/src/protocols/moonshot.ts +186 -0
- package/src/protocols/openai-sse.ts +156 -0
- package/src/protocols/openai.ts +97 -0
- package/src/protocols/qwen.ts +358 -0
- package/src/protocols/responses-sse.ts +224 -0
- package/src/protocols/sse-reader.ts +54 -0
- package/src/protocols/tool-arguments.ts +32 -0
- package/src/{providers/protocols → protocols}/types.ts +46 -37
- package/src/protocols/vercel-gateway.ts +391 -0
- package/src/runtime.ts +167 -0
- package/src/skills/index.ts +29 -0
- package/src/skills/management/admin.ts +170 -0
- package/src/skills/management/index.ts +27 -0
- package/src/skills/management/inputs.ts +79 -0
- package/src/skills/management/operations.ts +256 -0
- package/src/skills/management/types.ts +57 -0
- package/src/skills/registry.ts +120 -0
- package/src/skills/summaries.ts +48 -0
- package/src/skills/types.ts +65 -0
- package/src/test-utils/mock-sse.ts +3 -3
- package/src/tool-manager/define-tool.ts +201 -0
- package/src/tool-manager/formats.ts +146 -0
- package/src/tool-manager/identity.ts +80 -0
- package/src/tool-manager/in-process-provider.ts +164 -0
- package/src/tool-manager/index.ts +63 -0
- package/src/tool-manager/manager.ts +562 -0
- package/src/tool-manager/mcp-provider.ts +509 -0
- package/src/tool-manager/summaries.ts +136 -0
- package/src/tool-manager/types.ts +389 -0
- package/src/types.ts +750 -191
- package/dist/events-CU5D5ray.d.ts +0 -1128
- package/src/agent.ts +0 -409
- package/src/internal/update-plan.ts +0 -2
- package/src/internal/web-search.ts +0 -77
- package/src/mcp/client-manager.ts +0 -302
- package/src/mcp/index.ts +0 -2
- package/src/mcp/types.ts +0 -43
- package/src/providers/context-summarizer.ts +0 -70
- package/src/providers/index.ts +0 -125
- package/src/providers/model-registry.ts +0 -466
- package/src/providers/orchestrator.ts +0 -839
- package/src/providers/protocols/anthropic.ts +0 -406
- package/src/providers/protocols/ark.ts +0 -362
- package/src/providers/protocols/deepseek.ts +0 -344
- package/src/providers/protocols/gemini.ts +0 -350
- package/src/providers/protocols/index.ts +0 -36
- package/src/providers/protocols/openai.ts +0 -420
- package/src/providers/protocols/qwen.ts +0 -315
- package/src/providers/types.ts +0 -264
- package/src/providers/unified-adapter.ts +0 -367
- package/src/router.ts +0 -72
- package/src/tools.ts +0 -162
- package/src/utils.ts +0 -86
|
@@ -12,15 +12,31 @@
|
|
|
12
12
|
* - 工具调用格式差异(由 Family 处理)
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import type { ModelFamilyConfig } from '../
|
|
15
|
+
import type { ModelFamilyConfig } from '../families';
|
|
16
|
+
|
|
17
|
+
// ==================== 协议标识 ====================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 内置协议类型标识
|
|
21
|
+
*
|
|
22
|
+
* 默认带版本 _v1,当上游 API 有 breaking change 时新增 _v2、_v3 等。
|
|
23
|
+
*/
|
|
24
|
+
export type ProtocolId =
|
|
25
|
+
| 'ark_v1' | 'deepseek_v1' | 'qwen_v1' | 'glm_v1' | 'moonshot_v1' | 'minimax_v1'
|
|
26
|
+
| 'vercel_gateway_v1' | 'anthropic_v1' | 'gemini_v1' | 'openai_v1' | 'grok_v1'
|
|
27
|
+
;
|
|
28
|
+
|
|
29
|
+
/** 从 ProtocolId 提取 base 协议(如 anthropic_v1 → anthropic) */
|
|
30
|
+
export function getBaseProtocol(protocol: string): string {
|
|
31
|
+
return protocol.replace(/_v\d+$/, '');
|
|
32
|
+
}
|
|
16
33
|
|
|
17
34
|
// ==================== 原始事件类型 ====================
|
|
18
35
|
|
|
19
36
|
/**
|
|
20
37
|
* Protocol 原始事件类型
|
|
21
38
|
*
|
|
22
|
-
*
|
|
23
|
-
* 后续由 FamilyBehavior 转换为标准 StreamChunk
|
|
39
|
+
* Protocol 层产出的事件,Orchestrator 直接消费
|
|
24
40
|
*/
|
|
25
41
|
export type RawEventType =
|
|
26
42
|
| 'text_delta' // 文本增量
|
|
@@ -29,14 +45,10 @@ export type RawEventType =
|
|
|
29
45
|
| 'tool_call_start' // 工具调用开始
|
|
30
46
|
| 'tool_call_delta' // 工具调用参数增量
|
|
31
47
|
| 'tool_call_done' // 工具调用完成
|
|
32
|
-
| 'search_query' // 搜索查询
|
|
33
|
-
| 'search_result' // 搜索结果
|
|
34
48
|
| 'done' // 流结束
|
|
35
49
|
| 'error'; // 错误
|
|
36
50
|
|
|
37
|
-
/**
|
|
38
|
-
* 原始工具调用
|
|
39
|
-
*/
|
|
51
|
+
/** 工具调用 */
|
|
40
52
|
export interface RawToolCall {
|
|
41
53
|
id: string;
|
|
42
54
|
name: string;
|
|
@@ -45,26 +57,6 @@ export interface RawToolCall {
|
|
|
45
57
|
thoughtSignature?: string;
|
|
46
58
|
}
|
|
47
59
|
|
|
48
|
-
/**
|
|
49
|
-
* Protocol 消息中的工具调用
|
|
50
|
-
*/
|
|
51
|
-
export interface ProtocolToolCall {
|
|
52
|
-
id: string;
|
|
53
|
-
name: string;
|
|
54
|
-
arguments: string;
|
|
55
|
-
/** Gemini 需要的 thoughtSignature */
|
|
56
|
-
thoughtSignature?: string;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* 原始搜索结果
|
|
61
|
-
*/
|
|
62
|
-
export interface RawSearchResult {
|
|
63
|
-
title: string;
|
|
64
|
-
url: string;
|
|
65
|
-
snippet: string;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
60
|
/**
|
|
69
61
|
* Protocol 原始事件
|
|
70
62
|
*/
|
|
@@ -77,23 +69,29 @@ export interface RawTokenUsage {
|
|
|
77
69
|
cachedTokens?: number;
|
|
78
70
|
}
|
|
79
71
|
|
|
72
|
+
/** 输出内容片段(用于图片生成、多模态结果等非纯文本产物) */
|
|
73
|
+
export type RawOutputPart =
|
|
74
|
+
| { type: 'text'; text: string }
|
|
75
|
+
| { type: 'inline_data'; mimeType: string; data: string };
|
|
76
|
+
|
|
80
77
|
export interface RawEvent {
|
|
81
78
|
type: RawEventType;
|
|
82
79
|
|
|
83
80
|
// text_delta / thinking_delta
|
|
84
81
|
delta?: string;
|
|
85
82
|
|
|
83
|
+
// thinking_done(Anthropic 的 thinking signature)
|
|
84
|
+
thinkingSignature?: string;
|
|
85
|
+
|
|
86
86
|
// tool_call_*
|
|
87
87
|
toolCall?: Partial<RawToolCall>;
|
|
88
88
|
|
|
89
|
-
// search_*
|
|
90
|
-
searchQuery?: string;
|
|
91
|
-
searchResults?: RawSearchResult[];
|
|
92
|
-
|
|
93
89
|
// done
|
|
94
90
|
finishReason?: 'stop' | 'tool_calls' | 'length' | 'error';
|
|
95
91
|
/** Token 使用统计(done 事件携带) */
|
|
96
92
|
usage?: RawTokenUsage;
|
|
93
|
+
/** 完整输出片段(done 事件携带;用于图片生成等场景) */
|
|
94
|
+
outputParts?: RawOutputPart[];
|
|
97
95
|
|
|
98
96
|
// error
|
|
99
97
|
error?: string;
|
|
@@ -110,9 +108,18 @@ export interface ProtocolMessage {
|
|
|
110
108
|
|
|
111
109
|
/** 图片列表(base64 或 URL) */
|
|
112
110
|
images?: string[];
|
|
111
|
+
|
|
112
|
+
/** 多模态文件附件。Gemini 使用 fileData 引用 Files API URI。 */
|
|
113
|
+
attachments?: ProtocolAttachment[];
|
|
114
|
+
|
|
115
|
+
/** 思考内容(assistant 消息,工具调用循环需回传给 API) */
|
|
116
|
+
thinkingContent?: string;
|
|
117
|
+
|
|
118
|
+
/** 思考签名(Anthropic extended thinking 的 signature,工具调用循环需回传) */
|
|
119
|
+
thinkingSignature?: string;
|
|
113
120
|
|
|
114
121
|
/** 工具调用(assistant 消息) */
|
|
115
|
-
toolCalls?:
|
|
122
|
+
toolCalls?: RawToolCall[];
|
|
116
123
|
|
|
117
124
|
/** 工具调用 ID(tool 消息) */
|
|
118
125
|
toolCallId?: string;
|
|
@@ -121,10 +128,13 @@ export interface ProtocolMessage {
|
|
|
121
128
|
toolName?: string;
|
|
122
129
|
}
|
|
123
130
|
|
|
131
|
+
export type ProtocolAttachment =
|
|
132
|
+
| { type: 'file_data'; mimeType: string; fileUri: string };
|
|
133
|
+
|
|
124
134
|
/**
|
|
125
135
|
* Protocol 工具定义(简化格式)
|
|
126
136
|
*/
|
|
127
|
-
import type { JsonSchemaObject } from '
|
|
137
|
+
import type { JsonSchemaObject } from '../types';
|
|
128
138
|
|
|
129
139
|
export interface ProtocolToolDefinition {
|
|
130
140
|
name: string;
|
|
@@ -140,10 +150,10 @@ export interface ProtocolRequestOptions {
|
|
|
140
150
|
model: string;
|
|
141
151
|
/** 模型家族配置 */
|
|
142
152
|
familyConfig: ModelFamilyConfig;
|
|
153
|
+
/** 最大输出 token(从 ModelConfig.maxOutputTokens 直传) */
|
|
154
|
+
maxOutputTokens: number;
|
|
143
155
|
/** 是否启用 thinking */
|
|
144
156
|
enableThinking: boolean;
|
|
145
|
-
/** 是否启用搜索 */
|
|
146
|
-
enableSearch: boolean;
|
|
147
157
|
/** 中断信号 */
|
|
148
158
|
signal: AbortSignal;
|
|
149
159
|
}
|
|
@@ -186,4 +196,3 @@ export interface Protocol {
|
|
|
186
196
|
* Protocol 工厂函数类型
|
|
187
197
|
*/
|
|
188
198
|
export type ProtocolFactory = (config: ProtocolConfig) => Protocol;
|
|
189
|
-
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vercel AI Gateway Protocol(LanguageModelV3)
|
|
3
|
+
*
|
|
4
|
+
* 调用 Vercel AI Gateway 内部协议端点 /v3/ai/language-model,
|
|
5
|
+
* 使用 LanguageModelV3 格式,完整保留各厂商原生能力:
|
|
6
|
+
* - Gemini: thinking + thoughtSignature(tool call 循环必需)
|
|
7
|
+
* - GPT: reasoning summary
|
|
8
|
+
* - Claude: reasoning
|
|
9
|
+
*
|
|
10
|
+
* 所有厂商返回统一的事件格式(reasoning-delta / text-delta / tool-call 等),
|
|
11
|
+
* 厂商特有字段通过 providerMetadata 透传。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type {
|
|
15
|
+
Protocol,
|
|
16
|
+
ProtocolConfig,
|
|
17
|
+
ProtocolMessage,
|
|
18
|
+
ProtocolToolDefinition,
|
|
19
|
+
ProtocolRequestOptions,
|
|
20
|
+
RawEvent,
|
|
21
|
+
RawTokenUsage,
|
|
22
|
+
} from './types';
|
|
23
|
+
import { createModuleLogger } from '../logger';
|
|
24
|
+
import { friendlyHttpError } from './error-utils';
|
|
25
|
+
import { readSSEJsonStream } from './sse-reader';
|
|
26
|
+
import { parseProtocolToolArguments } from './tool-arguments';
|
|
27
|
+
|
|
28
|
+
const logger = createModuleLogger('VercelGateway');
|
|
29
|
+
|
|
30
|
+
const GATEWAY_BASE = 'https://ai-gateway.vercel.sh/v3/ai';
|
|
31
|
+
|
|
32
|
+
// ==================== 消息转换(LanguageModelV3 Prompt) ====================
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* LanguageModelV3 Prompt 格式:
|
|
36
|
+
* - system: { role: 'system', content: string }
|
|
37
|
+
* - user: { role: 'user', content: [{ type: 'text', text } | { type: 'file', data, mediaType }] }
|
|
38
|
+
* - assistant: { role: 'assistant', content: [text | reasoning | tool-call] }
|
|
39
|
+
* - tool: { role: 'tool', content: [{ type: 'tool-result', ... }] }
|
|
40
|
+
*/
|
|
41
|
+
function convertToPrompt(messages: ProtocolMessage[]): unknown[] {
|
|
42
|
+
const prompt: unknown[] = [];
|
|
43
|
+
|
|
44
|
+
for (const msg of messages) {
|
|
45
|
+
switch (msg.role) {
|
|
46
|
+
case 'system':
|
|
47
|
+
prompt.push({ role: 'system', content: msg.content });
|
|
48
|
+
break;
|
|
49
|
+
|
|
50
|
+
case 'user': {
|
|
51
|
+
const textContent = msg.content || (msg.images?.length ? '请分析这张图片' : '');
|
|
52
|
+
const parts: unknown[] = [{ type: 'text', text: textContent }];
|
|
53
|
+
if (msg.images?.length) {
|
|
54
|
+
for (const img of msg.images) {
|
|
55
|
+
const dataUrl = img.startsWith('data:') ? img : `data:image/jpeg;base64,${img}`;
|
|
56
|
+
parts.push({ type: 'file', data: dataUrl, mediaType: 'image/jpeg' });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
prompt.push({ role: 'user', content: parts });
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
case 'assistant': {
|
|
64
|
+
const parts: unknown[] = [];
|
|
65
|
+
if (msg.thinkingContent) {
|
|
66
|
+
// 回传思考内容,Gemini 的 thoughtSignature 在 providerOptions 中
|
|
67
|
+
const reasoningPart: Record<string, unknown> = { type: 'reasoning', text: msg.thinkingContent };
|
|
68
|
+
if (msg.toolCalls?.[0]?.thoughtSignature) {
|
|
69
|
+
reasoningPart.providerOptions = {
|
|
70
|
+
google: { thoughtSignature: msg.toolCalls[0].thoughtSignature },
|
|
71
|
+
vertex: { thoughtSignature: msg.toolCalls[0].thoughtSignature },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
parts.push(reasoningPart);
|
|
75
|
+
}
|
|
76
|
+
if (msg.toolCalls?.length) {
|
|
77
|
+
if (msg.content) {
|
|
78
|
+
parts.push({ type: 'text', text: msg.content });
|
|
79
|
+
}
|
|
80
|
+
for (const tc of msg.toolCalls) {
|
|
81
|
+
// V3 协议要求 input 是对象(Gemini 需要 google.protobuf.Struct)
|
|
82
|
+
const toolPart: Record<string, unknown> = {
|
|
83
|
+
type: 'tool-call',
|
|
84
|
+
toolCallId: tc.id,
|
|
85
|
+
toolName: tc.name,
|
|
86
|
+
input: parseProtocolToolArguments(tc.arguments, {
|
|
87
|
+
protocol: 'vercel-gateway',
|
|
88
|
+
toolCallId: tc.id,
|
|
89
|
+
toolName: tc.name,
|
|
90
|
+
}),
|
|
91
|
+
};
|
|
92
|
+
if (tc.thoughtSignature) {
|
|
93
|
+
toolPart.providerOptions = {
|
|
94
|
+
google: { thoughtSignature: tc.thoughtSignature },
|
|
95
|
+
vertex: { thoughtSignature: tc.thoughtSignature },
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
parts.push(toolPart);
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
parts.push({ type: 'text', text: msg.content || '' });
|
|
102
|
+
}
|
|
103
|
+
prompt.push({ role: 'assistant', content: parts });
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
case 'tool':
|
|
108
|
+
prompt.push({
|
|
109
|
+
role: 'tool',
|
|
110
|
+
content: [{
|
|
111
|
+
type: 'tool-result',
|
|
112
|
+
toolCallId: msg.toolCallId || '',
|
|
113
|
+
toolName: msg.toolName || 'unknown',
|
|
114
|
+
output: formatToolOutput(msg.content),
|
|
115
|
+
}],
|
|
116
|
+
});
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return prompt;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function formatToolOutput(content: string): { type: 'json'; value: unknown } | { type: 'text'; value: string } {
|
|
125
|
+
try {
|
|
126
|
+
return { type: 'json', value: JSON.parse(content) };
|
|
127
|
+
} catch {
|
|
128
|
+
return { type: 'text', value: content };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ==================== 工具转换 ====================
|
|
133
|
+
|
|
134
|
+
function convertTools(tools: ProtocolToolDefinition[]): unknown[] | undefined {
|
|
135
|
+
if (tools.length === 0) return undefined;
|
|
136
|
+
// V3 协议用 inputSchema(非 parameters),Claude/Bedrock 严格要求此字段
|
|
137
|
+
return tools.map(t => ({
|
|
138
|
+
type: 'function',
|
|
139
|
+
name: t.name,
|
|
140
|
+
description: t.description,
|
|
141
|
+
inputSchema: t.parameters,
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ==================== Provider Options(thinking 配置) ====================
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 根据 model ID 前缀构建厂商特有的 thinking 配置:
|
|
149
|
+
* - google/*: providerOptions.google.thinkingConfig
|
|
150
|
+
* - openai/*: providerOptions.openai.reasoningEffort + reasoningSummary
|
|
151
|
+
* - anthropic/*: providerOptions.anthropic.thinking
|
|
152
|
+
*/
|
|
153
|
+
function buildProviderOptions(modelId: string, enableThinking: boolean): Record<string, unknown> | undefined {
|
|
154
|
+
if (modelId.startsWith('google/')) {
|
|
155
|
+
if (!enableThinking) return undefined;
|
|
156
|
+
return {
|
|
157
|
+
google: { thinkingConfig: { thinkingLevel: 'high', includeThoughts: true } },
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (modelId.startsWith('openai/')) {
|
|
161
|
+
return {
|
|
162
|
+
openai: {
|
|
163
|
+
reasoningEffort: enableThinking ? 'high' : 'low',
|
|
164
|
+
reasoningSummary: enableThinking ? 'detailed' : 'auto',
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (modelId.startsWith('anthropic/')) {
|
|
169
|
+
if (!enableThinking) return undefined;
|
|
170
|
+
return {
|
|
171
|
+
anthropic: { thinking: { type: 'enabled', budgetTokens: 10000 } },
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ==================== 请求体构建 ====================
|
|
178
|
+
|
|
179
|
+
function buildRequestBody(
|
|
180
|
+
messages: ProtocolMessage[],
|
|
181
|
+
tools: ProtocolToolDefinition[],
|
|
182
|
+
options: ProtocolRequestOptions,
|
|
183
|
+
): Record<string, unknown> {
|
|
184
|
+
const body: Record<string, unknown> = {
|
|
185
|
+
prompt: convertToPrompt(messages),
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
body.maxOutputTokens = options.maxOutputTokens;
|
|
189
|
+
|
|
190
|
+
const providerOptions = buildProviderOptions(options.model, options.enableThinking);
|
|
191
|
+
if (providerOptions) {
|
|
192
|
+
body.providerOptions = providerOptions;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const convertedTools = convertTools(tools);
|
|
196
|
+
if (convertedTools) {
|
|
197
|
+
body.tools = convertedTools;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return body;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ==================== V3 SSE 流解析 ====================
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* LanguageModelV3 流事件类型:
|
|
207
|
+
*
|
|
208
|
+
* 思考: reasoning-start → reasoning-delta { delta } → reasoning-end
|
|
209
|
+
* 文本: text-start → text-delta { delta } → text-end
|
|
210
|
+
* 工具调用: tool-input-start { toolName } → tool-input-delta { delta } → tool-input-end → tool-call { toolCallId, toolName, input }
|
|
211
|
+
* 完成: finish { finishReason, usage }
|
|
212
|
+
* 错误: error { error }
|
|
213
|
+
*
|
|
214
|
+
* 所有事件都可能携带 providerMetadata(如 Gemini 的 thoughtSignature)
|
|
215
|
+
*/
|
|
216
|
+
/**
|
|
217
|
+
* 解析 LanguageModelV3 SSE 流
|
|
218
|
+
*
|
|
219
|
+
* Layer 1 负责 bytes → JSON,此处做 V3 格式的语义映射。
|
|
220
|
+
*/
|
|
221
|
+
async function* parseV3Stream(
|
|
222
|
+
reader: ReadableStreamDefaultReader<Uint8Array>,
|
|
223
|
+
): AsyncGenerator<RawEvent> {
|
|
224
|
+
let hasToolCalls = false;
|
|
225
|
+
const pendingCalls = new Map<string, { name: string; args: string; thoughtSignature?: string }>();
|
|
226
|
+
|
|
227
|
+
for await (const event of readSSEJsonStream(reader)) {
|
|
228
|
+
const type = event.type as string;
|
|
229
|
+
|
|
230
|
+
switch (type) {
|
|
231
|
+
case 'reasoning-delta': {
|
|
232
|
+
const delta = event.delta as string;
|
|
233
|
+
if (delta) {
|
|
234
|
+
yield { type: 'thinking_delta', delta };
|
|
235
|
+
}
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
case 'reasoning-end': {
|
|
239
|
+
yield { type: 'thinking_done' };
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
case 'text-delta': {
|
|
244
|
+
const delta = event.delta as string;
|
|
245
|
+
if (delta) {
|
|
246
|
+
yield { type: 'text_delta', delta };
|
|
247
|
+
}
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
case 'tool-input-start': {
|
|
252
|
+
hasToolCalls = true;
|
|
253
|
+
const id = event.id as string;
|
|
254
|
+
const name = event.toolName as string;
|
|
255
|
+
const sig = extractThoughtSignature(event);
|
|
256
|
+
pendingCalls.set(id, { name, args: '', thoughtSignature: sig });
|
|
257
|
+
yield { type: 'tool_call_start', toolCall: { id, name } };
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
case 'tool-input-delta': {
|
|
262
|
+
const id = event.id as string;
|
|
263
|
+
const delta = event.delta as string;
|
|
264
|
+
if (id && delta) {
|
|
265
|
+
const pending = pendingCalls.get(id);
|
|
266
|
+
if (pending) pending.args += delta;
|
|
267
|
+
yield { type: 'tool_call_delta', toolCall: { id, arguments: delta } };
|
|
268
|
+
}
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
case 'tool-call': {
|
|
273
|
+
const id = event.toolCallId as string;
|
|
274
|
+
const name = event.toolName as string;
|
|
275
|
+
const input = (event.input ?? '{}') as string;
|
|
276
|
+
const pending = pendingCalls.get(id);
|
|
277
|
+
const sig = extractThoughtSignature(event) || pending?.thoughtSignature;
|
|
278
|
+
yield {
|
|
279
|
+
type: 'tool_call_done',
|
|
280
|
+
toolCall: {
|
|
281
|
+
id,
|
|
282
|
+
name: name || pending?.name || '',
|
|
283
|
+
arguments: input || pending?.args || '{}',
|
|
284
|
+
...(sig ? { thoughtSignature: sig } : {}),
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
pendingCalls.delete(id);
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
case 'finish': {
|
|
292
|
+
const rawUsage = event.usage as Record<string, unknown> | undefined;
|
|
293
|
+
let usage: RawTokenUsage | undefined;
|
|
294
|
+
if (rawUsage) {
|
|
295
|
+
const input = rawUsage.inputTokens as Record<string, number> | undefined;
|
|
296
|
+
const output = rawUsage.outputTokens as Record<string, number> | undefined;
|
|
297
|
+
usage = {
|
|
298
|
+
promptTokens: input?.total ?? 0,
|
|
299
|
+
completionTokens: output?.total ?? 0,
|
|
300
|
+
totalTokens: (input?.total ?? 0) + (output?.total ?? 0),
|
|
301
|
+
reasoningTokens: output?.reasoning ?? 0,
|
|
302
|
+
cachedTokens: input?.cacheRead ?? 0,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
const reason = event.finishReason as Record<string, string> | undefined;
|
|
306
|
+
const unified = reason?.unified ?? 'stop';
|
|
307
|
+
const finishReason = hasToolCalls || unified === 'tool-calls' ? 'tool_calls' as const
|
|
308
|
+
: unified === 'length' ? 'length' as const
|
|
309
|
+
: unified === 'error' ? 'error' as const
|
|
310
|
+
: 'stop' as const;
|
|
311
|
+
yield { type: 'done', finishReason, usage };
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
case 'error': {
|
|
316
|
+
const err = event.error;
|
|
317
|
+
const msg = typeof err === 'string' ? err : JSON.stringify(err);
|
|
318
|
+
yield { type: 'error', error: msg };
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
yield { type: 'done', finishReason: hasToolCalls ? 'tool_calls' : 'stop' };
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** 从 providerMetadata 中提取 Gemini 的 thoughtSignature */
|
|
328
|
+
function extractThoughtSignature(event: Record<string, unknown>): string | undefined {
|
|
329
|
+
const meta = event.providerMetadata as Record<string, Record<string, unknown>> | undefined;
|
|
330
|
+
const sig = meta?.vertex?.thoughtSignature ?? meta?.google?.thoughtSignature;
|
|
331
|
+
return typeof sig === 'string' ? sig : undefined;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// ==================== Protocol 实现 ====================
|
|
335
|
+
|
|
336
|
+
class VercelGatewayProtocol implements Protocol {
|
|
337
|
+
readonly name = 'vercel_gateway';
|
|
338
|
+
private apiKey: string;
|
|
339
|
+
|
|
340
|
+
constructor(config: ProtocolConfig) {
|
|
341
|
+
this.apiKey = config.apiKey;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async *stream(
|
|
345
|
+
messages: ProtocolMessage[],
|
|
346
|
+
tools: ProtocolToolDefinition[],
|
|
347
|
+
options: ProtocolRequestOptions,
|
|
348
|
+
): AsyncGenerator<RawEvent> {
|
|
349
|
+
const body = buildRequestBody(messages, tools, options);
|
|
350
|
+
|
|
351
|
+
logger.debug({
|
|
352
|
+
model: options.model,
|
|
353
|
+
enableThinking: options.enableThinking,
|
|
354
|
+
toolsCount: tools.length,
|
|
355
|
+
}, 'Vercel Gateway V3 请求');
|
|
356
|
+
|
|
357
|
+
const response = await fetch(`${GATEWAY_BASE}/language-model`, {
|
|
358
|
+
method: 'POST',
|
|
359
|
+
headers: {
|
|
360
|
+
'Content-Type': 'application/json',
|
|
361
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
362
|
+
'ai-gateway-protocol-version': '0.0.1',
|
|
363
|
+
'ai-gateway-auth-method': 'api-key',
|
|
364
|
+
'ai-language-model-specification-version': '3',
|
|
365
|
+
'ai-language-model-id': options.model,
|
|
366
|
+
'ai-language-model-streaming': 'true',
|
|
367
|
+
},
|
|
368
|
+
body: JSON.stringify(body),
|
|
369
|
+
signal: options.signal,
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
if (!response.ok) {
|
|
373
|
+
const errorText = await response.text();
|
|
374
|
+
logger.error({ status: response.status, body: errorText.slice(0, 500) }, 'Vercel Gateway 错误');
|
|
375
|
+
yield { type: 'error', error: friendlyHttpError(response.status, errorText, 'Vercel Gateway') };
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const reader = response.body?.getReader();
|
|
380
|
+
if (!reader) {
|
|
381
|
+
yield { type: 'error', error: '无法获取响应流' };
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
yield* parseV3Stream(reader);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export function createVercelGatewayProtocol(config: ProtocolConfig): Protocol {
|
|
390
|
+
return new VercelGatewayProtocol(config);
|
|
391
|
+
}
|