@probeo/anymodel 0.2.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/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -25,9 +35,20 @@ __export(src_exports, {
25
35
  BatchManager: () => BatchManager,
26
36
  BatchStore: () => BatchStore,
27
37
  GenerationStatsStore: () => GenerationStatsStore,
38
+ appendFileQueued: () => appendFileQueued,
39
+ configureFsIO: () => configureFsIO,
40
+ createAnthropicBatchAdapter: () => createAnthropicBatchAdapter,
28
41
  createAnyModelServer: () => createAnyModelServer,
42
+ createOpenAIBatchAdapter: () => createOpenAIBatchAdapter,
43
+ ensureDir: () => ensureDir,
44
+ getFsQueueStatus: () => getFsQueueStatus,
45
+ joinPath: () => joinPath,
46
+ readFileQueued: () => readFileQueued,
29
47
  resolveConfig: () => resolveConfig,
30
- startServer: () => startServer
48
+ startServer: () => startServer,
49
+ waitForFsQueuesIdle: () => waitForFsQueuesIdle,
50
+ writeFileFlushedQueued: () => writeFileFlushedQueued,
51
+ writeFileQueued: () => writeFileQueued
31
52
  });
32
53
  module.exports = __toCommonJS(src_exports);
33
54
 
@@ -175,7 +196,7 @@ async function withRetry(fn, options = {}) {
175
196
  throw error;
176
197
  }
177
198
  const delay = computeDelay(attempt, opts, error);
178
- await new Promise((resolve3) => setTimeout(resolve3, delay));
199
+ await new Promise((resolve2) => setTimeout(resolve2, delay));
179
200
  }
180
201
  }
181
202
  throw lastError;
@@ -534,8 +555,8 @@ var SUPPORTED_PARAMS = /* @__PURE__ */ new Set([
534
555
  ]);
