@probeo/anymodel 0.3.0 → 0.3.1
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 +1 -10
- package/dist/cli.cjs +827 -108
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +2785 -30
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +848 -110
- 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 +2782 -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);
|
|
@@ -1321,6 +1376,9 @@ function createCustomAdapter(name, config) {
|
|
|
1321
1376
|
return {
|
|
1322
1377
|
...openaiAdapter,
|
|
1323
1378
|
name,
|
|
1379
|
+
supportsBatch() {
|
|
1380
|
+
return false;
|
|
1381
|
+
},
|
|
1324
1382
|
async listModels() {
|
|
1325
1383
|
if (config.models && config.models.length > 0) {
|
|
1326
1384
|
return config.models.map((modelId) => ({
|
|
@@ -1386,10 +1444,10 @@ function interpolateDeep(obj) {
|
|
|
1386
1444
|
}
|
|
1387
1445
|
return obj;
|
|
1388
1446
|
}
|
|
1389
|
-
function loadJsonFile(
|
|
1390
|
-
if (!(0, import_node_fs.existsSync)(
|
|
1447
|
+
function loadJsonFile(path2) {
|
|
1448
|
+
if (!(0, import_node_fs.existsSync)(path2)) return null;
|
|
1391
1449
|
try {
|
|
1392
|
-
const raw = (0, import_node_fs.readFileSync)(
|
|
1450
|
+
const raw = (0, import_node_fs.readFileSync)(path2, "utf-8");
|
|
1393
1451
|
const parsed = JSON.parse(raw);
|
|
1394
1452
|
return interpolateDeep(parsed);
|
|
1395
1453
|
} catch {
|
|
@@ -1490,93 +1548,228 @@ var GenerationStatsStore = class {
|
|
|
1490
1548
|
}
|
|
1491
1549
|
};
|
|
1492
1550
|
|
|
1493
|
-
// src/
|
|
1551
|
+
// src/utils/fs-io.ts
|
|
1552
|
+
var import_promises = require("fs/promises");
|
|
1494
1553
|
var import_node_fs2 = require("fs");
|
|
1495
|
-
var import_node_path2 = require("path");
|
|
1496
|
-
var
|
|
1497
|
-
var
|
|
1554
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
1555
|
+
var import_p_queue = __toESM(require("p-queue"), 1);
|
|
1556
|
+
var writeQueue = new import_p_queue.default({ concurrency: 10 });
|
|
1557
|
+
var readQueue = new import_p_queue.default({ concurrency: 20 });
|
|
1558
|
+
function configureFsIO(options) {
|
|
1559
|
+
if (options.readConcurrency !== void 0) {
|
|
1560
|
+
readQueue.concurrency = options.readConcurrency;
|
|
1561
|
+
}
|
|
1562
|
+
if (options.writeConcurrency !== void 0) {
|
|
1563
|
+
writeQueue.concurrency = options.writeConcurrency;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
var ensuredDirs = /* @__PURE__ */ new Set();
|
|
1567
|
+
var joinPathCache = /* @__PURE__ */ new Map();
|
|
1568
|
+
var dirnameCache = /* @__PURE__ */ new Map();
|
|
1569
|
+
var resolvePathCache = /* @__PURE__ */ new Map();
|
|
1570
|
+
async function ensureDir(dir) {
|
|
1571
|
+
if (!dir) return;
|
|
1572
|
+
if (ensuredDirs.has(dir)) return;
|
|
1573
|
+
await (0, import_promises.mkdir)(dir, { recursive: true });
|
|
1574
|
+
ensuredDirs.add(dir);
|
|
1575
|
+
}
|
|
1576
|
+
async function readFileQueued(filePath, encoding = "utf8") {
|
|
1577
|
+
return readQueue.add(async () => {
|
|
1578
|
+
return (0, import_promises.readFile)(filePath, encoding);
|
|
1579
|
+
});
|
|
1580
|
+
}
|
|
1581
|
+
async function readJsonQueued(filePath) {
|
|
1582
|
+
const raw = await readFileQueued(filePath, "utf8");
|
|
1583
|
+
return JSON.parse(raw);
|
|
1584
|
+
}
|
|
1585
|
+
async function readDirQueued(dirPath) {
|
|
1586
|
+
return readQueue.add(async () => {
|
|
1587
|
+
return (0, import_promises.readdir)(dirPath, { withFileTypes: true });
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
async function pathExistsQueued(p) {
|
|
1591
|
+
return readQueue.add(async () => {
|
|
1592
|
+
try {
|
|
1593
|
+
await (0, import_promises.stat)(p);
|
|
1594
|
+
return true;
|
|
1595
|
+
} catch {
|
|
1596
|
+
return false;
|
|
1597
|
+
}
|
|
1598
|
+
});
|
|
1599
|
+
}
|
|
1600
|
+
async function fileExistsQueued(filePath) {
|
|
1601
|
+
return readQueue.add(async () => {
|
|
1602
|
+
try {
|
|
1603
|
+
const s = await (0, import_promises.stat)(filePath);
|
|
1604
|
+
return s.isFile();
|
|
1605
|
+
} catch {
|
|
1606
|
+
return false;
|
|
1607
|
+
}
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
async function writeFileQueued(filePath, data) {
|
|
1611
|
+
await writeQueue.add(async () => {
|
|
1612
|
+
const dir = dirnameOf(filePath);
|
|
1613
|
+
await ensureDir(dir);
|
|
1614
|
+
await (0, import_promises.writeFile)(filePath, data);
|
|
1615
|
+
});
|
|
1616
|
+
}
|
|
1617
|
+
async function appendFileQueued(filePath, data) {
|
|
1618
|
+
await writeQueue.add(async () => {
|
|
1619
|
+
const dir = dirnameOf(filePath);
|
|
1620
|
+
await ensureDir(dir);
|
|
1621
|
+
await (0, import_promises.writeFile)(filePath, data, { flag: "a" });
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
async function writeFileFlushedQueued(filePath, data) {
|
|
1625
|
+
await writeQueue.add(async () => {
|
|
1626
|
+
const dir = dirnameOf(filePath);
|
|
1627
|
+
await ensureDir(dir);
|
|
1628
|
+
const tmpPath = joinPath(
|
|
1629
|
+
dir,
|
|
1630
|
+
`.${import_node_path2.default.basename(filePath)}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`
|
|
1631
|
+
);
|
|
1632
|
+
const fh = await (0, import_promises.open)(tmpPath, "w");
|
|
1633
|
+
try {
|
|
1634
|
+
await fh.writeFile(data);
|
|
1635
|
+
await fh.sync();
|
|
1636
|
+
} finally {
|
|
1637
|
+
await fh.close();
|
|
1638
|
+
}
|
|
1639
|
+
await (0, import_promises.rename)(tmpPath, filePath);
|
|
1640
|
+
try {
|
|
1641
|
+
const dh = await (0, import_promises.open)(dir, "r");
|
|
1642
|
+
try {
|
|
1643
|
+
await dh.sync();
|
|
1644
|
+
} finally {
|
|
1645
|
+
await dh.close();
|
|
1646
|
+
}
|
|
1647
|
+
} catch {
|
|
1648
|
+
}
|
|
1649
|
+
});
|
|
1650
|
+
}
|
|
1651
|
+
function joinPath(...segments) {
|
|
1652
|
+
const key = segments.join("\0");
|
|
1653
|
+
const cached = joinPathCache.get(key);
|
|
1654
|
+
if (cached !== void 0) return cached;
|
|
1655
|
+
const out = import_node_path2.default.join(...segments);
|
|
1656
|
+
joinPathCache.set(key, out);
|
|
1657
|
+
return out;
|
|
1658
|
+
}
|
|
1659
|
+
function dirnameOf(p) {
|
|
1660
|
+
const cached = dirnameCache.get(p);
|
|
1661
|
+
if (cached !== void 0) return cached;
|
|
1662
|
+
const out = import_node_path2.default.dirname(p);
|
|
1663
|
+
dirnameCache.set(p, out);
|
|
1664
|
+
return out;
|
|
1665
|
+
}
|
|
1666
|
+
function resolvePath(...segments) {
|
|
1667
|
+
const key = segments.join("\0");
|
|
1668
|
+
const cached = resolvePathCache.get(key);
|
|
1669
|
+
if (cached !== void 0) return cached;
|
|
1670
|
+
const out = import_node_path2.default.resolve(...segments);
|
|
1671
|
+
resolvePathCache.set(key, out);
|
|
1672
|
+
return out;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
// src/batch/store.ts
|
|
1676
|
+
var DEFAULT_BATCH_DIR = joinPath(process.cwd(), ".anymodel", "batches");
|
|
1498
1677
|
var BatchStore = class {
|
|
1499
1678
|
dir;
|
|
1679
|
+
initialized = false;
|
|
1500
1680
|
constructor(dir) {
|
|
1501
|
-
this.dir = (
|
|
1502
|
-
|
|
1681
|
+
this.dir = resolvePath(dir || DEFAULT_BATCH_DIR);
|
|
1682
|
+
}
|
|
1683
|
+
async init() {
|
|
1684
|
+
if (this.initialized) return;
|
|
1685
|
+
await ensureDir(this.dir);
|
|
1686
|
+
this.initialized = true;
|
|
1503
1687
|
}
|
|
1504
1688
|
batchDir(id) {
|
|
1505
|
-
return (
|
|
1689
|
+
return joinPath(this.dir, id);
|
|
1506
1690
|
}
|
|
1507
1691
|
/**
|
|
1508
1692
|
* Create a new batch directory and save initial metadata.
|
|
1509
1693
|
*/
|
|
1510
|
-
create(batch) {
|
|
1694
|
+
async create(batch) {
|
|
1695
|
+
await this.init();
|
|
1511
1696
|
const dir = this.batchDir(batch.id);
|
|
1512
|
-
|
|
1513
|
-
|
|
1697
|
+
await ensureDir(dir);
|
|
1698
|
+
await writeFileFlushedQueued(joinPath(dir, "meta.json"), JSON.stringify(batch, null, 2));
|
|
1514
1699
|
}
|
|
1515
1700
|
/**
|
|
1516
|
-
* Update batch metadata.
|
|
1701
|
+
* Update batch metadata (atomic write).
|
|
1517
1702
|
*/
|
|
1518
|
-
updateMeta(batch) {
|
|
1519
|
-
|
|
1520
|
-
|
|
1703
|
+
async updateMeta(batch) {
|
|
1704
|
+
await writeFileFlushedQueued(
|
|
1705
|
+
joinPath(this.batchDir(batch.id), "meta.json"),
|
|
1706
|
+
JSON.stringify(batch, null, 2)
|
|
1707
|
+
);
|
|
1521
1708
|
}
|
|
1522
1709
|
/**
|
|
1523
1710
|
* Save requests as JSONL.
|
|
1524
1711
|
*/
|
|
1525
|
-
saveRequests(id, requests) {
|
|
1526
|
-
const dir = this.batchDir(id);
|
|
1712
|
+
async saveRequests(id, requests) {
|
|
1527
1713
|
const lines = requests.map((r) => JSON.stringify(r)).join("\n") + "\n";
|
|
1528
|
-
|
|
1714
|
+
await writeFileQueued(joinPath(this.batchDir(id), "requests.jsonl"), lines);
|
|
1529
1715
|
}
|
|
1530
1716
|
/**
|
|
1531
1717
|
* Append a result to results.jsonl.
|
|
1532
1718
|
*/
|
|
1533
|
-
appendResult(id, result) {
|
|
1534
|
-
|
|
1535
|
-
|
|
1719
|
+
async appendResult(id, result) {
|
|
1720
|
+
await appendFileQueued(
|
|
1721
|
+
joinPath(this.batchDir(id), "results.jsonl"),
|
|
1722
|
+
JSON.stringify(result) + "\n"
|
|
1723
|
+
);
|
|
1536
1724
|
}
|
|
1537
1725
|
/**
|
|
1538
1726
|
* Save provider-specific state (e.g., provider batch ID).
|
|
1539
1727
|
*/
|
|
1540
|
-
saveProviderState(id, state) {
|
|
1541
|
-
|
|
1542
|
-
|
|
1728
|
+
async saveProviderState(id, state) {
|
|
1729
|
+
await writeFileFlushedQueued(
|
|
1730
|
+
joinPath(this.batchDir(id), "provider.json"),
|
|
1731
|
+
JSON.stringify(state, null, 2)
|
|
1732
|
+
);
|
|
1543
1733
|
}
|
|
1544
1734
|
/**
|
|
1545
1735
|
* Load provider state.
|
|
1546
1736
|
*/
|
|
1547
|
-
loadProviderState(id) {
|
|
1548
|
-
const
|
|
1549
|
-
if (!
|
|
1550
|
-
return
|
|
1737
|
+
async loadProviderState(id) {
|
|
1738
|
+
const p = joinPath(this.batchDir(id), "provider.json");
|
|
1739
|
+
if (!await fileExistsQueued(p)) return null;
|
|
1740
|
+
return readJsonQueued(p);
|
|
1551
1741
|
}
|
|
1552
1742
|
/**
|
|
1553
1743
|
* Get batch metadata.
|
|
1554
1744
|
*/
|
|
1555
|
-
getMeta(id) {
|
|
1556
|
-
const
|
|
1557
|
-
if (!
|
|
1558
|
-
return
|
|
1745
|
+
async getMeta(id) {
|
|
1746
|
+
const p = joinPath(this.batchDir(id), "meta.json");
|
|
1747
|
+
if (!await fileExistsQueued(p)) return null;
|
|
1748
|
+
return readJsonQueued(p);
|
|
1559
1749
|
}
|
|
1560
1750
|
/**
|
|
1561
1751
|
* Get all results for a batch.
|
|
1562
1752
|
*/
|
|
1563
|
-
getResults(id) {
|
|
1564
|
-
const
|
|
1565
|
-
if (!
|
|
1566
|
-
|
|
1753
|
+
async getResults(id) {
|
|
1754
|
+
const p = joinPath(this.batchDir(id), "results.jsonl");
|
|
1755
|
+
if (!await fileExistsQueued(p)) return [];
|
|
1756
|
+
const raw = await readFileQueued(p, "utf8");
|
|
1757
|
+
return raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
1567
1758
|
}
|
|
1568
1759
|
/**
|
|
1569
1760
|
* List all batch IDs.
|
|
1570
1761
|
*/
|
|
1571
|
-
listBatches() {
|
|
1572
|
-
|
|
1573
|
-
|
|
1762
|
+
async listBatches() {
|
|
1763
|
+
await this.init();
|
|
1764
|
+
if (!await pathExistsQueued(this.dir)) return [];
|
|
1765
|
+
const entries = await readDirQueued(this.dir);
|
|
1766
|
+
return entries.filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
1574
1767
|
}
|
|
1575
1768
|
/**
|
|
1576
1769
|
* Check if a batch exists.
|
|
1577
1770
|
*/
|
|
1578
|
-
exists(id) {
|
|
1579
|
-
return (
|
|
1771
|
+
async exists(id) {
|
|
1772
|
+
return fileExistsQueued(joinPath(this.batchDir(id), "meta.json"));
|
|
1580
1773
|
}
|
|
1581
1774
|
};
|
|
1582
1775
|
|
|
@@ -1585,10 +1778,27 @@ var BatchManager = class {
|
|
|
1585
1778
|
store;
|
|
1586
1779
|
router;
|
|
1587
1780
|
concurrencyLimit;
|
|
1781
|
+
defaultPollInterval;
|
|
1782
|
+
batchAdapters = /* @__PURE__ */ new Map();
|
|
1588
1783
|
constructor(router, options) {
|
|
1589
1784
|
this.store = new BatchStore(options?.dir);
|
|
1590
1785
|
this.router = router;
|
|
1591
1786
|
this.concurrencyLimit = options?.concurrency ?? 5;
|
|
1787
|
+
this.defaultPollInterval = options?.pollInterval ?? 5e3;
|
|
1788
|
+
}
|
|
1789
|
+
/**
|
|
1790
|
+
* Register a native batch adapter for a provider.
|
|
1791
|
+
*/
|
|
1792
|
+
registerBatchAdapter(providerName, adapter) {
|
|
1793
|
+
this.batchAdapters.set(providerName, adapter);
|
|
1794
|
+
}
|
|
1795
|
+
/**
|
|
1796
|
+
* Check if a provider has native batch support.
|
|
1797
|
+
*/
|
|
1798
|
+
getNativeBatchAdapter(model) {
|
|
1799
|
+
const providerName = model.split("/")[0];
|
|
1800
|
+
const adapter = this.batchAdapters.get(providerName);
|
|
1801
|
+
return adapter ? { adapter, providerName } : null;
|
|
1592
1802
|
}
|
|
1593
1803
|
/**
|
|
1594
1804
|
* Create a batch and return immediately (no polling).
|
|
@@ -1596,13 +1806,16 @@ var BatchManager = class {
|
|
|
1596
1806
|
async create(request) {
|
|
1597
1807
|
const id = generateId("batch");
|
|
1598
1808
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1809
|
+
const providerName = request.model.split("/")[0] || "unknown";
|
|
1810
|
+
const native = this.getNativeBatchAdapter(request.model);
|
|
1811
|
+
const batchMode = native ? "native" : "concurrent";
|
|
1599
1812
|
const batch = {
|
|
1600
1813
|
id,
|
|
1601
1814
|
object: "batch",
|
|
1602
1815
|
status: "pending",
|
|
1603
1816
|
model: request.model,
|
|
1604
|
-
provider_name:
|
|
1605
|
-
batch_mode:
|
|
1817
|
+
provider_name: providerName,
|
|
1818
|
+
batch_mode: batchMode,
|
|
1606
1819
|
total: request.requests.length,
|
|
1607
1820
|
completed: 0,
|
|
1608
1821
|
failed: 0,
|
|
@@ -1610,10 +1823,15 @@ var BatchManager = class {
|
|
|
1610
1823
|
completed_at: null,
|
|
1611
1824
|
expires_at: null
|
|
1612
1825
|
};
|
|
1613
|
-
this.store.create(batch);
|
|
1614
|
-
this.store.saveRequests(id, request.requests);
|
|
1615
|
-
|
|
1616
|
-
|
|
1826
|
+
await this.store.create(batch);
|
|
1827
|
+
await this.store.saveRequests(id, request.requests);
|
|
1828
|
+
if (native) {
|
|
1829
|
+
this.processNativeBatch(id, request, native.adapter).catch(() => {
|
|
1830
|
+
});
|
|
1831
|
+
} else {
|
|
1832
|
+
this.processConcurrentBatch(id, request).catch(() => {
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1617
1835
|
return batch;
|
|
1618
1836
|
}
|
|
1619
1837
|
/**
|
|
@@ -1627,14 +1845,19 @@ var BatchManager = class {
|
|
|
1627
1845
|
* Poll an existing batch until completion.
|
|
1628
1846
|
*/
|
|
1629
1847
|
async poll(id, options = {}) {
|
|
1630
|
-
const interval = options.interval ??
|
|
1848
|
+
const interval = options.interval ?? this.defaultPollInterval;
|
|
1631
1849
|
const timeout = options.timeout ?? 0;
|
|
1632
1850
|
const startTime = Date.now();
|
|
1633
1851
|
while (true) {
|
|
1634
|
-
|
|
1852
|
+
let batch = await this.store.getMeta(id);
|
|
1635
1853
|
if (!batch) {
|
|
1636
1854
|
throw new AnyModelError(404, `Batch ${id} not found`);
|
|
1637
1855
|
}
|
|
1856
|
+
if (batch.batch_mode === "native" && batch.status === "processing") {
|
|
1857
|
+
await this.syncNativeBatchStatus(id);
|
|
1858
|
+
batch = await this.store.getMeta(id);
|
|
1859
|
+
if (!batch) throw new AnyModelError(404, `Batch ${id} not found`);
|
|
1860
|
+
}
|
|
1638
1861
|
if (options.onProgress) {
|
|
1639
1862
|
options.onProgress(batch);
|
|
1640
1863
|
}
|
|
@@ -1644,24 +1867,24 @@ var BatchManager = class {
|
|
|
1644
1867
|
if (timeout > 0 && Date.now() - startTime > timeout) {
|
|
1645
1868
|
throw new AnyModelError(408, `Batch ${id} timed out after ${timeout}ms`);
|
|
1646
1869
|
}
|
|
1647
|
-
await new Promise((
|
|
1870
|
+
await new Promise((resolve2) => setTimeout(resolve2, interval));
|
|
1648
1871
|
}
|
|
1649
1872
|
}
|
|
1650
1873
|
/**
|
|
1651
1874
|
* Get the current status of a batch.
|
|
1652
1875
|
*/
|
|
1653
|
-
get(id) {
|
|
1876
|
+
async get(id) {
|
|
1654
1877
|
return this.store.getMeta(id);
|
|
1655
1878
|
}
|
|
1656
1879
|
/**
|
|
1657
1880
|
* Get results for a completed batch.
|
|
1658
1881
|
*/
|
|
1659
|
-
getResults(id) {
|
|
1660
|
-
const batch = this.store.getMeta(id);
|
|
1882
|
+
async getResults(id) {
|
|
1883
|
+
const batch = await this.store.getMeta(id);
|
|
1661
1884
|
if (!batch) {
|
|
1662
1885
|
throw new AnyModelError(404, `Batch ${id} not found`);
|
|
1663
1886
|
}
|
|
1664
|
-
const results = this.store.getResults(id);
|
|
1887
|
+
const results = await this.store.getResults(id);
|
|
1665
1888
|
const usage = {
|
|
1666
1889
|
total_prompt_tokens: 0,
|
|
1667
1890
|
total_completion_tokens: 0,
|
|
@@ -1683,37 +1906,119 @@ var BatchManager = class {
|
|
|
1683
1906
|
/**
|
|
1684
1907
|
* List all batches.
|
|
1685
1908
|
*/
|
|
1686
|
-
list() {
|
|
1687
|
-
|
|
1909
|
+
async list() {
|
|
1910
|
+
const ids = await this.store.listBatches();
|
|
1911
|
+
const batches = [];
|
|
1912
|
+
for (const id of ids) {
|
|
1913
|
+
const meta = await this.store.getMeta(id);
|
|
1914
|
+
if (meta) batches.push(meta);
|
|
1915
|
+
}
|
|
1916
|
+
return batches;
|
|
1688
1917
|
}
|
|
1689
1918
|
/**
|
|
1690
1919
|
* Cancel a batch.
|
|
1691
1920
|
*/
|
|
1692
|
-
cancel(id) {
|
|
1693
|
-
const batch = this.store.getMeta(id);
|
|
1921
|
+
async cancel(id) {
|
|
1922
|
+
const batch = await this.store.getMeta(id);
|
|
1694
1923
|
if (!batch) {
|
|
1695
1924
|
throw new AnyModelError(404, `Batch ${id} not found`);
|
|
1696
1925
|
}
|
|
1697
1926
|
if (batch.status === "completed" || batch.status === "cancelled") {
|
|
1698
1927
|
return batch;
|
|
1699
1928
|
}
|
|
1929
|
+
if (batch.batch_mode === "native") {
|
|
1930
|
+
const providerState = await this.store.loadProviderState(id);
|
|
1931
|
+
const adapter = this.batchAdapters.get(batch.provider_name);
|
|
1932
|
+
if (adapter && providerState?.providerBatchId) {
|
|
1933
|
+
try {
|
|
1934
|
+
await adapter.cancelBatch(providerState.providerBatchId);
|
|
1935
|
+
} catch {
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1700
1939
|
batch.status = "cancelled";
|
|
1701
1940
|
batch.completed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1702
|
-
this.store.updateMeta(batch);
|
|
1941
|
+
await this.store.updateMeta(batch);
|
|
1703
1942
|
return batch;
|
|
1704
1943
|
}
|
|
1705
1944
|
/**
|
|
1706
|
-
* Process batch
|
|
1945
|
+
* Process batch via native provider batch API.
|
|
1707
1946
|
*/
|
|
1708
|
-
async
|
|
1709
|
-
const batch = this.store.getMeta(batchId);
|
|
1947
|
+
async processNativeBatch(batchId, request, adapter) {
|
|
1948
|
+
const batch = await this.store.getMeta(batchId);
|
|
1949
|
+
if (!batch) return;
|
|
1950
|
+
try {
|
|
1951
|
+
const model = request.model.includes("/") ? request.model.split("/").slice(1).join("/") : request.model;
|
|
1952
|
+
const { providerBatchId, metadata } = await adapter.createBatch(
|
|
1953
|
+
model,
|
|
1954
|
+
request.requests,
|
|
1955
|
+
request.options
|
|
1956
|
+
);
|
|
1957
|
+
await this.store.saveProviderState(batchId, {
|
|
1958
|
+
providerBatchId,
|
|
1959
|
+
providerName: batch.provider_name,
|
|
1960
|
+
...metadata
|
|
1961
|
+
});
|
|
1962
|
+
batch.status = "processing";
|
|
1963
|
+
await this.store.updateMeta(batch);
|
|
1964
|
+
} catch (err) {
|
|
1965
|
+
batch.status = "failed";
|
|
1966
|
+
batch.completed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1967
|
+
await this.store.updateMeta(batch);
|
|
1968
|
+
throw err;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
/**
|
|
1972
|
+
* Sync native batch status from provider.
|
|
1973
|
+
*/
|
|
1974
|
+
async syncNativeBatchStatus(batchId) {
|
|
1975
|
+
const batch = await this.store.getMeta(batchId);
|
|
1976
|
+
if (!batch) return;
|
|
1977
|
+
const providerState = await this.store.loadProviderState(batchId);
|
|
1978
|
+
if (!providerState?.providerBatchId) return;
|
|
1979
|
+
const adapter = this.batchAdapters.get(batch.provider_name);
|
|
1980
|
+
if (!adapter) return;
|
|
1981
|
+
try {
|
|
1982
|
+
const status = await adapter.pollBatch(providerState.providerBatchId);
|
|
1983
|
+
batch.total = status.total || batch.total;
|
|
1984
|
+
batch.completed = status.completed;
|
|
1985
|
+
batch.failed = status.failed;
|
|
1986
|
+
if (status.status === "completed" || status.status === "failed" || status.status === "cancelled") {
|
|
1987
|
+
batch.status = status.status;
|
|
1988
|
+
batch.completed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1989
|
+
if (status.status === "completed" || status.status === "failed") {
|
|
1990
|
+
try {
|
|
1991
|
+
const results = await adapter.getBatchResults(providerState.providerBatchId);
|
|
1992
|
+
for (const result of results) {
|
|
1993
|
+
await this.store.appendResult(batchId, result);
|
|
1994
|
+
}
|
|
1995
|
+
batch.completed = results.filter((r) => r.status === "success").length;
|
|
1996
|
+
batch.failed = results.filter((r) => r.status === "error").length;
|
|
1997
|
+
} catch {
|
|
1998
|
+
if (batch.status !== "failed") {
|
|
1999
|
+
batch.status = "failed";
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
} else {
|
|
2004
|
+
batch.status = "processing";
|
|
2005
|
+
}
|
|
2006
|
+
await this.store.updateMeta(batch);
|
|
2007
|
+
} catch {
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
/**
|
|
2011
|
+
* Process batch requests concurrently (fallback path).
|
|
2012
|
+
*/
|
|
2013
|
+
async processConcurrentBatch(batchId, request) {
|
|
2014
|
+
const batch = await this.store.getMeta(batchId);
|
|
2015
|
+
if (!batch) return;
|
|
1710
2016
|
batch.status = "processing";
|
|
1711
|
-
this.store.updateMeta(batch);
|
|
2017
|
+
await this.store.updateMeta(batch);
|
|
1712
2018
|
const items = request.requests;
|
|
1713
|
-
const queue = [...items];
|
|
1714
2019
|
const active = /* @__PURE__ */ new Set();
|
|
1715
2020
|
const processItem = async (item) => {
|
|
1716
|
-
const current = this.store.getMeta(batchId);
|
|
2021
|
+
const current = await this.store.getMeta(batchId);
|
|
1717
2022
|
if (current?.status === "cancelled") return;
|
|
1718
2023
|
const chatRequest = {
|
|
1719
2024
|
model: request.model,
|
|
@@ -1745,17 +2050,19 @@ var BatchManager = class {
|
|
|
1745
2050
|
error: { code: error.code, message: error.message }
|
|
1746
2051
|
};
|
|
1747
2052
|
}
|
|
1748
|
-
this.store.appendResult(batchId, result);
|
|
1749
|
-
const meta = this.store.getMeta(batchId);
|
|
1750
|
-
if (
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
2053
|
+
await this.store.appendResult(batchId, result);
|
|
2054
|
+
const meta = await this.store.getMeta(batchId);
|
|
2055
|
+
if (meta) {
|
|
2056
|
+
if (result.status === "success") {
|
|
2057
|
+
meta.completed++;
|
|
2058
|
+
} else {
|
|
2059
|
+
meta.failed++;
|
|
2060
|
+
}
|
|
2061
|
+
await this.store.updateMeta(meta);
|
|
1754
2062
|
}
|
|
1755
|
-
this.store.updateMeta(meta);
|
|
1756
2063
|
};
|
|
1757
|
-
for (const item of
|
|
1758
|
-
const current = this.store.getMeta(batchId);
|
|
2064
|
+
for (const item of items) {
|
|
2065
|
+
const current = await this.store.getMeta(batchId);
|
|
1759
2066
|
if (current?.status === "cancelled") break;
|
|
1760
2067
|
if (active.size >= this.concurrencyLimit) {
|
|
1761
2068
|
await Promise.race(active);
|
|
@@ -1766,15 +2073,411 @@ var BatchManager = class {
|
|
|
1766
2073
|
active.add(promise);
|
|
1767
2074
|
}
|
|
1768
2075
|
await Promise.all(active);
|
|
1769
|
-
const finalMeta = this.store.getMeta(batchId);
|
|
1770
|
-
if (finalMeta.status !== "cancelled") {
|
|
2076
|
+
const finalMeta = await this.store.getMeta(batchId);
|
|
2077
|
+
if (finalMeta && finalMeta.status !== "cancelled") {
|
|
1771
2078
|
finalMeta.status = finalMeta.failed === finalMeta.total ? "failed" : "completed";
|
|
1772
2079
|
finalMeta.completed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1773
|
-
this.store.updateMeta(finalMeta);
|
|
2080
|
+
await this.store.updateMeta(finalMeta);
|
|
1774
2081
|
}
|
|
1775
2082
|
}
|
|
1776
2083
|
};
|
|
1777
2084
|
|
|
2085
|
+
// src/providers/openai-batch.ts
|
|
2086
|
+
var OPENAI_API_BASE2 = "https://api.openai.com/v1";
|
|
2087
|
+
function createOpenAIBatchAdapter(apiKey) {
|
|
2088
|
+
async function apiRequest(path2, options = {}) {
|
|
2089
|
+
const headers = {
|
|
2090
|
+
"Authorization": `Bearer ${apiKey}`
|
|
2091
|
+
};
|
|
2092
|
+
let fetchBody;
|
|
2093
|
+
if (options.formData) {
|
|
2094
|
+
fetchBody = options.formData;
|
|
2095
|
+
} else if (options.body) {
|
|
2096
|
+
headers["Content-Type"] = "application/json";
|
|
2097
|
+
fetchBody = JSON.stringify(options.body);
|
|
2098
|
+
}
|
|
2099
|
+
const res = await fetch(`${OPENAI_API_BASE2}${path2}`, {
|
|
2100
|
+
method: options.method || "GET",
|
|
2101
|
+
headers,
|
|
2102
|
+
body: fetchBody
|
|
2103
|
+
});
|
|
2104
|
+
if (!res.ok) {
|
|
2105
|
+
let errorBody;
|
|
2106
|
+
try {
|
|
2107
|
+
errorBody = await res.json();
|
|
2108
|
+
} catch {
|
|
2109
|
+
errorBody = { message: res.statusText };
|
|
2110
|
+
}
|
|
2111
|
+
const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
|
|
2112
|
+
throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
|
|
2113
|
+
provider_name: "openai",
|
|
2114
|
+
raw: errorBody
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2117
|
+
return res;
|
|
2118
|
+
}
|
|
2119
|
+
function buildJSONL(model, requests) {
|
|
2120
|
+
return requests.map((req) => {
|
|
2121
|
+
const body = {
|
|
2122
|
+
model,
|
|
2123
|
+
messages: req.messages
|
|
2124
|
+
};
|
|
2125
|
+
if (req.max_tokens !== void 0) body.max_tokens = req.max_tokens;
|
|
2126
|
+
if (req.temperature !== void 0) body.temperature = req.temperature;
|
|
2127
|
+
if (req.top_p !== void 0) body.top_p = req.top_p;
|
|
2128
|
+
if (req.stop !== void 0) body.stop = req.stop;
|
|
2129
|
+
if (req.response_format !== void 0) body.response_format = req.response_format;
|
|
2130
|
+
if (req.tools !== void 0) body.tools = req.tools;
|
|
2131
|
+
if (req.tool_choice !== void 0) body.tool_choice = req.tool_choice;
|
|
2132
|
+
return JSON.stringify({
|
|
2133
|
+
custom_id: req.custom_id,
|
|
2134
|
+
method: "POST",
|
|
2135
|
+
url: "/v1/chat/completions",
|
|
2136
|
+
body
|
|
2137
|
+
});
|
|
2138
|
+
}).join("\n");
|
|
2139
|
+
}
|
|
2140
|
+
function rePrefixId(id) {
|
|
2141
|
+
if (id && id.startsWith("chatcmpl-")) {
|
|
2142
|
+
return `gen-${id.substring(9)}`;
|
|
2143
|
+
}
|
|
2144
|
+
return id.startsWith("gen-") ? id : `gen-${id}`;
|
|
2145
|
+
}
|
|
2146
|
+
function translateOpenAIResponse(body) {
|
|
2147
|
+
return {
|
|
2148
|
+
id: rePrefixId(body.id || generateId()),
|
|
2149
|
+
object: "chat.completion",
|
|
2150
|
+
created: body.created || Math.floor(Date.now() / 1e3),
|
|
2151
|
+
model: `openai/${body.model}`,
|
|
2152
|
+
choices: body.choices,
|
|
2153
|
+
usage: body.usage
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
function mapStatus(openaiStatus) {
|
|
2157
|
+
switch (openaiStatus) {
|
|
2158
|
+
case "validating":
|
|
2159
|
+
case "finalizing":
|
|
2160
|
+
return "processing";
|
|
2161
|
+
case "in_progress":
|
|
2162
|
+
return "processing";
|
|
2163
|
+
case "completed":
|
|
2164
|
+
return "completed";
|
|
2165
|
+
case "failed":
|
|
2166
|
+
return "failed";
|
|
2167
|
+
case "expired":
|
|
2168
|
+
return "failed";
|
|
2169
|
+
case "cancelled":
|
|
2170
|
+
case "cancelling":
|
|
2171
|
+
return "cancelled";
|
|
2172
|
+
default:
|
|
2173
|
+
return "pending";
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
return {
|
|
2177
|
+
async createBatch(model, requests, options) {
|
|
2178
|
+
const jsonlContent = buildJSONL(model, requests);
|
|
2179
|
+
const blob = new Blob([jsonlContent], { type: "application/jsonl" });
|
|
2180
|
+
const formData = new FormData();
|
|
2181
|
+
formData.append("purpose", "batch");
|
|
2182
|
+
formData.append("file", blob, "batch_input.jsonl");
|
|
2183
|
+
const uploadRes = await apiRequest("/files", { method: "POST", formData });
|
|
2184
|
+
const fileData = await uploadRes.json();
|
|
2185
|
+
const inputFileId = fileData.id;
|
|
2186
|
+
const batchRes = await apiRequest("/batches", {
|
|
2187
|
+
method: "POST",
|
|
2188
|
+
body: {
|
|
2189
|
+
input_file_id: inputFileId,
|
|
2190
|
+
endpoint: "/v1/chat/completions",
|
|
2191
|
+
completion_window: "24h",
|
|
2192
|
+
metadata: options?.metadata
|
|
2193
|
+
}
|
|
2194
|
+
});
|
|
2195
|
+
const batchData = await batchRes.json();
|
|
2196
|
+
return {
|
|
2197
|
+
providerBatchId: batchData.id,
|
|
2198
|
+
metadata: {
|
|
2199
|
+
input_file_id: inputFileId,
|
|
2200
|
+
openai_status: batchData.status
|
|
2201
|
+
}
|
|
2202
|
+
};
|
|
2203
|
+
},
|
|
2204
|
+
async pollBatch(providerBatchId) {
|
|
2205
|
+
const res = await apiRequest(`/batches/${providerBatchId}`);
|
|
2206
|
+
const data = await res.json();
|
|
2207
|
+
const requestCounts = data.request_counts || {};
|
|
2208
|
+
return {
|
|
2209
|
+
status: mapStatus(data.status),
|
|
2210
|
+
total: requestCounts.total || 0,
|
|
2211
|
+
completed: requestCounts.completed || 0,
|
|
2212
|
+
failed: requestCounts.failed || 0
|
|
2213
|
+
};
|
|
2214
|
+
},
|
|
2215
|
+
async getBatchResults(providerBatchId) {
|
|
2216
|
+
const batchRes = await apiRequest(`/batches/${providerBatchId}`);
|
|
2217
|
+
const batchData = await batchRes.json();
|
|
2218
|
+
const results = [];
|
|
2219
|
+
if (batchData.output_file_id) {
|
|
2220
|
+
const outputRes = await apiRequest(`/files/${batchData.output_file_id}/content`);
|
|
2221
|
+
const outputText = await outputRes.text();
|
|
2222
|
+
for (const line of outputText.trim().split("\n")) {
|
|
2223
|
+
if (!line) continue;
|
|
2224
|
+
const item = JSON.parse(line);
|
|
2225
|
+
if (item.response?.status_code === 200) {
|
|
2226
|
+
results.push({
|
|
2227
|
+
custom_id: item.custom_id,
|
|
2228
|
+
status: "success",
|
|
2229
|
+
response: translateOpenAIResponse(item.response.body),
|
|
2230
|
+
error: null
|
|
2231
|
+
});
|
|
2232
|
+
} else {
|
|
2233
|
+
results.push({
|
|
2234
|
+
custom_id: item.custom_id,
|
|
2235
|
+
status: "error",
|
|
2236
|
+
response: null,
|
|
2237
|
+
error: {
|
|
2238
|
+
code: item.response?.status_code || 500,
|
|
2239
|
+
message: item.error?.message || item.response?.body?.error?.message || "Unknown error"
|
|
2240
|
+
}
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
if (batchData.error_file_id) {
|
|
2246
|
+
const errorRes = await apiRequest(`/files/${batchData.error_file_id}/content`);
|
|
2247
|
+
const errorText = await errorRes.text();
|
|
2248
|
+
for (const line of errorText.trim().split("\n")) {
|
|
2249
|
+
if (!line) continue;
|
|
2250
|
+
const item = JSON.parse(line);
|
|
2251
|
+
const existing = results.find((r) => r.custom_id === item.custom_id);
|
|
2252
|
+
if (!existing) {
|
|
2253
|
+
results.push({
|
|
2254
|
+
custom_id: item.custom_id,
|
|
2255
|
+
status: "error",
|
|
2256
|
+
response: null,
|
|
2257
|
+
error: {
|
|
2258
|
+
code: item.response?.status_code || 500,
|
|
2259
|
+
message: item.error?.message || "Batch item error"
|
|
2260
|
+
}
|
|
2261
|
+
});
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
return results;
|
|
2266
|
+
},
|
|
2267
|
+
async cancelBatch(providerBatchId) {
|
|
2268
|
+
await apiRequest(`/batches/${providerBatchId}/cancel`, { method: "POST" });
|
|
2269
|
+
}
|
|
2270
|
+
};
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
// src/providers/anthropic-batch.ts
|
|
2274
|
+
var ANTHROPIC_API_BASE2 = "https://api.anthropic.com/v1";
|
|
2275
|
+
var ANTHROPIC_VERSION2 = "2023-06-01";
|
|
2276
|
+
var DEFAULT_MAX_TOKENS2 = 4096;
|
|
2277
|
+
function createAnthropicBatchAdapter(apiKey) {
|
|
2278
|
+
async function apiRequest(path2, options = {}) {
|
|
2279
|
+
const headers = {
|
|
2280
|
+
"x-api-key": apiKey,
|
|
2281
|
+
"anthropic-version": ANTHROPIC_VERSION2,
|
|
2282
|
+
"Content-Type": "application/json"
|
|
2283
|
+
};
|
|
2284
|
+
const res = await fetch(`${ANTHROPIC_API_BASE2}${path2}`, {
|
|
2285
|
+
method: options.method || "GET",
|
|
2286
|
+
headers,
|
|
2287
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
2288
|
+
});
|
|
2289
|
+
if (!res.ok) {
|
|
2290
|
+
let errorBody;
|
|
2291
|
+
try {
|
|
2292
|
+
errorBody = await res.json();
|
|
2293
|
+
} catch {
|
|
2294
|
+
errorBody = { message: res.statusText };
|
|
2295
|
+
}
|
|
2296
|
+
const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
|
|
2297
|
+
throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
|
|
2298
|
+
provider_name: "anthropic",
|
|
2299
|
+
raw: errorBody
|
|
2300
|
+
});
|
|
2301
|
+
}
|
|
2302
|
+
return res;
|
|
2303
|
+
}
|
|
2304
|
+
function translateToAnthropicParams(model, req) {
|
|
2305
|
+
const params = {
|
|
2306
|
+
model,
|
|
2307
|
+
max_tokens: req.max_tokens || DEFAULT_MAX_TOKENS2
|
|
2308
|
+
};
|
|
2309
|
+
const systemMessages = req.messages.filter((m) => m.role === "system");
|
|
2310
|
+
const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
|
|
2311
|
+
if (systemMessages.length > 0) {
|
|
2312
|
+
params.system = systemMessages.map((m) => typeof m.content === "string" ? m.content : "").join("\n");
|
|
2313
|
+
}
|
|
2314
|
+
params.messages = nonSystemMessages.map((m) => ({
|
|
2315
|
+
role: m.role === "tool" ? "user" : m.role,
|
|
2316
|
+
content: m.tool_call_id ? [{ type: "tool_result", tool_use_id: m.tool_call_id, content: typeof m.content === "string" ? m.content : "" }] : m.content
|
|
2317
|
+
}));
|
|
2318
|
+
if (req.temperature !== void 0) params.temperature = req.temperature;
|
|
2319
|
+
if (req.top_p !== void 0) params.top_p = req.top_p;
|
|
2320
|
+
if (req.top_k !== void 0) params.top_k = req.top_k;
|
|
2321
|
+
if (req.stop !== void 0) params.stop_sequences = Array.isArray(req.stop) ? req.stop : [req.stop];
|
|
2322
|
+
if (req.tools && req.tools.length > 0) {
|
|
2323
|
+
params.tools = req.tools.map((t) => ({
|
|
2324
|
+
name: t.function.name,
|
|
2325
|
+
description: t.function.description || "",
|
|
2326
|
+
input_schema: t.function.parameters || { type: "object", properties: {} }
|
|
2327
|
+
}));
|
|
2328
|
+
if (req.tool_choice) {
|
|
2329
|
+
if (req.tool_choice === "auto") {
|
|
2330
|
+
params.tool_choice = { type: "auto" };
|
|
2331
|
+
} else if (req.tool_choice === "required") {
|
|
2332
|
+
params.tool_choice = { type: "any" };
|
|
2333
|
+
} else if (req.tool_choice === "none") {
|
|
2334
|
+
delete params.tools;
|
|
2335
|
+
} else if (typeof req.tool_choice === "object") {
|
|
2336
|
+
params.tool_choice = { type: "tool", name: req.tool_choice.function.name };
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
if (req.response_format) {
|
|
2341
|
+
if (req.response_format.type === "json_object" || req.response_format.type === "json_schema") {
|
|
2342
|
+
const jsonInstruction = "Respond with valid JSON only. Do not include any text outside the JSON object.";
|
|
2343
|
+
params.system = params.system ? `${jsonInstruction}
|
|
2344
|
+
|
|
2345
|
+
${params.system}` : jsonInstruction;
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
return params;
|
|
2349
|
+
}
|
|
2350
|
+
function mapStopReason(reason) {
|
|
2351
|
+
switch (reason) {
|
|
2352
|
+
case "end_turn":
|
|
2353
|
+
return "stop";
|
|
2354
|
+
case "max_tokens":
|
|
2355
|
+
return "length";
|
|
2356
|
+
case "tool_use":
|
|
2357
|
+
return "tool_calls";
|
|
2358
|
+
case "stop_sequence":
|
|
2359
|
+
return "stop";
|
|
2360
|
+
default:
|
|
2361
|
+
return "stop";
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
function translateAnthropicMessage(msg) {
|
|
2365
|
+
let content = "";
|
|
2366
|
+
const toolCalls = [];
|
|
2367
|
+
for (const block of msg.content || []) {
|
|
2368
|
+
if (block.type === "text") {
|
|
2369
|
+
content += block.text;
|
|
2370
|
+
} else if (block.type === "tool_use") {
|
|
2371
|
+
toolCalls.push({
|
|
2372
|
+
id: block.id,
|
|
2373
|
+
type: "function",
|
|
2374
|
+
function: {
|
|
2375
|
+
name: block.name,
|
|
2376
|
+
arguments: JSON.stringify(block.input)
|
|
2377
|
+
}
|
|
2378
|
+
});
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
const message = { role: "assistant", content };
|
|
2382
|
+
if (toolCalls.length > 0) {
|
|
2383
|
+
message.tool_calls = toolCalls;
|
|
2384
|
+
}
|
|
2385
|
+
return {
|
|
2386
|
+
id: generateId(),
|
|
2387
|
+
object: "chat.completion",
|
|
2388
|
+
created: Math.floor(Date.now() / 1e3),
|
|
2389
|
+
model: `anthropic/${msg.model}`,
|
|
2390
|
+
choices: [{
|
|
2391
|
+
index: 0,
|
|
2392
|
+
message,
|
|
2393
|
+
finish_reason: mapStopReason(msg.stop_reason)
|
|
2394
|
+
}],
|
|
2395
|
+
usage: {
|
|
2396
|
+
prompt_tokens: msg.usage?.input_tokens || 0,
|
|
2397
|
+
completion_tokens: msg.usage?.output_tokens || 0,
|
|
2398
|
+
total_tokens: (msg.usage?.input_tokens || 0) + (msg.usage?.output_tokens || 0)
|
|
2399
|
+
}
|
|
2400
|
+
};
|
|
2401
|
+
}
|
|
2402
|
+
return {
|
|
2403
|
+
async createBatch(model, requests, _options) {
|
|
2404
|
+
const batchRequests = requests.map((req) => ({
|
|
2405
|
+
custom_id: req.custom_id,
|
|
2406
|
+
params: translateToAnthropicParams(model, req)
|
|
2407
|
+
}));
|
|
2408
|
+
const res = await apiRequest("/messages/batches", {
|
|
2409
|
+
method: "POST",
|
|
2410
|
+
body: { requests: batchRequests }
|
|
2411
|
+
});
|
|
2412
|
+
const data = await res.json();
|
|
2413
|
+
return {
|
|
2414
|
+
providerBatchId: data.id,
|
|
2415
|
+
metadata: {
|
|
2416
|
+
anthropic_type: data.type,
|
|
2417
|
+
created_at: data.created_at
|
|
2418
|
+
}
|
|
2419
|
+
};
|
|
2420
|
+
},
|
|
2421
|
+
async pollBatch(providerBatchId) {
|
|
2422
|
+
const res = await apiRequest(`/messages/batches/${providerBatchId}`);
|
|
2423
|
+
const data = await res.json();
|
|
2424
|
+
const counts = data.request_counts || {};
|
|
2425
|
+
const total = (counts.processing || 0) + (counts.succeeded || 0) + (counts.errored || 0) + (counts.canceled || 0) + (counts.expired || 0);
|
|
2426
|
+
let status;
|
|
2427
|
+
if (data.processing_status === "ended") {
|
|
2428
|
+
if (counts.succeeded === 0 && (counts.errored > 0 || counts.expired > 0 || counts.canceled > 0)) {
|
|
2429
|
+
status = "failed";
|
|
2430
|
+
} else if (data.cancel_initiated_at) {
|
|
2431
|
+
status = "cancelled";
|
|
2432
|
+
} else {
|
|
2433
|
+
status = "completed";
|
|
2434
|
+
}
|
|
2435
|
+
} else {
|
|
2436
|
+
status = "processing";
|
|
2437
|
+
}
|
|
2438
|
+
return {
|
|
2439
|
+
status,
|
|
2440
|
+
total,
|
|
2441
|
+
completed: counts.succeeded || 0,
|
|
2442
|
+
failed: (counts.errored || 0) + (counts.expired || 0) + (counts.canceled || 0)
|
|
2443
|
+
};
|
|
2444
|
+
},
|
|
2445
|
+
async getBatchResults(providerBatchId) {
|
|
2446
|
+
const res = await apiRequest(`/messages/batches/${providerBatchId}/results`);
|
|
2447
|
+
const text = await res.text();
|
|
2448
|
+
const results = [];
|
|
2449
|
+
for (const line of text.trim().split("\n")) {
|
|
2450
|
+
if (!line) continue;
|
|
2451
|
+
const item = JSON.parse(line);
|
|
2452
|
+
if (item.result?.type === "succeeded") {
|
|
2453
|
+
results.push({
|
|
2454
|
+
custom_id: item.custom_id,
|
|
2455
|
+
status: "success",
|
|
2456
|
+
response: translateAnthropicMessage(item.result.message),
|
|
2457
|
+
error: null
|
|
2458
|
+
});
|
|
2459
|
+
} else {
|
|
2460
|
+
const errorType = item.result?.type || "unknown";
|
|
2461
|
+
const errorMsg = item.result?.error?.message || `Batch item ${errorType}`;
|
|
2462
|
+
results.push({
|
|
2463
|
+
custom_id: item.custom_id,
|
|
2464
|
+
status: "error",
|
|
2465
|
+
response: null,
|
|
2466
|
+
error: {
|
|
2467
|
+
code: errorType === "expired" ? 408 : 500,
|
|
2468
|
+
message: errorMsg
|
|
2469
|
+
}
|
|
2470
|
+
});
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
return results;
|
|
2474
|
+
},
|
|
2475
|
+
async cancelBatch(providerBatchId) {
|
|
2476
|
+
await apiRequest(`/messages/batches/${providerBatchId}/cancel`, { method: "POST" });
|
|
2477
|
+
}
|
|
2478
|
+
};
|
|
2479
|
+
}
|
|
2480
|
+
|
|
1778
2481
|
// src/client.ts
|
|
1779
2482
|
var AnyModel = class {
|
|
1780
2483
|
registry;
|
|
@@ -1790,6 +2493,9 @@ var AnyModel = class {
|
|
|
1790
2493
|
constructor(config = {}) {
|
|
1791
2494
|
this.config = resolveConfig(config);
|
|
1792
2495
|
this.registry = new ProviderRegistry();
|
|
2496
|
+
if (this.config.io) {
|
|
2497
|
+
configureFsIO(this.config.io);
|
|
2498
|
+
}
|
|
1793
2499
|
this.registerProviders();
|
|
1794
2500
|
this.router = new Router(this.registry, this.config.aliases, this.config);
|
|
1795
2501
|
this.chat = {
|
|
@@ -1839,8 +2545,10 @@ var AnyModel = class {
|
|
|
1839
2545
|
};
|
|
1840
2546
|
this.batchManager = new BatchManager(this.router, {
|
|
1841
2547
|
dir: this.config.batch?.dir,
|
|
1842
|
-
concurrency: this.config.batch?.concurrencyFallback
|
|
2548
|
+
concurrency: this.config.batch?.concurrencyFallback,
|
|
2549
|
+
pollInterval: this.config.batch?.pollInterval
|
|
1843
2550
|
});
|
|
2551
|
+
this.registerBatchAdapters();
|
|
1844
2552
|
this.batches = {
|
|
1845
2553
|
create: (request) => this.batchManager.create(request),
|
|
1846
2554
|
createAndPoll: (request, options) => this.batchManager.createAndPoll(request, options),
|
|
@@ -1892,6 +2600,17 @@ var AnyModel = class {
|
|
|
1892
2600
|
}
|
|
1893
2601
|
}
|
|
1894
2602
|
}
|
|
2603
|
+
registerBatchAdapters() {
|
|
2604
|
+
const config = this.config;
|
|
2605
|
+
const openaiKey = config.openai?.apiKey || process.env.OPENAI_API_KEY;
|
|
2606
|
+
if (openaiKey) {
|
|
2607
|
+
this.batchManager.registerBatchAdapter("openai", createOpenAIBatchAdapter(openaiKey));
|
|
2608
|
+
}
|
|
2609
|
+
const anthropicKey = config.anthropic?.apiKey || process.env.ANTHROPIC_API_KEY;
|
|
2610
|
+
if (anthropicKey) {
|
|
2611
|
+
this.batchManager.registerBatchAdapter("anthropic", createAnthropicBatchAdapter(anthropicKey));
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
1895
2614
|
applyDefaults(request) {
|
|
1896
2615
|
const defaults = this.config.defaults;
|
|
1897
2616
|
if (!defaults) return request;
|
|
@@ -1919,10 +2638,10 @@ var AnyModel = class {
|
|
|
1919
2638
|
|
|
1920
2639
|
// src/server.ts
|
|
1921
2640
|
function parseBody(req) {
|
|
1922
|
-
return new Promise((
|
|
2641
|
+
return new Promise((resolve2, reject) => {
|
|
1923
2642
|
const chunks = [];
|
|
1924
2643
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
1925
|
-
req.on("end", () =>
|
|
2644
|
+
req.on("end", () => resolve2(Buffer.concat(chunks).toString()));
|
|
1926
2645
|
req.on("error", reject);
|
|
1927
2646
|
});
|
|
1928
2647
|
}
|
|
@@ -1952,7 +2671,7 @@ function createAnyModelServer(options = {}) {
|
|
|
1952
2671
|
const basePath = "/api/v1";
|
|
1953
2672
|
const server = (0, import_node_http.createServer)(async (req, res) => {
|
|
1954
2673
|
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
1955
|
-
const
|
|
2674
|
+
const path2 = url.pathname;
|
|
1956
2675
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1957
2676
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
1958
2677
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
@@ -1962,11 +2681,11 @@ function createAnyModelServer(options = {}) {
|
|
|
1962
2681
|
return;
|
|
1963
2682
|
}
|
|
1964
2683
|
try {
|
|
1965
|
-
if (
|
|
2684
|
+
if (path2 === "/health" && req.method === "GET") {
|
|
1966
2685
|
sendJSON(res, 200, { status: "ok" });
|
|
1967
2686
|
return;
|
|
1968
2687
|
}
|
|
1969
|
-
if (
|
|
2688
|
+
if (path2 === `${basePath}/chat/completions` && req.method === "POST") {
|
|
1970
2689
|
const body = JSON.parse(await parseBody(req));
|
|
1971
2690
|
if (body.stream) {
|
|
1972
2691
|
const stream = await client.chat.completions.create(body);
|
|
@@ -1977,14 +2696,14 @@ function createAnyModelServer(options = {}) {
|
|
|
1977
2696
|
}
|
|
1978
2697
|
return;
|
|
1979
2698
|
}
|
|
1980
|
-
if (
|
|
2699
|
+
if (path2 === `${basePath}/models` && req.method === "GET") {
|
|
1981
2700
|
const provider = url.searchParams.get("provider") || void 0;
|
|
1982
2701
|
const models = await client.models.list({ provider });
|
|
1983
2702
|
sendJSON(res, 200, { object: "list", data: models });
|
|
1984
2703
|
return;
|
|
1985
2704
|
}
|
|
1986
|
-
if (
|
|
1987
|
-
const id =
|
|
2705
|
+
if (path2.startsWith(`${basePath}/generation/`) && req.method === "GET") {
|
|
2706
|
+
const id = path2.substring(`${basePath}/generation/`.length);
|
|
1988
2707
|
const stats = client.generation.get(id);
|
|
1989
2708
|
if (!stats) {
|
|
1990
2709
|
sendError(res, 404, `Generation ${id} not found`);
|
|
@@ -1993,26 +2712,26 @@ function createAnyModelServer(options = {}) {
|
|
|
1993
2712
|
sendJSON(res, 200, stats);
|
|
1994
2713
|
return;
|
|
1995
2714
|
}
|
|
1996
|
-
if (
|
|
2715
|
+
if (path2 === `${basePath}/batches` && req.method === "POST") {
|
|
1997
2716
|
const body = JSON.parse(await parseBody(req));
|
|
1998
2717
|
const batch = await client.batches.create(body);
|
|
1999
2718
|
sendJSON(res, 201, batch);
|
|
2000
2719
|
return;
|
|
2001
2720
|
}
|
|
2002
|
-
if (
|
|
2003
|
-
const batches = client.batches.list();
|
|
2721
|
+
if (path2 === `${basePath}/batches` && req.method === "GET") {
|
|
2722
|
+
const batches = await client.batches.list();
|
|
2004
2723
|
sendJSON(res, 200, { object: "list", data: batches });
|
|
2005
2724
|
return;
|
|
2006
2725
|
}
|
|
2007
|
-
if (
|
|
2008
|
-
const parts =
|
|
2726
|
+
if (path2.startsWith(`${basePath}/batches/`) && req.method === "GET") {
|
|
2727
|
+
const parts = path2.substring(`${basePath}/batches/`.length).split("/");
|
|
2009
2728
|
const id = parts[0];
|
|
2010
2729
|
if (parts[1] === "results") {
|
|
2011
|
-
const results = client.batches.results(id);
|
|
2730
|
+
const results = await client.batches.results(id);
|
|
2012
2731
|
sendJSON(res, 200, results);
|
|
2013
2732
|
return;
|
|
2014
2733
|
}
|
|
2015
|
-
const batch = client.batches.get(id);
|
|
2734
|
+
const batch = await client.batches.get(id);
|
|
2016
2735
|
if (!batch) {
|
|
2017
2736
|
sendError(res, 404, `Batch ${id} not found`);
|
|
2018
2737
|
return;
|
|
@@ -2020,16 +2739,16 @@ function createAnyModelServer(options = {}) {
|
|
|
2020
2739
|
sendJSON(res, 200, batch);
|
|
2021
2740
|
return;
|
|
2022
2741
|
}
|
|
2023
|
-
if (
|
|
2024
|
-
const parts =
|
|
2742
|
+
if (path2.startsWith(`${basePath}/batches/`) && req.method === "POST") {
|
|
2743
|
+
const parts = path2.substring(`${basePath}/batches/`.length).split("/");
|
|
2025
2744
|
const id = parts[0];
|
|
2026
2745
|
if (parts[1] === "cancel") {
|
|
2027
|
-
const batch = client.batches.cancel(id);
|
|
2746
|
+
const batch = await client.batches.cancel(id);
|
|
2028
2747
|
sendJSON(res, 200, batch);
|
|
2029
2748
|
return;
|
|
2030
2749
|
}
|
|
2031
2750
|
}
|
|
2032
|
-
sendError(res, 404, `Not found: ${
|
|
2751
|
+
sendError(res, 404, `Not found: ${path2}`);
|
|
2033
2752
|
} catch (err) {
|
|
2034
2753
|
const code = err?.code || 500;
|
|
2035
2754
|
const message = err?.message || "Internal server error";
|