@huyooo/ai-chat-core 0.3.7 → 0.3.8

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.
Files changed (220) hide show
  1. package/dist/adapter/index.d.ts +0 -1
  2. package/dist/adapter/model-adapter.d.ts +0 -1
  3. package/dist/adapter/model-options.d.ts +0 -1
  4. package/dist/adapter/types.d.ts +0 -1
  5. package/dist/chat-runtime.d.ts +0 -1
  6. package/dist/constants.d.ts +0 -1
  7. package/dist/events.d.ts +0 -1
  8. package/dist/extension/index.d.ts +0 -1
  9. package/dist/extension/types.d.ts +0 -1
  10. package/dist/families/index.d.ts +0 -1
  11. package/dist/families/presets.d.ts +0 -1
  12. package/dist/families/resolver.d.ts +0 -1
  13. package/dist/families/types.d.ts +0 -1
  14. package/dist/governance/command-safety.d.ts +0 -1
  15. package/dist/governance/governance.d.ts +0 -1
  16. package/dist/governance/index.d.ts +0 -1
  17. package/dist/governance/types.d.ts +0 -1
  18. package/dist/index.d.ts +0 -1
  19. package/dist/internal/management-args.d.ts +0 -1
  20. package/dist/internal/management-results.d.ts +0 -1
  21. package/dist/llm-config.d.ts +0 -1
  22. package/dist/logger/core.d.ts +0 -1
  23. package/dist/logger/index.d.ts +0 -1
  24. package/dist/orchestrator/compression-handler.d.ts +0 -1
  25. package/dist/orchestrator/context-compressor.d.ts +0 -1
  26. package/dist/orchestrator/context-summarizer.d.ts +0 -1
  27. package/dist/orchestrator/index.d.ts +0 -1
  28. package/dist/orchestrator/orchestrator.d.ts +0 -1
  29. package/dist/orchestrator/types.d.ts +0 -1
  30. package/dist/parts/index.d.ts +0 -1
  31. package/dist/parts/registry.d.ts +0 -1
  32. package/dist/parts/summaries.d.ts +0 -1
  33. package/dist/parts/types.d.ts +0 -1
  34. package/dist/platform.d.ts +0 -1
  35. package/dist/protocols/anthropic.d.ts +0 -1
  36. package/dist/protocols/ark.d.ts +0 -1
  37. package/dist/protocols/deepseek.d.ts +0 -1
  38. package/dist/protocols/error-utils.d.ts +0 -1
  39. package/dist/protocols/gemini.d.ts +0 -1
  40. package/dist/protocols/glm.d.ts +0 -1
  41. package/dist/protocols/grok.d.ts +0 -1
  42. package/dist/protocols/index.d.ts +0 -1
  43. package/dist/protocols/minimax.d.ts +0 -1
  44. package/dist/protocols/moonshot.d.ts +0 -1
  45. package/dist/protocols/openai-sse.d.ts +0 -1
  46. package/dist/protocols/openai.d.ts +0 -1
  47. package/dist/protocols/qwen.d.ts +0 -1
  48. package/dist/protocols/responses-sse.d.ts +0 -1
  49. package/dist/protocols/sse-reader.d.ts +0 -1
  50. package/dist/protocols/tool-arguments.d.ts +0 -1
  51. package/dist/protocols/types.d.ts +0 -1
  52. package/dist/protocols/vercel-gateway.d.ts +0 -1
  53. package/dist/runtime.d.ts +0 -1
  54. package/dist/skills/index.d.ts +0 -1
  55. package/dist/skills/management/admin.d.ts +0 -1
  56. package/dist/skills/management/index.d.ts +0 -1
  57. package/dist/skills/management/inputs.d.ts +0 -1
  58. package/dist/skills/management/operations.d.ts +0 -1
  59. package/dist/skills/management/types.d.ts +0 -1
  60. package/dist/skills/registry.d.ts +0 -1
  61. package/dist/skills/summaries.d.ts +0 -1
  62. package/dist/skills/types.d.ts +0 -1
  63. package/dist/test-utils/mock-sse.d.ts +0 -1
  64. package/dist/tool-manager/define-tool.d.ts +0 -1
  65. package/dist/tool-manager/formats.d.ts +0 -1
  66. package/dist/tool-manager/identity.d.ts +0 -1
  67. package/dist/tool-manager/in-process-provider.d.ts +0 -1
  68. package/dist/tool-manager/index.d.ts +0 -1
  69. package/dist/tool-manager/manager.d.ts +0 -1
  70. package/dist/tool-manager/mcp-provider.d.ts +0 -1
  71. package/dist/tool-manager/summaries.d.ts +0 -1
  72. package/dist/tool-manager/types.d.ts +0 -1
  73. package/dist/types.d.ts +0 -1
  74. package/package.json +2 -3
  75. package/dist/adapter/index.d.ts.map +0 -1
  76. package/dist/adapter/model-adapter.d.ts.map +0 -1
  77. package/dist/adapter/model-options.d.ts.map +0 -1
  78. package/dist/adapter/types.d.ts.map +0 -1
  79. package/dist/chat-runtime.d.ts.map +0 -1
  80. package/dist/constants.d.ts.map +0 -1
  81. package/dist/events.d.ts.map +0 -1
  82. package/dist/extension/index.d.ts.map +0 -1
  83. package/dist/extension/types.d.ts.map +0 -1
  84. package/dist/families/index.d.ts.map +0 -1
  85. package/dist/families/presets.d.ts.map +0 -1
  86. package/dist/families/resolver.d.ts.map +0 -1
  87. package/dist/families/types.d.ts.map +0 -1
  88. package/dist/governance/command-safety.d.ts.map +0 -1
  89. package/dist/governance/governance.d.ts.map +0 -1
  90. package/dist/governance/index.d.ts.map +0 -1
  91. package/dist/governance/types.d.ts.map +0 -1
  92. package/dist/index.d.ts.map +0 -1
  93. package/dist/internal/management-args.d.ts.map +0 -1
  94. package/dist/internal/management-results.d.ts.map +0 -1
  95. package/dist/llm-config.d.ts.map +0 -1
  96. package/dist/logger/core.d.ts.map +0 -1
  97. package/dist/logger/index.d.ts.map +0 -1
  98. package/dist/orchestrator/compression-handler.d.ts.map +0 -1
  99. package/dist/orchestrator/context-compressor.d.ts.map +0 -1
  100. package/dist/orchestrator/context-summarizer.d.ts.map +0 -1
  101. package/dist/orchestrator/index.d.ts.map +0 -1
  102. package/dist/orchestrator/orchestrator.d.ts.map +0 -1
  103. package/dist/orchestrator/types.d.ts.map +0 -1
  104. package/dist/parts/index.d.ts.map +0 -1
  105. package/dist/parts/registry.d.ts.map +0 -1
  106. package/dist/parts/summaries.d.ts.map +0 -1
  107. package/dist/parts/types.d.ts.map +0 -1
  108. package/dist/platform.d.ts.map +0 -1
  109. package/dist/protocols/anthropic.d.ts.map +0 -1
  110. package/dist/protocols/ark.d.ts.map +0 -1
  111. package/dist/protocols/deepseek.d.ts.map +0 -1
  112. package/dist/protocols/error-utils.d.ts.map +0 -1
  113. package/dist/protocols/gemini.d.ts.map +0 -1
  114. package/dist/protocols/glm.d.ts.map +0 -1
  115. package/dist/protocols/grok.d.ts.map +0 -1
  116. package/dist/protocols/index.d.ts.map +0 -1
  117. package/dist/protocols/minimax.d.ts.map +0 -1
  118. package/dist/protocols/moonshot.d.ts.map +0 -1
  119. package/dist/protocols/openai-sse.d.ts.map +0 -1
  120. package/dist/protocols/openai.d.ts.map +0 -1
  121. package/dist/protocols/qwen.d.ts.map +0 -1
  122. package/dist/protocols/responses-sse.d.ts.map +0 -1
  123. package/dist/protocols/sse-reader.d.ts.map +0 -1
  124. package/dist/protocols/tool-arguments.d.ts.map +0 -1
  125. package/dist/protocols/types.d.ts.map +0 -1
  126. package/dist/protocols/vercel-gateway.d.ts.map +0 -1
  127. package/dist/runtime.d.ts.map +0 -1
  128. package/dist/skills/index.d.ts.map +0 -1
  129. package/dist/skills/management/admin.d.ts.map +0 -1
  130. package/dist/skills/management/index.d.ts.map +0 -1
  131. package/dist/skills/management/inputs.d.ts.map +0 -1
  132. package/dist/skills/management/operations.d.ts.map +0 -1
  133. package/dist/skills/management/types.d.ts.map +0 -1
  134. package/dist/skills/registry.d.ts.map +0 -1
  135. package/dist/skills/summaries.d.ts.map +0 -1
  136. package/dist/skills/types.d.ts.map +0 -1
  137. package/dist/test-utils/mock-sse.d.ts.map +0 -1
  138. package/dist/tool-manager/define-tool.d.ts.map +0 -1
  139. package/dist/tool-manager/formats.d.ts.map +0 -1
  140. package/dist/tool-manager/identity.d.ts.map +0 -1
  141. package/dist/tool-manager/in-process-provider.d.ts.map +0 -1
  142. package/dist/tool-manager/index.d.ts.map +0 -1
  143. package/dist/tool-manager/manager.d.ts.map +0 -1
  144. package/dist/tool-manager/mcp-provider.d.ts.map +0 -1
  145. package/dist/tool-manager/summaries.d.ts.map +0 -1
  146. package/dist/tool-manager/types.d.ts.map +0 -1
  147. package/dist/types.d.ts.map +0 -1
  148. package/src/adapter/index.ts +0 -25
  149. package/src/adapter/model-adapter.ts +0 -196
  150. package/src/adapter/model-options.ts +0 -143
  151. package/src/adapter/types.ts +0 -41
  152. package/src/chat-runtime.ts +0 -515
  153. package/src/constants.ts +0 -32
  154. package/src/events.ts +0 -1084
  155. package/src/extension/index.ts +0 -24
  156. package/src/extension/types.ts +0 -49
  157. package/src/families/index.ts +0 -28
  158. package/src/families/presets.ts +0 -124
  159. package/src/families/resolver.ts +0 -22
  160. package/src/families/types.ts +0 -55
  161. package/src/governance/command-safety.ts +0 -224
  162. package/src/governance/governance.ts +0 -125
  163. package/src/governance/index.ts +0 -38
  164. package/src/governance/types.ts +0 -44
  165. package/src/index.ts +0 -426
  166. package/src/internal/management-args.ts +0 -39
  167. package/src/internal/management-results.ts +0 -60
  168. package/src/llm-config.ts +0 -137
  169. package/src/logger/core.ts +0 -96
  170. package/src/logger/index.ts +0 -8
  171. package/src/orchestrator/compression-handler.ts +0 -137
  172. package/src/orchestrator/context-compressor.ts +0 -249
  173. package/src/orchestrator/context-summarizer.ts +0 -123
  174. package/src/orchestrator/index.ts +0 -20
  175. package/src/orchestrator/orchestrator.ts +0 -1002
  176. package/src/orchestrator/types.ts +0 -70
  177. package/src/parts/index.ts +0 -20
  178. package/src/parts/registry.ts +0 -95
  179. package/src/parts/summaries.ts +0 -40
  180. package/src/parts/types.ts +0 -63
  181. package/src/platform.ts +0 -73
  182. package/src/protocols/anthropic.ts +0 -377
  183. package/src/protocols/ark.ts +0 -300
  184. package/src/protocols/deepseek.ts +0 -192
  185. package/src/protocols/error-utils.ts +0 -71
  186. package/src/protocols/gemini.ts +0 -352
  187. package/src/protocols/glm.ts +0 -212
  188. package/src/protocols/grok.ts +0 -98
  189. package/src/protocols/index.ts +0 -48
  190. package/src/protocols/minimax.ts +0 -308
  191. package/src/protocols/moonshot.ts +0 -186
  192. package/src/protocols/openai-sse.ts +0 -156
  193. package/src/protocols/openai.ts +0 -97
  194. package/src/protocols/qwen.ts +0 -358
  195. package/src/protocols/responses-sse.ts +0 -224
  196. package/src/protocols/sse-reader.ts +0 -54
  197. package/src/protocols/tool-arguments.ts +0 -32
  198. package/src/protocols/types.ts +0 -198
  199. package/src/protocols/vercel-gateway.ts +0 -391
  200. package/src/runtime.ts +0 -167
  201. package/src/skills/index.ts +0 -29
  202. package/src/skills/management/admin.ts +0 -170
  203. package/src/skills/management/index.ts +0 -27
  204. package/src/skills/management/inputs.ts +0 -79
  205. package/src/skills/management/operations.ts +0 -256
  206. package/src/skills/management/types.ts +0 -57
  207. package/src/skills/registry.ts +0 -120
  208. package/src/skills/summaries.ts +0 -48
  209. package/src/skills/types.ts +0 -65
  210. package/src/test-utils/mock-sse.ts +0 -32
  211. package/src/tool-manager/define-tool.ts +0 -201
  212. package/src/tool-manager/formats.ts +0 -146
  213. package/src/tool-manager/identity.ts +0 -80
  214. package/src/tool-manager/in-process-provider.ts +0 -164
  215. package/src/tool-manager/index.ts +0 -63
  216. package/src/tool-manager/manager.ts +0 -562
  217. package/src/tool-manager/mcp-provider.ts +0 -509
  218. package/src/tool-manager/summaries.ts +0 -136
  219. package/src/tool-manager/types.ts +0 -389
  220. package/src/types.ts +0 -1142