535
556
  function createOpenAIAdapter(apiKey, baseURL) {
536
557
  const base = baseURL || OPENAI_API_BASE;
537
- async function makeRequest(path, body, method = "POST") {
538
- const res = await fetch(`${base}${path}`, {
558
+ async function makeRequest(path2, body, method = "POST") {
559
+ const res = await fetch(`${base}${path2}`, {
539
560
  method,
540
561
  headers: {
541
562
  "Content-Type": "application/json",
@@ -686,6 +707,9 @@ function createOpenAIAdapter(apiKey, baseURL) {
686
707
  supportsParameter(param) {
687
708
  return SUPPORTED_PARAMS.has(param);
688
709
  },
710
+ supportsBatch() {
711
+ return true;
712
+ },
689
713
  async sendRequest(request) {
690
714
  const body = buildRequestBody(request);
691
715
  const res = await makeRequest("/chat/completions", body);
@@ -740,8 +764,8 @@ var FALLBACK_MODELS = [
740
764
  { 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) }
741
765
  ];
742
766
  function createAnthropicAdapter(apiKey) {
743
- async function makeRequest(path, body, stream = false) {
744
- const res = await fetch(`${ANTHROPIC_API_BASE}${path}`, {
767
+ async function makeRequest(path2, body, stream = false) {
768
+ const res = await fetch(`${ANTHROPIC_API_BASE}${path2}`, {
745
769
  method: "POST",
746
770
  headers: {
747
771
  "Content-Type": "application/json",
@@ -1035,6 +1059,9 @@ ${body.system}` : jsonInstruction;
1035
1059
  supportsParameter(param) {
1036
1060
  return SUPPORTED_PARAMS2.has(param);
1037
1061
  },
1062
+ supportsBatch() {
1063
+ return true;
1064
+ },
1038
1065
  async sendRequest(request) {
1039
1066
  const body = translateRequest(request);
1040
1067
  const res = await makeRequest("/messages", body);
@@ -1314,6 +1341,9 @@ function createGoogleAdapter(apiKey) {
1314
1341
  supportsParameter(param) {
1315
1342
  return SUPPORTED_PARAMS3.has(param);
1316
1343
  },
1344
+ supportsBatch() {
1345
+ return false;
1346
+ },
1317
1347
  async sendRequest(request) {
1318
1348
  const body = translateRequest(request);
1319
1349
  const url = getModelEndpoint(request.model, false);
@@ -1372,6 +1402,9 @@ function createCustomAdapter(name, config) {
1372
1402
  return {
1373
1403
  ...openaiAdapter,
1374
1404
  name,
1405
+ supportsBatch() {
1406
+ return false;
1407
+ },
1375
1408
  async listModels() {
1376
1409
  if (config.models && config.models.length > 0) {
1377
1410
  return config.models.map((modelId) => ({
@@ -1437,10 +1470,10 @@ function interpolateDeep(obj) {
1437
1470
  }
1438
1471
  return obj;
1439
1472
  }
1440
- function loadJsonFile(path) {
1441
- if (!(0, import_node_fs.existsSync)(path)) return null;
1473
+ function loadJsonFile(path2) {
1474
+ if (!(0, import_node_fs.existsSync)(path2)) return null;
1442
1475
  try {
1443
- const raw = (0, import_node_fs.readFileSync)(path, "utf-8");
1476
+ const raw = (0, import_node_fs.readFileSync)(path2, "utf-8");
1444
1477
  const parsed = JSON.parse(raw);
1445
1478
  return interpolateDeep(parsed);
1446
1479
  } catch {
@@ -1541,93 +1574,237 @@ var GenerationStatsStore = class {
1541
1574
  }
1542
1575
  };
1543
1576
 
1544
- // src/batch/store.ts
1577
+ // src/utils/fs-io.ts
1578
+ var import_promises = require("fs/promises");
1545
1579
  var import_node_fs2 = require("fs");
1546
- var import_node_path2 = require("path");
1547
- var import_node_os2 = require("os");
1548
- var DEFAULT_BATCH_DIR = (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".anymodel", "batches");
1580
+ var import_node_path2 = __toESM(require("path"), 1);
1581
+ var import_p_queue = __toESM(require("p-queue"), 1);
1582
+ var writeQueue = new import_p_queue.default({ concurrency: 10 });
1583
+ var readQueue = new import_p_queue.default({ concurrency: 20 });
1584
+ function configureFsIO(options) {
1585
+ if (options.readConcurrency !== void 0) {
1586
+ readQueue.concurrency = options.readConcurrency;
1587
+ }
1588
+ if (options.writeConcurrency !== void 0) {
1589
+ writeQueue.concurrency = options.writeConcurrency;
1590
+ }
1591
+ }
1592
+ var ensuredDirs = /* @__PURE__ */ new Set();
1593
+ var joinPathCache = /* @__PURE__ */ new Map();
1594
+ var dirnameCache = /* @__PURE__ */ new Map();
1595
+ var resolvePathCache = /* @__PURE__ */ new Map();
1596
+ async function ensureDir(dir) {
1597
+ if (!dir) return;
1598
+ if (ensuredDirs.has(dir)) return;
1599
+ await (0, import_promises.mkdir)(dir, { recursive: true });
1600
+ ensuredDirs.add(dir);
1601
+ }
1602
+ async function readFileQueued(filePath, encoding = "utf8") {
1603
+ return readQueue.add(async () => {
1604
+ return (0, import_promises.readFile)(filePath, encoding);
1605
+ });
1606
+ }
1607
+ async function readJsonQueued(filePath) {
1608
+ const raw = await readFileQueued(filePath, "utf8");
1609
+ return JSON.parse(raw);
1610
+ }
1611
+ async function readDirQueued(dirPath) {
1612
+ return readQueue.add(async () => {
1613
+ return (0, import_promises.readdir)(dirPath, { withFileTypes: true });
1614
+ });
1615
+ }
1616
+ async function pathExistsQueued(p) {
1617
+ return readQueue.add(async () => {
1618
+ try {
1619
+ await (0, import_promises.stat)(p);
1620
+ return true;
1621
+ } catch {
1622
+ return false;
1623
+ }
1624
+ });
1625
+ }
1626
+ async function fileExistsQueued(filePath) {
1627
+ return readQueue.add(async () => {
1628
+ try {
1629
+ const s = await (0, import_promises.stat)(filePath);
1630
+ return s.isFile();
1631
+ } catch {
1632
+ return false;
1633
+ }
1634
+ });
1635
+ }
1636
+ async function writeFileQueued(filePath, data) {
1637
+ await writeQueue.add(async () => {
1638
+ const dir = dirnameOf(filePath);
1639
+ await ensureDir(dir);
1640
+ await (0, import_promises.writeFile)(filePath, data);
1641
+ });
1642
+ }
1643
+ async function appendFileQueued(filePath, data) {
1644
+ await writeQueue.add(async () => {
1645
+ const dir = dirnameOf(filePath);
1646
+ await ensureDir(dir);
1647
+ await (0, import_promises.writeFile)(filePath, data, { flag: "a" });
1648
+ });
1649
+ }
1650
+ async function writeFileFlushedQueued(filePath, data) {
1651
+ await writeQueue.add(async () => {
1652
+ const dir = dirnameOf(filePath);
1653
+ await ensureDir(dir);
1654
+ const tmpPath = joinPath(
1655
+ dir,
1656
+ `.${import_node_path2.default.basename(filePath)}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`
1657
+ );
1658
+ const fh = await (0, import_promises.open)(tmpPath, "w");
1659
+ try {
1660
+ await fh.writeFile(data);
1661
+ await fh.sync();
1662
+ } finally {
1663
+ await fh.close();
1664
+ }
1665
+ await (0, import_promises.rename)(tmpPath, filePath);
1666
+ try {
1667
+ const dh = await (0, import_promises.open)(dir, "r");
1668
+ try {
1669
+ await dh.sync();
1670
+ } finally {
1671
+ await dh.close();
1672
+ }
1673
+ } catch {
1674
+ }
1675
+ });
1676
+ }
1677
+ function joinPath(...segments) {
1678
+ const key = segments.join("\0");
1679
+ const cached = joinPathCache.get(key);
1680
+ if (cached !== void 0) return cached;
1681
+ const out = import_node_path2.default.join(...segments);
1682
+ joinPathCache.set(key, out);
1683
+ return out;
1684
+ }
1685
+ function dirnameOf(p) {
1686
+ const cached = dirnameCache.get(p);
1687
+ if (cached !== void 0) return cached;
1688
+ const out = import_node_path2.default.dirname(p);
1689
+ dirnameCache.set(p, out);
1690
+ return out;
1691
+ }
1692
+ function resolvePath(...segments) {
1693
+ const key = segments.join("\0");
1694
+ const cached = resolvePathCache.get(key);
1695
+ if (cached !== void 0) return cached;
1696
+ const out = import_node_path2.default.resolve(...segments);
1697
+ resolvePathCache.set(key, out);
1698
+ return out;
1699
+ }
1700
+ function getFsQueueStatus() {
1701
+ return {
1702
+ read: { size: readQueue.size, pending: readQueue.pending },
1703
+ write: { size: writeQueue.size, pending: writeQueue.pending }
1704
+ };
1705
+ }
1706
+ async function waitForFsQueuesIdle() {
1707
+ await Promise.all([writeQueue.onIdle(), readQueue.onIdle()]);
1708
+ }
1709
+
1710
+ // src/batch/store.ts
1711
+ var DEFAULT_BATCH_DIR = joinPath(process.cwd(), ".anymodel", "batches");
1549
1712
  var BatchStore = class {
1550
1713
  dir;
1714
+ initialized = false;
1551
1715
  constructor(dir) {
1552
- this.dir = (0, import_node_path2.resolve)(dir || DEFAULT_BATCH_DIR);
1553
- (0, import_node_fs2.mkdirSync)(this.dir, { recursive: true });
1716
+ this.dir = resolvePath(dir || DEFAULT_BATCH_DIR);
1717
+ }
1718
+ async init() {
1719
+ if (this.initialized) return;
1720
+ await ensureDir(this.dir);
1721
+ this.initialized = true;
1554
1722
  }
1555
1723
  batchDir(id) {
1556
- return (0, import_node_path2.join)(this.dir, id);
1724
+ return joinPath(this.dir, id);
1557
1725
  }
1558
1726
  /**
1559
1727
  * Create a new batch directory and save initial metadata.
1560
1728
  */
1561
- create(batch) {
1729
+ async create(batch) {
1730
+ await this.init();
1562
1731
  const dir = this.batchDir(batch.id);
1563
- (0, import_node_fs2.mkdirSync)(dir, { recursive: true });
1564
- (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(dir, "meta.json"), JSON.stringify(batch, null, 2));
1732
+ await ensureDir(dir);
1733
+ await writeFileFlushedQueued(joinPath(dir, "meta.json"), JSON.stringify(batch, null, 2));
1565
1734
  }
1566
1735
  /**
1567
- * Update batch metadata.
1736
+ * Update batch metadata (atomic write).
1568
1737
  */
1569
- updateMeta(batch) {
1570
- const dir = this.batchDir(batch.id);
1571
- (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(dir, "meta.json"), JSON.stringify(batch, null, 2));
1738
+ async updateMeta(batch) {
1739
+ await writeFileFlushedQueued(
1740
+ joinPath(this.batchDir(batch.id), "meta.json"),
1741
+ JSON.stringify(batch, null, 2)
1742
+ );
1572
1743
  }
1573
1744
  /**
1574
1745
  * Save requests as JSONL.
1575
1746
  */
1576
- saveRequests(id, requests) {
1577
- const dir = this.batchDir(id);
1747
+ async saveRequests(id, requests) {
1578
1748
  const lines = requests.map((r) => JSON.stringify(r)).join("\n") + "\n";
1579
- (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(dir, "requests.jsonl"), lines);
1749
+ await writeFileQueued(joinPath(this.batchDir(id), "requests.jsonl"), lines);
1580
1750
  }
1581
1751
  /**
1582
1752
  * Append a result to results.jsonl.
1583
1753
  */
1584
- appendResult(id, result) {
1585
- const dir = this.batchDir(id);
1586
- (0, import_node_fs2.appendFileSync)((0, import_node_path2.join)(dir, "results.jsonl"), JSON.stringify(result) + "\n");
1754
+ async appendResult(id, result) {
1755
+ await appendFileQueued(
1756
+ joinPath(this.batchDir(id), "results.jsonl"),
1757
+ JSON.stringify(result) + "\n"
1758
+ );
1587
1759
  }
1588
1760
  /**
1589
1761
  * Save provider-specific state (e.g., provider batch ID).
1590
1762
  */
1591
- saveProviderState(id, state) {
1592
- const dir = this.batchDir(id);
1593
- (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(dir, "provider.json"), JSON.stringify(state, null, 2));
1763
+ async saveProviderState(id, state) {
1764
+ await writeFileFlushedQueued(
1765
+ joinPath(this.batchDir(id), "provider.json"),
1766
+ JSON.stringify(state, null, 2)
1767
+ );
1594
1768
  }
1595
1769
  /**
1596
1770
  * Load provider state.
1597
1771
  */
1598
- loadProviderState(id) {
1599
- const path = (0, import_node_path2.join)(this.batchDir(id), "provider.json");
1600
- if (!(0, import_node_fs2.existsSync)(path)) return null;
1601
- return JSON.parse((0, import_node_fs2.readFileSync)(path, "utf-8"));
1772
+ async loadProviderState(id) {
1773
+ const p = joinPath(this.batchDir(id), "provider.json");
1774
+ if (!await fileExistsQueued(p)) return null;
1775
+ return readJsonQueued(p);
1602
1776
  }
1603
1777
  /**
1604
1778
  * Get batch metadata.
1605
1779
  */
1606
- getMeta(id) {
1607
- const path = (0, import_node_path2.join)(this.batchDir(id), "meta.json");
1608
- if (!(0, import_node_fs2.existsSync)(path)) return null;
1609
- return JSON.parse((0, import_node_fs2.readFileSync)(path, "utf-8"));
1780
+ async getMeta(id) {
1781
+ const p = joinPath(this.batchDir(id), "meta.json");
1782
+ if (!await fileExistsQueued(p)) return null;
1783
+ return readJsonQueued(p);
1610
1784
  }
1611
1785
  /**
1612
1786
  * Get all results for a batch.
1613
1787
  */
1614
- getResults(id) {
1615
- const path = (0, import_node_path2.join)(this.batchDir(id), "results.jsonl");
1616
- if (!(0, import_node_fs2.existsSync)(path)) return [];
1617
- return (0, import_node_fs2.readFileSync)(path, "utf-8").trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
1788
+ async getResults(id) {
1789
+ const p = joinPath(this.batchDir(id), "results.jsonl");
1790
+ if (!await fileExistsQueued(p)) return [];
1791
+ const raw = await readFileQueued(p, "utf8");
1792
+ return raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
1618
1793
  }
1619
1794
  /**
1620
1795
  * List all batch IDs.
1621
1796
  */
1622
- listBatches() {
1623
- if (!(0, import_node_fs2.existsSync)(this.dir)) return [];
1624
- return (0, import_node_fs2.readdirSync)(this.dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
1797
+ async listBatches() {
1798
+ await this.init();
1799
+ if (!await pathExistsQueued(this.dir)) return [];
1800
+ const entries = await readDirQueued(this.dir);
1801
+ return entries.filter((d) => d.isDirectory()).map((d) => d.name).sort();
1625
1802
  }
1626
1803
  /**
1627
1804
  * Check if a batch exists.
1628
1805
  */
1629
- exists(id) {
1630
- return (0, import_node_fs2.existsSync)((0, import_node_path2.join)(this.batchDir(id), "meta.json"));
1806
+ async exists(id) {
1807
+ return fileExistsQueued(joinPath(this.batchDir(id), "meta.json"));
1631
1808
  }
1632
1809
  };
1633
1810
 
@@ -1636,10 +1813,27 @@ var BatchManager = class {
1636
1813
  store;
1637
1814
  router;
1638
1815
  concurrencyLimit;
1816
+ defaultPollInterval;
1817
+ batchAdapters = /* @__PURE__ */ new Map();
1639
1818
  constructor(router, options) {
1640
1819
  this.store = new BatchStore(options?.dir);
1641
1820
  this.router = router;
1642
1821
  this.concurrencyLimit = options?.concurrency ?? 5;
1822
+ this.defaultPollInterval = options?.pollInterval ?? 5e3;
1823
+ }
1824
+ /**
1825
+ * Register a native batch adapter for a provider.
1826
+ */
1827
+ registerBatchAdapter(providerName, adapter) {
1828
+ this.batchAdapters.set(providerName, adapter);
1829
+ }
1830
+ /**
1831
+ * Check if a provider has native batch support.
1832
+ */
1833
+ getNativeBatchAdapter(model) {
1834
+ const providerName = model.split("/")[0];
1835
+ const adapter = this.batchAdapters.get(providerName);
1836
+ return adapter ? { adapter, providerName } : null;
1643
1837
  }
1644
1838
  /**
1645
1839
  * Create a batch and return immediately (no polling).
@@ -1647,13 +1841,16 @@ var BatchManager = class {
1647
1841
  async create(request) {
1648
1842
  const id = generateId("batch");
1649
1843
  const now = (/* @__PURE__ */ new Date()).toISOString();
1844
+ const providerName = request.model.split("/")[0] || "unknown";
1845
+ const native = this.getNativeBatchAdapter(request.model);
1846
+ const batchMode = native ? "native" : "concurrent";
1650
1847
  const batch = {
1651
1848
  id,
1652
1849
  object: "batch",
1653
1850
  status: "pending",
1654
1851
  model: request.model,
1655
- provider_name: request.model.split("/")[0] || "unknown",
1656
- batch_mode: "concurrent",
1852
+ provider_name: providerName,
1853
+ batch_mode: batchMode,
1657
1854
  total: request.requests.length,
1658
1855
  completed: 0,
1659
1856
  failed: 0,
@@ -1661,10 +1858,15 @@ var BatchManager = class {
1661
1858
  completed_at: null,
1662
1859
  expires_at: null
1663
1860
  };
1664
- this.store.create(batch);
1665
- this.store.saveRequests(id, request.requests);
1666
- this.processBatch(id, request).catch(() => {
1667
- });
1861
+ await this.store.create(batch);
1862
+ await this.store.saveRequests(id, request.requests);
1863
+ if (native) {
1864
+ this.processNativeBatch(id, request, native.adapter).catch(() => {
1865
+ });
1866
+ } else {
1867
+ this.processConcurrentBatch(id, request).catch(() => {
1868
+ });
1869
+ }
1668
1870
  return batch;
1669
1871
  }
1670
1872
  /**
@@ -1678,14 +1880,19 @@ var BatchManager = class {
1678
1880
  * Poll an existing batch until completion.
1679
1881
  */
1680
1882
  async poll(id, options = {}) {
1681
- const interval = options.interval ?? 5e3;
1883
+ const interval = options.interval ?? this.defaultPollInterval;
1682
1884
  const timeout = options.timeout ?? 0;
1683
1885
  const startTime = Date.now();
1684
1886
  while (true) {
1685
- const batch = this.store.getMeta(id);
1887
+ let batch = await this.store.getMeta(id);
1686
1888
  if (!batch) {
1687
1889
  throw new AnyModelError(404, `Batch ${id} not found`);
1688
1890
  }
1891
+ if (batch.batch_mode === "native" && batch.status === "processing") {
1892
+ await this.syncNativeBatchStatus(id);
1893
+ batch = await this.store.getMeta(id);
1894
+ if (!batch) throw new AnyModelError(404, `Batch ${id} not found`);
1895
+ }
1689
1896
  if (options.onProgress) {
1690
1897
  options.onProgress(batch);
1691
1898
  }
@@ -1695,24 +1902,24 @@ var BatchManager = class {
1695
1902
  if (timeout > 0 && Date.now() - startTime > timeout) {
1696
1903
  throw new AnyModelError(408, `Batch ${id} timed out after ${timeout}ms`);
1697
1904
  }
1698
- await new Promise((resolve3) => setTimeout(resolve3, interval));
1905
+ await new Promise((resolve2) => setTimeout(resolve2, interval));
1699
1906
  }
1700
1907
  }
1701
1908
  /**
1702
1909
  * Get the current status of a batch.
1703
1910
  */
1704
- get(id) {
1911
+ async get(id) {
1705
1912
  return this.store.getMeta(id);
1706
1913
  }
1707
1914
  /**
1708
1915
  * Get results for a completed batch.
1709
1916
  */
1710
- getResults(id) {
1711
- const batch = this.store.getMeta(id);
1917
+ async getResults(id) {
1918
+ const batch = await this.store.getMeta(id);
1712
1919
  if (!batch) {
1713
1920
  throw new AnyModelError(404, `Batch ${id} not found`);
1714
1921
  }
1715
- const results = this.store.getResults(id);
1922
+ const results = await this.store.getResults(id);
1716
1923
  const usage = {
1717
1924
  total_prompt_tokens: 0,
1718
1925
  total_completion_tokens: 0,
@@ -1734,37 +1941,119 @@ var BatchManager = class {
1734
1941
  /**
1735
1942
  * List all batches.
1736
1943
  */
1737
- list() {
1738
- return this.store.listBatches().map((id) => this.store.getMeta(id)).filter((b) => b !== null);
1944
+ async list() {
1945
+ const ids = await this.store.listBatches();
1946
+ const batches = [];
1947
+ for (const id of ids) {
1948
+ const meta = await this.store.getMeta(id);
1949
+ if (meta) batches.push(meta);
1950
+ }
1951
+ return batches;
1739
1952
  }
1740
1953
  /**
1741
1954
  * Cancel a batch.
1742
1955
  */
1743
- cancel(id) {
1744
- const batch = this.store.getMeta(id);
1956
+ async cancel(id) {
1957
+ const batch = await this.store.getMeta(id);
1745
1958
  if (!batch) {
1746
1959
  throw new AnyModelError(404, `Batch ${id} not found`);
1747
1960
  }
1748
1961
  if (batch.status === "completed" || batch.status === "cancelled") {
1749
1962
  return batch;
1750
1963
  }
1964
+ if (batch.batch_mode === "native") {
1965
+ const providerState = await this.store.loadProviderState(id);
1966
+ const adapter = this.batchAdapters.get(batch.provider_name);
1967
+ if (adapter && providerState?.providerBatchId) {
1968
+ try {
1969
+ await adapter.cancelBatch(providerState.providerBatchId);
1970
+ } catch {
1971
+ }
1972
+ }
1973
+ }
1751
1974
  batch.status = "cancelled";
1752
1975
  batch.completed_at = (/* @__PURE__ */ new Date()).toISOString();
1753
- this.store.updateMeta(batch);
1976
+ await this.store.updateMeta(batch);
1754
1977
  return batch;
1755
1978
  }
1756
1979
  /**
1757
- * Process batch requests concurrently.
1980
+ * Process batch via native provider batch API.
1758
1981
  */
1759
- async processBatch(batchId, request) {
1760
- const batch = this.store.getMeta(batchId);
1982
+ async processNativeBatch(batchId, request, adapter) {
1983
+ const batch = await this.store.getMeta(batchId);
1984
+ if (!batch) return;
1985
+ try {
1986
+ const model = request.model.includes("/") ? request.model.split("/").slice(1).join("/") : request.model;
1987
+ const { providerBatchId, metadata } = await adapter.createBatch(
1988
+ model,
1989
+ request.requests,
1990
+ request.options
1991
+ );
1992
+ await this.store.saveProviderState(batchId, {
1993
+ providerBatchId,
1994
+ providerName: batch.provider_name,
1995
+ ...metadata
1996
+ });
1997
+ batch.status = "processing";
1998
+ await this.store.updateMeta(batch);
1999
+ } catch (err) {
2000
+ batch.status = "failed";
2001
+ batch.completed_at = (/* @__PURE__ */ new Date()).toISOString();
2002
+ await this.store.updateMeta(batch);
2003
+ throw err;
2004
+ }
2005
+ }
2006
+ /**
2007
+ * Sync native batch status from provider.
2008
+ */
2009
+ async syncNativeBatchStatus(batchId) {
2010
+ const batch = await this.store.getMeta(batchId);
2011
+ if (!batch) return;
2012
+ const providerState = await this.store.loadProviderState(batchId);
2013
+ if (!providerState?.providerBatchId) return;
2014
+ const adapter = this.batchAdapters.get(batch.provider_name);
2015
+ if (!adapter) return;
2016
+ try {
2017
+ const status = await adapter.pollBatch(providerState.providerBatchId);
2018
+ batch.total = status.total || batch.total;
2019
+ batch.completed = status.completed;
2020
+ batch.failed = status.failed;
2021
+ if (status.status === "completed" || status.status === "failed" || status.status === "cancelled") {
2022
+ batch.status = status.status;
2023
+ batch.completed_at = (/* @__PURE__ */ new Date()).toISOString();
2024
+ if (status.status === "completed" || status.status === "failed") {
2025
+ try {
2026
+ const results = await adapter.getBatchResults(providerState.providerBatchId);
2027
+ for (const result of results) {
2028
+ await this.store.appendResult(batchId, result);
2029
+ }
2030
+ batch.completed = results.filter((r) => r.status === "success").length;
2031
+ batch.failed = results.filter((r) => r.status === "error").length;
2032
+ } catch {
2033
+ if (batch.status !== "failed") {
2034
+ batch.status = "failed";
2035
+ }
2036
+ }
2037
+ }
2038
+ } else {
2039
+ batch.status = "processing";
2040
+ }
2041
+ await this.store.updateMeta(batch);
2042
+ } catch {
2043
+ }
2044
+ }
2045
+ /**
2046
+ * Process batch requests concurrently (fallback path).
2047
+ */
2048
+ async processConcurrentBatch(batchId, request) {
2049
+ const batch = await this.store.getMeta(batchId);
2050
+ if (!batch) return;
1761
2051
  batch.status = "processing";
1762
- this.store.updateMeta(batch);
2052
+ await this.store.updateMeta(batch);
1763
2053
  const items = request.requests;
1764
- const queue = [...items];
1765
2054
  const active = /* @__PURE__ */ new Set();
1766
2055
  const processItem = async (item) => {
1767
- const current = this.store.getMeta(batchId);
2056
+ const current = await this.store.getMeta(batchId);
1768
2057
  if (current?.status === "cancelled") return;
1769
2058
  const chatRequest = {
1770
2059
  model: request.model,
@@ -1796,17 +2085,19 @@ var BatchManager = class {
1796
2085
  error: { code: error.code, message: error.message }
1797
2086
  };
1798
2087
  }
1799
- this.store.appendResult(batchId, result);
1800
- const meta = this.store.getMeta(batchId);
1801
- if (result.status === "success") {
1802
- meta.completed++;
1803
- } else {
1804
- meta.failed++;
2088
+ await this.store.appendResult(batchId, result);
2089
+ const meta = await this.store.getMeta(batchId);
2090
+ if (meta) {
2091
+ if (result.status === "success") {
2092
+ meta.completed++;
2093
+ } else {
2094
+ meta.failed++;
2095
+ }
2096
+ await this.store.updateMeta(meta);
1805
2097
  }
1806
- this.store.updateMeta(meta);
1807
2098
  };
1808
- for (const item of queue) {
1809
- const current = this.store.getMeta(batchId);
2099
+ for (const item of items) {
2100
+ const current = await this.store.getMeta(batchId);
1810
2101
  if (current?.status === "cancelled") break;
1811
2102
  if (active.size >= this.concurrencyLimit) {
1812
2103
  await Promise.race(active);
@@ -1817,15 +2108,411 @@ var BatchManager = class {
1817
2108
  active.add(promise);
1818
2109
  }
1819
2110
  await Promise.all(active);
1820
- const finalMeta = this.store.getMeta(batchId);
1821
- if (finalMeta.status !== "cancelled") {
2111
+ const finalMeta = await this.store.getMeta(batchId);
2112
+ if (finalMeta && finalMeta.status !== "cancelled") {
1822
2113
  finalMeta.status = finalMeta.failed === finalMeta.total ? "failed" : "completed";
1823
2114
  finalMeta.completed_at = (/* @__PURE__ */ new Date()).toISOString();
1824
- this.store.updateMeta(finalMeta);
2115
+ await this.store.updateMeta(finalMeta);
1825
2116
  }
1826
2117
  }
1827
2118
  };
1828
2119
 
2120
+ // src/providers/openai-batch.ts
2121
+ var OPENAI_API_BASE2 = "https://api.openai.com/v1";
2122
+ function createOpenAIBatchAdapter(apiKey) {
2123
+ async function apiRequest(path2, options = {}) {
2124
+ const headers = {
2125
+ "Authorization": `Bearer ${apiKey}`
2126
+ };
2127
+ let fetchBody;
2128
+ if (options.formData) {
2129
+ fetchBody = options.formData;
2130
+ } else if (options.body) {
2131
+ headers["Content-Type"] = "application/json";
2132
+ fetchBody = JSON.stringify(options.body);
2133
+ }
2134
+ const res = await fetch(`${OPENAI_API_BASE2}${path2}`, {
2135
+ method: options.method || "GET",
2136
+ headers,
2137
+ body: fetchBody
2138
+ });
2139
+ if (!res.ok) {
2140
+ let errorBody;
2141
+ try {
2142
+ errorBody = await res.json();
2143
+ } catch {
2144
+ errorBody = { message: res.statusText };
2145
+ }
2146
+ const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
2147
+ throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
2148
+ provider_name: "openai",
2149
+ raw: errorBody
2150
+ });
2151
+ }
2152
+ return res;
2153
+ }
2154
+ function buildJSONL(model, requests) {
2155
+ return requests.map((req) => {
2156
+ const body = {
2157
+ model,
2158
+ messages: req.messages
2159
+ };
2160
+ if (req.max_tokens !== void 0) body.max_tokens = req.max_tokens;
2161
+ if (req.temperature !== void 0) body.temperature = req.temperature;
2162
+ if (req.top_p !== void 0) body.top_p = req.top_p;
2163
+ if (req.stop !== void 0) body.stop = req.stop;
2164
+ if (req.response_format !== void 0) body.response_format = req.response_format;
2165
+ if (req.tools !== void 0) body.tools = req.tools;
2166
+ if (req.tool_choice !== void 0) body.tool_choice = req.tool_choice;
2167
+ return JSON.stringify({
2168
+ custom_id: req.custom_id,
2169
+ method: "POST",
2170
+ url: "/v1/chat/completions",
2171
+ body
2172
+ });
2173
+ }).join("\n");
2174
+ }
2175
+ function rePrefixId(id) {
2176
+ if (id && id.startsWith("chatcmpl-")) {
2177
+ return `gen-${id.substring(9)}`;
2178
+ }
2179
+ return id.startsWith("gen-") ? id : `gen-${id}`;
2180
+ }
2181
+ function translateOpenAIResponse(body) {
2182
+ return {
2183
+ id: rePrefixId(body.id || generateId()),
2184
+ object: "chat.completion",
2185
+ created: body.created || Math.floor(Date.now() / 1e3),
2186
+ model: `openai/${body.model}`,
2187
+ choices: body.choices,
2188
+ usage: body.usage
2189
+ };
2190
+ }
2191
+ function mapStatus(openaiStatus) {
2192
+ switch (openaiStatus) {
2193
+ case "validating":
2194
+ case "finalizing":
2195
+ return "processing";
2196
+ case "in_progress":
2197
+ return "processing";
2198
+ case "completed":
2199
+ return "completed";
2200
+ case "failed":
2201
+ return "failed";
2202
+ case "expired":
2203
+ return "failed";
2204
+ case "cancelled":
2205
+ case "cancelling":
2206
+ return "cancelled";
2207
+ default:
2208
+ return "pending";
2209
+ }
2210
+ }
2211
+ return {
2212
+ async createBatch(model, requests, options) {
2213
+ const jsonlContent = buildJSONL(model, requests);
2214
+ const blob = new Blob([jsonlContent], { type: "application/jsonl" });
2215
+ const formData = new FormData();
2216
+ formData.append("purpose", "batch");
2217
+ formData.append("file", blob, "batch_input.jsonl");
2218
+ const uploadRes = await apiRequest("/files", { method: "POST", formData });
2219
+ const fileData = await uploadRes.json();
2220
+ const inputFileId = fileData.id;
2221
+ const batchRes = await apiRequest("/batches", {
2222
+ method: "POST",
2223
+ body: {
2224
+ input_file_id: inputFileId,
2225
+ endpoint: "/v1/chat/completions",
2226
+ completion_window: "24h",
2227
+ metadata: options?.metadata
2228
+ }
2229
+ });
2230
+ const batchData = await batchRes.json();
2231
+ return {
2232
+ providerBatchId: batchData.id,
2233
+ metadata: {
2234
+ input_file_id: inputFileId,
2235
+ openai_status: batchData.status
2236
+ }
2237
+ };
2238
+ },
2239
+ async pollBatch(providerBatchId) {
2240
+ const res = await apiRequest(`/batches/${providerBatchId}`);
2241
+ const data = await res.json();
2242
+ const requestCounts = data.request_counts || {};
2243
+ return {
2244
+ status: mapStatus(data.status),
2245
+ total: requestCounts.total || 0,
2246
+ completed: requestCounts.completed || 0,
2247
+ failed: requestCounts.failed || 0
2248
+ };
2249
+ },
2250
+ async getBatchResults(providerBatchId) {
2251
+ const batchRes = await apiRequest(`/batches/${providerBatchId}`);
2252
+ const batchData = await batchRes.json();
2253
+ const results = [];
2254
+ if (batchData.output_file_id) {
2255
+ const outputRes = await apiRequest(`/files/${batchData.output_file_id}/content`);
2256
+ const outputText = await outputRes.text();
2257
+ for (const line of outputText.trim().split("\n")) {
2258
+ if (!line) continue;
2259
+ const item = JSON.parse(line);
2260
+ if (item.response?.status_code === 200) {
2261
+ results.push({
2262
+ custom_id: item.custom_id,
2263
+ status: "success",
2264
+ response: translateOpenAIResponse(item.response.body),
2265
+ error: null
2266
+ });
2267
+ } else {
2268
+ results.push({
2269
+ custom_id: item.custom_id,
2270
+ status: "error",
2271
+ response: null,
2272
+ error: {
2273
+ code: item.response?.status_code || 500,
2274
+ message: item.error?.message || item.response?.body?.error?.message || "Unknown error"
2275
+ }
2276
+ });
2277
+ }
2278
+ }
2279
+ }
2280
+ if (batchData.error_file_id) {
2281
+ const errorRes = await apiRequest(`/files/${batchData.error_file_id}/content`);
2282
+ const errorText = await errorRes.text();
2283
+ for (const line of errorText.trim().split("\n")) {
2284
+ if (!line) continue;
2285
+ const item = JSON.parse(line);
2286
+ const existing = results.find((r) => r.custom_id === item.custom_id);
2287
+ if (!existing) {
2288
+ results.push({
2289
+ custom_id: item.custom_id,
2290
+ status: "error",
2291
+ response: null,
2292
+ error: {
2293
+ code: item.response?.status_code || 500,
2294
+ message: item.error?.message || "Batch item error"
2295
+ }
2296
+ });
2297
+ }
2298
+ }
2299
+ }
2300
+ return results;
2301
+ },
2302
+ async cancelBatch(providerBatchId) {
2303
+ await apiRequest(`/batches/${providerBatchId}/cancel`, { method: "POST" });
2304
+ }
2305
+ };
2306
+ }
2307
+
2308
+ // src/providers/anthropic-batch.ts
2309
+ var ANTHROPIC_API_BASE2 = "https://api.anthropic.com/v1";
2310
+ var ANTHROPIC_VERSION2 = "2023-06-01";
2311
+ var DEFAULT_MAX_TOKENS2 = 4096;
2312
+ function createAnthropicBatchAdapter(apiKey) {
2313
+ async function apiRequest(path2, options = {}) {
2314
+ const headers = {
2315
+ "x-api-key": apiKey,
2316
+ "anthropic-version": ANTHROPIC_VERSION2,
2317
+ "Content-Type": "application/json"
2318
+ };
2319
+ const res = await fetch(`${ANTHROPIC_API_BASE2}${path2}`, {
2320
+ method: options.method || "GET",
2321
+ headers,
2322
+ body: options.body ? JSON.stringify(options.body) : void 0
2323
+ });
2324
+ if (!res.ok) {
2325
+ let errorBody;
2326
+ try {
2327
+ errorBody = await res.json();
2328
+ } catch {
2329
+ errorBody = { message: res.statusText };
2330
+ }
2331
+ const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
2332
+ throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
2333
+ provider_name: "anthropic",
2334
+ raw: errorBody
2335
+ });
2336
+ }
2337
+ return res;
2338
+ }
2339
+ function translateToAnthropicParams(model, req) {
2340
+ const params = {
2341
+ model,
2342
+ max_tokens: req.max_tokens || DEFAULT_MAX_TOKENS2
2343
+ };
2344
+ const systemMessages = req.messages.filter((m) => m.role === "system");
2345
+ const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
2346
+ if (systemMessages.length > 0) {
2347
+ params.system = systemMessages.map((m) => typeof m.content === "string" ? m.content : "").join("\n");
2348
+ }
2349
+ params.messages = nonSystemMessages.map((m) => ({
2350
+ role: m.role === "tool" ? "user" : m.role,
2351
+ content: m.tool_call_id ? [{ type: "tool_result", tool_use_id: m.tool_call_id, content: typeof m.content === "string" ? m.content : "" }] : m.content
2352
+ }));
2353
+ if (req.temperature !== void 0) params.temperature = req.temperature;
2354
+ if (req.top_p !== void 0) params.top_p = req.top_p;
2355
+ if (req.top_k !== void 0) params.top_k = req.top_k;
2356
+ if (req.stop !== void 0) params.stop_sequences = Array.isArray(req.stop) ? req.stop : [req.stop];
2357
+ if (req.tools && req.tools.length > 0) {
2358
+ params.tools = req.tools.map((t) => ({
2359
+ name: t.function.name,
2360
+ description: t.function.description || "",
2361
+ input_schema: t.function.parameters || { type: "object", properties: {} }
2362
+ }));
2363
+ if (req.tool_choice) {
2364
+ if (req.tool_choice === "auto") {
2365
+ params.tool_choice = { type: "auto" };
2366
+ } else if (req.tool_choice === "required") {
2367
+ params.tool_choice = { type: "any" };
2368
+ } else if (req.tool_choice === "none") {
2369
+ delete params.tools;
2370
+ } else if (typeof req.tool_choice === "object") {
2371
+ params.tool_choice = { type: "tool", name: req.tool_choice.function.name };
2372
+ }
2373
+ }
2374
+ }
2375
+ if (req.response_format) {
2376
+ if (req.response_format.type === "json_object" || req.response_format.type === "json_schema") {
2377
+ const jsonInstruction = "Respond with valid JSON only. Do not include any text outside the JSON object.";
2378
+ params.system = params.system ? `${jsonInstruction}
2379
+
2380
+ ${params.system}` : jsonInstruction;
2381
+ }
2382
+ }
2383
+ return params;
2384
+ }
2385
+ function mapStopReason(reason) {
2386
+ switch (reason) {
2387
+ case "end_turn":
2388
+ return "stop";
2389
+ case "max_tokens":
2390
+ return "length";
2391
+ case "tool_use":
2392
+ return "tool_calls";
2393
+ case "stop_sequence":
2394
+ return "stop";
2395
+ default:
2396
+ return "stop";
2397
+ }
2398
+ }
2399
+ function translateAnthropicMessage(msg) {
2400
+ let content = "";
2401
+ const toolCalls = [];
2402
+ for (const block of msg.content || []) {
2403
+ if (block.type === "text") {
2404
+ content += block.text;
2405
+ } else if (block.type === "tool_use") {
2406
+ toolCalls.push({
2407
+ id: block.id,
2408
+ type: "function",
2409
+ function: {
2410
+ name: block.name,
2411
+ arguments: JSON.stringify(block.input)
2412
+ }
2413
+ });
2414
+ }
2415
+ }
2416
+ const message = { role: "assistant", content };
2417
+ if (toolCalls.length > 0) {
2418
+ message.tool_calls = toolCalls;
2419
+ }
2420
+ return {
2421
+ id: generateId(),
2422
+ object: "chat.completion",
2423
+ created: Math.floor(Date.now() / 1e3),
2424
+ model: `anthropic/${msg.model}`,
2425
+ choices: [{
2426
+ index: 0,
2427
+ message,
2428
+ finish_reason: mapStopReason(msg.stop_reason)
2429
+ }],
2430
+ usage: {
2431
+ prompt_tokens: msg.usage?.input_tokens || 0,
2432
+ completion_tokens: msg.usage?.output_tokens || 0,
2433
+ total_tokens: (msg.usage?.input_tokens || 0) + (msg.usage?.output_tokens || 0)
2434
+ }
2435
+ };
2436
+ }
2437
+ return {
2438
+ async createBatch(model, requests, _options) {
2439
+ const batchRequests = requests.map((req) => ({
2440
+ custom_id: req.custom_id,
2441
+ params: translateToAnthropicParams(model, req)
2442
+ }));
2443
+ const res = await apiRequest("/messages/batches", {
2444
+ method: "POST",
2445
+ body: { requests: batchRequests }
2446
+ });
2447
+ const data = await res.json();
2448
+ return {
2449
+ providerBatchId: data.id,
2450
+ metadata: {
2451
+ anthropic_type: data.type,
2452
+ created_at: data.created_at
2453
+ }
2454
+ };
2455
+ },
2456
+ async pollBatch(providerBatchId) {
2457
+ const res = await apiRequest(`/messages/batches/${providerBatchId}`);
2458
+ const data = await res.json();
2459
+ const counts = data.request_counts || {};
2460
+ const total = (counts.processing || 0) + (counts.succeeded || 0) + (counts.errored || 0) + (counts.canceled || 0) + (counts.expired || 0);
2461
+ let status;
2462
+ if (data.processing_status === "ended") {
2463
+ if (counts.succeeded === 0 && (counts.errored > 0 || counts.expired > 0 || counts.canceled > 0)) {
2464
+ status = "failed";
2465
+ } else if (data.cancel_initiated_at) {
2466
+ status = "cancelled";
2467
+ } else {
2468
+ status = "completed";
2469
+ }
2470
+ } else {
2471
+ status = "processing";
2472
+ }
2473
+ return {
2474
+ status,
2475
+ total,
2476
+ completed: counts.succeeded || 0,
2477
+ failed: (counts.errored || 0) + (counts.expired || 0) + (counts.canceled || 0)
2478
+ };
2479
+ },
2480
+ async getBatchResults(providerBatchId) {
2481
+ const res = await apiRequest(`/messages/batches/${providerBatchId}/results`);
2482
+ const text = await res.text();
2483
+ const results = [];
2484
+ for (const line of text.trim().split("\n")) {
2485
+ if (!line) continue;
2486
+ const item = JSON.parse(line);
2487
+ if (item.result?.type === "succeeded") {
2488
+ results.push({
2489
+ custom_id: item.custom_id,
2490
+ status: "success",
2491
+ response: translateAnthropicMessage(item.result.message),
2492
+ error: null
2493
+ });
2494
+ } else {
2495
+ const errorType = item.result?.type || "unknown";
2496
+ const errorMsg = item.result?.error?.message || `Batch item ${errorType}`;
2497
+ results.push({
2498
+ custom_id: item.custom_id,
2499
+ status: "error",
2500
+ response: null,
2501
+ error: {
2502
+ code: errorType === "expired" ? 408 : 500,
2503
+ message: errorMsg
2504
+ }
2505
+ });
2506
+ }
2507
+ }
2508
+ return results;
2509
+ },
2510
+ async cancelBatch(providerBatchId) {
2511
+ await apiRequest(`/messages/batches/${providerBatchId}/cancel`, { method: "POST" });
2512
+ }
2513
+ };
2514
+ }
2515
+
1829
2516
  // src/client.ts
1830
2517
  var AnyModel = class {
1831
2518
  registry;
@@ -1841,6 +2528,9 @@ var AnyModel = class {
1841
2528
  constructor(config = {}) {
1842
2529
  this.config = resolveConfig(config);
1843
2530
  this.registry = new ProviderRegistry();
2531
+ if (this.config.io) {
2532
+ configureFsIO(this.config.io);
2533
+ }
1844
2534
  this.registerProviders();
1845
2535
  this.router = new Router(this.registry, this.config.aliases, this.config);
1846
2536
  this.chat = {
@@ -1890,8 +2580,10 @@ var AnyModel = class {
1890
2580
  };
1891
2581
  this.batchManager = new BatchManager(this.router, {
1892
2582
  dir: this.config.batch?.dir,
1893
- concurrency: this.config.batch?.concurrencyFallback
2583
+ concurrency: this.config.batch?.concurrencyFallback,
2584
+ pollInterval: this.config.batch?.pollInterval
1894
2585
  });
2586
+ this.registerBatchAdapters();
1895
2587
  this.batches = {
1896
2588
  create: (request) => this.batchManager.create(request),
1897
2589
  createAndPoll: (request, options) => this.batchManager.createAndPoll(request, options),
@@ -1943,6 +2635,17 @@ var AnyModel = class {
1943
2635
  }
1944
2636
  }
1945
2637
  }
2638
+ registerBatchAdapters() {
2639
+ const config = this.config;
2640
+ const openaiKey = config.openai?.apiKey || process.env.OPENAI_API_KEY;
2641
+ if (openaiKey) {
2642
+ this.batchManager.registerBatchAdapter("openai", createOpenAIBatchAdapter(openaiKey));
2643
+ }
2644
+ const anthropicKey = config.anthropic?.apiKey || process.env.ANTHROPIC_API_KEY;
2645
+ if (anthropicKey) {
2646
+ this.batchManager.registerBatchAdapter("anthropic", createAnthropicBatchAdapter(anthropicKey));
2647
+ }
2648
+ }
1946
2649
  applyDefaults(request) {
1947
2650
  const defaults = this.config.defaults;
1948
2651
  if (!defaults) return request;
@@ -1971,10 +2674,10 @@ var AnyModel = class {
1971
2674
  // src/server.ts
1972
2675
  var import_node_http = require("http");
1973
2676
  function parseBody(req) {
1974
- return new Promise((resolve3, reject) => {
2677
+ return new Promise((resolve2, reject) => {
1975
2678
  const chunks = [];
1976
2679
  req.on("data", (chunk) => chunks.push(chunk));
1977
- req.on("end", () => resolve3(Buffer.concat(chunks).toString()));
2680
+ req.on("end", () => resolve2(Buffer.concat(chunks).toString()));
1978
2681
  req.on("error", reject);
1979
2682
  });
1980
2683
  }
@@ -2004,7 +2707,7 @@ function createAnyModelServer(options = {}) {
2004
2707
  const basePath = "/api/v1";
2005
2708
  const server = (0, import_node_http.createServer)(async (req, res) => {
2006
2709
  const url = new URL(req.url || "/", `http://${req.headers.host}`);
2007
- const path = url.pathname;
2710
+ const path2 = url.pathname;
2008
2711
  res.setHeader("Access-Control-Allow-Origin", "*");
2009
2712
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
2010
2713
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
@@ -2014,11 +2717,11 @@ function createAnyModelServer(options = {}) {
2014
2717
  return;
2015
2718
  }
2016
2719
  try {
2017
- if (path === "/health" && req.method === "GET") {
2720
+ if (path2 === "/health" && req.method === "GET") {
2018
2721
  sendJSON(res, 200, { status: "ok" });
2019
2722
  return;
2020
2723
  }
2021
- if (path === `${basePath}/chat/completions` && req.method === "POST") {
2724
+ if (path2 === `${basePath}/chat/completions` && req.method === "POST") {
2022
2725
  const body = JSON.parse(await parseBody(req));
2023
2726
  if (body.stream) {
2024
2727
  const stream = await client.chat.completions.create(body);
@@ -2029,14 +2732,14 @@ function createAnyModelServer(options = {}) {
2029
2732
  }
2030
2733
  return;
2031
2734
  }
2032
- if (path === `${basePath}/models` && req.method === "GET") {
2735
+ if (path2 === `${basePath}/models` && req.method === "GET") {
2033
2736
  const provider = url.searchParams.get("provider") || void 0;
2034
2737
  const models = await client.models.list({ provider });
2035
2738
  sendJSON(res, 200, { object: "list", data: models });
2036
2739
  return;
2037
2740
  }
2038
- if (path.startsWith(`${basePath}/generation/`) && req.method === "GET") {
2039
- const id = path.substring(`${basePath}/generation/`.length);
2741
+ if (path2.startsWith(`${basePath}/generation/`) && req.method === "GET") {
2742
+ const id = path2.substring(`${basePath}/generation/`.length);
2040
2743
  const stats = client.generation.get(id);
2041
2744
  if (!stats) {
2042
2745
  sendError(res, 404, `Generation ${id} not found`);
@@ -2045,26 +2748,26 @@ function createAnyModelServer(options = {}) {
2045
2748
  sendJSON(res, 200, stats);
2046
2749
  return;
2047
2750
  }
2048
- if (path === `${basePath}/batches` && req.method === "POST") {
2751
+ if (path2 === `${basePath}/batches` && req.method === "POST") {
2049
2752
  const body = JSON.parse(await parseBody(req));
2050
2753
  const batch = await client.batches.create(body);
2051
2754
  sendJSON(res, 201, batch);
2052
2755
  return;
2053
2756
  }
2054
- if (path === `${basePath}/batches` && req.method === "GET") {
2055
- const batches = client.batches.list();
2757
+ if (path2 === `${basePath}/batches` && req.method === "GET") {
2758
+ const batches = await client.batches.list();
2056
2759
  sendJSON(res, 200, { object: "list", data: batches });
2057
2760
  return;
2058
2761
  }
2059
- if (path.startsWith(`${basePath}/batches/`) && req.method === "GET") {
2060
- const parts = path.substring(`${basePath}/batches/`.length).split("/");
2762
+ if (path2.startsWith(`${basePath}/batches/`) && req.method === "GET") {
2763
+ const parts = path2.substring(`${basePath}/batches/`.length).split("/");
2061
2764
  const id = parts[0];
2062
2765
  if (parts[1] === "results") {
2063
- const results = client.batches.results(id);
2766
+ const results = await client.batches.results(id);
2064
2767
  sendJSON(res, 200, results);
2065
2768
  return;
2066
2769
  }
2067
- const batch = client.batches.get(id);
2770
+ const batch = await client.batches.get(id);
2068
2771
  if (!batch) {
2069
2772
  sendError(res, 404, `Batch ${id} not found`);
2070
2773
  return;
@@ -2072,16 +2775,16 @@ function createAnyModelServer(options = {}) {
2072
2775
  sendJSON(res, 200, batch);
2073
2776
  return;
2074
2777
  }
2075
- if (path.startsWith(`${basePath}/batches/`) && req.method === "POST") {
2076
- const parts = path.substring(`${basePath}/batches/`.length).split("/");
2778
+ if (path2.startsWith(`${basePath}/batches/`) && req.method === "POST") {
2779
+ const parts = path2.substring(`${basePath}/batches/`.length).split("/");
2077
2780
  const id = parts[0];
2078
2781
  if (parts[1] === "cancel") {
2079
- const batch = client.batches.cancel(id);
2782
+ const batch = await client.batches.cancel(id);
2080
2783
  sendJSON(res, 200, batch);
2081
2784
  return;
2082
2785
  }
2083
2786
  }
2084
- sendError(res, 404, `Not found: ${path}`);
2787
+ sendError(res, 404, `Not found: ${path2}`);
2085
2788
  } catch (err) {
2086
2789
  const code = err?.code || 500;
2087
2790
  const message = err?.message || "Internal server error";
@@ -2117,8 +2820,19 @@ function startServer(options = {}) {
2117
2820
  BatchManager,
2118
2821
  BatchStore,
2119
2822
  GenerationStatsStore,
2823
+ appendFileQueued,
2824
+ configureFsIO,
2825
+ createAnthropicBatchAdapter,
2120
2826
  createAnyModelServer,
2827
+ createOpenAIBatchAdapter,
2828
+ ensureDir,
2829
+ getFsQueueStatus,
2830
+ joinPath,
2831
+ readFileQueued,
2121
2832
  resolveConfig,
2122
- startServer
2833
+ startServer,
2834
+ waitForFsQueuesIdle,
2835
+ writeFileFlushedQueued,
2836
+ writeFileQueued
2123
2837
  });
2124
2838
  //# sourceMappingURL=index.cjs.map