aicodeswitch 4.0.4 → 5.1.0

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 (77) hide show
  1. package/README.md +6 -5
  2. package/UPGRADE.md +5 -6
  3. package/dist/server/coding-plan.js +94 -0
  4. package/dist/server/config-managed-fields.js +1 -0
  5. package/dist/server/conversions/compact.js +613 -0
  6. package/dist/server/conversions/detector.js +70 -0
  7. package/dist/server/conversions/index.js +290 -0
  8. package/dist/server/conversions/pairs/claude-completions/request.js +167 -0
  9. package/dist/server/conversions/pairs/claude-completions/response.js +56 -0
  10. package/dist/server/conversions/pairs/claude-completions/streaming.js +259 -0
  11. package/dist/server/conversions/pairs/claude-gemini/request.js +130 -0
  12. package/dist/server/conversions/pairs/claude-gemini/response.js +65 -0
  13. package/dist/server/conversions/pairs/claude-gemini/streaming.js +199 -0
  14. package/dist/server/conversions/pairs/claude-responses/request.js +190 -0
  15. package/dist/server/conversions/pairs/claude-responses/response.js +89 -0
  16. package/dist/server/conversions/pairs/claude-responses/streaming.js +266 -0
  17. package/dist/server/conversions/pairs/completions-claude/request.js +111 -0
  18. package/dist/server/conversions/pairs/completions-claude/response.js +67 -0
  19. package/dist/server/conversions/pairs/completions-claude/streaming.js +165 -0
  20. package/dist/server/conversions/pairs/completions-gemini/request.js +169 -0
  21. package/dist/server/conversions/pairs/completions-gemini/response.js +70 -0
  22. package/dist/server/conversions/pairs/completions-gemini/streaming.js +132 -0
  23. package/dist/server/conversions/pairs/completions-responses/request.js +149 -0
  24. package/dist/server/conversions/pairs/completions-responses/response.js +74 -0
  25. package/dist/server/conversions/pairs/completions-responses/streaming.js +189 -0
  26. package/dist/server/conversions/pairs/gemini-claude/request.js +118 -0
  27. package/dist/server/conversions/pairs/gemini-claude/response.js +45 -0
  28. package/dist/server/conversions/pairs/gemini-claude/streaming.js +146 -0
  29. package/dist/server/conversions/pairs/gemini-completions/request.js +151 -0
  30. package/dist/server/conversions/pairs/gemini-completions/response.js +54 -0
  31. package/dist/server/conversions/pairs/gemini-completions/streaming.js +108 -0
  32. package/dist/server/conversions/pairs/gemini-responses/request.js +18 -0
  33. package/dist/server/conversions/pairs/gemini-responses/response.js +18 -0
  34. package/dist/server/conversions/pairs/gemini-responses/streaming.js +43 -0
  35. package/dist/server/conversions/pairs/responses-claude/request.js +180 -0
  36. package/dist/server/conversions/pairs/responses-claude/response.js +70 -0
  37. package/dist/server/conversions/pairs/responses-claude/streaming.js +345 -0
  38. package/dist/server/conversions/pairs/responses-completions/request.js +207 -0
  39. package/dist/server/conversions/pairs/responses-completions/response.js +96 -0
  40. package/dist/server/conversions/pairs/responses-completions/streaming.js +344 -0
  41. package/dist/server/conversions/pairs/responses-gemini/request.js +18 -0
  42. package/dist/server/conversions/pairs/responses-gemini/response.js +18 -0
  43. package/dist/server/conversions/pairs/responses-gemini/streaming.js +43 -0
  44. package/dist/server/conversions/pairs/responses-responses/request.js +115 -0
  45. package/dist/server/conversions/pipeline.js +296 -0
  46. package/dist/server/conversions/stream-converter-adapter.js +49 -0
  47. package/dist/server/conversions/thinking/effort.js +61 -0
  48. package/dist/server/conversions/thinking/mapper.js +59 -0
  49. package/dist/server/conversions/thinking/providers.js +80 -0
  50. package/dist/server/conversions/types.js +5 -0
  51. package/dist/server/conversions/url-normalizer.js +58 -0
  52. package/dist/server/conversions/utils/format-mappers.js +57 -0
  53. package/dist/server/conversions/utils/id.js +33 -0
  54. package/dist/server/conversions/utils/stop-reasons.js +95 -0
  55. package/dist/server/conversions/utils/streaming-helpers.js +59 -0
  56. package/dist/server/conversions/utils/tool-schema.js +169 -0
  57. package/dist/server/conversions/utils/usage.js +82 -0
  58. package/dist/server/fs-database.js +465 -135
  59. package/dist/server/main.js +93 -33
  60. package/dist/server/original-config-reader.js +1 -1
  61. package/dist/server/proxy-server.js +887 -633
  62. package/dist/server/transformers/chunk-collector.js +5 -1
  63. package/dist/server/transformers/streaming.js +6 -3235
  64. package/dist/server/type-migration.js +2 -3
  65. package/dist/server/utils.js +5 -0
  66. package/dist/ui/assets/{index-C7G0whng.css → index-BHR12ImE.css} +1 -1
  67. package/dist/ui/assets/index-Rwiqttz-.js +517 -0
  68. package/dist/ui/index.html +2 -2
  69. package/package.json +1 -1
  70. package/dist/server/transformers/transformers.js +0 -1767
  71. package/dist/ui/assets/index-Dl-B9pXM.js +0 -514
  72. package/schema/claude.schema.md +0 -946
  73. package/schema/deepseek-chat.schema.md +0 -799
  74. package/schema/gemini.schema.md +0 -1408
  75. package/schema/openai-chat-completions.schema.md +0 -1088
  76. package/schema/openai-responses.schema.md +0 -226196
  77. package/schema/stream.md +0 -2592
