@probeo/anymodel 0.3.0 → 0.4.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 +5 -8
- package/dist/cli.cjs +1016 -110
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +2972 -30
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1037 -112
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +106 -22
- package/dist/index.d.ts +632 -14
- package/dist/index.js +2969 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/batch/index.d.ts +0 -4
- package/dist/batch/index.d.ts.map +0 -1
- package/dist/batch/index.js +0 -3
- package/dist/batch/index.js.map +0 -1
- package/dist/batch/manager.d.ts +0 -72
- package/dist/batch/manager.d.ts.map +0 -1
- package/dist/batch/manager.js +0 -328
- package/dist/batch/manager.js.map +0 -1
- package/dist/batch/store.d.ts +0 -54
- package/dist/batch/store.d.ts.map +0 -1
- package/dist/batch/store.js +0 -109
- package/dist/batch/store.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/client.d.ts +0 -42
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -181
- package/dist/client.js.map +0 -1
- package/dist/config.d.ts +0 -6
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -120
- package/dist/config.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/providers/adapter.d.ts +0 -33
- package/dist/providers/adapter.d.ts.map +0 -1
- package/dist/providers/adapter.js +0 -2
- package/dist/providers/adapter.js.map +0 -1
- package/dist/providers/anthropic-batch.d.ts +0 -3
- package/dist/providers/anthropic-batch.d.ts.map +0 -1
- package/dist/providers/anthropic-batch.js +0 -228
- package/dist/providers/anthropic-batch.js.map +0 -1
- package/dist/providers/anthropic.d.ts +0 -3
- package/dist/providers/anthropic.d.ts.map +0 -1
- package/dist/providers/anthropic.js +0 -358
- package/dist/providers/anthropic.js.map +0 -1
- package/dist/providers/custom.d.ts +0 -8
- package/dist/providers/custom.d.ts.map +0 -1
- package/dist/providers/custom.js +0 -61
- package/dist/providers/custom.js.map +0 -1
- package/dist/providers/google.d.ts +0 -3
- package/dist/providers/google.d.ts.map +0 -1
- package/dist/providers/google.js +0 -331
- package/dist/providers/google.js.map +0 -1
- package/dist/providers/index.d.ts +0 -6
- package/dist/providers/index.d.ts.map +0 -1
- package/dist/providers/index.js +0 -5
- package/dist/providers/index.js.map +0 -1
- package/dist/providers/openai-batch.d.ts +0 -3
- package/dist/providers/openai-batch.d.ts.map +0 -1
- package/dist/providers/openai-batch.js +0 -208
- package/dist/providers/openai-batch.js.map +0 -1
- package/dist/providers/openai.d.ts +0 -3
- package/dist/providers/openai.d.ts.map +0 -1
- package/dist/providers/openai.js +0 -221
- package/dist/providers/openai.js.map +0 -1
- package/dist/providers/registry.d.ts +0 -10
- package/dist/providers/registry.d.ts.map +0 -1
- package/dist/providers/registry.js +0 -27
- package/dist/providers/registry.js.map +0 -1
- package/dist/router.d.ts +0 -29
- package/dist/router.d.ts.map +0 -1
- package/dist/router.js +0 -212
- package/dist/router.js.map +0 -1
- package/dist/server.d.ts +0 -10
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js +0 -149
- package/dist/server.js.map +0 -1
- package/dist/types.d.ts +0 -283
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -21
- package/dist/types.js.map +0 -1
- package/dist/utils/fs-io.d.ts +0 -40
- package/dist/utils/fs-io.d.ts.map +0 -1
- package/dist/utils/fs-io.js +0 -203
- package/dist/utils/fs-io.js.map +0 -1
- package/dist/utils/generation-stats.d.ts +0 -25
- package/dist/utils/generation-stats.d.ts.map +0 -1
- package/dist/utils/generation-stats.js +0 -46
- package/dist/utils/generation-stats.js.map +0 -1
- package/dist/utils/id.d.ts +0 -2
- package/dist/utils/id.d.ts.map +0 -1
- package/dist/utils/id.js +0 -6
- package/dist/utils/id.js.map +0 -1
- package/dist/utils/model-parser.d.ts +0 -6
- package/dist/utils/model-parser.d.ts.map +0 -1
- package/dist/utils/model-parser.js +0 -21
- package/dist/utils/model-parser.js.map +0 -1
- package/dist/utils/rate-limiter.d.ts +0 -36
- package/dist/utils/rate-limiter.d.ts.map +0 -1
- package/dist/utils/rate-limiter.js +0 -80
- package/dist/utils/rate-limiter.js.map +0 -1
- package/dist/utils/retry.d.ts +0 -7
- package/dist/utils/retry.d.ts.map +0 -1
- package/dist/utils/retry.js +0 -54
- package/dist/utils/retry.js.map +0 -1
- package/dist/utils/transforms.d.ts +0 -7
- package/dist/utils/transforms.d.ts.map +0 -1
- package/dist/utils/transforms.js +0 -66
- package/dist/utils/transforms.js.map +0 -1
- package/dist/utils/validate.d.ts +0 -3
- package/dist/utils/validate.d.ts.map +0 -1
- package/dist/utils/validate.js +0 -31
- package/dist/utils/validate.js.map +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
3
25
|
|
|
4
26
|
// src/server.ts
|
|
5
27
|
var import_node_http = require("http");
|
|
@@ -148,7 +170,7 @@ async function withRetry(fn, options = {}) {
|
|
|
148
170
|
throw error;
|
|
149
171
|
}
|
|
150
172
|
const delay = computeDelay(attempt, opts, error);
|
|
151
|
-
await new Promise((
|
|
173
|
+
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
152
174
|
}
|
|
153
175
|
}
|
|
154
176
|
throw lastError;
|
|
@@ -507,8 +529,8 @@ var SUPPORTED_PARAMS = /* @__PURE__ */ new Set([
|
|
|
507
529
|
]);
|
|
508
530
|
function createOpenAIAdapter(apiKey, baseURL) {
|
|
509
531
|
const base = baseURL || OPENAI_API_BASE;
|
|
510
|
-
async function makeRequest(
|
|
511
|
-
const res = await fetch(`${base}${
|
|
532
|
+
async function makeRequest(path2, body, method = "POST") {
|
|
533
|
+
const res = await fetch(`${base}${path2}`, {
|
|
512
534
|
method,
|
|
513
535
|
headers: {
|
|
514
536
|
"Content-Type": "application/json",
|
|
@@ -623,7 +645,19 @@ function createOpenAIAdapter(apiKey, baseURL) {
|
|
|
623
645
|
async listModels() {
|
|
624
646
|
const res = await makeRequest("/models", void 0, "GET");
|
|
625
647
|
const data = await res.json();
|
|
626
|
-
return (data.data || []).filter((m) =>
|
|
648
|
+
return (data.data || []).filter((m) => {
|
|
649
|
+
const id = m.id;
|
|
650
|
+
if (id.includes("embedding")) return false;
|
|
651
|
+
if (id.includes("whisper")) return false;
|
|
652
|
+
if (id.includes("tts")) return false;
|
|
653
|
+
if (id.includes("dall-e")) return false;
|
|
654
|
+
if (id.includes("davinci")) return false;
|
|
655
|
+
if (id.includes("babbage")) return false;
|
|
656
|
+
if (id.includes("moderation")) return false;
|
|
657
|
+
if (id.includes("realtime")) return false;
|
|
658
|
+
if (id.startsWith("ft:")) return false;
|
|
659
|
+
return id.startsWith("gpt-") || id.startsWith("o1") || id.startsWith("o3") || id.startsWith("o4") || id.startsWith("chatgpt-");
|
|
660
|
+
}).map((m) => ({
|
|
627
661
|
id: `openai/${m.id}`,
|
|
628
662
|
name: m.id,
|
|
629
663
|
created: m.created,
|
|
@@ -647,6 +681,9 @@ function createOpenAIAdapter(apiKey, baseURL) {
|
|
|
647
681
|
supportsParameter(param) {
|
|
648
682
|
return SUPPORTED_PARAMS.has(param);
|
|
649
683
|
},
|
|
684
|
+
supportsBatch() {
|
|
685
|
+
return true;
|
|
686
|
+
},
|
|
650
687
|
async sendRequest(request) {
|
|
651
688
|
const body = buildRequestBody(request);
|
|
652
689
|
const res = await makeRequest("/chat/completions", body);
|
|
@@ -690,14 +727,19 @@ var SUPPORTED_PARAMS2 = /* @__PURE__ */ new Set([
|
|
|
690
727
|
"response_format"
|
|
691
728
|
]);
|
|
692
729
|
var FALLBACK_MODELS = [
|
|
693
|
-
|
|
730
|
+
// Claude 4.6
|
|
731
|
+
{ id: "anthropic/claude-opus-4-6", name: "Claude Opus 4.6", created: 0, description: "Most capable model", context_length: 2e5, pricing: { prompt: "0.000015", completion: "0.000075" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image"], output_modalities: ["text"], tokenizer: "claude" }, top_provider: { context_length: 2e5, max_completion_tokens: 32768, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS2) },
|
|
694
732
|
{ id: "anthropic/claude-sonnet-4-6", name: "Claude Sonnet 4.6", created: 0, description: "Best balance of speed and capability", context_length: 2e5, pricing: { prompt: "0.000003", completion: "0.000015" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image"], output_modalities: ["text"], tokenizer: "claude" }, top_provider: { context_length: 2e5, max_completion_tokens: 16384, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS2) },
|
|
733
|
+
// Claude 4.5
|
|
695
734
|
{ id: "anthropic/claude-sonnet-4-5-20251022", name: "Claude Sonnet 4.5", created: 0, description: "Previous generation balanced model", context_length: 2e5, pricing: { prompt: "0.000003", completion: "0.000015" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image"], output_modalities: ["text"], tokenizer: "claude" }, top_provider: { context_length: 2e5, max_completion_tokens: 16384, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS2) },
|
|
696
|
-
{ id: "anthropic/claude-haiku-4-5", name: "Claude Haiku 4.5", created: 0, description: "
|
|
735
|
+
{ id: "anthropic/claude-haiku-4-5", name: "Claude Haiku 4.5", created: 0, description: "Fast and compact", context_length: 2e5, pricing: { prompt: "0.000001", completion: "0.000005" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image"], output_modalities: ["text"], tokenizer: "claude" }, top_provider: { context_length: 2e5, max_completion_tokens: 8192, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS2) },
|
|
736
|
+
// Claude 3.5
|
|
737
|
+
{ id: "anthropic/claude-3-5-sonnet-20241022", name: "Claude 3.5 Sonnet", created: 0, description: "Legacy balanced model", context_length: 2e5, pricing: { prompt: "0.000003", completion: "0.000015" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image"], output_modalities: ["text"], tokenizer: "claude" }, top_provider: { context_length: 2e5, max_completion_tokens: 8192, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS2) },
|
|
738
|
+
{ id: "anthropic/claude-3-5-haiku-20241022", name: "Claude 3.5 Haiku", created: 0, description: "Legacy fast model", context_length: 2e5, pricing: { prompt: "0.0000008", completion: "0.000004" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image"], output_modalities: ["text"], tokenizer: "claude" }, top_provider: { context_length: 2e5, max_completion_tokens: 8192, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS2) }
|
|
697
739
|
];
|
|
698
740
|
function createAnthropicAdapter(apiKey) {
|
|
699
|
-
async function makeRequest(
|
|
700
|
-
const res = await fetch(`${ANTHROPIC_API_BASE}${
|
|
741
|
+
async function makeRequest(path2, body, stream = false) {
|
|
742
|
+
const res = await fetch(`${ANTHROPIC_API_BASE}${path2}`, {
|
|
701
743
|
method: "POST",
|
|
702
744
|
headers: {
|
|
703
745
|
"Content-Type": "application/json",
|
|
@@ -991,6 +1033,9 @@ ${body.system}` : jsonInstruction;
|
|
|
991
1033
|
supportsParameter(param) {
|
|
992
1034
|
return SUPPORTED_PARAMS2.has(param);
|
|
993
1035
|
},
|
|
1036
|
+
supportsBatch() {
|
|
1037
|
+
return true;
|
|
1038
|
+
},
|
|
994
1039
|
async sendRequest(request) {
|
|
995
1040
|
const body = translateRequest(request);
|
|
996
1041
|
const res = await makeRequest("/messages", body);
|
|
@@ -1025,8 +1070,15 @@ var SUPPORTED_PARAMS3 = /* @__PURE__ */ new Set([
|
|
|
1025
1070
|
"response_format"
|
|
1026
1071
|
]);
|
|
1027
1072
|
var FALLBACK_MODELS2 = [
|
|
1073
|
+
// Gemini 2.5
|
|
1028
1074
|
{ id: "google/gemini-2.5-pro", name: "Gemini 2.5 Pro", created: 0, description: "Most capable Gemini model", context_length: 1048576, pricing: { prompt: "0.00000125", completion: "0.000005" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image", "video", "audio"], output_modalities: ["text"], tokenizer: "gemini" }, top_provider: { context_length: 1048576, max_completion_tokens: 65536, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS3) },
|
|
1029
|
-
{ id: "google/gemini-2.5-flash", name: "Gemini 2.5 Flash", created: 0, description: "Fast and efficient", context_length: 1048576, pricing: { prompt: "0.00000015", completion: "0.0000006" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image", "video", "audio"], output_modalities: ["text"], tokenizer: "gemini" }, top_provider: { context_length: 1048576, max_completion_tokens: 65536, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS3) }
|
|
1075
|
+
{ id: "google/gemini-2.5-flash", name: "Gemini 2.5 Flash", created: 0, description: "Fast and efficient", context_length: 1048576, pricing: { prompt: "0.00000015", completion: "0.0000006" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image", "video", "audio"], output_modalities: ["text"], tokenizer: "gemini" }, top_provider: { context_length: 1048576, max_completion_tokens: 65536, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS3) },
|
|
1076
|
+
// Gemini 2.0
|
|
1077
|
+
{ id: "google/gemini-2.0-flash", name: "Gemini 2.0 Flash", created: 0, description: "Fast multimodal model", context_length: 1048576, pricing: { prompt: "0.0000001", completion: "0.0000004" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image", "video", "audio"], output_modalities: ["text"], tokenizer: "gemini" }, top_provider: { context_length: 1048576, max_completion_tokens: 65536, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS3) },
|
|
1078
|
+
{ id: "google/gemini-2.0-flash-lite", name: "Gemini 2.0 Flash Lite", created: 0, description: "Lightweight and fast", context_length: 1048576, pricing: { prompt: "0.00000005", completion: "0.0000002" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image", "video", "audio"], output_modalities: ["text"], tokenizer: "gemini" }, top_provider: { context_length: 1048576, max_completion_tokens: 65536, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS3) },
|
|
1079
|
+
// Gemini 1.5
|
|
1080
|
+
{ id: "google/gemini-1.5-pro", name: "Gemini 1.5 Pro", created: 0, description: "Previous generation pro model", context_length: 2097152, pricing: { prompt: "0.00000125", completion: "0.000005" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image", "video", "audio"], output_modalities: ["text"], tokenizer: "gemini" }, top_provider: { context_length: 2097152, max_completion_tokens: 8192, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS3) },
|
|
1081
|
+
{ id: "google/gemini-1.5-flash", name: "Gemini 1.5 Flash", created: 0, description: "Previous generation flash model", context_length: 1048576, pricing: { prompt: "0.000000075", completion: "0.0000003" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image", "video", "audio"], output_modalities: ["text"], tokenizer: "gemini" }, top_provider: { context_length: 1048576, max_completion_tokens: 8192, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS3) }
|
|
1030
1082
|
];
|
|
1031
1083
|
function createGoogleAdapter(apiKey) {
|
|
1032
1084
|
function getModelEndpoint(model, stream) {
|
|
@@ -1263,6 +1315,9 @@ function createGoogleAdapter(apiKey) {
|
|
|
1263
1315
|
supportsParameter(param) {
|
|
1264
1316
|
return SUPPORTED_PARAMS3.has(param);
|
|
1265
1317
|
},
|
|
1318
|
+
supportsBatch() {
|
|
1319
|
+
return false;
|
|
1320
|
+
},
|
|
1266
1321
|
async sendRequest(request) {
|
|
1267
1322
|
const body = translateRequest(request);
|
|
1268
1323
|
const url = getModelEndpoint(request.model, false);
|
|
@@ -1315,12 +1370,199 @@ function createGoogleAdapter(apiKey) {
|
|
|
1315
1370
|
return adapter;
|
|
1316
1371
|
}
|
|
1317
1372
|
|
|
1373
|
+
// src/providers/perplexity.ts
|
|
1374
|
+
var PERPLEXITY_API_BASE = "https://api.perplexity.ai";
|
|
1375
|
+
var SUPPORTED_PARAMS4 = /* @__PURE__ */ new Set([
|
|
1376
|
+
"temperature",
|
|
1377
|
+
"max_tokens",
|
|
1378
|
+
"top_p",
|
|
1379
|
+
"frequency_penalty",
|
|
1380
|
+
"presence_penalty",
|
|
1381
|
+
"stream",
|
|
1382
|
+
"stop",
|
|
1383
|
+
"response_format",
|
|
1384
|
+
"tools",
|
|
1385
|
+
"tool_choice"
|
|
1386
|
+
]);
|
|
1387
|
+
var MODELS = [
|
|
1388
|
+
{ id: "sonar", name: "Sonar", context: 128e3, maxOutput: 4096, modality: "text->text", inputModalities: ["text"] },
|
|
1389
|
+
{ id: "sonar-pro", name: "Sonar Pro", context: 2e5, maxOutput: 8192, modality: "text->text", inputModalities: ["text"] },
|
|
1390
|
+
{ id: "sonar-reasoning", name: "Sonar Reasoning", context: 128e3, maxOutput: 8192, modality: "text->text", inputModalities: ["text"] },
|
|
1391
|
+
{ id: "sonar-reasoning-pro", name: "Sonar Reasoning Pro", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] },
|
|
1392
|
+
{ id: "sonar-deep-research", name: "Sonar Deep Research", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] },
|
|
1393
|
+
{ id: "r1-1776", name: "R1 1776", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] }
|
|
1394
|
+
];
|
|
1395
|
+
function createPerplexityAdapter(apiKey) {
|
|
1396
|
+
async function makeRequest(path2, body, method = "POST") {
|
|
1397
|
+
const res = await fetch(`${PERPLEXITY_API_BASE}${path2}`, {
|
|
1398
|
+
method,
|
|
1399
|
+
headers: {
|
|
1400
|
+
"Content-Type": "application/json",
|
|
1401
|
+
"Authorization": `Bearer ${apiKey}`
|
|
1402
|
+
},
|
|
1403
|
+
body: body ? JSON.stringify(body) : void 0
|
|
1404
|
+
});
|
|
1405
|
+
if (!res.ok) {
|
|
1406
|
+
let errorBody;
|
|
1407
|
+
try {
|
|
1408
|
+
errorBody = await res.json();
|
|
1409
|
+
} catch {
|
|
1410
|
+
errorBody = { message: res.statusText };
|
|
1411
|
+
}
|
|
1412
|
+
const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
|
|
1413
|
+
throw new AnyModelError(mapErrorCode(res.status), msg, {
|
|
1414
|
+
provider_name: "perplexity",
|
|
1415
|
+
raw: errorBody
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
return res;
|
|
1419
|
+
}
|
|
1420
|
+
function mapErrorCode(status) {
|
|
1421
|
+
if (status === 401 || status === 403) return 401;
|
|
1422
|
+
if (status === 429) return 429;
|
|
1423
|
+
if (status === 400 || status === 422) return 400;
|
|
1424
|
+
if (status >= 500) return 502;
|
|
1425
|
+
return status;
|
|
1426
|
+
}
|
|
1427
|
+
function rePrefixId(id) {
|
|
1428
|
+
if (id && id.startsWith("chatcmpl-")) {
|
|
1429
|
+
return `gen-${id.substring(9)}`;
|
|
1430
|
+
}
|
|
1431
|
+
return id.startsWith("gen-") ? id : `gen-${id}`;
|
|
1432
|
+
}
|
|
1433
|
+
function buildRequestBody(request) {
|
|
1434
|
+
const body = {
|
|
1435
|
+
model: request.model,
|
|
1436
|
+
messages: request.messages
|
|
1437
|
+
};
|
|
1438
|
+
if (request.temperature !== void 0) body.temperature = request.temperature;
|
|
1439
|
+
if (request.max_tokens !== void 0) body.max_tokens = request.max_tokens;
|
|
1440
|
+
if (request.top_p !== void 0) body.top_p = request.top_p;
|
|
1441
|
+
if (request.frequency_penalty !== void 0) body.frequency_penalty = request.frequency_penalty;
|
|
1442
|
+
if (request.presence_penalty !== void 0) body.presence_penalty = request.presence_penalty;
|
|
1443
|
+
if (request.stop !== void 0) body.stop = request.stop;
|
|
1444
|
+
if (request.stream !== void 0) body.stream = request.stream;
|
|
1445
|
+
if (request.response_format !== void 0) body.response_format = request.response_format;
|
|
1446
|
+
if (request.tools !== void 0) body.tools = request.tools;
|
|
1447
|
+
if (request.tool_choice !== void 0) body.tool_choice = request.tool_choice;
|
|
1448
|
+
return body;
|
|
1449
|
+
}
|
|
1450
|
+
const adapter = {
|
|
1451
|
+
name: "perplexity",
|
|
1452
|
+
translateRequest(request) {
|
|
1453
|
+
return buildRequestBody(request);
|
|
1454
|
+
},
|
|
1455
|
+
translateResponse(response) {
|
|
1456
|
+
const r = response;
|
|
1457
|
+
const result = {
|
|
1458
|
+
id: rePrefixId(r.id),
|
|
1459
|
+
object: "chat.completion",
|
|
1460
|
+
created: r.created,
|
|
1461
|
+
model: `perplexity/${r.model}`,
|
|
1462
|
+
choices: r.choices,
|
|
1463
|
+
usage: r.usage
|
|
1464
|
+
};
|
|
1465
|
+
if (r.citations && result.choices?.[0]?.message) {
|
|
1466
|
+
result.citations = r.citations;
|
|
1467
|
+
}
|
|
1468
|
+
return result;
|
|
1469
|
+
},
|
|
1470
|
+
async *translateStream(stream) {
|
|
1471
|
+
const reader = stream.getReader();
|
|
1472
|
+
const decoder = new TextDecoder();
|
|
1473
|
+
let buffer = "";
|
|
1474
|
+
try {
|
|
1475
|
+
while (true) {
|
|
1476
|
+
const { done, value } = await reader.read();
|
|
1477
|
+
if (done) break;
|
|
1478
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1479
|
+
const lines = buffer.split("\n");
|
|
1480
|
+
buffer = lines.pop() || "";
|
|
1481
|
+
for (const line of lines) {
|
|
1482
|
+
const trimmed = line.trim();
|
|
1483
|
+
if (!trimmed || trimmed.startsWith(":")) continue;
|
|
1484
|
+
if (trimmed === "data: [DONE]") return;
|
|
1485
|
+
if (trimmed.startsWith("data: ")) {
|
|
1486
|
+
const json = JSON.parse(trimmed.substring(6));
|
|
1487
|
+
json.id = rePrefixId(json.id);
|
|
1488
|
+
json.model = `perplexity/${json.model}`;
|
|
1489
|
+
yield json;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
} finally {
|
|
1494
|
+
reader.releaseLock();
|
|
1495
|
+
}
|
|
1496
|
+
},
|
|
1497
|
+
translateError(error) {
|
|
1498
|
+
if (error instanceof AnyModelError) {
|
|
1499
|
+
return { code: error.code, message: error.message, metadata: error.metadata };
|
|
1500
|
+
}
|
|
1501
|
+
const err = error;
|
|
1502
|
+
const status = err?.status || err?.code || 500;
|
|
1503
|
+
return {
|
|
1504
|
+
code: mapErrorCode(status),
|
|
1505
|
+
message: err?.message || "Unknown Perplexity error",
|
|
1506
|
+
metadata: { provider_name: "perplexity", raw: error }
|
|
1507
|
+
};
|
|
1508
|
+
},
|
|
1509
|
+
async listModels() {
|
|
1510
|
+
return MODELS.map((m) => ({
|
|
1511
|
+
id: `perplexity/${m.id}`,
|
|
1512
|
+
name: m.name,
|
|
1513
|
+
created: 0,
|
|
1514
|
+
description: "",
|
|
1515
|
+
context_length: m.context,
|
|
1516
|
+
pricing: { prompt: "0", completion: "0" },
|
|
1517
|
+
architecture: {
|
|
1518
|
+
modality: m.modality,
|
|
1519
|
+
input_modalities: m.inputModalities,
|
|
1520
|
+
output_modalities: ["text"],
|
|
1521
|
+
tokenizer: "unknown"
|
|
1522
|
+
},
|
|
1523
|
+
top_provider: {
|
|
1524
|
+
context_length: m.context,
|
|
1525
|
+
max_completion_tokens: m.maxOutput,
|
|
1526
|
+
is_moderated: false
|
|
1527
|
+
},
|
|
1528
|
+
supported_parameters: Array.from(SUPPORTED_PARAMS4)
|
|
1529
|
+
}));
|
|
1530
|
+
},
|
|
1531
|
+
supportsParameter(param) {
|
|
1532
|
+
return SUPPORTED_PARAMS4.has(param);
|
|
1533
|
+
},
|
|
1534
|
+
supportsBatch() {
|
|
1535
|
+
return false;
|
|
1536
|
+
},
|
|
1537
|
+
async sendRequest(request) {
|
|
1538
|
+
const body = buildRequestBody(request);
|
|
1539
|
+
const res = await makeRequest("/chat/completions", body);
|
|
1540
|
+
const json = await res.json();
|
|
1541
|
+
return adapter.translateResponse(json);
|
|
1542
|
+
},
|
|
1543
|
+
async sendStreamingRequest(request) {
|
|
1544
|
+
const body = buildRequestBody({ ...request, stream: true });
|
|
1545
|
+
const res = await makeRequest("/chat/completions", body);
|
|
1546
|
+
if (!res.body) {
|
|
1547
|
+
throw new AnyModelError(502, "No response body for streaming request", {
|
|
1548
|
+
provider_name: "perplexity"
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
return adapter.translateStream(res.body);
|
|
1552
|
+
}
|
|
1553
|
+
};
|
|
1554
|
+
return adapter;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1318
1557
|
// src/providers/custom.ts
|
|
1319
1558
|
function createCustomAdapter(name, config) {
|
|
1320
1559
|
const openaiAdapter = createOpenAIAdapter(config.apiKey || "", config.baseURL);
|
|
1321
1560
|
return {
|
|
1322
1561
|
...openaiAdapter,
|
|
1323
1562
|
name,
|
|
1563
|
+
supportsBatch() {
|
|
1564
|
+
return false;
|
|
1565
|
+
},
|
|
1324
1566
|
async listModels() {
|
|
1325
1567
|
if (config.models && config.models.length > 0) {
|
|
1326
1568
|
return config.models.map((modelId) => ({
|
|
@@ -1386,10 +1628,10 @@ function interpolateDeep(obj) {
|
|
|
1386
1628
|
}
|
|
1387
1629
|
return obj;
|
|
1388
1630
|
}
|
|
1389
|
-
function loadJsonFile(
|
|
1390
|
-
if (!(0, import_node_fs.existsSync)(
|
|
1631
|
+
function loadJsonFile(path2) {
|
|
1632
|
+
if (!(0, import_node_fs.existsSync)(path2)) return null;
|
|
1391
1633
|
try {
|
|
1392
|
-
const raw = (0, import_node_fs.readFileSync)(
|
|
1634
|
+
const raw = (0, import_node_fs.readFileSync)(path2, "utf-8");
|
|
1393
1635
|
const parsed = JSON.parse(raw);
|
|
1394
1636
|
return interpolateDeep(parsed);
|
|
1395
1637
|
} catch {
|
|
@@ -1490,93 +1732,228 @@ var GenerationStatsStore = class {
|
|
|
1490
1732
|
}
|
|
1491
1733
|
};
|
|
1492
1734
|
|
|
1493
|
-
// src/
|
|
1735
|
+
// src/utils/fs-io.ts
|
|
1736
|
+
var import_promises = require("fs/promises");
|
|
1494
1737
|
var import_node_fs2 = require("fs");
|
|
1495
|
-
var import_node_path2 = require("path");
|
|
1496
|
-
var
|
|
1497
|
-
var
|
|
1738
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
1739
|
+
var import_p_queue = __toESM(require("p-queue"), 1);
|
|
1740
|
+
var writeQueue = new import_p_queue.default({ concurrency: 10 });
|
|
1741
|
+
var readQueue = new import_p_queue.default({ concurrency: 20 });
|
|
1742
|
+
function configureFsIO(options) {
|
|
1743
|
+
if (options.readConcurrency !== void 0) {
|
|
1744
|
+
readQueue.concurrency = options.readConcurrency;
|
|
1745
|
+
}
|
|
1746
|
+
if (options.writeConcurrency !== void 0) {
|
|
1747
|
+
writeQueue.concurrency = options.writeConcurrency;
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
var ensuredDirs = /* @__PURE__ */ new Set();
|
|
1751
|
+
var joinPathCache = /* @__PURE__ */ new Map();
|
|
1752
|
+
var dirnameCache = /* @__PURE__ */ new Map();
|
|
1753
|
+
var resolvePathCache = /* @__PURE__ */ new Map();
|
|
1754
|
+
async function ensureDir(dir) {
|
|
1755
|
+
if (!dir) return;
|
|
1756
|
+
if (ensuredDirs.has(dir)) return;
|
|
1757
|
+
await (0, import_promises.mkdir)(dir, { recursive: true });
|
|
1758
|
+
ensuredDirs.add(dir);
|
|
1759
|
+
}
|
|
1760
|
+
async function readFileQueued(filePath, encoding = "utf8") {
|
|
1761
|
+
return readQueue.add(async () => {
|
|
1762
|
+
return (0, import_promises.readFile)(filePath, encoding);
|
|
1763
|
+
});
|
|
1764
|
+
}
|
|
1765
|
+
async function readJsonQueued(filePath) {
|
|
1766
|
+
const raw = await readFileQueued(filePath, "utf8");
|
|
1767
|
+
return JSON.parse(raw);
|
|
1768
|
+
}
|
|
1769
|
+
async function readDirQueued(dirPath) {
|
|
1770
|
+
return readQueue.add(async () => {
|
|
1771
|
+
return (0, import_promises.readdir)(dirPath, { withFileTypes: true });
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
async function pathExistsQueued(p) {
|
|
1775
|
+
return readQueue.add(async () => {
|
|
1776
|
+
try {
|
|
1777
|
+
await (0, import_promises.stat)(p);
|
|
1778
|
+
return true;
|
|
1779
|
+
} catch {
|
|
1780
|
+
return false;
|
|
1781
|
+
}
|
|
1782
|
+
});
|
|
1783
|
+
}
|
|
1784
|
+
async function fileExistsQueued(filePath) {
|
|
1785
|
+
return readQueue.add(async () => {
|
|
1786
|
+
try {
|
|
1787
|
+
const s = await (0, import_promises.stat)(filePath);
|
|
1788
|
+
return s.isFile();
|
|
1789
|
+
} catch {
|
|
1790
|
+
return false;
|
|
1791
|
+
}
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1794
|
+
async function writeFileQueued(filePath, data) {
|
|
1795
|
+
await writeQueue.add(async () => {
|
|
1796
|
+
const dir = dirnameOf(filePath);
|
|
1797
|
+
await ensureDir(dir);
|
|
1798
|
+
await (0, import_promises.writeFile)(filePath, data);
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
async function appendFileQueued(filePath, data) {
|
|
1802
|
+
await writeQueue.add(async () => {
|
|
1803
|
+
const dir = dirnameOf(filePath);
|
|
1804
|
+
await ensureDir(dir);
|
|
1805
|
+
await (0, import_promises.writeFile)(filePath, data, { flag: "a" });
|
|
1806
|
+
});
|
|
1807
|
+
}
|
|
1808
|
+
async function writeFileFlushedQueued(filePath, data) {
|
|
1809
|
+
await writeQueue.add(async () => {
|
|
1810
|
+
const dir = dirnameOf(filePath);
|
|
1811
|
+
await ensureDir(dir);
|
|
1812
|
+
const tmpPath = joinPath(
|
|
1813
|
+
dir,
|
|
1814
|
+
`.${import_node_path2.default.basename(filePath)}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`
|
|
1815
|
+
);
|
|
1816
|
+
const fh = await (0, import_promises.open)(tmpPath, "w");
|
|
1817
|
+
try {
|
|
1818
|
+
await fh.writeFile(data);
|
|
1819
|
+
await fh.sync();
|
|
1820
|
+
} finally {
|
|
1821
|
+
await fh.close();
|
|
1822
|
+
}
|
|
1823
|
+
await (0, import_promises.rename)(tmpPath, filePath);
|
|
1824
|
+
try {
|
|
1825
|
+
const dh = await (0, import_promises.open)(dir, "r");
|
|
1826
|
+
try {
|
|
1827
|
+
await dh.sync();
|
|
1828
|
+
} finally {
|
|
1829
|
+
await dh.close();
|
|
1830
|
+
}
|
|
1831
|
+
} catch {
|
|
1832
|
+
}
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
function joinPath(...segments) {
|
|
1836
|
+
const key = segments.join("\0");
|
|
1837
|
+
const cached = joinPathCache.get(key);
|
|
1838
|
+
if (cached !== void 0) return cached;
|
|
1839
|
+
const out = import_node_path2.default.join(...segments);
|
|
1840
|
+
joinPathCache.set(key, out);
|
|
1841
|
+
return out;
|
|
1842
|
+
}
|
|
1843
|
+
function dirnameOf(p) {
|
|
1844
|
+
const cached = dirnameCache.get(p);
|
|
1845
|
+
if (cached !== void 0) return cached;
|
|
1846
|
+
const out = import_node_path2.default.dirname(p);
|
|
1847
|
+
dirnameCache.set(p, out);
|
|
1848
|
+
return out;
|
|
1849
|
+
}
|
|
1850
|
+
function resolvePath(...segments) {
|
|
1851
|
+
const key = segments.join("\0");
|
|
1852
|
+
const cached = resolvePathCache.get(key);
|
|
1853
|
+
if (cached !== void 0) return cached;
|
|
1854
|
+
const out = import_node_path2.default.resolve(...segments);
|
|
1855
|
+
resolvePathCache.set(key, out);
|
|
1856
|
+
return out;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
// src/batch/store.ts
|
|
1860
|
+
var DEFAULT_BATCH_DIR = joinPath(process.cwd(), ".anymodel", "batches");
|
|
1498
1861
|
var BatchStore = class {
|
|
1499
1862
|
dir;
|
|
1863
|
+
initialized = false;
|
|
1500
1864
|
constructor(dir) {
|
|
1501
|
-
this.dir = (
|
|
1502
|
-
|
|
1865
|
+
this.dir = resolvePath(dir || DEFAULT_BATCH_DIR);
|
|
1866
|
+
}
|
|
1867
|
+
async init() {
|
|
1868
|
+
if (this.initialized) return;
|
|
1869
|
+
await ensureDir(this.dir);
|
|
1870
|
+
this.initialized = true;
|
|
1503
1871
|
}
|
|
1504
1872
|
batchDir(id) {
|
|
1505
|
-
return (
|
|
1873
|
+
return joinPath(this.dir, id);
|
|
1506
1874
|
}
|
|
1507
1875
|
/**
|
|
1508
1876
|
* Create a new batch directory and save initial metadata.
|
|
1509
1877
|
*/
|
|
1510
|
-
create(batch) {
|
|
1878
|
+
async create(batch) {
|
|
1879
|
+
await this.init();
|
|
1511
1880
|
const dir = this.batchDir(batch.id);
|
|
1512
|
-
|
|
1513
|
-
|
|
1881
|
+
await ensureDir(dir);
|
|
1882
|
+
await writeFileFlushedQueued(joinPath(dir, "meta.json"), JSON.stringify(batch, null, 2));
|
|
1514
1883
|
}
|
|
1515
1884
|
/**
|
|
1516
|
-
* Update batch metadata.
|
|
1885
|
+
* Update batch metadata (atomic write).
|
|
1517
1886
|
*/
|
|
1518
|
-
updateMeta(batch) {
|
|
1519
|
-
|
|
1520
|
-
|
|
1887
|
+
async updateMeta(batch) {
|
|
1888
|
+
await writeFileFlushedQueued(
|
|
1889
|
+
joinPath(this.batchDir(batch.id), "meta.json"),
|
|
1890
|
+
JSON.stringify(batch, null, 2)
|
|
1891
|
+
);
|
|
1521
1892
|
}
|
|
1522
1893
|
/**
|
|
1523
1894
|
* Save requests as JSONL.
|
|
1524
1895
|
*/
|
|
1525
|
-
saveRequests(id, requests) {
|
|
1526
|
-
const dir = this.batchDir(id);
|
|
1896
|
+
async saveRequests(id, requests) {
|
|
1527
1897
|
const lines = requests.map((r) => JSON.stringify(r)).join("\n") + "\n";
|
|
1528
|
-
|
|
1898
|
+
await writeFileQueued(joinPath(this.batchDir(id), "requests.jsonl"), lines);
|
|
1529
1899
|
}
|
|
1530
1900
|
/**
|
|
1531
1901
|
* Append a result to results.jsonl.
|
|
1532
1902
|
*/
|
|
1533
|
-
appendResult(id, result) {
|
|
1534
|
-
|
|
1535
|
-
|
|
1903
|
+
async appendResult(id, result) {
|
|
1904
|
+
await appendFileQueued(
|
|
1905
|
+
joinPath(this.batchDir(id), "results.jsonl"),
|
|
1906
|
+
JSON.stringify(result) + "\n"
|
|
1907
|
+
);
|
|
1536
1908
|
}
|
|
1537
1909
|
/**
|
|
1538
1910
|
* Save provider-specific state (e.g., provider batch ID).
|
|
1539
1911
|
*/
|
|
1540
|
-
saveProviderState(id, state) {
|
|
1541
|
-
|
|
1542
|
-
|
|
1912
|
+
async saveProviderState(id, state) {
|
|
1913
|
+
await writeFileFlushedQueued(
|
|
1914
|
+
joinPath(this.batchDir(id), "provider.json"),
|
|
1915
|
+
JSON.stringify(state, null, 2)
|
|
1916
|
+
);
|
|
1543
1917
|
}
|
|
1544
1918
|
/**
|
|
1545
1919
|
* Load provider state.
|
|
1546
1920
|
*/
|
|
1547
|
-
loadProviderState(id) {
|
|
1548
|
-
const
|
|
1549
|
-
if (!
|
|
1550
|
-
return
|
|
1921
|
+
async loadProviderState(id) {
|
|
1922
|
+
const p = joinPath(this.batchDir(id), "provider.json");
|
|
1923
|
+
if (!await fileExistsQueued(p)) return null;
|
|
1924
|
+
return readJsonQueued(p);
|
|
1551
1925
|
}
|
|
1552
1926
|
/**
|
|
1553
1927
|
* Get batch metadata.
|
|
1554
1928
|
*/
|
|
1555
|
-
getMeta(id) {
|
|
1556
|
-
const
|
|
1557
|
-
if (!
|
|
1558
|
-
return
|
|
1929
|
+
async getMeta(id) {
|
|
1930
|
+
const p = joinPath(this.batchDir(id), "meta.json");
|
|
1931
|
+
if (!await fileExistsQueued(p)) return null;
|
|
1932
|
+
return readJsonQueued(p);
|
|
1559
1933
|
}
|
|
1560
1934
|
/**
|
|
1561
1935
|
* Get all results for a batch.
|
|
1562
1936
|
*/
|
|
1563
|
-
getResults(id) {
|
|
1564
|
-
const
|
|
1565
|
-
if (!
|
|
1566
|
-
|
|
1937
|
+
async getResults(id) {
|
|
1938
|
+
const p = joinPath(this.batchDir(id), "results.jsonl");
|
|
1939
|
+
if (!await fileExistsQueued(p)) return [];
|
|
1940
|
+
const raw = await readFileQueued(p, "utf8");
|
|
1941
|
+
return raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
1567
1942
|
}
|
|
1568
1943
|
/**
|
|
1569
1944
|
* List all batch IDs.
|
|
1570
1945
|
*/
|
|
1571
|
-
listBatches() {
|
|
1572
|
-
|
|
1573
|
-
|
|
1946
|
+
async listBatches() {
|
|
1947
|
+
await this.init();
|
|
1948
|
+
if (!await pathExistsQueued(this.dir)) return [];
|
|
1949
|
+
const entries = await readDirQueued(this.dir);
|
|
1950
|
+
return entries.filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
1574
1951
|
}
|
|
1575
1952
|
/**
|
|
1576
1953
|
* Check if a batch exists.
|
|
1577
1954
|
*/
|
|
1578
|
-
exists(id) {
|
|
1579
|
-
return (
|
|
1955
|
+
async exists(id) {
|
|
1956
|
+
return fileExistsQueued(joinPath(this.batchDir(id), "meta.json"));
|
|
1580
1957
|
}
|
|
1581
1958
|
};
|
|
1582
1959
|
|
|
@@ -1585,10 +1962,27 @@ var BatchManager = class {
|
|
|
1585
1962
|
store;
|
|
1586
1963
|
router;
|
|
1587
1964
|
concurrencyLimit;
|
|
1965
|
+
defaultPollInterval;
|
|
1966
|
+
batchAdapters = /* @__PURE__ */ new Map();
|
|
1588
1967
|
constructor(router, options) {
|
|
1589
1968
|
this.store = new BatchStore(options?.dir);
|
|
1590
1969
|
this.router = router;
|
|
1591
1970
|
this.concurrencyLimit = options?.concurrency ?? 5;
|
|
1971
|
+
this.defaultPollInterval = options?.pollInterval ?? 5e3;
|
|
1972
|
+
}
|
|
1973
|
+
/**
|
|
1974
|
+
* Register a native batch adapter for a provider.
|
|
1975
|
+
*/
|
|
1976
|
+
registerBatchAdapter(providerName, adapter) {
|
|
1977
|
+
this.batchAdapters.set(providerName, adapter);
|
|
1978
|
+
}
|
|
1979
|
+
/**
|
|
1980
|
+
* Check if a provider has native batch support.
|
|
1981
|
+
*/
|
|
1982
|
+
getNativeBatchAdapter(model) {
|
|
1983
|
+
const providerName = model.split("/")[0];
|
|
1984
|
+
const adapter = this.batchAdapters.get(providerName);
|
|
1985
|
+
return adapter ? { adapter, providerName } : null;
|
|
1592
1986
|
}
|
|
1593
1987
|
/**
|
|
1594
1988
|
* Create a batch and return immediately (no polling).
|
|
@@ -1596,13 +1990,16 @@ var BatchManager = class {
|
|
|
1596
1990
|
async create(request) {
|
|
1597
1991
|
const id = generateId("batch");
|
|
1598
1992
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1993
|
+
const providerName = request.model.split("/")[0] || "unknown";
|
|
1994
|
+
const native = this.getNativeBatchAdapter(request.model);
|
|
1995
|
+
const batchMode = native ? "native" : "concurrent";
|
|
1599
1996
|
const batch = {
|
|
1600
1997
|
id,
|
|
1601
1998
|
object: "batch",
|
|
1602
1999
|
status: "pending",
|
|
1603
2000
|
model: request.model,
|
|
1604
|
-
provider_name:
|
|
1605
|
-
batch_mode:
|
|
2001
|
+
provider_name: providerName,
|
|
2002
|
+
batch_mode: batchMode,
|
|
1606
2003
|
total: request.requests.length,
|
|
1607
2004
|
completed: 0,
|
|
1608
2005
|
failed: 0,
|
|
@@ -1610,10 +2007,15 @@ var BatchManager = class {
|
|
|
1610
2007
|
completed_at: null,
|
|
1611
2008
|
expires_at: null
|
|
1612
2009
|
};
|
|
1613
|
-
this.store.create(batch);
|
|
1614
|
-
this.store.saveRequests(id, request.requests);
|
|
1615
|
-
|
|
1616
|
-
|
|
2010
|
+
await this.store.create(batch);
|
|
2011
|
+
await this.store.saveRequests(id, request.requests);
|
|
2012
|
+
if (native) {
|
|
2013
|
+
this.processNativeBatch(id, request, native.adapter).catch(() => {
|
|
2014
|
+
});
|
|
2015
|
+
} else {
|
|
2016
|
+
this.processConcurrentBatch(id, request).catch(() => {
|
|
2017
|
+
});
|
|
2018
|
+
}
|
|
1617
2019
|
return batch;
|
|
1618
2020
|
}
|
|
1619
2021
|
/**
|
|
@@ -1627,14 +2029,19 @@ var BatchManager = class {
|
|
|
1627
2029
|
* Poll an existing batch until completion.
|
|
1628
2030
|
*/
|
|
1629
2031
|
async poll(id, options = {}) {
|
|
1630
|
-
const interval = options.interval ??
|
|
2032
|
+
const interval = options.interval ?? this.defaultPollInterval;
|
|
1631
2033
|
const timeout = options.timeout ?? 0;
|
|
1632
2034
|
const startTime = Date.now();
|
|
1633
2035
|
while (true) {
|
|
1634
|
-
|
|
2036
|
+
let batch = await this.store.getMeta(id);
|
|
1635
2037
|
if (!batch) {
|
|
1636
2038
|
throw new AnyModelError(404, `Batch ${id} not found`);
|
|
1637
2039
|
}
|
|
2040
|
+
if (batch.batch_mode === "native" && batch.status === "processing") {
|
|
2041
|
+
await this.syncNativeBatchStatus(id);
|
|
2042
|
+
batch = await this.store.getMeta(id);
|
|
2043
|
+
if (!batch) throw new AnyModelError(404, `Batch ${id} not found`);
|
|
2044
|
+
}
|
|
1638
2045
|
if (options.onProgress) {
|
|
1639
2046
|
options.onProgress(batch);
|
|
1640
2047
|
}
|
|
@@ -1644,24 +2051,24 @@ var BatchManager = class {
|
|
|
1644
2051
|
if (timeout > 0 && Date.now() - startTime > timeout) {
|
|
1645
2052
|
throw new AnyModelError(408, `Batch ${id} timed out after ${timeout}ms`);
|
|
1646
2053
|
}
|
|
1647
|
-
await new Promise((
|
|
2054
|
+
await new Promise((resolve2) => setTimeout(resolve2, interval));
|
|
1648
2055
|
}
|
|
1649
2056
|
}
|
|
1650
2057
|
/**
|
|
1651
2058
|
* Get the current status of a batch.
|
|
1652
2059
|
*/
|
|
1653
|
-
get(id) {
|
|
2060
|
+
async get(id) {
|
|
1654
2061
|
return this.store.getMeta(id);
|
|
1655
2062
|
}
|
|
1656
2063
|
/**
|
|
1657
2064
|
* Get results for a completed batch.
|
|
1658
2065
|
*/
|
|
1659
|
-
getResults(id) {
|
|
1660
|
-
const batch = this.store.getMeta(id);
|
|
2066
|
+
async getResults(id) {
|
|
2067
|
+
const batch = await this.store.getMeta(id);
|
|
1661
2068
|
if (!batch) {
|
|
1662
2069
|
throw new AnyModelError(404, `Batch ${id} not found`);
|
|
1663
2070
|
}
|
|
1664
|
-
const results = this.store.getResults(id);
|
|
2071
|
+
const results = await this.store.getResults(id);
|
|
1665
2072
|
const usage = {
|
|
1666
2073
|
total_prompt_tokens: 0,
|
|
1667
2074
|
total_completion_tokens: 0,
|
|
@@ -1683,37 +2090,119 @@ var BatchManager = class {
|
|
|
1683
2090
|
/**
|
|
1684
2091
|
* List all batches.
|
|
1685
2092
|
*/
|
|
1686
|
-
list() {
|
|
1687
|
-
|
|
2093
|
+
async list() {
|
|
2094
|
+
const ids = await this.store.listBatches();
|
|
2095
|
+
const batches = [];
|
|
2096
|
+
for (const id of ids) {
|
|
2097
|
+
const meta = await this.store.getMeta(id);
|
|
2098
|
+
if (meta) batches.push(meta);
|
|
2099
|
+
}
|
|
2100
|
+
return batches;
|
|
1688
2101
|
}
|
|
1689
2102
|
/**
|
|
1690
2103
|
* Cancel a batch.
|
|
1691
2104
|
*/
|
|
1692
|
-
cancel(id) {
|
|
1693
|
-
const batch = this.store.getMeta(id);
|
|
2105
|
+
async cancel(id) {
|
|
2106
|
+
const batch = await this.store.getMeta(id);
|
|
1694
2107
|
if (!batch) {
|
|
1695
2108
|
throw new AnyModelError(404, `Batch ${id} not found`);
|
|
1696
2109
|
}
|
|
1697
2110
|
if (batch.status === "completed" || batch.status === "cancelled") {
|
|
1698
2111
|
return batch;
|
|
1699
2112
|
}
|
|
2113
|
+
if (batch.batch_mode === "native") {
|
|
2114
|
+
const providerState = await this.store.loadProviderState(id);
|
|
2115
|
+
const adapter = this.batchAdapters.get(batch.provider_name);
|
|
2116
|
+
if (adapter && providerState?.providerBatchId) {
|
|
2117
|
+
try {
|
|
2118
|
+
await adapter.cancelBatch(providerState.providerBatchId);
|
|
2119
|
+
} catch {
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
1700
2123
|
batch.status = "cancelled";
|
|
1701
2124
|
batch.completed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1702
|
-
this.store.updateMeta(batch);
|
|
2125
|
+
await this.store.updateMeta(batch);
|
|
1703
2126
|
return batch;
|
|
1704
2127
|
}
|
|
1705
2128
|
/**
|
|
1706
|
-
* Process batch
|
|
2129
|
+
* Process batch via native provider batch API.
|
|
2130
|
+
*/
|
|
2131
|
+
async processNativeBatch(batchId, request, adapter) {
|
|
2132
|
+
const batch = await this.store.getMeta(batchId);
|
|
2133
|
+
if (!batch) return;
|
|
2134
|
+
try {
|
|
2135
|
+
const model = request.model.includes("/") ? request.model.split("/").slice(1).join("/") : request.model;
|
|
2136
|
+
const { providerBatchId, metadata } = await adapter.createBatch(
|
|
2137
|
+
model,
|
|
2138
|
+
request.requests,
|
|
2139
|
+
request.options
|
|
2140
|
+
);
|
|
2141
|
+
await this.store.saveProviderState(batchId, {
|
|
2142
|
+
providerBatchId,
|
|
2143
|
+
providerName: batch.provider_name,
|
|
2144
|
+
...metadata
|
|
2145
|
+
});
|
|
2146
|
+
batch.status = "processing";
|
|
2147
|
+
await this.store.updateMeta(batch);
|
|
2148
|
+
} catch (err) {
|
|
2149
|
+
batch.status = "failed";
|
|
2150
|
+
batch.completed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
2151
|
+
await this.store.updateMeta(batch);
|
|
2152
|
+
throw err;
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
/**
|
|
2156
|
+
* Sync native batch status from provider.
|
|
1707
2157
|
*/
|
|
1708
|
-
async
|
|
1709
|
-
const batch = this.store.getMeta(batchId);
|
|
2158
|
+
async syncNativeBatchStatus(batchId) {
|
|
2159
|
+
const batch = await this.store.getMeta(batchId);
|
|
2160
|
+
if (!batch) return;
|
|
2161
|
+
const providerState = await this.store.loadProviderState(batchId);
|
|
2162
|
+
if (!providerState?.providerBatchId) return;
|
|
2163
|
+
const adapter = this.batchAdapters.get(batch.provider_name);
|
|
2164
|
+
if (!adapter) return;
|
|
2165
|
+
try {
|
|
2166
|
+
const status = await adapter.pollBatch(providerState.providerBatchId);
|
|
2167
|
+
batch.total = status.total || batch.total;
|
|
2168
|
+
batch.completed = status.completed;
|
|
2169
|
+
batch.failed = status.failed;
|
|
2170
|
+
if (status.status === "completed" || status.status === "failed" || status.status === "cancelled") {
|
|
2171
|
+
batch.status = status.status;
|
|
2172
|
+
batch.completed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
2173
|
+
if (status.status === "completed" || status.status === "failed") {
|
|
2174
|
+
try {
|
|
2175
|
+
const results = await adapter.getBatchResults(providerState.providerBatchId);
|
|
2176
|
+
for (const result of results) {
|
|
2177
|
+
await this.store.appendResult(batchId, result);
|
|
2178
|
+
}
|
|
2179
|
+
batch.completed = results.filter((r) => r.status === "success").length;
|
|
2180
|
+
batch.failed = results.filter((r) => r.status === "error").length;
|
|
2181
|
+
} catch {
|
|
2182
|
+
if (batch.status !== "failed") {
|
|
2183
|
+
batch.status = "failed";
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
} else {
|
|
2188
|
+
batch.status = "processing";
|
|
2189
|
+
}
|
|
2190
|
+
await this.store.updateMeta(batch);
|
|
2191
|
+
} catch {
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
/**
|
|
2195
|
+
* Process batch requests concurrently (fallback path).
|
|
2196
|
+
*/
|
|
2197
|
+
async processConcurrentBatch(batchId, request) {
|
|
2198
|
+
const batch = await this.store.getMeta(batchId);
|
|
2199
|
+
if (!batch) return;
|
|
1710
2200
|
batch.status = "processing";
|
|
1711
|
-
this.store.updateMeta(batch);
|
|
2201
|
+
await this.store.updateMeta(batch);
|
|
1712
2202
|
const items = request.requests;
|
|
1713
|
-
const queue = [...items];
|
|
1714
2203
|
const active = /* @__PURE__ */ new Set();
|
|
1715
2204
|
const processItem = async (item) => {
|
|
1716
|
-
const current = this.store.getMeta(batchId);
|
|
2205
|
+
const current = await this.store.getMeta(batchId);
|
|
1717
2206
|
if (current?.status === "cancelled") return;
|
|
1718
2207
|
const chatRequest = {
|
|
1719
2208
|
model: request.model,
|
|
@@ -1745,17 +2234,19 @@ var BatchManager = class {
|
|
|
1745
2234
|
error: { code: error.code, message: error.message }
|
|
1746
2235
|
};
|
|
1747
2236
|
}
|
|
1748
|
-
this.store.appendResult(batchId, result);
|
|
1749
|
-
const meta = this.store.getMeta(batchId);
|
|
1750
|
-
if (
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
2237
|
+
await this.store.appendResult(batchId, result);
|
|
2238
|
+
const meta = await this.store.getMeta(batchId);
|
|
2239
|
+
if (meta) {
|
|
2240
|
+
if (result.status === "success") {
|
|
2241
|
+
meta.completed++;
|
|
2242
|
+
} else {
|
|
2243
|
+
meta.failed++;
|
|
2244
|
+
}
|
|
2245
|
+
await this.store.updateMeta(meta);
|
|
1754
2246
|
}
|
|
1755
|
-
this.store.updateMeta(meta);
|
|
1756
2247
|
};
|
|
1757
|
-
for (const item of
|
|
1758
|
-
const current = this.store.getMeta(batchId);
|
|
2248
|
+
for (const item of items) {
|
|
2249
|
+
const current = await this.store.getMeta(batchId);
|
|
1759
2250
|
if (current?.status === "cancelled") break;
|
|
1760
2251
|
if (active.size >= this.concurrencyLimit) {
|
|
1761
2252
|
await Promise.race(active);
|
|
@@ -1766,15 +2257,411 @@ var BatchManager = class {
|
|
|
1766
2257
|
active.add(promise);
|
|
1767
2258
|
}
|
|
1768
2259
|
await Promise.all(active);
|
|
1769
|
-
const finalMeta = this.store.getMeta(batchId);
|
|
1770
|
-
if (finalMeta.status !== "cancelled") {
|
|
2260
|
+
const finalMeta = await this.store.getMeta(batchId);
|
|
2261
|
+
if (finalMeta && finalMeta.status !== "cancelled") {
|
|
1771
2262
|
finalMeta.status = finalMeta.failed === finalMeta.total ? "failed" : "completed";
|
|
1772
2263
|
finalMeta.completed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1773
|
-
this.store.updateMeta(finalMeta);
|
|
2264
|
+
await this.store.updateMeta(finalMeta);
|
|
1774
2265
|
}
|
|
1775
2266
|
}
|
|
1776
2267
|
};
|
|
1777
2268
|
|
|
2269
|
+
// src/providers/openai-batch.ts
|
|
2270
|
+
var OPENAI_API_BASE2 = "https://api.openai.com/v1";
|
|
2271
|
+
function createOpenAIBatchAdapter(apiKey) {
|
|
2272
|
+
async function apiRequest(path2, options = {}) {
|
|
2273
|
+
const headers = {
|
|
2274
|
+
"Authorization": `Bearer ${apiKey}`
|
|
2275
|
+
};
|
|
2276
|
+
let fetchBody;
|
|
2277
|
+
if (options.formData) {
|
|
2278
|
+
fetchBody = options.formData;
|
|
2279
|
+
} else if (options.body) {
|
|
2280
|
+
headers["Content-Type"] = "application/json";
|
|
2281
|
+
fetchBody = JSON.stringify(options.body);
|
|
2282
|
+
}
|
|
2283
|
+
const res = await fetch(`${OPENAI_API_BASE2}${path2}`, {
|
|
2284
|
+
method: options.method || "GET",
|
|
2285
|
+
headers,
|
|
2286
|
+
body: fetchBody
|
|
2287
|
+
});
|
|
2288
|
+
if (!res.ok) {
|
|
2289
|
+
let errorBody;
|
|
2290
|
+
try {
|
|
2291
|
+
errorBody = await res.json();
|
|
2292
|
+
} catch {
|
|
2293
|
+
errorBody = { message: res.statusText };
|
|
2294
|
+
}
|
|
2295
|
+
const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
|
|
2296
|
+
throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
|
|
2297
|
+
provider_name: "openai",
|
|
2298
|
+
raw: errorBody
|
|
2299
|
+
});
|
|
2300
|
+
}
|
|
2301
|
+
return res;
|
|
2302
|
+
}
|
|
2303
|
+
function buildJSONL(model, requests) {
|
|
2304
|
+
return requests.map((req) => {
|
|
2305
|
+
const body = {
|
|
2306
|
+
model,
|
|
2307
|
+
messages: req.messages
|
|
2308
|
+
};
|
|
2309
|
+
if (req.max_tokens !== void 0) body.max_tokens = req.max_tokens;
|
|
2310
|
+
if (req.temperature !== void 0) body.temperature = req.temperature;
|
|
2311
|
+
if (req.top_p !== void 0) body.top_p = req.top_p;
|
|
2312
|
+
if (req.stop !== void 0) body.stop = req.stop;
|
|
2313
|
+
if (req.response_format !== void 0) body.response_format = req.response_format;
|
|
2314
|
+
if (req.tools !== void 0) body.tools = req.tools;
|
|
2315
|
+
if (req.tool_choice !== void 0) body.tool_choice = req.tool_choice;
|
|
2316
|
+
return JSON.stringify({
|
|
2317
|
+
custom_id: req.custom_id,
|
|
2318
|
+
method: "POST",
|
|
2319
|
+
url: "/v1/chat/completions",
|
|
2320
|
+
body
|
|
2321
|
+
});
|
|
2322
|
+
}).join("\n");
|
|
2323
|
+
}
|
|
2324
|
+
function rePrefixId(id) {
|
|
2325
|
+
if (id && id.startsWith("chatcmpl-")) {
|
|
2326
|
+
return `gen-${id.substring(9)}`;
|
|
2327
|
+
}
|
|
2328
|
+
return id.startsWith("gen-") ? id : `gen-${id}`;
|
|
2329
|
+
}
|
|
2330
|
+
function translateOpenAIResponse(body) {
|
|
2331
|
+
return {
|
|
2332
|
+
id: rePrefixId(body.id || generateId()),
|
|
2333
|
+
object: "chat.completion",
|
|
2334
|
+
created: body.created || Math.floor(Date.now() / 1e3),
|
|
2335
|
+
model: `openai/${body.model}`,
|
|
2336
|
+
choices: body.choices,
|
|
2337
|
+
usage: body.usage
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2340
|
+
function mapStatus(openaiStatus) {
|
|
2341
|
+
switch (openaiStatus) {
|
|
2342
|
+
case "validating":
|
|
2343
|
+
case "finalizing":
|
|
2344
|
+
return "processing";
|
|
2345
|
+
case "in_progress":
|
|
2346
|
+
return "processing";
|
|
2347
|
+
case "completed":
|
|
2348
|
+
return "completed";
|
|
2349
|
+
case "failed":
|
|
2350
|
+
return "failed";
|
|
2351
|
+
case "expired":
|
|
2352
|
+
return "failed";
|
|
2353
|
+
case "cancelled":
|
|
2354
|
+
case "cancelling":
|
|
2355
|
+
return "cancelled";
|
|
2356
|
+
default:
|
|
2357
|
+
return "pending";
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
return {
|
|
2361
|
+
async createBatch(model, requests, options) {
|
|
2362
|
+
const jsonlContent = buildJSONL(model, requests);
|
|
2363
|
+
const blob = new Blob([jsonlContent], { type: "application/jsonl" });
|
|
2364
|
+
const formData = new FormData();
|
|
2365
|
+
formData.append("purpose", "batch");
|
|
2366
|
+
formData.append("file", blob, "batch_input.jsonl");
|
|
2367
|
+
const uploadRes = await apiRequest("/files", { method: "POST", formData });
|
|
2368
|
+
const fileData = await uploadRes.json();
|
|
2369
|
+
const inputFileId = fileData.id;
|
|
2370
|
+
const batchRes = await apiRequest("/batches", {
|
|
2371
|
+
method: "POST",
|
|
2372
|
+
body: {
|
|
2373
|
+
input_file_id: inputFileId,
|
|
2374
|
+
endpoint: "/v1/chat/completions",
|
|
2375
|
+
completion_window: "24h",
|
|
2376
|
+
metadata: options?.metadata
|
|
2377
|
+
}
|
|
2378
|
+
});
|
|
2379
|
+
const batchData = await batchRes.json();
|
|
2380
|
+
return {
|
|
2381
|
+
providerBatchId: batchData.id,
|
|
2382
|
+
metadata: {
|
|
2383
|
+
input_file_id: inputFileId,
|
|
2384
|
+
openai_status: batchData.status
|
|
2385
|
+
}
|
|
2386
|
+
};
|
|
2387
|
+
},
|
|
2388
|
+
async pollBatch(providerBatchId) {
|
|
2389
|
+
const res = await apiRequest(`/batches/${providerBatchId}`);
|
|
2390
|
+
const data = await res.json();
|
|
2391
|
+
const requestCounts = data.request_counts || {};
|
|
2392
|
+
return {
|
|
2393
|
+
status: mapStatus(data.status),
|
|
2394
|
+
total: requestCounts.total || 0,
|
|
2395
|
+
completed: requestCounts.completed || 0,
|
|
2396
|
+
failed: requestCounts.failed || 0
|
|
2397
|
+
};
|
|
2398
|
+
},
|
|
2399
|
+
async getBatchResults(providerBatchId) {
|
|
2400
|
+
const batchRes = await apiRequest(`/batches/${providerBatchId}`);
|
|
2401
|
+
const batchData = await batchRes.json();
|
|
2402
|
+
const results = [];
|
|
2403
|
+
if (batchData.output_file_id) {
|
|
2404
|
+
const outputRes = await apiRequest(`/files/${batchData.output_file_id}/content`);
|
|
2405
|
+
const outputText = await outputRes.text();
|
|
2406
|
+
for (const line of outputText.trim().split("\n")) {
|
|
2407
|
+
if (!line) continue;
|
|
2408
|
+
const item = JSON.parse(line);
|
|
2409
|
+
if (item.response?.status_code === 200) {
|
|
2410
|
+
results.push({
|
|
2411
|
+
custom_id: item.custom_id,
|
|
2412
|
+
status: "success",
|
|
2413
|
+
response: translateOpenAIResponse(item.response.body),
|
|
2414
|
+
error: null
|
|
2415
|
+
});
|
|
2416
|
+
} else {
|
|
2417
|
+
results.push({
|
|
2418
|
+
custom_id: item.custom_id,
|
|
2419
|
+
status: "error",
|
|
2420
|
+
response: null,
|
|
2421
|
+
error: {
|
|
2422
|
+
code: item.response?.status_code || 500,
|
|
2423
|
+
message: item.error?.message || item.response?.body?.error?.message || "Unknown error"
|
|
2424
|
+
}
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
if (batchData.error_file_id) {
|
|
2430
|
+
const errorRes = await apiRequest(`/files/${batchData.error_file_id}/content`);
|
|
2431
|
+
const errorText = await errorRes.text();
|
|
2432
|
+
for (const line of errorText.trim().split("\n")) {
|
|
2433
|
+
if (!line) continue;
|
|
2434
|
+
const item = JSON.parse(line);
|
|
2435
|
+
const existing = results.find((r) => r.custom_id === item.custom_id);
|
|
2436
|
+
if (!existing) {
|
|
2437
|
+
results.push({
|
|
2438
|
+
custom_id: item.custom_id,
|
|
2439
|
+
status: "error",
|
|
2440
|
+
response: null,
|
|
2441
|
+
error: {
|
|
2442
|
+
code: item.response?.status_code || 500,
|
|
2443
|
+
message: item.error?.message || "Batch item error"
|
|
2444
|
+
}
|
|
2445
|
+
});
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
return results;
|
|
2450
|
+
},
|
|
2451
|
+
async cancelBatch(providerBatchId) {
|
|
2452
|
+
await apiRequest(`/batches/${providerBatchId}/cancel`, { method: "POST" });
|
|
2453
|
+
}
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
// src/providers/anthropic-batch.ts
|
|
2458
|
+
var ANTHROPIC_API_BASE2 = "https://api.anthropic.com/v1";
|
|
2459
|
+
var ANTHROPIC_VERSION2 = "2023-06-01";
|
|
2460
|
+
var DEFAULT_MAX_TOKENS2 = 4096;
|
|
2461
|
+
function createAnthropicBatchAdapter(apiKey) {
|
|
2462
|
+
async function apiRequest(path2, options = {}) {
|
|
2463
|
+
const headers = {
|
|
2464
|
+
"x-api-key": apiKey,
|
|
2465
|
+
"anthropic-version": ANTHROPIC_VERSION2,
|
|
2466
|
+
"Content-Type": "application/json"
|
|
2467
|
+
};
|
|
2468
|
+
const res = await fetch(`${ANTHROPIC_API_BASE2}${path2}`, {
|
|
2469
|
+
method: options.method || "GET",
|
|
2470
|
+
headers,
|
|
2471
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
2472
|
+
});
|
|
2473
|
+
if (!res.ok) {
|
|
2474
|
+
let errorBody;
|
|
2475
|
+
try {
|
|
2476
|
+
errorBody = await res.json();
|
|
2477
|
+
} catch {
|
|
2478
|
+
errorBody = { message: res.statusText };
|
|
2479
|
+
}
|
|
2480
|
+
const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
|
|
2481
|
+
throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
|
|
2482
|
+
provider_name: "anthropic",
|
|
2483
|
+
raw: errorBody
|
|
2484
|
+
});
|
|
2485
|
+
}
|
|
2486
|
+
return res;
|
|
2487
|
+
}
|
|
2488
|
+
function translateToAnthropicParams(model, req) {
|
|
2489
|
+
const params = {
|
|
2490
|
+
model,
|
|
2491
|
+
max_tokens: req.max_tokens || DEFAULT_MAX_TOKENS2
|
|
2492
|
+
};
|
|
2493
|
+
const systemMessages = req.messages.filter((m) => m.role === "system");
|
|
2494
|
+
const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
|
|
2495
|
+
if (systemMessages.length > 0) {
|
|
2496
|
+
params.system = systemMessages.map((m) => typeof m.content === "string" ? m.content : "").join("\n");
|
|
2497
|
+
}
|
|
2498
|
+
params.messages = nonSystemMessages.map((m) => ({
|
|
2499
|
+
role: m.role === "tool" ? "user" : m.role,
|
|
2500
|
+
content: m.tool_call_id ? [{ type: "tool_result", tool_use_id: m.tool_call_id, content: typeof m.content === "string" ? m.content : "" }] : m.content
|
|
2501
|
+
}));
|
|
2502
|
+
if (req.temperature !== void 0) params.temperature = req.temperature;
|
|
2503
|
+
if (req.top_p !== void 0) params.top_p = req.top_p;
|
|
2504
|
+
if (req.top_k !== void 0) params.top_k = req.top_k;
|
|
2505
|
+
if (req.stop !== void 0) params.stop_sequences = Array.isArray(req.stop) ? req.stop : [req.stop];
|
|
2506
|
+
if (req.tools && req.tools.length > 0) {
|
|
2507
|
+
params.tools = req.tools.map((t) => ({
|
|
2508
|
+
name: t.function.name,
|
|
2509
|
+
description: t.function.description || "",
|
|
2510
|
+
input_schema: t.function.parameters || { type: "object", properties: {} }
|
|
2511
|
+
}));
|
|
2512
|
+
if (req.tool_choice) {
|
|
2513
|
+
if (req.tool_choice === "auto") {
|
|
2514
|
+
params.tool_choice = { type: "auto" };
|
|
2515
|
+
} else if (req.tool_choice === "required") {
|
|
2516
|
+
params.tool_choice = { type: "any" };
|
|
2517
|
+
} else if (req.tool_choice === "none") {
|
|
2518
|
+
delete params.tools;
|
|
2519
|
+
} else if (typeof req.tool_choice === "object") {
|
|
2520
|
+
params.tool_choice = { type: "tool", name: req.tool_choice.function.name };
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
if (req.response_format) {
|
|
2525
|
+
if (req.response_format.type === "json_object" || req.response_format.type === "json_schema") {
|
|
2526
|
+
const jsonInstruction = "Respond with valid JSON only. Do not include any text outside the JSON object.";
|
|
2527
|
+
params.system = params.system ? `${jsonInstruction}
|
|
2528
|
+
|
|
2529
|
+
${params.system}` : jsonInstruction;
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
return params;
|
|
2533
|
+
}
|
|
2534
|
+
function mapStopReason(reason) {
|
|
2535
|
+
switch (reason) {
|
|
2536
|
+
case "end_turn":
|
|
2537
|
+
return "stop";
|
|
2538
|
+
case "max_tokens":
|
|
2539
|
+
return "length";
|
|
2540
|
+
case "tool_use":
|
|
2541
|
+
return "tool_calls";
|
|
2542
|
+
case "stop_sequence":
|
|
2543
|
+
return "stop";
|
|
2544
|
+
default:
|
|
2545
|
+
return "stop";
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
function translateAnthropicMessage(msg) {
|
|
2549
|
+
let content = "";
|
|
2550
|
+
const toolCalls = [];
|
|
2551
|
+
for (const block of msg.content || []) {
|
|
2552
|
+
if (block.type === "text") {
|
|
2553
|
+
content += block.text;
|
|
2554
|
+
} else if (block.type === "tool_use") {
|
|
2555
|
+
toolCalls.push({
|
|
2556
|
+
id: block.id,
|
|
2557
|
+
type: "function",
|
|
2558
|
+
function: {
|
|
2559
|
+
name: block.name,
|
|
2560
|
+
arguments: JSON.stringify(block.input)
|
|
2561
|
+
}
|
|
2562
|
+
});
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
const message = { role: "assistant", content };
|
|
2566
|
+
if (toolCalls.length > 0) {
|
|
2567
|
+
message.tool_calls = toolCalls;
|
|
2568
|
+
}
|
|
2569
|
+
return {
|
|
2570
|
+
id: generateId(),
|
|
2571
|
+
object: "chat.completion",
|
|
2572
|
+
created: Math.floor(Date.now() / 1e3),
|
|
2573
|
+
model: `anthropic/${msg.model}`,
|
|
2574
|
+
choices: [{
|
|
2575
|
+
index: 0,
|
|
2576
|
+
message,
|
|
2577
|
+
finish_reason: mapStopReason(msg.stop_reason)
|
|
2578
|
+
}],
|
|
2579
|
+
usage: {
|
|
2580
|
+
prompt_tokens: msg.usage?.input_tokens || 0,
|
|
2581
|
+
completion_tokens: msg.usage?.output_tokens || 0,
|
|
2582
|
+
total_tokens: (msg.usage?.input_tokens || 0) + (msg.usage?.output_tokens || 0)
|
|
2583
|
+
}
|
|
2584
|
+
};
|
|
2585
|
+
}
|
|
2586
|
+
return {
|
|
2587
|
+
async createBatch(model, requests, _options) {
|
|
2588
|
+
const batchRequests = requests.map((req) => ({
|
|
2589
|
+
custom_id: req.custom_id,
|
|
2590
|
+
params: translateToAnthropicParams(model, req)
|
|
2591
|
+
}));
|
|
2592
|
+
const res = await apiRequest("/messages/batches", {
|
|
2593
|
+
method: "POST",
|
|
2594
|
+
body: { requests: batchRequests }
|
|
2595
|
+
});
|
|
2596
|
+
const data = await res.json();
|
|
2597
|
+
return {
|
|
2598
|
+
providerBatchId: data.id,
|
|
2599
|
+
metadata: {
|
|
2600
|
+
anthropic_type: data.type,
|
|
2601
|
+
created_at: data.created_at
|
|
2602
|
+
}
|
|
2603
|
+
};
|
|
2604
|
+
},
|
|
2605
|
+
async pollBatch(providerBatchId) {
|
|
2606
|
+
const res = await apiRequest(`/messages/batches/${providerBatchId}`);
|
|
2607
|
+
const data = await res.json();
|
|
2608
|
+
const counts = data.request_counts || {};
|
|
2609
|
+
const total = (counts.processing || 0) + (counts.succeeded || 0) + (counts.errored || 0) + (counts.canceled || 0) + (counts.expired || 0);
|
|
2610
|
+
let status;
|
|
2611
|
+
if (data.processing_status === "ended") {
|
|
2612
|
+
if (counts.succeeded === 0 && (counts.errored > 0 || counts.expired > 0 || counts.canceled > 0)) {
|
|
2613
|
+
status = "failed";
|
|
2614
|
+
} else if (data.cancel_initiated_at) {
|
|
2615
|
+
status = "cancelled";
|
|
2616
|
+
} else {
|
|
2617
|
+
status = "completed";
|
|
2618
|
+
}
|
|
2619
|
+
} else {
|
|
2620
|
+
status = "processing";
|
|
2621
|
+
}
|
|
2622
|
+
return {
|
|
2623
|
+
status,
|
|
2624
|
+
total,
|
|
2625
|
+
completed: counts.succeeded || 0,
|
|
2626
|
+
failed: (counts.errored || 0) + (counts.expired || 0) + (counts.canceled || 0)
|
|
2627
|
+
};
|
|
2628
|
+
},
|
|
2629
|
+
async getBatchResults(providerBatchId) {
|
|
2630
|
+
const res = await apiRequest(`/messages/batches/${providerBatchId}/results`);
|
|
2631
|
+
const text = await res.text();
|
|
2632
|
+
const results = [];
|
|
2633
|
+
for (const line of text.trim().split("\n")) {
|
|
2634
|
+
if (!line) continue;
|
|
2635
|
+
const item = JSON.parse(line);
|
|
2636
|
+
if (item.result?.type === "succeeded") {
|
|
2637
|
+
results.push({
|
|
2638
|
+
custom_id: item.custom_id,
|
|
2639
|
+
status: "success",
|
|
2640
|
+
response: translateAnthropicMessage(item.result.message),
|
|
2641
|
+
error: null
|
|
2642
|
+
});
|
|
2643
|
+
} else {
|
|
2644
|
+
const errorType = item.result?.type || "unknown";
|
|
2645
|
+
const errorMsg = item.result?.error?.message || `Batch item ${errorType}`;
|
|
2646
|
+
results.push({
|
|
2647
|
+
custom_id: item.custom_id,
|
|
2648
|
+
status: "error",
|
|
2649
|
+
response: null,
|
|
2650
|
+
error: {
|
|
2651
|
+
code: errorType === "expired" ? 408 : 500,
|
|
2652
|
+
message: errorMsg
|
|
2653
|
+
}
|
|
2654
|
+
});
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
return results;
|
|
2658
|
+
},
|
|
2659
|
+
async cancelBatch(providerBatchId) {
|
|
2660
|
+
await apiRequest(`/messages/batches/${providerBatchId}/cancel`, { method: "POST" });
|
|
2661
|
+
}
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2664
|
+
|
|
1778
2665
|
// src/client.ts
|
|
1779
2666
|
var AnyModel = class {
|
|
1780
2667
|
registry;
|
|
@@ -1790,6 +2677,9 @@ var AnyModel = class {
|
|
|
1790
2677
|
constructor(config = {}) {
|
|
1791
2678
|
this.config = resolveConfig(config);
|
|
1792
2679
|
this.registry = new ProviderRegistry();
|
|
2680
|
+
if (this.config.io) {
|
|
2681
|
+
configureFsIO(this.config.io);
|
|
2682
|
+
}
|
|
1793
2683
|
this.registerProviders();
|
|
1794
2684
|
this.router = new Router(this.registry, this.config.aliases, this.config);
|
|
1795
2685
|
this.chat = {
|
|
@@ -1839,8 +2729,10 @@ var AnyModel = class {
|
|
|
1839
2729
|
};
|
|
1840
2730
|
this.batchManager = new BatchManager(this.router, {
|
|
1841
2731
|
dir: this.config.batch?.dir,
|
|
1842
|
-
concurrency: this.config.batch?.concurrencyFallback
|
|
2732
|
+
concurrency: this.config.batch?.concurrencyFallback,
|
|
2733
|
+
pollInterval: this.config.batch?.pollInterval
|
|
1843
2734
|
});
|
|
2735
|
+
this.registerBatchAdapters();
|
|
1844
2736
|
this.batches = {
|
|
1845
2737
|
create: (request) => this.batchManager.create(request),
|
|
1846
2738
|
createAndPoll: (request, options) => this.batchManager.createAndPoll(request, options),
|
|
@@ -1865,14 +2757,17 @@ var AnyModel = class {
|
|
|
1865
2757
|
if (googleKey) {
|
|
1866
2758
|
this.registry.register("google", createGoogleAdapter(googleKey));
|
|
1867
2759
|
}
|
|
2760
|
+
const perplexityKey = config.perplexity?.apiKey || process.env.PERPLEXITY_API_KEY;
|
|
2761
|
+
if (perplexityKey) {
|
|
2762
|
+
this.registry.register("perplexity", createPerplexityAdapter(perplexityKey));
|
|
2763
|
+
}
|
|
1868
2764
|
const builtinProviders = [
|
|
1869
2765
|
{ name: "mistral", baseURL: "https://api.mistral.ai/v1", configKey: "mistral", envVar: "MISTRAL_API_KEY" },
|
|
1870
2766
|
{ name: "groq", baseURL: "https://api.groq.com/openai/v1", configKey: "groq", envVar: "GROQ_API_KEY" },
|
|
1871
2767
|
{ name: "deepseek", baseURL: "https://api.deepseek.com", configKey: "deepseek", envVar: "DEEPSEEK_API_KEY" },
|
|
1872
2768
|
{ name: "xai", baseURL: "https://api.x.ai/v1", configKey: "xai", envVar: "XAI_API_KEY" },
|
|
1873
2769
|
{ name: "together", baseURL: "https://api.together.xyz/v1", configKey: "together", envVar: "TOGETHER_API_KEY" },
|
|
1874
|
-
{ name: "fireworks", baseURL: "https://api.fireworks.ai/inference/v1", configKey: "fireworks", envVar: "FIREWORKS_API_KEY" }
|
|
1875
|
-
{ name: "perplexity", baseURL: "https://api.perplexity.ai", configKey: "perplexity", envVar: "PERPLEXITY_API_KEY" }
|
|
2770
|
+
{ name: "fireworks", baseURL: "https://api.fireworks.ai/inference/v1", configKey: "fireworks", envVar: "FIREWORKS_API_KEY" }
|
|
1876
2771
|
];
|
|
1877
2772
|
for (const { name, baseURL, configKey, envVar } of builtinProviders) {
|
|
1878
2773
|
const providerConfig = config[configKey];
|
|
@@ -1892,6 +2787,17 @@ var AnyModel = class {
|
|
|
1892
2787
|
}
|
|
1893
2788
|
}
|
|
1894
2789
|
}
|
|
2790
|
+
registerBatchAdapters() {
|
|
2791
|
+
const config = this.config;
|
|
2792
|
+
const openaiKey = config.openai?.apiKey || process.env.OPENAI_API_KEY;
|
|
2793
|
+
if (openaiKey) {
|
|
2794
|
+
this.batchManager.registerBatchAdapter("openai", createOpenAIBatchAdapter(openaiKey));
|
|
2795
|
+
}
|
|
2796
|
+
const anthropicKey = config.anthropic?.apiKey || process.env.ANTHROPIC_API_KEY;
|
|
2797
|
+
if (anthropicKey) {
|
|
2798
|
+
this.batchManager.registerBatchAdapter("anthropic", createAnthropicBatchAdapter(anthropicKey));
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
1895
2801
|
applyDefaults(request) {
|
|
1896
2802
|
const defaults = this.config.defaults;
|
|
1897
2803
|
if (!defaults) return request;
|
|
@@ -1919,10 +2825,10 @@ var AnyModel = class {
|
|
|
1919
2825
|
|
|
1920
2826
|
// src/server.ts
|
|
1921
2827
|
function parseBody(req) {
|
|
1922
|
-
return new Promise((
|
|
2828
|
+
return new Promise((resolve2, reject) => {
|
|
1923
2829
|
const chunks = [];
|
|
1924
2830
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
1925
|
-
req.on("end", () =>
|
|
2831
|
+
req.on("end", () => resolve2(Buffer.concat(chunks).toString()));
|
|
1926
2832
|
req.on("error", reject);
|
|
1927
2833
|
});
|
|
1928
2834
|
}
|
|
@@ -1952,7 +2858,7 @@ function createAnyModelServer(options = {}) {
|
|
|
1952
2858
|
const basePath = "/api/v1";
|
|
1953
2859
|
const server = (0, import_node_http.createServer)(async (req, res) => {
|
|
1954
2860
|
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
1955
|
-
const
|
|
2861
|
+
const path2 = url.pathname;
|
|
1956
2862
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1957
2863
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
1958
2864
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
@@ -1962,11 +2868,11 @@ function createAnyModelServer(options = {}) {
|
|
|
1962
2868
|
return;
|
|
1963
2869
|
}
|
|
1964
2870
|
try {
|
|
1965
|
-
if (
|
|
2871
|
+
if (path2 === "/health" && req.method === "GET") {
|
|
1966
2872
|
sendJSON(res, 200, { status: "ok" });
|
|
1967
2873
|
return;
|
|
1968
2874
|
}
|
|
1969
|
-
if (
|
|
2875
|
+
if (path2 === `${basePath}/chat/completions` && req.method === "POST") {
|
|
1970
2876
|
const body = JSON.parse(await parseBody(req));
|
|
1971
2877
|
if (body.stream) {
|
|
1972
2878
|
const stream = await client.chat.completions.create(body);
|
|
@@ -1977,14 +2883,14 @@ function createAnyModelServer(options = {}) {
|
|
|
1977
2883
|
}
|
|
1978
2884
|
return;
|
|
1979
2885
|
}
|
|
1980
|
-
if (
|
|
2886
|
+
if (path2 === `${basePath}/models` && req.method === "GET") {
|
|
1981
2887
|
const provider = url.searchParams.get("provider") || void 0;
|
|
1982
2888
|
const models = await client.models.list({ provider });
|
|
1983
2889
|
sendJSON(res, 200, { object: "list", data: models });
|
|
1984
2890
|
return;
|
|
1985
2891
|
}
|
|
1986
|
-
if (
|
|
1987
|
-
const id =
|
|
2892
|
+
if (path2.startsWith(`${basePath}/generation/`) && req.method === "GET") {
|
|
2893
|
+
const id = path2.substring(`${basePath}/generation/`.length);
|
|
1988
2894
|
const stats = client.generation.get(id);
|
|
1989
2895
|
if (!stats) {
|
|
1990
2896
|
sendError(res, 404, `Generation ${id} not found`);
|
|
@@ -1993,26 +2899,26 @@ function createAnyModelServer(options = {}) {
|
|
|
1993
2899
|
sendJSON(res, 200, stats);
|
|
1994
2900
|
return;
|
|
1995
2901
|
}
|
|
1996
|
-
if (
|
|
2902
|
+
if (path2 === `${basePath}/batches` && req.method === "POST") {
|
|
1997
2903
|
const body = JSON.parse(await parseBody(req));
|
|
1998
2904
|
const batch = await client.batches.create(body);
|
|
1999
2905
|
sendJSON(res, 201, batch);
|
|
2000
2906
|
return;
|
|
2001
2907
|
}
|
|
2002
|
-
if (
|
|
2003
|
-
const batches = client.batches.list();
|
|
2908
|
+
if (path2 === `${basePath}/batches` && req.method === "GET") {
|
|
2909
|
+
const batches = await client.batches.list();
|
|
2004
2910
|
sendJSON(res, 200, { object: "list", data: batches });
|
|
2005
2911
|
return;
|
|
2006
2912
|
}
|
|
2007
|
-
if (
|
|
2008
|
-
const parts =
|
|
2913
|
+
if (path2.startsWith(`${basePath}/batches/`) && req.method === "GET") {
|
|
2914
|
+
const parts = path2.substring(`${basePath}/batches/`.length).split("/");
|
|
2009
2915
|
const id = parts[0];
|
|
2010
2916
|
if (parts[1] === "results") {
|
|
2011
|
-
const results = client.batches.results(id);
|
|
2917
|
+
const results = await client.batches.results(id);
|
|
2012
2918
|
sendJSON(res, 200, results);
|
|
2013
2919
|
return;
|
|
2014
2920
|
}
|
|
2015
|
-
const batch = client.batches.get(id);
|
|
2921
|
+
const batch = await client.batches.get(id);
|
|
2016
2922
|
if (!batch) {
|
|
2017
2923
|
sendError(res, 404, `Batch ${id} not found`);
|
|
2018
2924
|
return;
|
|
@@ -2020,16 +2926,16 @@ function createAnyModelServer(options = {}) {
|
|
|
2020
2926
|
sendJSON(res, 200, batch);
|
|
2021
2927
|
return;
|
|
2022
2928
|
}
|
|
2023
|
-
if (
|
|
2024
|
-
const parts =
|
|
2929
|
+
if (path2.startsWith(`${basePath}/batches/`) && req.method === "POST") {
|
|
2930
|
+
const parts = path2.substring(`${basePath}/batches/`.length).split("/");
|
|
2025
2931
|
const id = parts[0];
|
|
2026
2932
|
if (parts[1] === "cancel") {
|
|
2027
|
-
const batch = client.batches.cancel(id);
|
|
2933
|
+
const batch = await client.batches.cancel(id);
|
|
2028
2934
|
sendJSON(res, 200, batch);
|
|
2029
2935
|
return;
|
|
2030
2936
|
}
|
|
2031
2937
|
}
|
|
2032
|
-
sendError(res, 404, `Not found: ${
|
|
2938
|
+
sendError(res, 404, `Not found: ${path2}`);
|
|
2033
2939
|
} catch (err) {
|
|
2034
2940
|
const code = err?.code || 500;
|
|
2035
2941
|
const message = err?.message || "Internal server error";
|