@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.cjs
CHANGED
|
@@ -508,6 +508,25 @@ var Router = class {
|
|
|
508
508
|
}
|
|
509
509
|
};
|
|
510
510
|
|
|
511
|
+
// src/utils/fetch-with-timeout.ts
|
|
512
|
+
var _defaultTimeout = 12e4;
|
|
513
|
+
var _flexTimeout = 6e5;
|
|
514
|
+
function setDefaultTimeout(ms) {
|
|
515
|
+
_defaultTimeout = ms;
|
|
516
|
+
}
|
|
517
|
+
function getFlexTimeout() {
|
|
518
|
+
return _flexTimeout;
|
|
519
|
+
}
|
|
520
|
+
function fetchWithTimeout(url, init, timeoutMs) {
|
|
521
|
+
const ms = timeoutMs ?? _defaultTimeout;
|
|
522
|
+
const signal = AbortSignal.timeout(ms);
|
|
523
|
+
if (init?.signal) {
|
|
524
|
+
const combined = AbortSignal.any([signal, init.signal]);
|
|
525
|
+
return fetch(url, { ...init, signal: combined });
|
|
526
|
+
}
|
|
527
|
+
return fetch(url, { ...init, signal });
|
|
528
|
+
}
|
|
529
|
+
|
|
511
530
|
// src/providers/openai.ts
|
|
512
531
|
var OPENAI_API_BASE = "https://api.openai.com/v1";
|
|
513
532
|
var SUPPORTED_PARAMS = /* @__PURE__ */ new Set([
|
|
@@ -525,19 +544,20 @@ var SUPPORTED_PARAMS = /* @__PURE__ */ new Set([
|
|
|
525
544
|
"tools",
|
|
526
545
|
"tool_choice",
|
|
527
546
|
"user",
|
|
528
|
-
"logit_bias"
|
|
547
|
+
"logit_bias",
|
|
548
|
+
"service_tier"
|
|
529
549
|
]);
|
|
530
550
|
function createOpenAIAdapter(apiKey, baseURL) {
|
|
531
551
|
const base = baseURL || OPENAI_API_BASE;
|
|
532
|
-
async function makeRequest(path2, body, method = "POST") {
|
|
533
|
-
const res = await
|
|
552
|
+
async function makeRequest(path2, body, method = "POST", timeoutMs) {
|
|
553
|
+
const res = await fetchWithTimeout(`${base}${path2}`, {
|
|
534
554
|
method,
|
|
535
555
|
headers: {
|
|
536
556
|
"Content-Type": "application/json",
|
|
537
557
|
"Authorization": `Bearer ${apiKey}`
|
|
538
558
|
},
|
|
539
559
|
body: body ? JSON.stringify(body) : void 0
|
|
540
|
-
});
|
|
560
|
+
}, timeoutMs);
|
|
541
561
|
if (!res.ok) {
|
|
542
562
|
let errorBody;
|
|
543
563
|
try {
|
|
@@ -585,6 +605,7 @@ function createOpenAIAdapter(apiKey, baseURL) {
|
|
|
585
605
|
if (request.tools !== void 0) body.tools = request.tools;
|
|
586
606
|
if (request.tool_choice !== void 0) body.tool_choice = request.tool_choice;
|
|
587
607
|
if (request.user !== void 0) body.user = request.user;
|
|
608
|
+
if (request.service_tier !== void 0) body.service_tier = request.service_tier;
|
|
588
609
|
return body;
|
|
589
610
|
}
|
|
590
611
|
const adapter = {
|
|
@@ -686,13 +707,15 @@ function createOpenAIAdapter(apiKey, baseURL) {
|
|
|
686
707
|
},
|
|
687
708
|
async sendRequest(request) {
|
|
688
709
|
const body = buildRequestBody(request);
|
|
689
|
-
const
|
|
710
|
+
const timeout = request.service_tier === "flex" ? getFlexTimeout() : void 0;
|
|
711
|
+
const res = await makeRequest("/chat/completions", body, "POST", timeout);
|
|
690
712
|
const json = await res.json();
|
|
691
713
|
return adapter.translateResponse(json);
|
|
692
714
|
},
|
|
693
715
|
async sendStreamingRequest(request) {
|
|
694
716
|
const body = buildRequestBody({ ...request, stream: true });
|
|
695
|
-
const
|
|
717
|
+
const timeout = request.service_tier === "flex" ? getFlexTimeout() : void 0;
|
|
718
|
+
const res = await makeRequest("/chat/completions", body, "POST", timeout);
|
|
696
719
|
if (!res.body) {
|
|
697
720
|
throw new AnyModelError(502, "No response body for streaming request", {
|
|
698
721
|
provider_name: "openai"
|
|
@@ -739,7 +762,7 @@ var FALLBACK_MODELS = [
|
|
|
739
762
|
];
|
|
740
763
|
function createAnthropicAdapter(apiKey) {
|
|
741
764
|
async function makeRequest(path2, body, stream = false) {
|
|
742
|
-
const res = await
|
|
765
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE}${path2}`, {
|
|
743
766
|
method: "POST",
|
|
744
767
|
headers: {
|
|
745
768
|
"Content-Type": "application/json",
|
|
@@ -996,7 +1019,7 @@ ${body.system}` : jsonInstruction;
|
|
|
996
1019
|
},
|
|
997
1020
|
async listModels() {
|
|
998
1021
|
try {
|
|
999
|
-
const res = await
|
|
1022
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE}/models`, {
|
|
1000
1023
|
method: "GET",
|
|
1001
1024
|
headers: {
|
|
1002
1025
|
"x-api-key": apiKey,
|
|
@@ -1281,7 +1304,7 @@ function createGoogleAdapter(apiKey) {
|
|
|
1281
1304
|
},
|
|
1282
1305
|
async listModels() {
|
|
1283
1306
|
try {
|
|
1284
|
-
const res = await
|
|
1307
|
+
const res = await fetchWithTimeout(`${GEMINI_API_BASE}/models?key=${apiKey}`);
|
|
1285
1308
|
if (!res.ok) return FALLBACK_MODELS2;
|
|
1286
1309
|
const data = await res.json();
|
|
1287
1310
|
const models = data.models || [];
|
|
@@ -1316,12 +1339,12 @@ function createGoogleAdapter(apiKey) {
|
|
|
1316
1339
|
return SUPPORTED_PARAMS3.has(param);
|
|
1317
1340
|
},
|
|
1318
1341
|
supportsBatch() {
|
|
1319
|
-
return
|
|
1342
|
+
return true;
|
|
1320
1343
|
},
|
|
1321
1344
|
async sendRequest(request) {
|
|
1322
1345
|
const body = translateRequest(request);
|
|
1323
1346
|
const url = getModelEndpoint(request.model, false);
|
|
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)
|
|
@@ -1344,7 +1367,7 @@ function createGoogleAdapter(apiKey) {
|
|
|
1344
1367
|
async sendStreamingRequest(request) {
|
|
1345
1368
|
const body = translateRequest(request);
|
|
1346
1369
|
const url = getModelEndpoint(request.model, true);
|
|
1347
|
-
const res = await
|
|
1370
|
+
const res = await fetchWithTimeout(url, {
|
|
1348
1371
|
method: "POST",
|
|
1349
1372
|
headers: { "Content-Type": "application/json" },
|
|
1350
1373
|
body: JSON.stringify(body)
|
|
@@ -1370,6 +1393,190 @@ function createGoogleAdapter(apiKey) {
|
|
|
1370
1393
|
return adapter;
|
|
1371
1394
|
}
|
|
1372
1395
|
|
|
1396
|
+
// src/providers/perplexity.ts
|
|
1397
|
+
var PERPLEXITY_API_BASE = "https://api.perplexity.ai";
|
|
1398
|
+
var SUPPORTED_PARAMS4 = /* @__PURE__ */ new Set([
|
|
1399
|
+
"temperature",
|
|
1400
|
+
"max_tokens",
|
|
1401
|
+
"top_p",
|
|
1402
|
+
"frequency_penalty",
|
|
1403
|
+
"presence_penalty",
|
|
1404
|
+
"stream",
|
|
1405
|
+
"stop",
|
|
1406
|
+
"response_format",
|
|
1407
|
+
"tools",
|
|
1408
|
+
"tool_choice"
|
|
1409
|
+
]);
|
|
1410
|
+
var MODELS = [
|
|
1411
|
+
{ id: "sonar", name: "Sonar", context: 128e3, maxOutput: 4096, modality: "text->text", inputModalities: ["text"] },
|
|
1412
|
+
{ id: "sonar-pro", name: "Sonar Pro", context: 2e5, maxOutput: 8192, modality: "text->text", inputModalities: ["text"] },
|
|
1413
|
+
{ id: "sonar-reasoning", name: "Sonar Reasoning", context: 128e3, maxOutput: 8192, modality: "text->text", inputModalities: ["text"] },
|
|
1414
|
+
{ id: "sonar-reasoning-pro", name: "Sonar Reasoning Pro", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] },
|
|
1415
|
+
{ id: "sonar-deep-research", name: "Sonar Deep Research", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] },
|
|
1416
|
+
{ id: "r1-1776", name: "R1 1776", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] }
|
|
1417
|
+
];
|
|
1418
|
+
function createPerplexityAdapter(apiKey) {
|
|
1419
|
+
async function makeRequest(path2, body, method = "POST") {
|
|
1420
|
+
const res = await fetchWithTimeout(`${PERPLEXITY_API_BASE}${path2}`, {
|
|
1421
|
+
method,
|
|
1422
|
+
headers: {
|
|
1423
|
+
"Content-Type": "application/json",
|
|
1424
|
+
"Authorization": `Bearer ${apiKey}`
|
|
1425
|
+
},
|
|
1426
|
+
body: body ? JSON.stringify(body) : void 0
|
|
1427
|
+
});
|
|
1428
|
+
if (!res.ok) {
|
|
1429
|
+
let errorBody;
|
|
1430
|
+
try {
|
|
1431
|
+
errorBody = await res.json();
|
|
1432
|
+
} catch {
|
|
1433
|
+
errorBody = { message: res.statusText };
|
|
1434
|
+
}
|
|
1435
|
+
const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
|
|
1436
|
+
throw new AnyModelError(mapErrorCode(res.status), msg, {
|
|
1437
|
+
provider_name: "perplexity",
|
|
1438
|
+
raw: errorBody
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1441
|
+
return res;
|
|
1442
|
+
}
|
|
1443
|
+
function mapErrorCode(status) {
|
|
1444
|
+
if (status === 401 || status === 403) return 401;
|
|
1445
|
+
if (status === 429) return 429;
|
|
1446
|
+
if (status === 400 || status === 422) return 400;
|
|
1447
|
+
if (status >= 500) return 502;
|
|
1448
|
+
return status;
|
|
1449
|
+
}
|
|
1450
|
+
function rePrefixId(id) {
|
|
1451
|
+
if (id && id.startsWith("chatcmpl-")) {
|
|
1452
|
+
return `gen-${id.substring(9)}`;
|
|
1453
|
+
}
|
|
1454
|
+
return id.startsWith("gen-") ? id : `gen-${id}`;
|
|
1455
|
+
}
|
|
1456
|
+
function buildRequestBody(request) {
|
|
1457
|
+
const body = {
|
|
1458
|
+
model: request.model,
|
|
1459
|
+
messages: request.messages
|
|
1460
|
+
};
|
|
1461
|
+
if (request.temperature !== void 0) body.temperature = request.temperature;
|
|
1462
|
+
if (request.max_tokens !== void 0) body.max_tokens = request.max_tokens;
|
|
1463
|
+
if (request.top_p !== void 0) body.top_p = request.top_p;
|
|
1464
|
+
if (request.frequency_penalty !== void 0) body.frequency_penalty = request.frequency_penalty;
|
|
1465
|
+
if (request.presence_penalty !== void 0) body.presence_penalty = request.presence_penalty;
|
|
1466
|
+
if (request.stop !== void 0) body.stop = request.stop;
|
|
1467
|
+
if (request.stream !== void 0) body.stream = request.stream;
|
|
1468
|
+
if (request.response_format !== void 0) body.response_format = request.response_format;
|
|
1469
|
+
if (request.tools !== void 0) body.tools = request.tools;
|
|
1470
|
+
if (request.tool_choice !== void 0) body.tool_choice = request.tool_choice;
|
|
1471
|
+
return body;
|
|
1472
|
+
}
|
|
1473
|
+
const adapter = {
|
|
1474
|
+
name: "perplexity",
|
|
1475
|
+
translateRequest(request) {
|
|
1476
|
+
return buildRequestBody(request);
|
|
1477
|
+
},
|
|
1478
|
+
translateResponse(response) {
|
|
1479
|
+
const r = response;
|
|
1480
|
+
const result = {
|
|
1481
|
+
id: rePrefixId(r.id),
|
|
1482
|
+
object: "chat.completion",
|
|
1483
|
+
created: r.created,
|
|
1484
|
+
model: `perplexity/${r.model}`,
|
|
1485
|
+
choices: r.choices,
|
|
1486
|
+
usage: r.usage
|
|
1487
|
+
};
|
|
1488
|
+
if (r.citations && result.choices?.[0]?.message) {
|
|
1489
|
+
result.citations = r.citations;
|
|
1490
|
+
}
|
|
1491
|
+
return result;
|
|
1492
|
+
},
|
|
1493
|
+
async *translateStream(stream) {
|
|
1494
|
+
const reader = stream.getReader();
|
|
1495
|
+
const decoder = new TextDecoder();
|
|
1496
|
+
let buffer = "";
|
|
1497
|
+
try {
|
|
1498
|
+
while (true) {
|
|
1499
|
+
const { done, value } = await reader.read();
|
|
1500
|
+
if (done) break;
|
|
1501
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1502
|
+
const lines = buffer.split("\n");
|
|
1503
|
+
buffer = lines.pop() || "";
|
|
1504
|
+
for (const line of lines) {
|
|
1505
|
+
const trimmed = line.trim();
|
|
1506
|
+
if (!trimmed || trimmed.startsWith(":")) continue;
|
|
1507
|
+
if (trimmed === "data: [DONE]") return;
|
|
1508
|
+
if (trimmed.startsWith("data: ")) {
|
|
1509
|
+
const json = JSON.parse(trimmed.substring(6));
|
|
1510
|
+
json.id = rePrefixId(json.id);
|
|
1511
|
+
json.model = `perplexity/${json.model}`;
|
|
1512
|
+
yield json;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
} finally {
|
|
1517
|
+
reader.releaseLock();
|
|
1518
|
+
}
|
|
1519
|
+
},
|
|
1520
|
+
translateError(error) {
|
|
1521
|
+
if (error instanceof AnyModelError) {
|
|
1522
|
+
return { code: error.code, message: error.message, metadata: error.metadata };
|
|
1523
|
+
}
|
|
1524
|
+
const err = error;
|
|
1525
|
+
const status = err?.status || err?.code || 500;
|
|
1526
|
+
return {
|
|
1527
|
+
code: mapErrorCode(status),
|
|
1528
|
+
message: err?.message || "Unknown Perplexity error",
|
|
1529
|
+
metadata: { provider_name: "perplexity", raw: error }
|
|
1530
|
+
};
|
|
1531
|
+
},
|
|
1532
|
+
async listModels() {
|
|
1533
|
+
return MODELS.map((m) => ({
|
|
1534
|
+
id: `perplexity/${m.id}`,
|
|
1535
|
+
name: m.name,
|
|
1536
|
+
created: 0,
|
|
1537
|
+
description: "",
|
|
1538
|
+
context_length: m.context,
|
|
1539
|
+
pricing: { prompt: "0", completion: "0" },
|
|
1540
|
+
architecture: {
|
|
1541
|
+
modality: m.modality,
|
|
1542
|
+
input_modalities: m.inputModalities,
|
|
1543
|
+
output_modalities: ["text"],
|
|
1544
|
+
tokenizer: "unknown"
|
|
1545
|
+
},
|
|
1546
|
+
top_provider: {
|
|
1547
|
+
context_length: m.context,
|
|
1548
|
+
max_completion_tokens: m.maxOutput,
|
|
1549
|
+
is_moderated: false
|
|
1550
|
+
},
|
|
1551
|
+
supported_parameters: Array.from(SUPPORTED_PARAMS4)
|
|
1552
|
+
}));
|
|
1553
|
+
},
|
|
1554
|
+
supportsParameter(param) {
|
|
1555
|
+
return SUPPORTED_PARAMS4.has(param);
|
|
1556
|
+
},
|
|
1557
|
+
supportsBatch() {
|
|
1558
|
+
return false;
|
|
1559
|
+
},
|
|
1560
|
+
async sendRequest(request) {
|
|
1561
|
+
const body = buildRequestBody(request);
|
|
1562
|
+
const res = await makeRequest("/chat/completions", body);
|
|
1563
|
+
const json = await res.json();
|
|
1564
|
+
return adapter.translateResponse(json);
|
|
1565
|
+
},
|
|
1566
|
+
async sendStreamingRequest(request) {
|
|
1567
|
+
const body = buildRequestBody({ ...request, stream: true });
|
|
1568
|
+
const res = await makeRequest("/chat/completions", body);
|
|
1569
|
+
if (!res.body) {
|
|
1570
|
+
throw new AnyModelError(502, "No response body for streaming request", {
|
|
1571
|
+
provider_name: "perplexity"
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
return adapter.translateStream(res.body);
|
|
1575
|
+
}
|
|
1576
|
+
};
|
|
1577
|
+
return adapter;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1373
1580
|
// src/providers/custom.ts
|
|
1374
1581
|
function createCustomAdapter(name, config) {
|
|
1375
1582
|
const openaiAdapter = createOpenAIAdapter(config.apiKey || "", config.baseURL);
|
|
@@ -2082,6 +2289,51 @@ var BatchManager = class {
|
|
|
2082
2289
|
}
|
|
2083
2290
|
};
|
|
2084
2291
|
|
|
2292
|
+
// src/utils/token-estimate.ts
|
|
2293
|
+
var CHARS_PER_TOKEN2 = 4;
|
|
2294
|
+
var MODEL_LIMITS = [
|
|
2295
|
+
// OpenAI
|
|
2296
|
+
{ pattern: "gpt-4o-mini", limit: { contextLength: 128e3, maxCompletionTokens: 16384 } },
|
|
2297
|
+
{ pattern: "gpt-4o", limit: { contextLength: 128e3, maxCompletionTokens: 16384 } },
|
|
2298
|
+
{ pattern: "gpt-4-turbo", limit: { contextLength: 128e3, maxCompletionTokens: 4096 } },
|
|
2299
|
+
{ pattern: "gpt-3.5-turbo", limit: { contextLength: 16385, maxCompletionTokens: 4096 } },
|
|
2300
|
+
{ pattern: "o1", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2301
|
+
{ pattern: "o3", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2302
|
+
{ pattern: "o4-mini", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2303
|
+
// Anthropic
|
|
2304
|
+
{ pattern: "claude-opus-4", limit: { contextLength: 2e5, maxCompletionTokens: 32768 } },
|
|
2305
|
+
{ pattern: "claude-sonnet-4", limit: { contextLength: 2e5, maxCompletionTokens: 16384 } },
|
|
2306
|
+
{ pattern: "claude-haiku-4", limit: { contextLength: 2e5, maxCompletionTokens: 8192 } },
|
|
2307
|
+
{ pattern: "claude-3.5-sonnet", limit: { contextLength: 2e5, maxCompletionTokens: 8192 } },
|
|
2308
|
+
{ pattern: "claude-3-opus", limit: { contextLength: 2e5, maxCompletionTokens: 4096 } },
|
|
2309
|
+
// Google
|
|
2310
|
+
{ pattern: "gemini-2.5-pro", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2311
|
+
{ pattern: "gemini-2.5-flash", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2312
|
+
{ pattern: "gemini-2.0-flash", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2313
|
+
{ pattern: "gemini-1.5-pro", limit: { contextLength: 2097152, maxCompletionTokens: 8192 } },
|
|
2314
|
+
{ pattern: "gemini-1.5-flash", limit: { contextLength: 1048576, maxCompletionTokens: 8192 } }
|
|
2315
|
+
];
|
|
2316
|
+
var DEFAULT_LIMIT = { contextLength: 128e3, maxCompletionTokens: 4096 };
|
|
2317
|
+
function getModelLimits(model) {
|
|
2318
|
+
const bare = model.includes("/") ? model.slice(model.indexOf("/") + 1) : model;
|
|
2319
|
+
for (const entry of MODEL_LIMITS) {
|
|
2320
|
+
if (bare.startsWith(entry.pattern) || bare.includes(entry.pattern)) {
|
|
2321
|
+
return entry.limit;
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
return DEFAULT_LIMIT;
|
|
2325
|
+
}
|
|
2326
|
+
function resolveMaxTokens(model, messages, userMaxTokens) {
|
|
2327
|
+
if (userMaxTokens !== void 0) return userMaxTokens;
|
|
2328
|
+
const inputChars = JSON.stringify(messages).length;
|
|
2329
|
+
const estimatedInput = Math.ceil(inputChars / CHARS_PER_TOKEN2);
|
|
2330
|
+
const estimatedWithMargin = Math.ceil(estimatedInput * 1.05);
|
|
2331
|
+
const limits = getModelLimits(model);
|
|
2332
|
+
const available = limits.contextLength - estimatedWithMargin;
|
|
2333
|
+
const result = Math.min(limits.maxCompletionTokens, available);
|
|
2334
|
+
return Math.max(1, result);
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2085
2337
|
// src/providers/openai-batch.ts
|
|
2086
2338
|
var OPENAI_API_BASE2 = "https://api.openai.com/v1";
|
|
2087
2339
|
function createOpenAIBatchAdapter(apiKey) {
|
|
@@ -2096,7 +2348,7 @@ function createOpenAIBatchAdapter(apiKey) {
|
|
|
2096
2348
|
headers["Content-Type"] = "application/json";
|
|
2097
2349
|
fetchBody = JSON.stringify(options.body);
|
|
2098
2350
|
}
|
|
2099
|
-
const res = await
|
|
2351
|
+
const res = await fetchWithTimeout(`${OPENAI_API_BASE2}${path2}`, {
|
|
2100
2352
|
method: options.method || "GET",
|
|
2101
2353
|
headers,
|
|
2102
2354
|
body: fetchBody
|
|
@@ -2122,7 +2374,7 @@ function createOpenAIBatchAdapter(apiKey) {
|
|
|
2122
2374
|
model,
|
|
2123
2375
|
messages: req.messages
|
|
2124
2376
|
};
|
|
2125
|
-
|
|
2377
|
+
body.max_tokens = req.max_tokens !== void 0 ? req.max_tokens : resolveMaxTokens(model, req.messages);
|
|
2126
2378
|
if (req.temperature !== void 0) body.temperature = req.temperature;
|
|
2127
2379
|
if (req.top_p !== void 0) body.top_p = req.top_p;
|
|
2128
2380
|
if (req.stop !== void 0) body.stop = req.stop;
|
|
@@ -2281,7 +2533,7 @@ function createAnthropicBatchAdapter(apiKey) {
|
|
|
2281
2533
|
"anthropic-version": ANTHROPIC_VERSION2,
|
|
2282
2534
|
"Content-Type": "application/json"
|
|
2283
2535
|
};
|
|
2284
|
-
const res = await
|
|
2536
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE2}${path2}`, {
|
|
2285
2537
|
method: options.method || "GET",
|
|
2286
2538
|
headers,
|
|
2287
2539
|
body: options.body ? JSON.stringify(options.body) : void 0
|
|
@@ -2304,7 +2556,7 @@ function createAnthropicBatchAdapter(apiKey) {
|
|
|
2304
2556
|
function translateToAnthropicParams(model, req) {
|
|
2305
2557
|
const params = {
|
|
2306
2558
|
model,
|
|
2307
|
-
max_tokens: req.max_tokens || DEFAULT_MAX_TOKENS2
|
|
2559
|
+
max_tokens: resolveMaxTokens(model, req.messages, req.max_tokens || DEFAULT_MAX_TOKENS2)
|
|
2308
2560
|
};
|
|
2309
2561
|
const systemMessages = req.messages.filter((m) => m.role === "system");
|
|
2310
2562
|
const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
|
|
@@ -2478,6 +2730,284 @@ ${params.system}` : jsonInstruction;
|
|
|
2478
2730
|
};
|
|
2479
2731
|
}
|
|
2480
2732
|
|
|
2733
|
+
// src/providers/google-batch.ts
|
|
2734
|
+
var GEMINI_API_BASE2 = "https://generativelanguage.googleapis.com/v1beta";
|
|
2735
|
+
function createGoogleBatchAdapter(apiKey) {
|
|
2736
|
+
async function apiRequest(path2, options = {}) {
|
|
2737
|
+
const headers = {
|
|
2738
|
+
"Content-Type": "application/json",
|
|
2739
|
+
"x-goog-api-key": apiKey
|
|
2740
|
+
};
|
|
2741
|
+
const res = await fetchWithTimeout(`${GEMINI_API_BASE2}${path2}`, {
|
|
2742
|
+
method: options.method || "GET",
|
|
2743
|
+
headers,
|
|
2744
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
2745
|
+
});
|
|
2746
|
+
if (!res.ok) {
|
|
2747
|
+
let errorBody;
|
|
2748
|
+
try {
|
|
2749
|
+
errorBody = await res.json();
|
|
2750
|
+
} catch {
|
|
2751
|
+
errorBody = { message: res.statusText };
|
|
2752
|
+
}
|
|
2753
|
+
const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
|
|
2754
|
+
throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
|
|
2755
|
+
provider_name: "google",
|
|
2756
|
+
raw: errorBody
|
|
2757
|
+
});
|
|
2758
|
+
}
|
|
2759
|
+
return res;
|
|
2760
|
+
}
|
|
2761
|
+
function translateRequestToGemini(model, req) {
|
|
2762
|
+
const body = {};
|
|
2763
|
+
const systemMessages = req.messages.filter((m) => m.role === "system");
|
|
2764
|
+
const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
|
|
2765
|
+
if (systemMessages.length > 0) {
|
|
2766
|
+
body.systemInstruction = {
|
|
2767
|
+
parts: [{ text: systemMessages.map((m) => typeof m.content === "string" ? m.content : "").join("\n") }]
|
|
2768
|
+
};
|
|
2769
|
+
}
|
|
2770
|
+
body.contents = nonSystemMessages.map((m) => ({
|
|
2771
|
+
role: m.role === "assistant" ? "model" : "user",
|
|
2772
|
+
parts: typeof m.content === "string" ? [{ text: m.content }] : Array.isArray(m.content) ? m.content.map((p) => p.type === "text" ? { text: p.text } : { text: "" }) : [{ text: "" }]
|
|
2773
|
+
}));
|
|
2774
|
+
const generationConfig = {};
|
|
2775
|
+
if (req.temperature !== void 0) generationConfig.temperature = req.temperature;
|
|
2776
|
+
generationConfig.maxOutputTokens = req.max_tokens !== void 0 ? req.max_tokens : resolveMaxTokens(model, req.messages);
|
|
2777
|
+
if (req.top_p !== void 0) generationConfig.topP = req.top_p;
|
|
2778
|
+
if (req.top_k !== void 0) generationConfig.topK = req.top_k;
|
|
2779
|
+
if (req.stop !== void 0) {
|
|
2780
|
+
generationConfig.stopSequences = Array.isArray(req.stop) ? req.stop : [req.stop];
|
|
2781
|
+
}
|
|
2782
|
+
if (req.response_format) {
|
|
2783
|
+
if (req.response_format.type === "json_object") {
|
|
2784
|
+
generationConfig.responseMimeType = "application/json";
|
|
2785
|
+
} else if (req.response_format.type === "json_schema") {
|
|
2786
|
+
generationConfig.responseMimeType = "application/json";
|
|
2787
|
+
generationConfig.responseSchema = req.response_format.json_schema?.schema;
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
if (Object.keys(generationConfig).length > 0) {
|
|
2791
|
+
body.generationConfig = generationConfig;
|
|
2792
|
+
}
|
|
2793
|
+
if (req.tools && req.tools.length > 0) {
|
|
2794
|
+
body.tools = [{
|
|
2795
|
+
functionDeclarations: req.tools.map((t) => ({
|
|
2796
|
+
name: t.function.name,
|
|
2797
|
+
description: t.function.description || "",
|
|
2798
|
+
parameters: t.function.parameters || {}
|
|
2799
|
+
}))
|
|
2800
|
+
}];
|
|
2801
|
+
if (req.tool_choice) {
|
|
2802
|
+
if (req.tool_choice === "auto") {
|
|
2803
|
+
body.toolConfig = { functionCallingConfig: { mode: "AUTO" } };
|
|
2804
|
+
} else if (req.tool_choice === "required") {
|
|
2805
|
+
body.toolConfig = { functionCallingConfig: { mode: "ANY" } };
|
|
2806
|
+
} else if (req.tool_choice === "none") {
|
|
2807
|
+
body.toolConfig = { functionCallingConfig: { mode: "NONE" } };
|
|
2808
|
+
} else if (typeof req.tool_choice === "object") {
|
|
2809
|
+
body.toolConfig = {
|
|
2810
|
+
functionCallingConfig: {
|
|
2811
|
+
mode: "ANY",
|
|
2812
|
+
allowedFunctionNames: [req.tool_choice.function.name]
|
|
2813
|
+
}
|
|
2814
|
+
};
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
return body;
|
|
2819
|
+
}
|
|
2820
|
+
function mapFinishReason(reason) {
|
|
2821
|
+
switch (reason) {
|
|
2822
|
+
case "STOP":
|
|
2823
|
+
return "stop";
|
|
2824
|
+
case "MAX_TOKENS":
|
|
2825
|
+
return "length";
|
|
2826
|
+
case "SAFETY":
|
|
2827
|
+
return "content_filter";
|
|
2828
|
+
case "RECITATION":
|
|
2829
|
+
return "content_filter";
|
|
2830
|
+
default:
|
|
2831
|
+
return "stop";
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
function translateGeminiResponse(response, model) {
|
|
2835
|
+
const candidate = response.candidates?.[0];
|
|
2836
|
+
let content = "";
|
|
2837
|
+
const toolCalls = [];
|
|
2838
|
+
for (const part of candidate?.content?.parts || []) {
|
|
2839
|
+
if (part.text) {
|
|
2840
|
+
content += part.text;
|
|
2841
|
+
} else if (part.functionCall) {
|
|
2842
|
+
toolCalls.push({
|
|
2843
|
+
id: generateId("call"),
|
|
2844
|
+
type: "function",
|
|
2845
|
+
function: {
|
|
2846
|
+
name: part.functionCall.name,
|
|
2847
|
+
arguments: JSON.stringify(part.functionCall.args || {})
|
|
2848
|
+
}
|
|
2849
|
+
});
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
const message = { role: "assistant", content };
|
|
2853
|
+
if (toolCalls.length > 0) {
|
|
2854
|
+
message.tool_calls = toolCalls;
|
|
2855
|
+
}
|
|
2856
|
+
const finishReason = toolCalls.length > 0 ? "tool_calls" : mapFinishReason(candidate?.finishReason || "STOP");
|
|
2857
|
+
return {
|
|
2858
|
+
id: generateId(),
|
|
2859
|
+
object: "chat.completion",
|
|
2860
|
+
created: Math.floor(Date.now() / 1e3),
|
|
2861
|
+
model: `google/${model}`,
|
|
2862
|
+
choices: [{ index: 0, message, finish_reason: finishReason }],
|
|
2863
|
+
usage: {
|
|
2864
|
+
prompt_tokens: response.usageMetadata?.promptTokenCount || 0,
|
|
2865
|
+
completion_tokens: response.usageMetadata?.candidatesTokenCount || 0,
|
|
2866
|
+
total_tokens: response.usageMetadata?.totalTokenCount || 0
|
|
2867
|
+
}
|
|
2868
|
+
};
|
|
2869
|
+
}
|
|
2870
|
+
function mapBatchState(state) {
|
|
2871
|
+
switch (state) {
|
|
2872
|
+
case "JOB_STATE_PENDING":
|
|
2873
|
+
return "pending";
|
|
2874
|
+
case "JOB_STATE_RUNNING":
|
|
2875
|
+
return "processing";
|
|
2876
|
+
case "JOB_STATE_SUCCEEDED":
|
|
2877
|
+
return "completed";
|
|
2878
|
+
case "JOB_STATE_FAILED":
|
|
2879
|
+
return "failed";
|
|
2880
|
+
case "JOB_STATE_CANCELLED":
|
|
2881
|
+
return "cancelled";
|
|
2882
|
+
case "JOB_STATE_EXPIRED":
|
|
2883
|
+
return "failed";
|
|
2884
|
+
default:
|
|
2885
|
+
return "pending";
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
return {
|
|
2889
|
+
async createBatch(model, requests, _options) {
|
|
2890
|
+
const batchRequests = requests.map((req) => ({
|
|
2891
|
+
request: translateRequestToGemini(model, req),
|
|
2892
|
+
metadata: { key: req.custom_id }
|
|
2893
|
+
}));
|
|
2894
|
+
const res = await apiRequest(`/models/${model}:batchGenerateContent`, {
|
|
2895
|
+
method: "POST",
|
|
2896
|
+
body: {
|
|
2897
|
+
batch: {
|
|
2898
|
+
display_name: `anymodel-batch-${Date.now()}`,
|
|
2899
|
+
input_config: {
|
|
2900
|
+
requests: {
|
|
2901
|
+
requests: batchRequests
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
});
|
|
2907
|
+
const data = await res.json();
|
|
2908
|
+
const batchName = data.name || data.batch?.name;
|
|
2909
|
+
if (!batchName) {
|
|
2910
|
+
throw new AnyModelError(502, "No batch name in Google response", {
|
|
2911
|
+
provider_name: "google",
|
|
2912
|
+
raw: data
|
|
2913
|
+
});
|
|
2914
|
+
}
|
|
2915
|
+
return {
|
|
2916
|
+
providerBatchId: batchName,
|
|
2917
|
+
metadata: {
|
|
2918
|
+
model,
|
|
2919
|
+
total_requests: requests.length
|
|
2920
|
+
}
|
|
2921
|
+
};
|
|
2922
|
+
},
|
|
2923
|
+
async pollBatch(providerBatchId) {
|
|
2924
|
+
const res = await apiRequest(`/${providerBatchId}`);
|
|
2925
|
+
const data = await res.json();
|
|
2926
|
+
const state = data.state || "JOB_STATE_PENDING";
|
|
2927
|
+
const status = mapBatchState(state);
|
|
2928
|
+
const totalCount = data.totalCount || data.metadata?.total_requests || 0;
|
|
2929
|
+
const successCount = data.succeededCount || 0;
|
|
2930
|
+
const failedCount = data.failedCount || 0;
|
|
2931
|
+
return {
|
|
2932
|
+
status,
|
|
2933
|
+
total: totalCount || successCount + failedCount,
|
|
2934
|
+
completed: successCount,
|
|
2935
|
+
failed: failedCount
|
|
2936
|
+
};
|
|
2937
|
+
},
|
|
2938
|
+
async getBatchResults(providerBatchId) {
|
|
2939
|
+
const batchRes = await apiRequest(`/${providerBatchId}`);
|
|
2940
|
+
const batchData = await batchRes.json();
|
|
2941
|
+
const results = [];
|
|
2942
|
+
const model = batchData.metadata?.model || "unknown";
|
|
2943
|
+
if (batchData.response?.inlinedResponses) {
|
|
2944
|
+
for (const item of batchData.response.inlinedResponses) {
|
|
2945
|
+
const customId = item.metadata?.key || `request-${results.length}`;
|
|
2946
|
+
if (item.response) {
|
|
2947
|
+
results.push({
|
|
2948
|
+
custom_id: customId,
|
|
2949
|
+
status: "success",
|
|
2950
|
+
response: translateGeminiResponse(item.response, model),
|
|
2951
|
+
error: null
|
|
2952
|
+
});
|
|
2953
|
+
} else if (item.error) {
|
|
2954
|
+
results.push({
|
|
2955
|
+
custom_id: customId,
|
|
2956
|
+
status: "error",
|
|
2957
|
+
response: null,
|
|
2958
|
+
error: {
|
|
2959
|
+
code: item.error.code || 500,
|
|
2960
|
+
message: item.error.message || "Batch item failed"
|
|
2961
|
+
}
|
|
2962
|
+
});
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
return results;
|
|
2966
|
+
}
|
|
2967
|
+
const responsesFile = batchData.response?.responsesFileName || batchData.outputConfig?.file_name;
|
|
2968
|
+
if (responsesFile) {
|
|
2969
|
+
const downloadUrl = `${GEMINI_API_BASE2}/${responsesFile}:download?alt=media`;
|
|
2970
|
+
const fileRes = await fetchWithTimeout(downloadUrl, {
|
|
2971
|
+
headers: { "x-goog-api-key": apiKey }
|
|
2972
|
+
});
|
|
2973
|
+
if (!fileRes.ok) {
|
|
2974
|
+
throw new AnyModelError(502, "Failed to download batch results file", {
|
|
2975
|
+
provider_name: "google"
|
|
2976
|
+
});
|
|
2977
|
+
}
|
|
2978
|
+
const text = await fileRes.text();
|
|
2979
|
+
for (const line of text.trim().split("\n")) {
|
|
2980
|
+
if (!line) continue;
|
|
2981
|
+
const item = JSON.parse(line);
|
|
2982
|
+
const customId = item.key || item.metadata?.key || `request-${results.length}`;
|
|
2983
|
+
if (item.response) {
|
|
2984
|
+
results.push({
|
|
2985
|
+
custom_id: customId,
|
|
2986
|
+
status: "success",
|
|
2987
|
+
response: translateGeminiResponse(item.response, model),
|
|
2988
|
+
error: null
|
|
2989
|
+
});
|
|
2990
|
+
} else if (item.error) {
|
|
2991
|
+
results.push({
|
|
2992
|
+
custom_id: customId,
|
|
2993
|
+
status: "error",
|
|
2994
|
+
response: null,
|
|
2995
|
+
error: {
|
|
2996
|
+
code: item.error.code || 500,
|
|
2997
|
+
message: item.error.message || "Batch item failed"
|
|
2998
|
+
}
|
|
2999
|
+
});
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
return results;
|
|
3004
|
+
},
|
|
3005
|
+
async cancelBatch(providerBatchId) {
|
|
3006
|
+
await apiRequest(`/${providerBatchId}:cancel`, { method: "POST" });
|
|
3007
|
+
}
|
|
3008
|
+
};
|
|
3009
|
+
}
|
|
3010
|
+
|
|
2481
3011
|
// src/client.ts
|
|
2482
3012
|
var AnyModel = class {
|
|
2483
3013
|
registry;
|
|
@@ -2493,6 +3023,7 @@ var AnyModel = class {
|
|
|
2493
3023
|
constructor(config = {}) {
|
|
2494
3024
|
this.config = resolveConfig(config);
|
|
2495
3025
|
this.registry = new ProviderRegistry();
|
|
3026
|
+
setDefaultTimeout((this.config.defaults?.timeout ?? 120) * 1e3);
|
|
2496
3027
|
if (this.config.io) {
|
|
2497
3028
|
configureFsIO(this.config.io);
|
|
2498
3029
|
}
|
|
@@ -2573,14 +3104,17 @@ var AnyModel = class {
|
|
|
2573
3104
|
if (googleKey) {
|
|
2574
3105
|
this.registry.register("google", createGoogleAdapter(googleKey));
|
|
2575
3106
|
}
|
|
3107
|
+
const perplexityKey = config.perplexity?.apiKey || process.env.PERPLEXITY_API_KEY;
|
|
3108
|
+
if (perplexityKey) {
|
|
3109
|
+
this.registry.register("perplexity", createPerplexityAdapter(perplexityKey));
|
|
3110
|
+
}
|
|
2576
3111
|
const builtinProviders = [
|
|
2577
3112
|
{ name: "mistral", baseURL: "https://api.mistral.ai/v1", configKey: "mistral", envVar: "MISTRAL_API_KEY" },
|
|
2578
3113
|
{ name: "groq", baseURL: "https://api.groq.com/openai/v1", configKey: "groq", envVar: "GROQ_API_KEY" },
|
|
2579
3114
|
{ name: "deepseek", baseURL: "https://api.deepseek.com", configKey: "deepseek", envVar: "DEEPSEEK_API_KEY" },
|
|
2580
3115
|
{ name: "xai", baseURL: "https://api.x.ai/v1", configKey: "xai", envVar: "XAI_API_KEY" },
|
|
2581
3116
|
{ name: "together", baseURL: "https://api.together.xyz/v1", configKey: "together", envVar: "TOGETHER_API_KEY" },
|
|
2582
|
-
{ name: "fireworks", baseURL: "https://api.fireworks.ai/inference/v1", configKey: "fireworks", envVar: "FIREWORKS_API_KEY" }
|
|
2583
|
-
{ name: "perplexity", baseURL: "https://api.perplexity.ai", configKey: "perplexity", envVar: "PERPLEXITY_API_KEY" }
|
|
3117
|
+
{ name: "fireworks", baseURL: "https://api.fireworks.ai/inference/v1", configKey: "fireworks", envVar: "FIREWORKS_API_KEY" }
|
|
2584
3118
|
];
|
|
2585
3119
|
for (const { name, baseURL, configKey, envVar } of builtinProviders) {
|
|
2586
3120
|
const providerConfig = config[configKey];
|
|
@@ -2610,6 +3144,10 @@ var AnyModel = class {
|
|
|
2610
3144
|
if (anthropicKey) {
|
|
2611
3145
|
this.batchManager.registerBatchAdapter("anthropic", createAnthropicBatchAdapter(anthropicKey));
|
|
2612
3146
|
}
|
|
3147
|
+
const googleKey = config.google?.apiKey || process.env.GOOGLE_API_KEY;
|
|
3148
|
+
if (googleKey) {
|
|
3149
|
+
this.batchManager.registerBatchAdapter("google", createGoogleBatchAdapter(googleKey));
|
|
3150
|
+
}
|
|
2613
3151
|
}
|
|
2614
3152
|
applyDefaults(request) {
|
|
2615
3153
|
const defaults = this.config.defaults;
|