@@ -1,1767 +0,0 @@
1
- "use strict";
2
- /**
3
- * 请求转换器统一导出文件
4
- * 提供语义化的函数名,明确表示转换的方向
5
- */
6
- var __rest = (this && this.__rest) || function (s, e) {
7
- var t = {};
8
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
9
- t[p] = s[p];
10
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
11
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
12
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
13
- t[p[i]] = s[p[i]];
14
- }
15
- return t;
16
- };
17
- Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.extractTokenUsageFromClaudeUsage = exports.extractTokenUsageFromOpenAIUsage = exports.extractTokenUsageFromGeminiUsage = exports.sanitizeSchemaForGeminiFunctionDeclaration = exports.applyPayloadOverride = exports.applyToolsOverride = exports.applyModelOverride = exports.isRequestOpenAIModels = exports.shouldUseDeveloperRoleAsSystemRole = exports.isResponsesRequest = void 0;
19
- exports.transformRequestFromResponsesToGemini = transformRequestFromResponsesToGemini;
20
- exports.transformRequestFromResponsesToClaude = transformRequestFromResponsesToClaude;
21
- exports.transformRequestFromResponsesToChatCompletions = transformRequestFromResponsesToChatCompletions;
22
- exports.transformRequestFromClaudeToGemini = transformRequestFromClaudeToGemini;
23
- exports.transformRequestFromClaudeToResponses = transformRequestFromClaudeToResponses;
24
- exports.transformRequestFromClaudeToChatCompletions = transformRequestFromClaudeToChatCompletions;
25
- exports.transformResponseFromChatCompletionsToResponses = transformResponseFromChatCompletionsToResponses;
26
- exports.transformResponseFromClaudeToResponses = transformResponseFromClaudeToResponses;
27
- exports.transformResponseFromGeminiToResponses = transformResponseFromGeminiToResponses;
28
- exports.transformResponseFromChatCompletionsToClaude = transformResponseFromChatCompletionsToClaude;
29
- exports.transformResponseFromResponsesToClaude = transformResponseFromResponsesToClaude;
30
- exports.transformResponseFromGeminiToClaude = transformResponseFromGeminiToClaude;
31
- /**
32
- * 判断请求是否为 Responses API 的请求格式
33
- */
34
- const isResponsesRequest = (data) => {
35
- // Responses API 格式的特征:有 input 字段,没有 messages 字段
36
- return data && typeof data === 'object' &&
37
- 'input' in data &&
38
- !('messages' in data);
39
- };
40
- exports.isResponsesRequest = isResponsesRequest;
41
- /**
42
- * 判断是否使用developer作为系统提示词角色,而不使用system作为角色
43
- * @param model
44
- * @returns
45
- */
46
- const shouldUseDeveloperRoleAsSystemRole = (model) => {
47
- const modelsToUseSystemPrefix = `gpt-2,gpt-3,gpt-4`.split(',');
48
- if (modelsToUseSystemPrefix.some(item => model.toLowerCase().startsWith(item))) {
49
- return false;
50
- }
51
- // gpt-的更高版本模型都是developer
52
- const modelsToUseDeveloper = `o1,o2,o3,gpt-`.split(',');
53
- if (modelsToUseDeveloper.some(item => model.toLowerCase().startsWith(item))) {
54
- return true;
55
- }
56
- // 其他非openai的模型都是system
57
- return false;
58
- };
59
- exports.shouldUseDeveloperRoleAsSystemRole = shouldUseDeveloperRoleAsSystemRole;
60
- const isRequestOpenAIModels = (model) => {
61
- if (!model || typeof model !== 'string') {
62
- return false;
63
- }
64
- const gptModelNames = 'gpt-,o1,o2,o3'.split(',');
65
- return gptModelNames.some(item => model.toLowerCase().startsWith(item));
66
- };
67
- exports.isRequestOpenAIModels = isRequestOpenAIModels;
68
- /**
69
- * 对模型属性进行覆盖
70
- * @param data
71
- * @param realModelName 真正提交到API接口的模型名称
72
- * @returns
73
- */
74
- const applyModelOverride = (data, realModelName) => {
75
- if (!data || typeof data !== 'object') {
76
- return data;
77
- }
78
- if (!realModelName) {
79
- return data;
80
- }
81
- return Object.assign(Object.assign({}, data), { model: realModelName });
82
- };
83
- exports.applyModelOverride = applyModelOverride;
84
- /**
85
- * 对工具属性进行覆盖
86
- * @param tools
87
- * @param realModelName 提交到API的真实模型名称
88
- */
89
- const applyToolsOverride = (tools, realModelName) => {
90
- if (!tools) {
91
- return;
92
- }
93
- if ((0, exports.isRequestOpenAIModels)(realModelName)) {
94
- return tools;
95
- }
96
- return tools.map((tool) => {
97
- const { type, parameters, format, description } = tool, others = __rest(tool, ["type", "parameters", "format", "description"]);
98
- if (type === 'custom') {
99
- return Object.assign(Object.assign({}, others), { type: 'function', description: `${description}${format ? '\n\nFormat: ' + JSON.stringify(format) : ''}`, parameters: parameters || {} });
100
- }
101
- return tool;
102
- }).filter(item => item.type === 'function');
103
- };
104
- exports.applyToolsOverride = applyToolsOverride;
105
- /**
106
- * 对 payload 进行简单处理
107
- * @param data
108
- * @param realModelName 提交到API的真实模型名称
109
- * @returns
110
- */
111
- const applyPayloadOverride = (data, realModelName) => {
112
- if ((0, exports.isRequestOpenAIModels)(realModelName)) {
113
- return data;
114
- }
115
- const overrided = (0, exports.applyModelOverride)(data, realModelName);
116
- return overrided;
117
- // const tools = applyToolsOverride(data.tools, realModelName);
118
- // if (overrided.text) {
119
- // delete overrided.text.verbosity;
120
- // }
121
- // delete overrided.prompt_cache_key;
122
- // delete overrided.include;
123
- // return {
124
- // ...overrided,
125
- // tools,
126
- // };
127
- };
128
- exports.applyPayloadOverride = applyPayloadOverride;
129
- const GEMINI_ALLOWED_SCHEMA_KEYS = new Set([
130
- 'type',
131
- 'format',
132
- 'description',
133
- 'nullable',
134
- 'enum',
135
- 'properties',
136
- 'required',
137
- 'items',
138
- 'minimum',
139
- 'maximum',
140
- 'minLength',
141
- 'maxLength',
142
- 'pattern',
143
- 'minItems',
144
- 'maxItems',
145
- ]);
146
- /**
147
- * Gemini FunctionDeclaration.parameters 仅支持 OpenAPI 子集。
148
- * 这里将通用 JSON Schema 清洗为 Gemini 可接受的结构,避免 400 Unknown name 错误。
149
- */
150
- const sanitizeSchemaForGeminiFunctionDeclaration = (schema) => {
151
- const sanitize = (value) => {
152
- if (!value || typeof value !== 'object') {
153
- return undefined;
154
- }
155
- if (Array.isArray(value)) {
156
- return value.map((item) => sanitize(item)).filter((item) => item !== undefined);
157
- }
158
- const result = {};
159
- const sourceType = value.type;
160
- // JSON Schema type 允许数组(如 ["object", "null"]),Gemini 仅接受字符串
161
- if (Array.isArray(sourceType)) {
162
- const nonNullType = sourceType.find((t) => typeof t === 'string' && t !== 'null');
163
- if (typeof nonNullType === 'string') {
164
- result.type = nonNullType;
165
- }
166
- if (sourceType.includes('null')) {
167
- result.nullable = true;
168
- }
169
- }
170
- else if (typeof sourceType === 'string') {
171
- result.type = sourceType;
172
- }
173
- // const 在 Gemini schema 中不被接受,降级为单值 enum
174
- if (value.const !== undefined) {
175
- result.enum = [value.const];
176
- }
177
- for (const key of Object.keys(value)) {
178
- if (!GEMINI_ALLOWED_SCHEMA_KEYS.has(key)) {
179
- continue;
180
- }
181
- // type 已在上方做过标准化(string / nullable)
182
- if (key === 'type') {
183
- continue;
184
- }
185
- if (key === 'properties' && value.properties && typeof value.properties === 'object' && !Array.isArray(value.properties)) {
186
- const cleanedProperties = {};
187
- for (const [propKey, propValue] of Object.entries(value.properties)) {
188
- const cleanedProperty = sanitize(propValue);
189
- if (cleanedProperty !== undefined) {
190
- cleanedProperties[propKey] = cleanedProperty;
191
- }
192
- }
193
- result.properties = cleanedProperties;
194
- continue;
195
- }
196
- if (key === 'items') {
197
- const cleanedItems = sanitize(value.items);
198
- if (cleanedItems !== undefined) {
199
- result.items = cleanedItems;
200
- }
201
- continue;
202
- }
203
- result[key] = value[key];
204
- }
205
- if (!result.type) {
206
- if (result.properties) {
207
- result.type = 'object';
208
- }
209
- else if (result.items) {
210
- result.type = 'array';
211
- }
212
- }
213
- return Object.keys(result).length > 0 ? result : undefined;
214
- };
215
- return sanitize(schema) || { type: 'object', properties: {} };
216
- };
217
- exports.sanitizeSchemaForGeminiFunctionDeclaration = sanitizeSchemaForGeminiFunctionDeclaration;
218
- const isValidThinkingBudget = (value) => typeof value === 'number' && Number.isFinite(value) && value > 0;
219
- const createGeminiThinkingConfigFromClaudeThinking = (thinking) => {
220
- if (!thinking || typeof thinking !== 'object') {
221
- return undefined;
222
- }
223
- const hasBudget = isValidThinkingBudget(thinking.budget_tokens);
224
- const thinkingConfig = {};
225
- if (thinking.type === 'enabled') {
226
- thinkingConfig.includeThoughts = true;
227
- if (hasBudget) {
228
- thinkingConfig.thinkingBudget = thinking.budget_tokens;
229
- }
230
- else {
231
- thinkingConfig.thinkingLevel = 'HIGH';
232
- }
233
- }
234
- else if (thinking.type === 'auto') {
235
- thinkingConfig.includeThoughts = true;
236
- if (hasBudget) {
237
- thinkingConfig.thinkingBudget = thinking.budget_tokens;
238
- }
239
- else {
240
- thinkingConfig.thinkingLevel = 'LOW';
241
- }
242
- }
243
- else if (thinking.type === 'disabled') {
244
- thinkingConfig.includeThoughts = false;
245
- }
246
- return Object.keys(thinkingConfig).length > 0 ? thinkingConfig : undefined;
247
- };
248
- const createGeminiThinkingConfigFromResponsesReasoning = (reasoning) => {
249
- if (!reasoning || typeof reasoning !== 'object') {
250
- return undefined;
251
- }
252
- const hasBudget = isValidThinkingBudget(reasoning.budget_tokens);
253
- const thinkingConfig = {};
254
- if (reasoning.type === 'enabled' || reasoning.type === 'auto') {
255
- thinkingConfig.includeThoughts = true;
256
- if (hasBudget) {
257
- thinkingConfig.thinkingBudget = reasoning.budget_tokens;
258
- }
259
- else if (reasoning.effort === 'low') {
260
- thinkingConfig.thinkingLevel = 'LOW';
261
- }
262
- else if (reasoning.effort === 'medium') {
263
- thinkingConfig.thinkingLevel = 'MEDIUM';
264
- }
265
- else if (reasoning.effort === 'high') {
266
- thinkingConfig.thinkingLevel = 'HIGH';
267
- }
268
- }
269
- else if (reasoning.type === 'disabled') {
270
- thinkingConfig.includeThoughts = false;
271
- }
272
- return Object.keys(thinkingConfig).length > 0 ? thinkingConfig : undefined;
273
- };
274
- /**
275
- * 从 Gemini usage 中提取 TokenUsage
276
- */
277
- const extractTokenUsageFromGeminiUsage = (usage) => {
278
- if (!usage) {
279
- return undefined;
280
- }
281
- return {
282
- inputTokens: usage.promptTokenCount,
283
- outputTokens: usage.candidatesTokenCount,
284
- totalTokens: usage.totalTokenCount,
285
- cacheReadInputTokens: usage.cachedContentTokenCount,
286
- };
287
- };
288
- exports.extractTokenUsageFromGeminiUsage = extractTokenUsageFromGeminiUsage;
289
- const extractTokenUsageFromOpenAIUsage = (usage) => {
290
- if (!usage) {
291
- return undefined;
292
- }
293
- return {
294
- inputTokens: (usage === null || usage === void 0 ? void 0 : usage.input_tokens) || 0,
295
- outputTokens: (usage === null || usage === void 0 ? void 0 : usage.output_tokens) || (usage === null || usage === void 0 ? void 0 : usage.completion_tokens) || 0,
296
- totalTokens: (usage === null || usage === void 0 ? void 0 : usage.total_tokens) || 0,
297
- cacheReadInputTokens: (usage === null || usage === void 0 ? void 0 : usage.cached_tokens) || 0,
298
- };
299
- };
300
- exports.extractTokenUsageFromOpenAIUsage = extractTokenUsageFromOpenAIUsage;
301
- const extractTokenUsageFromClaudeUsage = (usage) => {
302
- if (!usage) {
303
- return undefined;
304
- }
305
- return {
306
- inputTokens: (usage === null || usage === void 0 ? void 0 : usage.input_tokens) || 0,
307
- outputTokens: (usage === null || usage === void 0 ? void 0 : usage.output_tokens) || 0,
308
- totalTokens: (usage === null || usage === void 0 ? void 0 : usage.input_tokens) !== undefined && (usage === null || usage === void 0 ? void 0 : usage.output_tokens) !== undefined
309
- ? (usage === null || usage === void 0 ? void 0 : usage.input_tokens) + (usage === null || usage === void 0 ? void 0 : usage.output_tokens)
310
- : undefined,
311
- cacheReadInputTokens: (usage === null || usage === void 0 ? void 0 : usage.cache_read_input_tokens) || 0,
312
- };
313
- };
314
- exports.extractTokenUsageFromClaudeUsage = extractTokenUsageFromClaudeUsage;
315
- // ------------------------- codex 发起请求 -------------------------
316
- /**
317
- * 将 codex 发起的对 Responses API 的请求转换为对 Gemini API 的请求的数据格式
318
- */
319
- function transformRequestFromResponsesToGemini(body, _targetModel) {
320
- var _a;
321
- const { instructions, input, max_output_tokens, max_tokens, temperature, top_p, stop, tools, tool_choice, reasoning } = body;
322
- const geminiRequest = {
323
- contents: [],
324
- };
325
- // 处理 instructions 字段(系统提示词)
326
- if (instructions && typeof instructions === 'string') {
327
- geminiRequest.systemInstruction = {
328
- role: 'user',
329
- parts: [{ text: instructions }],
330
- };
331
- }
332
- // 处理 input 字段(消息数组)
333
- if (Array.isArray(input)) {
334
- for (const msg of input) {
335
- if (!msg || msg.type !== 'message') {
336
- continue;
337
- }
338
- // Gemini API 不支持 developer 角色,映射为 user
339
- const geminiRole = msg.role === 'assistant' ? 'model' : 'user';
340
- const geminiContent = {
341
- role: geminiRole,
342
- parts: [],
343
- };
344
- // 处理 content 数组
345
- if (Array.isArray(msg.content)) {
346
- for (const contentItem of msg.content) {
347
- if (!contentItem || typeof contentItem !== 'object') {
348
- continue;
349
- }
350
- // 处理文本内容: input_text -> text
351
- if (contentItem.type === 'input_text' && typeof contentItem.text === 'string') {
352
- geminiContent.parts.push({ text: contentItem.text });
353
- }
354
- // 处理图像内容: input_image -> inlineData
355
- if (contentItem.type === 'input_image' && contentItem.image_url) {
356
- const imageUrl = contentItem.image_url.url;
357
- if (typeof imageUrl === 'string') {
358
- if (imageUrl.startsWith('data:')) {
359
- // 处理 base64 格式图像
360
- const match = imageUrl.match(/^data:([^;]+);base64,(.+)$/);
361
- if (match) {
362
- geminiContent.parts.push({
363
- inlineData: {
364
- mimeType: match[1],
365
- data: match[2],
366
- },
367
- });
368
- }
369
- }
370
- // URL 格式需要下载后转换,这里暂时跳过
371
- }
372
- }
373
- }
374
- }
375
- // 处理字符串格式 content
376
- else if (typeof msg.content === 'string') {
377
- geminiContent.parts.push({ text: msg.content });
378
- }
379
- // 确保至少有一个 part
380
- if (geminiContent.parts.length === 0) {
381
- geminiContent.parts.push({ text: '' });
382
- }
383
- geminiRequest.contents.push(geminiContent);
384
- }
385
- }
386
- // 构建生成配置
387
- const generationConfig = {};
388
- if (typeof temperature === 'number') {
389
- generationConfig.temperature = temperature;
390
- }
391
- if (typeof top_p === 'number') {
392
- generationConfig.topP = top_p;
393
- }
394
- if (typeof max_output_tokens === 'number') {
395
- generationConfig.maxOutputTokens = max_output_tokens;
396
- }
397
- else if (typeof max_tokens === 'number') {
398
- generationConfig.maxOutputTokens = max_tokens;
399
- }
400
- // 处理 stop: Responses API 是数组,Gemini 也是数组
401
- if (Array.isArray(stop)) {
402
- generationConfig.stopSequences = stop;
403
- }
404
- else if (typeof stop === 'string') {
405
- generationConfig.stopSequences = [stop];
406
- }
407
- // 处理 reasoning -> thinkingConfig
408
- if (reasoning) {
409
- const thinkingConfig = createGeminiThinkingConfigFromResponsesReasoning(reasoning);
410
- if (thinkingConfig) {
411
- generationConfig.thinkingConfig = thinkingConfig;
412
- }
413
- }
414
- if (Object.keys(generationConfig).length > 0) {
415
- geminiRequest.generationConfig = generationConfig;
416
- }
417
- // 转换 tools
418
- if (Array.isArray(tools)) {
419
- const functionDeclarations = [];
420
- for (const tool of tools) {
421
- if (tool && tool.type === 'function' && tool.function) {
422
- functionDeclarations.push({
423
- name: tool.function.name,
424
- description: tool.function.description || '',
425
- parameters: (0, exports.sanitizeSchemaForGeminiFunctionDeclaration)(tool.function.parameters || { type: 'object', properties: {}, required: [] }),
426
- });
427
- }
428
- }
429
- if (functionDeclarations.length > 0) {
430
- geminiRequest.tools = [{ functionDeclarations }];
431
- }
432
- }
433
- // 转换 tool_choice -> toolConfig
434
- if (tool_choice !== undefined) {
435
- const toolConfig = { functionCallingConfig: {} };
436
- if (tool_choice === 'auto') {
437
- toolConfig.functionCallingConfig.mode = 'AUTO';
438
- }
439
- else if (tool_choice === 'required' || tool_choice === 'any') {
440
- toolConfig.functionCallingConfig.mode = 'ANY';
441
- }
442
- else if (tool_choice === 'none') {
443
- toolConfig.functionCallingConfig.mode = 'NONE';
444
- }
445
- else if (typeof tool_choice === 'object' && tool_choice.type === 'function') {
446
- const tc = tool_choice;
447
- if ((_a = tc.function) === null || _a === void 0 ? void 0 : _a.name) {
448
- toolConfig.functionCallingConfig.mode = 'ANY';
449
- toolConfig.functionCallingConfig.allowedFunctionNames = [tc.function.name];
450
- }
451
- }
452
- if (Object.keys(toolConfig.functionCallingConfig).length > 0) {
453
- geminiRequest.toolConfig = toolConfig;
454
- }
455
- }
456
- return geminiRequest;
457
- }
458
- /**
459
- * 将 codex 发起的对 Responses API 的请求转换为对 Claude API 的请求的数据格式
460
- */
461
- function transformRequestFromResponsesToClaude(body, targetModel) {
462
- var _a;
463
- const { model, instructions, input, max_output_tokens, max_tokens, temperature, top_p, stop, tools, tool_choice } = body;
464
- const messages = [];
465
- // 1. 处理 instructions 字段(系统提示词)
466
- // Claude API 使用 system 字段,不在 messages 中
467
- let system = '';
468
- if (instructions && typeof instructions === 'string') {
469
- system = instructions;
470
- }
471
- // 2. 处理 input 字段(消息数组)
472
- if (Array.isArray(input)) {
473
- for (const msg of input) {
474
- if (!msg || msg.type !== 'message') {
475
- continue;
476
- }
477
- // 提取 content 数组中的内容
478
- const contentBlocks = [];
479
- if (Array.isArray(msg.content)) {
480
- for (const contentItem of msg.content) {
481
- if (!contentItem || typeof contentItem !== 'object') {
482
- continue;
483
- }
484
- // input_text -> text
485
- if (contentItem.type === 'input_text' && typeof contentItem.text === 'string') {
486
- contentBlocks.push({ type: 'text', text: contentItem.text });
487
- }
488
- // input_image -> image(Claude 格式)
489
- if (contentItem.type === 'input_image' && contentItem.image_url) {
490
- const imageUrl = contentItem.image_url.url;
491
- if (typeof imageUrl === 'string') {
492
- if (imageUrl.startsWith('data:')) {
493
- const match = imageUrl.match(/^data:([^;]+);base64,(.+)$/);
494
- if (match) {
495
- contentBlocks.push({
496
- type: 'image',
497
- source: {
498
- type: 'base64',
499
- media_type: match[1],
500
- data: match[2],
501
- },
502
- });
503
- }
504
- }
505
- else {
506
- // URL 格式
507
- contentBlocks.push({
508
- type: 'image',
509
- source: {
510
- type: 'url',
511
- url: imageUrl,
512
- },
513
- });
514
- }
515
- }
516
- }
517
- }
518
- }
519
- else if (typeof msg.content === 'string') {
520
- contentBlocks.push({ type: 'text', text: msg.content });
521
- }
522
- // 映射角色: developer -> user
523
- const role = msg.role === 'developer' ? 'user' : msg.role;
524
- // 添加到 messages 数组
525
- if (contentBlocks.length > 0) {
526
- messages.push({
527
- role,
528
- content: contentBlocks,
529
- });
530
- }
531
- }
532
- }
533
- // 3. 构建转换后的请求
534
- const transformed = {
535
- model: targetModel || model,
536
- max_tokens: max_output_tokens || max_tokens,
537
- };
538
- // 添加 messages
539
- if (messages.length > 0) {
540
- transformed.messages = messages;
541
- }
542
- // 添加 system
543
- if (system) {
544
- transformed.system = system;
545
- }
546
- // 4. 处理工具定义转换
547
- // Responses API: { type: "function", function: { name, description, parameters } }
548
- // Claude: { name, description, input_schema: parameters }
549
- if (Array.isArray(tools)) {
550
- transformed.tools = tools.map((tool) => {
551
- if (tool && tool.type === 'function' && tool.function) {
552
- return {
553
- name: tool.function.name,
554
- description: tool.function.description || '',
555
- input_schema: tool.function.parameters || { type: 'object', properties: {}, required: [] },
556
- };
557
- }
558
- return null;
559
- }).filter(Boolean);
560
- }
561
- // 5. 处理 tool_choice 映射
562
- // Responses API: "auto" | "required" | "none" | { type: "function", function: { name } }
563
- // Claude: "auto" | "any" | "none" | { type: "tool", name: string }
564
- if (tool_choice !== undefined) {
565
- if (tool_choice === 'auto') {
566
- transformed.tool_choice = 'auto';
567
- }
568
- else if (tool_choice === 'required') {
569
- transformed.tool_choice = 'any';
570
- }
571
- else if (tool_choice === 'none') {
572
- transformed.tool_choice = 'none';
573
- }
574
- else if (typeof tool_choice === 'object' && tool_choice.type === 'function') {
575
- const tc = tool_choice;
576
- if ((_a = tc.function) === null || _a === void 0 ? void 0 : _a.name) {
577
- transformed.tool_choice = {
578
- type: 'tool',
579
- name: tc.function.name,
580
- };
581
- }
582
- }
583
- }
584
- // 6. 处理其他参数
585
- if (typeof temperature === 'number') {
586
- transformed.temperature = temperature;
587
- }
588
- if (typeof top_p === 'number') {
589
- transformed.top_p = top_p;
590
- }
591
- if (body.stream !== undefined) {
592
- transformed.stream = body.stream;
593
- }
594
- // 处理 stop: Responses API 是数组,Claude 是 stop_sequences
595
- if (Array.isArray(stop)) {
596
- transformed.stop_sequences = stop;
597
- }
598
- else if (typeof stop === 'string') {
599
- transformed.stop_sequences = [stop];
600
- }
601
- return transformed;
602
- }
603
- /**
604
- * 将 codex 发起的对 Responses API 的请求转换为对 Chat Completions API 的请求的数据格式
605
- * @param body Responses API 格式的请求体
606
- * @param targetModel 目标模型名称(可选)
607
- * @returns Chat Completions API 格式的请求体
608
- */
609
- function transformRequestFromResponsesToChatCompletions(body, targetModel) {
610
- var _a;
611
- const { model, instructions, input, max_output_tokens, reasoning, tools, tool_choice, parallel_tool_calls, max_tool_calls } = body, others = __rest(body, ["model", "instructions", "input", "max_output_tokens", "reasoning", "tools", "tool_choice", "parallel_tool_calls", "max_tool_calls"]);
612
- /**
613
- * 将 Responses API 的 content item 转换为 Chat Completions 格式
614
- * Responses API: { type: "input_text", text: "..." }
615
- * Chat Completions: { type: "text", text: "..." }
616
- * Responses API: { type: "input_image", image_url: { url: "..." } }
617
- * Chat Completions: { type: "image_url", image_url: { url: "..." } }
618
- */
619
- const transformResponsesContentItemToChatCompletionContentItem = (item) => {
620
- if (!item || typeof item !== 'object') {
621
- return null;
622
- }
623
- if (item.type === 'input_text') {
624
- return { type: 'text', text: item.text };
625
- }
626
- if (item.type === 'input_image' && item.image_url) {
627
- return { type: 'image_url', image_url: item.image_url };
628
- }
629
- // 其他类型直接返回
630
- return item;
631
- };
632
- // 输入验证:检查 input 是否为数组
633
- if (!Array.isArray(input)) {
634
- // 如果 input 不是数组,尝试处理字符串格式
635
- const messages = [];
636
- const systemRole = (0, exports.shouldUseDeveloperRoleAsSystemRole)(targetModel || model) ? 'developer' : 'system';
637
- // 处理 instructions
638
- if (instructions && typeof instructions === 'string') {
639
- messages.push({
640
- role: systemRole,
641
- content: instructions,
642
- });
643
- }
644
- // 处理 input(如果是字符串)
645
- if (typeof input === 'string' && input) {
646
- messages.push({
647
- role: 'user',
648
- content: input,
649
- });
650
- }
651
- // 构建转换后的请求
652
- const result = {
653
- model: targetModel || model,
654
- messages,
655
- };
656
- // 处理 max_tokens(Responses API 使用 max_output_tokens)
657
- if (typeof max_output_tokens === 'number') {
658
- result.max_tokens = max_output_tokens;
659
- }
660
- else if (typeof others.max_tokens === 'number') {
661
- result.max_tokens = others.max_tokens;
662
- }
663
- // 处理 tools
664
- if (Array.isArray(tools)) {
665
- result.tools = tools;
666
- }
667
- // 处理 tool_choice
668
- if (tool_choice !== undefined) {
669
- result.tool_choice = tool_choice;
670
- }
671
- // 处理 parallel_tool_calls
672
- if (parallel_tool_calls !== undefined) {
673
- result.parallel_tool_calls = parallel_tool_calls;
674
- }
675
- // 处理 reasoning(Chat Completions API 可能不直接支持,但保留用于兼容)
676
- if (reasoning) {
677
- result.reasoning = reasoning;
678
- }
679
- // 添加其他兼容的字段(temperature, top_p, stream 等)
680
- const compatibleFields = ['temperature', 'top_p', 'stream', 'stop', 'frequency_penalty', 'presence_penalty', 'seed', 'user', 'response_format'];
681
- for (const field of compatibleFields) {
682
- if (others[field] !== undefined) {
683
- result[field] = others[field];
684
- }
685
- }
686
- return result;
687
- }
688
- // 提取 developer 消息和普通消息
689
- const developerItems = input.filter((item) => item.type === 'message' && item.role === 'developer');
690
- const nonDeveloperItems = input.filter((item) => item.type === 'message' && item.role !== 'developer');
691
- // 构建 messages 数组
692
- const messages = [];
693
- // 处理系统/developer 消息
694
- const systemRole = (0, exports.shouldUseDeveloperRoleAsSystemRole)(targetModel || model) ? 'developer' : 'system';
695
- const systemContentItems = [];
696
- // 添加 instructions
697
- if (instructions && typeof instructions === 'string') {
698
- systemContentItems.push({ type: 'text', text: instructions });
699
- }
700
- // 添加 developer 消息中的内容
701
- for (const item of developerItems) {
702
- if (!item)
703
- continue;
704
- const itemContent = Array.isArray(item.content)
705
- ? item.content.map(transformResponsesContentItemToChatCompletionContentItem).filter(Boolean)
706
- : (typeof item.content === 'string' ? [{ type: 'text', text: item.content }] : []);
707
- systemContentItems.push(...itemContent);
708
- }
709
- // 只有当有系统内容时才添加系统消息
710
- if (systemContentItems.length > 0) {
711
- messages.push({
712
- role: systemRole,
713
- content: systemContentItems,
714
- });
715
- }
716
- // 处理非 developer 消息
717
- for (const item of nonDeveloperItems) {
718
- if (!item || item.type !== 'message')
719
- continue;
720
- const messageContent = Array.isArray(item.content)
721
- ? item.content.map(transformResponsesContentItemToChatCompletionContentItem).filter(Boolean)
722
- : (typeof item.content === 'string' ? item.content : []);
723
- messages.push({
724
- role: item.role,
725
- content: messageContent,
726
- });
727
- }
728
- // 构建转换后的请求
729
- const result = {
730
- model: targetModel || model,
731
- messages,
732
- };
733
- // 处理 max_tokens(Responses API 使用 max_output_tokens)
734
- if (typeof max_output_tokens === 'number') {
735
- result.max_tokens = max_output_tokens;
736
- }
737
- else if (typeof others.max_tokens === 'number') {
738
- result.max_tokens = others.max_tokens;
739
- }
740
- // 处理 tools:需要确保格式正确且不包含空函数
741
- if (Array.isArray(tools) && tools.length > 0) {
742
- const validTools = tools.map((tool) => {
743
- if (tool && tool.type === 'function' && tool.function) {
744
- const fn = tool.function;
745
- // 验证必需字段
746
- if (!fn.name || typeof fn.name !== 'string') {
747
- return null;
748
- }
749
- // 确保参数格式正确
750
- const params = fn.parameters || { type: 'object', properties: {}, required: [] };
751
- if (typeof params !== 'object' || params.type !== 'object') {
752
- return null;
753
- }
754
- return {
755
- type: 'function',
756
- function: Object.assign({ name: fn.name, description: fn.description || '', parameters: params }, (fn.strict !== undefined && { strict: fn.strict }))
757
- };
758
- }
759
- return null;
760
- }).filter(Boolean);
761
- // 只有当有有效工具时才添加 tools 字段
762
- if (validTools.length > 0) {
763
- result.tools = validTools;
764
- }
765
- }
766
- // 处理 tool_choice
767
- if (tool_choice !== undefined) {
768
- // 验证 tool_choice 格式
769
- if (typeof tool_choice === 'string' && ['auto', 'none', 'required'].includes(tool_choice)) {
770
- result.tool_choice = tool_choice;
771
- }
772
- else if (typeof tool_choice === 'object' && tool_choice.type === 'function' && ((_a = tool_choice.function) === null || _a === void 0 ? void 0 : _a.name)) {
773
- result.tool_choice = {
774
- type: 'function',
775
- function: { name: tool_choice.function.name }
776
- };
777
- }
778
- }
779
- // 处理 parallel_tool_calls
780
- if (parallel_tool_calls !== undefined) {
781
- result.parallel_tool_calls = parallel_tool_calls;
782
- }
783
- // 处理 reasoning(Chat Completions API 可能不直接支持,但保留用于兼容)
784
- if (reasoning) {
785
- result.reasoning = reasoning;
786
- }
787
- // 添加其他兼容的字段(temperature, top_p, stream 等)
788
- const compatibleFields = ['temperature', 'top_p', 'stream', 'stop', 'frequency_penalty', 'presence_penalty', 'seed', 'user', 'response_format'];
789
- for (const field of compatibleFields) {
790
- if (others[field] !== undefined) {
791
- result[field] = others[field];
792
- }
793
- }
794
- return result;
795
- }
796
- // ----------------------- claude code 发起的请求转换 -----------------------
797
- /**
798
- * 将 claude code 发起的请求转换成对 gemini API 的请求
799
- * @param body
800
- * @param _model
801
- */
802
- function transformRequestFromClaudeToGemini(body, _model) {
803
- const { system, messages, max_tokens, temperature, top_p, stop_sequences, tools, tool_choice, thinking } = body;
804
- const geminiRequest = {
805
- contents: [],
806
- };
807
- // 处理 system 指令
808
- if (system) {
809
- let systemText = '';
810
- if (typeof system === 'string') {
811
- systemText = system;
812
- }
813
- else if (Array.isArray(system)) {
814
- const textParts = [];
815
- for (const block of system) {
816
- if (block && block.type === 'text' && typeof block.text === 'string') {
817
- textParts.push(block.text);
818
- }
819
- }
820
- systemText = textParts.join('\n\n');
821
- }
822
- else if (system && typeof system === 'object' && system.type === 'text') {
823
- systemText = system.text;
824
- }
825
- if (systemText) {
826
- geminiRequest.systemInstruction = {
827
- role: 'user',
828
- parts: [{ text: systemText }]
829
- };
830
- }
831
- }
832
- // 转换 messages
833
- if (Array.isArray(messages)) {
834
- for (const msg of messages) {
835
- if (!msg || typeof msg !== 'object')
836
- continue;
837
- const geminiRole = msg.role === 'assistant' ? 'model' :
838
- msg.role === 'tool' ? 'function' : 'user';
839
- const geminiContent = {
840
- role: geminiRole,
841
- parts: [],
842
- };
843
- if (typeof msg.content === 'string') {
844
- geminiContent.parts.push({ text: msg.content });
845
- }
846
- else if (Array.isArray(msg.content)) {
847
- for (const block of msg.content) {
848
- if (!block || typeof block !== 'object')
849
- continue;
850
- // text → text
851
- if (block.type === 'text' && typeof block.text === 'string') {
852
- geminiContent.parts.push({ text: block.text });
853
- }
854
- // image → inlineData
855
- if (block.type === 'image' && block.source) {
856
- const source = block.source;
857
- if (source.type === 'base64' && source.data && source.media_type) {
858
- geminiContent.parts.push({
859
- inlineData: {
860
- mimeType: source.media_type,
861
- data: source.data
862
- }
863
- });
864
- }
865
- else if (source.type === 'url' && source.url) {
866
- // URL 格式需要特殊处理,这里暂时跳过
867
- }
868
- }
869
- // tool_use → functionCall
870
- if (block.type === 'tool_use') {
871
- geminiContent.parts.push({
872
- functionCall: {
873
- name: block.name || 'tool',
874
- args: block.input || {}
875
- }
876
- });
877
- }
878
- // tool_result → functionResponse
879
- if (block.type === 'tool_result') {
880
- // Claude 的 tool_result 包含 tool_use_id,但 Gemini 需要的是函数名
881
- // 这里暂时使用 'tool' 作为函数名,实际应该从上下文中获取正确的函数名
882
- geminiContent.parts.push({
883
- functionResponse: {
884
- name: 'tool', // TODO: 应该从之前的 tool_use 中获取正确的函数名
885
- response: typeof msg.content === 'string' ? msg.content : msg.content || {}
886
- }
887
- });
888
- }
889
- }
890
- }
891
- else if (typeof msg.content === 'string') {
892
- // tool 消息的处理
893
- geminiContent.parts.push({
894
- functionResponse: {
895
- name: 'tool', // TODO: 应该从 tool_use_id 对应的函数中获取
896
- response: msg.content || {}
897
- }
898
- });
899
- }
900
- // 确保至少有一个 part
901
- if (geminiContent.parts.length === 0) {
902
- geminiContent.parts.push({ text: '' });
903
- }
904
- geminiRequest.contents.push(geminiContent);
905
- }
906
- }
907
- // 构建生成配置
908
- const generationConfig = {};
909
- if (typeof temperature === 'number') {
910
- generationConfig.temperature = temperature;
911
- }
912
- if (typeof top_p === 'number') {
913
- generationConfig.topP = top_p;
914
- }
915
- if (typeof max_tokens === 'number') {
916
- generationConfig.maxOutputTokens = max_tokens;
917
- }
918
- // 处理 stop_sequences -> stopSequences
919
- if (Array.isArray(stop_sequences)) {
920
- generationConfig.stopSequences = stop_sequences;
921
- }
922
- // 转换 thinking → thinkingConfig
923
- if (thinking) {
924
- const thinkingConfig = createGeminiThinkingConfigFromClaudeThinking(thinking);
925
- if (thinkingConfig) {
926
- generationConfig.thinkingConfig = thinkingConfig;
927
- }
928
- }
929
- if (Object.keys(generationConfig).length > 0) {
930
- geminiRequest.generationConfig = generationConfig;
931
- }
932
- // 转换 tools
933
- // Claude: { name, description, input_schema }
934
- // Gemini: { functionDeclarations: [{ name, description, parameters }] }
935
- if (Array.isArray(tools)) {
936
- const functionDeclarations = [];
937
- for (const tool of tools) {
938
- if (tool && tool.name) {
939
- functionDeclarations.push({
940
- name: tool.name,
941
- description: tool.description || '',
942
- parameters: (0, exports.sanitizeSchemaForGeminiFunctionDeclaration)(tool.input_schema || { type: 'object', properties: {}, required: [] })
943
- });
944
- }
945
- }
946
- if (functionDeclarations.length > 0) {
947
- geminiRequest.tools = [{ functionDeclarations }];
948
- }
949
- }
950
- // 转换 tool_choice
951
- if (tool_choice) {
952
- const toolConfig = { functionCallingConfig: {} };
953
- if (tool_choice === 'auto') {
954
- toolConfig.functionCallingConfig.mode = 'AUTO';
955
- }
956
- else if (tool_choice === 'any' || tool_choice === 'required') {
957
- toolConfig.functionCallingConfig.mode = 'ANY';
958
- }
959
- else if (tool_choice === 'none') {
960
- toolConfig.functionCallingConfig.mode = 'NONE';
961
- }
962
- else if (typeof tool_choice === 'object') {
963
- const tc = tool_choice;
964
- if (tc.type === 'tool' && tc.name) {
965
- toolConfig.functionCallingConfig.mode = 'ANY';
966
- toolConfig.functionCallingConfig.allowedFunctionNames = [tc.name];
967
- }
968
- }
969
- if (Object.keys(toolConfig.functionCallingConfig).length > 0) {
970
- geminiRequest.toolConfig = toolConfig;
971
- }
972
- }
973
- return geminiRequest;
974
- }
975
- /**
976
- * 将 claude code 发起的请求转换成对 responses API 的请求
977
- * @param body
978
- * @param model
979
- */
980
- function transformRequestFromClaudeToResponses(body, model) {
981
- const { system, messages, max_tokens, temperature, top_p, stop_sequences, tools, tool_choice } = body;
982
- const input = [];
983
- let instructions = '';
984
- // 处理 system 指令
985
- if (system) {
986
- if (typeof system === 'string') {
987
- instructions = system;
988
- }
989
- else if (Array.isArray(system)) {
990
- const textParts = [];
991
- for (const block of system) {
992
- if (block && block.type === 'text' && typeof block.text === 'string') {
993
- textParts.push(block.text);
994
- }
995
- }
996
- instructions = textParts.join('\n\n');
997
- }
998
- else if (system && typeof system === 'object' && system.type === 'text') {
999
- instructions = system.text;
1000
- }
1001
- }
1002
- // 转换 messages
1003
- if (Array.isArray(messages)) {
1004
- for (const msg of messages) {
1005
- if (!msg || typeof msg !== 'object')
1006
- continue;
1007
- const content = [];
1008
- if (typeof msg.content === 'string') {
1009
- content.push({
1010
- type: 'input_text',
1011
- text: msg.content
1012
- });
1013
- }
1014
- else if (Array.isArray(msg.content)) {
1015
- for (const block of msg.content) {
1016
- if (!block || typeof block !== 'object')
1017
- continue;
1018
- // text → input_text
1019
- if (block.type === 'text' && typeof block.text === 'string') {
1020
- content.push({
1021
- type: 'input_text',
1022
- text: block.text
1023
- });
1024
- }
1025
- // image → input_image
1026
- if (block.type === 'image' && block.source) {
1027
- const source = block.source;
1028
- if (source.type === 'base64' && source.data && source.media_type) {
1029
- const dataUrl = `data:${source.media_type};base64,${source.data}`;
1030
- content.push({
1031
- type: 'input_image',
1032
- image_url: { url: dataUrl }
1033
- });
1034
- }
1035
- else if (source.type === 'url' && source.url) {
1036
- content.push({
1037
- type: 'input_image',
1038
- image_url: { url: source.url }
1039
- });
1040
- }
1041
- }
1042
- // tool_use → function_call (Responses API 格式)
1043
- if (block.type === 'tool_use') {
1044
- // Responses API 在 output 中包含 function_call,不是在 input 中
1045
- // 所以这里不转换 tool_use,只保留文本和图像
1046
- }
1047
- }
1048
- }
1049
- input.push({
1050
- type: 'message',
1051
- role: msg.role,
1052
- content: content
1053
- });
1054
- }
1055
- }
1056
- const result = {
1057
- model: model || body.model,
1058
- input,
1059
- };
1060
- if (instructions) {
1061
- result.instructions = instructions;
1062
- }
1063
- if (typeof temperature === 'number') {
1064
- result.temperature = temperature;
1065
- }
1066
- if (typeof top_p === 'number') {
1067
- result.top_p = top_p;
1068
- }
1069
- if (typeof max_tokens === 'number') {
1070
- result.max_output_tokens = max_tokens;
1071
- }
1072
- // 处理 stop_sequences -> stop
1073
- if (Array.isArray(stop_sequences)) {
1074
- result.stop = stop_sequences;
1075
- }
1076
- else if (typeof stop_sequences === 'string') {
1077
- result.stop = [stop_sequences];
1078
- }
1079
- // 处理 tools: Claude 格式 → Responses API 格式
1080
- // Claude: { name, description, input_schema }
1081
- // Responses: { type: "function", function: { name, description, parameters } }
1082
- if (Array.isArray(tools)) {
1083
- result.tools = tools.map((tool) => {
1084
- if (tool && tool.name) {
1085
- return {
1086
- type: 'function',
1087
- function: {
1088
- name: tool.name,
1089
- description: tool.description || '',
1090
- parameters: tool.input_schema || { type: 'object', properties: {}, required: [] },
1091
- },
1092
- };
1093
- }
1094
- return null;
1095
- }).filter(Boolean);
1096
- }
1097
- // 处理 tool_choice
1098
- // Claude: "auto" | "any" | "none" | { type: "tool", name }
1099
- // Responses: "auto" | "required" | "none" | { type: "function", function: { name } }
1100
- if (tool_choice !== undefined) {
1101
- if (tool_choice === 'auto') {
1102
- result.tool_choice = 'auto';
1103
- }
1104
- else if (tool_choice === 'any') {
1105
- result.tool_choice = 'required';
1106
- }
1107
- else if (tool_choice === 'none') {
1108
- result.tool_choice = 'none';
1109
- }
1110
- else if (typeof tool_choice === 'object' && tool_choice.type === 'tool') {
1111
- const tc = tool_choice;
1112
- if (tc.name) {
1113
- result.tool_choice = {
1114
- type: 'function',
1115
- function: { name: tc.name },
1116
- };
1117
- }
1118
- }
1119
- }
1120
- return result;
1121
- }
1122
- /**
1123
- * 将 claude code 发起的请求转换成对 chat-completions API 的请求
1124
- * @param body
1125
- * @param model
1126
- */
1127
- function transformRequestFromClaudeToChatCompletions(body, model) {
1128
- const { system, messages, max_tokens, temperature, top_p, stop_sequences, tools, tool_choice, thinking } = body;
1129
- const transformedMessages = [];
1130
- let systemText = '';
1131
- // 处理 system 指令
1132
- if (system) {
1133
- if (typeof system === 'string') {
1134
- systemText = system;
1135
- }
1136
- else if (Array.isArray(system)) {
1137
- const textParts = [];
1138
- for (const block of system) {
1139
- if (block && block.type === 'text' && typeof block.text === 'string') {
1140
- textParts.push(block.text);
1141
- }
1142
- }
1143
- systemText = textParts.join('\n\n');
1144
- }
1145
- else if (system && typeof system === 'object' && system.type === 'text') {
1146
- systemText = system.text;
1147
- }
1148
- }
1149
- // 转换 messages
1150
- if (Array.isArray(messages)) {
1151
- for (const msg of messages) {
1152
- if (!msg || typeof msg !== 'object')
1153
- continue;
1154
- const transformedMsg = {
1155
- role: msg.role,
1156
- };
1157
- if (typeof msg.content === 'string') {
1158
- transformedMsg.content = msg.content;
1159
- }
1160
- else if (Array.isArray(msg.content)) {
1161
- const content = [];
1162
- for (const block of msg.content) {
1163
- if (!block || typeof block !== 'object')
1164
- continue;
1165
- // text → text
1166
- if (block.type === 'text' && typeof block.text === 'string') {
1167
- content.push({ type: 'text', text: block.text });
1168
- }
1169
- // image → image_url
1170
- if (block.type === 'image' && block.source) {
1171
- const source = block.source;
1172
- if (source.type === 'base64' && source.data && source.media_type) {
1173
- const dataUrl = `data:${source.media_type};base64,${source.data}`;
1174
- content.push({
1175
- type: 'image_url',
1176
- image_url: { url: dataUrl }
1177
- });
1178
- }
1179
- else if (source.type === 'url' && source.url) {
1180
- content.push({
1181
- type: 'image_url',
1182
- image_url: { url: source.url }
1183
- });
1184
- }
1185
- }
1186
- // tool_use → tool_calls
1187
- if (block.type === 'tool_use') {
1188
- if (!transformedMsg.tool_calls) {
1189
- transformedMsg.tool_calls = [];
1190
- }
1191
- transformedMsg.tool_calls.push({
1192
- id: block.id,
1193
- type: 'function',
1194
- function: {
1195
- name: block.name || 'tool',
1196
- arguments: JSON.stringify(block.input || {})
1197
- }
1198
- });
1199
- }
1200
- // thinking → thinking(Chat Completions API 可能不支持,但保留)
1201
- if (block.type === 'thinking') {
1202
- content.push({
1203
- type: 'thinking',
1204
- thinking: block.thinking
1205
- });
1206
- }
1207
- }
1208
- if (content.length > 0 || !transformedMsg.tool_calls) {
1209
- transformedMsg.content = content.length > 0 ? content : '';
1210
- }
1211
- }
1212
- // tool_result → tool 消息
1213
- if (msg.role === 'tool') {
1214
- if (typeof msg.content === 'string') {
1215
- transformedMsg.content = msg.content;
1216
- }
1217
- else if (Array.isArray(msg.content)) {
1218
- const toolContent = [];
1219
- for (const block of msg.content) {
1220
- if (block && block.type === 'text' && typeof block.text === 'string') {
1221
- toolContent.push(block.text);
1222
- }
1223
- }
1224
- transformedMsg.content = toolContent.join('\n');
1225
- }
1226
- transformedMsg.tool_call_id = msg.tool_use_id || '';
1227
- }
1228
- transformedMessages.push(transformedMsg);
1229
- }
1230
- }
1231
- const result = {
1232
- model: model || body.model,
1233
- messages: transformedMessages,
1234
- };
1235
- // 处理 system
1236
- if (systemText) {
1237
- result.system = systemText;
1238
- }
1239
- // 处理 max_tokens
1240
- if (typeof max_tokens === 'number') {
1241
- result.max_tokens = max_tokens;
1242
- }
1243
- // 处理 temperature
1244
- if (typeof temperature === 'number') {
1245
- result.temperature = temperature;
1246
- }
1247
- // 处理 top_p
1248
- if (typeof top_p === 'number') {
1249
- result.top_p = top_p;
1250
- }
1251
- // 处理 stop_sequences -> stop
1252
- if (Array.isArray(stop_sequences)) {
1253
- result.stop = stop_sequences;
1254
- }
1255
- else if (typeof stop_sequences === 'string') {
1256
- result.stop = [stop_sequences];
1257
- }
1258
- // 转换 tools: Claude 格式 → Chat Completions 格式
1259
- // Claude: { name, description, input_schema }
1260
- // Chat Completions: { type: "function", function: { name, description, parameters } }
1261
- if (Array.isArray(tools)) {
1262
- result.tools = tools.map((tool) => {
1263
- if (tool && tool.name) {
1264
- return {
1265
- type: 'function',
1266
- function: {
1267
- name: tool.name,
1268
- description: tool.description || '',
1269
- parameters: tool.input_schema || { type: 'object', properties: {}, required: [] }
1270
- }
1271
- };
1272
- }
1273
- return null;
1274
- }).filter(Boolean);
1275
- }
1276
- // 转换 tool_choice
1277
- // Claude: "auto" | "any" | "none" | { type: "tool", name }
1278
- // Chat Completions: "auto" | "required" | "none" | { type: "function", function: { name } }
1279
- if (tool_choice !== undefined) {
1280
- if (tool_choice === 'auto') {
1281
- result.tool_choice = 'auto';
1282
- }
1283
- else if (tool_choice === 'any') {
1284
- result.tool_choice = 'required';
1285
- }
1286
- else if (tool_choice === 'none') {
1287
- result.tool_choice = 'none';
1288
- }
1289
- else if (typeof tool_choice === 'object' && tool_choice.type === 'tool') {
1290
- const tc = tool_choice;
1291
- if (tc.name) {
1292
- result.tool_choice = {
1293
- type: 'function',
1294
- function: { name: tc.name }
1295
- };
1296
- }
1297
- }
1298
- }
1299
- // 转换 thinking → reasoning
1300
- if (thinking) {
1301
- result.reasoning = thinking;
1302
- }
1303
- return result;
1304
- }
1305
- // ===================== codex 得到 responses 数据 =======================
1306
- function transformResponseFromChatCompletionsToResponses(response) {
1307
- if (!response || typeof response !== 'object') {
1308
- return response;
1309
- }
1310
- const choice = Array.isArray(response.choices) && response.choices.length > 0 ? response.choices[0] : null;
1311
- const output = [];
1312
- // 转换消息内容
1313
- if (choice === null || choice === void 0 ? void 0 : choice.message) {
1314
- const message = choice.message;
1315
- const messageContent = [];
1316
- // 处理文本内容
1317
- if (typeof message.content === 'string') {
1318
- messageContent.push({
1319
- type: 'output_text',
1320
- text: message.content
1321
- });
1322
- }
1323
- else if (Array.isArray(message.content)) {
1324
- for (const item of message.content) {
1325
- if (!item || typeof item !== 'object')
1326
- continue;
1327
- if (item.type === 'text' && typeof item.text === 'string') {
1328
- messageContent.push({
1329
- type: 'output_text',
1330
- text: item.text
1331
- });
1332
- }
1333
- if (item.type === 'image_url' && item.image_url) {
1334
- messageContent.push({
1335
- type: 'image',
1336
- source: {
1337
- type: 'url',
1338
- url: item.image_url.url
1339
- }
1340
- });
1341
- }
1342
- }
1343
- }
1344
- if (messageContent.length > 0) {
1345
- output.push({
1346
- type: 'message',
1347
- role: 'assistant',
1348
- content: messageContent
1349
- });
1350
- }
1351
- // 处理工具调用
1352
- if (Array.isArray(message.tool_calls)) {
1353
- for (const toolCall of message.tool_calls) {
1354
- if (toolCall && toolCall.type === 'function') {
1355
- output.push({
1356
- type: 'function_call',
1357
- id: toolCall.id,
1358
- name: toolCall.function.name,
1359
- arguments: toolCall.function.arguments || '{}'
1360
- });
1361
- }
1362
- }
1363
- }
1364
- }
1365
- // 如果没有内容,添加空消息
1366
- if (output.length === 0) {
1367
- output.push({
1368
- type: 'message',
1369
- role: 'assistant',
1370
- content: [{
1371
- type: 'output_text',
1372
- text: ''
1373
- }]
1374
- });
1375
- }
1376
- // 映射 finish reason 到 status
1377
- // Chat Completions: "stop" | "length" | "tool_calls" | "content_filter"
1378
- // Responses: "completed" | "incomplete"
1379
- let status = 'completed';
1380
- if ((choice === null || choice === void 0 ? void 0 : choice.finish_reason) === 'length' || (choice === null || choice === void 0 ? void 0 : choice.finish_reason) === 'max_tokens') {
1381
- status = 'incomplete';
1382
- }
1383
- return {
1384
- id: response.id || `response_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
1385
- object: 'response',
1386
- created_at: response.created || Math.floor(Date.now() / 1000),
1387
- model: response.model,
1388
- output,
1389
- status,
1390
- incomplete_details: status === 'incomplete' ? { reason: 'max_tokens' } : undefined,
1391
- usage: response.usage ? Object.assign({ input_tokens: response.usage.prompt_tokens, output_tokens: response.usage.completion_tokens, total_tokens: response.usage.total_tokens }, (response.usage.cached_tokens && { input_tokens_details: { cached_tokens: response.usage.cached_tokens } })) : undefined
1392
- };
1393
- }
1394
- function transformResponseFromClaudeToResponses(response) {
1395
- var _a, _b, _c, _d;
1396
- if (!response || typeof response !== 'object') {
1397
- return response;
1398
- }
1399
- const output = [];
1400
- // 转换 content blocks
1401
- if (Array.isArray(response.content)) {
1402
- const messageContent = [];
1403
- for (const block of response.content) {
1404
- if (!block || typeof block !== 'object')
1405
- continue;
1406
- if (block.type === 'text' && typeof block.text === 'string') {
1407
- messageContent.push({
1408
- type: 'output_text',
1409
- text: block.text
1410
- });
1411
- }
1412
- if (block.type === 'image' && block.source) {
1413
- messageContent.push({
1414
- type: 'image',
1415
- source: block.source
1416
- });
1417
- }
1418
- if (block.type === 'tool_use') {
1419
- output.push({
1420
- type: 'function_call',
1421
- id: block.id,
1422
- name: block.name,
1423
- arguments: JSON.stringify(block.input || {})
1424
- });
1425
- }
1426
- }
1427
- if (messageContent.length > 0) {
1428
- output.push({
1429
- type: 'message',
1430
- role: 'assistant',
1431
- content: messageContent
1432
- });
1433
- }
1434
- }
1435
- // 如果没有内容,添加空消息
1436
- if (output.length === 0) {
1437
- output.push({
1438
- type: 'message',
1439
- role: 'assistant',
1440
- content: [{
1441
- type: 'output_text',
1442
- text: ''
1443
- }]
1444
- });
1445
- }
1446
- // 映射 stop_reason 为 status
1447
- // Claude: "end_turn" | "max_tokens" | "tool_use" | "stop_sequence" | "max_thinking_length"
1448
- // Responses: "completed" | "incomplete"
1449
- let status = 'completed';
1450
- if (response.stop_reason === 'max_tokens' || response.stop_reason === 'max_thinking_length') {
1451
- status = 'incomplete';
1452
- }
1453
- return {
1454
- id: response.id || `response_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
1455
- object: 'response',
1456
- created_at: Math.floor(Date.now() / 1000),
1457
- model: response.model,
1458
- output,
1459
- status,
1460
- incomplete_details: status === 'incomplete' ? { reason: 'max_tokens' } : undefined,
1461
- usage: response.usage ? {
1462
- input_tokens: ((_a = response.usage) === null || _a === void 0 ? void 0 : _a.input_tokens) || 0,
1463
- output_tokens: ((_b = response.usage) === null || _b === void 0 ? void 0 : _b.output_tokens) || 0,
1464
- total_tokens: ((_c = response.usage) === null || _c === void 0 ? void 0 : _c.total_tokens) || 0,
1465
- cache_read_input_tokens: ((_d = response.usage) === null || _d === void 0 ? void 0 : _d.cache_read_input_tokens) || 0
1466
- } : undefined
1467
- };
1468
- }
1469
- function transformResponseFromGeminiToResponses(response) {
1470
- var _a;
1471
- if (!response || typeof response !== 'object') {
1472
- return response;
1473
- }
1474
- const candidate = Array.isArray(response.candidates) && response.candidates.length > 0 ? response.candidates[0] : null;
1475
- const output = [];
1476
- // 转换 content parts
1477
- if ((_a = candidate === null || candidate === void 0 ? void 0 : candidate.content) === null || _a === void 0 ? void 0 : _a.parts) {
1478
- const messageContent = [];
1479
- for (const part of candidate.content.parts) {
1480
- const p = part;
1481
- if (p.text && typeof p.text === 'string') {
1482
- messageContent.push({
1483
- type: 'output_text',
1484
- text: p.text
1485
- });
1486
- }
1487
- if (p.inlineData) {
1488
- messageContent.push({
1489
- type: 'image',
1490
- source: {
1491
- type: 'base64',
1492
- media_type: p.inlineData.mimeType,
1493
- data: p.inlineData.data
1494
- }
1495
- });
1496
- }
1497
- if (p.functionCall) {
1498
- output.push({
1499
- type: 'function_call',
1500
- id: `call_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
1501
- name: p.functionCall.name || 'tool',
1502
- arguments: JSON.stringify(p.functionCall.args || {})
1503
- });
1504
- }
1505
- }
1506
- if (messageContent.length > 0) {
1507
- output.push({
1508
- type: 'message',
1509
- role: 'assistant',
1510
- content: messageContent
1511
- });
1512
- }
1513
- }
1514
- // 如果没有内容,添加空消息
1515
- if (output.length === 0) {
1516
- output.push({
1517
- type: 'message',
1518
- role: 'assistant',
1519
- content: [{
1520
- type: 'output_text',
1521
- text: ''
1522
- }]
1523
- });
1524
- }
1525
- // 映射 finishReason 为 status
1526
- // Gemini: "STOP" | "MAX_TOKENS" | "SAFETY" | "RECITATION" | "OTHER" | "MALFORMED_FUNCTION_CALL"
1527
- // Responses: "completed" | "incomplete"
1528
- let status = 'completed';
1529
- if ((candidate === null || candidate === void 0 ? void 0 : candidate.finishReason) === 'MAX_TOKENS') {
1530
- status = 'incomplete';
1531
- }
1532
- return {
1533
- id: `response_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
1534
- object: 'response',
1535
- created_at: Math.floor(Date.now() / 1000),
1536
- model: response.model || 'gemini',
1537
- output,
1538
- status,
1539
- incomplete_details: status === 'incomplete' ? { reason: 'max_tokens' } : undefined,
1540
- usage: response.usageMetadata ? {
1541
- input_tokens: response.usageMetadata.promptTokenCount,
1542
- output_tokens: response.usageMetadata.candidatesTokenCount,
1543
- total_tokens: response.usageMetadata.totalTokenCount
1544
- } : undefined
1545
- };
1546
- }
1547
- // ===================== claude code 得到 claude 数据 =======================
1548
- function transformResponseFromChatCompletionsToClaude(response) {
1549
- if (!response || typeof response !== 'object') {
1550
- return response;
1551
- }
1552
- const choice = Array.isArray(response.choices) && response.choices.length > 0 ? response.choices[0] : null;
1553
- const contentBlocks = [];
1554
- // 转换消息内容
1555
- if (choice === null || choice === void 0 ? void 0 : choice.message) {
1556
- const message = choice.message;
1557
- if (typeof message.content === 'string') {
1558
- contentBlocks.push({ type: 'text', text: message.content });
1559
- }
1560
- else if (Array.isArray(message.content)) {
1561
- for (const item of message.content) {
1562
- if (!item || typeof item !== 'object')
1563
- continue;
1564
- if (item.type === 'text' && typeof item.text === 'string') {
1565
- contentBlocks.push({ type: 'text', text: item.text });
1566
- }
1567
- if (item.type === 'image_url' && item.image_url) {
1568
- const url = item.image_url.url;
1569
- if (typeof url === 'string') {
1570
- if (url.startsWith('data:')) {
1571
- const match = url.match(/^data:([^;]+);base64,(.+)$/);
1572
- if (match) {
1573
- contentBlocks.push({
1574
- type: 'image',
1575
- source: {
1576
- type: 'base64',
1577
- media_type: match[1],
1578
- data: match[2]
1579
- }
1580
- });
1581
- }
1582
- }
1583
- else {
1584
- contentBlocks.push({
1585
- type: 'image',
1586
- source: {
1587
- type: 'url',
1588
- url: url
1589
- }
1590
- });
1591
- }
1592
- }
1593
- }
1594
- }
1595
- }
1596
- // 处理工具调用
1597
- if (Array.isArray(message.tool_calls)) {
1598
- for (const toolCall of message.tool_calls) {
1599
- if (toolCall && toolCall.type === 'function') {
1600
- contentBlocks.push({
1601
- type: 'tool_use',
1602
- id: toolCall.id,
1603
- name: toolCall.function.name,
1604
- input: JSON.parse(toolCall.function.arguments || '{}')
1605
- });
1606
- }
1607
- }
1608
- }
1609
- }
1610
- // 如果没有内容,添加空文本
1611
- if (contentBlocks.length === 0) {
1612
- contentBlocks.push({ type: 'text', text: '' });
1613
- }
1614
- // 转换 usage
1615
- const usage = response.usage ? {
1616
- input_tokens: response.usage.prompt_tokens,
1617
- output_tokens: response.usage.completion_tokens,
1618
- total_tokens: response.usage.total_tokens,
1619
- } : undefined;
1620
- // 映射 finish reason
1621
- const stopReasonMap = {
1622
- 'stop': 'end_turn',
1623
- 'length': 'max_tokens',
1624
- 'content_filter': 'content_filter',
1625
- };
1626
- return {
1627
- id: response.id,
1628
- type: 'message',
1629
- role: 'assistant',
1630
- model: response.model,
1631
- content: contentBlocks,
1632
- stop_reason: stopReasonMap[choice === null || choice === void 0 ? void 0 : choice.finish_reason] || 'end_turn',
1633
- stop_sequence: null,
1634
- usage: usage || {
1635
- input_tokens: 0,
1636
- output_tokens: 0,
1637
- cache_read_input_tokens: 0,
1638
- },
1639
- };
1640
- }
1641
- function transformResponseFromResponsesToClaude(response) {
1642
- var _a, _b, _c, _d, _e;
1643
- if (!response || typeof response !== 'object') {
1644
- return response;
1645
- }
1646
- const contentBlocks = [];
1647
- // 遍历 output 数组
1648
- if (Array.isArray(response.output)) {
1649
- for (const outputItem of response.output) {
1650
- if (outputItem.type === 'message' && Array.isArray(outputItem.content)) {
1651
- for (const part of outputItem.content) {
1652
- if (part.type === 'output_text' && typeof part.text === 'string') {
1653
- contentBlocks.push({ type: 'text', text: part.text });
1654
- }
1655
- if (part.type === 'image' && part.source) {
1656
- contentBlocks.push({ type: 'image', source: part.source });
1657
- }
1658
- }
1659
- }
1660
- if (outputItem.type === 'function_call') {
1661
- contentBlocks.push({
1662
- type: 'tool_use',
1663
- id: outputItem.id,
1664
- name: outputItem.name,
1665
- input: JSON.parse(outputItem.arguments || '{}')
1666
- });
1667
- }
1668
- }
1669
- }
1670
- // 如果没有内容,添加空文本
1671
- if (contentBlocks.length === 0) {
1672
- contentBlocks.push({ type: 'text', text: '' });
1673
- }
1674
- // 转换 usage
1675
- const usage = response.usage ? {
1676
- input_tokens: ((_a = response.usage) === null || _a === void 0 ? void 0 : _a.input_tokens) || 0,
1677
- output_tokens: ((_b = response.usage) === null || _b === void 0 ? void 0 : _b.output_tokens) || 0,
1678
- total_tokens: ((_c = response.usage) === null || _c === void 0 ? void 0 : _c.total_tokens) || 0,
1679
- cache_read_input_tokens: ((_d = response.usage) === null || _d === void 0 ? void 0 : _d.cache_read_input_tokens) || 0,
1680
- } : undefined;
1681
- // 转换 stop_reason
1682
- let stop_reason = 'end_turn';
1683
- if (response.status === 'incomplete') {
1684
- stop_reason = ((_e = response.incomplete_details) === null || _e === void 0 ? void 0 : _e.reason) === 'max_tokens' ? 'max_tokens' : 'end_turn';
1685
- }
1686
- return {
1687
- id: response.id,
1688
- type: 'message',
1689
- role: 'assistant',
1690
- model: response.model,
1691
- content: contentBlocks,
1692
- stop_reason,
1693
- stop_sequence: null,
1694
- usage: usage || {
1695
- input_tokens: 0,
1696
- output_tokens: 0,
1697
- cache_read_input_tokens: 0,
1698
- },
1699
- };
1700
- }
1701
- function transformResponseFromGeminiToClaude(response) {
1702
- var _a;
1703
- if (!response || typeof response !== 'object') {
1704
- return response;
1705
- }
1706
- const candidate = Array.isArray(response.candidates) && response.candidates.length > 0 ? response.candidates[0] : null;
1707
- const contentBlocks = [];
1708
- // 转换 content parts
1709
- if ((_a = candidate === null || candidate === void 0 ? void 0 : candidate.content) === null || _a === void 0 ? void 0 : _a.parts) {
1710
- for (const part of candidate.content.parts) {
1711
- const p = part;
1712
- if (p.text && typeof p.text === 'string') {
1713
- contentBlocks.push({ type: 'text', text: p.text });
1714
- }
1715
- if (p.inlineData) {
1716
- contentBlocks.push({
1717
- type: 'image',
1718
- source: {
1719
- type: 'base64',
1720
- media_type: p.inlineData.mimeType,
1721
- data: p.inlineData.data
1722
- }
1723
- });
1724
- }
1725
- if (p.functionCall) {
1726
- contentBlocks.push({
1727
- type: 'tool_use',
1728
- id: `tool_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
1729
- name: p.functionCall.name || 'tool',
1730
- input: p.functionCall.args || {}
1731
- });
1732
- }
1733
- }
1734
- }
1735
- // 如果没有内容,添加空文本
1736
- if (contentBlocks.length === 0) {
1737
- contentBlocks.push({ type: 'text', text: '' });
1738
- }
1739
- // 转换 usage
1740
- const usage = response.usageMetadata ? {
1741
- input_tokens: response.usageMetadata.promptTokenCount,
1742
- output_tokens: response.usageMetadata.candidatesTokenCount,
1743
- total_tokens: response.usageMetadata.totalTokenCount,
1744
- cache_read_input_tokens: response.usageMetadata.cachedContentTokenCount,
1745
- } : undefined;
1746
- // 映射 finish reason
1747
- const stopReasonMap = {
1748
- 'STOP': 'end_turn',
1749
- 'MAX_TOKENS': 'max_tokens',
1750
- 'SAFETY': 'content_filter',
1751
- 'RECITATION': 'content_filter',
1752
- };
1753
- return {
1754
- id: `msg_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
1755
- type: 'message',
1756
- role: 'assistant',
1757
- model: 'gemini',
1758
- content: contentBlocks,
1759
- stop_reason: stopReasonMap[candidate === null || candidate === void 0 ? void 0 : candidate.finishReason] || 'end_turn',
1760
- stop_sequence: null,
1761
- usage: usage || {
1762
- input_tokens: 0,
1763
- output_tokens: 0,
1764
- cache_read_input_tokens: 0,
1765
- },
1766
- };
1767
- }