@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/cli.js CHANGED
@@ -147,7 +147,7 @@ async function withRetry(fn, options = {}) {
147
147
  throw error;
148
148
  }
149
149
  const delay = computeDelay(attempt, opts, error);
150
- await new Promise((resolve3) => setTimeout(resolve3, delay));
150
+ await new Promise((resolve2) => setTimeout(resolve2, delay));
151
151
  }
152
152
  }
153
153
  throw lastError;
@@ -506,8 +506,8 @@ var SUPPORTED_PARAMS = /* @__PURE__ */ new Set([
506
506
  ]);
507
507
  function createOpenAIAdapter(apiKey, baseURL) {
508
508
  const base = baseURL || OPENAI_API_BASE;
509
- async function makeRequest(path, body, method = "POST") {
510
- const res = await fetch(`${base}${path}`, {
509
+ async function makeRequest(path2, body, method = "POST") {
510
+ const res = await fetch(`${base}${path2}`, {
511
511
  method,
512
512
  headers: {
513
513
  "Content-Type": "application/json",
@@ -658,6 +658,9 @@ function createOpenAIAdapter(apiKey, baseURL) {
658
658
  supportsParameter(param) {
659
659
  return SUPPORTED_PARAMS.has(param);
660
660
  },
661
+ supportsBatch() {
662
+ return true;
663
+ },
661
664
  async sendRequest(request) {
662
665
  const body = buildRequestBody(request);
663
666
  const res = await makeRequest("/chat/completions", body);
@@ -712,8 +715,8 @@ var FALLBACK_MODELS = [
712
715
  { 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) }
713
716
  ];
714
717
  function createAnthropicAdapter(apiKey) {
715
- async function makeRequest(path, body, stream = false) {
716
- const res = await fetch(`${ANTHROPIC_API_BASE}${path}`, {
718
+ async function makeRequest(path2, body, stream = false) {
719
+ const res = await fetch(`${ANTHROPIC_API_BASE}${path2}`, {
717
720
  method: "POST",
718
721
  headers: {
719
722
  "Content-Type": "application/json",
@@ -1007,6 +1010,9 @@ ${body.system}` : jsonInstruction;
1007
1010
  supportsParameter(param) {
1008
1011
  return SUPPORTED_PARAMS2.has(param);
1009
1012
  },
1013
+ supportsBatch() {
1014
+ return true;
1015
+ },
1010
1016
  async sendRequest(request) {
1011
1017
  const body = translateRequest(request);
1012
1018
  const res = await makeRequest("/messages", body);
@@ -1286,6 +1292,9 @@ function createGoogleAdapter(apiKey) {
1286
1292
  supportsParameter(param) {
1287
1293
  return SUPPORTED_PARAMS3.has(param);
1288
1294
  },
1295
+ supportsBatch() {
1296
+ return false;
1297
+ },
1289
1298
  async sendRequest(request) {
1290
1299
  const body = translateRequest(request);
1291
1300
  const url = getModelEndpoint(request.model, false);
@@ -1344,6 +1353,9 @@ function createCustomAdapter(name, config) {
1344
1353
  return {
1345
1354
  ...openaiAdapter,
1346
1355
  name,
1356
+ supportsBatch() {
1357
+ return false;
1358
+ },
1347
1359
  async listModels() {
1348
1360
  if (config.models && config.models.length > 0) {
1349
1361
  return config.models.map((modelId) => ({
@@ -1409,10 +1421,10 @@ function interpolateDeep(obj) {
1409
1421
  }
1410
1422
  return obj;
1411
1423
  }
1412
- function loadJsonFile(path) {
1413
- if (!existsSync(path)) return null;
1424
+ function loadJsonFile(path2) {
1425
+ if (!existsSync(path2)) return null;
1414
1426
  try {
1415
- const raw = readFileSync(path, "utf-8");
1427
+ const raw = readFileSync(path2, "utf-8");
1416
1428
  const parsed = JSON.parse(raw);
1417
1429
  return interpolateDeep(parsed);
1418
1430
  } catch {
@@ -1513,93 +1525,228 @@ var GenerationStatsStore = class {
1513
1525
  }
1514
1526
  };
1515
1527
 
1528
+ // src/utils/fs-io.ts
1529
+ import { mkdir, open, readFile as fsReadFile, rename, writeFile as fsWriteFile, readdir as fsReaddir, stat as fsStat } from "fs/promises";
1530
+ import { createWriteStream } from "fs";
1531
+ import path from "path";
1532
+ import PQueue from "p-queue";
1533
+ var writeQueue = new PQueue({ concurrency: 10 });
1534
+ var readQueue = new PQueue({ concurrency: 20 });
1535
+ function configureFsIO(options) {
1536
+ if (options.readConcurrency !== void 0) {
1537
+ readQueue.concurrency = options.readConcurrency;
1538
+ }
1539
+ if (options.writeConcurrency !== void 0) {
1540
+ writeQueue.concurrency = options.writeConcurrency;
1541
+ }
1542
+ }
1543
+ var ensuredDirs = /* @__PURE__ */ new Set();
1544
+ var joinPathCache = /* @__PURE__ */ new Map();
1545
+ var dirnameCache = /* @__PURE__ */ new Map();
1546
+ var resolvePathCache = /* @__PURE__ */ new Map();
1547
+ async function ensureDir(dir) {
1548
+ if (!dir) return;
1549
+ if (ensuredDirs.has(dir)) return;
1550
+ await mkdir(dir, { recursive: true });
1551
+ ensuredDirs.add(dir);
1552
+ }
1553
+ async function readFileQueued(filePath, encoding = "utf8") {
1554
+ return readQueue.add(async () => {
1555
+ return fsReadFile(filePath, encoding);
1556
+ });
1557
+ }
1558
+ async function readJsonQueued(filePath) {
1559
+ const raw = await readFileQueued(filePath, "utf8");
1560
+ return JSON.parse(raw);
1561
+ }
1562
+ async function readDirQueued(dirPath) {
1563
+ return readQueue.add(async () => {
1564
+ return fsReaddir(dirPath, { withFileTypes: true });
1565
+ });
1566
+ }
1567
+ async function pathExistsQueued(p) {
1568
+ return readQueue.add(async () => {
1569
+ try {
1570
+ await fsStat(p);
1571
+ return true;
1572
+ } catch {
1573
+ return false;
1574
+ }
1575
+ });
1576
+ }
1577
+ async function fileExistsQueued(filePath) {
1578
+ return readQueue.add(async () => {
1579
+ try {
1580
+ const s = await fsStat(filePath);
1581
+ return s.isFile();
1582
+ } catch {
1583
+ return false;
1584
+ }
1585
+ });
1586
+ }
1587
+ async function writeFileQueued(filePath, data) {
1588
+ await writeQueue.add(async () => {
1589
+ const dir = dirnameOf(filePath);
1590
+ await ensureDir(dir);
1591
+ await fsWriteFile(filePath, data);
1592
+ });
1593
+ }
1594
+ async function appendFileQueued(filePath, data) {
1595
+ await writeQueue.add(async () => {
1596
+ const dir = dirnameOf(filePath);
1597
+ await ensureDir(dir);
1598
+ await fsWriteFile(filePath, data, { flag: "a" });
1599
+ });
1600
+ }
1601
+ async function writeFileFlushedQueued(filePath, data) {
1602
+ await writeQueue.add(async () => {
1603
+ const dir = dirnameOf(filePath);
1604
+ await ensureDir(dir);
1605
+ const tmpPath = joinPath(
1606
+ dir,
1607
+ `.${path.basename(filePath)}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`
1608
+ );
1609
+ const fh = await open(tmpPath, "w");
1610
+ try {
1611
+ await fh.writeFile(data);
1612
+ await fh.sync();
1613
+ } finally {
1614
+ await fh.close();
1615
+ }
1616
+ await rename(tmpPath, filePath);
1617
+ try {
1618
+ const dh = await open(dir, "r");
1619
+ try {
1620
+ await dh.sync();
1621
+ } finally {
1622
+ await dh.close();
1623
+ }
1624
+ } catch {
1625
+ }
1626
+ });
1627
+ }
1628
+ function joinPath(...segments) {
1629
+ const key = segments.join("\0");
1630
+ const cached = joinPathCache.get(key);
1631
+ if (cached !== void 0) return cached;
1632
+ const out = path.join(...segments);
1633
+ joinPathCache.set(key, out);
1634
+ return out;
1635
+ }
1636
+ function dirnameOf(p) {
1637
+ const cached = dirnameCache.get(p);
1638
+ if (cached !== void 0) return cached;
1639
+ const out = path.dirname(p);
1640
+ dirnameCache.set(p, out);
1641
+ return out;
1642
+ }
1643
+ function resolvePath(...segments) {
1644
+ const key = segments.join("\0");
1645
+ const cached = resolvePathCache.get(key);
1646
+ if (cached !== void 0) return cached;
1647
+ const out = path.resolve(...segments);
1648
+ resolvePathCache.set(key, out);
1649
+ return out;
1650
+ }
1651
+
1516
1652
  // src/batch/store.ts
1517
- import { mkdirSync, writeFileSync, readFileSync as readFileSync2, existsSync as existsSync2, readdirSync, appendFileSync } from "fs";
1518
- import { join as join2, resolve as resolve2 } from "path";
1519
- import { homedir as homedir2 } from "os";
1520
- var DEFAULT_BATCH_DIR = join2(homedir2(), ".anymodel", "batches");
1653
+ var DEFAULT_BATCH_DIR = joinPath(process.cwd(), ".anymodel", "batches");
1521
1654
  var BatchStore = class {
1522
1655
  dir;
1656
+ initialized = false;
1523
1657
  constructor(dir) {
1524
- this.dir = resolve2(dir || DEFAULT_BATCH_DIR);
1525
- mkdirSync(this.dir, { recursive: true });
1658
+ this.dir = resolvePath(dir || DEFAULT_BATCH_DIR);
1659
+ }
1660
+ async init() {
1661
+ if (this.initialized) return;
1662
+ await ensureDir(this.dir);
1663
+ this.initialized = true;
1526
1664
  }
1527
1665
  batchDir(id) {
1528
- return join2(this.dir, id);
1666
+ return joinPath(this.dir, id);
1529
1667
  }
1530
1668
  /**
1531
1669
  * Create a new batch directory and save initial metadata.
1532
1670
  */
1533
- create(batch) {
1671
+ async create(batch) {
1672
+ await this.init();
1534
1673
  const dir = this.batchDir(batch.id);
1535
- mkdirSync(dir, { recursive: true });
1536
- writeFileSync(join2(dir, "meta.json"), JSON.stringify(batch, null, 2));
1674
+ await ensureDir(dir);
1675
+ await writeFileFlushedQueued(joinPath(dir, "meta.json"), JSON.stringify(batch, null, 2));
1537
1676
  }
1538
1677
  /**
1539
- * Update batch metadata.
1678
+ * Update batch metadata (atomic write).
1540
1679
  */
1541
- updateMeta(batch) {
1542
- const dir = this.batchDir(batch.id);
1543
- writeFileSync(join2(dir, "meta.json"), JSON.stringify(batch, null, 2));
1680
+ async updateMeta(batch) {
1681
+ await writeFileFlushedQueued(
1682
+ joinPath(this.batchDir(batch.id), "meta.json"),
1683
+ JSON.stringify(batch, null, 2)
1684
+ );
1544
1685
  }
1545
1686
  /**
1546
1687
  * Save requests as JSONL.
1547
1688
  */
1548
- saveRequests(id, requests) {
1549
- const dir = this.batchDir(id);
1689
+ async saveRequests(id, requests) {
1550
1690
  const lines = requests.map((r) => JSON.stringify(r)).join("\n") + "\n";
1551
- writeFileSync(join2(dir, "requests.jsonl"), lines);
1691
+ await writeFileQueued(joinPath(this.batchDir(id), "requests.jsonl"), lines);
1552
1692
  }
1553
1693
  /**
1554
1694
  * Append a result to results.jsonl.
1555
1695
  */
1556
- appendResult(id, result) {
1557
- const dir = this.batchDir(id);
1558
- appendFileSync(join2(dir, "results.jsonl"), JSON.stringify(result) + "\n");
1696
+ async appendResult(id, result) {
1697
+ await appendFileQueued(
1698
+ joinPath(this.batchDir(id), "results.jsonl"),
1699
+ JSON.stringify(result) + "\n"
1700
+ );
1559
1701
  }
1560
1702
  /**
1561
1703
  * Save provider-specific state (e.g., provider batch ID).
1562
1704
  */
1563
- saveProviderState(id, state) {
1564
- const dir = this.batchDir(id);
1565
- writeFileSync(join2(dir, "provider.json"), JSON.stringify(state, null, 2));
1705
+ async saveProviderState(id, state) {
1706
+ await writeFileFlushedQueued(
1707
+ joinPath(this.batchDir(id), "provider.json"),
1708
+ JSON.stringify(state, null, 2)
1709
+ );
1566
1710
  }
1567
1711
  /**
1568
1712
  * Load provider state.
1569
1713
  */
1570
- loadProviderState(id) {
1571
- const path = join2(this.batchDir(id), "provider.json");
1572
- if (!existsSync2(path)) return null;
1573
- return JSON.parse(readFileSync2(path, "utf-8"));
1714
+ async loadProviderState(id) {
1715
+ const p = joinPath(this.batchDir(id), "provider.json");
1716
+ if (!await fileExistsQueued(p)) return null;
1717
+ return readJsonQueued(p);
1574
1718
  }
1575
1719
  /**
1576
1720
  * Get batch metadata.
1577
1721
  */
1578
- getMeta(id) {
1579
- const path = join2(this.batchDir(id), "meta.json");
1580
- if (!existsSync2(path)) return null;
1581
- return JSON.parse(readFileSync2(path, "utf-8"));
1722
+ async getMeta(id) {
1723
+ const p = joinPath(this.batchDir(id), "meta.json");
1724
+ if (!await fileExistsQueued(p)) return null;
1725
+ return readJsonQueued(p);
1582
1726
  }
1583
1727
  /**
1584
1728
  * Get all results for a batch.
1585
1729
  */
1586
- getResults(id) {
1587
- const path = join2(this.batchDir(id), "results.jsonl");
1588
- if (!existsSync2(path)) return [];
1589
- return readFileSync2(path, "utf-8").trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
1730
+ async getResults(id) {
1731
+ const p = joinPath(this.batchDir(id), "results.jsonl");
1732
+ if (!await fileExistsQueued(p)) return [];
1733
+ const raw = await readFileQueued(p, "utf8");
1734
+ return raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
1590
1735
  }
1591
1736
  /**
1592
1737
  * List all batch IDs.
1593
1738
  */
1594
- listBatches() {
1595
- if (!existsSync2(this.dir)) return [];
1596
- return readdirSync(this.dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
1739
+ async listBatches() {
1740
+ await this.init();
1741
+ if (!await pathExistsQueued(this.dir)) return [];
1742
+ const entries = await readDirQueued(this.dir);
1743
+ return entries.filter((d) => d.isDirectory()).map((d) => d.name).sort();
1597
1744
  }
1598
1745
  /**
1599
1746
  * Check if a batch exists.
1600
1747
  */
1601
- exists(id) {
1602
- return existsSync2(join2(this.batchDir(id), "meta.json"));
1748
+ async exists(id) {
1749
+ return fileExistsQueued(joinPath(this.batchDir(id), "meta.json"));
1603
1750
  }
1604
1751
  };
1605
1752
 
@@ -1608,10 +1755,27 @@ var BatchManager = class {
1608
1755
  store;
1609
1756
  router;
1610
1757
  concurrencyLimit;
1758
+ defaultPollInterval;
1759
+ batchAdapters = /* @__PURE__ */ new Map();
1611
1760
  constructor(router, options) {
1612
1761
  this.store = new BatchStore(options?.dir);
1613
1762
  this.router = router;
1614
1763
  this.concurrencyLimit = options?.concurrency ?? 5;
1764
+ this.defaultPollInterval = options?.pollInterval ?? 5e3;
1765
+ }
1766
+ /**
1767
+ * Register a native batch adapter for a provider.
1768
+ */
1769
+ registerBatchAdapter(providerName, adapter) {
1770
+ this.batchAdapters.set(providerName, adapter);
1771
+ }
1772
+ /**
1773
+ * Check if a provider has native batch support.
1774
+ */
1775
+ getNativeBatchAdapter(model) {
1776
+ const providerName = model.split("/")[0];
1777
+ const adapter = this.batchAdapters.get(providerName);
1778
+ return adapter ? { adapter, providerName } : null;
1615
1779
  }
1616
1780
  /**
1617
1781
  * Create a batch and return immediately (no polling).
@@ -1619,13 +1783,16 @@ var BatchManager = class {
1619
1783
  async create(request) {
1620
1784
  const id = generateId("batch");
1621
1785
  const now = (/* @__PURE__ */ new Date()).toISOString();
1786
+ const providerName = request.model.split("/")[0] || "unknown";
1787
+ const native = this.getNativeBatchAdapter(request.model);
1788
+ const batchMode = native ? "native" : "concurrent";
1622
1789
  const batch = {
1623
1790
  id,
1624
1791
  object: "batch",
1625
1792
  status: "pending",
1626
1793
  model: request.model,
1627
- provider_name: request.model.split("/")[0] || "unknown",
1628
- batch_mode: "concurrent",
1794
+ provider_name: providerName,
1795
+ batch_mode: batchMode,
1629
1796
  total: request.requests.length,
1630
1797
  completed: 0,
1631
1798
  failed: 0,
@@ -1633,10 +1800,15 @@ var BatchManager = class {
1633
1800
  completed_at: null,
1634
1801
  expires_at: null
1635
1802
  };
1636
- this.store.create(batch);
1637
- this.store.saveRequests(id, request.requests);
1638
- this.processBatch(id, request).catch(() => {
1639
- });
1803
+ await this.store.create(batch);
1804
+ await this.store.saveRequests(id, request.requests);
1805
+ if (native) {
1806
+ this.processNativeBatch(id, request, native.adapter).catch(() => {
1807
+ });
1808
+ } else {
1809
+ this.processConcurrentBatch(id, request).catch(() => {
1810
+ });
1811
+ }
1640
1812
  return batch;
1641
1813
  }
1642
1814
  /**
@@ -1650,14 +1822,19 @@ var BatchManager = class {
1650
1822
  * Poll an existing batch until completion.
1651
1823
  */
1652
1824
  async poll(id, options = {}) {
1653
- const interval = options.interval ?? 5e3;
1825
+ const interval = options.interval ?? this.defaultPollInterval;
1654
1826
  const timeout = options.timeout ?? 0;
1655
1827
  const startTime = Date.now();
1656
1828
  while (true) {
1657
- const batch = this.store.getMeta(id);
1829
+ let batch = await this.store.getMeta(id);
1658
1830
  if (!batch) {
1659
1831
  throw new AnyModelError(404, `Batch ${id} not found`);
1660
1832
  }
1833
+ if (batch.batch_mode === "native" && batch.status === "processing") {
1834
+ await this.syncNativeBatchStatus(id);
1835
+ batch = await this.store.getMeta(id);
1836
+ if (!batch) throw new AnyModelError(404, `Batch ${id} not found`);
1837
+ }
1661
1838
  if (options.onProgress) {
1662
1839
  options.onProgress(batch);
1663
1840
  }
@@ -1667,24 +1844,24 @@ var BatchManager = class {
1667
1844
  if (timeout > 0 && Date.now() - startTime > timeout) {
1668
1845
  throw new AnyModelError(408, `Batch ${id} timed out after ${timeout}ms`);
1669
1846
  }
1670
- await new Promise((resolve3) => setTimeout(resolve3, interval));
1847
+ await new Promise((resolve2) => setTimeout(resolve2, interval));
1671
1848
  }
1672
1849
  }
1673
1850
  /**
1674
1851
  * Get the current status of a batch.
1675
1852
  */
1676
- get(id) {
1853
+ async get(id) {
1677
1854
  return this.store.getMeta(id);
1678
1855
  }
1679
1856
  /**
1680
1857
  * Get results for a completed batch.
1681
1858
  */
1682
- getResults(id) {
1683
- const batch = this.store.getMeta(id);
1859
+ async getResults(id) {
1860
+ const batch = await this.store.getMeta(id);
1684
1861
  if (!batch) {
1685
1862
  throw new AnyModelError(404, `Batch ${id} not found`);
1686
1863
  }
1687
- const results = this.store.getResults(id);
1864
+ const results = await this.store.getResults(id);
1688
1865
  const usage = {
1689
1866
  total_prompt_tokens: 0,
1690
1867
  total_completion_tokens: 0,
@@ -1706,37 +1883,119 @@ var BatchManager = class {
1706
1883
  /**
1707
1884
  * List all batches.
1708
1885
  */
1709
- list() {
1710
- return this.store.listBatches().map((id) => this.store.getMeta(id)).filter((b) => b !== null);
1886
+ async list() {
1887
+ const ids = await this.store.listBatches();
1888
+ const batches = [];
1889
+ for (const id of ids) {
1890
+ const meta = await this.store.getMeta(id);
1891
+ if (meta) batches.push(meta);
1892
+ }
1893
+ return batches;
1711
1894
  }
1712
1895
  /**
1713
1896
  * Cancel a batch.
1714
1897
  */
1715
- cancel(id) {
1716
- const batch = this.store.getMeta(id);
1898
+ async cancel(id) {
1899
+ const batch = await this.store.getMeta(id);
1717
1900
  if (!batch) {
1718
1901
  throw new AnyModelError(404, `Batch ${id} not found`);
1719
1902
  }
1720
1903
  if (batch.status === "completed" || batch.status === "cancelled") {
1721
1904
  return batch;
1722
1905
  }
1906
+ if (batch.batch_mode === "native") {
1907
+ const providerState = await this.store.loadProviderState(id);
1908
+ const adapter = this.batchAdapters.get(batch.provider_name);
1909
+ if (adapter && providerState?.providerBatchId) {
1910
+ try {
1911
+ await adapter.cancelBatch(providerState.providerBatchId);
1912
+ } catch {
1913
+ }
1914
+ }
1915
+ }
1723
1916
  batch.status = "cancelled";
1724
1917
  batch.completed_at = (/* @__PURE__ */ new Date()).toISOString();
1725
- this.store.updateMeta(batch);
1918
+ await this.store.updateMeta(batch);
1726
1919
  return batch;
1727
1920
  }
1728
1921
  /**
1729
- * Process batch requests concurrently.
1922
+ * Process batch via native provider batch API.
1923
+ */
1924
+ async processNativeBatch(batchId, request, adapter) {
1925
+ const batch = await this.store.getMeta(batchId);
1926
+ if (!batch) return;
1927
+ try {
1928
+ const model = request.model.includes("/") ? request.model.split("/").slice(1).join("/") : request.model;
1929
+ const { providerBatchId, metadata } = await adapter.createBatch(
1930
+ model,
1931
+ request.requests,
1932
+ request.options
1933
+ );
1934
+ await this.store.saveProviderState(batchId, {
1935
+ providerBatchId,
1936
+ providerName: batch.provider_name,
1937
+ ...metadata
1938
+ });
1939
+ batch.status = "processing";
1940
+ await this.store.updateMeta(batch);
1941
+ } catch (err) {
1942
+ batch.status = "failed";
1943
+ batch.completed_at = (/* @__PURE__ */ new Date()).toISOString();
1944
+ await this.store.updateMeta(batch);
1945
+ throw err;
1946
+ }
1947
+ }
1948
+ /**
1949
+ * Sync native batch status from provider.
1730
1950
  */
1731
- async processBatch(batchId, request) {
1732
- const batch = this.store.getMeta(batchId);
1951
+ async syncNativeBatchStatus(batchId) {
1952
+ const batch = await this.store.getMeta(batchId);
1953
+ if (!batch) return;
1954
+ const providerState = await this.store.loadProviderState(batchId);
1955
+ if (!providerState?.providerBatchId) return;
1956
+ const adapter = this.batchAdapters.get(batch.provider_name);
1957
+ if (!adapter) return;
1958
+ try {
1959
+ const status = await adapter.pollBatch(providerState.providerBatchId);
1960
+ batch.total = status.total || batch.total;
1961
+ batch.completed = status.completed;
1962
+ batch.failed = status.failed;
1963
+ if (status.status === "completed" || status.status === "failed" || status.status === "cancelled") {
1964
+ batch.status = status.status;
1965
+ batch.completed_at = (/* @__PURE__ */ new Date()).toISOString();
1966
+ if (status.status === "completed" || status.status === "failed") {
1967
+ try {
1968
+ const results = await adapter.getBatchResults(providerState.providerBatchId);
1969
+ for (const result of results) {
1970
+ await this.store.appendResult(batchId, result);
1971
+ }
1972
+ batch.completed = results.filter((r) => r.status === "success").length;
1973
+ batch.failed = results.filter((r) => r.status === "error").length;
1974
+ } catch {
1975
+ if (batch.status !== "failed") {
1976
+ batch.status = "failed";
1977
+ }
1978
+ }
1979
+ }
1980
+ } else {
1981
+ batch.status = "processing";
1982
+ }
1983
+ await this.store.updateMeta(batch);
1984
+ } catch {
1985
+ }
1986
+ }
1987
+ /**
1988
+ * Process batch requests concurrently (fallback path).
1989
+ */
1990
+ async processConcurrentBatch(batchId, request) {
1991
+ const batch = await this.store.getMeta(batchId);
1992
+ if (!batch) return;
1733
1993
  batch.status = "processing";
1734
- this.store.updateMeta(batch);
1994
+ await this.store.updateMeta(batch);
1735
1995
  const items = request.requests;
1736
- const queue = [...items];
1737
1996
  const active = /* @__PURE__ */ new Set();
1738
1997
  const processItem = async (item) => {
1739
- const current = this.store.getMeta(batchId);
1998
+ const current = await this.store.getMeta(batchId);
1740
1999
  if (current?.status === "cancelled") return;
1741
2000
  const chatRequest = {
1742
2001
  model: request.model,
@@ -1768,17 +2027,19 @@ var BatchManager = class {
1768
2027
  error: { code: error.code, message: error.message }
1769
2028
  };
1770
2029
  }
1771
- this.store.appendResult(batchId, result);
1772
- const meta = this.store.getMeta(batchId);
1773
- if (result.status === "success") {
1774
- meta.completed++;
1775
- } else {
1776
- meta.failed++;
2030
+ await this.store.appendResult(batchId, result);
2031
+ const meta = await this.store.getMeta(batchId);
2032
+ if (meta) {
2033
+ if (result.status === "success") {
2034
+ meta.completed++;
2035
+ } else {
2036
+ meta.failed++;
2037
+ }
2038
+ await this.store.updateMeta(meta);
1777
2039
  }
1778
- this.store.updateMeta(meta);
1779
2040
  };
1780
- for (const item of queue) {
1781
- const current = this.store.getMeta(batchId);
2041
+ for (const item of items) {
2042
+ const current = await this.store.getMeta(batchId);
1782
2043
  if (current?.status === "cancelled") break;
1783
2044
  if (active.size >= this.concurrencyLimit) {
1784
2045
  await Promise.race(active);
@@ -1789,15 +2050,411 @@ var BatchManager = class {
1789
2050
  active.add(promise);
1790
2051
  }
1791
2052
  await Promise.all(active);
1792
- const finalMeta = this.store.getMeta(batchId);
1793
- if (finalMeta.status !== "cancelled") {
2053
+ const finalMeta = await this.store.getMeta(batchId);
2054
+ if (finalMeta && finalMeta.status !== "cancelled") {
1794
2055
  finalMeta.status = finalMeta.failed === finalMeta.total ? "failed" : "completed";
1795
2056
  finalMeta.completed_at = (/* @__PURE__ */ new Date()).toISOString();
1796
- this.store.updateMeta(finalMeta);
2057
+ await this.store.updateMeta(finalMeta);
1797
2058
  }
1798
2059
  }
1799
2060
  };
1800
2061
 
2062
+ // src/providers/openai-batch.ts
2063
+ var OPENAI_API_BASE2 = "https://api.openai.com/v1";
2064
+ function createOpenAIBatchAdapter(apiKey) {
2065
+ async function apiRequest(path2, options = {}) {
2066
+ const headers = {
2067
+ "Authorization": `Bearer ${apiKey}`
2068
+ };
2069
+ let fetchBody;
2070
+ if (options.formData) {
2071
+ fetchBody = options.formData;
2072
+ } else if (options.body) {
2073
+ headers["Content-Type"] = "application/json";
2074
+ fetchBody = JSON.stringify(options.body);
2075
+ }
2076
+ const res = await fetch(`${OPENAI_API_BASE2}${path2}`, {
2077
+ method: options.method || "GET",
2078
+ headers,
2079
+ body: fetchBody
2080
+ });
2081
+ if (!res.ok) {
2082
+ let errorBody;
2083
+ try {
2084
+ errorBody = await res.json();
2085
+ } catch {
2086
+ errorBody = { message: res.statusText };
2087
+ }
2088
+ const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
2089
+ throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
2090
+ provider_name: "openai",
2091
+ raw: errorBody
2092
+ });
2093
+ }
2094
+ return res;
2095
+ }
2096
+ function buildJSONL(model, requests) {
2097
+ return requests.map((req) => {
2098
+ const body = {
2099
+ model,
2100
+ messages: req.messages
2101
+ };
2102
+ if (req.max_tokens !== void 0) body.max_tokens = req.max_tokens;
2103
+ if (req.temperature !== void 0) body.temperature = req.temperature;
2104
+ if (req.top_p !== void 0) body.top_p = req.top_p;
2105
+ if (req.stop !== void 0) body.stop = req.stop;
2106
+ if (req.response_format !== void 0) body.response_format = req.response_format;
2107
+ if (req.tools !== void 0) body.tools = req.tools;
2108
+ if (req.tool_choice !== void 0) body.tool_choice = req.tool_choice;
2109
+ return JSON.stringify({
2110
+ custom_id: req.custom_id,
2111
+ method: "POST",
2112
+ url: "/v1/chat/completions",
2113
+ body
2114
+ });
2115
+ }).join("\n");
2116
+ }
2117
+ function rePrefixId(id) {
2118
+ if (id && id.startsWith("chatcmpl-")) {
2119
+ return `gen-${id.substring(9)}`;
2120
+ }
2121
+ return id.startsWith("gen-") ? id : `gen-${id}`;
2122
+ }
2123
+ function translateOpenAIResponse(body) {
2124
+ return {
2125
+ id: rePrefixId(body.id || generateId()),
2126
+ object: "chat.completion",
2127
+ created: body.created || Math.floor(Date.now() / 1e3),
2128
+ model: `openai/${body.model}`,
2129
+ choices: body.choices,
2130
+ usage: body.usage
2131
+ };
2132
+ }
2133
+ function mapStatus(openaiStatus) {
2134
+ switch (openaiStatus) {
2135
+ case "validating":
2136
+ case "finalizing":
2137
+ return "processing";
2138
+ case "in_progress":
2139
+ return "processing";
2140
+ case "completed":
2141
+ return "completed";
2142
+ case "failed":
2143
+ return "failed";
2144
+ case "expired":
2145
+ return "failed";
2146
+ case "cancelled":
2147
+ case "cancelling":
2148
+ return "cancelled";
2149
+ default:
2150
+ return "pending";
2151
+ }
2152
+ }
2153
+ return {
2154
+ async createBatch(model, requests, options) {
2155
+ const jsonlContent = buildJSONL(model, requests);
2156
+ const blob = new Blob([jsonlContent], { type: "application/jsonl" });
2157
+ const formData = new FormData();
2158
+ formData.append("purpose", "batch");
2159
+ formData.append("file", blob, "batch_input.jsonl");
2160
+ const uploadRes = await apiRequest("/files", { method: "POST", formData });
2161
+ const fileData = await uploadRes.json();
2162
+ const inputFileId = fileData.id;
2163
+ const batchRes = await apiRequest("/batches", {
2164
+ method: "POST",
2165
+ body: {
2166
+ input_file_id: inputFileId,
2167
+ endpoint: "/v1/chat/completions",
2168
+ completion_window: "24h",
2169
+ metadata: options?.metadata
2170
+ }
2171
+ });
2172
+ const batchData = await batchRes.json();
2173
+ return {
2174
+ providerBatchId: batchData.id,
2175
+ metadata: {
2176
+ input_file_id: inputFileId,
2177
+ openai_status: batchData.status
2178
+ }
2179
+ };
2180
+ },
2181
+ async pollBatch(providerBatchId) {
2182
+ const res = await apiRequest(`/batches/${providerBatchId}`);
2183
+ const data = await res.json();
2184
+ const requestCounts = data.request_counts || {};
2185
+ return {
2186
+ status: mapStatus(data.status),
2187
+ total: requestCounts.total || 0,
2188
+ completed: requestCounts.completed || 0,
2189
+ failed: requestCounts.failed || 0
2190
+ };
2191
+ },
2192
+ async getBatchResults(providerBatchId) {
2193
+ const batchRes = await apiRequest(`/batches/${providerBatchId}`);
2194
+ const batchData = await batchRes.json();
2195
+ const results = [];
2196
+ if (batchData.output_file_id) {
2197
+ const outputRes = await apiRequest(`/files/${batchData.output_file_id}/content`);
2198
+ const outputText = await outputRes.text();
2199
+ for (const line of outputText.trim().split("\n")) {
2200
+ if (!line) continue;
2201
+ const item = JSON.parse(line);
2202
+ if (item.response?.status_code === 200) {
2203
+ results.push({
2204
+ custom_id: item.custom_id,
2205
+ status: "success",
2206
+ response: translateOpenAIResponse(item.response.body),
2207
+ error: null
2208
+ });
2209
+ } else {
2210
+ results.push({
2211
+ custom_id: item.custom_id,
2212
+ status: "error",
2213
+ response: null,
2214
+ error: {
2215
+ code: item.response?.status_code || 500,
2216
+ message: item.error?.message || item.response?.body?.error?.message || "Unknown error"
2217
+ }
2218
+ });
2219
+ }
2220
+ }
2221
+ }
2222
+ if (batchData.error_file_id) {
2223
+ const errorRes = await apiRequest(`/files/${batchData.error_file_id}/content`);
2224
+ const errorText = await errorRes.text();
2225
+ for (const line of errorText.trim().split("\n")) {
2226
+ if (!line) continue;
2227
+ const item = JSON.parse(line);
2228
+ const existing = results.find((r) => r.custom_id === item.custom_id);
2229
+ if (!existing) {
2230
+ results.push({
2231
+ custom_id: item.custom_id,
2232
+ status: "error",
2233
+ response: null,
2234
+ error: {
2235
+ code: item.response?.status_code || 500,
2236
+ message: item.error?.message || "Batch item error"
2237
+ }
2238
+ });
2239
+ }
2240
+ }
2241
+ }
2242
+ return results;
2243
+ },
2244
+ async cancelBatch(providerBatchId) {
2245
+ await apiRequest(`/batches/${providerBatchId}/cancel`, { method: "POST" });
2246
+ }
2247
+ };
2248
+ }
2249
+
2250
+ // src/providers/anthropic-batch.ts
2251
+ var ANTHROPIC_API_BASE2 = "https://api.anthropic.com/v1";
2252
+ var ANTHROPIC_VERSION2 = "2023-06-01";
2253
+ var DEFAULT_MAX_TOKENS2 = 4096;
2254
+ function createAnthropicBatchAdapter(apiKey) {
2255
+ async function apiRequest(path2, options = {}) {
2256
+ const headers = {
2257
+ "x-api-key": apiKey,
2258
+ "anthropic-version": ANTHROPIC_VERSION2,
2259
+ "Content-Type": "application/json"
2260
+ };
2261
+ const res = await fetch(`${ANTHROPIC_API_BASE2}${path2}`, {
2262
+ method: options.method || "GET",
2263
+ headers,
2264
+ body: options.body ? JSON.stringify(options.body) : void 0
2265
+ });
2266
+ if (!res.ok) {
2267
+ let errorBody;
2268
+ try {
2269
+ errorBody = await res.json();
2270
+ } catch {
2271
+ errorBody = { message: res.statusText };
2272
+ }
2273
+ const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
2274
+ throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
2275
+ provider_name: "anthropic",
2276
+ raw: errorBody
2277
+ });
2278
+ }
2279
+ return res;
2280
+ }
2281
+ function translateToAnthropicParams(model, req) {
2282
+ const params = {
2283
+ model,
2284
+ max_tokens: req.max_tokens || DEFAULT_MAX_TOKENS2
2285
+ };
2286
+ const systemMessages = req.messages.filter((m) => m.role === "system");
2287
+ const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
2288
+ if (systemMessages.length > 0) {
2289
+ params.system = systemMessages.map((m) => typeof m.content === "string" ? m.content : "").join("\n");
2290
+ }
2291
+ params.messages = nonSystemMessages.map((m) => ({
2292
+ role: m.role === "tool" ? "user" : m.role,
2293
+ content: m.tool_call_id ? [{ type: "tool_result", tool_use_id: m.tool_call_id, content: typeof m.content === "string" ? m.content : "" }] : m.content
2294
+ }));
2295
+ if (req.temperature !== void 0) params.temperature = req.temperature;
2296
+ if (req.top_p !== void 0) params.top_p = req.top_p;
2297
+ if (req.top_k !== void 0) params.top_k = req.top_k;
2298
+ if (req.stop !== void 0) params.stop_sequences = Array.isArray(req.stop) ? req.stop : [req.stop];
2299
+ if (req.tools && req.tools.length > 0) {
2300
+ params.tools = req.tools.map((t) => ({
2301
+ name: t.function.name,
2302
+ description: t.function.description || "",
2303
+ input_schema: t.function.parameters || { type: "object", properties: {} }
2304
+ }));
2305
+ if (req.tool_choice) {
2306
+ if (req.tool_choice === "auto") {
2307
+ params.tool_choice = { type: "auto" };
2308
+ } else if (req.tool_choice === "required") {
2309
+ params.tool_choice = { type: "any" };
2310
+ } else if (req.tool_choice === "none") {
2311
+ delete params.tools;
2312
+ } else if (typeof req.tool_choice === "object") {
2313
+ params.tool_choice = { type: "tool", name: req.tool_choice.function.name };
2314
+ }
2315
+ }
2316
+ }
2317
+ if (req.response_format) {
2318
+ if (req.response_format.type === "json_object" || req.response_format.type === "json_schema") {
2319
+ const jsonInstruction = "Respond with valid JSON only. Do not include any text outside the JSON object.";
2320
+ params.system = params.system ? `${jsonInstruction}
2321
+
2322
+ ${params.system}` : jsonInstruction;
2323
+ }
2324
+ }
2325
+ return params;
2326
+ }
2327
+ function mapStopReason(reason) {
2328
+ switch (reason) {
2329
+ case "end_turn":
2330
+ return "stop";
2331
+ case "max_tokens":
2332
+ return "length";
2333
+ case "tool_use":
2334
+ return "tool_calls";
2335
+ case "stop_sequence":
2336
+ return "stop";
2337
+ default:
2338
+ return "stop";
2339
+ }
2340
+ }
2341
+ function translateAnthropicMessage(msg) {
2342
+ let content = "";
2343
+ const toolCalls = [];
2344
+ for (const block of msg.content || []) {
2345
+ if (block.type === "text") {
2346
+ content += block.text;
2347
+ } else if (block.type === "tool_use") {
2348
+ toolCalls.push({
2349
+ id: block.id,
2350
+ type: "function",
2351
+ function: {
2352
+ name: block.name,
2353
+ arguments: JSON.stringify(block.input)
2354
+ }
2355
+ });
2356
+ }
2357
+ }
2358
+ const message = { role: "assistant", content };
2359
+ if (toolCalls.length > 0) {
2360
+ message.tool_calls = toolCalls;
2361
+ }
2362
+ return {
2363
+ id: generateId(),
2364
+ object: "chat.completion",
2365
+ created: Math.floor(Date.now() / 1e3),
2366
+ model: `anthropic/${msg.model}`,
2367
+ choices: [{
2368
+ index: 0,
2369
+ message,
2370
+ finish_reason: mapStopReason(msg.stop_reason)
2371
+ }],
2372
+ usage: {
2373
+ prompt_tokens: msg.usage?.input_tokens || 0,
2374
+ completion_tokens: msg.usage?.output_tokens || 0,
2375
+ total_tokens: (msg.usage?.input_tokens || 0) + (msg.usage?.output_tokens || 0)
2376
+ }
2377
+ };
2378
+ }
2379
+ return {
2380
+ async createBatch(model, requests, _options) {
2381
+ const batchRequests = requests.map((req) => ({
2382
+ custom_id: req.custom_id,
2383
+ params: translateToAnthropicParams(model, req)
2384
+ }));
2385
+ const res = await apiRequest("/messages/batches", {
2386
+ method: "POST",
2387
+ body: { requests: batchRequests }
2388
+ });
2389
+ const data = await res.json();
2390
+ return {
2391
+ providerBatchId: data.id,
2392
+ metadata: {
2393
+ anthropic_type: data.type,
2394
+ created_at: data.created_at
2395
+ }
2396
+ };
2397
+ },
2398
+ async pollBatch(providerBatchId) {
2399
+ const res = await apiRequest(`/messages/batches/${providerBatchId}`);
2400
+ const data = await res.json();
2401
+ const counts = data.request_counts || {};
2402
+ const total = (counts.processing || 0) + (counts.succeeded || 0) + (counts.errored || 0) + (counts.canceled || 0) + (counts.expired || 0);
2403
+ let status;
2404
+ if (data.processing_status === "ended") {
2405
+ if (counts.succeeded === 0 && (counts.errored > 0 || counts.expired > 0 || counts.canceled > 0)) {
2406
+ status = "failed";
2407
+ } else if (data.cancel_initiated_at) {
2408
+ status = "cancelled";
2409
+ } else {
2410
+ status = "completed";
2411
+ }
2412
+ } else {
2413
+ status = "processing";
2414
+ }
2415
+ return {
2416
+ status,
2417
+ total,
2418
+ completed: counts.succeeded || 0,
2419
+ failed: (counts.errored || 0) + (counts.expired || 0) + (counts.canceled || 0)
2420
+ };
2421
+ },
2422
+ async getBatchResults(providerBatchId) {
2423
+ const res = await apiRequest(`/messages/batches/${providerBatchId}/results`);
2424
+ const text = await res.text();
2425
+ const results = [];
2426
+ for (const line of text.trim().split("\n")) {
2427
+ if (!line) continue;
2428
+ const item = JSON.parse(line);
2429
+ if (item.result?.type === "succeeded") {
2430
+ results.push({
2431
+ custom_id: item.custom_id,
2432
+ status: "success",
2433
+ response: translateAnthropicMessage(item.result.message),
2434
+ error: null
2435
+ });
2436
+ } else {
2437
+ const errorType = item.result?.type || "unknown";
2438
+ const errorMsg = item.result?.error?.message || `Batch item ${errorType}`;
2439
+ results.push({
2440
+ custom_id: item.custom_id,
2441
+ status: "error",
2442
+ response: null,
2443
+ error: {
2444
+ code: errorType === "expired" ? 408 : 500,
2445
+ message: errorMsg
2446
+ }
2447
+ });
2448
+ }
2449
+ }
2450
+ return results;
2451
+ },
2452
+ async cancelBatch(providerBatchId) {
2453
+ await apiRequest(`/messages/batches/${providerBatchId}/cancel`, { method: "POST" });
2454
+ }
2455
+ };
2456
+ }
2457
+
1801
2458
  // src/client.ts
1802
2459
  var AnyModel = class {
1803
2460
  registry;
@@ -1813,6 +2470,9 @@ var AnyModel = class {
1813
2470
  constructor(config = {}) {
1814
2471
  this.config = resolveConfig(config);
1815
2472
  this.registry = new ProviderRegistry();
2473
+ if (this.config.io) {
2474
+ configureFsIO(this.config.io);
2475
+ }
1816
2476
  this.registerProviders();
1817
2477
  this.router = new Router(this.registry, this.config.aliases, this.config);
1818
2478
  this.chat = {
@@ -1862,8 +2522,10 @@ var AnyModel = class {
1862
2522
  };
1863
2523
  this.batchManager = new BatchManager(this.router, {
1864
2524
  dir: this.config.batch?.dir,
1865
- concurrency: this.config.batch?.concurrencyFallback
2525
+ concurrency: this.config.batch?.concurrencyFallback,
2526
+ pollInterval: this.config.batch?.pollInterval
1866
2527
  });
2528
+ this.registerBatchAdapters();
1867
2529
  this.batches = {
1868
2530
  create: (request) => this.batchManager.create(request),
1869
2531
  createAndPoll: (request, options) => this.batchManager.createAndPoll(request, options),
@@ -1915,6 +2577,17 @@ var AnyModel = class {
1915
2577
  }
1916
2578
  }
1917
2579
  }
2580
+ registerBatchAdapters() {
2581
+ const config = this.config;
2582
+ const openaiKey = config.openai?.apiKey || process.env.OPENAI_API_KEY;
2583
+ if (openaiKey) {
2584
+ this.batchManager.registerBatchAdapter("openai", createOpenAIBatchAdapter(openaiKey));
2585
+ }
2586
+ const anthropicKey = config.anthropic?.apiKey || process.env.ANTHROPIC_API_KEY;
2587
+ if (anthropicKey) {
2588
+ this.batchManager.registerBatchAdapter("anthropic", createAnthropicBatchAdapter(anthropicKey));
2589
+ }
2590
+ }
1918
2591
  applyDefaults(request) {
1919
2592
  const defaults = this.config.defaults;
1920
2593
  if (!defaults) return request;
@@ -1942,10 +2615,10 @@ var AnyModel = class {
1942
2615
 
1943
2616
  // src/server.ts
1944
2617
  function parseBody(req) {
1945
- return new Promise((resolve3, reject) => {
2618
+ return new Promise((resolve2, reject) => {
1946
2619
  const chunks = [];
1947
2620
  req.on("data", (chunk) => chunks.push(chunk));
1948
- req.on("end", () => resolve3(Buffer.concat(chunks).toString()));
2621
+ req.on("end", () => resolve2(Buffer.concat(chunks).toString()));
1949
2622
  req.on("error", reject);
1950
2623
  });
1951
2624
  }
@@ -1975,7 +2648,7 @@ function createAnyModelServer(options = {}) {
1975
2648
  const basePath = "/api/v1";
1976
2649
  const server = createServer(async (req, res) => {
1977
2650
  const url = new URL(req.url || "/", `http://${req.headers.host}`);
1978
- const path = url.pathname;
2651
+ const path2 = url.pathname;
1979
2652
  res.setHeader("Access-Control-Allow-Origin", "*");
1980
2653
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
1981
2654
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
@@ -1985,11 +2658,11 @@ function createAnyModelServer(options = {}) {
1985
2658
  return;
1986
2659
  }
1987
2660
  try {
1988
- if (path === "/health" && req.method === "GET") {
2661
+ if (path2 === "/health" && req.method === "GET") {
1989
2662
  sendJSON(res, 200, { status: "ok" });
1990
2663
  return;
1991
2664
  }
1992
- if (path === `${basePath}/chat/completions` && req.method === "POST") {
2665
+ if (path2 === `${basePath}/chat/completions` && req.method === "POST") {
1993
2666
  const body = JSON.parse(await parseBody(req));
1994
2667
  if (body.stream) {
1995
2668
  const stream = await client.chat.completions.create(body);
@@ -2000,14 +2673,14 @@ function createAnyModelServer(options = {}) {
2000
2673
  }
2001
2674
  return;
2002
2675
  }
2003
- if (path === `${basePath}/models` && req.method === "GET") {
2676
+ if (path2 === `${basePath}/models` && req.method === "GET") {
2004
2677
  const provider = url.searchParams.get("provider") || void 0;
2005
2678
  const models = await client.models.list({ provider });
2006
2679
  sendJSON(res, 200, { object: "list", data: models });
2007
2680
  return;
2008
2681
  }
2009
- if (path.startsWith(`${basePath}/generation/`) && req.method === "GET") {
2010
- const id = path.substring(`${basePath}/generation/`.length);
2682
+ if (path2.startsWith(`${basePath}/generation/`) && req.method === "GET") {
2683
+ const id = path2.substring(`${basePath}/generation/`.length);
2011
2684
  const stats = client.generation.get(id);
2012
2685
  if (!stats) {
2013
2686
  sendError(res, 404, `Generation ${id} not found`);
@@ -2016,26 +2689,26 @@ function createAnyModelServer(options = {}) {
2016
2689
  sendJSON(res, 200, stats);
2017
2690
  return;
2018
2691
  }
2019
- if (path === `${basePath}/batches` && req.method === "POST") {
2692
+ if (path2 === `${basePath}/batches` && req.method === "POST") {
2020
2693
  const body = JSON.parse(await parseBody(req));
2021
2694
  const batch = await client.batches.create(body);
2022
2695
  sendJSON(res, 201, batch);
2023
2696
  return;
2024
2697
  }
2025
- if (path === `${basePath}/batches` && req.method === "GET") {
2026
- const batches = client.batches.list();
2698
+ if (path2 === `${basePath}/batches` && req.method === "GET") {
2699
+ const batches = await client.batches.list();
2027
2700
  sendJSON(res, 200, { object: "list", data: batches });
2028
2701
  return;
2029
2702
  }
2030
- if (path.startsWith(`${basePath}/batches/`) && req.method === "GET") {
2031
- const parts = path.substring(`${basePath}/batches/`.length).split("/");
2703
+ if (path2.startsWith(`${basePath}/batches/`) && req.method === "GET") {
2704
+ const parts = path2.substring(`${basePath}/batches/`.length).split("/");
2032
2705
  const id = parts[0];
2033
2706
  if (parts[1] === "results") {
2034
- const results = client.batches.results(id);
2707
+ const results = await client.batches.results(id);
2035
2708
  sendJSON(res, 200, results);
2036
2709
  return;
2037
2710
  }
2038
- const batch = client.batches.get(id);
2711
+ const batch = await client.batches.get(id);
2039
2712
  if (!batch) {
2040
2713
  sendError(res, 404, `Batch ${id} not found`);
2041
2714
  return;
@@ -2043,16 +2716,16 @@ function createAnyModelServer(options = {}) {
2043
2716
  sendJSON(res, 200, batch);
2044
2717
  return;
2045
2718
  }
2046
- if (path.startsWith(`${basePath}/batches/`) && req.method === "POST") {
2047
- const parts = path.substring(`${basePath}/batches/`.length).split("/");
2719
+ if (path2.startsWith(`${basePath}/batches/`) && req.method === "POST") {
2720
+ const parts = path2.substring(`${basePath}/batches/`.length).split("/");
2048
2721
  const id = parts[0];
2049
2722
  if (parts[1] === "cancel") {
2050
- const batch = client.batches.cancel(id);
2723
+ const batch = await client.batches.cancel(id);
2051
2724
  sendJSON(res, 200, batch);
2052
2725
  return;
2053
2726
  }
2054
2727
  }
2055
- sendError(res, 404, `Not found: ${path}`);
2728
+ sendError(res, 404, `Not found: ${path2}`);
2056
2729
  } catch (err) {
2057
2730
  const code = err?.code || 500;
2058
2731
  const message = err?.message || "Internal server error";