@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.cjs
CHANGED
|
@@ -39,12 +39,15 @@ __export(src_exports, {
|
|
|
39
39
|
configureFsIO: () => configureFsIO,
|
|
40
40
|
createAnthropicBatchAdapter: () => createAnthropicBatchAdapter,
|
|
41
41
|
createAnyModelServer: () => createAnyModelServer,
|
|
42
|
+
createGoogleBatchAdapter: () => createGoogleBatchAdapter,
|
|
42
43
|
createOpenAIBatchAdapter: () => createOpenAIBatchAdapter,
|
|
43
44
|
ensureDir: () => ensureDir,
|
|
45
|
+
estimateTokenCount: () => estimateTokenCount,
|
|
44
46
|
getFsQueueStatus: () => getFsQueueStatus,
|
|
45
47
|
joinPath: () => joinPath,
|
|
46
48
|
readFileQueued: () => readFileQueued,
|
|
47
49
|
resolveConfig: () => resolveConfig,
|
|
50
|
+
resolveMaxTokens: () => resolveMaxTokens,
|
|
48
51
|
startServer: () => startServer,
|
|
49
52
|
waitForFsQueuesIdle: () => waitForFsQueuesIdle,
|
|
50
53
|
writeFileFlushedQueued: () => writeFileFlushedQueued,
|
|
@@ -534,6 +537,25 @@ var Router = class {
|
|
|
534
537
|
}
|
|
535
538
|
};
|
|
536
539
|
|
|
540
|
+
// src/utils/fetch-with-timeout.ts
|
|
541
|
+
var _defaultTimeout = 12e4;
|
|
542
|
+
var _flexTimeout = 6e5;
|
|
543
|
+
function setDefaultTimeout(ms) {
|
|
544
|
+
_defaultTimeout = ms;
|
|
545
|
+
}
|
|
546
|
+
function getFlexTimeout() {
|
|
547
|
+
return _flexTimeout;
|
|
548
|
+
}
|
|
549
|
+
function fetchWithTimeout(url, init, timeoutMs) {
|
|
550
|
+
const ms = timeoutMs ?? _defaultTimeout;
|
|
551
|
+
const signal = AbortSignal.timeout(ms);
|
|
552
|
+
if (init?.signal) {
|
|
553
|
+
const combined = AbortSignal.any([signal, init.signal]);
|
|
554
|
+
return fetch(url, { ...init, signal: combined });
|
|
555
|
+
}
|
|
556
|
+
return fetch(url, { ...init, signal });
|
|
557
|
+
}
|
|
558
|
+
|
|
537
559
|
// src/providers/openai.ts
|
|
538
560
|
var OPENAI_API_BASE = "https://api.openai.com/v1";
|
|
539
561
|
var SUPPORTED_PARAMS = /* @__PURE__ */ new Set([
|
|
@@ -551,19 +573,20 @@ var SUPPORTED_PARAMS = /* @__PURE__ */ new Set([
|
|
|
551
573
|
"tools",
|
|
552
574
|
"tool_choice",
|
|
553
575
|
"user",
|
|
554
|
-
"logit_bias"
|
|
576
|
+
"logit_bias",
|
|
577
|
+
"service_tier"
|
|
555
578
|
]);
|
|
556
579
|
function createOpenAIAdapter(apiKey, baseURL) {
|
|
557
580
|
const base = baseURL || OPENAI_API_BASE;
|
|
558
|
-
async function makeRequest(path2, body, method = "POST") {
|
|
559
|
-
const res = await
|
|
581
|
+
async function makeRequest(path2, body, method = "POST", timeoutMs) {
|
|
582
|
+
const res = await fetchWithTimeout(`${base}${path2}`, {
|
|
560
583
|
method,
|
|
561
584
|
headers: {
|
|
562
585
|
"Content-Type": "application/json",
|
|
563
586
|
"Authorization": `Bearer ${apiKey}`
|
|
564
587
|
},
|
|
565
588
|
body: body ? JSON.stringify(body) : void 0
|
|
566
|
-
});
|
|
589
|
+
}, timeoutMs);
|
|
567
590
|
if (!res.ok) {
|
|
568
591
|
let errorBody;
|
|
569
592
|
try {
|
|
@@ -611,6 +634,7 @@ function createOpenAIAdapter(apiKey, baseURL) {
|
|
|
611
634
|
if (request.tools !== void 0) body.tools = request.tools;
|
|
612
635
|
if (request.tool_choice !== void 0) body.tool_choice = request.tool_choice;
|
|
613
636
|
if (request.user !== void 0) body.user = request.user;
|
|
637
|
+
if (request.service_tier !== void 0) body.service_tier = request.service_tier;
|
|
614
638
|
return body;
|
|
615
639
|
}
|
|
616
640
|
const adapter = {
|
|
@@ -712,13 +736,15 @@ function createOpenAIAdapter(apiKey, baseURL) {
|
|
|
712
736
|
},
|
|
713
737
|
async sendRequest(request) {
|
|
714
738
|
const body = buildRequestBody(request);
|
|
715
|
-
const
|
|
739
|
+
const timeout = request.service_tier === "flex" ? getFlexTimeout() : void 0;
|
|
740
|
+
const res = await makeRequest("/chat/completions", body, "POST", timeout);
|
|
716
741
|
const json = await res.json();
|
|
717
742
|
return adapter.translateResponse(json);
|
|
718
743
|
},
|
|
719
744
|
async sendStreamingRequest(request) {
|
|
720
745
|
const body = buildRequestBody({ ...request, stream: true });
|
|
721
|
-
const
|
|
746
|
+
const timeout = request.service_tier === "flex" ? getFlexTimeout() : void 0;
|
|
747
|
+
const res = await makeRequest("/chat/completions", body, "POST", timeout);
|
|
722
748
|
if (!res.body) {
|
|
723
749
|
throw new AnyModelError(502, "No response body for streaming request", {
|
|
724
750
|
provider_name: "openai"
|
|
@@ -765,7 +791,7 @@ var FALLBACK_MODELS = [
|
|
|
765
791
|
];
|
|
766
792
|
function createAnthropicAdapter(apiKey) {
|
|
767
793
|
async function makeRequest(path2, body, stream = false) {
|
|
768
|
-
const res = await
|
|
794
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE}${path2}`, {
|
|
769
795
|
method: "POST",
|
|
770
796
|
headers: {
|
|
771
797
|
"Content-Type": "application/json",
|
|
@@ -1022,7 +1048,7 @@ ${body.system}` : jsonInstruction;
|
|
|
1022
1048
|
},
|
|
1023
1049
|
async listModels() {
|
|
1024
1050
|
try {
|
|
1025
|
-
const res = await
|
|
1051
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE}/models`, {
|
|
1026
1052
|
method: "GET",
|
|
1027
1053
|
headers: {
|
|
1028
1054
|
"x-api-key": apiKey,
|
|
@@ -1307,7 +1333,7 @@ function createGoogleAdapter(apiKey) {
|
|
|
1307
1333
|
},
|
|
1308
1334
|
async listModels() {
|
|
1309
1335
|
try {
|
|
1310
|
-
const res = await
|
|
1336
|
+
const res = await fetchWithTimeout(`${GEMINI_API_BASE}/models?key=${apiKey}`);
|
|
1311
1337
|
if (!res.ok) return FALLBACK_MODELS2;
|
|
1312
1338
|
const data = await res.json();
|
|
1313
1339
|
const models = data.models || [];
|
|
@@ -1342,12 +1368,12 @@ function createGoogleAdapter(apiKey) {
|
|
|
1342
1368
|
return SUPPORTED_PARAMS3.has(param);
|
|
1343
1369
|
},
|
|
1344
1370
|
supportsBatch() {
|
|
1345
|
-
return
|
|
1371
|
+
return true;
|
|
1346
1372
|
},
|
|
1347
1373
|
async sendRequest(request) {
|
|
1348
1374
|
const body = translateRequest(request);
|
|
1349
1375
|
const url = getModelEndpoint(request.model, false);
|
|
1350
|
-
const res = await
|
|
1376
|
+
const res = await fetchWithTimeout(url, {
|
|
1351
1377
|
method: "POST",
|
|
1352
1378
|
headers: { "Content-Type": "application/json" },
|
|
1353
1379
|
body: JSON.stringify(body)
|
|
@@ -1370,7 +1396,7 @@ function createGoogleAdapter(apiKey) {
|
|
|
1370
1396
|
async sendStreamingRequest(request) {
|
|
1371
1397
|
const body = translateRequest(request);
|
|
1372
1398
|
const url = getModelEndpoint(request.model, true);
|
|
1373
|
-
const res = await
|
|
1399
|
+
const res = await fetchWithTimeout(url, {
|
|
1374
1400
|
method: "POST",
|
|
1375
1401
|
headers: { "Content-Type": "application/json" },
|
|
1376
1402
|
body: JSON.stringify(body)
|
|
@@ -1396,6 +1422,190 @@ function createGoogleAdapter(apiKey) {
|
|
|
1396
1422
|
return adapter;
|
|
1397
1423
|
}
|
|
1398
1424
|
|
|
1425
|
+
// src/providers/perplexity.ts
|
|
1426
|
+
var PERPLEXITY_API_BASE = "https://api.perplexity.ai";
|
|
1427
|
+
var SUPPORTED_PARAMS4 = /* @__PURE__ */ new Set([
|
|
1428
|
+
"temperature",
|
|
1429
|
+
"max_tokens",
|
|
1430
|
+
"top_p",
|
|
1431
|
+
"frequency_penalty",
|
|
1432
|
+
"presence_penalty",
|
|
1433
|
+
"stream",
|
|
1434
|
+
"stop",
|
|
1435
|
+
"response_format",
|
|
1436
|
+
"tools",
|
|
1437
|
+
"tool_choice"
|
|
1438
|
+
]);
|
|
1439
|
+
var MODELS = [
|
|
1440
|
+
{ id: "sonar", name: "Sonar", context: 128e3, maxOutput: 4096, modality: "text->text", inputModalities: ["text"] },
|
|
1441
|
+
{ id: "sonar-pro", name: "Sonar Pro", context: 2e5, maxOutput: 8192, modality: "text->text", inputModalities: ["text"] },
|
|
1442
|
+
{ id: "sonar-reasoning", name: "Sonar Reasoning", context: 128e3, maxOutput: 8192, modality: "text->text", inputModalities: ["text"] },
|
|
1443
|
+
{ id: "sonar-reasoning-pro", name: "Sonar Reasoning Pro", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] },
|
|
1444
|
+
{ id: "sonar-deep-research", name: "Sonar Deep Research", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] },
|
|
1445
|
+
{ id: "r1-1776", name: "R1 1776", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] }
|
|
1446
|
+
];
|
|
1447
|
+
function createPerplexityAdapter(apiKey) {
|
|
1448
|
+
async function makeRequest(path2, body, method = "POST") {
|
|
1449
|
+
const res = await fetchWithTimeout(`${PERPLEXITY_API_BASE}${path2}`, {
|
|
1450
|
+
method,
|
|
1451
|
+
headers: {
|
|
1452
|
+
"Content-Type": "application/json",
|
|
1453
|
+
"Authorization": `Bearer ${apiKey}`
|
|
1454
|
+
},
|
|
1455
|
+
body: body ? JSON.stringify(body) : void 0
|
|
1456
|
+
});
|
|
1457
|
+
if (!res.ok) {
|
|
1458
|
+
let errorBody;
|
|
1459
|
+
try {
|
|
1460
|
+
errorBody = await res.json();
|
|
1461
|
+
} catch {
|
|
1462
|
+
errorBody = { message: res.statusText };
|
|
1463
|
+
}
|
|
1464
|
+
const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
|
|
1465
|
+
throw new AnyModelError(mapErrorCode(res.status), msg, {
|
|
1466
|
+
provider_name: "perplexity",
|
|
1467
|
+
raw: errorBody
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1470
|
+
return res;
|
|
1471
|
+
}
|
|
1472
|
+
function mapErrorCode(status) {
|
|
1473
|
+
if (status === 401 || status === 403) return 401;
|
|
1474
|
+
if (status === 429) return 429;
|
|
1475
|
+
if (status === 400 || status === 422) return 400;
|
|
1476
|
+
if (status >= 500) return 502;
|
|
1477
|
+
return status;
|
|
1478
|
+
}
|
|
1479
|
+
function rePrefixId(id) {
|
|
1480
|
+
if (id && id.startsWith("chatcmpl-")) {
|
|
1481
|
+
return `gen-${id.substring(9)}`;
|
|
1482
|
+
}
|
|
1483
|
+
return id.startsWith("gen-") ? id : `gen-${id}`;
|
|
1484
|
+
}
|
|
1485
|
+
function buildRequestBody(request) {
|
|
1486
|
+
const body = {
|
|
1487
|
+
model: request.model,
|
|
1488
|
+
messages: request.messages
|
|
1489
|
+
};
|
|
1490
|
+
if (request.temperature !== void 0) body.temperature = request.temperature;
|
|
1491
|
+
if (request.max_tokens !== void 0) body.max_tokens = request.max_tokens;
|
|
1492
|
+
if (request.top_p !== void 0) body.top_p = request.top_p;
|
|
1493
|
+
if (request.frequency_penalty !== void 0) body.frequency_penalty = request.frequency_penalty;
|
|
1494
|
+
if (request.presence_penalty !== void 0) body.presence_penalty = request.presence_penalty;
|
|
1495
|
+
if (request.stop !== void 0) body.stop = request.stop;
|
|
1496
|
+
if (request.stream !== void 0) body.stream = request.stream;
|
|
1497
|
+
if (request.response_format !== void 0) body.response_format = request.response_format;
|
|
1498
|
+
if (request.tools !== void 0) body.tools = request.tools;
|
|
1499
|
+
if (request.tool_choice !== void 0) body.tool_choice = request.tool_choice;
|
|
1500
|
+
return body;
|
|
1501
|
+
}
|
|
1502
|
+
const adapter = {
|
|
1503
|
+
name: "perplexity",
|
|
1504
|
+
translateRequest(request) {
|
|
1505
|
+
return buildRequestBody(request);
|
|
1506
|
+
},
|
|
1507
|
+
translateResponse(response) {
|
|
1508
|
+
const r = response;
|
|
1509
|
+
const result = {
|
|
1510
|
+
id: rePrefixId(r.id),
|
|
1511
|
+
object: "chat.completion",
|
|
1512
|
+
created: r.created,
|
|
1513
|
+
model: `perplexity/${r.model}`,
|
|
1514
|
+
choices: r.choices,
|
|
1515
|
+
usage: r.usage
|
|
1516
|
+
};
|
|
1517
|
+
if (r.citations && result.choices?.[0]?.message) {
|
|
1518
|
+
result.citations = r.citations;
|
|
1519
|
+
}
|
|
1520
|
+
return result;
|
|
1521
|
+
},
|
|
1522
|
+
async *translateStream(stream) {
|
|
1523
|
+
const reader = stream.getReader();
|
|
1524
|
+
const decoder = new TextDecoder();
|
|
1525
|
+
let buffer = "";
|
|
1526
|
+
try {
|
|
1527
|
+
while (true) {
|
|
1528
|
+
const { done, value } = await reader.read();
|
|
1529
|
+
if (done) break;
|
|
1530
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1531
|
+
const lines = buffer.split("\n");
|
|
1532
|
+
buffer = lines.pop() || "";
|
|
1533
|
+
for (const line of lines) {
|
|
1534
|
+
const trimmed = line.trim();
|
|
1535
|
+
if (!trimmed || trimmed.startsWith(":")) continue;
|
|
1536
|
+
if (trimmed === "data: [DONE]") return;
|
|
1537
|
+
if (trimmed.startsWith("data: ")) {
|
|
1538
|
+
const json = JSON.parse(trimmed.substring(6));
|
|
1539
|
+
json.id = rePrefixId(json.id);
|
|
1540
|
+
json.model = `perplexity/${json.model}`;
|
|
1541
|
+
yield json;
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
} finally {
|
|
1546
|
+
reader.releaseLock();
|
|
1547
|
+
}
|
|
1548
|
+
},
|
|
1549
|
+
translateError(error) {
|
|
1550
|
+
if (error instanceof AnyModelError) {
|
|
1551
|
+
return { code: error.code, message: error.message, metadata: error.metadata };
|
|
1552
|
+
}
|
|
1553
|
+
const err = error;
|
|
1554
|
+
const status = err?.status || err?.code || 500;
|
|
1555
|
+
return {
|
|
1556
|
+
code: mapErrorCode(status),
|
|
1557
|
+
message: err?.message || "Unknown Perplexity error",
|
|
1558
|
+
metadata: { provider_name: "perplexity", raw: error }
|
|
1559
|
+
};
|
|
1560
|
+
},
|
|
1561
|
+
async listModels() {
|
|
1562
|
+
return MODELS.map((m) => ({
|
|
1563
|
+
id: `perplexity/${m.id}`,
|
|
1564
|
+
name: m.name,
|
|
1565
|
+
created: 0,
|
|
1566
|
+
description: "",
|
|
1567
|
+
context_length: m.context,
|
|
1568
|
+
pricing: { prompt: "0", completion: "0" },
|
|
1569
|
+
architecture: {
|
|
1570
|
+
modality: m.modality,
|
|
1571
|
+
input_modalities: m.inputModalities,
|
|
1572
|
+
output_modalities: ["text"],
|
|
1573
|
+
tokenizer: "unknown"
|
|
1574
|
+
},
|
|
1575
|
+
top_provider: {
|
|
1576
|
+
context_length: m.context,
|
|
1577
|
+
max_completion_tokens: m.maxOutput,
|
|
1578
|
+
is_moderated: false
|
|
1579
|
+
},
|
|
1580
|
+
supported_parameters: Array.from(SUPPORTED_PARAMS4)
|
|
1581
|
+
}));
|
|
1582
|
+
},
|
|
1583
|
+
supportsParameter(param) {
|
|
1584
|
+
return SUPPORTED_PARAMS4.has(param);
|
|
1585
|
+
},
|
|
1586
|
+
supportsBatch() {
|
|
1587
|
+
return false;
|
|
1588
|
+
},
|
|
1589
|
+
async sendRequest(request) {
|
|
1590
|
+
const body = buildRequestBody(request);
|
|
1591
|
+
const res = await makeRequest("/chat/completions", body);
|
|
1592
|
+
const json = await res.json();
|
|
1593
|
+
return adapter.translateResponse(json);
|
|
1594
|
+
},
|
|
1595
|
+
async sendStreamingRequest(request) {
|
|
1596
|
+
const body = buildRequestBody({ ...request, stream: true });
|
|
1597
|
+
const res = await makeRequest("/chat/completions", body);
|
|
1598
|
+
if (!res.body) {
|
|
1599
|
+
throw new AnyModelError(502, "No response body for streaming request", {
|
|
1600
|
+
provider_name: "perplexity"
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
return adapter.translateStream(res.body);
|
|
1604
|
+
}
|
|
1605
|
+
};
|
|
1606
|
+
return adapter;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1399
1609
|
// src/providers/custom.ts
|
|
1400
1610
|
function createCustomAdapter(name, config) {
|
|
1401
1611
|
const openaiAdapter = createOpenAIAdapter(config.apiKey || "", config.baseURL);
|
|
@@ -2117,6 +2327,54 @@ var BatchManager = class {
|
|
|
2117
2327
|
}
|
|
2118
2328
|
};
|
|
2119
2329
|
|
|
2330
|
+
// src/utils/token-estimate.ts
|
|
2331
|
+
var CHARS_PER_TOKEN2 = 4;
|
|
2332
|
+
function estimateTokenCount(text) {
|
|
2333
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN2);
|
|
2334
|
+
}
|
|
2335
|
+
var MODEL_LIMITS = [
|
|
2336
|
+
// OpenAI
|
|
2337
|
+
{ pattern: "gpt-4o-mini", limit: { contextLength: 128e3, maxCompletionTokens: 16384 } },
|
|
2338
|
+
{ pattern: "gpt-4o", limit: { contextLength: 128e3, maxCompletionTokens: 16384 } },
|
|
2339
|
+
{ pattern: "gpt-4-turbo", limit: { contextLength: 128e3, maxCompletionTokens: 4096 } },
|
|
2340
|
+
{ pattern: "gpt-3.5-turbo", limit: { contextLength: 16385, maxCompletionTokens: 4096 } },
|
|
2341
|
+
{ pattern: "o1", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2342
|
+
{ pattern: "o3", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2343
|
+
{ pattern: "o4-mini", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2344
|
+
// Anthropic
|
|
2345
|
+
{ pattern: "claude-opus-4", limit: { contextLength: 2e5, maxCompletionTokens: 32768 } },
|
|
2346
|
+
{ pattern: "claude-sonnet-4", limit: { contextLength: 2e5, maxCompletionTokens: 16384 } },
|
|
2347
|
+
{ pattern: "claude-haiku-4", limit: { contextLength: 2e5, maxCompletionTokens: 8192 } },
|
|
2348
|
+
{ pattern: "claude-3.5-sonnet", limit: { contextLength: 2e5, maxCompletionTokens: 8192 } },
|
|
2349
|
+
{ pattern: "claude-3-opus", limit: { contextLength: 2e5, maxCompletionTokens: 4096 } },
|
|
2350
|
+
// Google
|
|
2351
|
+
{ pattern: "gemini-2.5-pro", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2352
|
+
{ pattern: "gemini-2.5-flash", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2353
|
+
{ pattern: "gemini-2.0-flash", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2354
|
+
{ pattern: "gemini-1.5-pro", limit: { contextLength: 2097152, maxCompletionTokens: 8192 } },
|
|
2355
|
+
{ pattern: "gemini-1.5-flash", limit: { contextLength: 1048576, maxCompletionTokens: 8192 } }
|
|
2356
|
+
];
|
|
2357
|
+
var DEFAULT_LIMIT = { contextLength: 128e3, maxCompletionTokens: 4096 };
|
|
2358
|
+
function getModelLimits(model) {
|
|
2359
|
+
const bare = model.includes("/") ? model.slice(model.indexOf("/") + 1) : model;
|
|
2360
|
+
for (const entry of MODEL_LIMITS) {
|
|
2361
|
+
if (bare.startsWith(entry.pattern) || bare.includes(entry.pattern)) {
|
|
2362
|
+
return entry.limit;
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
return DEFAULT_LIMIT;
|
|
2366
|
+
}
|
|
2367
|
+
function resolveMaxTokens(model, messages, userMaxTokens) {
|
|
2368
|
+
if (userMaxTokens !== void 0) return userMaxTokens;
|
|
2369
|
+
const inputChars = JSON.stringify(messages).length;
|
|
2370
|
+
const estimatedInput = Math.ceil(inputChars / CHARS_PER_TOKEN2);
|
|
2371
|
+
const estimatedWithMargin = Math.ceil(estimatedInput * 1.05);
|
|
2372
|
+
const limits = getModelLimits(model);
|
|
2373
|
+
const available = limits.contextLength - estimatedWithMargin;
|
|
2374
|
+
const result = Math.min(limits.maxCompletionTokens, available);
|
|
2375
|
+
return Math.max(1, result);
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2120
2378
|
// src/providers/openai-batch.ts
|
|
2121
2379
|
var OPENAI_API_BASE2 = "https://api.openai.com/v1";
|
|
2122
2380
|
function createOpenAIBatchAdapter(apiKey) {
|
|
@@ -2131,7 +2389,7 @@ function createOpenAIBatchAdapter(apiKey) {
|
|
|
2131
2389
|
headers["Content-Type"] = "application/json";
|
|
2132
2390
|
fetchBody = JSON.stringify(options.body);
|
|
2133
2391
|
}
|
|
2134
|
-
const res = await
|
|
2392
|
+
const res = await fetchWithTimeout(`${OPENAI_API_BASE2}${path2}`, {
|
|
2135
2393
|
method: options.method || "GET",
|
|
2136
2394
|
headers,
|
|
2137
2395
|
body: fetchBody
|
|
@@ -2157,7 +2415,7 @@ function createOpenAIBatchAdapter(apiKey) {
|
|
|
2157
2415
|
model,
|
|
2158
2416
|
messages: req.messages
|
|
2159
2417
|
};
|
|
2160
|
-
|
|
2418
|
+
body.max_tokens = req.max_tokens !== void 0 ? req.max_tokens : resolveMaxTokens(model, req.messages);
|
|
2161
2419
|
if (req.temperature !== void 0) body.temperature = req.temperature;
|
|
2162
2420
|
if (req.top_p !== void 0) body.top_p = req.top_p;
|
|
2163
2421
|
if (req.stop !== void 0) body.stop = req.stop;
|
|
@@ -2316,7 +2574,7 @@ function createAnthropicBatchAdapter(apiKey) {
|
|
|
2316
2574
|
"anthropic-version": ANTHROPIC_VERSION2,
|
|
2317
2575
|
"Content-Type": "application/json"
|
|
2318
2576
|
};
|
|
2319
|
-
const res = await
|
|
2577
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE2}${path2}`, {
|
|
2320
2578
|
method: options.method || "GET",
|
|
2321
2579
|
headers,
|
|
2322
2580
|
body: options.body ? JSON.stringify(options.body) : void 0
|
|
@@ -2339,7 +2597,7 @@ function createAnthropicBatchAdapter(apiKey) {
|
|
|
2339
2597
|
function translateToAnthropicParams(model, req) {
|
|
2340
2598
|
const params = {
|
|
2341
2599
|
model,
|
|
2342
|
-
max_tokens: req.max_tokens || DEFAULT_MAX_TOKENS2
|
|
2600
|
+
max_tokens: resolveMaxTokens(model, req.messages, req.max_tokens || DEFAULT_MAX_TOKENS2)
|
|
2343
2601
|
};
|
|
2344
2602
|
const systemMessages = req.messages.filter((m) => m.role === "system");
|
|
2345
2603
|
const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
|
|
@@ -2513,6 +2771,284 @@ ${params.system}` : jsonInstruction;
|
|
|
2513
2771
|
};
|
|
2514
2772
|
}
|
|
2515
2773
|
|
|
2774
|
+
// src/providers/google-batch.ts
|
|
2775
|
+
var GEMINI_API_BASE2 = "https://generativelanguage.googleapis.com/v1beta";
|
|
2776
|
+
function createGoogleBatchAdapter(apiKey) {
|
|
2777
|
+
async function apiRequest(path2, options = {}) {
|
|
2778
|
+
const headers = {
|
|
2779
|
+
"Content-Type": "application/json",
|
|
2780
|
+
"x-goog-api-key": apiKey
|
|
2781
|
+
};
|
|
2782
|
+
const res = await fetchWithTimeout(`${GEMINI_API_BASE2}${path2}`, {
|
|
2783
|
+
method: options.method || "GET",
|
|
2784
|
+
headers,
|
|
2785
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
2786
|
+
});
|
|
2787
|
+
if (!res.ok) {
|
|
2788
|
+
let errorBody;
|
|
2789
|
+
try {
|
|
2790
|
+
errorBody = await res.json();
|
|
2791
|
+
} catch {
|
|
2792
|
+
errorBody = { message: res.statusText };
|
|
2793
|
+
}
|
|
2794
|
+
const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
|
|
2795
|
+
throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
|
|
2796
|
+
provider_name: "google",
|
|
2797
|
+
raw: errorBody
|
|
2798
|
+
});
|
|
2799
|
+
}
|
|
2800
|
+
return res;
|
|
2801
|
+
}
|
|
2802
|
+
function translateRequestToGemini(model, req) {
|
|
2803
|
+
const body = {};
|
|
2804
|
+
const systemMessages = req.messages.filter((m) => m.role === "system");
|
|
2805
|
+
const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
|
|
2806
|
+
if (systemMessages.length > 0) {
|
|
2807
|
+
body.systemInstruction = {
|
|
2808
|
+
parts: [{ text: systemMessages.map((m) => typeof m.content === "string" ? m.content : "").join("\n") }]
|
|
2809
|
+
};
|
|
2810
|
+
}
|
|
2811
|
+
body.contents = nonSystemMessages.map((m) => ({
|
|
2812
|
+
role: m.role === "assistant" ? "model" : "user",
|
|
2813
|
+
parts: typeof m.content === "string" ? [{ text: m.content }] : Array.isArray(m.content) ? m.content.map((p) => p.type === "text" ? { text: p.text } : { text: "" }) : [{ text: "" }]
|
|
2814
|
+
}));
|
|
2815
|
+
const generationConfig = {};
|
|
2816
|
+
if (req.temperature !== void 0) generationConfig.temperature = req.temperature;
|
|
2817
|
+
generationConfig.maxOutputTokens = req.max_tokens !== void 0 ? req.max_tokens : resolveMaxTokens(model, req.messages);
|
|
2818
|
+
if (req.top_p !== void 0) generationConfig.topP = req.top_p;
|
|
2819
|
+
if (req.top_k !== void 0) generationConfig.topK = req.top_k;
|
|
2820
|
+
if (req.stop !== void 0) {
|
|
2821
|
+
generationConfig.stopSequences = Array.isArray(req.stop) ? req.stop : [req.stop];
|
|
2822
|
+
}
|
|
2823
|
+
if (req.response_format) {
|
|
2824
|
+
if (req.response_format.type === "json_object") {
|
|
2825
|
+
generationConfig.responseMimeType = "application/json";
|
|
2826
|
+
} else if (req.response_format.type === "json_schema") {
|
|
2827
|
+
generationConfig.responseMimeType = "application/json";
|
|
2828
|
+
generationConfig.responseSchema = req.response_format.json_schema?.schema;
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
if (Object.keys(generationConfig).length > 0) {
|
|
2832
|
+
body.generationConfig = generationConfig;
|
|
2833
|
+
}
|
|
2834
|
+
if (req.tools && req.tools.length > 0) {
|
|
2835
|
+
body.tools = [{
|
|
2836
|
+
functionDeclarations: req.tools.map((t) => ({
|
|
2837
|
+
name: t.function.name,
|
|
2838
|
+
description: t.function.description || "",
|
|
2839
|
+
parameters: t.function.parameters || {}
|
|
2840
|
+
}))
|
|
2841
|
+
}];
|
|
2842
|
+
if (req.tool_choice) {
|
|
2843
|
+
if (req.tool_choice === "auto") {
|
|
2844
|
+
body.toolConfig = { functionCallingConfig: { mode: "AUTO" } };
|
|
2845
|
+
} else if (req.tool_choice === "required") {
|
|
2846
|
+
body.toolConfig = { functionCallingConfig: { mode: "ANY" } };
|
|
2847
|
+
} else if (req.tool_choice === "none") {
|
|
2848
|
+
body.toolConfig = { functionCallingConfig: { mode: "NONE" } };
|
|
2849
|
+
} else if (typeof req.tool_choice === "object") {
|
|
2850
|
+
body.toolConfig = {
|
|
2851
|
+
functionCallingConfig: {
|
|
2852
|
+
mode: "ANY",
|
|
2853
|
+
allowedFunctionNames: [req.tool_choice.function.name]
|
|
2854
|
+
}
|
|
2855
|
+
};
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
return body;
|
|
2860
|
+
}
|
|
2861
|
+
function mapFinishReason(reason) {
|
|
2862
|
+
switch (reason) {
|
|
2863
|
+
case "STOP":
|
|
2864
|
+
return "stop";
|
|
2865
|
+
case "MAX_TOKENS":
|
|
2866
|
+
return "length";
|
|
2867
|
+
case "SAFETY":
|
|
2868
|
+
return "content_filter";
|
|
2869
|
+
case "RECITATION":
|
|
2870
|
+
return "content_filter";
|
|
2871
|
+
default:
|
|
2872
|
+
return "stop";
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
function translateGeminiResponse(response, model) {
|
|
2876
|
+
const candidate = response.candidates?.[0];
|
|
2877
|
+
let content = "";
|
|
2878
|
+
const toolCalls = [];
|
|
2879
|
+
for (const part of candidate?.content?.parts || []) {
|
|
2880
|
+
if (part.text) {
|
|
2881
|
+
content += part.text;
|
|
2882
|
+
} else if (part.functionCall) {
|
|
2883
|
+
toolCalls.push({
|
|
2884
|
+
id: generateId("call"),
|
|
2885
|
+
type: "function",
|
|
2886
|
+
function: {
|
|
2887
|
+
name: part.functionCall.name,
|
|
2888
|
+
arguments: JSON.stringify(part.functionCall.args || {})
|
|
2889
|
+
}
|
|
2890
|
+
});
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
const message = { role: "assistant", content };
|
|
2894
|
+
if (toolCalls.length > 0) {
|
|
2895
|
+
message.tool_calls = toolCalls;
|
|
2896
|
+
}
|
|
2897
|
+
const finishReason = toolCalls.length > 0 ? "tool_calls" : mapFinishReason(candidate?.finishReason || "STOP");
|
|
2898
|
+
return {
|
|
2899
|
+
id: generateId(),
|
|
2900
|
+
object: "chat.completion",
|
|
2901
|
+
created: Math.floor(Date.now() / 1e3),
|
|
2902
|
+
model: `google/${model}`,
|
|
2903
|
+
choices: [{ index: 0, message, finish_reason: finishReason }],
|
|
2904
|
+
usage: {
|
|
2905
|
+
prompt_tokens: response.usageMetadata?.promptTokenCount || 0,
|
|
2906
|
+
completion_tokens: response.usageMetadata?.candidatesTokenCount || 0,
|
|
2907
|
+
total_tokens: response.usageMetadata?.totalTokenCount || 0
|
|
2908
|
+
}
|
|
2909
|
+
};
|
|
2910
|
+
}
|
|
2911
|
+
function mapBatchState(state) {
|
|
2912
|
+
switch (state) {
|
|
2913
|
+
case "JOB_STATE_PENDING":
|
|
2914
|
+
return "pending";
|
|
2915
|
+
case "JOB_STATE_RUNNING":
|
|
2916
|
+
return "processing";
|
|
2917
|
+
case "JOB_STATE_SUCCEEDED":
|
|
2918
|
+
return "completed";
|
|
2919
|
+
case "JOB_STATE_FAILED":
|
|
2920
|
+
return "failed";
|
|
2921
|
+
case "JOB_STATE_CANCELLED":
|
|
2922
|
+
return "cancelled";
|
|
2923
|
+
case "JOB_STATE_EXPIRED":
|
|
2924
|
+
return "failed";
|
|
2925
|
+
default:
|
|
2926
|
+
return "pending";
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
return {
|
|
2930
|
+
async createBatch(model, requests, _options) {
|
|
2931
|
+
const batchRequests = requests.map((req) => ({
|
|
2932
|
+
request: translateRequestToGemini(model, req),
|
|
2933
|
+
metadata: { key: req.custom_id }
|
|
2934
|
+
}));
|
|
2935
|
+
const res = await apiRequest(`/models/${model}:batchGenerateContent`, {
|
|
2936
|
+
method: "POST",
|
|
2937
|
+
body: {
|
|
2938
|
+
batch: {
|
|
2939
|
+
display_name: `anymodel-batch-${Date.now()}`,
|
|
2940
|
+
input_config: {
|
|
2941
|
+
requests: {
|
|
2942
|
+
requests: batchRequests
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
});
|
|
2948
|
+
const data = await res.json();
|
|
2949
|
+
const batchName = data.name || data.batch?.name;
|
|
2950
|
+
if (!batchName) {
|
|
2951
|
+
throw new AnyModelError(502, "No batch name in Google response", {
|
|
2952
|
+
provider_name: "google",
|
|
2953
|
+
raw: data
|
|
2954
|
+
});
|
|
2955
|
+
}
|
|
2956
|
+
return {
|
|
2957
|
+
providerBatchId: batchName,
|
|
2958
|
+
metadata: {
|
|
2959
|
+
model,
|
|
2960
|
+
total_requests: requests.length
|
|
2961
|
+
}
|
|
2962
|
+
};
|
|
2963
|
+
},
|
|
2964
|
+
async pollBatch(providerBatchId) {
|
|
2965
|
+
const res = await apiRequest(`/${providerBatchId}`);
|
|
2966
|
+
const data = await res.json();
|
|
2967
|
+
const state = data.state || "JOB_STATE_PENDING";
|
|
2968
|
+
const status = mapBatchState(state);
|
|
2969
|
+
const totalCount = data.totalCount || data.metadata?.total_requests || 0;
|
|
2970
|
+
const successCount = data.succeededCount || 0;
|
|
2971
|
+
const failedCount = data.failedCount || 0;
|
|
2972
|
+
return {
|
|
2973
|
+
status,
|
|
2974
|
+
total: totalCount || successCount + failedCount,
|
|
2975
|
+
completed: successCount,
|
|
2976
|
+
failed: failedCount
|
|
2977
|
+
};
|
|
2978
|
+
},
|
|
2979
|
+
async getBatchResults(providerBatchId) {
|
|
2980
|
+
const batchRes = await apiRequest(`/${providerBatchId}`);
|
|
2981
|
+
const batchData = await batchRes.json();
|
|
2982
|
+
const results = [];
|
|
2983
|
+
const model = batchData.metadata?.model || "unknown";
|
|
2984
|
+
if (batchData.response?.inlinedResponses) {
|
|
2985
|
+
for (const item of batchData.response.inlinedResponses) {
|
|
2986
|
+
const customId = item.metadata?.key || `request-${results.length}`;
|
|
2987
|
+
if (item.response) {
|
|
2988
|
+
results.push({
|
|
2989
|
+
custom_id: customId,
|
|
2990
|
+
status: "success",
|
|
2991
|
+
response: translateGeminiResponse(item.response, model),
|
|
2992
|
+
error: null
|
|
2993
|
+
});
|
|
2994
|
+
} else if (item.error) {
|
|
2995
|
+
results.push({
|
|
2996
|
+
custom_id: customId,
|
|
2997
|
+
status: "error",
|
|
2998
|
+
response: null,
|
|
2999
|
+
error: {
|
|
3000
|
+
code: item.error.code || 500,
|
|
3001
|
+
message: item.error.message || "Batch item failed"
|
|
3002
|
+
}
|
|
3003
|
+
});
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
return results;
|
|
3007
|
+
}
|
|
3008
|
+
const responsesFile = batchData.response?.responsesFileName || batchData.outputConfig?.file_name;
|
|
3009
|
+
if (responsesFile) {
|
|
3010
|
+
const downloadUrl = `${GEMINI_API_BASE2}/${responsesFile}:download?alt=media`;
|
|
3011
|
+
const fileRes = await fetchWithTimeout(downloadUrl, {
|
|
3012
|
+
headers: { "x-goog-api-key": apiKey }
|
|
3013
|
+
});
|
|
3014
|
+
if (!fileRes.ok) {
|
|
3015
|
+
throw new AnyModelError(502, "Failed to download batch results file", {
|
|
3016
|
+
provider_name: "google"
|
|
3017
|
+
});
|
|
3018
|
+
}
|
|
3019
|
+
const text = await fileRes.text();
|
|
3020
|
+
for (const line of text.trim().split("\n")) {
|
|
3021
|
+
if (!line) continue;
|
|
3022
|
+
const item = JSON.parse(line);
|
|
3023
|
+
const customId = item.key || item.metadata?.key || `request-${results.length}`;
|
|
3024
|
+
if (item.response) {
|
|
3025
|
+
results.push({
|
|
3026
|
+
custom_id: customId,
|
|
3027
|
+
status: "success",
|
|
3028
|
+
response: translateGeminiResponse(item.response, model),
|
|
3029
|
+
error: null
|
|
3030
|
+
});
|
|
3031
|
+
} else if (item.error) {
|
|
3032
|
+
results.push({
|
|
3033
|
+
custom_id: customId,
|
|
3034
|
+
status: "error",
|
|
3035
|
+
response: null,
|
|
3036
|
+
error: {
|
|
3037
|
+
code: item.error.code || 500,
|
|
3038
|
+
message: item.error.message || "Batch item failed"
|
|
3039
|
+
}
|
|
3040
|
+
});
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
return results;
|
|
3045
|
+
},
|
|
3046
|
+
async cancelBatch(providerBatchId) {
|
|
3047
|
+
await apiRequest(`/${providerBatchId}:cancel`, { method: "POST" });
|
|
3048
|
+
}
|
|
3049
|
+
};
|
|
3050
|
+
}
|
|
3051
|
+
|
|
2516
3052
|
// src/client.ts
|
|
2517
3053
|
var AnyModel = class {
|
|
2518
3054
|
registry;
|
|
@@ -2528,6 +3064,7 @@ var AnyModel = class {
|
|
|
2528
3064
|
constructor(config = {}) {
|
|
2529
3065
|
this.config = resolveConfig(config);
|
|
2530
3066
|
this.registry = new ProviderRegistry();
|
|
3067
|
+
setDefaultTimeout((this.config.defaults?.timeout ?? 120) * 1e3);
|
|
2531
3068
|
if (this.config.io) {
|
|
2532
3069
|
configureFsIO(this.config.io);
|
|
2533
3070
|
}
|
|
@@ -2608,14 +3145,17 @@ var AnyModel = class {
|
|
|
2608
3145
|
if (googleKey) {
|
|
2609
3146
|
this.registry.register("google", createGoogleAdapter(googleKey));
|
|
2610
3147
|
}
|
|
3148
|
+
const perplexityKey = config.perplexity?.apiKey || process.env.PERPLEXITY_API_KEY;
|
|
3149
|
+
if (perplexityKey) {
|
|
3150
|
+
this.registry.register("perplexity", createPerplexityAdapter(perplexityKey));
|
|
3151
|
+
}
|
|
2611
3152
|
const builtinProviders = [
|
|
2612
3153
|
{ name: "mistral", baseURL: "https://api.mistral.ai/v1", configKey: "mistral", envVar: "MISTRAL_API_KEY" },
|
|
2613
3154
|
{ name: "groq", baseURL: "https://api.groq.com/openai/v1", configKey: "groq", envVar: "GROQ_API_KEY" },
|
|
2614
3155
|
{ name: "deepseek", baseURL: "https://api.deepseek.com", configKey: "deepseek", envVar: "DEEPSEEK_API_KEY" },
|
|
2615
3156
|
{ name: "xai", baseURL: "https://api.x.ai/v1", configKey: "xai", envVar: "XAI_API_KEY" },
|
|
2616
3157
|
{ name: "together", baseURL: "https://api.together.xyz/v1", configKey: "together", envVar: "TOGETHER_API_KEY" },
|
|
2617
|
-
{ name: "fireworks", baseURL: "https://api.fireworks.ai/inference/v1", configKey: "fireworks", envVar: "FIREWORKS_API_KEY" }
|
|
2618
|
-
{ name: "perplexity", baseURL: "https://api.perplexity.ai", configKey: "perplexity", envVar: "PERPLEXITY_API_KEY" }
|
|
3158
|
+
{ name: "fireworks", baseURL: "https://api.fireworks.ai/inference/v1", configKey: "fireworks", envVar: "FIREWORKS_API_KEY" }
|
|
2619
3159
|
];
|
|
2620
3160
|
for (const { name, baseURL, configKey, envVar } of builtinProviders) {
|
|
2621
3161
|
const providerConfig = config[configKey];
|
|
@@ -2645,6 +3185,10 @@ var AnyModel = class {
|
|
|
2645
3185
|
if (anthropicKey) {
|
|
2646
3186
|
this.batchManager.registerBatchAdapter("anthropic", createAnthropicBatchAdapter(anthropicKey));
|
|
2647
3187
|
}
|
|
3188
|
+
const googleKey = config.google?.apiKey || process.env.GOOGLE_API_KEY;
|
|
3189
|
+
if (googleKey) {
|
|
3190
|
+
this.batchManager.registerBatchAdapter("google", createGoogleBatchAdapter(googleKey));
|
|
3191
|
+
}
|
|
2648
3192
|
}
|
|
2649
3193
|
applyDefaults(request) {
|
|
2650
3194
|
const defaults = this.config.defaults;
|
|
@@ -2824,12 +3368,15 @@ function startServer(options = {}) {
|
|
|
2824
3368
|
configureFsIO,
|
|
2825
3369
|
createAnthropicBatchAdapter,
|
|
2826
3370
|
createAnyModelServer,
|
|
3371
|
+
createGoogleBatchAdapter,
|
|
2827
3372
|
createOpenAIBatchAdapter,
|
|
2828
3373
|
ensureDir,
|
|
3374
|
+
estimateTokenCount,
|
|
2829
3375
|
getFsQueueStatus,
|
|
2830
3376
|
joinPath,
|
|
2831
3377
|
readFileQueued,
|
|
2832
3378
|
resolveConfig,
|
|
3379
|
+
resolveMaxTokens,
|
|
2833
3380
|
startServer,
|
|
2834
3381
|
waitForFsQueuesIdle,
|
|
2835
3382
|
writeFileFlushedQueued,
|