aicodeswitch 3.9.4 → 4.0.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.
- package/README.md +37 -36
- package/UPGRADE.md +5 -3
- package/bin/restore.js +126 -22
- package/bin/start.js +29 -41
- package/bin/stop.js +3 -3
- package/bin/utils/config-helpers.js +198 -0
- package/dist/server/config-managed-fields.js +69 -0
- package/dist/server/config-merge.js +260 -0
- package/dist/server/database-factory.js +11 -181
- package/dist/server/fs-database.js +209 -72
- package/dist/server/main.js +365 -283
- package/dist/server/original-config-reader.js +154 -88
- package/dist/server/proxy-server.js +993 -387
- package/dist/server/rules-status-service.js +285 -54
- package/dist/server/transformers/chunk-collector.js +26 -4
- package/dist/server/transformers/streaming.js +2334 -280
- package/dist/server/transformers/transformers.js +1765 -0
- package/dist/ui/assets/index-GQBwe1Rm.js +514 -0
- package/dist/ui/index.html +1 -1
- package/package.json +4 -8
- package/schema/claude.schema.md +15 -14
- package/schema/deepseek-chat.schema.md +799 -0
- package/schema/{openai.schema.md → openai-chat-completions.schema.md} +9 -1083
- package/schema/openai-responses.schema.md +226196 -0
- package/schema/stream.md +2592 -0
- package/dist/server/database.js +0 -1609
- package/dist/server/migrate-to-fs.js +0 -353
- package/dist/server/transformers/claude-openai.js +0 -868
- package/dist/server/transformers/gemini.js +0 -625
- package/dist/ui/assets/index-BqSYpNgU.js +0 -511
|
@@ -0,0 +1,1765 @@
|
|
|
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.input_tokens,
|
|
295
|
+
outputTokens: usage.output_tokens || usage.completion_tokens,
|
|
296
|
+
totalTokens: usage.total_tokens,
|
|
297
|
+
cacheReadInputTokens: usage.cached_tokens,
|
|
298
|
+
};
|
|
299
|
+
};
|
|
300
|
+
exports.extractTokenUsageFromOpenAIUsage = extractTokenUsageFromOpenAIUsage;
|
|
301
|
+
const extractTokenUsageFromClaudeUsage = (usage) => {
|
|
302
|
+
var _a, _b;
|
|
303
|
+
if (!usage) {
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
inputTokens: (_a = usage.input_tokens) !== null && _a !== void 0 ? _a : 0,
|
|
308
|
+
outputTokens: (_b = usage.output_tokens) !== null && _b !== void 0 ? _b : 0,
|
|
309
|
+
totalTokens: usage.input_tokens !== undefined && usage.output_tokens !== undefined
|
|
310
|
+
? usage.input_tokens + usage.output_tokens
|
|
311
|
+
: undefined,
|
|
312
|
+
cacheReadInputTokens: usage.cache_read_input_tokens,
|
|
313
|
+
};
|
|
314
|
+
};
|
|
315
|
+
exports.extractTokenUsageFromClaudeUsage = extractTokenUsageFromClaudeUsage;
|
|
316
|
+
// ------------------------- codex 发起请求 -------------------------
|
|
317
|
+
/**
|
|
318
|
+
* 将 codex 发起的对 Responses API 的请求转换为对 Gemini API 的请求的数据格式
|
|
319
|
+
*/
|
|
320
|
+
function transformRequestFromResponsesToGemini(body, _targetModel) {
|
|
321
|
+
var _a;
|
|
322
|
+
const { instructions, input, max_output_tokens, max_tokens, temperature, top_p, stop, tools, tool_choice, reasoning } = body;
|
|
323
|
+
const geminiRequest = {
|
|
324
|
+
contents: [],
|
|
325
|
+
};
|
|
326
|
+
// 处理 instructions 字段(系统提示词)
|
|
327
|
+
if (instructions && typeof instructions === 'string') {
|
|
328
|
+
geminiRequest.systemInstruction = {
|
|
329
|
+
role: 'user',
|
|
330
|
+
parts: [{ text: instructions }],
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
// 处理 input 字段(消息数组)
|
|
334
|
+
if (Array.isArray(input)) {
|
|
335
|
+
for (const msg of input) {
|
|
336
|
+
if (!msg || msg.type !== 'message') {
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
// Gemini API 不支持 developer 角色,映射为 user
|
|
340
|
+
const geminiRole = msg.role === 'assistant' ? 'model' : 'user';
|
|
341
|
+
const geminiContent = {
|
|
342
|
+
role: geminiRole,
|
|
343
|
+
parts: [],
|
|
344
|
+
};
|
|
345
|
+
// 处理 content 数组
|
|
346
|
+
if (Array.isArray(msg.content)) {
|
|
347
|
+
for (const contentItem of msg.content) {
|
|
348
|
+
if (!contentItem || typeof contentItem !== 'object') {
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
// 处理文本内容: input_text -> text
|
|
352
|
+
if (contentItem.type === 'input_text' && typeof contentItem.text === 'string') {
|
|
353
|
+
geminiContent.parts.push({ text: contentItem.text });
|
|
354
|
+
}
|
|
355
|
+
// 处理图像内容: input_image -> inlineData
|
|
356
|
+
if (contentItem.type === 'input_image' && contentItem.image_url) {
|
|
357
|
+
const imageUrl = contentItem.image_url.url;
|
|
358
|
+
if (typeof imageUrl === 'string') {
|
|
359
|
+
if (imageUrl.startsWith('data:')) {
|
|
360
|
+
// 处理 base64 格式图像
|
|
361
|
+
const match = imageUrl.match(/^data:([^;]+);base64,(.+)$/);
|
|
362
|
+
if (match) {
|
|
363
|
+
geminiContent.parts.push({
|
|
364
|
+
inlineData: {
|
|
365
|
+
mimeType: match[1],
|
|
366
|
+
data: match[2],
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// URL 格式需要下载后转换,这里暂时跳过
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// 处理字符串格式 content
|
|
377
|
+
else if (typeof msg.content === 'string') {
|
|
378
|
+
geminiContent.parts.push({ text: msg.content });
|
|
379
|
+
}
|
|
380
|
+
// 确保至少有一个 part
|
|
381
|
+
if (geminiContent.parts.length === 0) {
|
|
382
|
+
geminiContent.parts.push({ text: '' });
|
|
383
|
+
}
|
|
384
|
+
geminiRequest.contents.push(geminiContent);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// 构建生成配置
|
|
388
|
+
const generationConfig = {};
|
|
389
|
+
if (typeof temperature === 'number') {
|
|
390
|
+
generationConfig.temperature = temperature;
|
|
391
|
+
}
|
|
392
|
+
if (typeof top_p === 'number') {
|
|
393
|
+
generationConfig.topP = top_p;
|
|
394
|
+
}
|
|
395
|
+
if (typeof max_output_tokens === 'number') {
|
|
396
|
+
generationConfig.maxOutputTokens = max_output_tokens;
|
|
397
|
+
}
|
|
398
|
+
else if (typeof max_tokens === 'number') {
|
|
399
|
+
generationConfig.maxOutputTokens = max_tokens;
|
|
400
|
+
}
|
|
401
|
+
// 处理 stop: Responses API 是数组,Gemini 也是数组
|
|
402
|
+
if (Array.isArray(stop)) {
|
|
403
|
+
generationConfig.stopSequences = stop;
|
|
404
|
+
}
|
|
405
|
+
else if (typeof stop === 'string') {
|
|
406
|
+
generationConfig.stopSequences = [stop];
|
|
407
|
+
}
|
|
408
|
+
// 处理 reasoning -> thinkingConfig
|
|
409
|
+
if (reasoning) {
|
|
410
|
+
const thinkingConfig = createGeminiThinkingConfigFromResponsesReasoning(reasoning);
|
|
411
|
+
if (thinkingConfig) {
|
|
412
|
+
generationConfig.thinkingConfig = thinkingConfig;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (Object.keys(generationConfig).length > 0) {
|
|
416
|
+
geminiRequest.generationConfig = generationConfig;
|
|
417
|
+
}
|
|
418
|
+
// 转换 tools
|
|
419
|
+
if (Array.isArray(tools)) {
|
|
420
|
+
const functionDeclarations = [];
|
|
421
|
+
for (const tool of tools) {
|
|
422
|
+
if (tool && tool.type === 'function' && tool.function) {
|
|
423
|
+
functionDeclarations.push({
|
|
424
|
+
name: tool.function.name,
|
|
425
|
+
description: tool.function.description || '',
|
|
426
|
+
parameters: (0, exports.sanitizeSchemaForGeminiFunctionDeclaration)(tool.function.parameters || { type: 'object', properties: {}, required: [] }),
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
if (functionDeclarations.length > 0) {
|
|
431
|
+
geminiRequest.tools = [{ functionDeclarations }];
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
// 转换 tool_choice -> toolConfig
|
|
435
|
+
if (tool_choice !== undefined) {
|
|
436
|
+
const toolConfig = { functionCallingConfig: {} };
|
|
437
|
+
if (tool_choice === 'auto') {
|
|
438
|
+
toolConfig.functionCallingConfig.mode = 'AUTO';
|
|
439
|
+
}
|
|
440
|
+
else if (tool_choice === 'required' || tool_choice === 'any') {
|
|
441
|
+
toolConfig.functionCallingConfig.mode = 'ANY';
|
|
442
|
+
}
|
|
443
|
+
else if (tool_choice === 'none') {
|
|
444
|
+
toolConfig.functionCallingConfig.mode = 'NONE';
|
|
445
|
+
}
|
|
446
|
+
else if (typeof tool_choice === 'object' && tool_choice.type === 'function') {
|
|
447
|
+
const tc = tool_choice;
|
|
448
|
+
if ((_a = tc.function) === null || _a === void 0 ? void 0 : _a.name) {
|
|
449
|
+
toolConfig.functionCallingConfig.mode = 'ANY';
|
|
450
|
+
toolConfig.functionCallingConfig.allowedFunctionNames = [tc.function.name];
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (Object.keys(toolConfig.functionCallingConfig).length > 0) {
|
|
454
|
+
geminiRequest.toolConfig = toolConfig;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return geminiRequest;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* 将 codex 发起的对 Responses API 的请求转换为对 Claude API 的请求的数据格式
|
|
461
|
+
*/
|
|
462
|
+
function transformRequestFromResponsesToClaude(body, targetModel) {
|
|
463
|
+
var _a;
|
|
464
|
+
const { model, instructions, input, max_output_tokens, max_tokens, temperature, top_p, stop, tools, tool_choice } = body;
|
|
465
|
+
const messages = [];
|
|
466
|
+
// 1. 处理 instructions 字段(系统提示词)
|
|
467
|
+
// Claude API 使用 system 字段,不在 messages 中
|
|
468
|
+
let system = '';
|
|
469
|
+
if (instructions && typeof instructions === 'string') {
|
|
470
|
+
system = instructions;
|
|
471
|
+
}
|
|
472
|
+
// 2. 处理 input 字段(消息数组)
|
|
473
|
+
if (Array.isArray(input)) {
|
|
474
|
+
for (const msg of input) {
|
|
475
|
+
if (!msg || msg.type !== 'message') {
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
// 提取 content 数组中的内容
|
|
479
|
+
const contentBlocks = [];
|
|
480
|
+
if (Array.isArray(msg.content)) {
|
|
481
|
+
for (const contentItem of msg.content) {
|
|
482
|
+
if (!contentItem || typeof contentItem !== 'object') {
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
// input_text -> text
|
|
486
|
+
if (contentItem.type === 'input_text' && typeof contentItem.text === 'string') {
|
|
487
|
+
contentBlocks.push({ type: 'text', text: contentItem.text });
|
|
488
|
+
}
|
|
489
|
+
// input_image -> image(Claude 格式)
|
|
490
|
+
if (contentItem.type === 'input_image' && contentItem.image_url) {
|
|
491
|
+
const imageUrl = contentItem.image_url.url;
|
|
492
|
+
if (typeof imageUrl === 'string') {
|
|
493
|
+
if (imageUrl.startsWith('data:')) {
|
|
494
|
+
const match = imageUrl.match(/^data:([^;]+);base64,(.+)$/);
|
|
495
|
+
if (match) {
|
|
496
|
+
contentBlocks.push({
|
|
497
|
+
type: 'image',
|
|
498
|
+
source: {
|
|
499
|
+
type: 'base64',
|
|
500
|
+
media_type: match[1],
|
|
501
|
+
data: match[2],
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
// URL 格式
|
|
508
|
+
contentBlocks.push({
|
|
509
|
+
type: 'image',
|
|
510
|
+
source: {
|
|
511
|
+
type: 'url',
|
|
512
|
+
url: imageUrl,
|
|
513
|
+
},
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
else if (typeof msg.content === 'string') {
|
|
521
|
+
contentBlocks.push({ type: 'text', text: msg.content });
|
|
522
|
+
}
|
|
523
|
+
// 映射角色: developer -> user
|
|
524
|
+
const role = msg.role === 'developer' ? 'user' : msg.role;
|
|
525
|
+
// 添加到 messages 数组
|
|
526
|
+
if (contentBlocks.length > 0) {
|
|
527
|
+
messages.push({
|
|
528
|
+
role,
|
|
529
|
+
content: contentBlocks,
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
// 3. 构建转换后的请求
|
|
535
|
+
const transformed = {
|
|
536
|
+
model: targetModel || model,
|
|
537
|
+
max_tokens: max_output_tokens || max_tokens,
|
|
538
|
+
};
|
|
539
|
+
// 添加 messages
|
|
540
|
+
if (messages.length > 0) {
|
|
541
|
+
transformed.messages = messages;
|
|
542
|
+
}
|
|
543
|
+
// 添加 system
|
|
544
|
+
if (system) {
|
|
545
|
+
transformed.system = system;
|
|
546
|
+
}
|
|
547
|
+
// 4. 处理工具定义转换
|
|
548
|
+
// Responses API: { type: "function", function: { name, description, parameters } }
|
|
549
|
+
// Claude: { name, description, input_schema: parameters }
|
|
550
|
+
if (Array.isArray(tools)) {
|
|
551
|
+
transformed.tools = tools.map((tool) => {
|
|
552
|
+
if (tool && tool.type === 'function' && tool.function) {
|
|
553
|
+
return {
|
|
554
|
+
name: tool.function.name,
|
|
555
|
+
description: tool.function.description || '',
|
|
556
|
+
input_schema: tool.function.parameters || { type: 'object', properties: {}, required: [] },
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
return null;
|
|
560
|
+
}).filter(Boolean);
|
|
561
|
+
}
|
|
562
|
+
// 5. 处理 tool_choice 映射
|
|
563
|
+
// Responses API: "auto" | "required" | "none" | { type: "function", function: { name } }
|
|
564
|
+
// Claude: "auto" | "any" | "none" | { type: "tool", name: string }
|
|
565
|
+
if (tool_choice !== undefined) {
|
|
566
|
+
if (tool_choice === 'auto') {
|
|
567
|
+
transformed.tool_choice = 'auto';
|
|
568
|
+
}
|
|
569
|
+
else if (tool_choice === 'required') {
|
|
570
|
+
transformed.tool_choice = 'any';
|
|
571
|
+
}
|
|
572
|
+
else if (tool_choice === 'none') {
|
|
573
|
+
transformed.tool_choice = 'none';
|
|
574
|
+
}
|
|
575
|
+
else if (typeof tool_choice === 'object' && tool_choice.type === 'function') {
|
|
576
|
+
const tc = tool_choice;
|
|
577
|
+
if ((_a = tc.function) === null || _a === void 0 ? void 0 : _a.name) {
|
|
578
|
+
transformed.tool_choice = {
|
|
579
|
+
type: 'tool',
|
|
580
|
+
name: tc.function.name,
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
// 6. 处理其他参数
|
|
586
|
+
if (typeof temperature === 'number') {
|
|
587
|
+
transformed.temperature = temperature;
|
|
588
|
+
}
|
|
589
|
+
if (typeof top_p === 'number') {
|
|
590
|
+
transformed.top_p = top_p;
|
|
591
|
+
}
|
|
592
|
+
if (body.stream !== undefined) {
|
|
593
|
+
transformed.stream = body.stream;
|
|
594
|
+
}
|
|
595
|
+
// 处理 stop: Responses API 是数组,Claude 是 stop_sequences
|
|
596
|
+
if (Array.isArray(stop)) {
|
|
597
|
+
transformed.stop_sequences = stop;
|
|
598
|
+
}
|
|
599
|
+
else if (typeof stop === 'string') {
|
|
600
|
+
transformed.stop_sequences = [stop];
|
|
601
|
+
}
|
|
602
|
+
return transformed;
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* 将 codex 发起的对 Responses API 的请求转换为对 Chat Completions API 的请求的数据格式
|
|
606
|
+
* @param body Responses API 格式的请求体
|
|
607
|
+
* @param targetModel 目标模型名称(可选)
|
|
608
|
+
* @returns Chat Completions API 格式的请求体
|
|
609
|
+
*/
|
|
610
|
+
function transformRequestFromResponsesToChatCompletions(body, targetModel) {
|
|
611
|
+
var _a;
|
|
612
|
+
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"]);
|
|
613
|
+
/**
|
|
614
|
+
* 将 Responses API 的 content item 转换为 Chat Completions 格式
|
|
615
|
+
* Responses API: { type: "input_text", text: "..." }
|
|
616
|
+
* Chat Completions: { type: "text", text: "..." }
|
|
617
|
+
* Responses API: { type: "input_image", image_url: { url: "..." } }
|
|
618
|
+
* Chat Completions: { type: "image_url", image_url: { url: "..." } }
|
|
619
|
+
*/
|
|
620
|
+
const transformResponsesContentItemToChatCompletionContentItem = (item) => {
|
|
621
|
+
if (!item || typeof item !== 'object') {
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
if (item.type === 'input_text') {
|
|
625
|
+
return { type: 'text', text: item.text };
|
|
626
|
+
}
|
|
627
|
+
if (item.type === 'input_image' && item.image_url) {
|
|
628
|
+
return { type: 'image_url', image_url: item.image_url };
|
|
629
|
+
}
|
|
630
|
+
// 其他类型直接返回
|
|
631
|
+
return item;
|
|
632
|
+
};
|
|
633
|
+
// 输入验证:检查 input 是否为数组
|
|
634
|
+
if (!Array.isArray(input)) {
|
|
635
|
+
// 如果 input 不是数组,尝试处理字符串格式
|
|
636
|
+
const messages = [];
|
|
637
|
+
const systemRole = (0, exports.shouldUseDeveloperRoleAsSystemRole)(targetModel || model) ? 'developer' : 'system';
|
|
638
|
+
// 处理 instructions
|
|
639
|
+
if (instructions && typeof instructions === 'string') {
|
|
640
|
+
messages.push({
|
|
641
|
+
role: systemRole,
|
|
642
|
+
content: instructions,
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
// 处理 input(如果是字符串)
|
|
646
|
+
if (typeof input === 'string' && input) {
|
|
647
|
+
messages.push({
|
|
648
|
+
role: 'user',
|
|
649
|
+
content: input,
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
// 构建转换后的请求
|
|
653
|
+
const result = {
|
|
654
|
+
model: targetModel || model,
|
|
655
|
+
messages,
|
|
656
|
+
};
|
|
657
|
+
// 处理 max_tokens(Responses API 使用 max_output_tokens)
|
|
658
|
+
if (typeof max_output_tokens === 'number') {
|
|
659
|
+
result.max_tokens = max_output_tokens;
|
|
660
|
+
}
|
|
661
|
+
else if (typeof others.max_tokens === 'number') {
|
|
662
|
+
result.max_tokens = others.max_tokens;
|
|
663
|
+
}
|
|
664
|
+
// 处理 tools
|
|
665
|
+
if (Array.isArray(tools)) {
|
|
666
|
+
result.tools = tools;
|
|
667
|
+
}
|
|
668
|
+
// 处理 tool_choice
|
|
669
|
+
if (tool_choice !== undefined) {
|
|
670
|
+
result.tool_choice = tool_choice;
|
|
671
|
+
}
|
|
672
|
+
// 处理 parallel_tool_calls
|
|
673
|
+
if (parallel_tool_calls !== undefined) {
|
|
674
|
+
result.parallel_tool_calls = parallel_tool_calls;
|
|
675
|
+
}
|
|
676
|
+
// 处理 reasoning(Chat Completions API 可能不直接支持,但保留用于兼容)
|
|
677
|
+
if (reasoning) {
|
|
678
|
+
result.reasoning = reasoning;
|
|
679
|
+
}
|
|
680
|
+
// 添加其他兼容的字段(temperature, top_p, stream 等)
|
|
681
|
+
const compatibleFields = ['temperature', 'top_p', 'stream', 'stop', 'frequency_penalty', 'presence_penalty', 'seed', 'user', 'response_format'];
|
|
682
|
+
for (const field of compatibleFields) {
|
|
683
|
+
if (others[field] !== undefined) {
|
|
684
|
+
result[field] = others[field];
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
return result;
|
|
688
|
+
}
|
|
689
|
+
// 提取 developer 消息和普通消息
|
|
690
|
+
const developerItems = input.filter((item) => item.type === 'message' && item.role === 'developer');
|
|
691
|
+
const nonDeveloperItems = input.filter((item) => item.type === 'message' && item.role !== 'developer');
|
|
692
|
+
// 构建 messages 数组
|
|
693
|
+
const messages = [];
|
|
694
|
+
// 处理系统/developer 消息
|
|
695
|
+
const systemRole = (0, exports.shouldUseDeveloperRoleAsSystemRole)(targetModel || model) ? 'developer' : 'system';
|
|
696
|
+
const systemContentItems = [];
|
|
697
|
+
// 添加 instructions
|
|
698
|
+
if (instructions && typeof instructions === 'string') {
|
|
699
|
+
systemContentItems.push({ type: 'text', text: instructions });
|
|
700
|
+
}
|
|
701
|
+
// 添加 developer 消息中的内容
|
|
702
|
+
for (const item of developerItems) {
|
|
703
|
+
if (!item)
|
|
704
|
+
continue;
|
|
705
|
+
const itemContent = Array.isArray(item.content)
|
|
706
|
+
? item.content.map(transformResponsesContentItemToChatCompletionContentItem).filter(Boolean)
|
|
707
|
+
: (typeof item.content === 'string' ? [{ type: 'text', text: item.content }] : []);
|
|
708
|
+
systemContentItems.push(...itemContent);
|
|
709
|
+
}
|
|
710
|
+
// 只有当有系统内容时才添加系统消息
|
|
711
|
+
if (systemContentItems.length > 0) {
|
|
712
|
+
messages.push({
|
|
713
|
+
role: systemRole,
|
|
714
|
+
content: systemContentItems,
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
// 处理非 developer 消息
|
|
718
|
+
for (const item of nonDeveloperItems) {
|
|
719
|
+
if (!item || item.type !== 'message')
|
|
720
|
+
continue;
|
|
721
|
+
const messageContent = Array.isArray(item.content)
|
|
722
|
+
? item.content.map(transformResponsesContentItemToChatCompletionContentItem).filter(Boolean)
|
|
723
|
+
: (typeof item.content === 'string' ? item.content : []);
|
|
724
|
+
messages.push({
|
|
725
|
+
role: item.role,
|
|
726
|
+
content: messageContent,
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
// 构建转换后的请求
|
|
730
|
+
const result = {
|
|
731
|
+
model: targetModel || model,
|
|
732
|
+
messages,
|
|
733
|
+
};
|
|
734
|
+
// 处理 max_tokens(Responses API 使用 max_output_tokens)
|
|
735
|
+
if (typeof max_output_tokens === 'number') {
|
|
736
|
+
result.max_tokens = max_output_tokens;
|
|
737
|
+
}
|
|
738
|
+
else if (typeof others.max_tokens === 'number') {
|
|
739
|
+
result.max_tokens = others.max_tokens;
|
|
740
|
+
}
|
|
741
|
+
// 处理 tools:需要确保格式正确且不包含空函数
|
|
742
|
+
if (Array.isArray(tools) && tools.length > 0) {
|
|
743
|
+
const validTools = tools.map((tool) => {
|
|
744
|
+
if (tool && tool.type === 'function' && tool.function) {
|
|
745
|
+
const fn = tool.function;
|
|
746
|
+
// 验证必需字段
|
|
747
|
+
if (!fn.name || typeof fn.name !== 'string') {
|
|
748
|
+
return null;
|
|
749
|
+
}
|
|
750
|
+
// 确保参数格式正确
|
|
751
|
+
const params = fn.parameters || { type: 'object', properties: {}, required: [] };
|
|
752
|
+
if (typeof params !== 'object' || params.type !== 'object') {
|
|
753
|
+
return null;
|
|
754
|
+
}
|
|
755
|
+
return {
|
|
756
|
+
type: 'function',
|
|
757
|
+
function: Object.assign({ name: fn.name, description: fn.description || '', parameters: params }, (fn.strict !== undefined && { strict: fn.strict }))
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
return null;
|
|
761
|
+
}).filter(Boolean);
|
|
762
|
+
// 只有当有有效工具时才添加 tools 字段
|
|
763
|
+
if (validTools.length > 0) {
|
|
764
|
+
result.tools = validTools;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
// 处理 tool_choice
|
|
768
|
+
if (tool_choice !== undefined) {
|
|
769
|
+
// 验证 tool_choice 格式
|
|
770
|
+
if (typeof tool_choice === 'string' && ['auto', 'none', 'required'].includes(tool_choice)) {
|
|
771
|
+
result.tool_choice = tool_choice;
|
|
772
|
+
}
|
|
773
|
+
else if (typeof tool_choice === 'object' && tool_choice.type === 'function' && ((_a = tool_choice.function) === null || _a === void 0 ? void 0 : _a.name)) {
|
|
774
|
+
result.tool_choice = {
|
|
775
|
+
type: 'function',
|
|
776
|
+
function: { name: tool_choice.function.name }
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
// 处理 parallel_tool_calls
|
|
781
|
+
if (parallel_tool_calls !== undefined) {
|
|
782
|
+
result.parallel_tool_calls = parallel_tool_calls;
|
|
783
|
+
}
|
|
784
|
+
// 处理 reasoning(Chat Completions API 可能不直接支持,但保留用于兼容)
|
|
785
|
+
if (reasoning) {
|
|
786
|
+
result.reasoning = reasoning;
|
|
787
|
+
}
|
|
788
|
+
// 添加其他兼容的字段(temperature, top_p, stream 等)
|
|
789
|
+
const compatibleFields = ['temperature', 'top_p', 'stream', 'stop', 'frequency_penalty', 'presence_penalty', 'seed', 'user', 'response_format'];
|
|
790
|
+
for (const field of compatibleFields) {
|
|
791
|
+
if (others[field] !== undefined) {
|
|
792
|
+
result[field] = others[field];
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
return result;
|
|
796
|
+
}
|
|
797
|
+
// ----------------------- claude code 发起的请求转换 -----------------------
|
|
798
|
+
/**
|
|
799
|
+
* 将 claude code 发起的请求转换成对 gemini API 的请求
|
|
800
|
+
* @param body
|
|
801
|
+
* @param _model
|
|
802
|
+
*/
|
|
803
|
+
function transformRequestFromClaudeToGemini(body, _model) {
|
|
804
|
+
const { system, messages, max_tokens, temperature, top_p, stop_sequences, tools, tool_choice, thinking } = body;
|
|
805
|
+
const geminiRequest = {
|
|
806
|
+
contents: [],
|
|
807
|
+
};
|
|
808
|
+
// 处理 system 指令
|
|
809
|
+
if (system) {
|
|
810
|
+
let systemText = '';
|
|
811
|
+
if (typeof system === 'string') {
|
|
812
|
+
systemText = system;
|
|
813
|
+
}
|
|
814
|
+
else if (Array.isArray(system)) {
|
|
815
|
+
const textParts = [];
|
|
816
|
+
for (const block of system) {
|
|
817
|
+
if (block && block.type === 'text' && typeof block.text === 'string') {
|
|
818
|
+
textParts.push(block.text);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
systemText = textParts.join('\n\n');
|
|
822
|
+
}
|
|
823
|
+
else if (system && typeof system === 'object' && system.type === 'text') {
|
|
824
|
+
systemText = system.text;
|
|
825
|
+
}
|
|
826
|
+
if (systemText) {
|
|
827
|
+
geminiRequest.systemInstruction = {
|
|
828
|
+
role: 'user',
|
|
829
|
+
parts: [{ text: systemText }]
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
// 转换 messages
|
|
834
|
+
if (Array.isArray(messages)) {
|
|
835
|
+
for (const msg of messages) {
|
|
836
|
+
if (!msg || typeof msg !== 'object')
|
|
837
|
+
continue;
|
|
838
|
+
const geminiRole = msg.role === 'assistant' ? 'model' :
|
|
839
|
+
msg.role === 'tool' ? 'function' : 'user';
|
|
840
|
+
const geminiContent = {
|
|
841
|
+
role: geminiRole,
|
|
842
|
+
parts: [],
|
|
843
|
+
};
|
|
844
|
+
if (typeof msg.content === 'string') {
|
|
845
|
+
geminiContent.parts.push({ text: msg.content });
|
|
846
|
+
}
|
|
847
|
+
else if (Array.isArray(msg.content)) {
|
|
848
|
+
for (const block of msg.content) {
|
|
849
|
+
if (!block || typeof block !== 'object')
|
|
850
|
+
continue;
|
|
851
|
+
// text → text
|
|
852
|
+
if (block.type === 'text' && typeof block.text === 'string') {
|
|
853
|
+
geminiContent.parts.push({ text: block.text });
|
|
854
|
+
}
|
|
855
|
+
// image → inlineData
|
|
856
|
+
if (block.type === 'image' && block.source) {
|
|
857
|
+
const source = block.source;
|
|
858
|
+
if (source.type === 'base64' && source.data && source.media_type) {
|
|
859
|
+
geminiContent.parts.push({
|
|
860
|
+
inlineData: {
|
|
861
|
+
mimeType: source.media_type,
|
|
862
|
+
data: source.data
|
|
863
|
+
}
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
else if (source.type === 'url' && source.url) {
|
|
867
|
+
// URL 格式需要特殊处理,这里暂时跳过
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
// tool_use → functionCall
|
|
871
|
+
if (block.type === 'tool_use') {
|
|
872
|
+
geminiContent.parts.push({
|
|
873
|
+
functionCall: {
|
|
874
|
+
name: block.name || 'tool',
|
|
875
|
+
args: block.input || {}
|
|
876
|
+
}
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
// tool_result → functionResponse
|
|
880
|
+
if (block.type === 'tool_result') {
|
|
881
|
+
// Claude 的 tool_result 包含 tool_use_id,但 Gemini 需要的是函数名
|
|
882
|
+
// 这里暂时使用 'tool' 作为函数名,实际应该从上下文中获取正确的函数名
|
|
883
|
+
geminiContent.parts.push({
|
|
884
|
+
functionResponse: {
|
|
885
|
+
name: 'tool', // TODO: 应该从之前的 tool_use 中获取正确的函数名
|
|
886
|
+
response: typeof msg.content === 'string' ? msg.content : msg.content || {}
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
else if (typeof msg.content === 'string') {
|
|
893
|
+
// tool 消息的处理
|
|
894
|
+
geminiContent.parts.push({
|
|
895
|
+
functionResponse: {
|
|
896
|
+
name: 'tool', // TODO: 应该从 tool_use_id 对应的函数中获取
|
|
897
|
+
response: msg.content || {}
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
// 确保至少有一个 part
|
|
902
|
+
if (geminiContent.parts.length === 0) {
|
|
903
|
+
geminiContent.parts.push({ text: '' });
|
|
904
|
+
}
|
|
905
|
+
geminiRequest.contents.push(geminiContent);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
// 构建生成配置
|
|
909
|
+
const generationConfig = {};
|
|
910
|
+
if (typeof temperature === 'number') {
|
|
911
|
+
generationConfig.temperature = temperature;
|
|
912
|
+
}
|
|
913
|
+
if (typeof top_p === 'number') {
|
|
914
|
+
generationConfig.topP = top_p;
|
|
915
|
+
}
|
|
916
|
+
if (typeof max_tokens === 'number') {
|
|
917
|
+
generationConfig.maxOutputTokens = max_tokens;
|
|
918
|
+
}
|
|
919
|
+
// 处理 stop_sequences -> stopSequences
|
|
920
|
+
if (Array.isArray(stop_sequences)) {
|
|
921
|
+
generationConfig.stopSequences = stop_sequences;
|
|
922
|
+
}
|
|
923
|
+
// 转换 thinking → thinkingConfig
|
|
924
|
+
if (thinking) {
|
|
925
|
+
const thinkingConfig = createGeminiThinkingConfigFromClaudeThinking(thinking);
|
|
926
|
+
if (thinkingConfig) {
|
|
927
|
+
generationConfig.thinkingConfig = thinkingConfig;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
if (Object.keys(generationConfig).length > 0) {
|
|
931
|
+
geminiRequest.generationConfig = generationConfig;
|
|
932
|
+
}
|
|
933
|
+
// 转换 tools
|
|
934
|
+
// Claude: { name, description, input_schema }
|
|
935
|
+
// Gemini: { functionDeclarations: [{ name, description, parameters }] }
|
|
936
|
+
if (Array.isArray(tools)) {
|
|
937
|
+
const functionDeclarations = [];
|
|
938
|
+
for (const tool of tools) {
|
|
939
|
+
if (tool && tool.name) {
|
|
940
|
+
functionDeclarations.push({
|
|
941
|
+
name: tool.name,
|
|
942
|
+
description: tool.description || '',
|
|
943
|
+
parameters: (0, exports.sanitizeSchemaForGeminiFunctionDeclaration)(tool.input_schema || { type: 'object', properties: {}, required: [] })
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
if (functionDeclarations.length > 0) {
|
|
948
|
+
geminiRequest.tools = [{ functionDeclarations }];
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
// 转换 tool_choice
|
|
952
|
+
if (tool_choice) {
|
|
953
|
+
const toolConfig = { functionCallingConfig: {} };
|
|
954
|
+
if (tool_choice === 'auto') {
|
|
955
|
+
toolConfig.functionCallingConfig.mode = 'AUTO';
|
|
956
|
+
}
|
|
957
|
+
else if (tool_choice === 'any' || tool_choice === 'required') {
|
|
958
|
+
toolConfig.functionCallingConfig.mode = 'ANY';
|
|
959
|
+
}
|
|
960
|
+
else if (tool_choice === 'none') {
|
|
961
|
+
toolConfig.functionCallingConfig.mode = 'NONE';
|
|
962
|
+
}
|
|
963
|
+
else if (typeof tool_choice === 'object') {
|
|
964
|
+
const tc = tool_choice;
|
|
965
|
+
if (tc.type === 'tool' && tc.name) {
|
|
966
|
+
toolConfig.functionCallingConfig.mode = 'ANY';
|
|
967
|
+
toolConfig.functionCallingConfig.allowedFunctionNames = [tc.name];
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
if (Object.keys(toolConfig.functionCallingConfig).length > 0) {
|
|
971
|
+
geminiRequest.toolConfig = toolConfig;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
return geminiRequest;
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* 将 claude code 发起的请求转换成对 responses API 的请求
|
|
978
|
+
* @param body
|
|
979
|
+
* @param model
|
|
980
|
+
*/
|
|
981
|
+
function transformRequestFromClaudeToResponses(body, model) {
|
|
982
|
+
const { system, messages, max_tokens, temperature, top_p, stop_sequences, tools, tool_choice } = body;
|
|
983
|
+
const input = [];
|
|
984
|
+
let instructions = '';
|
|
985
|
+
// 处理 system 指令
|
|
986
|
+
if (system) {
|
|
987
|
+
if (typeof system === 'string') {
|
|
988
|
+
instructions = system;
|
|
989
|
+
}
|
|
990
|
+
else if (Array.isArray(system)) {
|
|
991
|
+
const textParts = [];
|
|
992
|
+
for (const block of system) {
|
|
993
|
+
if (block && block.type === 'text' && typeof block.text === 'string') {
|
|
994
|
+
textParts.push(block.text);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
instructions = textParts.join('\n\n');
|
|
998
|
+
}
|
|
999
|
+
else if (system && typeof system === 'object' && system.type === 'text') {
|
|
1000
|
+
instructions = system.text;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
// 转换 messages
|
|
1004
|
+
if (Array.isArray(messages)) {
|
|
1005
|
+
for (const msg of messages) {
|
|
1006
|
+
if (!msg || typeof msg !== 'object')
|
|
1007
|
+
continue;
|
|
1008
|
+
const content = [];
|
|
1009
|
+
if (typeof msg.content === 'string') {
|
|
1010
|
+
content.push({
|
|
1011
|
+
type: 'input_text',
|
|
1012
|
+
text: msg.content
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
else if (Array.isArray(msg.content)) {
|
|
1016
|
+
for (const block of msg.content) {
|
|
1017
|
+
if (!block || typeof block !== 'object')
|
|
1018
|
+
continue;
|
|
1019
|
+
// text → input_text
|
|
1020
|
+
if (block.type === 'text' && typeof block.text === 'string') {
|
|
1021
|
+
content.push({
|
|
1022
|
+
type: 'input_text',
|
|
1023
|
+
text: block.text
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
// image → input_image
|
|
1027
|
+
if (block.type === 'image' && block.source) {
|
|
1028
|
+
const source = block.source;
|
|
1029
|
+
if (source.type === 'base64' && source.data && source.media_type) {
|
|
1030
|
+
const dataUrl = `data:${source.media_type};base64,${source.data}`;
|
|
1031
|
+
content.push({
|
|
1032
|
+
type: 'input_image',
|
|
1033
|
+
image_url: { url: dataUrl }
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
else if (source.type === 'url' && source.url) {
|
|
1037
|
+
content.push({
|
|
1038
|
+
type: 'input_image',
|
|
1039
|
+
image_url: { url: source.url }
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
// tool_use → function_call (Responses API 格式)
|
|
1044
|
+
if (block.type === 'tool_use') {
|
|
1045
|
+
// Responses API 在 output 中包含 function_call,不是在 input 中
|
|
1046
|
+
// 所以这里不转换 tool_use,只保留文本和图像
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
input.push({
|
|
1051
|
+
type: 'message',
|
|
1052
|
+
role: msg.role,
|
|
1053
|
+
content: content
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
const result = {
|
|
1058
|
+
model: model || body.model,
|
|
1059
|
+
input,
|
|
1060
|
+
};
|
|
1061
|
+
if (instructions) {
|
|
1062
|
+
result.instructions = instructions;
|
|
1063
|
+
}
|
|
1064
|
+
if (typeof temperature === 'number') {
|
|
1065
|
+
result.temperature = temperature;
|
|
1066
|
+
}
|
|
1067
|
+
if (typeof top_p === 'number') {
|
|
1068
|
+
result.top_p = top_p;
|
|
1069
|
+
}
|
|
1070
|
+
if (typeof max_tokens === 'number') {
|
|
1071
|
+
result.max_output_tokens = max_tokens;
|
|
1072
|
+
}
|
|
1073
|
+
// 处理 stop_sequences -> stop
|
|
1074
|
+
if (Array.isArray(stop_sequences)) {
|
|
1075
|
+
result.stop = stop_sequences;
|
|
1076
|
+
}
|
|
1077
|
+
else if (typeof stop_sequences === 'string') {
|
|
1078
|
+
result.stop = [stop_sequences];
|
|
1079
|
+
}
|
|
1080
|
+
// 处理 tools: Claude 格式 → Responses API 格式
|
|
1081
|
+
// Claude: { name, description, input_schema }
|
|
1082
|
+
// Responses: { type: "function", function: { name, description, parameters } }
|
|
1083
|
+
if (Array.isArray(tools)) {
|
|
1084
|
+
result.tools = tools.map((tool) => {
|
|
1085
|
+
if (tool && tool.name) {
|
|
1086
|
+
return {
|
|
1087
|
+
type: 'function',
|
|
1088
|
+
function: {
|
|
1089
|
+
name: tool.name,
|
|
1090
|
+
description: tool.description || '',
|
|
1091
|
+
parameters: tool.input_schema || { type: 'object', properties: {}, required: [] },
|
|
1092
|
+
},
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
return null;
|
|
1096
|
+
}).filter(Boolean);
|
|
1097
|
+
}
|
|
1098
|
+
// 处理 tool_choice
|
|
1099
|
+
// Claude: "auto" | "any" | "none" | { type: "tool", name }
|
|
1100
|
+
// Responses: "auto" | "required" | "none" | { type: "function", function: { name } }
|
|
1101
|
+
if (tool_choice !== undefined) {
|
|
1102
|
+
if (tool_choice === 'auto') {
|
|
1103
|
+
result.tool_choice = 'auto';
|
|
1104
|
+
}
|
|
1105
|
+
else if (tool_choice === 'any') {
|
|
1106
|
+
result.tool_choice = 'required';
|
|
1107
|
+
}
|
|
1108
|
+
else if (tool_choice === 'none') {
|
|
1109
|
+
result.tool_choice = 'none';
|
|
1110
|
+
}
|
|
1111
|
+
else if (typeof tool_choice === 'object' && tool_choice.type === 'tool') {
|
|
1112
|
+
const tc = tool_choice;
|
|
1113
|
+
if (tc.name) {
|
|
1114
|
+
result.tool_choice = {
|
|
1115
|
+
type: 'function',
|
|
1116
|
+
function: { name: tc.name },
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return result;
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* 将 claude code 发起的请求转换成对 chat-completions API 的请求
|
|
1125
|
+
* @param body
|
|
1126
|
+
* @param model
|
|
1127
|
+
*/
|
|
1128
|
+
function transformRequestFromClaudeToChatCompletions(body, model) {
|
|
1129
|
+
const { system, messages, max_tokens, temperature, top_p, stop_sequences, tools, tool_choice, thinking } = body;
|
|
1130
|
+
const transformedMessages = [];
|
|
1131
|
+
let systemText = '';
|
|
1132
|
+
// 处理 system 指令
|
|
1133
|
+
if (system) {
|
|
1134
|
+
if (typeof system === 'string') {
|
|
1135
|
+
systemText = system;
|
|
1136
|
+
}
|
|
1137
|
+
else if (Array.isArray(system)) {
|
|
1138
|
+
const textParts = [];
|
|
1139
|
+
for (const block of system) {
|
|
1140
|
+
if (block && block.type === 'text' && typeof block.text === 'string') {
|
|
1141
|
+
textParts.push(block.text);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
systemText = textParts.join('\n\n');
|
|
1145
|
+
}
|
|
1146
|
+
else if (system && typeof system === 'object' && system.type === 'text') {
|
|
1147
|
+
systemText = system.text;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
// 转换 messages
|
|
1151
|
+
if (Array.isArray(messages)) {
|
|
1152
|
+
for (const msg of messages) {
|
|
1153
|
+
if (!msg || typeof msg !== 'object')
|
|
1154
|
+
continue;
|
|
1155
|
+
const transformedMsg = {
|
|
1156
|
+
role: msg.role,
|
|
1157
|
+
};
|
|
1158
|
+
if (typeof msg.content === 'string') {
|
|
1159
|
+
transformedMsg.content = msg.content;
|
|
1160
|
+
}
|
|
1161
|
+
else if (Array.isArray(msg.content)) {
|
|
1162
|
+
const content = [];
|
|
1163
|
+
for (const block of msg.content) {
|
|
1164
|
+
if (!block || typeof block !== 'object')
|
|
1165
|
+
continue;
|
|
1166
|
+
// text → text
|
|
1167
|
+
if (block.type === 'text' && typeof block.text === 'string') {
|
|
1168
|
+
content.push({ type: 'text', text: block.text });
|
|
1169
|
+
}
|
|
1170
|
+
// image → image_url
|
|
1171
|
+
if (block.type === 'image' && block.source) {
|
|
1172
|
+
const source = block.source;
|
|
1173
|
+
if (source.type === 'base64' && source.data && source.media_type) {
|
|
1174
|
+
const dataUrl = `data:${source.media_type};base64,${source.data}`;
|
|
1175
|
+
content.push({
|
|
1176
|
+
type: 'image_url',
|
|
1177
|
+
image_url: { url: dataUrl }
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
else if (source.type === 'url' && source.url) {
|
|
1181
|
+
content.push({
|
|
1182
|
+
type: 'image_url',
|
|
1183
|
+
image_url: { url: source.url }
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
// tool_use → tool_calls
|
|
1188
|
+
if (block.type === 'tool_use') {
|
|
1189
|
+
if (!transformedMsg.tool_calls) {
|
|
1190
|
+
transformedMsg.tool_calls = [];
|
|
1191
|
+
}
|
|
1192
|
+
transformedMsg.tool_calls.push({
|
|
1193
|
+
id: block.id,
|
|
1194
|
+
type: 'function',
|
|
1195
|
+
function: {
|
|
1196
|
+
name: block.name || 'tool',
|
|
1197
|
+
arguments: JSON.stringify(block.input || {})
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
// thinking → thinking(Chat Completions API 可能不支持,但保留)
|
|
1202
|
+
if (block.type === 'thinking') {
|
|
1203
|
+
content.push({
|
|
1204
|
+
type: 'thinking',
|
|
1205
|
+
thinking: block.thinking
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
if (content.length > 0 || !transformedMsg.tool_calls) {
|
|
1210
|
+
transformedMsg.content = content.length > 0 ? content : '';
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
// tool_result → tool 消息
|
|
1214
|
+
if (msg.role === 'tool') {
|
|
1215
|
+
if (typeof msg.content === 'string') {
|
|
1216
|
+
transformedMsg.content = msg.content;
|
|
1217
|
+
}
|
|
1218
|
+
else if (Array.isArray(msg.content)) {
|
|
1219
|
+
const toolContent = [];
|
|
1220
|
+
for (const block of msg.content) {
|
|
1221
|
+
if (block && block.type === 'text' && typeof block.text === 'string') {
|
|
1222
|
+
toolContent.push(block.text);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
transformedMsg.content = toolContent.join('\n');
|
|
1226
|
+
}
|
|
1227
|
+
transformedMsg.tool_call_id = msg.tool_use_id || '';
|
|
1228
|
+
}
|
|
1229
|
+
transformedMessages.push(transformedMsg);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
const result = {
|
|
1233
|
+
model: model || body.model,
|
|
1234
|
+
messages: transformedMessages,
|
|
1235
|
+
};
|
|
1236
|
+
// 处理 system
|
|
1237
|
+
if (systemText) {
|
|
1238
|
+
result.system = systemText;
|
|
1239
|
+
}
|
|
1240
|
+
// 处理 max_tokens
|
|
1241
|
+
if (typeof max_tokens === 'number') {
|
|
1242
|
+
result.max_tokens = max_tokens;
|
|
1243
|
+
}
|
|
1244
|
+
// 处理 temperature
|
|
1245
|
+
if (typeof temperature === 'number') {
|
|
1246
|
+
result.temperature = temperature;
|
|
1247
|
+
}
|
|
1248
|
+
// 处理 top_p
|
|
1249
|
+
if (typeof top_p === 'number') {
|
|
1250
|
+
result.top_p = top_p;
|
|
1251
|
+
}
|
|
1252
|
+
// 处理 stop_sequences -> stop
|
|
1253
|
+
if (Array.isArray(stop_sequences)) {
|
|
1254
|
+
result.stop = stop_sequences;
|
|
1255
|
+
}
|
|
1256
|
+
else if (typeof stop_sequences === 'string') {
|
|
1257
|
+
result.stop = [stop_sequences];
|
|
1258
|
+
}
|
|
1259
|
+
// 转换 tools: Claude 格式 → Chat Completions 格式
|
|
1260
|
+
// Claude: { name, description, input_schema }
|
|
1261
|
+
// Chat Completions: { type: "function", function: { name, description, parameters } }
|
|
1262
|
+
if (Array.isArray(tools)) {
|
|
1263
|
+
result.tools = tools.map((tool) => {
|
|
1264
|
+
if (tool && tool.name) {
|
|
1265
|
+
return {
|
|
1266
|
+
type: 'function',
|
|
1267
|
+
function: {
|
|
1268
|
+
name: tool.name,
|
|
1269
|
+
description: tool.description || '',
|
|
1270
|
+
parameters: tool.input_schema || { type: 'object', properties: {}, required: [] }
|
|
1271
|
+
}
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
return null;
|
|
1275
|
+
}).filter(Boolean);
|
|
1276
|
+
}
|
|
1277
|
+
// 转换 tool_choice
|
|
1278
|
+
// Claude: "auto" | "any" | "none" | { type: "tool", name }
|
|
1279
|
+
// Chat Completions: "auto" | "required" | "none" | { type: "function", function: { name } }
|
|
1280
|
+
if (tool_choice !== undefined) {
|
|
1281
|
+
if (tool_choice === 'auto') {
|
|
1282
|
+
result.tool_choice = 'auto';
|
|
1283
|
+
}
|
|
1284
|
+
else if (tool_choice === 'any') {
|
|
1285
|
+
result.tool_choice = 'required';
|
|
1286
|
+
}
|
|
1287
|
+
else if (tool_choice === 'none') {
|
|
1288
|
+
result.tool_choice = 'none';
|
|
1289
|
+
}
|
|
1290
|
+
else if (typeof tool_choice === 'object' && tool_choice.type === 'tool') {
|
|
1291
|
+
const tc = tool_choice;
|
|
1292
|
+
if (tc.name) {
|
|
1293
|
+
result.tool_choice = {
|
|
1294
|
+
type: 'function',
|
|
1295
|
+
function: { name: tc.name }
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
// 转换 thinking → reasoning
|
|
1301
|
+
if (thinking) {
|
|
1302
|
+
result.reasoning = thinking;
|
|
1303
|
+
}
|
|
1304
|
+
return result;
|
|
1305
|
+
}
|
|
1306
|
+
// ===================== codex 得到 responses 数据 =======================
|
|
1307
|
+
function transformResponseFromChatCompletionsToResponses(response) {
|
|
1308
|
+
if (!response || typeof response !== 'object') {
|
|
1309
|
+
return response;
|
|
1310
|
+
}
|
|
1311
|
+
const choice = Array.isArray(response.choices) && response.choices.length > 0 ? response.choices[0] : null;
|
|
1312
|
+
const output = [];
|
|
1313
|
+
// 转换消息内容
|
|
1314
|
+
if (choice === null || choice === void 0 ? void 0 : choice.message) {
|
|
1315
|
+
const message = choice.message;
|
|
1316
|
+
const messageContent = [];
|
|
1317
|
+
// 处理文本内容
|
|
1318
|
+
if (typeof message.content === 'string') {
|
|
1319
|
+
messageContent.push({
|
|
1320
|
+
type: 'output_text',
|
|
1321
|
+
text: message.content
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
else if (Array.isArray(message.content)) {
|
|
1325
|
+
for (const item of message.content) {
|
|
1326
|
+
if (!item || typeof item !== 'object')
|
|
1327
|
+
continue;
|
|
1328
|
+
if (item.type === 'text' && typeof item.text === 'string') {
|
|
1329
|
+
messageContent.push({
|
|
1330
|
+
type: 'output_text',
|
|
1331
|
+
text: item.text
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
if (item.type === 'image_url' && item.image_url) {
|
|
1335
|
+
messageContent.push({
|
|
1336
|
+
type: 'image',
|
|
1337
|
+
source: {
|
|
1338
|
+
type: 'url',
|
|
1339
|
+
url: item.image_url.url
|
|
1340
|
+
}
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
if (messageContent.length > 0) {
|
|
1346
|
+
output.push({
|
|
1347
|
+
type: 'message',
|
|
1348
|
+
role: 'assistant',
|
|
1349
|
+
content: messageContent
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
// 处理工具调用
|
|
1353
|
+
if (Array.isArray(message.tool_calls)) {
|
|
1354
|
+
for (const toolCall of message.tool_calls) {
|
|
1355
|
+
if (toolCall && toolCall.type === 'function') {
|
|
1356
|
+
output.push({
|
|
1357
|
+
type: 'function_call',
|
|
1358
|
+
id: toolCall.id,
|
|
1359
|
+
name: toolCall.function.name,
|
|
1360
|
+
arguments: toolCall.function.arguments || '{}'
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
// 如果没有内容,添加空消息
|
|
1367
|
+
if (output.length === 0) {
|
|
1368
|
+
output.push({
|
|
1369
|
+
type: 'message',
|
|
1370
|
+
role: 'assistant',
|
|
1371
|
+
content: [{
|
|
1372
|
+
type: 'output_text',
|
|
1373
|
+
text: ''
|
|
1374
|
+
}]
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
// 映射 finish reason 到 status
|
|
1378
|
+
// Chat Completions: "stop" | "length" | "tool_calls" | "content_filter"
|
|
1379
|
+
// Responses: "completed" | "incomplete"
|
|
1380
|
+
let status = 'completed';
|
|
1381
|
+
if ((choice === null || choice === void 0 ? void 0 : choice.finish_reason) === 'length' || (choice === null || choice === void 0 ? void 0 : choice.finish_reason) === 'max_tokens') {
|
|
1382
|
+
status = 'incomplete';
|
|
1383
|
+
}
|
|
1384
|
+
return {
|
|
1385
|
+
id: response.id || `response_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
|
|
1386
|
+
object: 'response',
|
|
1387
|
+
created_at: response.created || Math.floor(Date.now() / 1000),
|
|
1388
|
+
model: response.model,
|
|
1389
|
+
output,
|
|
1390
|
+
status,
|
|
1391
|
+
incomplete_details: status === 'incomplete' ? { reason: 'max_tokens' } : undefined,
|
|
1392
|
+
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
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1395
|
+
function transformResponseFromClaudeToResponses(response) {
|
|
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: response.usage.input_tokens,
|
|
1463
|
+
output_tokens: response.usage.output_tokens,
|
|
1464
|
+
total_tokens: response.usage.total_tokens
|
|
1465
|
+
} : undefined
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
function transformResponseFromGeminiToResponses(response) {
|
|
1469
|
+
var _a;
|
|
1470
|
+
if (!response || typeof response !== 'object') {
|
|
1471
|
+
return response;
|
|
1472
|
+
}
|
|
1473
|
+
const candidate = Array.isArray(response.candidates) && response.candidates.length > 0 ? response.candidates[0] : null;
|
|
1474
|
+
const output = [];
|
|
1475
|
+
// 转换 content parts
|
|
1476
|
+
if ((_a = candidate === null || candidate === void 0 ? void 0 : candidate.content) === null || _a === void 0 ? void 0 : _a.parts) {
|
|
1477
|
+
const messageContent = [];
|
|
1478
|
+
for (const part of candidate.content.parts) {
|
|
1479
|
+
const p = part;
|
|
1480
|
+
if (p.text && typeof p.text === 'string') {
|
|
1481
|
+
messageContent.push({
|
|
1482
|
+
type: 'output_text',
|
|
1483
|
+
text: p.text
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
if (p.inlineData) {
|
|
1487
|
+
messageContent.push({
|
|
1488
|
+
type: 'image',
|
|
1489
|
+
source: {
|
|
1490
|
+
type: 'base64',
|
|
1491
|
+
media_type: p.inlineData.mimeType,
|
|
1492
|
+
data: p.inlineData.data
|
|
1493
|
+
}
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
if (p.functionCall) {
|
|
1497
|
+
output.push({
|
|
1498
|
+
type: 'function_call',
|
|
1499
|
+
id: `call_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
|
|
1500
|
+
name: p.functionCall.name || 'tool',
|
|
1501
|
+
arguments: JSON.stringify(p.functionCall.args || {})
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
if (messageContent.length > 0) {
|
|
1506
|
+
output.push({
|
|
1507
|
+
type: 'message',
|
|
1508
|
+
role: 'assistant',
|
|
1509
|
+
content: messageContent
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
// 如果没有内容,添加空消息
|
|
1514
|
+
if (output.length === 0) {
|
|
1515
|
+
output.push({
|
|
1516
|
+
type: 'message',
|
|
1517
|
+
role: 'assistant',
|
|
1518
|
+
content: [{
|
|
1519
|
+
type: 'output_text',
|
|
1520
|
+
text: ''
|
|
1521
|
+
}]
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
// 映射 finishReason 为 status
|
|
1525
|
+
// Gemini: "STOP" | "MAX_TOKENS" | "SAFETY" | "RECITATION" | "OTHER" | "MALFORMED_FUNCTION_CALL"
|
|
1526
|
+
// Responses: "completed" | "incomplete"
|
|
1527
|
+
let status = 'completed';
|
|
1528
|
+
if ((candidate === null || candidate === void 0 ? void 0 : candidate.finishReason) === 'MAX_TOKENS') {
|
|
1529
|
+
status = 'incomplete';
|
|
1530
|
+
}
|
|
1531
|
+
return {
|
|
1532
|
+
id: `response_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
|
|
1533
|
+
object: 'response',
|
|
1534
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
1535
|
+
model: response.model || 'gemini',
|
|
1536
|
+
output,
|
|
1537
|
+
status,
|
|
1538
|
+
incomplete_details: status === 'incomplete' ? { reason: 'max_tokens' } : undefined,
|
|
1539
|
+
usage: response.usageMetadata ? {
|
|
1540
|
+
input_tokens: response.usageMetadata.promptTokenCount,
|
|
1541
|
+
output_tokens: response.usageMetadata.candidatesTokenCount,
|
|
1542
|
+
total_tokens: response.usageMetadata.totalTokenCount
|
|
1543
|
+
} : undefined
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
// ===================== claude code 得到 claude 数据 =======================
|
|
1547
|
+
function transformResponseFromChatCompletionsToClaude(response) {
|
|
1548
|
+
if (!response || typeof response !== 'object') {
|
|
1549
|
+
return response;
|
|
1550
|
+
}
|
|
1551
|
+
const choice = Array.isArray(response.choices) && response.choices.length > 0 ? response.choices[0] : null;
|
|
1552
|
+
const contentBlocks = [];
|
|
1553
|
+
// 转换消息内容
|
|
1554
|
+
if (choice === null || choice === void 0 ? void 0 : choice.message) {
|
|
1555
|
+
const message = choice.message;
|
|
1556
|
+
if (typeof message.content === 'string') {
|
|
1557
|
+
contentBlocks.push({ type: 'text', text: message.content });
|
|
1558
|
+
}
|
|
1559
|
+
else if (Array.isArray(message.content)) {
|
|
1560
|
+
for (const item of message.content) {
|
|
1561
|
+
if (!item || typeof item !== 'object')
|
|
1562
|
+
continue;
|
|
1563
|
+
if (item.type === 'text' && typeof item.text === 'string') {
|
|
1564
|
+
contentBlocks.push({ type: 'text', text: item.text });
|
|
1565
|
+
}
|
|
1566
|
+
if (item.type === 'image_url' && item.image_url) {
|
|
1567
|
+
const url = item.image_url.url;
|
|
1568
|
+
if (typeof url === 'string') {
|
|
1569
|
+
if (url.startsWith('data:')) {
|
|
1570
|
+
const match = url.match(/^data:([^;]+);base64,(.+)$/);
|
|
1571
|
+
if (match) {
|
|
1572
|
+
contentBlocks.push({
|
|
1573
|
+
type: 'image',
|
|
1574
|
+
source: {
|
|
1575
|
+
type: 'base64',
|
|
1576
|
+
media_type: match[1],
|
|
1577
|
+
data: match[2]
|
|
1578
|
+
}
|
|
1579
|
+
});
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
else {
|
|
1583
|
+
contentBlocks.push({
|
|
1584
|
+
type: 'image',
|
|
1585
|
+
source: {
|
|
1586
|
+
type: 'url',
|
|
1587
|
+
url: url
|
|
1588
|
+
}
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
// 处理工具调用
|
|
1596
|
+
if (Array.isArray(message.tool_calls)) {
|
|
1597
|
+
for (const toolCall of message.tool_calls) {
|
|
1598
|
+
if (toolCall && toolCall.type === 'function') {
|
|
1599
|
+
contentBlocks.push({
|
|
1600
|
+
type: 'tool_use',
|
|
1601
|
+
id: toolCall.id,
|
|
1602
|
+
name: toolCall.function.name,
|
|
1603
|
+
input: JSON.parse(toolCall.function.arguments || '{}')
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
// 如果没有内容,添加空文本
|
|
1610
|
+
if (contentBlocks.length === 0) {
|
|
1611
|
+
contentBlocks.push({ type: 'text', text: '' });
|
|
1612
|
+
}
|
|
1613
|
+
// 转换 usage
|
|
1614
|
+
const usage = response.usage ? {
|
|
1615
|
+
input_tokens: response.usage.prompt_tokens,
|
|
1616
|
+
output_tokens: response.usage.completion_tokens,
|
|
1617
|
+
total_tokens: response.usage.total_tokens,
|
|
1618
|
+
} : undefined;
|
|
1619
|
+
// 映射 finish reason
|
|
1620
|
+
const stopReasonMap = {
|
|
1621
|
+
'stop': 'end_turn',
|
|
1622
|
+
'length': 'max_tokens',
|
|
1623
|
+
'content_filter': 'content_filter',
|
|
1624
|
+
};
|
|
1625
|
+
return {
|
|
1626
|
+
id: response.id,
|
|
1627
|
+
type: 'message',
|
|
1628
|
+
role: 'assistant',
|
|
1629
|
+
model: response.model,
|
|
1630
|
+
content: contentBlocks,
|
|
1631
|
+
stop_reason: stopReasonMap[choice === null || choice === void 0 ? void 0 : choice.finish_reason] || 'end_turn',
|
|
1632
|
+
stop_sequence: null,
|
|
1633
|
+
usage: usage || {
|
|
1634
|
+
input_tokens: 0,
|
|
1635
|
+
output_tokens: 0,
|
|
1636
|
+
cache_read_input_tokens: 0,
|
|
1637
|
+
},
|
|
1638
|
+
};
|
|
1639
|
+
}
|
|
1640
|
+
function transformResponseFromResponsesToClaude(response) {
|
|
1641
|
+
var _a;
|
|
1642
|
+
if (!response || typeof response !== 'object') {
|
|
1643
|
+
return response;
|
|
1644
|
+
}
|
|
1645
|
+
const contentBlocks = [];
|
|
1646
|
+
// 遍历 output 数组
|
|
1647
|
+
if (Array.isArray(response.output)) {
|
|
1648
|
+
for (const outputItem of response.output) {
|
|
1649
|
+
if (outputItem.type === 'message' && Array.isArray(outputItem.content)) {
|
|
1650
|
+
for (const part of outputItem.content) {
|
|
1651
|
+
if (part.type === 'output_text' && typeof part.text === 'string') {
|
|
1652
|
+
contentBlocks.push({ type: 'text', text: part.text });
|
|
1653
|
+
}
|
|
1654
|
+
if (part.type === 'image' && part.source) {
|
|
1655
|
+
contentBlocks.push({ type: 'image', source: part.source });
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
if (outputItem.type === 'function_call') {
|
|
1660
|
+
contentBlocks.push({
|
|
1661
|
+
type: 'tool_use',
|
|
1662
|
+
id: outputItem.id,
|
|
1663
|
+
name: outputItem.name,
|
|
1664
|
+
input: JSON.parse(outputItem.arguments || '{}')
|
|
1665
|
+
});
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
// 如果没有内容,添加空文本
|
|
1670
|
+
if (contentBlocks.length === 0) {
|
|
1671
|
+
contentBlocks.push({ type: 'text', text: '' });
|
|
1672
|
+
}
|
|
1673
|
+
// 转换 usage
|
|
1674
|
+
const usage = response.usage ? {
|
|
1675
|
+
input_tokens: response.usage.input_tokens,
|
|
1676
|
+
output_tokens: response.usage.output_tokens,
|
|
1677
|
+
total_tokens: response.usage.total_tokens,
|
|
1678
|
+
} : undefined;
|
|
1679
|
+
// 转换 stop_reason
|
|
1680
|
+
let stop_reason = 'end_turn';
|
|
1681
|
+
if (response.status === 'incomplete') {
|
|
1682
|
+
stop_reason = ((_a = response.incomplete_details) === null || _a === void 0 ? void 0 : _a.reason) === 'max_tokens' ? 'max_tokens' : 'end_turn';
|
|
1683
|
+
}
|
|
1684
|
+
return {
|
|
1685
|
+
id: response.id,
|
|
1686
|
+
type: 'message',
|
|
1687
|
+
role: 'assistant',
|
|
1688
|
+
model: response.model,
|
|
1689
|
+
content: contentBlocks,
|
|
1690
|
+
stop_reason,
|
|
1691
|
+
stop_sequence: null,
|
|
1692
|
+
usage: usage || {
|
|
1693
|
+
input_tokens: 0,
|
|
1694
|
+
output_tokens: 0,
|
|
1695
|
+
cache_read_input_tokens: 0,
|
|
1696
|
+
},
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
function transformResponseFromGeminiToClaude(response) {
|
|
1700
|
+
var _a;
|
|
1701
|
+
if (!response || typeof response !== 'object') {
|
|
1702
|
+
return response;
|
|
1703
|
+
}
|
|
1704
|
+
const candidate = Array.isArray(response.candidates) && response.candidates.length > 0 ? response.candidates[0] : null;
|
|
1705
|
+
const contentBlocks = [];
|
|
1706
|
+
// 转换 content parts
|
|
1707
|
+
if ((_a = candidate === null || candidate === void 0 ? void 0 : candidate.content) === null || _a === void 0 ? void 0 : _a.parts) {
|
|
1708
|
+
for (const part of candidate.content.parts) {
|
|
1709
|
+
const p = part;
|
|
1710
|
+
if (p.text && typeof p.text === 'string') {
|
|
1711
|
+
contentBlocks.push({ type: 'text', text: p.text });
|
|
1712
|
+
}
|
|
1713
|
+
if (p.inlineData) {
|
|
1714
|
+
contentBlocks.push({
|
|
1715
|
+
type: 'image',
|
|
1716
|
+
source: {
|
|
1717
|
+
type: 'base64',
|
|
1718
|
+
media_type: p.inlineData.mimeType,
|
|
1719
|
+
data: p.inlineData.data
|
|
1720
|
+
}
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
if (p.functionCall) {
|
|
1724
|
+
contentBlocks.push({
|
|
1725
|
+
type: 'tool_use',
|
|
1726
|
+
id: `tool_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
|
|
1727
|
+
name: p.functionCall.name || 'tool',
|
|
1728
|
+
input: p.functionCall.args || {}
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
// 如果没有内容,添加空文本
|
|
1734
|
+
if (contentBlocks.length === 0) {
|
|
1735
|
+
contentBlocks.push({ type: 'text', text: '' });
|
|
1736
|
+
}
|
|
1737
|
+
// 转换 usage
|
|
1738
|
+
const usage = response.usageMetadata ? {
|
|
1739
|
+
input_tokens: response.usageMetadata.promptTokenCount,
|
|
1740
|
+
output_tokens: response.usageMetadata.candidatesTokenCount,
|
|
1741
|
+
total_tokens: response.usageMetadata.totalTokenCount,
|
|
1742
|
+
cache_read_input_tokens: response.usageMetadata.cachedContentTokenCount,
|
|
1743
|
+
} : undefined;
|
|
1744
|
+
// 映射 finish reason
|
|
1745
|
+
const stopReasonMap = {
|
|
1746
|
+
'STOP': 'end_turn',
|
|
1747
|
+
'MAX_TOKENS': 'max_tokens',
|
|
1748
|
+
'SAFETY': 'content_filter',
|
|
1749
|
+
'RECITATION': 'content_filter',
|
|
1750
|
+
};
|
|
1751
|
+
return {
|
|
1752
|
+
id: `msg_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
|
|
1753
|
+
type: 'message',
|
|
1754
|
+
role: 'assistant',
|
|
1755
|
+
model: 'gemini',
|
|
1756
|
+
content: contentBlocks,
|
|
1757
|
+
stop_reason: stopReasonMap[candidate === null || candidate === void 0 ? void 0 : candidate.finishReason] || 'end_turn',
|
|
1758
|
+
stop_sequence: null,
|
|
1759
|
+
usage: usage || {
|
|
1760
|
+
input_tokens: 0,
|
|
1761
|
+
output_tokens: 0,
|
|
1762
|
+
cache_read_input_tokens: 0,
|
|
1763
|
+
},
|
|
1764
|
+
};
|
|
1765
|
+
}
|