@probeo/anymodel 0.3.1 → 0.5.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 +27 -3
- package/dist/cli.cjs +556 -18
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +556 -18
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +565 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +562 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -485,6 +485,25 @@ var Router = class {
|
|
|
485
485
|
}
|
|
486
486
|
};
|
|
487
487
|
|
|
488
|
+
// src/utils/fetch-with-timeout.ts
|
|
489
|
+
var _defaultTimeout = 12e4;
|
|
490
|
+
var _flexTimeout = 6e5;
|
|
491
|
+
function setDefaultTimeout(ms) {
|
|
492
|
+
_defaultTimeout = ms;
|
|
493
|
+
}
|
|
494
|
+
function getFlexTimeout() {
|
|
495
|
+
return _flexTimeout;
|
|
496
|
+
}
|
|
497
|
+
function fetchWithTimeout(url, init, timeoutMs) {
|
|
498
|
+
const ms = timeoutMs ?? _defaultTimeout;
|
|
499
|
+
const signal = AbortSignal.timeout(ms);
|
|
500
|
+
if (init?.signal) {
|
|
501
|
+
const combined = AbortSignal.any([signal, init.signal]);
|
|
502
|
+
return fetch(url, { ...init, signal: combined });
|
|
503
|
+
}
|
|
504
|
+
return fetch(url, { ...init, signal });
|
|
505
|
+
}
|
|
506
|
+
|
|
488
507
|
// src/providers/openai.ts
|
|
489
508
|
var OPENAI_API_BASE = "https://api.openai.com/v1";
|
|
490
509
|
var SUPPORTED_PARAMS = /* @__PURE__ */ new Set([
|
|
@@ -502,19 +521,20 @@ var SUPPORTED_PARAMS = /* @__PURE__ */ new Set([
|
|
|
502
521
|
"tools",
|
|
503
522
|
"tool_choice",
|
|
504
523
|
"user",
|
|
505
|
-
"logit_bias"
|
|
524
|
+
"logit_bias",
|
|
525
|
+
"service_tier"
|
|
506
526
|
]);
|
|
507
527
|
function createOpenAIAdapter(apiKey, baseURL) {
|
|
508
528
|
const base = baseURL || OPENAI_API_BASE;
|
|
509
|
-
async function makeRequest(path2, body, method = "POST") {
|
|
510
|
-
const res = await
|
|
529
|
+
async function makeRequest(path2, body, method = "POST", timeoutMs) {
|
|
530
|
+
const res = await fetchWithTimeout(`${base}${path2}`, {
|
|
511
531
|
method,
|
|
512
532
|
headers: {
|
|
513
533
|
"Content-Type": "application/json",
|
|
514
534
|
"Authorization": `Bearer ${apiKey}`
|
|
515
535
|
},
|
|
516
536
|
body: body ? JSON.stringify(body) : void 0
|
|
517
|
-
});
|
|
537
|
+
}, timeoutMs);
|
|
518
538
|
if (!res.ok) {
|
|
519
539
|
let errorBody;
|
|
520
540
|
try {
|
|
@@ -562,6 +582,7 @@ function createOpenAIAdapter(apiKey, baseURL) {
|
|
|
562
582
|
if (request.tools !== void 0) body.tools = request.tools;
|
|
563
583
|
if (request.tool_choice !== void 0) body.tool_choice = request.tool_choice;
|
|
564
584
|
if (request.user !== void 0) body.user = request.user;
|
|
585
|
+
if (request.service_tier !== void 0) body.service_tier = request.service_tier;
|
|
565
586
|
return body;
|
|
566
587
|
}
|
|
567
588
|
const adapter = {
|
|
@@ -663,13 +684,15 @@ function createOpenAIAdapter(apiKey, baseURL) {
|
|
|
663
684
|
},
|
|
664
685
|
async sendRequest(request) {
|
|
665
686
|
const body = buildRequestBody(request);
|
|
666
|
-
const
|
|
687
|
+
const timeout = request.service_tier === "flex" ? getFlexTimeout() : void 0;
|
|
688
|
+
const res = await makeRequest("/chat/completions", body, "POST", timeout);
|
|
667
689
|
const json = await res.json();
|
|
668
690
|
return adapter.translateResponse(json);
|
|
669
691
|
},
|
|
670
692
|
async sendStreamingRequest(request) {
|
|
671
693
|
const body = buildRequestBody({ ...request, stream: true });
|
|
672
|
-
const
|
|
694
|
+
const timeout = request.service_tier === "flex" ? getFlexTimeout() : void 0;
|
|
695
|
+
const res = await makeRequest("/chat/completions", body, "POST", timeout);
|
|
673
696
|
if (!res.body) {
|
|
674
697
|
throw new AnyModelError(502, "No response body for streaming request", {
|
|
675
698
|
provider_name: "openai"
|
|
@@ -716,7 +739,7 @@ var FALLBACK_MODELS = [
|
|
|
716
739
|
];
|
|
717
740
|
function createAnthropicAdapter(apiKey) {
|
|
718
741
|
async function makeRequest(path2, body, stream = false) {
|
|
719
|
-
const res = await
|
|
742
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE}${path2}`, {
|
|
720
743
|
method: "POST",
|
|
721
744
|
headers: {
|
|
722
745
|
"Content-Type": "application/json",
|
|
@@ -973,7 +996,7 @@ ${body.system}` : jsonInstruction;
|
|
|
973
996
|
},
|
|
974
997
|
async listModels() {
|
|
975
998
|
try {
|
|
976
|
-
const res = await
|
|
999
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE}/models`, {
|
|
977
1000
|
method: "GET",
|
|
978
1001
|
headers: {
|
|
979
1002
|
"x-api-key": apiKey,
|
|
@@ -1258,7 +1281,7 @@ function createGoogleAdapter(apiKey) {
|
|
|
1258
1281
|
},
|
|
1259
1282
|
async listModels() {
|
|
1260
1283
|
try {
|
|
1261
|
-
const res = await
|
|
1284
|
+
const res = await fetchWithTimeout(`${GEMINI_API_BASE}/models?key=${apiKey}`);
|
|
1262
1285
|
if (!res.ok) return FALLBACK_MODELS2;
|
|
1263
1286
|
const data = await res.json();
|
|
1264
1287
|
const models = data.models || [];
|
|
@@ -1293,12 +1316,12 @@ function createGoogleAdapter(apiKey) {
|
|
|
1293
1316
|
return SUPPORTED_PARAMS3.has(param);
|
|
1294
1317
|
},
|
|
1295
1318
|
supportsBatch() {
|
|
1296
|
-
return
|
|
1319
|
+
return true;
|
|
1297
1320
|
},
|
|
1298
1321
|
async sendRequest(request) {
|
|
1299
1322
|
const body = translateRequest(request);
|
|
1300
1323
|
const url = getModelEndpoint(request.model, false);
|
|
1301
|
-
const res = await
|
|
1324
|
+
const res = await fetchWithTimeout(url, {
|
|
1302
1325
|
method: "POST",
|
|
1303
1326
|
headers: { "Content-Type": "application/json" },
|
|
1304
1327
|
body: JSON.stringify(body)
|
|
@@ -1321,7 +1344,7 @@ function createGoogleAdapter(apiKey) {
|
|
|
1321
1344
|
async sendStreamingRequest(request) {
|
|
1322
1345
|
const body = translateRequest(request);
|
|
1323
1346
|
const url = getModelEndpoint(request.model, true);
|
|
1324
|
-
const res = await
|
|
1347
|
+
const res = await fetchWithTimeout(url, {
|
|
1325
1348
|
method: "POST",
|
|
1326
1349
|
headers: { "Content-Type": "application/json" },
|
|
1327
1350
|
body: JSON.stringify(body)
|
|
@@ -1347,6 +1370,190 @@ function createGoogleAdapter(apiKey) {
|
|
|
1347
1370
|
return adapter;
|
|
1348
1371
|
}
|
|
1349
1372
|
|
|
1373
|
+
// src/providers/perplexity.ts
|
|
1374
|
+
var PERPLEXITY_API_BASE = "https://api.perplexity.ai";
|
|
1375
|
+
var SUPPORTED_PARAMS4 = /* @__PURE__ */ new Set([
|
|
1376
|
+
"temperature",
|
|
1377
|
+
"max_tokens",
|
|
1378
|
+
"top_p",
|
|
1379
|
+
"frequency_penalty",
|
|
1380
|
+
"presence_penalty",
|
|
1381
|
+
"stream",
|
|
1382
|
+
"stop",
|
|
1383
|
+
"response_format",
|
|
1384
|
+
"tools",
|
|
1385
|
+
"tool_choice"
|
|
1386
|
+
]);
|
|
1387
|
+
var MODELS = [
|
|
1388
|
+
{ id: "sonar", name: "Sonar", context: 128e3, maxOutput: 4096, modality: "text->text", inputModalities: ["text"] },
|
|
1389
|
+
{ id: "sonar-pro", name: "Sonar Pro", context: 2e5, maxOutput: 8192, modality: "text->text", inputModalities: ["text"] },
|
|
1390
|
+
{ id: "sonar-reasoning", name: "Sonar Reasoning", context: 128e3, maxOutput: 8192, modality: "text->text", inputModalities: ["text"] },
|
|
1391
|
+
{ id: "sonar-reasoning-pro", name: "Sonar Reasoning Pro", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] },
|
|
1392
|
+
{ id: "sonar-deep-research", name: "Sonar Deep Research", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] },
|
|
1393
|
+
{ id: "r1-1776", name: "R1 1776", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] }
|
|
1394
|
+
];
|
|
1395
|
+
function createPerplexityAdapter(apiKey) {
|
|
1396
|
+
async function makeRequest(path2, body, method = "POST") {
|
|
1397
|
+
const res = await fetchWithTimeout(`${PERPLEXITY_API_BASE}${path2}`, {
|
|
1398
|
+
method,
|
|
1399
|
+
headers: {
|
|
1400
|
+
"Content-Type": "application/json",
|
|
1401
|
+
"Authorization": `Bearer ${apiKey}`
|
|
1402
|
+
},
|
|
1403
|
+
body: body ? JSON.stringify(body) : void 0
|
|
1404
|
+
});
|
|
1405
|
+
if (!res.ok) {
|
|
1406
|
+
let errorBody;
|
|
1407
|
+
try {
|
|
1408
|
+
errorBody = await res.json();
|
|
1409
|
+
} catch {
|
|
1410
|
+
errorBody = { message: res.statusText };
|
|
1411
|
+
}
|
|
1412
|
+
const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
|
|
1413
|
+
throw new AnyModelError(mapErrorCode(res.status), msg, {
|
|
1414
|
+
provider_name: "perplexity",
|
|
1415
|
+
raw: errorBody
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
return res;
|
|
1419
|
+
}
|
|
1420
|
+
function mapErrorCode(status) {
|
|
1421
|
+
if (status === 401 || status === 403) return 401;
|
|
1422
|
+
if (status === 429) return 429;
|
|
1423
|
+
if (status === 400 || status === 422) return 400;
|
|
1424
|
+
if (status >= 500) return 502;
|
|
1425
|
+
return status;
|
|
1426
|
+
}
|
|
1427
|
+
function rePrefixId(id) {
|
|
1428
|
+
if (id && id.startsWith("chatcmpl-")) {
|
|
1429
|
+
return `gen-${id.substring(9)}`;
|
|
1430
|
+
}
|
|
1431
|
+
return id.startsWith("gen-") ? id : `gen-${id}`;
|
|
1432
|
+
}
|
|
1433
|
+
function buildRequestBody(request) {
|
|
1434
|
+
const body = {
|
|
1435
|
+
model: request.model,
|
|
1436
|
+
messages: request.messages
|
|
1437
|
+
};
|
|
1438
|
+
if (request.temperature !== void 0) body.temperature = request.temperature;
|
|
1439
|
+
if (request.max_tokens !== void 0) body.max_tokens = request.max_tokens;
|
|
1440
|
+
if (request.top_p !== void 0) body.top_p = request.top_p;
|
|
1441
|
+
if (request.frequency_penalty !== void 0) body.frequency_penalty = request.frequency_penalty;
|
|
1442
|
+
if (request.presence_penalty !== void 0) body.presence_penalty = request.presence_penalty;
|
|
1443
|
+
if (request.stop !== void 0) body.stop = request.stop;
|
|
1444
|
+
if (request.stream !== void 0) body.stream = request.stream;
|
|
1445
|
+
if (request.response_format !== void 0) body.response_format = request.response_format;
|
|
1446
|
+
if (request.tools !== void 0) body.tools = request.tools;
|
|
1447
|
+
if (request.tool_choice !== void 0) body.tool_choice = request.tool_choice;
|
|
1448
|
+
return body;
|
|
1449
|
+
}
|
|
1450
|
+
const adapter = {
|
|
1451
|
+
name: "perplexity",
|
|
1452
|
+
translateRequest(request) {
|
|
1453
|
+
return buildRequestBody(request);
|
|
1454
|
+
},
|
|
1455
|
+
translateResponse(response) {
|
|
1456
|
+
const r = response;
|
|
1457
|
+
const result = {
|
|
1458
|
+
id: rePrefixId(r.id),
|
|
1459
|
+
object: "chat.completion",
|
|
1460
|
+
created: r.created,
|
|
1461
|
+
model: `perplexity/${r.model}`,
|
|
1462
|
+
choices: r.choices,
|
|
1463
|
+
usage: r.usage
|
|
1464
|
+
};
|
|
1465
|
+
if (r.citations && result.choices?.[0]?.message) {
|
|
1466
|
+
result.citations = r.citations;
|
|
1467
|
+
}
|
|
1468
|
+
return result;
|
|
1469
|
+
},
|
|
1470
|
+
async *translateStream(stream) {
|
|
1471
|
+
const reader = stream.getReader();
|
|
1472
|
+
const decoder = new TextDecoder();
|
|
1473
|
+
let buffer = "";
|
|
1474
|
+
try {
|
|
1475
|
+
while (true) {
|
|
1476
|
+
const { done, value } = await reader.read();
|
|
1477
|
+
if (done) break;
|
|
1478
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1479
|
+
const lines = buffer.split("\n");
|
|
1480
|
+
buffer = lines.pop() || "";
|
|
1481
|
+
for (const line of lines) {
|
|
1482
|
+
const trimmed = line.trim();
|
|
1483
|
+
if (!trimmed || trimmed.startsWith(":")) continue;
|
|
1484
|
+
if (trimmed === "data: [DONE]") return;
|
|
1485
|
+
if (trimmed.startsWith("data: ")) {
|
|
1486
|
+
const json = JSON.parse(trimmed.substring(6));
|
|
1487
|
+
json.id = rePrefixId(json.id);
|
|
1488
|
+
json.model = `perplexity/${json.model}`;
|
|
1489
|
+
yield json;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
} finally {
|
|
1494
|
+
reader.releaseLock();
|
|
1495
|
+
}
|
|
1496
|
+
},
|
|
1497
|
+
translateError(error) {
|
|
1498
|
+
if (error instanceof AnyModelError) {
|
|
1499
|
+
return { code: error.code, message: error.message, metadata: error.metadata };
|
|
1500
|
+
}
|
|
1501
|
+
const err = error;
|
|
1502
|
+
const status = err?.status || err?.code || 500;
|
|
1503
|
+
return {
|
|
1504
|
+
code: mapErrorCode(status),
|
|
1505
|
+
message: err?.message || "Unknown Perplexity error",
|
|
1506
|
+
metadata: { provider_name: "perplexity", raw: error }
|
|
1507
|
+
};
|
|
1508
|
+
},
|
|
1509
|
+
async listModels() {
|
|
1510
|
+
return MODELS.map((m) => ({
|
|
1511
|
+
id: `perplexity/${m.id}`,
|
|
1512
|
+
name: m.name,
|
|
1513
|
+
created: 0,
|
|
1514
|
+
description: "",
|
|
1515
|
+
context_length: m.context,
|
|
1516
|
+
pricing: { prompt: "0", completion: "0" },
|
|
1517
|
+
architecture: {
|
|
1518
|
+
modality: m.modality,
|
|
1519
|
+
input_modalities: m.inputModalities,
|
|
1520
|
+
output_modalities: ["text"],
|
|
1521
|
+
tokenizer: "unknown"
|
|
1522
|
+
},
|
|
1523
|
+
top_provider: {
|
|
1524
|
+
context_length: m.context,
|
|
1525
|
+
max_completion_tokens: m.maxOutput,
|
|
1526
|
+
is_moderated: false
|
|
1527
|
+
},
|
|
1528
|
+
supported_parameters: Array.from(SUPPORTED_PARAMS4)
|
|
1529
|
+
}));
|
|
1530
|
+
},
|
|
1531
|
+
supportsParameter(param) {
|
|
1532
|
+
return SUPPORTED_PARAMS4.has(param);
|
|
1533
|
+
},
|
|
1534
|
+
supportsBatch() {
|
|
1535
|
+
return false;
|
|
1536
|
+
},
|
|
1537
|
+
async sendRequest(request) {
|
|
1538
|
+
const body = buildRequestBody(request);
|
|
1539
|
+
const res = await makeRequest("/chat/completions", body);
|
|
1540
|
+
const json = await res.json();
|
|
1541
|
+
return adapter.translateResponse(json);
|
|
1542
|
+
},
|
|
1543
|
+
async sendStreamingRequest(request) {
|
|
1544
|
+
const body = buildRequestBody({ ...request, stream: true });
|
|
1545
|
+
const res = await makeRequest("/chat/completions", body);
|
|
1546
|
+
if (!res.body) {
|
|
1547
|
+
throw new AnyModelError(502, "No response body for streaming request", {
|
|
1548
|
+
provider_name: "perplexity"
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
return adapter.translateStream(res.body);
|
|
1552
|
+
}
|
|
1553
|
+
};
|
|
1554
|
+
return adapter;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1350
1557
|
// src/providers/custom.ts
|
|
1351
1558
|
function createCustomAdapter(name, config) {
|
|
1352
1559
|
const openaiAdapter = createOpenAIAdapter(config.apiKey || "", config.baseURL);
|
|
@@ -2059,6 +2266,51 @@ var BatchManager = class {
|
|
|
2059
2266
|
}
|
|
2060
2267
|
};
|
|
2061
2268
|
|
|
2269
|
+
// src/utils/token-estimate.ts
|
|
2270
|
+
var CHARS_PER_TOKEN2 = 4;
|
|
2271
|
+
var MODEL_LIMITS = [
|
|
2272
|
+
// OpenAI
|
|
2273
|
+
{ pattern: "gpt-4o-mini", limit: { contextLength: 128e3, maxCompletionTokens: 16384 } },
|
|
2274
|
+
{ pattern: "gpt-4o", limit: { contextLength: 128e3, maxCompletionTokens: 16384 } },
|
|
2275
|
+
{ pattern: "gpt-4-turbo", limit: { contextLength: 128e3, maxCompletionTokens: 4096 } },
|
|
2276
|
+
{ pattern: "gpt-3.5-turbo", limit: { contextLength: 16385, maxCompletionTokens: 4096 } },
|
|
2277
|
+
{ pattern: "o1", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2278
|
+
{ pattern: "o3", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2279
|
+
{ pattern: "o4-mini", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2280
|
+
// Anthropic
|
|
2281
|
+
{ pattern: "claude-opus-4", limit: { contextLength: 2e5, maxCompletionTokens: 32768 } },
|
|
2282
|
+
{ pattern: "claude-sonnet-4", limit: { contextLength: 2e5, maxCompletionTokens: 16384 } },
|
|
2283
|
+
{ pattern: "claude-haiku-4", limit: { contextLength: 2e5, maxCompletionTokens: 8192 } },
|
|
2284
|
+
{ pattern: "claude-3.5-sonnet", limit: { contextLength: 2e5, maxCompletionTokens: 8192 } },
|
|
2285
|
+
{ pattern: "claude-3-opus", limit: { contextLength: 2e5, maxCompletionTokens: 4096 } },
|
|
2286
|
+
// Google
|
|
2287
|
+
{ pattern: "gemini-2.5-pro", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2288
|
+
{ pattern: "gemini-2.5-flash", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2289
|
+
{ pattern: "gemini-2.0-flash", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2290
|
+
{ pattern: "gemini-1.5-pro", limit: { contextLength: 2097152, maxCompletionTokens: 8192 } },
|
|
2291
|
+
{ pattern: "gemini-1.5-flash", limit: { contextLength: 1048576, maxCompletionTokens: 8192 } }
|
|
2292
|
+
];
|
|
2293
|
+
var DEFAULT_LIMIT = { contextLength: 128e3, maxCompletionTokens: 4096 };
|
|
2294
|
+
function getModelLimits(model) {
|
|
2295
|
+
const bare = model.includes("/") ? model.slice(model.indexOf("/") + 1) : model;
|
|
2296
|
+
for (const entry of MODEL_LIMITS) {
|
|
2297
|
+
if (bare.startsWith(entry.pattern) || bare.includes(entry.pattern)) {
|
|
2298
|
+
return entry.limit;
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
return DEFAULT_LIMIT;
|
|
2302
|
+
}
|
|
2303
|
+
function resolveMaxTokens(model, messages, userMaxTokens) {
|
|
2304
|
+
if (userMaxTokens !== void 0) return userMaxTokens;
|
|
2305
|
+
const inputChars = JSON.stringify(messages).length;
|
|
2306
|
+
const estimatedInput = Math.ceil(inputChars / CHARS_PER_TOKEN2);
|
|
2307
|
+
const estimatedWithMargin = Math.ceil(estimatedInput * 1.05);
|
|
2308
|
+
const limits = getModelLimits(model);
|
|
2309
|
+
const available = limits.contextLength - estimatedWithMargin;
|
|
2310
|
+
const result = Math.min(limits.maxCompletionTokens, available);
|
|
2311
|
+
return Math.max(1, result);
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2062
2314
|
// src/providers/openai-batch.ts
|
|
2063
2315
|
var OPENAI_API_BASE2 = "https://api.openai.com/v1";
|
|
2064
2316
|
function createOpenAIBatchAdapter(apiKey) {
|
|
@@ -2073,7 +2325,7 @@ function createOpenAIBatchAdapter(apiKey) {
|
|
|
2073
2325
|
headers["Content-Type"] = "application/json";
|
|
2074
2326
|
fetchBody = JSON.stringify(options.body);
|
|
2075
2327
|
}
|
|
2076
|
-
const res = await
|
|
2328
|
+
const res = await fetchWithTimeout(`${OPENAI_API_BASE2}${path2}`, {
|
|
2077
2329
|
method: options.method || "GET",
|
|
2078
2330
|
headers,
|
|
2079
2331
|
body: fetchBody
|
|
@@ -2099,7 +2351,7 @@ function createOpenAIBatchAdapter(apiKey) {
|
|
|
2099
2351
|
model,
|
|
2100
2352
|
messages: req.messages
|
|
2101
2353
|
};
|
|
2102
|
-
|
|
2354
|
+
body.max_tokens = req.max_tokens !== void 0 ? req.max_tokens : resolveMaxTokens(model, req.messages);
|
|
2103
2355
|
if (req.temperature !== void 0) body.temperature = req.temperature;
|
|
2104
2356
|
if (req.top_p !== void 0) body.top_p = req.top_p;
|
|
2105
2357
|
if (req.stop !== void 0) body.stop = req.stop;
|
|
@@ -2258,7 +2510,7 @@ function createAnthropicBatchAdapter(apiKey) {
|
|
|
2258
2510
|
"anthropic-version": ANTHROPIC_VERSION2,
|
|
2259
2511
|
"Content-Type": "application/json"
|
|
2260
2512
|
};
|
|
2261
|
-
const res = await
|
|
2513
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE2}${path2}`, {
|
|
2262
2514
|
method: options.method || "GET",
|
|
2263
2515
|
headers,
|
|
2264
2516
|
body: options.body ? JSON.stringify(options.body) : void 0
|
|
@@ -2281,7 +2533,7 @@ function createAnthropicBatchAdapter(apiKey) {
|
|
|
2281
2533
|
function translateToAnthropicParams(model, req) {
|
|
2282
2534
|
const params = {
|
|
2283
2535
|
model,
|
|
2284
|
-
max_tokens: req.max_tokens || DEFAULT_MAX_TOKENS2
|
|
2536
|
+
max_tokens: resolveMaxTokens(model, req.messages, req.max_tokens || DEFAULT_MAX_TOKENS2)
|
|
2285
2537
|
};
|
|
2286
2538
|
const systemMessages = req.messages.filter((m) => m.role === "system");
|
|
2287
2539
|
const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
|
|
@@ -2455,6 +2707,284 @@ ${params.system}` : jsonInstruction;
|
|
|
2455
2707
|
};
|
|
2456
2708
|
}
|
|
2457
2709
|
|
|
2710
|
+
// src/providers/google-batch.ts
|
|
2711
|
+
var GEMINI_API_BASE2 = "https://generativelanguage.googleapis.com/v1beta";
|
|
2712
|
+
function createGoogleBatchAdapter(apiKey) {
|
|
2713
|
+
async function apiRequest(path2, options = {}) {
|
|
2714
|
+
const headers = {
|
|
2715
|
+
"Content-Type": "application/json",
|
|
2716
|
+
"x-goog-api-key": apiKey
|
|
2717
|
+
};
|
|
2718
|
+
const res = await fetchWithTimeout(`${GEMINI_API_BASE2}${path2}`, {
|
|
2719
|
+
method: options.method || "GET",
|
|
2720
|
+
headers,
|
|
2721
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
2722
|
+
});
|
|
2723
|
+
if (!res.ok) {
|
|
2724
|
+
let errorBody;
|
|
2725
|
+
try {
|
|
2726
|
+
errorBody = await res.json();
|
|
2727
|
+
} catch {
|
|
2728
|
+
errorBody = { message: res.statusText };
|
|
2729
|
+
}
|
|
2730
|
+
const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
|
|
2731
|
+
throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
|
|
2732
|
+
provider_name: "google",
|
|
2733
|
+
raw: errorBody
|
|
2734
|
+
});
|
|
2735
|
+
}
|
|
2736
|
+
return res;
|
|
2737
|
+
}
|
|
2738
|
+
function translateRequestToGemini(model, req) {
|
|
2739
|
+
const body = {};
|
|
2740
|
+
const systemMessages = req.messages.filter((m) => m.role === "system");
|
|
2741
|
+
const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
|
|
2742
|
+
if (systemMessages.length > 0) {
|
|
2743
|
+
body.systemInstruction = {
|
|
2744
|
+
parts: [{ text: systemMessages.map((m) => typeof m.content === "string" ? m.content : "").join("\n") }]
|
|
2745
|
+
};
|
|
2746
|
+
}
|
|
2747
|
+
body.contents = nonSystemMessages.map((m) => ({
|
|
2748
|
+
role: m.role === "assistant" ? "model" : "user",
|
|
2749
|
+
parts: typeof m.content === "string" ? [{ text: m.content }] : Array.isArray(m.content) ? m.content.map((p) => p.type === "text" ? { text: p.text } : { text: "" }) : [{ text: "" }]
|
|
2750
|
+
}));
|
|
2751
|
+
const generationConfig = {};
|
|
2752
|
+
if (req.temperature !== void 0) generationConfig.temperature = req.temperature;
|
|
2753
|
+
generationConfig.maxOutputTokens = req.max_tokens !== void 0 ? req.max_tokens : resolveMaxTokens(model, req.messages);
|
|
2754
|
+
if (req.top_p !== void 0) generationConfig.topP = req.top_p;
|
|
2755
|
+
if (req.top_k !== void 0) generationConfig.topK = req.top_k;
|
|
2756
|
+
if (req.stop !== void 0) {
|
|
2757
|
+
generationConfig.stopSequences = Array.isArray(req.stop) ? req.stop : [req.stop];
|
|
2758
|
+
}
|
|
2759
|
+
if (req.response_format) {
|
|
2760
|
+
if (req.response_format.type === "json_object") {
|
|
2761
|
+
generationConfig.responseMimeType = "application/json";
|
|
2762
|
+
} else if (req.response_format.type === "json_schema") {
|
|
2763
|
+
generationConfig.responseMimeType = "application/json";
|
|
2764
|
+
generationConfig.responseSchema = req.response_format.json_schema?.schema;
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
if (Object.keys(generationConfig).length > 0) {
|
|
2768
|
+
body.generationConfig = generationConfig;
|
|
2769
|
+
}
|
|
2770
|
+
if (req.tools && req.tools.length > 0) {
|
|
2771
|
+
body.tools = [{
|
|
2772
|
+
functionDeclarations: req.tools.map((t) => ({
|
|
2773
|
+
name: t.function.name,
|
|
2774
|
+
description: t.function.description || "",
|
|
2775
|
+
parameters: t.function.parameters || {}
|
|
2776
|
+
}))
|
|
2777
|
+
}];
|
|
2778
|
+
if (req.tool_choice) {
|
|
2779
|
+
if (req.tool_choice === "auto") {
|
|
2780
|
+
body.toolConfig = { functionCallingConfig: { mode: "AUTO" } };
|
|
2781
|
+
} else if (req.tool_choice === "required") {
|
|
2782
|
+
body.toolConfig = { functionCallingConfig: { mode: "ANY" } };
|
|
2783
|
+
} else if (req.tool_choice === "none") {
|
|
2784
|
+
body.toolConfig = { functionCallingConfig: { mode: "NONE" } };
|
|
2785
|
+
} else if (typeof req.tool_choice === "object") {
|
|
2786
|
+
body.toolConfig = {
|
|
2787
|
+
functionCallingConfig: {
|
|
2788
|
+
mode: "ANY",
|
|
2789
|
+
allowedFunctionNames: [req.tool_choice.function.name]
|
|
2790
|
+
}
|
|
2791
|
+
};
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
return body;
|
|
2796
|
+
}
|
|
2797
|
+
function mapFinishReason(reason) {
|
|
2798
|
+
switch (reason) {
|
|
2799
|
+
case "STOP":
|
|
2800
|
+
return "stop";
|
|
2801
|
+
case "MAX_TOKENS":
|
|
2802
|
+
return "length";
|
|
2803
|
+
case "SAFETY":
|
|
2804
|
+
return "content_filter";
|
|
2805
|
+
case "RECITATION":
|
|
2806
|
+
return "content_filter";
|
|
2807
|
+
default:
|
|
2808
|
+
return "stop";
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
function translateGeminiResponse(response, model) {
|
|
2812
|
+
const candidate = response.candidates?.[0];
|
|
2813
|
+
let content = "";
|
|
2814
|
+
const toolCalls = [];
|
|
2815
|
+
for (const part of candidate?.content?.parts || []) {
|
|
2816
|
+
if (part.text) {
|
|
2817
|
+
content += part.text;
|
|
2818
|
+
} else if (part.functionCall) {
|
|
2819
|
+
toolCalls.push({
|
|
2820
|
+
id: generateId("call"),
|
|
2821
|
+
type: "function",
|
|
2822
|
+
function: {
|
|
2823
|
+
name: part.functionCall.name,
|
|
2824
|
+
arguments: JSON.stringify(part.functionCall.args || {})
|
|
2825
|
+
}
|
|
2826
|
+
});
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
const message = { role: "assistant", content };
|
|
2830
|
+
if (toolCalls.length > 0) {
|
|
2831
|
+
message.tool_calls = toolCalls;
|
|
2832
|
+
}
|
|
2833
|
+
const finishReason = toolCalls.length > 0 ? "tool_calls" : mapFinishReason(candidate?.finishReason || "STOP");
|
|
2834
|
+
return {
|
|
2835
|
+
id: generateId(),
|
|
2836
|
+
object: "chat.completion",
|
|
2837
|
+
created: Math.floor(Date.now() / 1e3),
|
|
2838
|
+
model: `google/${model}`,
|
|
2839
|
+
choices: [{ index: 0, message, finish_reason: finishReason }],
|
|
2840
|
+
usage: {
|
|
2841
|
+
prompt_tokens: response.usageMetadata?.promptTokenCount || 0,
|
|
2842
|
+
completion_tokens: response.usageMetadata?.candidatesTokenCount || 0,
|
|
2843
|
+
total_tokens: response.usageMetadata?.totalTokenCount || 0
|
|
2844
|
+
}
|
|
2845
|
+
};
|
|
2846
|
+
}
|
|
2847
|
+
function mapBatchState(state) {
|
|
2848
|
+
switch (state) {
|
|
2849
|
+
case "JOB_STATE_PENDING":
|
|
2850
|
+
return "pending";
|
|
2851
|
+
case "JOB_STATE_RUNNING":
|
|
2852
|
+
return "processing";
|
|
2853
|
+
case "JOB_STATE_SUCCEEDED":
|
|
2854
|
+
return "completed";
|
|
2855
|
+
case "JOB_STATE_FAILED":
|
|
2856
|
+
return "failed";
|
|
2857
|
+
case "JOB_STATE_CANCELLED":
|
|
2858
|
+
return "cancelled";
|
|
2859
|
+
case "JOB_STATE_EXPIRED":
|
|
2860
|
+
return "failed";
|
|
2861
|
+
default:
|
|
2862
|
+
return "pending";
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
return {
|
|
2866
|
+
async createBatch(model, requests, _options) {
|
|
2867
|
+
const batchRequests = requests.map((req) => ({
|
|
2868
|
+
request: translateRequestToGemini(model, req),
|
|
2869
|
+
metadata: { key: req.custom_id }
|
|
2870
|
+
}));
|
|
2871
|
+
const res = await apiRequest(`/models/${model}:batchGenerateContent`, {
|
|
2872
|
+
method: "POST",
|
|
2873
|
+
body: {
|
|
2874
|
+
batch: {
|
|
2875
|
+
display_name: `anymodel-batch-${Date.now()}`,
|
|
2876
|
+
input_config: {
|
|
2877
|
+
requests: {
|
|
2878
|
+
requests: batchRequests
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
});
|
|
2884
|
+
const data = await res.json();
|
|
2885
|
+
const batchName = data.name || data.batch?.name;
|
|
2886
|
+
if (!batchName) {
|
|
2887
|
+
throw new AnyModelError(502, "No batch name in Google response", {
|
|
2888
|
+
provider_name: "google",
|
|
2889
|
+
raw: data
|
|
2890
|
+
});
|
|
2891
|
+
}
|
|
2892
|
+
return {
|
|
2893
|
+
providerBatchId: batchName,
|
|
2894
|
+
metadata: {
|
|
2895
|
+
model,
|
|
2896
|
+
total_requests: requests.length
|
|
2897
|
+
}
|
|
2898
|
+
};
|
|
2899
|
+
},
|
|
2900
|
+
async pollBatch(providerBatchId) {
|
|
2901
|
+
const res = await apiRequest(`/${providerBatchId}`);
|
|
2902
|
+
const data = await res.json();
|
|
2903
|
+
const state = data.state || "JOB_STATE_PENDING";
|
|
2904
|
+
const status = mapBatchState(state);
|
|
2905
|
+
const totalCount = data.totalCount || data.metadata?.total_requests || 0;
|
|
2906
|
+
const successCount = data.succeededCount || 0;
|
|
2907
|
+
const failedCount = data.failedCount || 0;
|
|
2908
|
+
return {
|
|
2909
|
+
status,
|
|
2910
|
+
total: totalCount || successCount + failedCount,
|
|
2911
|
+
completed: successCount,
|
|
2912
|
+
failed: failedCount
|
|
2913
|
+
};
|
|
2914
|
+
},
|
|
2915
|
+
async getBatchResults(providerBatchId) {
|
|
2916
|
+
const batchRes = await apiRequest(`/${providerBatchId}`);
|
|
2917
|
+
const batchData = await batchRes.json();
|
|
2918
|
+
const results = [];
|
|
2919
|
+
const model = batchData.metadata?.model || "unknown";
|
|
2920
|
+
if (batchData.response?.inlinedResponses) {
|
|
2921
|
+
for (const item of batchData.response.inlinedResponses) {
|
|
2922
|
+
const customId = item.metadata?.key || `request-${results.length}`;
|
|
2923
|
+
if (item.response) {
|
|
2924
|
+
results.push({
|
|
2925
|
+
custom_id: customId,
|
|
2926
|
+
status: "success",
|
|
2927
|
+
response: translateGeminiResponse(item.response, model),
|
|
2928
|
+
error: null
|
|
2929
|
+
});
|
|
2930
|
+
} else if (item.error) {
|
|
2931
|
+
results.push({
|
|
2932
|
+
custom_id: customId,
|
|
2933
|
+
status: "error",
|
|
2934
|
+
response: null,
|
|
2935
|
+
error: {
|
|
2936
|
+
code: item.error.code || 500,
|
|
2937
|
+
message: item.error.message || "Batch item failed"
|
|
2938
|
+
}
|
|
2939
|
+
});
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
return results;
|
|
2943
|
+
}
|
|
2944
|
+
const responsesFile = batchData.response?.responsesFileName || batchData.outputConfig?.file_name;
|
|
2945
|
+
if (responsesFile) {
|
|
2946
|
+
const downloadUrl = `${GEMINI_API_BASE2}/${responsesFile}:download?alt=media`;
|
|
2947
|
+
const fileRes = await fetchWithTimeout(downloadUrl, {
|
|
2948
|
+
headers: { "x-goog-api-key": apiKey }
|
|
2949
|
+
});
|
|
2950
|
+
if (!fileRes.ok) {
|
|
2951
|
+
throw new AnyModelError(502, "Failed to download batch results file", {
|
|
2952
|
+
provider_name: "google"
|
|
2953
|
+
});
|
|
2954
|
+
}
|
|
2955
|
+
const text = await fileRes.text();
|
|
2956
|
+
for (const line of text.trim().split("\n")) {
|
|
2957
|
+
if (!line) continue;
|
|
2958
|
+
const item = JSON.parse(line);
|
|
2959
|
+
const customId = item.key || item.metadata?.key || `request-${results.length}`;
|
|
2960
|
+
if (item.response) {
|
|
2961
|
+
results.push({
|
|
2962
|
+
custom_id: customId,
|
|
2963
|
+
status: "success",
|
|
2964
|
+
response: translateGeminiResponse(item.response, model),
|
|
2965
|
+
error: null
|
|
2966
|
+
});
|
|
2967
|
+
} else if (item.error) {
|
|
2968
|
+
results.push({
|
|
2969
|
+
custom_id: customId,
|
|
2970
|
+
status: "error",
|
|
2971
|
+
response: null,
|
|
2972
|
+
error: {
|
|
2973
|
+
code: item.error.code || 500,
|
|
2974
|
+
message: item.error.message || "Batch item failed"
|
|
2975
|
+
}
|
|
2976
|
+
});
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
return results;
|
|
2981
|
+
},
|
|
2982
|
+
async cancelBatch(providerBatchId) {
|
|
2983
|
+
await apiRequest(`/${providerBatchId}:cancel`, { method: "POST" });
|
|
2984
|
+
}
|
|
2985
|
+
};
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2458
2988
|
// src/client.ts
|
|
2459
2989
|
var AnyModel = class {
|
|
2460
2990
|
registry;
|
|
@@ -2470,6 +3000,7 @@ var AnyModel = class {
|
|
|
2470
3000
|
constructor(config = {}) {
|
|
2471
3001
|
this.config = resolveConfig(config);
|
|
2472
3002
|
this.registry = new ProviderRegistry();
|
|
3003
|
+
setDefaultTimeout((this.config.defaults?.timeout ?? 120) * 1e3);
|
|
2473
3004
|
if (this.config.io) {
|
|
2474
3005
|
configureFsIO(this.config.io);
|
|
2475
3006
|
}
|
|
@@ -2550,14 +3081,17 @@ var AnyModel = class {
|
|
|
2550
3081
|
if (googleKey) {
|
|
2551
3082
|
this.registry.register("google", createGoogleAdapter(googleKey));
|
|
2552
3083
|
}
|
|
3084
|
+
const perplexityKey = config.perplexity?.apiKey || process.env.PERPLEXITY_API_KEY;
|
|
3085
|
+
if (perplexityKey) {
|
|
3086
|
+
this.registry.register("perplexity", createPerplexityAdapter(perplexityKey));
|
|
3087
|
+
}
|
|
2553
3088
|
const builtinProviders = [
|
|
2554
3089
|
{ name: "mistral", baseURL: "https://api.mistral.ai/v1", configKey: "mistral", envVar: "MISTRAL_API_KEY" },
|
|
2555
3090
|
{ name: "groq", baseURL: "https://api.groq.com/openai/v1", configKey: "groq", envVar: "GROQ_API_KEY" },
|
|
2556
3091
|
{ name: "deepseek", baseURL: "https://api.deepseek.com", configKey: "deepseek", envVar: "DEEPSEEK_API_KEY" },
|
|
2557
3092
|
{ name: "xai", baseURL: "https://api.x.ai/v1", configKey: "xai", envVar: "XAI_API_KEY" },
|
|
2558
3093
|
{ name: "together", baseURL: "https://api.together.xyz/v1", configKey: "together", envVar: "TOGETHER_API_KEY" },
|
|
2559
|
-
{ name: "fireworks", baseURL: "https://api.fireworks.ai/inference/v1", configKey: "fireworks", envVar: "FIREWORKS_API_KEY" }
|
|
2560
|
-
{ name: "perplexity", baseURL: "https://api.perplexity.ai", configKey: "perplexity", envVar: "PERPLEXITY_API_KEY" }
|
|
3094
|
+
{ name: "fireworks", baseURL: "https://api.fireworks.ai/inference/v1", configKey: "fireworks", envVar: "FIREWORKS_API_KEY" }
|
|
2561
3095
|
];
|
|
2562
3096
|
for (const { name, baseURL, configKey, envVar } of builtinProviders) {
|
|
2563
3097
|
const providerConfig = config[configKey];
|
|
@@ -2587,6 +3121,10 @@ var AnyModel = class {
|
|
|
2587
3121
|
if (anthropicKey) {
|
|
2588
3122
|
this.batchManager.registerBatchAdapter("anthropic", createAnthropicBatchAdapter(anthropicKey));
|
|
2589
3123
|
}
|
|
3124
|
+
const googleKey = config.google?.apiKey || process.env.GOOGLE_API_KEY;
|
|
3125
|
+
if (googleKey) {
|
|
3126
|
+
this.batchManager.registerBatchAdapter("google", createGoogleBatchAdapter(googleKey));
|
|
3127
|
+
}
|
|
2590
3128
|
}
|
|
2591
3129
|
applyDefaults(request) {
|
|
2592
3130
|
const defaults = this.config.defaults;
|