@@ -1,377 +0,0 @@
1
- /**
2
- * Anthropic Protocol(原生 Messages API)
3
- *
4
- * 完整实现 Anthropic Messages API(/v1/messages):
5
- * - SSE 事件流:message_start → content_block_start/delta/stop → message_delta → message_stop
6
- * - Extended thinking:thinking_delta + signature_delta(signature 用于工具调用循环回传)
7
- * - 工具调用:tool_use content block + input_json_delta
8
- * - 支持 AIMLAPI 代理(Bearer auth)和 Anthropic 直连(x-api-key auth)
9
- */
10
-
11
- import type {
12
- Protocol,
13
- ProtocolConfig,
14
- ProtocolMessage,
15
- ProtocolToolDefinition,
16
- ProtocolRequestOptions,
17
- RawEvent,
18
- RawToolCall,
19
- RawTokenUsage,
20
- } from './types';
21
- import { DEFAULT_AIMLAPI_URL } from '../constants';
22
- import { createModuleLogger } from '../logger';
23
- import { friendlyHttpError } from './error-utils';
24
- import { readSSEJsonStream } from './sse-reader';
25
- import { parseProtocolToolArguments } from './tool-arguments';
26
-
27
- const logger = createModuleLogger('AnthropicProtocol');
28
-
29
- const ANTHROPIC_DIRECT_URL = 'https://api.anthropic.com/v1';
30
-
31
- export class AnthropicProtocol implements Protocol {
32
- readonly name = 'anthropic';
33
-
34
- private apiKey: string;
35
- private apiUrl: string;
36
- private isDirect: boolean;
37
-
38
- constructor(config: ProtocolConfig) {
39
- this.apiKey = config.apiKey;
40
- this.apiUrl = config.apiUrl ?? DEFAULT_AIMLAPI_URL;
41
- this.isDirect = this.apiUrl.includes('api.anthropic.com');
42
- }
43
-
44
- async *stream(
45
- messages: ProtocolMessage[],
46
- tools: ProtocolToolDefinition[],
47
- options: ProtocolRequestOptions
48
- ): AsyncGenerator<RawEvent> {
49
- const { systemPrompt, convertedMessages } = convertMessages(messages);
50
- const requestBody = buildRequestBody(systemPrompt, convertedMessages, tools, options);
51
- const url = `${this.apiUrl}/messages`;
52
-
53
- logger.debug({
54
- url,
55
- model: options.model,
56
- enableThinking: options.enableThinking,
57
- toolsCount: tools.length,
58
- }, '发送 Anthropic 请求');
59
-
60
- const headers: Record<string, string> = {
61
- 'Content-Type': 'application/json',
62
- 'anthropic-version': '2023-06-01',
63
- };
64
-
65
- // Anthropic 直连用 x-api-key,代理用 Bearer
66
- if (this.isDirect) {
67
- headers['x-api-key'] = this.apiKey;
68
- } else {
69
- headers['Authorization'] = `Bearer ${this.apiKey}`;
70
- }
71
-
72
- const response = await fetch(url, {
73
- method: 'POST',
74
- headers,
75
- body: JSON.stringify(requestBody),
76
- signal: options.signal,
77
- });
78
-
79
- if (!response.ok) {
80
- const errorText = await response.text();
81
- logger.error({ status: response.status, body: errorText.slice(0, 500) }, 'Anthropic API 错误');
82
- yield { type: 'error', error: friendlyHttpError(response.status, errorText, 'Anthropic') };
83
- return;
84
- }
85
-
86
- const reader = response.body?.getReader();
87
- if (!reader) {
88
- yield { type: 'error', error: '无法获取响应流' };
89
- return;
90
- }
91
-
92
- yield* parseSSE(reader);
93
- }
94
- }
95
-
96
- // ==================== 请求体构建 ====================
97
-
98
- function buildRequestBody(
99
- systemPrompt: string | undefined,
100
- messages: unknown[],
101
- tools: ProtocolToolDefinition[],
102
- options: ProtocolRequestOptions
103
- ): Record<string, unknown> {
104
- const maxTokens = options.maxOutputTokens;
105
-
106
- const body: Record<string, unknown> = {
107
- model: options.model,
108
- messages,
109
- max_tokens: maxTokens,
110
- stream: true,
111
- };
112
-
113
- if (systemPrompt) {
114
- body.system = systemPrompt;
115
- }
116
-
117
- if (options.enableThinking) {
118
- // Opus 4.6+ 推荐 adaptive,Sonnet 等用 enabled + budget_tokens
119
- const isOpus46 = options.model.includes('opus-4-6') || options.model.includes('opus-4.6');
120
- if (isOpus46) {
121
- body.thinking = { type: 'adaptive' };
122
- } else {
123
- const budgetTokens = Math.min(10000, maxTokens - 1);
124
- body.thinking = { type: 'enabled', budget_tokens: budgetTokens };
125
- }
126
- }
127
-
128
- if (tools.length > 0) {
129
- body.tools = tools.map(t => ({
130
- name: t.name,
131
- description: t.description,
132
- input_schema: t.parameters,
133
- }));
134
- }
135
-
136
- return body;
137
- }
138
-
139
- // ==================== 消息转换 ====================
140
-
141
- /**
142
- * system 消息提取为独立参数,其余转为 user/assistant/tool 序列
143
- */
144
- function convertMessages(messages: ProtocolMessage[]): { systemPrompt?: string; convertedMessages: unknown[] } {
145
- let systemPrompt: string | undefined;
146
- const result: unknown[] = [];
147
-
148
- for (const msg of messages) {
149
- switch (msg.role) {
150
- case 'system':
151
- systemPrompt = msg.content;
152
- break;
153
-
154
- case 'user': {
155
- const textContent = msg.content || (msg.images?.length ? '请分析这张图片' : '');
156
- if (msg.images?.length) {
157
- const content: unknown[] = [{ type: 'text', text: textContent }];
158
- for (const img of msg.images) {
159
- const imageData = img.startsWith('data:')
160
- ? img.replace(/^data:image\/\w+;base64,/, '')
161
- : img;
162
- content.push({
163
- type: 'image',
164
- source: { type: 'base64', media_type: 'image/jpeg', data: imageData },
165
- });
166
- }
167
- result.push({ role: 'user', content });
168
- } else {
169
- result.push({ role: 'user', content: textContent });
170
- }
171
- break;
172
- }
173
-
174
- case 'assistant':
175
- if (msg.toolCalls?.length) {
176
- const content: unknown[] = [];
177
- if (msg.thinkingContent) {
178
- content.push({
179
- type: 'thinking',
180
- thinking: msg.thinkingContent,
181
- signature: msg.thinkingSignature || '',
182
- });
183
- }
184
- if (msg.content) {
185
- content.push({ type: 'text', text: msg.content });
186
- }
187
- for (const tc of msg.toolCalls) {
188
- content.push({
189
- type: 'tool_use',
190
- id: tc.id,
191
- name: tc.name,
192
- input: parseProtocolToolArguments(tc.arguments, {
193
- protocol: 'anthropic',
194
- toolCallId: tc.id,
195
- toolName: tc.name,
196
- }),
197
- });
198
- }
199
- result.push({ role: 'assistant', content });
200
- } else if (msg.content) {
201
- result.push({ role: 'assistant', content: msg.content });
202
- }
203
- break;
204
-
205
- case 'tool':
206
- result.push({
207
- role: 'user',
208
- content: [{
209
- type: 'tool_result',
210
- tool_use_id: msg.toolCallId,
211
- content: msg.content,
212
- }],
213
- });
214
- break;
215
- }
216
- }
217
-
218
- return { systemPrompt, convertedMessages: result };
219
- }
220
-
221
- // ==================== SSE 流解析 ====================
222
-
223
- /**
224
- * 解析 Anthropic 原生 SSE 流
225
- *
226
- * Layer 1 负责 bytes → JSON,此处通过 json.type 做语义映射。
227
- * Anthropic SSE 的 event: 行与 json.type 一致,直接用 json.type 即可。
228
- */
229
- async function* parseSSE(
230
- reader: ReadableStreamDefaultReader<Uint8Array>,
231
- ): AsyncGenerator<RawEvent> {
232
- let activeBlockType: 'thinking' | 'text' | 'tool_use' | '' = '';
233
- const toolCalls = new Map<string, RawToolCall>();
234
- let currentToolId = '';
235
- let hasThinking = false;
236
- let thinkingSignature = '';
237
- let usage: RawTokenUsage | undefined;
238
-
239
- for await (const json of readSSEJsonStream(reader)) {
240
- switch (json.type) {
241
- case 'message_start': {
242
- const msgUsage = (json.message as Record<string, unknown>)?.usage as Record<string, number> | undefined;
243
- if (msgUsage) {
244
- usage = {
245
- promptTokens: msgUsage.input_tokens ?? 0,
246
- completionTokens: msgUsage.output_tokens ?? 0,
247
- totalTokens: (msgUsage.input_tokens ?? 0) + (msgUsage.output_tokens ?? 0),
248
- cachedTokens: (msgUsage.cache_read_input_tokens ?? 0) + (msgUsage.cache_creation_input_tokens ?? 0),
249
- };
250
- }
251
- break;
252
- }
253
-
254
- case 'content_block_start': {
255
- const block = json.content_block as Record<string, unknown> | undefined;
256
- if (!block) break;
257
-
258
- switch (block.type) {
259
- case 'thinking':
260
- activeBlockType = 'thinking';
261
- break;
262
- case 'text':
263
- activeBlockType = 'text';
264
- break;
265
- case 'tool_use': {
266
- activeBlockType = 'tool_use';
267
- const id = block.id as string;
268
- const name = block.name as string;
269
- currentToolId = id;
270
- toolCalls.set(id, { id, name, arguments: '' });
271
- yield { type: 'tool_call_start', toolCall: { id, name } };
272
- break;
273
- }
274
- }
275
- break;
276
- }
277
-
278
- case 'content_block_delta': {
279
- const delta = json.delta as Record<string, unknown> | undefined;
280
- if (!delta) break;
281
-
282
- switch (delta.type) {
283
- case 'thinking_delta':
284
- if (delta.thinking) {
285
- hasThinking = true;
286
- yield { type: 'thinking_delta', delta: delta.thinking as string };
287
- }
288
- break;
289
-
290
- case 'signature_delta':
291
- if (delta.signature) {
292
- thinkingSignature = delta.signature as string;
293
- }
294
- break;
295
-
296
- case 'text_delta':
297
- if (delta.text) {
298
- yield { type: 'text_delta', delta: delta.text as string };
299
- }
300
- break;
301
-
302
- case 'input_json_delta':
303
- if (currentToolId && delta.partial_json) {
304
- const tc = toolCalls.get(currentToolId);
305
- if (tc) {
306
- tc.arguments += delta.partial_json;
307
- yield { type: 'tool_call_delta', toolCall: { id: currentToolId, arguments: delta.partial_json as string } };
308
- }
309
- }
310
- break;
311
- }
312
- break;
313
- }
314
-
315
- case 'content_block_stop': {
316
- if (activeBlockType === 'thinking' && hasThinking) {
317
- yield { type: 'thinking_done', thinkingSignature };
318
- } else if (activeBlockType === 'tool_use' && currentToolId) {
319
- const tc = toolCalls.get(currentToolId);
320
- if (tc) {
321
- yield { type: 'tool_call_done', toolCall: tc };
322
- }
323
- currentToolId = '';
324
- }
325
- activeBlockType = '';
326
- break;
327
- }
328
-
329
- case 'message_delta': {
330
- const delta = json.delta as Record<string, unknown> | undefined;
331
- const deltaUsage = json.usage as Record<string, number> | undefined;
332
-
333
- if (deltaUsage) {
334
- const inputTokens = usage?.promptTokens ?? 0;
335
- const outputTokens = deltaUsage.output_tokens ?? usage?.completionTokens ?? 0;
336
- const cachedTokens = (deltaUsage.cache_read_input_tokens ?? 0) + (deltaUsage.cache_creation_input_tokens ?? 0);
337
- usage = {
338
- promptTokens: inputTokens,
339
- completionTokens: outputTokens,
340
- totalTokens: inputTokens + outputTokens,
341
- cachedTokens: cachedTokens || usage?.cachedTokens,
342
- };
343
- }
344
-
345
- const stopReason = delta?.stop_reason as string | undefined;
346
- if (stopReason) {
347
- const finishReason: RawEvent['finishReason'] =
348
- stopReason === 'tool_use' ? 'tool_calls' :
349
- stopReason === 'max_tokens' ? 'length' :
350
- 'stop';
351
- yield { type: 'done', finishReason, usage };
352
- return;
353
- }
354
- break;
355
- }
356
-
357
- case 'message_stop':
358
- yield { type: 'done', finishReason: toolCalls.size > 0 ? 'tool_calls' : 'stop', usage };
359
- return;
360
-
361
- case 'error': {
362
- const err = json.error as Record<string, unknown> | undefined;
363
- const errorMsg = (err?.message as string) || JSON.stringify(json);
364
- logger.error({ error: errorMsg }, 'Anthropic SSE error');
365
- yield { type: 'error', error: errorMsg };
366
- return;
367
- }
368
- }
369
- }
370
-
371
- // 流异常结束兜底
372
- yield { type: 'done', finishReason: toolCalls.size > 0 ? 'tool_calls' : 'stop', usage };
373
- }
374
-
375
- export function createAnthropicProtocol(config: ProtocolConfig): AnthropicProtocol {
376
- return new AnthropicProtocol(config);
377
- }
@@ -1,300 +0,0 @@
1
- /**
2
- * ARK Protocol(火山引擎 Responses API)
3
- *
4
- * 只负责:
5
- * - HTTP 请求发送
6
- * - SSE 流解析
7
- * - 原始事件产出
8
- */
9
-
10
- import type {
11
- Protocol,
12
- ProtocolConfig,
13
- ProtocolMessage,
14
- ProtocolToolDefinition,
15
- ProtocolRequestOptions,
16
- RawEvent,
17
- RawToolCall,
18
- RawTokenUsage,
19
- } from './types';
20
- import { DEFAULT_ARK_URL } from '../constants';
21
- import { createModuleLogger } from '../logger';
22
- import { friendlyHttpError } from './error-utils';
23
- import { readSSEJsonStream } from './sse-reader';
24
-
25
- const logger = createModuleLogger('ArkProtocol');
26
-
27
- /**
28
- * ARK Protocol 实现
29
- */
30
- export class ArkProtocol implements Protocol {
31
- readonly name = 'ark';
32
-
33
- private apiKey: string;
34
- private apiUrl: string;
35
-
36
- constructor(config: ProtocolConfig) {
37
- this.apiKey = config.apiKey;
38
- this.apiUrl = config.apiUrl ?? DEFAULT_ARK_URL;
39
- }
40
-
41
- /**
42
- * 发送请求并返回原始事件流
43
- */
44
- async *stream(
45
- messages: ProtocolMessage[],
46
- tools: ProtocolToolDefinition[],
47
- options: ProtocolRequestOptions
48
- ): AsyncGenerator<RawEvent> {
49
- // 构建请求体
50
- const requestBody = this.buildRequestBody(messages, tools, options);
51
-
52
- logger.debug({
53
- url: `${this.apiUrl}/responses`,
54
- model: options.model,
55
- enableThinking: options.enableThinking,
56
- toolsCount: tools.length,
57
- }, '发送 ARK 请求');
58
-
59
- // 发送请求
60
- const response = await fetch(`${this.apiUrl}/responses`, {
61
- method: 'POST',
62
- headers: {
63
- 'Authorization': `Bearer ${this.apiKey}`,
64
- 'Content-Type': 'application/json',
65
- },
66
- body: JSON.stringify(requestBody),
67
- signal: options.signal,
68
- });
69
-
70
- if (!response.ok) {
71
- const errorText = await response.text();
72
- logger.error({ status: response.status, body: errorText.slice(0, 500) }, 'ARK API 错误');
73
- yield { type: 'error', error: friendlyHttpError(response.status, errorText, 'ARK') };
74
- return;
75
- }
76
-
77
- const reader = response.body?.getReader();
78
- if (!reader) {
79
- yield { type: 'error', error: '无法获取响应流' };
80
- return;
81
- }
82
-
83
- yield* this.parseSSE(reader);
84
- }
85
-
86
- /**
87
- * 构建请求体
88
- */
89
- private buildRequestBody(
90
- messages: ProtocolMessage[],
91
- tools: ProtocolToolDefinition[],
92
- options: ProtocolRequestOptions
93
- ): Record<string, unknown> {
94
- const input = this.convertMessages(messages);
95
-
96
- const body: Record<string, unknown> = {
97
- model: options.model,
98
- stream: true,
99
- max_output_tokens: options.maxOutputTokens,
100
- input,
101
- // ARK 不支持 include: ['usage'],通过 response.completed 事件的 usage 字段获取
102
- };
103
-
104
- const apiTools: unknown[] = [];
105
-
106
- for (const t of tools) {
107
- apiTools.push({
108
- type: 'function',
109
- name: t.name,
110
- description: t.description,
111
- parameters: t.parameters,
112
- });
113
- }
114
-
115
- if (apiTools.length > 0) {
116
- body.tools = apiTools;
117
- }
118
-
119
- // 启用 thinking
120
- if (options.enableThinking) {
121
- body.thinking = { type: 'enabled' };
122
- }
123
-
124
- return body;
125
- }
126
-
127
- /**
128
- * 转换消息格式
129
- */
130
- private convertMessages(messages: ProtocolMessage[]): unknown[] {
131
- const input: unknown[] = [];
132
-
133
- for (const msg of messages) {
134
- switch (msg.role) {
135
- case 'system':
136
- input.push({ role: 'system', content: msg.content });
137
- break;
138
-
139
- case 'user': {
140
- // ARK API 要求 input_text 必须有内容,当只有图片时提供默认提示
141
- const textContent = msg.content || (msg.images?.length ? '请分析这张图片' : '');
142
- const content: unknown[] = [{ type: 'input_text', text: textContent }];
143
- if (msg.images?.length) {
144
- for (const img of msg.images) {
145
- content.push({
146
- type: 'input_image',
147
- image_url: img.startsWith('data:') ? img : `data:image/jpeg;base64,${img}`,
148
- });
149
- }
150
- }
151
- input.push({ role: 'user', content });
152
- break;
153
- }
154
-
155
- case 'assistant':
156
- if (msg.toolCalls?.length) {
157
- // 如果 assistant 同时有文本和工具调用,先保留文本内容
158
- // 避免模型丢失推理上下文导致重复调用工具
159
- if (msg.content) {
160
- input.push({
161
- type: 'message',
162
- role: 'assistant',
163
- content: [{ type: 'output_text', text: msg.content }],
164
- });
165
- }
166
- for (const tc of msg.toolCalls) {
167
- input.push({
168
- type: 'function_call',
169
- call_id: tc.id,
170
- name: tc.name,
171
- arguments: tc.arguments,
172
- });
173
- }
174
- } else {
175
- input.push({
176
- role: 'developer',
177
- content: `[上一轮AI回复]: ${msg.content}`,
178
- });
179
- }
180
- break;
181
-
182
- case 'tool':
183
- input.push({
184
- type: 'function_call_output',
185
- call_id: msg.toolCallId,
186
- output: msg.content,
187
- });
188
- break;
189
- }
190
- }
191
-
192
- return input;
193
- }
194
-
195
- private async *parseSSE(reader: ReadableStreamDefaultReader<Uint8Array>): AsyncGenerator<RawEvent> {
196
- const pendingToolCalls = new Map<string, RawToolCall>();
197
- let currentFunctionCallId: string | null = null;
198
- let thinkingDone = false;
199
- let textStarted = false;
200
- let lastUsage: RawTokenUsage | undefined;
201
-
202
- for await (const event of readSSEJsonStream(reader)) {
203
- const eventUsage = (event.response as Record<string, unknown>)?.usage ?? event.usage;
204
- if (eventUsage) {
205
- const u = eventUsage as Record<string, unknown>;
206
- const inputTokens = (u.input_tokens as number) ?? (u.prompt_tokens as number) ?? 0;
207
- const outputTokens = (u.output_tokens as number) ?? (u.completion_tokens as number) ?? 0;
208
- const outputDetails = u.output_tokens_details as Record<string, number> | undefined;
209
- const inputDetails = u.input_tokens_details as Record<string, number> | undefined;
210
- lastUsage = {
211
- promptTokens: inputTokens,
212
- completionTokens: outputTokens,
213
- totalTokens: inputTokens + outputTokens,
214
- reasoningTokens: outputDetails?.reasoning_tokens ?? 0,
215
- cachedTokens: inputDetails?.cached_tokens ?? 0,
216
- };
217
- }
218
-
219
- switch (event.type) {
220
- case 'response.output_item.added': {
221
- const item = event.item as Record<string, unknown> | undefined;
222
- if (item?.type === 'function_call' && item.call_id) {
223
- if (!item.name) logger.warn({ id: item.call_id }, 'function_call added 缺少 name');
224
- currentFunctionCallId = item.call_id as string;
225
- pendingToolCalls.set(currentFunctionCallId, {
226
- id: currentFunctionCallId,
227
- name: (item.name as string) || '',
228
- arguments: (item.arguments as string) || '',
229
- });
230
- yield { type: 'tool_call_start', toolCall: { id: currentFunctionCallId, name: (item.name as string) || '' } };
231
- }
232
- break;
233
- }
234
-
235
- case 'response.function_call_arguments.delta': {
236
- if (currentFunctionCallId) {
237
- const call = pendingToolCalls.get(currentFunctionCallId);
238
- if (call) {
239
- call.arguments += (event.delta as string) || '';
240
- yield { type: 'tool_call_delta', toolCall: { id: currentFunctionCallId, arguments: (event.delta as string) || '' } };
241
- }
242
- }
243
- break;
244
- }
245
-
246
- case 'response.function_call_arguments.done':
247
- case 'response.output_item.done': {
248
- const item = event.item as Record<string, unknown> | undefined;
249
- if (item?.type === 'function_call' && item.call_id) {
250
- const callId = item.call_id as string;
251
- const existing = pendingToolCalls.get(callId);
252
- pendingToolCalls.set(callId, {
253
- id: callId,
254
- name: (item.name as string) || existing?.name || '',
255
- arguments: (item.arguments as string) || existing?.arguments || '{}',
256
- });
257
- }
258
- break;
259
- }
260
-
261
- case 'response.output_text.delta':
262
- if (event.delta) {
263
- if (!textStarted) {
264
- textStarted = true;
265
- if (!thinkingDone) {
266
- thinkingDone = true;
267
- yield { type: 'thinking_done' };
268
- }
269
- }
270
- yield { type: 'text_delta', delta: event.delta as string };
271
- }
272
- break;
273
-
274
- case 'response.reasoning_summary_text.delta':
275
- if (event.delta && !thinkingDone) {
276
- yield { type: 'thinking_delta', delta: event.delta as string };
277
- }
278
- break;
279
- }
280
- }
281
-
282
- // 流结束兜底
283
- if (pendingToolCalls.size > 0) {
284
- for (const tc of pendingToolCalls.values()) {
285
- yield { type: 'tool_call_done', toolCall: tc };
286
- }
287
- yield { type: 'done', finishReason: 'tool_calls', usage: lastUsage };
288
- } else {
289
- yield { type: 'done', finishReason: 'stop', usage: lastUsage };
290
- }
291
- }
292
- }
293
-
294
- /**
295
- * 创建 ARK Protocol
296
- */
297
- export function createArkProtocol(config: ProtocolConfig): ArkProtocol {
298
- return new ArkProtocol(config);
299
- }
300
-