@aituber-onair/chat 0.35.0 → 0.37.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.ja.md +6 -6
- package/README.md +7 -8
- package/dist/cjs/constants/claude.d.ts +1 -0
- package/dist/cjs/constants/claude.d.ts.map +1 -1
- package/dist/cjs/constants/claude.js +3 -1
- package/dist/cjs/constants/claude.js.map +1 -1
- package/dist/cjs/constants/gemini.d.ts +0 -7
- package/dist/cjs/constants/gemini.d.ts.map +1 -1
- package/dist/cjs/constants/gemini.js +1 -10
- package/dist/cjs/constants/gemini.js.map +1 -1
- package/dist/cjs/services/providers/claude/ClaudeChatService.d.ts +0 -24
- package/dist/cjs/services/providers/claude/ClaudeChatService.d.ts.map +1 -1
- package/dist/cjs/services/providers/claude/ClaudeChatService.js +3 -113
- package/dist/cjs/services/providers/claude/ClaudeChatService.js.map +1 -1
- package/dist/cjs/services/providers/claude/ClaudeChatServiceProvider.d.ts.map +1 -1
- package/dist/cjs/services/providers/claude/ClaudeChatServiceProvider.js +1 -0
- package/dist/cjs/services/providers/claude/ClaudeChatServiceProvider.js.map +1 -1
- package/dist/cjs/services/providers/claude/claudeMessageConverter.d.ts +6 -0
- package/dist/cjs/services/providers/claude/claudeMessageConverter.d.ts.map +1 -0
- package/dist/cjs/services/providers/claude/claudeMessageConverter.js +95 -0
- package/dist/cjs/services/providers/claude/claudeMessageConverter.js.map +1 -0
- package/dist/cjs/services/providers/gemini/GeminiChatService.d.ts +0 -21
- package/dist/cjs/services/providers/gemini/GeminiChatService.d.ts.map +1 -1
- package/dist/cjs/services/providers/gemini/GeminiChatService.js +14 -254
- package/dist/cjs/services/providers/gemini/GeminiChatService.js.map +1 -1
- package/dist/cjs/services/providers/gemini/geminiMessageConverter.d.ts +17 -0
- package/dist/cjs/services/providers/gemini/geminiMessageConverter.d.ts.map +1 -0
- package/dist/cjs/services/providers/gemini/geminiMessageConverter.js +183 -0
- package/dist/cjs/services/providers/gemini/geminiMessageConverter.js.map +1 -0
- package/dist/cjs/services/providers/gemini/geminiToolAdapter.d.ts +23 -0
- package/dist/cjs/services/providers/gemini/geminiToolAdapter.d.ts.map +1 -0
- package/dist/cjs/services/providers/gemini/geminiToolAdapter.js +47 -0
- package/dist/cjs/services/providers/gemini/geminiToolAdapter.js.map +1 -0
- package/dist/cjs/services/providers/openai/OpenAIChatService.d.ts +3 -28
- package/dist/cjs/services/providers/openai/OpenAIChatService.d.ts.map +1 -1
- package/dist/cjs/services/providers/openai/OpenAIChatService.js +15 -250
- package/dist/cjs/services/providers/openai/OpenAIChatService.js.map +1 -1
- package/dist/cjs/services/providers/openai/openaiRequestBuilder.d.ts +33 -0
- package/dist/cjs/services/providers/openai/openaiRequestBuilder.d.ts.map +1 -0
- package/dist/cjs/services/providers/openai/openaiRequestBuilder.js +218 -0
- package/dist/cjs/services/providers/openai/openaiRequestBuilder.js.map +1 -0
- package/dist/cjs/services/providers/openai/openaiToolBuilder.d.ts +9 -0
- package/dist/cjs/services/providers/openai/openaiToolBuilder.d.ts.map +1 -0
- package/dist/cjs/services/providers/openai/openaiToolBuilder.js +36 -0
- package/dist/cjs/services/providers/openai/openaiToolBuilder.js.map +1 -0
- package/dist/esm/constants/claude.d.ts +1 -0
- package/dist/esm/constants/claude.d.ts.map +1 -1
- package/dist/esm/constants/claude.js +2 -0
- package/dist/esm/constants/claude.js.map +1 -1
- package/dist/esm/constants/gemini.d.ts +0 -7
- package/dist/esm/constants/gemini.d.ts.map +1 -1
- package/dist/esm/constants/gemini.js +0 -9
- package/dist/esm/constants/gemini.js.map +1 -1
- package/dist/esm/services/providers/claude/ClaudeChatService.d.ts +0 -24
- package/dist/esm/services/providers/claude/ClaudeChatService.d.ts.map +1 -1
- package/dist/esm/services/providers/claude/ClaudeChatService.js +3 -113
- package/dist/esm/services/providers/claude/ClaudeChatService.js.map +1 -1
- package/dist/esm/services/providers/claude/ClaudeChatServiceProvider.d.ts.map +1 -1
- package/dist/esm/services/providers/claude/ClaudeChatServiceProvider.js +2 -1
- package/dist/esm/services/providers/claude/ClaudeChatServiceProvider.js.map +1 -1
- package/dist/esm/services/providers/claude/claudeMessageConverter.d.ts +6 -0
- package/dist/esm/services/providers/claude/claudeMessageConverter.d.ts.map +1 -0
- package/dist/esm/services/providers/claude/claudeMessageConverter.js +89 -0
- package/dist/esm/services/providers/claude/claudeMessageConverter.js.map +1 -0
- package/dist/esm/services/providers/gemini/GeminiChatService.d.ts +0 -21
- package/dist/esm/services/providers/gemini/GeminiChatService.d.ts.map +1 -1
- package/dist/esm/services/providers/gemini/GeminiChatService.js +14 -254
- package/dist/esm/services/providers/gemini/GeminiChatService.js.map +1 -1
- package/dist/esm/services/providers/gemini/geminiMessageConverter.d.ts +17 -0
- package/dist/esm/services/providers/gemini/geminiMessageConverter.d.ts.map +1 -0
- package/dist/esm/services/providers/gemini/geminiMessageConverter.js +178 -0
- package/dist/esm/services/providers/gemini/geminiMessageConverter.js.map +1 -0
- package/dist/esm/services/providers/gemini/geminiToolAdapter.d.ts +23 -0
- package/dist/esm/services/providers/gemini/geminiToolAdapter.d.ts.map +1 -0
- package/dist/esm/services/providers/gemini/geminiToolAdapter.js +42 -0
- package/dist/esm/services/providers/gemini/geminiToolAdapter.js.map +1 -0
- package/dist/esm/services/providers/openai/OpenAIChatService.d.ts +3 -28
- package/dist/esm/services/providers/openai/OpenAIChatService.d.ts.map +1 -1
- package/dist/esm/services/providers/openai/OpenAIChatService.js +17 -252
- package/dist/esm/services/providers/openai/OpenAIChatService.js.map +1 -1
- package/dist/esm/services/providers/openai/openaiRequestBuilder.d.ts +33 -0
- package/dist/esm/services/providers/openai/openaiRequestBuilder.d.ts.map +1 -0
- package/dist/esm/services/providers/openai/openaiRequestBuilder.js +212 -0
- package/dist/esm/services/providers/openai/openaiRequestBuilder.js.map +1 -0
- package/dist/esm/services/providers/openai/openaiToolBuilder.d.ts +9 -0
- package/dist/esm/services/providers/openai/openaiToolBuilder.d.ts.map +1 -0
- package/dist/esm/services/providers/openai/openaiToolBuilder.js +33 -0
- package/dist/esm/services/providers/openai/openaiToolBuilder.js.map +1 -0
- package/dist/umd/aituber-onair-chat.js +552 -548
- package/dist/umd/aituber-onair-chat.min.js +10 -10
- package/package.json +1 -1
|
@@ -84,14 +84,13 @@ var AITuberOnAirChat = (() => {
|
|
|
84
84
|
MODEL_CLAUDE_4_6_OPUS: () => MODEL_CLAUDE_4_6_OPUS,
|
|
85
85
|
MODEL_CLAUDE_4_6_SONNET: () => MODEL_CLAUDE_4_6_SONNET,
|
|
86
86
|
MODEL_CLAUDE_4_7_OPUS: () => MODEL_CLAUDE_4_7_OPUS,
|
|
87
|
+
MODEL_CLAUDE_4_8_OPUS: () => MODEL_CLAUDE_4_8_OPUS,
|
|
87
88
|
MODEL_CLAUDE_4_OPUS: () => MODEL_CLAUDE_4_OPUS,
|
|
88
89
|
MODEL_CLAUDE_4_SONNET: () => MODEL_CLAUDE_4_SONNET,
|
|
89
90
|
MODEL_DEEPSEEK_CHAT: () => MODEL_DEEPSEEK_CHAT,
|
|
90
91
|
MODEL_DEEPSEEK_REASONER: () => MODEL_DEEPSEEK_REASONER,
|
|
91
92
|
MODEL_DEEPSEEK_V4_FLASH: () => MODEL_DEEPSEEK_V4_FLASH,
|
|
92
93
|
MODEL_DEEPSEEK_V4_PRO: () => MODEL_DEEPSEEK_V4_PRO,
|
|
93
|
-
MODEL_GEMINI_2_0_FLASH: () => MODEL_GEMINI_2_0_FLASH,
|
|
94
|
-
MODEL_GEMINI_2_0_FLASH_LITE: () => MODEL_GEMINI_2_0_FLASH_LITE,
|
|
95
94
|
MODEL_GEMINI_2_5_FLASH: () => MODEL_GEMINI_2_5_FLASH,
|
|
96
95
|
MODEL_GEMINI_2_5_FLASH_LITE: () => MODEL_GEMINI_2_5_FLASH_LITE,
|
|
97
96
|
MODEL_GEMINI_2_5_FLASH_LITE_PREVIEW_06_17: () => MODEL_GEMINI_2_5_FLASH_LITE_PREVIEW_06_17,
|
|
@@ -307,8 +306,6 @@ var AITuberOnAirChat = (() => {
|
|
|
307
306
|
var MODEL_GEMINI_2_5_FLASH = "gemini-2.5-flash";
|
|
308
307
|
var MODEL_GEMINI_2_5_FLASH_LITE = "gemini-2.5-flash-lite";
|
|
309
308
|
var MODEL_GEMINI_2_5_FLASH_LITE_PREVIEW_06_17 = "gemini-2.5-flash-lite-preview-06-17";
|
|
310
|
-
var MODEL_GEMINI_2_0_FLASH = "gemini-2.0-flash";
|
|
311
|
-
var MODEL_GEMINI_2_0_FLASH_LITE = "gemini-2.0-flash-lite";
|
|
312
309
|
var GEMINI_RECOMMENDED_MODELS = [
|
|
313
310
|
MODEL_GEMINI_3_5_FLASH,
|
|
314
311
|
MODEL_GEMINI_3_1_FLASH_LITE,
|
|
@@ -323,9 +320,7 @@ var AITuberOnAirChat = (() => {
|
|
|
323
320
|
var GEMINI_DEPRECATED_MODELS = [
|
|
324
321
|
MODEL_GEMINI_3_1_FLASH_LITE_PREVIEW,
|
|
325
322
|
MODEL_GEMINI_3_PRO_PREVIEW,
|
|
326
|
-
MODEL_GEMINI_2_5_FLASH_LITE_PREVIEW_06_17
|
|
327
|
-
MODEL_GEMINI_2_0_FLASH,
|
|
328
|
-
MODEL_GEMINI_2_0_FLASH_LITE
|
|
323
|
+
MODEL_GEMINI_2_5_FLASH_LITE_PREVIEW_06_17
|
|
329
324
|
];
|
|
330
325
|
var GEMINI_VISION_SUPPORTED_MODELS = [
|
|
331
326
|
...GEMINI_RECOMMENDED_MODELS,
|
|
@@ -346,6 +341,7 @@ var AITuberOnAirChat = (() => {
|
|
|
346
341
|
var MODEL_CLAUDE_4_6_SONNET = "claude-sonnet-4-6";
|
|
347
342
|
var MODEL_CLAUDE_4_6_OPUS = "claude-opus-4-6";
|
|
348
343
|
var MODEL_CLAUDE_4_7_OPUS = "claude-opus-4-7";
|
|
344
|
+
var MODEL_CLAUDE_4_8_OPUS = "claude-opus-4-8";
|
|
349
345
|
var CLAUDE_VISION_SUPPORTED_MODELS = [
|
|
350
346
|
MODEL_CLAUDE_3_HAIKU,
|
|
351
347
|
MODEL_CLAUDE_4_SONNET,
|
|
@@ -355,7 +351,8 @@ var AITuberOnAirChat = (() => {
|
|
|
355
351
|
MODEL_CLAUDE_4_5_OPUS,
|
|
356
352
|
MODEL_CLAUDE_4_6_SONNET,
|
|
357
353
|
MODEL_CLAUDE_4_6_OPUS,
|
|
358
|
-
MODEL_CLAUDE_4_7_OPUS
|
|
354
|
+
MODEL_CLAUDE_4_7_OPUS,
|
|
355
|
+
MODEL_CLAUDE_4_8_OPUS
|
|
359
356
|
];
|
|
360
357
|
|
|
361
358
|
// src/constants/openrouter.ts
|
|
@@ -1247,6 +1244,94 @@ If it's in another language, summarize in that language.
|
|
|
1247
1244
|
};
|
|
1248
1245
|
}
|
|
1249
1246
|
|
|
1247
|
+
// src/services/providers/claude/claudeMessageConverter.ts
|
|
1248
|
+
function convertMessagesToClaudeFormat(messages) {
|
|
1249
|
+
return messages.map((msg) => ({
|
|
1250
|
+
role: mapRoleToClaude(msg.role),
|
|
1251
|
+
content: msg.content
|
|
1252
|
+
}));
|
|
1253
|
+
}
|
|
1254
|
+
function convertVisionMessagesToClaudeFormat(messages) {
|
|
1255
|
+
return messages.map((msg) => {
|
|
1256
|
+
if (typeof msg.content === "string") {
|
|
1257
|
+
return {
|
|
1258
|
+
role: mapRoleToClaude(msg.role),
|
|
1259
|
+
content: [
|
|
1260
|
+
{
|
|
1261
|
+
type: "text",
|
|
1262
|
+
text: msg.content
|
|
1263
|
+
}
|
|
1264
|
+
]
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
if (Array.isArray(msg.content)) {
|
|
1268
|
+
const content = msg.content.map((block) => {
|
|
1269
|
+
if (block.type === "image_url") {
|
|
1270
|
+
if (block.image_url.url.startsWith("data:")) {
|
|
1271
|
+
const m = block.image_url.url.match(/^data:([^;]+);base64,(.+)$/);
|
|
1272
|
+
if (m) {
|
|
1273
|
+
return {
|
|
1274
|
+
type: "image",
|
|
1275
|
+
source: {
|
|
1276
|
+
type: "base64",
|
|
1277
|
+
media_type: m[1],
|
|
1278
|
+
data: m[2]
|
|
1279
|
+
}
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
return null;
|
|
1283
|
+
}
|
|
1284
|
+
return {
|
|
1285
|
+
type: "image",
|
|
1286
|
+
source: {
|
|
1287
|
+
type: "url",
|
|
1288
|
+
url: block.image_url.url,
|
|
1289
|
+
media_type: getMimeTypeFromUrl(block.image_url.url)
|
|
1290
|
+
}
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
return block;
|
|
1294
|
+
}).filter((b) => b);
|
|
1295
|
+
return {
|
|
1296
|
+
role: mapRoleToClaude(msg.role),
|
|
1297
|
+
content
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
return {
|
|
1301
|
+
role: mapRoleToClaude(msg.role),
|
|
1302
|
+
content: []
|
|
1303
|
+
};
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
function mapRoleToClaude(role) {
|
|
1307
|
+
switch (role) {
|
|
1308
|
+
case "system":
|
|
1309
|
+
return "system";
|
|
1310
|
+
case "user":
|
|
1311
|
+
return "user";
|
|
1312
|
+
case "assistant":
|
|
1313
|
+
return "assistant";
|
|
1314
|
+
default:
|
|
1315
|
+
return "user";
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
function getMimeTypeFromUrl(url) {
|
|
1319
|
+
const extension = url.split(".").pop()?.toLowerCase();
|
|
1320
|
+
switch (extension) {
|
|
1321
|
+
case "jpg":
|
|
1322
|
+
case "jpeg":
|
|
1323
|
+
return "image/jpeg";
|
|
1324
|
+
case "png":
|
|
1325
|
+
return "image/png";
|
|
1326
|
+
case "gif":
|
|
1327
|
+
return "image/gif";
|
|
1328
|
+
case "webp":
|
|
1329
|
+
return "image/webp";
|
|
1330
|
+
default:
|
|
1331
|
+
return "image/jpeg";
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1250
1335
|
// src/services/providers/claude/ClaudeChatService.ts
|
|
1251
1336
|
var ClaudeChatService = class {
|
|
1252
1337
|
/**
|
|
@@ -1353,112 +1438,6 @@ If it's in another language, summarize in that language.
|
|
|
1353
1438
|
toolErrorMessage: "processVisionChat received tool_calls. ChatProcessor must use chatOnce() loop when tools are enabled."
|
|
1354
1439
|
});
|
|
1355
1440
|
}
|
|
1356
|
-
/**
|
|
1357
|
-
* Convert AITuber OnAir messages to Claude format
|
|
1358
|
-
* @param messages Array of messages
|
|
1359
|
-
* @returns Claude formatted messages
|
|
1360
|
-
*/
|
|
1361
|
-
convertMessagesToClaudeFormat(messages) {
|
|
1362
|
-
return messages.map((msg) => {
|
|
1363
|
-
return {
|
|
1364
|
-
role: this.mapRoleToClaude(msg.role),
|
|
1365
|
-
content: msg.content
|
|
1366
|
-
};
|
|
1367
|
-
});
|
|
1368
|
-
}
|
|
1369
|
-
/**
|
|
1370
|
-
* Convert AITuber OnAir vision messages to Claude format
|
|
1371
|
-
* @param messages Array of vision messages
|
|
1372
|
-
* @returns Claude formatted vision messages
|
|
1373
|
-
*/
|
|
1374
|
-
convertVisionMessagesToClaudeFormat(messages) {
|
|
1375
|
-
return messages.map((msg) => {
|
|
1376
|
-
if (typeof msg.content === "string") {
|
|
1377
|
-
return {
|
|
1378
|
-
role: this.mapRoleToClaude(msg.role),
|
|
1379
|
-
content: [
|
|
1380
|
-
{
|
|
1381
|
-
type: "text",
|
|
1382
|
-
text: msg.content
|
|
1383
|
-
}
|
|
1384
|
-
]
|
|
1385
|
-
};
|
|
1386
|
-
}
|
|
1387
|
-
if (Array.isArray(msg.content)) {
|
|
1388
|
-
const content = msg.content.map((block) => {
|
|
1389
|
-
if (block.type === "image_url") {
|
|
1390
|
-
if (block.image_url.url.startsWith("data:")) {
|
|
1391
|
-
const m = block.image_url.url.match(
|
|
1392
|
-
/^data:([^;]+);base64,(.+)$/
|
|
1393
|
-
);
|
|
1394
|
-
if (m) {
|
|
1395
|
-
return {
|
|
1396
|
-
type: "image",
|
|
1397
|
-
source: { type: "base64", media_type: m[1], data: m[2] }
|
|
1398
|
-
};
|
|
1399
|
-
}
|
|
1400
|
-
return null;
|
|
1401
|
-
}
|
|
1402
|
-
return {
|
|
1403
|
-
type: "image",
|
|
1404
|
-
source: {
|
|
1405
|
-
type: "url",
|
|
1406
|
-
url: block.image_url.url,
|
|
1407
|
-
media_type: this.getMimeTypeFromUrl(block.image_url.url)
|
|
1408
|
-
}
|
|
1409
|
-
};
|
|
1410
|
-
}
|
|
1411
|
-
return block;
|
|
1412
|
-
}).filter((b) => b);
|
|
1413
|
-
return {
|
|
1414
|
-
role: this.mapRoleToClaude(msg.role),
|
|
1415
|
-
content
|
|
1416
|
-
};
|
|
1417
|
-
}
|
|
1418
|
-
return {
|
|
1419
|
-
role: this.mapRoleToClaude(msg.role),
|
|
1420
|
-
content: []
|
|
1421
|
-
};
|
|
1422
|
-
});
|
|
1423
|
-
}
|
|
1424
|
-
/**
|
|
1425
|
-
* Map AITuber OnAir roles to Claude roles
|
|
1426
|
-
* @param role AITuber OnAir role
|
|
1427
|
-
* @returns Claude role
|
|
1428
|
-
*/
|
|
1429
|
-
mapRoleToClaude(role) {
|
|
1430
|
-
switch (role) {
|
|
1431
|
-
case "system":
|
|
1432
|
-
return "system";
|
|
1433
|
-
case "user":
|
|
1434
|
-
return "user";
|
|
1435
|
-
case "assistant":
|
|
1436
|
-
return "assistant";
|
|
1437
|
-
default:
|
|
1438
|
-
return "user";
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
/**
|
|
1442
|
-
* Get MIME type from URL
|
|
1443
|
-
* @param url Image URL
|
|
1444
|
-
* @returns MIME type
|
|
1445
|
-
*/
|
|
1446
|
-
getMimeTypeFromUrl(url) {
|
|
1447
|
-
const extension = url.split(".").pop()?.toLowerCase();
|
|
1448
|
-
switch (extension) {
|
|
1449
|
-
case "jpg":
|
|
1450
|
-
case "jpeg":
|
|
1451
|
-
return "image/jpeg";
|
|
1452
|
-
case "png":
|
|
1453
|
-
return "image/png";
|
|
1454
|
-
case "gif":
|
|
1455
|
-
return "image/gif";
|
|
1456
|
-
case "webp":
|
|
1457
|
-
return "image/webp";
|
|
1458
|
-
default:
|
|
1459
|
-
return "image/jpeg";
|
|
1460
|
-
}
|
|
1461
|
-
}
|
|
1462
1441
|
/**
|
|
1463
1442
|
* Call Claude API
|
|
1464
1443
|
* @param messages Array of messages to send
|
|
@@ -1478,9 +1457,7 @@ If it's in another language, summarize in that language.
|
|
|
1478
1457
|
const body = {
|
|
1479
1458
|
model,
|
|
1480
1459
|
system,
|
|
1481
|
-
messages: hasVision ?
|
|
1482
|
-
content
|
|
1483
|
-
) : this.convertMessagesToClaudeFormat(content),
|
|
1460
|
+
messages: hasVision ? convertVisionMessagesToClaudeFormat(content) : convertMessagesToClaudeFormat(content),
|
|
1484
1461
|
stream,
|
|
1485
1462
|
max_tokens: maxTokens !== void 0 ? maxTokens : getMaxTokensForResponseLength(this.responseLength)
|
|
1486
1463
|
};
|
|
@@ -1743,6 +1720,7 @@ If it's in another language, summarize in that language.
|
|
|
1743
1720
|
MODEL_CLAUDE_4_6_SONNET,
|
|
1744
1721
|
MODEL_CLAUDE_4_6_OPUS,
|
|
1745
1722
|
MODEL_CLAUDE_4_7_OPUS,
|
|
1723
|
+
MODEL_CLAUDE_4_8_OPUS,
|
|
1746
1724
|
MODEL_CLAUDE_3_HAIKU
|
|
1747
1725
|
];
|
|
1748
1726
|
}
|
|
@@ -1934,7 +1912,49 @@ If it's in another language, summarize in that language.
|
|
|
1934
1912
|
};
|
|
1935
1913
|
}
|
|
1936
1914
|
|
|
1937
|
-
// src/services/providers/openai/
|
|
1915
|
+
// src/services/providers/openai/openaiToolBuilder.ts
|
|
1916
|
+
function buildOpenAIToolsDefinition({
|
|
1917
|
+
tools,
|
|
1918
|
+
mcpServers,
|
|
1919
|
+
isResponsesAPI
|
|
1920
|
+
}) {
|
|
1921
|
+
const toolDefs = [];
|
|
1922
|
+
if (tools.length > 0) {
|
|
1923
|
+
toolDefs.push(
|
|
1924
|
+
...buildOpenAICompatibleTools(
|
|
1925
|
+
tools,
|
|
1926
|
+
isResponsesAPI ? "responses" : "chat-completions"
|
|
1927
|
+
)
|
|
1928
|
+
);
|
|
1929
|
+
}
|
|
1930
|
+
if (mcpServers.length > 0 && isResponsesAPI) {
|
|
1931
|
+
toolDefs.push(...buildOpenAIMCPToolsDefinition(mcpServers));
|
|
1932
|
+
}
|
|
1933
|
+
return toolDefs;
|
|
1934
|
+
}
|
|
1935
|
+
function buildOpenAIMCPToolsDefinition(mcpServers) {
|
|
1936
|
+
return mcpServers.map((server) => {
|
|
1937
|
+
const mcpDef = {
|
|
1938
|
+
type: "mcp",
|
|
1939
|
+
server_label: server.name,
|
|
1940
|
+
server_url: server.url
|
|
1941
|
+
};
|
|
1942
|
+
if (server.require_approval) {
|
|
1943
|
+
mcpDef.require_approval = server.require_approval;
|
|
1944
|
+
}
|
|
1945
|
+
if (server.tool_configuration?.allowed_tools) {
|
|
1946
|
+
mcpDef.allowed_tools = server.tool_configuration.allowed_tools;
|
|
1947
|
+
}
|
|
1948
|
+
if (server.authorization_token) {
|
|
1949
|
+
mcpDef.headers = {
|
|
1950
|
+
Authorization: `Bearer ${server.authorization_token}`
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
return mcpDef;
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
// src/services/providers/openai/openaiRequestBuilder.ts
|
|
1938
1958
|
var GPT5_RESPONSE_LENGTH_MIN_TOKENS = {
|
|
1939
1959
|
[CHAT_RESPONSE_LENGTH.VERY_SHORT]: 800,
|
|
1940
1960
|
[CHAT_RESPONSE_LENGTH.SHORT]: 1200,
|
|
@@ -1956,6 +1976,177 @@ If it's in another language, summarize in that language.
|
|
|
1956
1976
|
"deepseek",
|
|
1957
1977
|
"mistral"
|
|
1958
1978
|
]);
|
|
1979
|
+
function buildOpenAIRequestBody({
|
|
1980
|
+
provider,
|
|
1981
|
+
endpoint,
|
|
1982
|
+
messages,
|
|
1983
|
+
model,
|
|
1984
|
+
stream,
|
|
1985
|
+
tools,
|
|
1986
|
+
mcpServers,
|
|
1987
|
+
responseLength,
|
|
1988
|
+
verbosity,
|
|
1989
|
+
reasoning_effort,
|
|
1990
|
+
enableReasoningSummary,
|
|
1991
|
+
maxTokens
|
|
1992
|
+
}) {
|
|
1993
|
+
const isResponsesAPI = endpoint === ENDPOINT_OPENAI_RESPONSES_API;
|
|
1994
|
+
validateMCPCompatibility(endpoint, mcpServers);
|
|
1995
|
+
const body = {
|
|
1996
|
+
model,
|
|
1997
|
+
stream
|
|
1998
|
+
};
|
|
1999
|
+
const tokenLimit = resolveOpenAITokenLimit({
|
|
2000
|
+
provider,
|
|
2001
|
+
model,
|
|
2002
|
+
responseLength,
|
|
2003
|
+
reasoning_effort,
|
|
2004
|
+
maxTokens
|
|
2005
|
+
});
|
|
2006
|
+
if (isResponsesAPI) {
|
|
2007
|
+
if (tokenLimit !== void 0) {
|
|
2008
|
+
body.max_output_tokens = tokenLimit;
|
|
2009
|
+
}
|
|
2010
|
+
} else {
|
|
2011
|
+
if (tokenLimit !== void 0) {
|
|
2012
|
+
if (usesCompatibleChatCompletions(provider)) {
|
|
2013
|
+
body.max_tokens = tokenLimit;
|
|
2014
|
+
} else {
|
|
2015
|
+
body.max_completion_tokens = tokenLimit;
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
if (isResponsesAPI) {
|
|
2020
|
+
body.input = cleanMessagesForResponsesAPI(messages);
|
|
2021
|
+
} else {
|
|
2022
|
+
body.messages = provider === "mistral" ? cleanMessagesForMistralChatCompletions(messages) : messages;
|
|
2023
|
+
}
|
|
2024
|
+
if (isGPT5Model(model)) {
|
|
2025
|
+
if (isResponsesAPI) {
|
|
2026
|
+
if (reasoning_effort) {
|
|
2027
|
+
body.reasoning = {
|
|
2028
|
+
...body.reasoning,
|
|
2029
|
+
effort: reasoning_effort
|
|
2030
|
+
};
|
|
2031
|
+
if (enableReasoningSummary) {
|
|
2032
|
+
body.reasoning.summary = "auto";
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
if (verbosity) {
|
|
2036
|
+
body.text = {
|
|
2037
|
+
...body.text,
|
|
2038
|
+
format: { type: "text" },
|
|
2039
|
+
verbosity
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
} else {
|
|
2043
|
+
if (reasoning_effort) {
|
|
2044
|
+
body.reasoning_effort = reasoning_effort;
|
|
2045
|
+
}
|
|
2046
|
+
if (verbosity) {
|
|
2047
|
+
body.verbosity = verbosity;
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
if (provider === "mistral" && isMistralReasoningEffortModel(model) && reasoning_effort && isMistralReasoningEffort(reasoning_effort)) {
|
|
2052
|
+
body.reasoning_effort = reasoning_effort;
|
|
2053
|
+
}
|
|
2054
|
+
const toolDefinitions = buildOpenAIToolsDefinition({
|
|
2055
|
+
tools,
|
|
2056
|
+
mcpServers,
|
|
2057
|
+
isResponsesAPI
|
|
2058
|
+
});
|
|
2059
|
+
if (toolDefinitions.length > 0) {
|
|
2060
|
+
body.tools = toolDefinitions;
|
|
2061
|
+
if (!isResponsesAPI) {
|
|
2062
|
+
body.tool_choice = "auto";
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
return body;
|
|
2066
|
+
}
|
|
2067
|
+
function resolveOpenAITokenLimit({
|
|
2068
|
+
provider,
|
|
2069
|
+
model,
|
|
2070
|
+
responseLength,
|
|
2071
|
+
reasoning_effort,
|
|
2072
|
+
maxTokens
|
|
2073
|
+
}) {
|
|
2074
|
+
if (maxTokens !== void 0) {
|
|
2075
|
+
return maxTokens;
|
|
2076
|
+
}
|
|
2077
|
+
const baseTokenLimit = usesCompatibleChatCompletions(provider) ? responseLength !== void 0 ? getMaxTokensForResponseLength(responseLength) : void 0 : getMaxTokensForResponseLength(responseLength);
|
|
2078
|
+
if (provider !== "openai" || !isGPT5Model(model) || responseLength === void 0) {
|
|
2079
|
+
return baseTokenLimit;
|
|
2080
|
+
}
|
|
2081
|
+
const effectiveReasoningEffort = reasoning_effort ?? getDefaultReasoningEffortForGPT5Model(model);
|
|
2082
|
+
return Math.max(
|
|
2083
|
+
baseTokenLimit ?? 0,
|
|
2084
|
+
GPT5_RESPONSE_LENGTH_MIN_TOKENS[responseLength],
|
|
2085
|
+
GPT5_REASONING_MIN_TOKENS[effectiveReasoningEffort]
|
|
2086
|
+
);
|
|
2087
|
+
}
|
|
2088
|
+
function validateMCPCompatibility(endpoint, mcpServers) {
|
|
2089
|
+
if (mcpServers.length > 0 && endpoint === ENDPOINT_OPENAI_CHAT_COMPLETIONS_API) {
|
|
2090
|
+
throw new Error(
|
|
2091
|
+
`MCP servers are not supported with Chat Completions API. Current endpoint: ${endpoint}. Please use OpenAI Responses API endpoint: ${ENDPOINT_OPENAI_RESPONSES_API}. MCP tools are only available in the Responses API endpoint.`
|
|
2092
|
+
);
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
function cleanMessagesForResponsesAPI(messages) {
|
|
2096
|
+
return messages.map((msg) => {
|
|
2097
|
+
const role = msg.role === "tool" ? "user" : msg.role;
|
|
2098
|
+
const cleanMsg = {
|
|
2099
|
+
role
|
|
2100
|
+
};
|
|
2101
|
+
if (typeof msg.content === "string") {
|
|
2102
|
+
cleanMsg.content = msg.content;
|
|
2103
|
+
} else if (Array.isArray(msg.content)) {
|
|
2104
|
+
cleanMsg.content = msg.content.map((block) => {
|
|
2105
|
+
if (block.type === "text") {
|
|
2106
|
+
return {
|
|
2107
|
+
type: "input_text",
|
|
2108
|
+
text: block.text
|
|
2109
|
+
};
|
|
2110
|
+
} else if (block.type === "image_url") {
|
|
2111
|
+
return {
|
|
2112
|
+
type: "input_image",
|
|
2113
|
+
image_url: block.image_url.url
|
|
2114
|
+
};
|
|
2115
|
+
}
|
|
2116
|
+
return block;
|
|
2117
|
+
});
|
|
2118
|
+
} else {
|
|
2119
|
+
cleanMsg.content = msg.content;
|
|
2120
|
+
}
|
|
2121
|
+
return cleanMsg;
|
|
2122
|
+
});
|
|
2123
|
+
}
|
|
2124
|
+
function cleanMessagesForMistralChatCompletions(messages) {
|
|
2125
|
+
return messages.map((msg) => {
|
|
2126
|
+
const cleanMsg = {
|
|
2127
|
+
role: msg.role
|
|
2128
|
+
};
|
|
2129
|
+
if (!Array.isArray(msg.content)) {
|
|
2130
|
+
cleanMsg.content = msg.content;
|
|
2131
|
+
return cleanMsg;
|
|
2132
|
+
}
|
|
2133
|
+
cleanMsg.content = msg.content.map((block) => {
|
|
2134
|
+
if (block.type === "image_url" && typeof block.image_url === "object" && typeof block.image_url?.url === "string") {
|
|
2135
|
+
return {
|
|
2136
|
+
type: "image_url",
|
|
2137
|
+
image_url: block.image_url.url
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
2140
|
+
return block;
|
|
2141
|
+
});
|
|
2142
|
+
return cleanMsg;
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
function usesCompatibleChatCompletions(provider) {
|
|
2146
|
+
return OPENAI_COMPATIBLE_CHAT_COMPLETIONS_PROVIDERS.has(provider);
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
// src/services/providers/openai/OpenAIChatService.ts
|
|
1959
2150
|
var OpenAIChatService = class {
|
|
1960
2151
|
/**
|
|
1961
2152
|
* Constructor
|
|
@@ -2108,7 +2299,20 @@ If it's in another language, summarize in that language.
|
|
|
2108
2299
|
return stream ? this.parseStream(res, onPartialResponse) : this.parseOneShot(await res.json());
|
|
2109
2300
|
}
|
|
2110
2301
|
async callOpenAI(messages, model, stream = false, maxTokens) {
|
|
2111
|
-
const body =
|
|
2302
|
+
const body = buildOpenAIRequestBody({
|
|
2303
|
+
provider: this.provider,
|
|
2304
|
+
endpoint: this.endpoint,
|
|
2305
|
+
messages,
|
|
2306
|
+
model,
|
|
2307
|
+
stream,
|
|
2308
|
+
tools: this.tools,
|
|
2309
|
+
mcpServers: this.mcpServers,
|
|
2310
|
+
responseLength: this.responseLength,
|
|
2311
|
+
verbosity: this.verbosity,
|
|
2312
|
+
reasoning_effort: this.reasoning_effort,
|
|
2313
|
+
enableReasoningSummary: this.enableReasoningSummary,
|
|
2314
|
+
maxTokens
|
|
2315
|
+
});
|
|
2112
2316
|
const headers = {};
|
|
2113
2317
|
const shouldSendAuthorization = this.provider !== "openai-compatible" || this.apiKey.trim() !== "";
|
|
2114
2318
|
if (shouldSendAuthorization) {
|
|
@@ -2117,199 +2321,6 @@ If it's in another language, summarize in that language.
|
|
|
2117
2321
|
const res = await ChatServiceHttpClient.post(this.endpoint, body, headers);
|
|
2118
2322
|
return res;
|
|
2119
2323
|
}
|
|
2120
|
-
/**
|
|
2121
|
-
* Build request body based on the endpoint type
|
|
2122
|
-
*/
|
|
2123
|
-
buildRequestBody(messages, model, stream, maxTokens) {
|
|
2124
|
-
const isResponsesAPI = this.endpoint === ENDPOINT_OPENAI_RESPONSES_API;
|
|
2125
|
-
this.validateMCPCompatibility();
|
|
2126
|
-
const body = {
|
|
2127
|
-
model,
|
|
2128
|
-
stream
|
|
2129
|
-
};
|
|
2130
|
-
const tokenLimit = this.resolveTokenLimit(model, maxTokens);
|
|
2131
|
-
if (isResponsesAPI) {
|
|
2132
|
-
if (tokenLimit !== void 0) {
|
|
2133
|
-
body.max_output_tokens = tokenLimit;
|
|
2134
|
-
}
|
|
2135
|
-
} else {
|
|
2136
|
-
if (tokenLimit !== void 0) {
|
|
2137
|
-
if (this.usesCompatibleChatCompletions()) {
|
|
2138
|
-
body.max_tokens = tokenLimit;
|
|
2139
|
-
} else {
|
|
2140
|
-
body.max_completion_tokens = tokenLimit;
|
|
2141
|
-
}
|
|
2142
|
-
}
|
|
2143
|
-
}
|
|
2144
|
-
if (isResponsesAPI) {
|
|
2145
|
-
body.input = this.cleanMessagesForResponsesAPI(messages);
|
|
2146
|
-
} else {
|
|
2147
|
-
body.messages = this.provider === "mistral" ? this.cleanMessagesForMistralChatCompletions(messages) : messages;
|
|
2148
|
-
}
|
|
2149
|
-
if (isGPT5Model(model)) {
|
|
2150
|
-
if (isResponsesAPI) {
|
|
2151
|
-
if (this.reasoning_effort) {
|
|
2152
|
-
body.reasoning = {
|
|
2153
|
-
...body.reasoning,
|
|
2154
|
-
effort: this.reasoning_effort
|
|
2155
|
-
};
|
|
2156
|
-
if (this.enableReasoningSummary) {
|
|
2157
|
-
body.reasoning.summary = "auto";
|
|
2158
|
-
}
|
|
2159
|
-
}
|
|
2160
|
-
if (this.verbosity) {
|
|
2161
|
-
body.text = {
|
|
2162
|
-
...body.text,
|
|
2163
|
-
format: { type: "text" },
|
|
2164
|
-
verbosity: this.verbosity
|
|
2165
|
-
};
|
|
2166
|
-
}
|
|
2167
|
-
} else {
|
|
2168
|
-
if (this.reasoning_effort) {
|
|
2169
|
-
body.reasoning_effort = this.reasoning_effort;
|
|
2170
|
-
}
|
|
2171
|
-
if (this.verbosity) {
|
|
2172
|
-
body.verbosity = this.verbosity;
|
|
2173
|
-
}
|
|
2174
|
-
}
|
|
2175
|
-
}
|
|
2176
|
-
if (this.provider === "mistral" && isMistralReasoningEffortModel(model) && this.reasoning_effort && isMistralReasoningEffort(this.reasoning_effort)) {
|
|
2177
|
-
body.reasoning_effort = this.reasoning_effort;
|
|
2178
|
-
}
|
|
2179
|
-
const tools = this.buildToolsDefinition();
|
|
2180
|
-
if (tools.length > 0) {
|
|
2181
|
-
body.tools = tools;
|
|
2182
|
-
if (!isResponsesAPI) {
|
|
2183
|
-
body.tool_choice = "auto";
|
|
2184
|
-
}
|
|
2185
|
-
}
|
|
2186
|
-
return body;
|
|
2187
|
-
}
|
|
2188
|
-
resolveTokenLimit(model, maxTokens) {
|
|
2189
|
-
if (maxTokens !== void 0) {
|
|
2190
|
-
return maxTokens;
|
|
2191
|
-
}
|
|
2192
|
-
const baseTokenLimit = this.usesCompatibleChatCompletions() ? this.responseLength !== void 0 ? getMaxTokensForResponseLength(this.responseLength) : void 0 : getMaxTokensForResponseLength(this.responseLength);
|
|
2193
|
-
if (this.provider !== "openai" || !isGPT5Model(model) || this.responseLength === void 0) {
|
|
2194
|
-
return baseTokenLimit;
|
|
2195
|
-
}
|
|
2196
|
-
const effectiveReasoningEffort = this.reasoning_effort ?? getDefaultReasoningEffortForGPT5Model(model);
|
|
2197
|
-
return Math.max(
|
|
2198
|
-
baseTokenLimit ?? 0,
|
|
2199
|
-
GPT5_RESPONSE_LENGTH_MIN_TOKENS[this.responseLength],
|
|
2200
|
-
GPT5_REASONING_MIN_TOKENS[effectiveReasoningEffort]
|
|
2201
|
-
);
|
|
2202
|
-
}
|
|
2203
|
-
/**
|
|
2204
|
-
* Validate MCP servers compatibility with the current endpoint
|
|
2205
|
-
*/
|
|
2206
|
-
validateMCPCompatibility() {
|
|
2207
|
-
if (this.mcpServers.length > 0 && this.endpoint === ENDPOINT_OPENAI_CHAT_COMPLETIONS_API) {
|
|
2208
|
-
throw new Error(
|
|
2209
|
-
`MCP servers are not supported with Chat Completions API. Current endpoint: ${this.endpoint}. Please use OpenAI Responses API endpoint: ${ENDPOINT_OPENAI_RESPONSES_API}. MCP tools are only available in the Responses API endpoint.`
|
|
2210
|
-
);
|
|
2211
|
-
}
|
|
2212
|
-
}
|
|
2213
|
-
/**
|
|
2214
|
-
* Clean messages for Responses API (remove timestamp and other extra properties)
|
|
2215
|
-
*/
|
|
2216
|
-
cleanMessagesForResponsesAPI(messages) {
|
|
2217
|
-
return messages.map((msg) => {
|
|
2218
|
-
const role = msg.role === "tool" ? "user" : msg.role;
|
|
2219
|
-
const cleanMsg = {
|
|
2220
|
-
role
|
|
2221
|
-
};
|
|
2222
|
-
if (typeof msg.content === "string") {
|
|
2223
|
-
cleanMsg.content = msg.content;
|
|
2224
|
-
} else if (Array.isArray(msg.content)) {
|
|
2225
|
-
cleanMsg.content = msg.content.map((block) => {
|
|
2226
|
-
if (block.type === "text") {
|
|
2227
|
-
return {
|
|
2228
|
-
type: "input_text",
|
|
2229
|
-
text: block.text
|
|
2230
|
-
};
|
|
2231
|
-
} else if (block.type === "image_url") {
|
|
2232
|
-
return {
|
|
2233
|
-
type: "input_image",
|
|
2234
|
-
image_url: block.image_url.url
|
|
2235
|
-
// Extract the URL string directly
|
|
2236
|
-
};
|
|
2237
|
-
}
|
|
2238
|
-
return block;
|
|
2239
|
-
});
|
|
2240
|
-
} else {
|
|
2241
|
-
cleanMsg.content = msg.content;
|
|
2242
|
-
}
|
|
2243
|
-
return cleanMsg;
|
|
2244
|
-
});
|
|
2245
|
-
}
|
|
2246
|
-
cleanMessagesForMistralChatCompletions(messages) {
|
|
2247
|
-
return messages.map((msg) => {
|
|
2248
|
-
const cleanMsg = {
|
|
2249
|
-
role: msg.role
|
|
2250
|
-
};
|
|
2251
|
-
if (!Array.isArray(msg.content)) {
|
|
2252
|
-
cleanMsg.content = msg.content;
|
|
2253
|
-
return cleanMsg;
|
|
2254
|
-
}
|
|
2255
|
-
cleanMsg.content = msg.content.map((block) => {
|
|
2256
|
-
if (block.type === "image_url" && typeof block.image_url === "object" && typeof block.image_url?.url === "string") {
|
|
2257
|
-
return {
|
|
2258
|
-
type: "image_url",
|
|
2259
|
-
image_url: block.image_url.url
|
|
2260
|
-
};
|
|
2261
|
-
}
|
|
2262
|
-
return block;
|
|
2263
|
-
});
|
|
2264
|
-
return cleanMsg;
|
|
2265
|
-
});
|
|
2266
|
-
}
|
|
2267
|
-
/**
|
|
2268
|
-
* Build tools definition based on the endpoint type
|
|
2269
|
-
*/
|
|
2270
|
-
buildToolsDefinition() {
|
|
2271
|
-
const isResponsesAPI = this.endpoint === ENDPOINT_OPENAI_RESPONSES_API;
|
|
2272
|
-
const toolDefs = [];
|
|
2273
|
-
if (this.tools.length > 0) {
|
|
2274
|
-
toolDefs.push(
|
|
2275
|
-
...buildOpenAICompatibleTools(
|
|
2276
|
-
this.tools,
|
|
2277
|
-
isResponsesAPI ? "responses" : "chat-completions"
|
|
2278
|
-
)
|
|
2279
|
-
);
|
|
2280
|
-
}
|
|
2281
|
-
if (this.mcpServers.length > 0 && isResponsesAPI) {
|
|
2282
|
-
toolDefs.push(...this.buildMCPToolsDefinition());
|
|
2283
|
-
}
|
|
2284
|
-
return toolDefs;
|
|
2285
|
-
}
|
|
2286
|
-
/**
|
|
2287
|
-
* Build MCP tools definition for Responses API
|
|
2288
|
-
*/
|
|
2289
|
-
buildMCPToolsDefinition() {
|
|
2290
|
-
return this.mcpServers.map((server) => {
|
|
2291
|
-
const mcpDef = {
|
|
2292
|
-
type: "mcp",
|
|
2293
|
-
// Using 'mcp' as indicated by the error message
|
|
2294
|
-
server_label: server.name,
|
|
2295
|
-
// Use server_label as required by API
|
|
2296
|
-
server_url: server.url
|
|
2297
|
-
// Use server_url instead of url
|
|
2298
|
-
};
|
|
2299
|
-
if (server.require_approval) {
|
|
2300
|
-
mcpDef.require_approval = server.require_approval;
|
|
2301
|
-
}
|
|
2302
|
-
if (server.tool_configuration?.allowed_tools) {
|
|
2303
|
-
mcpDef.allowed_tools = server.tool_configuration.allowed_tools;
|
|
2304
|
-
}
|
|
2305
|
-
if (server.authorization_token) {
|
|
2306
|
-
mcpDef.headers = {
|
|
2307
|
-
Authorization: `Bearer ${server.authorization_token}`
|
|
2308
|
-
};
|
|
2309
|
-
}
|
|
2310
|
-
return mcpDef;
|
|
2311
|
-
});
|
|
2312
|
-
}
|
|
2313
2324
|
async handleStream(res, onPartial) {
|
|
2314
2325
|
return parseOpenAICompatibleTextStream(res, onPartial);
|
|
2315
2326
|
}
|
|
@@ -2321,9 +2332,6 @@ If it's in another language, summarize in that language.
|
|
|
2321
2332
|
parseOneShot(data) {
|
|
2322
2333
|
return parseOpenAICompatibleOneShot(data);
|
|
2323
2334
|
}
|
|
2324
|
-
usesCompatibleChatCompletions() {
|
|
2325
|
-
return OPENAI_COMPATIBLE_CHAT_COMPLETIONS_PROVIDERS.has(this.provider);
|
|
2326
|
-
}
|
|
2327
2335
|
};
|
|
2328
2336
|
|
|
2329
2337
|
// src/services/providers/deepseek/DeepSeekChatService.ts
|
|
@@ -2495,6 +2503,221 @@ If it's in another language, summarize in that language.
|
|
|
2495
2503
|
}
|
|
2496
2504
|
};
|
|
2497
2505
|
|
|
2506
|
+
// src/services/providers/gemini/geminiMessageConverter.ts
|
|
2507
|
+
function mapRoleToGemini(role) {
|
|
2508
|
+
switch (role) {
|
|
2509
|
+
case "system":
|
|
2510
|
+
return "model";
|
|
2511
|
+
case "user":
|
|
2512
|
+
return "user";
|
|
2513
|
+
case "assistant":
|
|
2514
|
+
return "model";
|
|
2515
|
+
default:
|
|
2516
|
+
return "user";
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
function convertMessagesToGeminiFormat(messages, options = {}) {
|
|
2520
|
+
const gemini = [];
|
|
2521
|
+
let currentRole = null;
|
|
2522
|
+
let currentParts = [];
|
|
2523
|
+
const pushCurrent = () => {
|
|
2524
|
+
if (currentRole && currentParts.length) {
|
|
2525
|
+
gemini.push({ role: currentRole, parts: [...currentParts] });
|
|
2526
|
+
currentParts = [];
|
|
2527
|
+
}
|
|
2528
|
+
};
|
|
2529
|
+
for (const msg of messages) {
|
|
2530
|
+
const role = mapRoleToGemini(msg.role);
|
|
2531
|
+
if (msg.tool_calls) {
|
|
2532
|
+
pushCurrent();
|
|
2533
|
+
for (const call of msg.tool_calls) {
|
|
2534
|
+
options.callIdMap?.set(call.id, call.function.name);
|
|
2535
|
+
gemini.push({
|
|
2536
|
+
role: "model",
|
|
2537
|
+
parts: [
|
|
2538
|
+
{
|
|
2539
|
+
functionCall: {
|
|
2540
|
+
name: call.function.name,
|
|
2541
|
+
args: JSON.parse(call.function.arguments || "{}")
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
]
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2547
|
+
continue;
|
|
2548
|
+
}
|
|
2549
|
+
if (msg.role === "tool") {
|
|
2550
|
+
pushCurrent();
|
|
2551
|
+
const funcName = msg.name ?? options.callIdMap?.get(msg.tool_call_id) ?? "result";
|
|
2552
|
+
gemini.push({
|
|
2553
|
+
role: "user",
|
|
2554
|
+
parts: [
|
|
2555
|
+
{
|
|
2556
|
+
functionResponse: {
|
|
2557
|
+
name: funcName,
|
|
2558
|
+
response: normalizeToolResult(safeJsonParse(msg.content))
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
]
|
|
2562
|
+
});
|
|
2563
|
+
continue;
|
|
2564
|
+
}
|
|
2565
|
+
if (role !== currentRole) pushCurrent();
|
|
2566
|
+
currentRole = role;
|
|
2567
|
+
currentParts.push({ text: msg.content });
|
|
2568
|
+
}
|
|
2569
|
+
pushCurrent();
|
|
2570
|
+
return gemini;
|
|
2571
|
+
}
|
|
2572
|
+
async function convertVisionMessagesToGeminiFormat(messages, options = {}) {
|
|
2573
|
+
const imageFetcher = options.imageFetcher ?? ChatServiceHttpClient.get;
|
|
2574
|
+
const encodeBlob = options.blobToBase64 ?? blobToBase64;
|
|
2575
|
+
const geminiMessages = [];
|
|
2576
|
+
let currentRole = null;
|
|
2577
|
+
let currentParts = [];
|
|
2578
|
+
for (const msg of messages) {
|
|
2579
|
+
const role = mapRoleToGemini(msg.role);
|
|
2580
|
+
if (msg.tool_calls) {
|
|
2581
|
+
for (const call of msg.tool_calls) {
|
|
2582
|
+
geminiMessages.push({
|
|
2583
|
+
role: "model",
|
|
2584
|
+
parts: [
|
|
2585
|
+
{
|
|
2586
|
+
functionCall: {
|
|
2587
|
+
name: call.function.name,
|
|
2588
|
+
args: JSON.parse(call.function.arguments || "{}")
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
]
|
|
2592
|
+
});
|
|
2593
|
+
}
|
|
2594
|
+
continue;
|
|
2595
|
+
}
|
|
2596
|
+
if (msg.role === "tool") {
|
|
2597
|
+
const funcName = msg.name ?? options.callIdMap?.get(msg.tool_call_id) ?? "result";
|
|
2598
|
+
geminiMessages.push({
|
|
2599
|
+
role: "user",
|
|
2600
|
+
parts: [
|
|
2601
|
+
{
|
|
2602
|
+
functionResponse: {
|
|
2603
|
+
name: funcName,
|
|
2604
|
+
response: normalizeToolResult(
|
|
2605
|
+
safeJsonParse(msg.content)
|
|
2606
|
+
)
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
]
|
|
2610
|
+
});
|
|
2611
|
+
continue;
|
|
2612
|
+
}
|
|
2613
|
+
if (role !== currentRole && currentParts.length > 0) {
|
|
2614
|
+
geminiMessages.push({
|
|
2615
|
+
role: currentRole,
|
|
2616
|
+
parts: [...currentParts]
|
|
2617
|
+
});
|
|
2618
|
+
currentParts = [];
|
|
2619
|
+
}
|
|
2620
|
+
currentRole = role;
|
|
2621
|
+
if (typeof msg.content === "string") {
|
|
2622
|
+
currentParts.push({ text: msg.content });
|
|
2623
|
+
} else if (Array.isArray(msg.content)) {
|
|
2624
|
+
for (const block of msg.content) {
|
|
2625
|
+
if (block.type === "text") {
|
|
2626
|
+
currentParts.push({ text: block.text });
|
|
2627
|
+
} else if (block.type === "image_url") {
|
|
2628
|
+
try {
|
|
2629
|
+
const imageResponse = await imageFetcher(block.image_url.url);
|
|
2630
|
+
const imageBlob = await imageResponse.blob();
|
|
2631
|
+
const base64Data = await encodeBlob(imageBlob);
|
|
2632
|
+
currentParts.push({
|
|
2633
|
+
inlineData: {
|
|
2634
|
+
mimeType: imageBlob.type || "image/jpeg",
|
|
2635
|
+
data: base64Data.split(",")[1]
|
|
2636
|
+
}
|
|
2637
|
+
});
|
|
2638
|
+
} catch (error) {
|
|
2639
|
+
console.error("Error processing image:", error);
|
|
2640
|
+
throw new Error(`Failed to process image: ${error.message}`);
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
if (currentRole && currentParts.length > 0) {
|
|
2647
|
+
geminiMessages.push({
|
|
2648
|
+
role: currentRole,
|
|
2649
|
+
parts: [...currentParts]
|
|
2650
|
+
});
|
|
2651
|
+
}
|
|
2652
|
+
return geminiMessages;
|
|
2653
|
+
}
|
|
2654
|
+
function safeJsonParse(str) {
|
|
2655
|
+
try {
|
|
2656
|
+
return JSON.parse(str);
|
|
2657
|
+
} catch {
|
|
2658
|
+
return str;
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
function normalizeToolResult(val) {
|
|
2662
|
+
if (val === null) return { content: null };
|
|
2663
|
+
if (typeof val === "object") return val;
|
|
2664
|
+
return { content: val };
|
|
2665
|
+
}
|
|
2666
|
+
function blobToBase64(blob) {
|
|
2667
|
+
return new Promise((resolve, reject) => {
|
|
2668
|
+
const reader = new FileReader();
|
|
2669
|
+
reader.onloadend = () => resolve(reader.result);
|
|
2670
|
+
reader.onerror = reject;
|
|
2671
|
+
reader.readAsDataURL(blob);
|
|
2672
|
+
});
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
// src/services/providers/gemini/geminiToolAdapter.ts
|
|
2676
|
+
function buildGeminiToolDeclarations(tools, mcpToolSchemas) {
|
|
2677
|
+
return [...tools, ...mcpToolSchemas].map((tool) => ({
|
|
2678
|
+
name: tool.name,
|
|
2679
|
+
description: tool.description,
|
|
2680
|
+
parameters: tool.parameters
|
|
2681
|
+
}));
|
|
2682
|
+
}
|
|
2683
|
+
function buildGeminiToolConfig(tools, mcpToolSchemas) {
|
|
2684
|
+
const functionDeclarations = buildGeminiToolDeclarations(
|
|
2685
|
+
tools,
|
|
2686
|
+
mcpToolSchemas
|
|
2687
|
+
);
|
|
2688
|
+
if (functionDeclarations.length === 0) {
|
|
2689
|
+
return void 0;
|
|
2690
|
+
}
|
|
2691
|
+
return {
|
|
2692
|
+
tools: [
|
|
2693
|
+
{
|
|
2694
|
+
functionDeclarations
|
|
2695
|
+
}
|
|
2696
|
+
],
|
|
2697
|
+
toolConfig: {
|
|
2698
|
+
functionCallingConfig: {
|
|
2699
|
+
mode: "AUTO"
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
};
|
|
2703
|
+
}
|
|
2704
|
+
function createFallbackMCPToolSchemas(servers) {
|
|
2705
|
+
return servers.map((server) => ({
|
|
2706
|
+
name: `mcp_${server.name}_search`,
|
|
2707
|
+
description: `Search using ${server.name} MCP server (fallback)`,
|
|
2708
|
+
parameters: {
|
|
2709
|
+
type: "object",
|
|
2710
|
+
properties: {
|
|
2711
|
+
query: {
|
|
2712
|
+
type: "string",
|
|
2713
|
+
description: "Search query"
|
|
2714
|
+
}
|
|
2715
|
+
},
|
|
2716
|
+
required: ["query"]
|
|
2717
|
+
}
|
|
2718
|
+
}));
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2498
2721
|
// src/services/providers/gemini/GeminiChatService.ts
|
|
2499
2722
|
var GeminiChatService = class {
|
|
2500
2723
|
/**
|
|
@@ -2524,21 +2747,6 @@ If it's in another language, summarize in that language.
|
|
|
2524
2747
|
this.tools = tools;
|
|
2525
2748
|
this.mcpServers = mcpServers;
|
|
2526
2749
|
}
|
|
2527
|
-
/* ────────────────────────────────── */
|
|
2528
|
-
/* Utilities */
|
|
2529
|
-
/* ────────────────────────────────── */
|
|
2530
|
-
safeJsonParse(str) {
|
|
2531
|
-
try {
|
|
2532
|
-
return JSON.parse(str);
|
|
2533
|
-
} catch {
|
|
2534
|
-
return str;
|
|
2535
|
-
}
|
|
2536
|
-
}
|
|
2537
|
-
normalizeToolResult(val) {
|
|
2538
|
-
if (val === null) return { content: null };
|
|
2539
|
-
if (typeof val === "object") return val;
|
|
2540
|
-
return { content: val };
|
|
2541
|
-
}
|
|
2542
2750
|
isGemma4Model(model) {
|
|
2543
2751
|
return /^gemma-4-/.test(model);
|
|
2544
2752
|
}
|
|
@@ -2642,20 +2850,7 @@ If it's in another language, summarize in that language.
|
|
|
2642
2850
|
this.mcpSchemasInitialized = true;
|
|
2643
2851
|
} catch (error) {
|
|
2644
2852
|
console.warn("Failed to initialize MCP schemas, using fallback:", error);
|
|
2645
|
-
this.mcpToolSchemas = this.mcpServers
|
|
2646
|
-
name: `mcp_${server.name}_search`,
|
|
2647
|
-
description: `Search using ${server.name} MCP server (fallback)`,
|
|
2648
|
-
parameters: {
|
|
2649
|
-
type: "object",
|
|
2650
|
-
properties: {
|
|
2651
|
-
query: {
|
|
2652
|
-
type: "string",
|
|
2653
|
-
description: "Search query"
|
|
2654
|
-
}
|
|
2655
|
-
},
|
|
2656
|
-
required: ["query"]
|
|
2657
|
-
}
|
|
2658
|
-
}));
|
|
2853
|
+
this.mcpToolSchemas = createFallbackMCPToolSchemas(this.mcpServers);
|
|
2659
2854
|
this.mcpSchemasInitialized = true;
|
|
2660
2855
|
}
|
|
2661
2856
|
}
|
|
@@ -2715,64 +2910,6 @@ If it's in another language, summarize in that language.
|
|
|
2715
2910
|
}
|
|
2716
2911
|
}
|
|
2717
2912
|
/* ────────────────────────────────── */
|
|
2718
|
-
/* OpenAI → Gemini conversion */
|
|
2719
|
-
/* ────────────────────────────────── */
|
|
2720
|
-
convertMessagesToGeminiFormat(messages) {
|
|
2721
|
-
const gemini = [];
|
|
2722
|
-
let currentRole = null;
|
|
2723
|
-
let currentParts = [];
|
|
2724
|
-
const pushCurrent = () => {
|
|
2725
|
-
if (currentRole && currentParts.length) {
|
|
2726
|
-
gemini.push({ role: currentRole, parts: [...currentParts] });
|
|
2727
|
-
currentParts = [];
|
|
2728
|
-
}
|
|
2729
|
-
};
|
|
2730
|
-
for (const msg of messages) {
|
|
2731
|
-
const role = this.mapRoleToGemini(msg.role);
|
|
2732
|
-
if (msg.tool_calls) {
|
|
2733
|
-
pushCurrent();
|
|
2734
|
-
for (const call of msg.tool_calls) {
|
|
2735
|
-
this.callIdMap.set(call.id, call.function.name);
|
|
2736
|
-
gemini.push({
|
|
2737
|
-
role: "model",
|
|
2738
|
-
parts: [
|
|
2739
|
-
{
|
|
2740
|
-
functionCall: {
|
|
2741
|
-
name: call.function.name,
|
|
2742
|
-
args: JSON.parse(call.function.arguments || "{}")
|
|
2743
|
-
}
|
|
2744
|
-
}
|
|
2745
|
-
]
|
|
2746
|
-
});
|
|
2747
|
-
}
|
|
2748
|
-
continue;
|
|
2749
|
-
}
|
|
2750
|
-
if (msg.role === "tool") {
|
|
2751
|
-
pushCurrent();
|
|
2752
|
-
const funcName = msg.name ?? this.callIdMap.get(msg.tool_call_id) ?? "result";
|
|
2753
|
-
gemini.push({
|
|
2754
|
-
role: "user",
|
|
2755
|
-
parts: [
|
|
2756
|
-
{
|
|
2757
|
-
functionResponse: {
|
|
2758
|
-
name: funcName,
|
|
2759
|
-
response: this.normalizeToolResult(
|
|
2760
|
-
this.safeJsonParse(msg.content)
|
|
2761
|
-
)
|
|
2762
|
-
}
|
|
2763
|
-
}
|
|
2764
|
-
]
|
|
2765
|
-
});
|
|
2766
|
-
continue;
|
|
2767
|
-
}
|
|
2768
|
-
if (role !== currentRole) pushCurrent();
|
|
2769
|
-
currentRole = role;
|
|
2770
|
-
currentParts.push({ text: msg.content });
|
|
2771
|
-
}
|
|
2772
|
-
pushCurrent();
|
|
2773
|
-
return gemini;
|
|
2774
|
-
}
|
|
2775
|
-
/* ────────────────────────────────── */
|
|
2776
2913
|
/* HTTP call */
|
|
2777
2914
|
/* ────────────────────────────────── */
|
|
2778
2915
|
async callGemini(messages, model, stream = false, maxTokens) {
|
|
@@ -2781,9 +2918,14 @@ If it's in another language, summarize in that language.
|
|
|
2781
2918
|
(b) => b?.type === "image_url" || b?.inlineData
|
|
2782
2919
|
)
|
|
2783
2920
|
);
|
|
2784
|
-
const contents = hasVision ? await
|
|
2785
|
-
messages
|
|
2786
|
-
|
|
2921
|
+
const contents = hasVision ? await convertVisionMessagesToGeminiFormat(
|
|
2922
|
+
messages,
|
|
2923
|
+
{
|
|
2924
|
+
callIdMap: this.callIdMap
|
|
2925
|
+
}
|
|
2926
|
+
) : convertMessagesToGeminiFormat(messages, {
|
|
2927
|
+
callIdMap: this.callIdMap
|
|
2928
|
+
});
|
|
2787
2929
|
const body = {
|
|
2788
2930
|
contents,
|
|
2789
2931
|
generationConfig: {
|
|
@@ -2796,37 +2938,18 @@ If it's in another language, summarize in that language.
|
|
|
2796
2938
|
thinkingLevel: "MINIMAL"
|
|
2797
2939
|
};
|
|
2798
2940
|
}
|
|
2799
|
-
|
|
2800
|
-
if (this.tools.length > 0) {
|
|
2801
|
-
allToolDeclarations.push(
|
|
2802
|
-
...this.tools.map((t) => ({
|
|
2803
|
-
name: t.name,
|
|
2804
|
-
description: t.description,
|
|
2805
|
-
parameters: t.parameters
|
|
2806
|
-
}))
|
|
2807
|
-
);
|
|
2808
|
-
}
|
|
2941
|
+
let activeMCPToolSchemas = [];
|
|
2809
2942
|
if (this.mcpServers.length > 0) {
|
|
2810
2943
|
try {
|
|
2811
2944
|
await this.initializeMCPSchemas();
|
|
2812
|
-
|
|
2813
|
-
...this.mcpToolSchemas.map((t) => ({
|
|
2814
|
-
name: t.name,
|
|
2815
|
-
description: t.description,
|
|
2816
|
-
parameters: t.parameters
|
|
2817
|
-
}))
|
|
2818
|
-
);
|
|
2945
|
+
activeMCPToolSchemas = this.mcpToolSchemas;
|
|
2819
2946
|
} catch (error) {
|
|
2820
2947
|
console.warn("MCP initialization failed, skipping MCP tools:", error);
|
|
2821
2948
|
}
|
|
2822
2949
|
}
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
functionDeclarations: allToolDeclarations
|
|
2827
|
-
}
|
|
2828
|
-
];
|
|
2829
|
-
body.toolConfig = { functionCallingConfig: { mode: "AUTO" } };
|
|
2950
|
+
const toolConfig = buildGeminiToolConfig(this.tools, activeMCPToolSchemas);
|
|
2951
|
+
if (toolConfig) {
|
|
2952
|
+
Object.assign(body, toolConfig);
|
|
2830
2953
|
}
|
|
2831
2954
|
const fetchOnce = async (ver, payload) => {
|
|
2832
2955
|
const fn = stream ? "streamGenerateContent" : "generateContent";
|
|
@@ -2863,125 +2986,6 @@ If it's in another language, summarize in that language.
|
|
|
2863
2986
|
throw error;
|
|
2864
2987
|
}
|
|
2865
2988
|
}
|
|
2866
|
-
/**
|
|
2867
|
-
* Convert AITuber OnAir vision messages to Gemini format
|
|
2868
|
-
* @param messages Array of vision messages
|
|
2869
|
-
* @returns Gemini formatted vision messages
|
|
2870
|
-
*/
|
|
2871
|
-
async convertVisionMessagesToGeminiFormat(messages) {
|
|
2872
|
-
const geminiMessages = [];
|
|
2873
|
-
let currentRole = null;
|
|
2874
|
-
let currentParts = [];
|
|
2875
|
-
for (const msg of messages) {
|
|
2876
|
-
const role = this.mapRoleToGemini(msg.role);
|
|
2877
|
-
if (msg.tool_calls) {
|
|
2878
|
-
for (const call of msg.tool_calls) {
|
|
2879
|
-
geminiMessages.push({
|
|
2880
|
-
role: "model",
|
|
2881
|
-
parts: [
|
|
2882
|
-
{
|
|
2883
|
-
functionCall: {
|
|
2884
|
-
name: call.function.name,
|
|
2885
|
-
args: JSON.parse(call.function.arguments || "{}")
|
|
2886
|
-
}
|
|
2887
|
-
}
|
|
2888
|
-
]
|
|
2889
|
-
});
|
|
2890
|
-
}
|
|
2891
|
-
continue;
|
|
2892
|
-
}
|
|
2893
|
-
if (msg.role === "tool") {
|
|
2894
|
-
const funcName = msg.name ?? this.callIdMap.get(msg.tool_call_id) ?? "result";
|
|
2895
|
-
geminiMessages.push({
|
|
2896
|
-
role: "user",
|
|
2897
|
-
parts: [
|
|
2898
|
-
{
|
|
2899
|
-
functionResponse: {
|
|
2900
|
-
name: funcName,
|
|
2901
|
-
response: this.normalizeToolResult(
|
|
2902
|
-
this.safeJsonParse(msg.content)
|
|
2903
|
-
)
|
|
2904
|
-
}
|
|
2905
|
-
}
|
|
2906
|
-
]
|
|
2907
|
-
});
|
|
2908
|
-
continue;
|
|
2909
|
-
}
|
|
2910
|
-
if (role !== currentRole && currentParts.length > 0) {
|
|
2911
|
-
geminiMessages.push({
|
|
2912
|
-
role: currentRole,
|
|
2913
|
-
parts: [...currentParts]
|
|
2914
|
-
});
|
|
2915
|
-
currentParts = [];
|
|
2916
|
-
}
|
|
2917
|
-
currentRole = role;
|
|
2918
|
-
if (typeof msg.content === "string") {
|
|
2919
|
-
currentParts.push({ text: msg.content });
|
|
2920
|
-
} else if (Array.isArray(msg.content)) {
|
|
2921
|
-
for (const block of msg.content) {
|
|
2922
|
-
if (block.type === "text") {
|
|
2923
|
-
currentParts.push({ text: block.text });
|
|
2924
|
-
} else if (block.type === "image_url") {
|
|
2925
|
-
try {
|
|
2926
|
-
const imageResponse = await ChatServiceHttpClient.get(
|
|
2927
|
-
block.image_url.url
|
|
2928
|
-
);
|
|
2929
|
-
const imageBlob = await imageResponse.blob();
|
|
2930
|
-
const base64Data = await this.blobToBase64(imageBlob);
|
|
2931
|
-
currentParts.push({
|
|
2932
|
-
inlineData: {
|
|
2933
|
-
mimeType: imageBlob.type || "image/jpeg",
|
|
2934
|
-
data: base64Data.split(",")[1]
|
|
2935
|
-
// Remove the "data:image/jpeg;base64," prefix
|
|
2936
|
-
}
|
|
2937
|
-
});
|
|
2938
|
-
} catch (error) {
|
|
2939
|
-
console.error("Error processing image:", error);
|
|
2940
|
-
throw new Error(`Failed to process image: ${error.message}`);
|
|
2941
|
-
}
|
|
2942
|
-
}
|
|
2943
|
-
}
|
|
2944
|
-
}
|
|
2945
|
-
}
|
|
2946
|
-
if (currentRole && currentParts.length > 0) {
|
|
2947
|
-
geminiMessages.push({
|
|
2948
|
-
role: currentRole,
|
|
2949
|
-
parts: [...currentParts]
|
|
2950
|
-
});
|
|
2951
|
-
}
|
|
2952
|
-
return geminiMessages;
|
|
2953
|
-
}
|
|
2954
|
-
/**
|
|
2955
|
-
* Convert Blob to Base64 string
|
|
2956
|
-
* @param blob Image blob
|
|
2957
|
-
* @returns Promise with base64 encoded string
|
|
2958
|
-
*/
|
|
2959
|
-
blobToBase64(blob) {
|
|
2960
|
-
return new Promise((resolve, reject) => {
|
|
2961
|
-
const reader = new FileReader();
|
|
2962
|
-
reader.onloadend = () => resolve(reader.result);
|
|
2963
|
-
reader.onerror = reject;
|
|
2964
|
-
reader.readAsDataURL(blob);
|
|
2965
|
-
});
|
|
2966
|
-
}
|
|
2967
|
-
/**
|
|
2968
|
-
* Map AITuber OnAir roles to Gemini roles
|
|
2969
|
-
* @param role AITuber OnAir role
|
|
2970
|
-
* @returns Gemini role
|
|
2971
|
-
*/
|
|
2972
|
-
mapRoleToGemini(role) {
|
|
2973
|
-
switch (role) {
|
|
2974
|
-
case "system":
|
|
2975
|
-
return "model";
|
|
2976
|
-
// Gemini uses 'model' for system messages
|
|
2977
|
-
case "user":
|
|
2978
|
-
return "user";
|
|
2979
|
-
case "assistant":
|
|
2980
|
-
return "model";
|
|
2981
|
-
default:
|
|
2982
|
-
return "user";
|
|
2983
|
-
}
|
|
2984
|
-
}
|
|
2985
2989
|
/* ────────────────────────────────────────────────────────── */
|
|
2986
2990
|
/* Convert NDJSON stream to common format */
|
|
2987
2991
|
/* ────────────────────────────────────────────────────────── */
|