aiexecode 1.0.90 → 1.0.92
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.
Potentially problematic release.
This version of aiexecode might be problematic. Click here for more details.
- package/README.md +1 -0
- package/index.js +13 -11
- package/mcp-agent-lib/init.sh +3 -0
- package/mcp-agent-lib/package-lock.json +14 -1
- package/mcp-agent-lib/package.json +4 -6
- package/mcp-agent-lib/sampleFastMCPClient/client.py +25 -0
- package/mcp-agent-lib/sampleFastMCPClient/run.sh +3 -0
- package/mcp-agent-lib/sampleFastMCPServer/run.sh +3 -0
- package/mcp-agent-lib/sampleFastMCPServer/server.py +12 -0
- package/mcp-agent-lib/sampleFastMCPServerElicitationRequest/run.sh +3 -0
- package/mcp-agent-lib/sampleFastMCPServerElicitationRequest/server.py +43 -0
- package/mcp-agent-lib/sampleFastMCPServerRootsRequest/server.py +63 -0
- package/mcp-agent-lib/sampleMCPHost/index.js +182 -63
- package/mcp-agent-lib/sampleMCPHost/mcp_config.json +7 -1
- package/mcp-agent-lib/sampleMCPHostFeatures/elicitation.js +151 -0
- package/mcp-agent-lib/sampleMCPHostFeatures/index.js +166 -0
- package/mcp-agent-lib/sampleMCPHostFeatures/roots.js +197 -0
- package/mcp-agent-lib/src/mcp_client.js +129 -67
- package/mcp-agent-lib/src/mcp_message_logger.js +516 -0
- package/package.json +3 -1
- package/payload_viewer/out/404/index.html +1 -1
- package/payload_viewer/out/404.html +1 -1
- package/payload_viewer/out/index.html +1 -1
- package/payload_viewer/out/index.txt +1 -1
- package/src/LLMClient/client.js +992 -0
- package/src/LLMClient/converters/input-normalizer.js +238 -0
- package/src/LLMClient/converters/responses-to-claude.js +454 -0
- package/src/LLMClient/converters/responses-to-gemini.js +648 -0
- package/src/LLMClient/converters/responses-to-ollama.js +348 -0
- package/src/LLMClient/errors.js +372 -0
- package/src/LLMClient/index.js +31 -0
- package/src/commands/apikey.js +10 -22
- package/src/commands/model.js +28 -28
- package/src/commands/reasoning_effort.js +9 -23
- package/src/config/ai_models.js +212 -0
- package/src/config/feature_flags.js +1 -1
- package/src/frontend/App.js +5 -10
- package/src/frontend/components/CurrentModelView.js +0 -33
- package/src/frontend/components/Footer.js +3 -3
- package/src/frontend/components/ModelListView.js +30 -87
- package/src/frontend/components/ModelUpdatedView.js +7 -142
- package/src/frontend/components/SetupWizard.js +37 -32
- package/src/system/ai_request.js +57 -42
- package/src/util/config.js +26 -4
- package/src/util/setup_wizard.js +1 -6
- package/mcp-agent-lib/.claude/settings.local.json +0 -9
- package/src/config/openai_models.js +0 -152
- /package/payload_viewer/out/_next/static/{w4dMVYalgk7djrLxRxWiE → d0-fu2rgYnshgGFPxr1CR}/_buildManifest.js +0 -0
- /package/payload_viewer/out/_next/static/{w4dMVYalgk7djrLxRxWiE → d0-fu2rgYnshgGFPxr1CR}/_clientMiddlewareManifest.json +0 -0
- /package/payload_viewer/out/_next/static/{w4dMVYalgk7djrLxRxWiE → d0-fu2rgYnshgGFPxr1CR}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert Responses API format to Gemini format
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Remove markdown code block wrapper from text
|
|
7
|
+
* Handles all possible cases mechanically without regex
|
|
8
|
+
* @param {string} text - Raw text possibly wrapped in markdown code block
|
|
9
|
+
* @returns {string} Cleaned text
|
|
10
|
+
*/
|
|
11
|
+
function removeMarkdownCodeBlock(text) {
|
|
12
|
+
// Case 1: null, undefined, empty string, non-string
|
|
13
|
+
if (!text || typeof text !== 'string') {
|
|
14
|
+
return text;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let result = text;
|
|
18
|
+
let startIndex = 0;
|
|
19
|
+
let endIndex = result.length;
|
|
20
|
+
|
|
21
|
+
// ============================================
|
|
22
|
+
// PHASE 1: Process opening ``` marker
|
|
23
|
+
// ============================================
|
|
24
|
+
|
|
25
|
+
// Case 2: Text doesn't start with ``` at all
|
|
26
|
+
if (!result.startsWith('```')) {
|
|
27
|
+
// No opening marker - return as is (trimmed)
|
|
28
|
+
return result.trim();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Case 3: Text starts with ``` - remove it
|
|
32
|
+
startIndex = 3;
|
|
33
|
+
|
|
34
|
+
// Now check what comes after ```
|
|
35
|
+
// Possible cases:
|
|
36
|
+
// - ```\n... (no language identifier)
|
|
37
|
+
// - ```json\n... (with language identifier)
|
|
38
|
+
// - ```json ... (language identifier with spaces)
|
|
39
|
+
// - ``` json\n... (spaces before language identifier)
|
|
40
|
+
// - ```{... (no newline, content starts immediately - edge case)
|
|
41
|
+
|
|
42
|
+
// Skip any spaces immediately after ```
|
|
43
|
+
while (startIndex < endIndex && result[startIndex] === ' ') {
|
|
44
|
+
startIndex++;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Case 4: After ``` and optional spaces, look for newline
|
|
48
|
+
const firstNewlinePos = result.indexOf('\n', startIndex);
|
|
49
|
+
|
|
50
|
+
if (firstNewlinePos === -1) {
|
|
51
|
+
// Case 5: No newline found after ``` - entire rest is content
|
|
52
|
+
// Example: ```{"key":"value"}``` or ```{"key":"value"}
|
|
53
|
+
// Content starts right after ``` (and any spaces we skipped)
|
|
54
|
+
// Don't advance startIndex further - keep content starting position
|
|
55
|
+
} else {
|
|
56
|
+
// Case 6: Newline exists - check what's between ``` and \n
|
|
57
|
+
const betweenBackticksAndNewline = result.substring(startIndex, firstNewlinePos);
|
|
58
|
+
|
|
59
|
+
// Trim to check if it's a language identifier
|
|
60
|
+
const trimmed = betweenBackticksAndNewline.trim();
|
|
61
|
+
|
|
62
|
+
if (trimmed.length === 0) {
|
|
63
|
+
// Case 7: Nothing between ``` and \n (or only whitespace)
|
|
64
|
+
// Example: ```\n... or ``` \n...
|
|
65
|
+
// Content starts after the newline
|
|
66
|
+
startIndex = firstNewlinePos + 1;
|
|
67
|
+
} else {
|
|
68
|
+
// Case 8: Something exists between ``` and \n
|
|
69
|
+
// Check if it looks like a language identifier (alphanumeric, underscore, dash only)
|
|
70
|
+
let isLanguageIdentifier = true;
|
|
71
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
72
|
+
const char = trimmed[i];
|
|
73
|
+
const isValid = (char >= 'a' && char <= 'z') ||
|
|
74
|
+
(char >= 'A' && char <= 'Z') ||
|
|
75
|
+
(char >= '0' && char <= '9') ||
|
|
76
|
+
char === '_' || char === '-';
|
|
77
|
+
if (!isValid) {
|
|
78
|
+
isLanguageIdentifier = false;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (isLanguageIdentifier) {
|
|
84
|
+
// Case 9: It's a language identifier like "json", "javascript", etc.
|
|
85
|
+
// Skip it and the newline - content starts after newline
|
|
86
|
+
startIndex = firstNewlinePos + 1;
|
|
87
|
+
} else {
|
|
88
|
+
// Case 10: It's actual content (contains special chars like {, [, etc.)
|
|
89
|
+
// Example: ```{"key": "value"}\n...
|
|
90
|
+
// Keep this content - don't skip anything
|
|
91
|
+
// startIndex already points to start of this content
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ============================================
|
|
97
|
+
// PHASE 2: Process closing ``` marker
|
|
98
|
+
// ============================================
|
|
99
|
+
|
|
100
|
+
// Work backwards from end to find closing ```
|
|
101
|
+
|
|
102
|
+
// Case 11: Find where actual content ends (before any trailing ``` and whitespace)
|
|
103
|
+
|
|
104
|
+
// Step 1: Skip trailing whitespace from the end
|
|
105
|
+
let checkPos = endIndex - 1;
|
|
106
|
+
while (checkPos >= startIndex && (result[checkPos] === ' ' || result[checkPos] === '\t' ||
|
|
107
|
+
result[checkPos] === '\n' || result[checkPos] === '\r')) {
|
|
108
|
+
checkPos--;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Step 2: Check if we have ``` at this position (working backwards)
|
|
112
|
+
if (checkPos >= startIndex + 2 &&
|
|
113
|
+
result[checkPos] === '`' &&
|
|
114
|
+
result[checkPos - 1] === '`' &&
|
|
115
|
+
result[checkPos - 2] === '`') {
|
|
116
|
+
// Case 12: Found closing ``` marker
|
|
117
|
+
// Move back before the ```
|
|
118
|
+
endIndex = checkPos - 2;
|
|
119
|
+
|
|
120
|
+
// Also trim any whitespace before the ```
|
|
121
|
+
while (endIndex > startIndex && (result[endIndex - 1] === ' ' || result[endIndex - 1] === '\t' ||
|
|
122
|
+
result[endIndex - 1] === '\n' || result[endIndex - 1] === '\r')) {
|
|
123
|
+
endIndex--;
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
// Case 13: No closing ``` found
|
|
127
|
+
// Use position after trimming trailing whitespace
|
|
128
|
+
// Add 1 because checkPos is the last non-whitespace character's index
|
|
129
|
+
endIndex = checkPos + 1;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ============================================
|
|
133
|
+
// PHASE 3: Extract and return cleaned content
|
|
134
|
+
// ============================================
|
|
135
|
+
|
|
136
|
+
// Case 14: Extract the content between processed boundaries
|
|
137
|
+
if (startIndex >= endIndex) {
|
|
138
|
+
// Case 15: Nothing left after processing (empty content between markers)
|
|
139
|
+
return '';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
result = result.substring(startIndex, endIndex);
|
|
143
|
+
|
|
144
|
+
// Case 16: Final trim to remove any remaining edge whitespace
|
|
145
|
+
return result.trim();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Remove Gemini-incompatible fields from JSON Schema
|
|
150
|
+
* @param {Object} schema - JSON Schema object
|
|
151
|
+
* @returns {Object} Cleaned schema
|
|
152
|
+
*/
|
|
153
|
+
function cleanSchemaForGemini(schema) {
|
|
154
|
+
if (!schema || typeof schema !== 'object') {
|
|
155
|
+
return schema;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Deep clone to avoid mutating original
|
|
159
|
+
const cleaned = JSON.parse(JSON.stringify(schema));
|
|
160
|
+
|
|
161
|
+
// Recursive function to clean schema objects
|
|
162
|
+
function cleanObject(obj) {
|
|
163
|
+
if (!obj || typeof obj !== 'object') {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Remove Gemini-incompatible fields
|
|
168
|
+
delete obj.additionalProperties;
|
|
169
|
+
delete obj.$schema;
|
|
170
|
+
delete obj.$id;
|
|
171
|
+
delete obj.$ref;
|
|
172
|
+
delete obj.definitions;
|
|
173
|
+
delete obj.$defs;
|
|
174
|
+
|
|
175
|
+
// Recursively clean nested objects
|
|
176
|
+
for (const key in obj) {
|
|
177
|
+
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
178
|
+
if (Array.isArray(obj[key])) {
|
|
179
|
+
obj[key].forEach(item => cleanObject(item));
|
|
180
|
+
} else {
|
|
181
|
+
cleanObject(obj[key]);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
cleanObject(cleaned);
|
|
188
|
+
return cleaned;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Convert Responses API request to Gemini format
|
|
193
|
+
* @param {Object} responsesRequest - Responses API format request
|
|
194
|
+
* @returns {Object} Gemini format request
|
|
195
|
+
*/
|
|
196
|
+
export function convertResponsesRequestToGeminiFormat(responsesRequest) {
|
|
197
|
+
const geminiRequest = {
|
|
198
|
+
contents: []
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// Convert input to contents
|
|
202
|
+
if (typeof responsesRequest.input === 'string') {
|
|
203
|
+
// Simple string input
|
|
204
|
+
geminiRequest.contents.push({
|
|
205
|
+
role: 'user',
|
|
206
|
+
parts: [{ text: responsesRequest.input }]
|
|
207
|
+
});
|
|
208
|
+
} else if (Array.isArray(responsesRequest.input)) {
|
|
209
|
+
// Array input - could be messages or output items
|
|
210
|
+
for (const item of responsesRequest.input) {
|
|
211
|
+
// Handle output items (no role, has type)
|
|
212
|
+
if (!item.role && item.type) {
|
|
213
|
+
if (item.type === 'message') {
|
|
214
|
+
// Message item from output
|
|
215
|
+
const parts = [];
|
|
216
|
+
if (item.content && Array.isArray(item.content)) {
|
|
217
|
+
for (const contentBlock of item.content) {
|
|
218
|
+
if (contentBlock.type === 'output_text' && contentBlock.text) {
|
|
219
|
+
parts.push({ text: contentBlock.text });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (parts.length > 0) {
|
|
224
|
+
geminiRequest.contents.push({
|
|
225
|
+
role: 'model',
|
|
226
|
+
parts: parts
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
} else if (item.type === 'function_call') {
|
|
230
|
+
// Function call from output - convert to Gemini functionCall
|
|
231
|
+
geminiRequest.contents.push({
|
|
232
|
+
role: 'model',
|
|
233
|
+
parts: [
|
|
234
|
+
{
|
|
235
|
+
functionCall: {
|
|
236
|
+
name: item.name,
|
|
237
|
+
args: JSON.parse(item.arguments || '{}')
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
});
|
|
242
|
+
} else if (item.type === 'function_call_output') {
|
|
243
|
+
// Function call output - convert to functionResponse
|
|
244
|
+
let response;
|
|
245
|
+
if (typeof item.output === 'string') {
|
|
246
|
+
try {
|
|
247
|
+
// Try to parse as JSON
|
|
248
|
+
response = JSON.parse(item.output);
|
|
249
|
+
} catch {
|
|
250
|
+
// If not valid JSON, wrap plain text in object
|
|
251
|
+
response = { result: item.output };
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
response = item.output;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
geminiRequest.contents.push({
|
|
258
|
+
role: 'function',
|
|
259
|
+
parts: [
|
|
260
|
+
{
|
|
261
|
+
functionResponse: {
|
|
262
|
+
name: geminiRequest.contents[geminiRequest.contents.length - 1]?.parts?.[0]?.functionCall?.name || 'unknown',
|
|
263
|
+
response: response
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
]
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
// Skip other types like 'reasoning'
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (item.role && item.content) {
|
|
274
|
+
// Message format
|
|
275
|
+
// Handle content that might be an array (OpenAI Responses API format)
|
|
276
|
+
const content = Array.isArray(item.content)
|
|
277
|
+
? item.content.map(c => c.type === 'input_text' || c.type === 'text' ? c.text : c).filter(Boolean).join('\n')
|
|
278
|
+
: item.content;
|
|
279
|
+
|
|
280
|
+
if (item.role === 'system') {
|
|
281
|
+
// System messages go to systemInstruction in Gemini
|
|
282
|
+
geminiRequest.systemInstruction = {
|
|
283
|
+
parts: [{ text: content }]
|
|
284
|
+
};
|
|
285
|
+
} else if (item.role === 'tool') {
|
|
286
|
+
// Tool result
|
|
287
|
+
const lastContent = geminiRequest.contents[geminiRequest.contents.length - 1];
|
|
288
|
+
if (lastContent && lastContent.role === 'model') {
|
|
289
|
+
// Add function response
|
|
290
|
+
let response;
|
|
291
|
+
if (typeof item.content === 'string') {
|
|
292
|
+
try {
|
|
293
|
+
// Try to parse as JSON
|
|
294
|
+
response = JSON.parse(item.content);
|
|
295
|
+
} catch {
|
|
296
|
+
// If not valid JSON, wrap plain text in object
|
|
297
|
+
response = { result: item.content };
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
response = item.content;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
geminiRequest.contents.push({
|
|
304
|
+
role: 'function',
|
|
305
|
+
parts: [
|
|
306
|
+
{
|
|
307
|
+
functionResponse: {
|
|
308
|
+
name: item.name,
|
|
309
|
+
response: response
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
]
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
} else if (item.role === 'assistant' && Array.isArray(item.content)) {
|
|
316
|
+
// Assistant with output array (might contain function_call items)
|
|
317
|
+
const parts = [];
|
|
318
|
+
|
|
319
|
+
for (const outputItem of item.content) {
|
|
320
|
+
if (outputItem.type === 'message' && outputItem.content) {
|
|
321
|
+
// Message item - extract text content
|
|
322
|
+
for (const contentBlock of outputItem.content) {
|
|
323
|
+
if (contentBlock.type === 'output_text' && contentBlock.text) {
|
|
324
|
+
parts.push({ text: contentBlock.text });
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
} else if (outputItem.type === 'function_call') {
|
|
328
|
+
// Function call - convert to Gemini functionCall
|
|
329
|
+
parts.push({
|
|
330
|
+
functionCall: {
|
|
331
|
+
name: outputItem.name,
|
|
332
|
+
args: JSON.parse(outputItem.arguments || '{}')
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Add message only if there's content
|
|
339
|
+
if (parts.length > 0) {
|
|
340
|
+
geminiRequest.contents.push({
|
|
341
|
+
role: 'model',
|
|
342
|
+
parts: parts
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
} else if (item.role === 'assistant') {
|
|
346
|
+
// Assistant message (simple text)
|
|
347
|
+
const content = Array.isArray(item.content)
|
|
348
|
+
? item.content.map(c => c.type === 'input_text' || c.type === 'text' ? c.text : c).filter(Boolean).join('\n')
|
|
349
|
+
: item.content;
|
|
350
|
+
geminiRequest.contents.push({
|
|
351
|
+
role: 'model',
|
|
352
|
+
parts: [{ text: content }]
|
|
353
|
+
});
|
|
354
|
+
} else {
|
|
355
|
+
// User message
|
|
356
|
+
geminiRequest.contents.push({
|
|
357
|
+
role: 'user',
|
|
358
|
+
parts: [{ text: content }]
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Handle instructions (system message)
|
|
366
|
+
if (responsesRequest.instructions) {
|
|
367
|
+
geminiRequest.systemInstruction = {
|
|
368
|
+
parts: [{ text: responsesRequest.instructions }]
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Convert tools from Responses API format to Gemini format
|
|
373
|
+
// Responses API: { type: 'custom', name, description }
|
|
374
|
+
// Gemini: { functionDeclarations: [{ name, description, parameters }] }
|
|
375
|
+
if (responsesRequest.tools && Array.isArray(responsesRequest.tools)) {
|
|
376
|
+
geminiRequest.tools = [
|
|
377
|
+
{
|
|
378
|
+
functionDeclarations: responsesRequest.tools.map(tool => {
|
|
379
|
+
let parameters;
|
|
380
|
+
|
|
381
|
+
if (tool.type === 'function' && tool.function) {
|
|
382
|
+
// Chat Completions format with nested function object
|
|
383
|
+
parameters = tool.function.parameters || {
|
|
384
|
+
type: 'object',
|
|
385
|
+
properties: {}
|
|
386
|
+
};
|
|
387
|
+
} else if (tool.type === 'function' && !tool.function) {
|
|
388
|
+
// Chat Completions format without nested function object
|
|
389
|
+
parameters = tool.parameters || {
|
|
390
|
+
type: 'object',
|
|
391
|
+
properties: {}
|
|
392
|
+
};
|
|
393
|
+
} else if (tool.type === 'custom') {
|
|
394
|
+
// Responses API format
|
|
395
|
+
parameters = tool.input_schema || {
|
|
396
|
+
type: 'object',
|
|
397
|
+
properties: {}
|
|
398
|
+
};
|
|
399
|
+
} else if (tool.name && tool.description) {
|
|
400
|
+
// Already in Gemini format (name, description, parameters)
|
|
401
|
+
parameters = tool.parameters || {
|
|
402
|
+
type: 'object',
|
|
403
|
+
properties: {}
|
|
404
|
+
};
|
|
405
|
+
} else {
|
|
406
|
+
// Fallback: extract name, description, parameters
|
|
407
|
+
parameters = tool.parameters || tool.input_schema || {
|
|
408
|
+
type: 'object',
|
|
409
|
+
properties: {}
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Clean parameters for Gemini compatibility
|
|
414
|
+
const cleanedParameters = cleanSchemaForGemini(parameters);
|
|
415
|
+
|
|
416
|
+
// Build function declaration
|
|
417
|
+
if (tool.type === 'function' && tool.function) {
|
|
418
|
+
return {
|
|
419
|
+
name: tool.function.name,
|
|
420
|
+
description: tool.function.description || `Function: ${tool.function.name}`,
|
|
421
|
+
parameters: cleanedParameters
|
|
422
|
+
};
|
|
423
|
+
} else if (tool.type === 'function' && !tool.function) {
|
|
424
|
+
return {
|
|
425
|
+
name: tool.name,
|
|
426
|
+
description: tool.description || `Function: ${tool.name}`,
|
|
427
|
+
parameters: cleanedParameters
|
|
428
|
+
};
|
|
429
|
+
} else if (tool.type === 'custom') {
|
|
430
|
+
return {
|
|
431
|
+
name: tool.name,
|
|
432
|
+
description: tool.description || `Tool: ${tool.name}`,
|
|
433
|
+
parameters: cleanedParameters
|
|
434
|
+
};
|
|
435
|
+
} else if (tool.name && tool.description) {
|
|
436
|
+
return {
|
|
437
|
+
name: tool.name,
|
|
438
|
+
description: tool.description,
|
|
439
|
+
parameters: cleanedParameters
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return {
|
|
444
|
+
name: tool.name || 'unknown',
|
|
445
|
+
description: tool.description || 'No description',
|
|
446
|
+
parameters: cleanedParameters
|
|
447
|
+
};
|
|
448
|
+
})
|
|
449
|
+
}
|
|
450
|
+
];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Generation config
|
|
454
|
+
geminiRequest.generationConfig = {};
|
|
455
|
+
|
|
456
|
+
// Max output tokens
|
|
457
|
+
if (responsesRequest.max_output_tokens !== undefined) {
|
|
458
|
+
geminiRequest.generationConfig.maxOutputTokens = responsesRequest.max_output_tokens;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Temperature (Gemini supports this)
|
|
462
|
+
if (responsesRequest.temperature !== undefined) {
|
|
463
|
+
geminiRequest.generationConfig.temperature = responsesRequest.temperature;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Top-p (Gemini supports this)
|
|
467
|
+
if (responsesRequest.top_p !== undefined) {
|
|
468
|
+
geminiRequest.generationConfig.topP = responsesRequest.top_p;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Remove empty generationConfig
|
|
472
|
+
if (Object.keys(geminiRequest.generationConfig).length === 0) {
|
|
473
|
+
delete geminiRequest.generationConfig;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Tool choice (Responses API to Gemini)
|
|
477
|
+
// Map tool_choice to Gemini's function_calling_config
|
|
478
|
+
if (responsesRequest.tool_choice !== undefined && geminiRequest.tools) {
|
|
479
|
+
geminiRequest.toolConfig = {
|
|
480
|
+
function_calling_config: {}
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
if (typeof responsesRequest.tool_choice === 'string') {
|
|
484
|
+
if (responsesRequest.tool_choice === 'none') {
|
|
485
|
+
// NONE mode: prohibit function calls
|
|
486
|
+
geminiRequest.toolConfig.function_calling_config.mode = 'NONE';
|
|
487
|
+
} else if (responsesRequest.tool_choice === 'required') {
|
|
488
|
+
// ANY mode: force function call
|
|
489
|
+
geminiRequest.toolConfig.function_calling_config.mode = 'ANY';
|
|
490
|
+
} else if (responsesRequest.tool_choice === 'auto') {
|
|
491
|
+
// AUTO mode: model decides (default, can be omitted)
|
|
492
|
+
geminiRequest.toolConfig.function_calling_config.mode = 'AUTO';
|
|
493
|
+
}
|
|
494
|
+
} else if (responsesRequest.tool_choice?.type === 'function' || responsesRequest.tool_choice?.type === 'custom') {
|
|
495
|
+
// Specific tool - use ANY mode with allowed_function_names
|
|
496
|
+
const toolName = responsesRequest.tool_choice.function?.name || responsesRequest.tool_choice.name;
|
|
497
|
+
geminiRequest.toolConfig.function_calling_config.mode = 'ANY';
|
|
498
|
+
geminiRequest.toolConfig.function_calling_config.allowed_function_names = [toolName];
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return geminiRequest;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Convert Gemini response to Responses API format
|
|
507
|
+
* @param {Object} geminiResponse - Gemini format response
|
|
508
|
+
* @param {string} model - Model name
|
|
509
|
+
* @param {Object} originalRequest - Original request for context
|
|
510
|
+
* @returns {Object} Responses API format response
|
|
511
|
+
*/
|
|
512
|
+
export function convertGeminiResponseToResponsesFormat(geminiResponse, model = 'gemini-2.5-flash', originalRequest = {}) {
|
|
513
|
+
const output = [];
|
|
514
|
+
let outputText = '';
|
|
515
|
+
|
|
516
|
+
// Process candidates
|
|
517
|
+
if (geminiResponse.candidates && geminiResponse.candidates.length > 0) {
|
|
518
|
+
const candidate = geminiResponse.candidates[0];
|
|
519
|
+
|
|
520
|
+
if (candidate.content && candidate.content.parts) {
|
|
521
|
+
// First pass: collect raw text
|
|
522
|
+
for (const part of candidate.content.parts) {
|
|
523
|
+
if (part.text) {
|
|
524
|
+
outputText += part.text;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Clean up markdown code blocks from output text (Gemini often wraps JSON in ```json...```)
|
|
529
|
+
let cleanedText = outputText;
|
|
530
|
+
if (originalRequest.text?.format?.type === 'json_schema') {
|
|
531
|
+
cleanedText = removeMarkdownCodeBlock(outputText);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Second pass: build message content with cleaned text
|
|
535
|
+
const messageContent = [];
|
|
536
|
+
let hasText = false;
|
|
537
|
+
|
|
538
|
+
for (const part of candidate.content.parts) {
|
|
539
|
+
if (part.text && !hasText) {
|
|
540
|
+
// Add cleaned text as a single content block (only once)
|
|
541
|
+
messageContent.push({
|
|
542
|
+
type: 'output_text',
|
|
543
|
+
text: cleanedText,
|
|
544
|
+
annotations: []
|
|
545
|
+
});
|
|
546
|
+
hasText = true;
|
|
547
|
+
} else if (part.functionCall) {
|
|
548
|
+
// Function call - add as separate function_call item
|
|
549
|
+
const callId = `call_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
550
|
+
output.push({
|
|
551
|
+
id: `fc_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
552
|
+
type: 'function_call',
|
|
553
|
+
status: 'completed',
|
|
554
|
+
arguments: JSON.stringify(part.functionCall.args || {}),
|
|
555
|
+
call_id: callId,
|
|
556
|
+
name: part.functionCall.name
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// Add message with text content if any
|
|
562
|
+
if (messageContent.length > 0) {
|
|
563
|
+
output.push({
|
|
564
|
+
id: `msg_${Date.now()}`,
|
|
565
|
+
type: 'message',
|
|
566
|
+
status: 'completed',
|
|
567
|
+
role: 'assistant',
|
|
568
|
+
content: messageContent
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// If no output items, create empty message
|
|
575
|
+
if (output.length === 0) {
|
|
576
|
+
output.push({
|
|
577
|
+
id: `msg_${Date.now()}`,
|
|
578
|
+
type: 'message',
|
|
579
|
+
status: 'completed',
|
|
580
|
+
role: 'assistant',
|
|
581
|
+
content: []
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Use cleaned text for output_text field
|
|
586
|
+
let cleanedOutputText = outputText;
|
|
587
|
+
if (originalRequest.text?.format?.type === 'json_schema') {
|
|
588
|
+
cleanedOutputText = removeMarkdownCodeBlock(outputText);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// Build Responses API response with ALL required fields
|
|
592
|
+
const responsesResponse = {
|
|
593
|
+
id: `resp_${Date.now()}`,
|
|
594
|
+
object: 'response',
|
|
595
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
596
|
+
status: 'completed',
|
|
597
|
+
background: false,
|
|
598
|
+
billing: {
|
|
599
|
+
payer: 'developer'
|
|
600
|
+
},
|
|
601
|
+
error: null,
|
|
602
|
+
incomplete_details: null,
|
|
603
|
+
instructions: originalRequest.instructions || null,
|
|
604
|
+
max_output_tokens: originalRequest.max_output_tokens || null,
|
|
605
|
+
max_tool_calls: null,
|
|
606
|
+
model: model,
|
|
607
|
+
output: output,
|
|
608
|
+
parallel_tool_calls: false,
|
|
609
|
+
previous_response_id: null,
|
|
610
|
+
prompt_cache_key: null,
|
|
611
|
+
prompt_cache_retention: null,
|
|
612
|
+
reasoning: {
|
|
613
|
+
effort: originalRequest.reasoning?.effort || null,
|
|
614
|
+
summary: originalRequest.reasoning?.summary || null
|
|
615
|
+
},
|
|
616
|
+
safety_identifier: null,
|
|
617
|
+
service_tier: 'default',
|
|
618
|
+
store: originalRequest.store !== undefined ? originalRequest.store : true,
|
|
619
|
+
temperature: originalRequest.temperature !== undefined ? originalRequest.temperature : 1,
|
|
620
|
+
text: originalRequest.text || {
|
|
621
|
+
format: {
|
|
622
|
+
type: 'text'
|
|
623
|
+
},
|
|
624
|
+
verbosity: 'medium'
|
|
625
|
+
},
|
|
626
|
+
tool_choice: originalRequest.tool_choice || 'auto',
|
|
627
|
+
tools: originalRequest.tools || [],
|
|
628
|
+
top_logprobs: 0,
|
|
629
|
+
top_p: originalRequest.top_p !== undefined ? originalRequest.top_p : 1,
|
|
630
|
+
truncation: 'disabled',
|
|
631
|
+
usage: {
|
|
632
|
+
input_tokens: geminiResponse.usageMetadata?.promptTokenCount || 0,
|
|
633
|
+
input_tokens_details: {
|
|
634
|
+
cached_tokens: 0
|
|
635
|
+
},
|
|
636
|
+
output_tokens: geminiResponse.usageMetadata?.candidatesTokenCount || 0,
|
|
637
|
+
output_tokens_details: {
|
|
638
|
+
reasoning_tokens: 0
|
|
639
|
+
},
|
|
640
|
+
total_tokens: geminiResponse.usageMetadata?.totalTokenCount || 0
|
|
641
|
+
},
|
|
642
|
+
user: null,
|
|
643
|
+
metadata: {},
|
|
644
|
+
output_text: cleanedOutputText
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
return responsesResponse;
|
|
648
|
+
}
|