@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/index.js
CHANGED
|
@@ -480,6 +480,25 @@ var Router = class {
|
|
|
480
480
|
}
|
|
481
481
|
};
|
|
482
482
|
|
|
483
|
+
// src/utils/fetch-with-timeout.ts
|
|
484
|
+
var _defaultTimeout = 12e4;
|
|
485
|
+
var _flexTimeout = 6e5;
|
|
486
|
+
function setDefaultTimeout(ms) {
|
|
487
|
+
_defaultTimeout = ms;
|
|
488
|
+
}
|
|
489
|
+
function getFlexTimeout() {
|
|
490
|
+
return _flexTimeout;
|
|
491
|
+
}
|
|
492
|
+
function fetchWithTimeout(url, init, timeoutMs) {
|
|
493
|
+
const ms = timeoutMs ?? _defaultTimeout;
|
|
494
|
+
const signal = AbortSignal.timeout(ms);
|
|
495
|
+
if (init?.signal) {
|
|
496
|
+
const combined = AbortSignal.any([signal, init.signal]);
|
|
497
|
+
return fetch(url, { ...init, signal: combined });
|
|
498
|
+
}
|
|
499
|
+
return fetch(url, { ...init, signal });
|
|
500
|
+
}
|
|
501
|
+
|
|
483
502
|
// src/providers/openai.ts
|
|
484
503
|
var OPENAI_API_BASE = "https://api.openai.com/v1";
|
|
485
504
|
var SUPPORTED_PARAMS = /* @__PURE__ */ new Set([
|
|
@@ -497,19 +516,20 @@ var SUPPORTED_PARAMS = /* @__PURE__ */ new Set([
|
|
|
497
516
|
"tools",
|
|
498
517
|
"tool_choice",
|
|
499
518
|
"user",
|
|
500
|
-
"logit_bias"
|
|
519
|
+
"logit_bias",
|
|
520
|
+
"service_tier"
|
|
501
521
|
]);
|
|
502
522
|
function createOpenAIAdapter(apiKey, baseURL) {
|
|
503
523
|
const base = baseURL || OPENAI_API_BASE;
|
|
504
|
-
async function makeRequest(path2, body, method = "POST") {
|
|
505
|
-
const res = await
|
|
524
|
+
async function makeRequest(path2, body, method = "POST", timeoutMs) {
|
|
525
|
+
const res = await fetchWithTimeout(`${base}${path2}`, {
|
|
506
526
|
method,
|
|
507
527
|
headers: {
|
|
508
528
|
"Content-Type": "application/json",
|
|
509
529
|
"Authorization": `Bearer ${apiKey}`
|
|
510
530
|
},
|
|
511
531
|
body: body ? JSON.stringify(body) : void 0
|
|
512
|
-
});
|
|
532
|
+
}, timeoutMs);
|
|
513
533
|
if (!res.ok) {
|
|
514
534
|
let errorBody;
|
|
515
535
|
try {
|
|
@@ -557,6 +577,7 @@ function createOpenAIAdapter(apiKey, baseURL) {
|
|
|
557
577
|
if (request.tools !== void 0) body.tools = request.tools;
|
|
558
578
|
if (request.tool_choice !== void 0) body.tool_choice = request.tool_choice;
|
|
559
579
|
if (request.user !== void 0) body.user = request.user;
|
|
580
|
+
if (request.service_tier !== void 0) body.service_tier = request.service_tier;
|
|
560
581
|
return body;
|
|
561
582
|
}
|
|
562
583
|
const adapter = {
|
|
@@ -658,13 +679,15 @@ function createOpenAIAdapter(apiKey, baseURL) {
|
|
|
658
679
|
},
|
|
659
680
|
async sendRequest(request) {
|
|
660
681
|
const body = buildRequestBody(request);
|
|
661
|
-
const
|
|
682
|
+
const timeout = request.service_tier === "flex" ? getFlexTimeout() : void 0;
|
|
683
|
+
const res = await makeRequest("/chat/completions", body, "POST", timeout);
|
|
662
684
|
const json = await res.json();
|
|
663
685
|
return adapter.translateResponse(json);
|
|
664
686
|
},
|
|
665
687
|
async sendStreamingRequest(request) {
|
|
666
688
|
const body = buildRequestBody({ ...request, stream: true });
|
|
667
|
-
const
|
|
689
|
+
const timeout = request.service_tier === "flex" ? getFlexTimeout() : void 0;
|
|
690
|
+
const res = await makeRequest("/chat/completions", body, "POST", timeout);
|
|
668
691
|
if (!res.body) {
|
|
669
692
|
throw new AnyModelError(502, "No response body for streaming request", {
|
|
670
693
|
provider_name: "openai"
|
|
@@ -711,7 +734,7 @@ var FALLBACK_MODELS = [
|
|
|
711
734
|
];
|
|
712
735
|
function createAnthropicAdapter(apiKey) {
|
|
713
736
|
async function makeRequest(path2, body, stream = false) {
|
|
714
|
-
const res = await
|
|
737
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE}${path2}`, {
|
|
715
738
|
method: "POST",
|
|
716
739
|
headers: {
|
|
717
740
|
"Content-Type": "application/json",
|
|
@@ -968,7 +991,7 @@ ${body.system}` : jsonInstruction;
|
|
|
968
991
|
},
|
|
969
992
|
async listModels() {
|
|
970
993
|
try {
|
|
971
|
-
const res = await
|
|
994
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE}/models`, {
|
|
972
995
|
method: "GET",
|
|
973
996
|
headers: {
|
|
974
997
|
"x-api-key": apiKey,
|
|
@@ -1253,7 +1276,7 @@ function createGoogleAdapter(apiKey) {
|
|
|
1253
1276
|
},
|
|
1254
1277
|
async listModels() {
|
|
1255
1278
|
try {
|
|
1256
|
-
const res = await
|
|
1279
|
+
const res = await fetchWithTimeout(`${GEMINI_API_BASE}/models?key=${apiKey}`);
|
|
1257
1280
|
if (!res.ok) return FALLBACK_MODELS2;
|
|
1258
1281
|
const data = await res.json();
|
|
1259
1282
|
const models = data.models || [];
|
|
@@ -1288,12 +1311,12 @@ function createGoogleAdapter(apiKey) {
|
|
|
1288
1311
|
return SUPPORTED_PARAMS3.has(param);
|
|
1289
1312
|
},
|
|
1290
1313
|
supportsBatch() {
|
|
1291
|
-
return
|
|
1314
|
+
return true;
|
|
1292
1315
|
},
|
|
1293
1316
|
async sendRequest(request) {
|
|
1294
1317
|
const body = translateRequest(request);
|
|
1295
1318
|
const url = getModelEndpoint(request.model, false);
|
|
1296
|
-
const res = await
|
|
1319
|
+
const res = await fetchWithTimeout(url, {
|
|
1297
1320
|
method: "POST",
|
|
1298
1321
|
headers: { "Content-Type": "application/json" },
|
|
1299
1322
|
body: JSON.stringify(body)
|
|
@@ -1316,7 +1339,7 @@ function createGoogleAdapter(apiKey) {
|
|
|
1316
1339
|
async sendStreamingRequest(request) {
|
|
1317
1340
|
const body = translateRequest(request);
|
|
1318
1341
|
const url = getModelEndpoint(request.model, true);
|
|
1319
|
-
const res = await
|
|
1342
|
+
const res = await fetchWithTimeout(url, {
|
|
1320
1343
|
method: "POST",
|
|
1321
1344
|
headers: { "Content-Type": "application/json" },
|
|
1322
1345
|
body: JSON.stringify(body)
|
|
@@ -1342,6 +1365,190 @@ function createGoogleAdapter(apiKey) {
|
|
|
1342
1365
|
return adapter;
|
|
1343
1366
|
}
|
|
1344
1367
|
|
|
1368
|
+
// src/providers/perplexity.ts
|
|
1369
|
+
var PERPLEXITY_API_BASE = "https://api.perplexity.ai";
|
|
1370
|
+
var SUPPORTED_PARAMS4 = /* @__PURE__ */ new Set([
|
|
1371
|
+
"temperature",
|
|
1372
|
+
"max_tokens",
|
|
1373
|
+
"top_p",
|
|
1374
|
+
"frequency_penalty",
|
|
1375
|
+
"presence_penalty",
|
|
1376
|
+
"stream",
|
|
1377
|
+
"stop",
|
|
1378
|
+
"response_format",
|
|
1379
|
+
"tools",
|
|
1380
|
+
"tool_choice"
|
|
1381
|
+
]);
|
|
1382
|
+
var MODELS = [
|
|
1383
|
+
{ id: "sonar", name: "Sonar", context: 128e3, maxOutput: 4096, modality: "text->text", inputModalities: ["text"] },
|
|
1384
|
+
{ id: "sonar-pro", name: "Sonar Pro", context: 2e5, maxOutput: 8192, modality: "text->text", inputModalities: ["text"] },
|
|
1385
|
+
{ id: "sonar-reasoning", name: "Sonar Reasoning", context: 128e3, maxOutput: 8192, modality: "text->text", inputModalities: ["text"] },
|
|
1386
|
+
{ id: "sonar-reasoning-pro", name: "Sonar Reasoning Pro", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] },
|
|
1387
|
+
{ id: "sonar-deep-research", name: "Sonar Deep Research", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] },
|
|
1388
|
+
{ id: "r1-1776", name: "R1 1776", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] }
|
|
1389
|
+
];
|
|
1390
|
+
function createPerplexityAdapter(apiKey) {
|
|
1391
|
+
async function makeRequest(path2, body, method = "POST") {
|
|
1392
|
+
const res = await fetchWithTimeout(`${PERPLEXITY_API_BASE}${path2}`, {
|
|
1393
|
+
method,
|
|
1394
|
+
headers: {
|
|
1395
|
+
"Content-Type": "application/json",
|
|
1396
|
+
"Authorization": `Bearer ${apiKey}`
|
|
1397
|
+
},
|
|
1398
|
+
body: body ? JSON.stringify(body) : void 0
|
|
1399
|
+
});
|
|
1400
|
+
if (!res.ok) {
|
|
1401
|
+
let errorBody;
|
|
1402
|
+
try {
|
|
1403
|
+
errorBody = await res.json();
|
|
1404
|
+
} catch {
|
|
1405
|
+
errorBody = { message: res.statusText };
|
|
1406
|
+
}
|
|
1407
|
+
const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
|
|
1408
|
+
throw new AnyModelError(mapErrorCode(res.status), msg, {
|
|
1409
|
+
provider_name: "perplexity",
|
|
1410
|
+
raw: errorBody
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
return res;
|
|
1414
|
+
}
|
|
1415
|
+
function mapErrorCode(status) {
|
|
1416
|
+
if (status === 401 || status === 403) return 401;
|
|
1417
|
+
if (status === 429) return 429;
|
|
1418
|
+
if (status === 400 || status === 422) return 400;
|
|
1419
|
+
if (status >= 500) return 502;
|
|
1420
|
+
return status;
|
|
1421
|
+
}
|
|
1422
|
+
function rePrefixId(id) {
|
|
1423
|
+
if (id && id.startsWith("chatcmpl-")) {
|
|
1424
|
+
return `gen-${id.substring(9)}`;
|
|
1425
|
+
}
|
|
1426
|
+
return id.startsWith("gen-") ? id : `gen-${id}`;
|
|
1427
|
+
}
|
|
1428
|
+
function buildRequestBody(request) {
|
|
1429
|
+
const body = {
|
|
1430
|
+
model: request.model,
|
|
1431
|
+
messages: request.messages
|
|
1432
|
+
};
|
|
1433
|
+
if (request.temperature !== void 0) body.temperature = request.temperature;
|
|
1434
|
+
if (request.max_tokens !== void 0) body.max_tokens = request.max_tokens;
|
|
1435
|
+
if (request.top_p !== void 0) body.top_p = request.top_p;
|
|
1436
|
+
if (request.frequency_penalty !== void 0) body.frequency_penalty = request.frequency_penalty;
|
|
1437
|
+
if (request.presence_penalty !== void 0) body.presence_penalty = request.presence_penalty;
|
|
1438
|
+
if (request.stop !== void 0) body.stop = request.stop;
|
|
1439
|
+
if (request.stream !== void 0) body.stream = request.stream;
|
|
1440
|
+
if (request.response_format !== void 0) body.response_format = request.response_format;
|
|
1441
|
+
if (request.tools !== void 0) body.tools = request.tools;
|
|
1442
|
+
if (request.tool_choice !== void 0) body.tool_choice = request.tool_choice;
|
|
1443
|
+
return body;
|
|
1444
|
+
}
|
|
1445
|
+
const adapter = {
|
|
1446
|
+
name: "perplexity",
|
|
1447
|
+
translateRequest(request) {
|
|
1448
|
+
return buildRequestBody(request);
|
|
1449
|
+
},
|
|
1450
|
+
translateResponse(response) {
|
|
1451
|
+
const r = response;
|
|
1452
|
+
const result = {
|
|
1453
|
+
id: rePrefixId(r.id),
|
|
1454
|
+
object: "chat.completion",
|
|
1455
|
+
created: r.created,
|
|
1456
|
+
model: `perplexity/${r.model}`,
|
|
1457
|
+
choices: r.choices,
|
|
1458
|
+
usage: r.usage
|
|
1459
|
+
};
|
|
1460
|
+
if (r.citations && result.choices?.[0]?.message) {
|
|
1461
|
+
result.citations = r.citations;
|
|
1462
|
+
}
|
|
1463
|
+
return result;
|
|
1464
|
+
},
|
|
1465
|
+
async *translateStream(stream) {
|
|
1466
|
+
const reader = stream.getReader();
|
|
1467
|
+
const decoder = new TextDecoder();
|
|
1468
|
+
let buffer = "";
|
|
1469
|
+
try {
|
|
1470
|
+
while (true) {
|
|
1471
|
+
const { done, value } = await reader.read();
|
|
1472
|
+
if (done) break;
|
|
1473
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1474
|
+
const lines = buffer.split("\n");
|
|
1475
|
+
buffer = lines.pop() || "";
|
|
1476
|
+
for (const line of lines) {
|
|
1477
|
+
const trimmed = line.trim();
|
|
1478
|
+
if (!trimmed || trimmed.startsWith(":")) continue;
|
|
1479
|
+
if (trimmed === "data: [DONE]") return;
|
|
1480
|
+
if (trimmed.startsWith("data: ")) {
|
|
1481
|
+
const json = JSON.parse(trimmed.substring(6));
|
|
1482
|
+
json.id = rePrefixId(json.id);
|
|
1483
|
+
json.model = `perplexity/${json.model}`;
|
|
1484
|
+
yield json;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
} finally {
|
|
1489
|
+
reader.releaseLock();
|
|
1490
|
+
}
|
|
1491
|
+
},
|
|
1492
|
+
translateError(error) {
|
|
1493
|
+
if (error instanceof AnyModelError) {
|
|
1494
|
+
return { code: error.code, message: error.message, metadata: error.metadata };
|
|
1495
|
+
}
|
|
1496
|
+
const err = error;
|
|
1497
|
+
const status = err?.status || err?.code || 500;
|
|
1498
|
+
return {
|
|
1499
|
+
code: mapErrorCode(status),
|
|
1500
|
+
message: err?.message || "Unknown Perplexity error",
|
|
1501
|
+
metadata: { provider_name: "perplexity", raw: error }
|
|
1502
|
+
};
|
|
1503
|
+
},
|
|
1504
|
+
async listModels() {
|
|
1505
|
+
return MODELS.map((m) => ({
|
|
1506
|
+
id: `perplexity/${m.id}`,
|
|
1507
|
+
name: m.name,
|
|
1508
|
+
created: 0,
|
|
1509
|
+
description: "",
|
|
1510
|
+
context_length: m.context,
|
|
1511
|
+
pricing: { prompt: "0", completion: "0" },
|
|
1512
|
+
architecture: {
|
|
1513
|
+
modality: m.modality,
|
|
1514
|
+
input_modalities: m.inputModalities,
|
|
1515
|
+
output_modalities: ["text"],
|
|
1516
|
+
tokenizer: "unknown"
|
|
1517
|
+
},
|
|
1518
|
+
top_provider: {
|
|
1519
|
+
context_length: m.context,
|
|
1520
|
+
max_completion_tokens: m.maxOutput,
|
|
1521
|
+
is_moderated: false
|
|
1522
|
+
},
|
|
1523
|
+
supported_parameters: Array.from(SUPPORTED_PARAMS4)
|
|
1524
|
+
}));
|
|
1525
|
+
},
|
|
1526
|
+
supportsParameter(param) {
|
|
1527
|
+
return SUPPORTED_PARAMS4.has(param);
|
|
1528
|
+
},
|
|
1529
|
+
supportsBatch() {
|
|
1530
|
+
return false;
|
|
1531
|
+
},
|
|
1532
|
+
async sendRequest(request) {
|
|
1533
|
+
const body = buildRequestBody(request);
|
|
1534
|
+
const res = await makeRequest("/chat/completions", body);
|
|
1535
|
+
const json = await res.json();
|
|
1536
|
+
return adapter.translateResponse(json);
|
|
1537
|
+
},
|
|
1538
|
+
async sendStreamingRequest(request) {
|
|
1539
|
+
const body = buildRequestBody({ ...request, stream: true });
|
|
1540
|
+
const res = await makeRequest("/chat/completions", body);
|
|
1541
|
+
if (!res.body) {
|
|
1542
|
+
throw new AnyModelError(502, "No response body for streaming request", {
|
|
1543
|
+
provider_name: "perplexity"
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
return adapter.translateStream(res.body);
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
return adapter;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1345
1552
|
// src/providers/custom.ts
|
|
1346
1553
|
function createCustomAdapter(name, config) {
|
|
1347
1554
|
const openaiAdapter = createOpenAIAdapter(config.apiKey || "", config.baseURL);
|
|
@@ -2063,6 +2270,54 @@ var BatchManager = class {
|
|
|
2063
2270
|
}
|
|
2064
2271
|
};
|
|
2065
2272
|
|
|
2273
|
+
// src/utils/token-estimate.ts
|
|
2274
|
+
var CHARS_PER_TOKEN2 = 4;
|
|
2275
|
+
function estimateTokenCount(text) {
|
|
2276
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN2);
|
|
2277
|
+
}
|
|
2278
|
+
var MODEL_LIMITS = [
|
|
2279
|
+
// OpenAI
|
|
2280
|
+
{ pattern: "gpt-4o-mini", limit: { contextLength: 128e3, maxCompletionTokens: 16384 } },
|
|
2281
|
+
{ pattern: "gpt-4o", limit: { contextLength: 128e3, maxCompletionTokens: 16384 } },
|
|
2282
|
+
{ pattern: "gpt-4-turbo", limit: { contextLength: 128e3, maxCompletionTokens: 4096 } },
|
|
2283
|
+
{ pattern: "gpt-3.5-turbo", limit: { contextLength: 16385, maxCompletionTokens: 4096 } },
|
|
2284
|
+
{ pattern: "o1", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2285
|
+
{ pattern: "o3", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2286
|
+
{ pattern: "o4-mini", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2287
|
+
// Anthropic
|
|
2288
|
+
{ pattern: "claude-opus-4", limit: { contextLength: 2e5, maxCompletionTokens: 32768 } },
|
|
2289
|
+
{ pattern: "claude-sonnet-4", limit: { contextLength: 2e5, maxCompletionTokens: 16384 } },
|
|
2290
|
+
{ pattern: "claude-haiku-4", limit: { contextLength: 2e5, maxCompletionTokens: 8192 } },
|
|
2291
|
+
{ pattern: "claude-3.5-sonnet", limit: { contextLength: 2e5, maxCompletionTokens: 8192 } },
|
|
2292
|
+
{ pattern: "claude-3-opus", limit: { contextLength: 2e5, maxCompletionTokens: 4096 } },
|
|
2293
|
+
// Google
|
|
2294
|
+
{ pattern: "gemini-2.5-pro", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2295
|
+
{ pattern: "gemini-2.5-flash", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2296
|
+
{ pattern: "gemini-2.0-flash", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2297
|
+
{ pattern: "gemini-1.5-pro", limit: { contextLength: 2097152, maxCompletionTokens: 8192 } },
|
|
2298
|
+
{ pattern: "gemini-1.5-flash", limit: { contextLength: 1048576, maxCompletionTokens: 8192 } }
|
|
2299
|
+
];
|
|
2300
|
+
var DEFAULT_LIMIT = { contextLength: 128e3, maxCompletionTokens: 4096 };
|
|
2301
|
+
function getModelLimits(model) {
|
|
2302
|
+
const bare = model.includes("/") ? model.slice(model.indexOf("/") + 1) : model;
|
|
2303
|
+
for (const entry of MODEL_LIMITS) {
|
|
2304
|
+
if (bare.startsWith(entry.pattern) || bare.includes(entry.pattern)) {
|
|
2305
|
+
return entry.limit;
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
return DEFAULT_LIMIT;
|
|
2309
|
+
}
|
|
2310
|
+
function resolveMaxTokens(model, messages, userMaxTokens) {
|
|
2311
|
+
if (userMaxTokens !== void 0) return userMaxTokens;
|
|
2312
|
+
const inputChars = JSON.stringify(messages).length;
|
|
2313
|
+
const estimatedInput = Math.ceil(inputChars / CHARS_PER_TOKEN2);
|
|
2314
|
+
const estimatedWithMargin = Math.ceil(estimatedInput * 1.05);
|
|
2315
|
+
const limits = getModelLimits(model);
|
|
2316
|
+
const available = limits.contextLength - estimatedWithMargin;
|
|
2317
|
+
const result = Math.min(limits.maxCompletionTokens, available);
|
|
2318
|
+
return Math.max(1, result);
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2066
2321
|
// src/providers/openai-batch.ts
|
|
2067
2322
|
var OPENAI_API_BASE2 = "https://api.openai.com/v1";
|
|
2068
2323
|
function createOpenAIBatchAdapter(apiKey) {
|
|
@@ -2077,7 +2332,7 @@ function createOpenAIBatchAdapter(apiKey) {
|
|
|
2077
2332
|
headers["Content-Type"] = "application/json";
|
|
2078
2333
|
fetchBody = JSON.stringify(options.body);
|
|
2079
2334
|
}
|
|
2080
|
-
const res = await
|
|
2335
|
+
const res = await fetchWithTimeout(`${OPENAI_API_BASE2}${path2}`, {
|
|
2081
2336
|
method: options.method || "GET",
|
|
2082
2337
|
headers,
|
|
2083
2338
|
body: fetchBody
|
|
@@ -2103,7 +2358,7 @@ function createOpenAIBatchAdapter(apiKey) {
|
|
|
2103
2358
|
model,
|
|
2104
2359
|
messages: req.messages
|
|
2105
2360
|
};
|
|
2106
|
-
|
|
2361
|
+
body.max_tokens = req.max_tokens !== void 0 ? req.max_tokens : resolveMaxTokens(model, req.messages);
|
|
2107
2362
|
if (req.temperature !== void 0) body.temperature = req.temperature;
|
|
2108
2363
|
if (req.top_p !== void 0) body.top_p = req.top_p;
|
|
2109
2364
|
if (req.stop !== void 0) body.stop = req.stop;
|
|
@@ -2262,7 +2517,7 @@ function createAnthropicBatchAdapter(apiKey) {
|
|
|
2262
2517
|
"anthropic-version": ANTHROPIC_VERSION2,
|
|
2263
2518
|
"Content-Type": "application/json"
|
|
2264
2519
|
};
|
|
2265
|
-
const res = await
|
|
2520
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE2}${path2}`, {
|
|
2266
2521
|
method: options.method || "GET",
|
|
2267
2522
|
headers,
|
|
2268
2523
|
body: options.body ? JSON.stringify(options.body) : void 0
|
|
@@ -2285,7 +2540,7 @@ function createAnthropicBatchAdapter(apiKey) {
|
|
|
2285
2540
|
function translateToAnthropicParams(model, req) {
|
|
2286
2541
|
const params = {
|
|
2287
2542
|
model,
|
|
2288
|
-
max_tokens: req.max_tokens || DEFAULT_MAX_TOKENS2
|
|
2543
|
+
max_tokens: resolveMaxTokens(model, req.messages, req.max_tokens || DEFAULT_MAX_TOKENS2)
|
|
2289
2544
|
};
|
|
2290
2545
|
const systemMessages = req.messages.filter((m) => m.role === "system");
|
|
2291
2546
|
const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
|
|
@@ -2459,6 +2714,284 @@ ${params.system}` : jsonInstruction;
|
|
|
2459
2714
|
};
|
|
2460
2715
|
}
|
|
2461
2716
|
|
|
2717
|
+
// src/providers/google-batch.ts
|
|
2718
|
+
var GEMINI_API_BASE2 = "https://generativelanguage.googleapis.com/v1beta";
|
|
2719
|
+
function createGoogleBatchAdapter(apiKey) {
|
|
2720
|
+
async function apiRequest(path2, options = {}) {
|
|
2721
|
+
const headers = {
|
|
2722
|
+
"Content-Type": "application/json",
|
|
2723
|
+
"x-goog-api-key": apiKey
|
|
2724
|
+
};
|
|
2725
|
+
const res = await fetchWithTimeout(`${GEMINI_API_BASE2}${path2}`, {
|
|
2726
|
+
method: options.method || "GET",
|
|
2727
|
+
headers,
|
|
2728
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
2729
|
+
});
|
|
2730
|
+
if (!res.ok) {
|
|
2731
|
+
let errorBody;
|
|
2732
|
+
try {
|
|
2733
|
+
errorBody = await res.json();
|
|
2734
|
+
} catch {
|
|
2735
|
+
errorBody = { message: res.statusText };
|
|
2736
|
+
}
|
|
2737
|
+
const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
|
|
2738
|
+
throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
|
|
2739
|
+
provider_name: "google",
|
|
2740
|
+
raw: errorBody
|
|
2741
|
+
});
|
|
2742
|
+
}
|
|
2743
|
+
return res;
|
|
2744
|
+
}
|
|
2745
|
+
function translateRequestToGemini(model, req) {
|
|
2746
|
+
const body = {};
|
|
2747
|
+
const systemMessages = req.messages.filter((m) => m.role === "system");
|
|
2748
|
+
const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
|
|
2749
|
+
if (systemMessages.length > 0) {
|
|
2750
|
+
body.systemInstruction = {
|
|
2751
|
+
parts: [{ text: systemMessages.map((m) => typeof m.content === "string" ? m.content : "").join("\n") }]
|
|
2752
|
+
};
|
|
2753
|
+
}
|
|
2754
|
+
body.contents = nonSystemMessages.map((m) => ({
|
|
2755
|
+
role: m.role === "assistant" ? "model" : "user",
|
|
2756
|
+
parts: typeof m.content === "string" ? [{ text: m.content }] : Array.isArray(m.content) ? m.content.map((p) => p.type === "text" ? { text: p.text } : { text: "" }) : [{ text: "" }]
|
|
2757
|
+
}));
|
|
2758
|
+
const generationConfig = {};
|
|
2759
|
+
if (req.temperature !== void 0) generationConfig.temperature = req.temperature;
|
|
2760
|
+
generationConfig.maxOutputTokens = req.max_tokens !== void 0 ? req.max_tokens : resolveMaxTokens(model, req.messages);
|
|
2761
|
+
if (req.top_p !== void 0) generationConfig.topP = req.top_p;
|
|
2762
|
+
if (req.top_k !== void 0) generationConfig.topK = req.top_k;
|
|
2763
|
+
if (req.stop !== void 0) {
|
|
2764
|
+
generationConfig.stopSequences = Array.isArray(req.stop) ? req.stop : [req.stop];
|
|
2765
|
+
}
|
|
2766
|
+
if (req.response_format) {
|
|
2767
|
+
if (req.response_format.type === "json_object") {
|
|
2768
|
+
generationConfig.responseMimeType = "application/json";
|
|
2769
|
+
} else if (req.response_format.type === "json_schema") {
|
|
2770
|
+
generationConfig.responseMimeType = "application/json";
|
|
2771
|
+
generationConfig.responseSchema = req.response_format.json_schema?.schema;
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
if (Object.keys(generationConfig).length > 0) {
|
|
2775
|
+
body.generationConfig = generationConfig;
|
|
2776
|
+
}
|
|
2777
|
+
if (req.tools && req.tools.length > 0) {
|
|
2778
|
+
body.tools = [{
|
|
2779
|
+
functionDeclarations: req.tools.map((t) => ({
|
|
2780
|
+
name: t.function.name,
|
|
2781
|
+
description: t.function.description || "",
|
|
2782
|
+
parameters: t.function.parameters || {}
|
|
2783
|
+
}))
|
|
2784
|
+
}];
|
|
2785
|
+
if (req.tool_choice) {
|
|
2786
|
+
if (req.tool_choice === "auto") {
|
|
2787
|
+
body.toolConfig = { functionCallingConfig: { mode: "AUTO" } };
|
|
2788
|
+
} else if (req.tool_choice === "required") {
|
|
2789
|
+
body.toolConfig = { functionCallingConfig: { mode: "ANY" } };
|
|
2790
|
+
} else if (req.tool_choice === "none") {
|
|
2791
|
+
body.toolConfig = { functionCallingConfig: { mode: "NONE" } };
|
|
2792
|
+
} else if (typeof req.tool_choice === "object") {
|
|
2793
|
+
body.toolConfig = {
|
|
2794
|
+
functionCallingConfig: {
|
|
2795
|
+
mode: "ANY",
|
|
2796
|
+
allowedFunctionNames: [req.tool_choice.function.name]
|
|
2797
|
+
}
|
|
2798
|
+
};
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
return body;
|
|
2803
|
+
}
|
|
2804
|
+
function mapFinishReason(reason) {
|
|
2805
|
+
switch (reason) {
|
|
2806
|
+
case "STOP":
|
|
2807
|
+
return "stop";
|
|
2808
|
+
case "MAX_TOKENS":
|
|
2809
|
+
return "length";
|
|
2810
|
+
case "SAFETY":
|
|
2811
|
+
return "content_filter";
|
|
2812
|
+
case "RECITATION":
|
|
2813
|
+
return "content_filter";
|
|
2814
|
+
default:
|
|
2815
|
+
return "stop";
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
function translateGeminiResponse(response, model) {
|
|
2819
|
+
const candidate = response.candidates?.[0];
|
|
2820
|
+
let content = "";
|
|
2821
|
+
const toolCalls = [];
|
|
2822
|
+
for (const part of candidate?.content?.parts || []) {
|
|
2823
|
+
if (part.text) {
|
|
2824
|
+
content += part.text;
|
|
2825
|
+
} else if (part.functionCall) {
|
|
2826
|
+
toolCalls.push({
|
|
2827
|
+
id: generateId("call"),
|
|
2828
|
+
type: "function",
|
|
2829
|
+
function: {
|
|
2830
|
+
name: part.functionCall.name,
|
|
2831
|
+
arguments: JSON.stringify(part.functionCall.args || {})
|
|
2832
|
+
}
|
|
2833
|
+
});
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
const message = { role: "assistant", content };
|
|
2837
|
+
if (toolCalls.length > 0) {
|
|
2838
|
+
message.tool_calls = toolCalls;
|
|
2839
|
+
}
|
|
2840
|
+
const finishReason = toolCalls.length > 0 ? "tool_calls" : mapFinishReason(candidate?.finishReason || "STOP");
|
|
2841
|
+
return {
|
|
2842
|
+
id: generateId(),
|
|
2843
|
+
object: "chat.completion",
|
|
2844
|
+
created: Math.floor(Date.now() / 1e3),
|
|
2845
|
+
model: `google/${model}`,
|
|
2846
|
+
choices: [{ index: 0, message, finish_reason: finishReason }],
|
|
2847
|
+
usage: {
|
|
2848
|
+
prompt_tokens: response.usageMetadata?.promptTokenCount || 0,
|
|
2849
|
+
completion_tokens: response.usageMetadata?.candidatesTokenCount || 0,
|
|
2850
|
+
total_tokens: response.usageMetadata?.totalTokenCount || 0
|
|
2851
|
+
}
|
|
2852
|
+
};
|
|
2853
|
+
}
|
|
2854
|
+
function mapBatchState(state) {
|
|
2855
|
+
switch (state) {
|
|
2856
|
+
case "JOB_STATE_PENDING":
|
|
2857
|
+
return "pending";
|
|
2858
|
+
case "JOB_STATE_RUNNING":
|
|
2859
|
+
return "processing";
|
|
2860
|
+
case "JOB_STATE_SUCCEEDED":
|
|
2861
|
+
return "completed";
|
|
2862
|
+
case "JOB_STATE_FAILED":
|
|
2863
|
+
return "failed";
|
|
2864
|
+
case "JOB_STATE_CANCELLED":
|
|
2865
|
+
return "cancelled";
|
|
2866
|
+
case "JOB_STATE_EXPIRED":
|
|
2867
|
+
return "failed";
|
|
2868
|
+
default:
|
|
2869
|
+
return "pending";
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
return {
|
|
2873
|
+
async createBatch(model, requests, _options) {
|
|
2874
|
+
const batchRequests = requests.map((req) => ({
|
|
2875
|
+
request: translateRequestToGemini(model, req),
|
|
2876
|
+
metadata: { key: req.custom_id }
|
|
2877
|
+
}));
|
|
2878
|
+
const res = await apiRequest(`/models/${model}:batchGenerateContent`, {
|
|
2879
|
+
method: "POST",
|
|
2880
|
+
body: {
|
|
2881
|
+
batch: {
|
|
2882
|
+
display_name: `anymodel-batch-${Date.now()}`,
|
|
2883
|
+
input_config: {
|
|
2884
|
+
requests: {
|
|
2885
|
+
requests: batchRequests
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
});
|
|
2891
|
+
const data = await res.json();
|
|
2892
|
+
const batchName = data.name || data.batch?.name;
|
|
2893
|
+
if (!batchName) {
|
|
2894
|
+
throw new AnyModelError(502, "No batch name in Google response", {
|
|
2895
|
+
provider_name: "google",
|
|
2896
|
+
raw: data
|
|
2897
|
+
});
|
|
2898
|
+
}
|
|
2899
|
+
return {
|
|
2900
|
+
providerBatchId: batchName,
|
|
2901
|
+
metadata: {
|
|
2902
|
+
model,
|
|
2903
|
+
total_requests: requests.length
|
|
2904
|
+
}
|
|
2905
|
+
};
|
|
2906
|
+
},
|
|
2907
|
+
async pollBatch(providerBatchId) {
|
|
2908
|
+
const res = await apiRequest(`/${providerBatchId}`);
|
|
2909
|
+
const data = await res.json();
|
|
2910
|
+
const state = data.state || "JOB_STATE_PENDING";
|
|
2911
|
+
const status = mapBatchState(state);
|
|
2912
|
+
const totalCount = data.totalCount || data.metadata?.total_requests || 0;
|
|
2913
|
+
const successCount = data.succeededCount || 0;
|
|
2914
|
+
const failedCount = data.failedCount || 0;
|
|
2915
|
+
return {
|
|
2916
|
+
status,
|
|
2917
|
+
total: totalCount || successCount + failedCount,
|
|
2918
|
+
completed: successCount,
|
|
2919
|
+
failed: failedCount
|
|
2920
|
+
};
|
|
2921
|
+
},
|
|
2922
|
+
async getBatchResults(providerBatchId) {
|
|
2923
|
+
const batchRes = await apiRequest(`/${providerBatchId}`);
|
|
2924
|
+
const batchData = await batchRes.json();
|
|
2925
|
+
const results = [];
|
|
2926
|
+
const model = batchData.metadata?.model || "unknown";
|
|
2927
|
+
if (batchData.response?.inlinedResponses) {
|
|
2928
|
+
for (const item of batchData.response.inlinedResponses) {
|
|
2929
|
+
const customId = item.metadata?.key || `request-${results.length}`;
|
|
2930
|
+
if (item.response) {
|
|
2931
|
+
results.push({
|
|
2932
|
+
custom_id: customId,
|
|
2933
|
+
status: "success",
|
|
2934
|
+
response: translateGeminiResponse(item.response, model),
|
|
2935
|
+
error: null
|
|
2936
|
+
});
|
|
2937
|
+
} else if (item.error) {
|
|
2938
|
+
results.push({
|
|
2939
|
+
custom_id: customId,
|
|
2940
|
+
status: "error",
|
|
2941
|
+
response: null,
|
|
2942
|
+
error: {
|
|
2943
|
+
code: item.error.code || 500,
|
|
2944
|
+
message: item.error.message || "Batch item failed"
|
|
2945
|
+
}
|
|
2946
|
+
});
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
return results;
|
|
2950
|
+
}
|
|
2951
|
+
const responsesFile = batchData.response?.responsesFileName || batchData.outputConfig?.file_name;
|
|
2952
|
+
if (responsesFile) {
|
|
2953
|
+
const downloadUrl = `${GEMINI_API_BASE2}/${responsesFile}:download?alt=media`;
|
|
2954
|
+
const fileRes = await fetchWithTimeout(downloadUrl, {
|
|
2955
|
+
headers: { "x-goog-api-key": apiKey }
|
|
2956
|
+
});
|
|
2957
|
+
if (!fileRes.ok) {
|
|
2958
|
+
throw new AnyModelError(502, "Failed to download batch results file", {
|
|
2959
|
+
provider_name: "google"
|
|
2960
|
+
});
|
|
2961
|
+
}
|
|
2962
|
+
const text = await fileRes.text();
|
|
2963
|
+
for (const line of text.trim().split("\n")) {
|
|
2964
|
+
if (!line) continue;
|
|
2965
|
+
const item = JSON.parse(line);
|
|
2966
|
+
const customId = item.key || item.metadata?.key || `request-${results.length}`;
|
|
2967
|
+
if (item.response) {
|
|
2968
|
+
results.push({
|
|
2969
|
+
custom_id: customId,
|
|
2970
|
+
status: "success",
|
|
2971
|
+
response: translateGeminiResponse(item.response, model),
|
|
2972
|
+
error: null
|
|
2973
|
+
});
|
|
2974
|
+
} else if (item.error) {
|
|
2975
|
+
results.push({
|
|
2976
|
+
custom_id: customId,
|
|
2977
|
+
status: "error",
|
|
2978
|
+
response: null,
|
|
2979
|
+
error: {
|
|
2980
|
+
code: item.error.code || 500,
|
|
2981
|
+
message: item.error.message || "Batch item failed"
|
|
2982
|
+
}
|
|
2983
|
+
});
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
return results;
|
|
2988
|
+
},
|
|
2989
|
+
async cancelBatch(providerBatchId) {
|
|
2990
|
+
await apiRequest(`/${providerBatchId}:cancel`, { method: "POST" });
|
|
2991
|
+
}
|
|
2992
|
+
};
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2462
2995
|
// src/client.ts
|
|
2463
2996
|
var AnyModel = class {
|
|
2464
2997
|
registry;
|
|
@@ -2474,6 +3007,7 @@ var AnyModel = class {
|
|
|
2474
3007
|
constructor(config = {}) {
|
|
2475
3008
|
this.config = resolveConfig(config);
|
|
2476
3009
|
this.registry = new ProviderRegistry();
|
|
3010
|
+
setDefaultTimeout((this.config.defaults?.timeout ?? 120) * 1e3);
|
|
2477
3011
|
if (this.config.io) {
|
|
2478
3012
|
configureFsIO(this.config.io);
|
|
2479
3013
|
}
|
|
@@ -2554,14 +3088,17 @@ var AnyModel = class {
|
|
|
2554
3088
|
if (googleKey) {
|
|
2555
3089
|
this.registry.register("google", createGoogleAdapter(googleKey));
|
|
2556
3090
|
}
|
|
3091
|
+
const perplexityKey = config.perplexity?.apiKey || process.env.PERPLEXITY_API_KEY;
|
|
3092
|
+
if (perplexityKey) {
|
|
3093
|
+
this.registry.register("perplexity", createPerplexityAdapter(perplexityKey));
|
|
3094
|
+
}
|
|
2557
3095
|
const builtinProviders = [
|
|
2558
3096
|
{ name: "mistral", baseURL: "https://api.mistral.ai/v1", configKey: "mistral", envVar: "MISTRAL_API_KEY" },
|
|
2559
3097
|
{ name: "groq", baseURL: "https://api.groq.com/openai/v1", configKey: "groq", envVar: "GROQ_API_KEY" },
|
|
2560
3098
|
{ name: "deepseek", baseURL: "https://api.deepseek.com", configKey: "deepseek", envVar: "DEEPSEEK_API_KEY" },
|
|
2561
3099
|
{ name: "xai", baseURL: "https://api.x.ai/v1", configKey: "xai", envVar: "XAI_API_KEY" },
|
|
2562
3100
|
{ name: "together", baseURL: "https://api.together.xyz/v1", configKey: "together", envVar: "TOGETHER_API_KEY" },
|
|
2563
|
-
{ name: "fireworks", baseURL: "https://api.fireworks.ai/inference/v1", configKey: "fireworks", envVar: "FIREWORKS_API_KEY" }
|
|
2564
|
-
{ name: "perplexity", baseURL: "https://api.perplexity.ai", configKey: "perplexity", envVar: "PERPLEXITY_API_KEY" }
|
|
3101
|
+
{ name: "fireworks", baseURL: "https://api.fireworks.ai/inference/v1", configKey: "fireworks", envVar: "FIREWORKS_API_KEY" }
|
|
2565
3102
|
];
|
|
2566
3103
|
for (const { name, baseURL, configKey, envVar } of builtinProviders) {
|
|
2567
3104
|
const providerConfig = config[configKey];
|
|
@@ -2591,6 +3128,10 @@ var AnyModel = class {
|
|
|
2591
3128
|
if (anthropicKey) {
|
|
2592
3129
|
this.batchManager.registerBatchAdapter("anthropic", createAnthropicBatchAdapter(anthropicKey));
|
|
2593
3130
|
}
|
|
3131
|
+
const googleKey = config.google?.apiKey || process.env.GOOGLE_API_KEY;
|
|
3132
|
+
if (googleKey) {
|
|
3133
|
+
this.batchManager.registerBatchAdapter("google", createGoogleBatchAdapter(googleKey));
|
|
3134
|
+
}
|
|
2594
3135
|
}
|
|
2595
3136
|
applyDefaults(request) {
|
|
2596
3137
|
const defaults = this.config.defaults;
|
|
@@ -2769,12 +3310,15 @@ export {
|
|
|
2769
3310
|
configureFsIO,
|
|
2770
3311
|
createAnthropicBatchAdapter,
|
|
2771
3312
|
createAnyModelServer,
|
|
3313
|
+
createGoogleBatchAdapter,
|
|
2772
3314
|
createOpenAIBatchAdapter,
|
|
2773
3315
|
ensureDir,
|
|
3316
|
+
estimateTokenCount,
|
|
2774
3317
|
getFsQueueStatus,
|
|
2775
3318
|
joinPath,
|
|
2776
3319
|
readFileQueued,
|
|
2777
3320
|
resolveConfig,
|
|
3321
|
+
resolveMaxTokens,
|
|
2778
3322
|
startServer,
|
|
2779
3323
|
waitForFsQueuesIdle,
|
|
2780
3324
|
writeFileFlushedQueued,
|