@probeo/anymodel 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +5 -8
  2. package/dist/cli.cjs +1016 -110
  3. package/dist/cli.cjs.map +1 -1
  4. package/dist/cli.d.ts +0 -2
  5. package/dist/cli.js +2972 -30
  6. package/dist/cli.js.map +1 -1
  7. package/dist/index.cjs +1037 -112
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.cts +106 -22
  10. package/dist/index.d.ts +632 -14
  11. package/dist/index.js +2969 -15
  12. package/dist/index.js.map +1 -1
  13. package/package.json +1 -1
  14. package/dist/batch/index.d.ts +0 -4
  15. package/dist/batch/index.d.ts.map +0 -1
  16. package/dist/batch/index.js +0 -3
  17. package/dist/batch/index.js.map +0 -1
  18. package/dist/batch/manager.d.ts +0 -72
  19. package/dist/batch/manager.d.ts.map +0 -1
  20. package/dist/batch/manager.js +0 -328
  21. package/dist/batch/manager.js.map +0 -1
  22. package/dist/batch/store.d.ts +0 -54
  23. package/dist/batch/store.d.ts.map +0 -1
  24. package/dist/batch/store.js +0 -109
  25. package/dist/batch/store.js.map +0 -1
  26. package/dist/cli.d.ts.map +0 -1
  27. package/dist/client.d.ts +0 -42
  28. package/dist/client.d.ts.map +0 -1
  29. package/dist/client.js +0 -181
  30. package/dist/client.js.map +0 -1
  31. package/dist/config.d.ts +0 -6
  32. package/dist/config.d.ts.map +0 -1
  33. package/dist/config.js +0 -120
  34. package/dist/config.js.map +0 -1
  35. package/dist/index.d.ts.map +0 -1
  36. package/dist/providers/adapter.d.ts +0 -33
  37. package/dist/providers/adapter.d.ts.map +0 -1
  38. package/dist/providers/adapter.js +0 -2
  39. package/dist/providers/adapter.js.map +0 -1
  40. package/dist/providers/anthropic-batch.d.ts +0 -3
  41. package/dist/providers/anthropic-batch.d.ts.map +0 -1
  42. package/dist/providers/anthropic-batch.js +0 -228
  43. package/dist/providers/anthropic-batch.js.map +0 -1
  44. package/dist/providers/anthropic.d.ts +0 -3
  45. package/dist/providers/anthropic.d.ts.map +0 -1
  46. package/dist/providers/anthropic.js +0 -358
  47. package/dist/providers/anthropic.js.map +0 -1
  48. package/dist/providers/custom.d.ts +0 -8
  49. package/dist/providers/custom.d.ts.map +0 -1
  50. package/dist/providers/custom.js +0 -61
  51. package/dist/providers/custom.js.map +0 -1
  52. package/dist/providers/google.d.ts +0 -3
  53. package/dist/providers/google.d.ts.map +0 -1
  54. package/dist/providers/google.js +0 -331
  55. package/dist/providers/google.js.map +0 -1
  56. package/dist/providers/index.d.ts +0 -6
  57. package/dist/providers/index.d.ts.map +0 -1
  58. package/dist/providers/index.js +0 -5
  59. package/dist/providers/index.js.map +0 -1
  60. package/dist/providers/openai-batch.d.ts +0 -3
  61. package/dist/providers/openai-batch.d.ts.map +0 -1
  62. package/dist/providers/openai-batch.js +0 -208
  63. package/dist/providers/openai-batch.js.map +0 -1
  64. package/dist/providers/openai.d.ts +0 -3
  65. package/dist/providers/openai.d.ts.map +0 -1
  66. package/dist/providers/openai.js +0 -221
  67. package/dist/providers/openai.js.map +0 -1
  68. package/dist/providers/registry.d.ts +0 -10
  69. package/dist/providers/registry.d.ts.map +0 -1
  70. package/dist/providers/registry.js +0 -27
  71. package/dist/providers/registry.js.map +0 -1
  72. package/dist/router.d.ts +0 -29
  73. package/dist/router.d.ts.map +0 -1
  74. package/dist/router.js +0 -212
  75. package/dist/router.js.map +0 -1
  76. package/dist/server.d.ts +0 -10
  77. package/dist/server.d.ts.map +0 -1
  78. package/dist/server.js +0 -149
  79. package/dist/server.js.map +0 -1
  80. package/dist/types.d.ts +0 -283
  81. package/dist/types.d.ts.map +0 -1
  82. package/dist/types.js +0 -21
  83. package/dist/types.js.map +0 -1
  84. package/dist/utils/fs-io.d.ts +0 -40
  85. package/dist/utils/fs-io.d.ts.map +0 -1
  86. package/dist/utils/fs-io.js +0 -203
  87. package/dist/utils/fs-io.js.map +0 -1
  88. package/dist/utils/generation-stats.d.ts +0 -25
  89. package/dist/utils/generation-stats.d.ts.map +0 -1
  90. package/dist/utils/generation-stats.js +0 -46
  91. package/dist/utils/generation-stats.js.map +0 -1
  92. package/dist/utils/id.d.ts +0 -2
  93. package/dist/utils/id.d.ts.map +0 -1
  94. package/dist/utils/id.js +0 -6
  95. package/dist/utils/id.js.map +0 -1
  96. package/dist/utils/model-parser.d.ts +0 -6
  97. package/dist/utils/model-parser.d.ts.map +0 -1
  98. package/dist/utils/model-parser.js +0 -21
  99. package/dist/utils/model-parser.js.map +0 -1
  100. package/dist/utils/rate-limiter.d.ts +0 -36
  101. package/dist/utils/rate-limiter.d.ts.map +0 -1
  102. package/dist/utils/rate-limiter.js +0 -80
  103. package/dist/utils/rate-limiter.js.map +0 -1
  104. package/dist/utils/retry.d.ts +0 -7
  105. package/dist/utils/retry.d.ts.map +0 -1
  106. package/dist/utils/retry.js +0 -54
  107. package/dist/utils/retry.js.map +0 -1
  108. package/dist/utils/transforms.d.ts +0 -7
  109. package/dist/utils/transforms.d.ts.map +0 -1
  110. package/dist/utils/transforms.js +0 -66
  111. package/dist/utils/transforms.js.map +0 -1
  112. package/dist/utils/validate.d.ts +0 -3
  113. package/dist/utils/validate.d.ts.map +0 -1
  114. package/dist/utils/validate.js +0 -31
  115. package/dist/utils/validate.js.map +0 -1
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",
@@ -650,7 +671,19 @@ function createOpenAIAdapter(apiKey, baseURL) {
650
671
  async listModels() {
651
672
  const res = await makeRequest("/models", void 0, "GET");
652
673
  const data = await res.json();
653
- return (data.data || []).filter((m) => m.id.startsWith("gpt-") || m.id.startsWith("o1") || m.id.startsWith("o3") || m.id.startsWith("o4") || m.id.startsWith("chatgpt-")).map((m) => ({
674
+ return (data.data || []).filter((m) => {
675
+ const id = m.id;
676
+ if (id.includes("embedding")) return false;
677
+ if (id.includes("whisper")) return false;
678
+ if (id.includes("tts")) return false;
679
+ if (id.includes("dall-e")) return false;
680
+ if (id.includes("davinci")) return false;
681
+ if (id.includes("babbage")) return false;
682
+ if (id.includes("moderation")) return false;
683
+ if (id.includes("realtime")) return false;
684
+ if (id.startsWith("ft:")) return false;
685
+ return id.startsWith("gpt-") || id.startsWith("o1") || id.startsWith("o3") || id.startsWith("o4") || id.startsWith("chatgpt-");
686
+ }).map((m) => ({
654
687
  id: `openai/${m.id}`,
655
688
  name: m.id,
656
689
  created: m.created,
@@ -674,6 +707,9 @@ function createOpenAIAdapter(apiKey, baseURL) {
674
707
  supportsParameter(param) {
675
708
  return SUPPORTED_PARAMS.has(param);
676
709
  },
710
+ supportsBatch() {
711
+ return true;
712
+ },
677
713
  async sendRequest(request) {
678
714
  const body = buildRequestBody(request);
679
715
  const res = await makeRequest("/chat/completions", body);
@@ -717,14 +753,19 @@ var SUPPORTED_PARAMS2 = /* @__PURE__ */ new Set([
717
753
  "response_format"
718
754
  ]);
719
755
  var FALLBACK_MODELS = [
720
- { id: "anthropic/claude-opus-4-6", name: "Claude Opus 4.6", created: 0, description: "Most capable model", context_length: 2e5, pricing: { prompt: "0.000005", completion: "0.000025" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image"], output_modalities: ["text"], tokenizer: "claude" }, top_provider: { context_length: 2e5, max_completion_tokens: 32768, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS2) },
756
+ // Claude 4.6
757
+ { id: "anthropic/claude-opus-4-6", name: "Claude Opus 4.6", created: 0, description: "Most capable model", context_length: 2e5, pricing: { prompt: "0.000015", completion: "0.000075" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image"], output_modalities: ["text"], tokenizer: "claude" }, top_provider: { context_length: 2e5, max_completion_tokens: 32768, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS2) },
721
758
  { id: "anthropic/claude-sonnet-4-6", name: "Claude Sonnet 4.6", created: 0, description: "Best balance of speed and capability", context_length: 2e5, pricing: { prompt: "0.000003", completion: "0.000015" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image"], output_modalities: ["text"], tokenizer: "claude" }, top_provider: { context_length: 2e5, max_completion_tokens: 16384, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS2) },
759
+ // Claude 4.5
722
760
  { id: "anthropic/claude-sonnet-4-5-20251022", name: "Claude Sonnet 4.5", created: 0, description: "Previous generation balanced model", context_length: 2e5, pricing: { prompt: "0.000003", completion: "0.000015" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image"], output_modalities: ["text"], tokenizer: "claude" }, top_provider: { context_length: 2e5, max_completion_tokens: 16384, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS2) },
723
- { id: "anthropic/claude-haiku-4-5", name: "Claude Haiku 4.5", created: 0, description: "Fastest and most compact", context_length: 2e5, pricing: { prompt: "0.000001", completion: "0.000005" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image"], output_modalities: ["text"], tokenizer: "claude" }, top_provider: { context_length: 2e5, max_completion_tokens: 8192, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS2) }
761
+ { id: "anthropic/claude-haiku-4-5", name: "Claude Haiku 4.5", created: 0, description: "Fast and compact", context_length: 2e5, pricing: { prompt: "0.000001", completion: "0.000005" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image"], output_modalities: ["text"], tokenizer: "claude" }, top_provider: { context_length: 2e5, max_completion_tokens: 8192, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS2) },
762
+ // Claude 3.5
763
+ { id: "anthropic/claude-3-5-sonnet-20241022", name: "Claude 3.5 Sonnet", created: 0, description: "Legacy balanced model", context_length: 2e5, pricing: { prompt: "0.000003", completion: "0.000015" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image"], output_modalities: ["text"], tokenizer: "claude" }, top_provider: { context_length: 2e5, max_completion_tokens: 8192, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS2) },
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) }
724
765
  ];
725
766
  function createAnthropicAdapter(apiKey) {
726
- async function makeRequest(path, body, stream = false) {
727
- 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}`, {
728
769
  method: "POST",
729
770
  headers: {
730
771
  "Content-Type": "application/json",
@@ -1018,6 +1059,9 @@ ${body.system}` : jsonInstruction;
1018
1059
  supportsParameter(param) {
1019
1060
  return SUPPORTED_PARAMS2.has(param);
1020
1061
  },
1062
+ supportsBatch() {
1063
+ return true;
1064
+ },
1021
1065
  async sendRequest(request) {
1022
1066
  const body = translateRequest(request);
1023
1067
  const res = await makeRequest("/messages", body);
@@ -1052,8 +1096,15 @@ var SUPPORTED_PARAMS3 = /* @__PURE__ */ new Set([
1052
1096
  "response_format"
1053
1097
  ]);
1054
1098
  var FALLBACK_MODELS2 = [
1099
+ // Gemini 2.5
1055
1100
  { id: "google/gemini-2.5-pro", name: "Gemini 2.5 Pro", created: 0, description: "Most capable Gemini model", context_length: 1048576, pricing: { prompt: "0.00000125", completion: "0.000005" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image", "video", "audio"], output_modalities: ["text"], tokenizer: "gemini" }, top_provider: { context_length: 1048576, max_completion_tokens: 65536, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS3) },
1056
- { id: "google/gemini-2.5-flash", name: "Gemini 2.5 Flash", created: 0, description: "Fast and efficient", context_length: 1048576, pricing: { prompt: "0.00000015", completion: "0.0000006" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image", "video", "audio"], output_modalities: ["text"], tokenizer: "gemini" }, top_provider: { context_length: 1048576, max_completion_tokens: 65536, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS3) }
1101
+ { id: "google/gemini-2.5-flash", name: "Gemini 2.5 Flash", created: 0, description: "Fast and efficient", context_length: 1048576, pricing: { prompt: "0.00000015", completion: "0.0000006" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image", "video", "audio"], output_modalities: ["text"], tokenizer: "gemini" }, top_provider: { context_length: 1048576, max_completion_tokens: 65536, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS3) },
1102
+ // Gemini 2.0
1103
+ { id: "google/gemini-2.0-flash", name: "Gemini 2.0 Flash", created: 0, description: "Fast multimodal model", context_length: 1048576, pricing: { prompt: "0.0000001", completion: "0.0000004" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image", "video", "audio"], output_modalities: ["text"], tokenizer: "gemini" }, top_provider: { context_length: 1048576, max_completion_tokens: 65536, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS3) },
1104
+ { id: "google/gemini-2.0-flash-lite", name: "Gemini 2.0 Flash Lite", created: 0, description: "Lightweight and fast", context_length: 1048576, pricing: { prompt: "0.00000005", completion: "0.0000002" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image", "video", "audio"], output_modalities: ["text"], tokenizer: "gemini" }, top_provider: { context_length: 1048576, max_completion_tokens: 65536, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS3) },
1105
+ // Gemini 1.5
1106
+ { id: "google/gemini-1.5-pro", name: "Gemini 1.5 Pro", created: 0, description: "Previous generation pro model", context_length: 2097152, pricing: { prompt: "0.00000125", completion: "0.000005" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image", "video", "audio"], output_modalities: ["text"], tokenizer: "gemini" }, top_provider: { context_length: 2097152, max_completion_tokens: 8192, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS3) },
1107
+ { id: "google/gemini-1.5-flash", name: "Gemini 1.5 Flash", created: 0, description: "Previous generation flash model", context_length: 1048576, pricing: { prompt: "0.000000075", completion: "0.0000003" }, architecture: { modality: "text+image->text", input_modalities: ["text", "image", "video", "audio"], output_modalities: ["text"], tokenizer: "gemini" }, top_provider: { context_length: 1048576, max_completion_tokens: 8192, is_moderated: false }, supported_parameters: Array.from(SUPPORTED_PARAMS3) }
1057
1108
  ];
1058
1109
  function createGoogleAdapter(apiKey) {
1059
1110
  function getModelEndpoint(model, stream) {
@@ -1290,6 +1341,9 @@ function createGoogleAdapter(apiKey) {
1290
1341
  supportsParameter(param) {
1291
1342
  return SUPPORTED_PARAMS3.has(param);
1292
1343
  },
1344
+ supportsBatch() {
1345
+ return false;
1346
+ },
1293
1347
  async sendRequest(request) {
1294
1348
  const body = translateRequest(request);
1295
1349
  const url = getModelEndpoint(request.model, false);
@@ -1342,12 +1396,199 @@ function createGoogleAdapter(apiKey) {
1342
1396
  return adapter;
1343
1397
  }
1344
1398
 
1399
+ // src/providers/perplexity.ts
1400
+ var PERPLEXITY_API_BASE = "https://api.perplexity.ai";
1401
+ var SUPPORTED_PARAMS4 = /* @__PURE__ */ new Set([
1402
+ "temperature",
1403
+ "max_tokens",
1404
+ "top_p",
1405
+ "frequency_penalty",
1406
+ "presence_penalty",
1407
+ "stream",
1408
+ "stop",
1409
+ "response_format",
1410
+ "tools",
1411
+ "tool_choice"
1412
+ ]);
1413
+ var MODELS = [
1414
+ { id: "sonar", name: "Sonar", context: 128e3, maxOutput: 4096, modality: "text->text", inputModalities: ["text"] },
1415
+ { id: "sonar-pro", name: "Sonar Pro", context: 2e5, maxOutput: 8192, modality: "text->text", inputModalities: ["text"] },
1416
+ { id: "sonar-reasoning", name: "Sonar Reasoning", context: 128e3, maxOutput: 8192, modality: "text->text", inputModalities: ["text"] },
1417
+ { id: "sonar-reasoning-pro", name: "Sonar Reasoning Pro", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] },
1418
+ { id: "sonar-deep-research", name: "Sonar Deep Research", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] },
1419
+ { id: "r1-1776", name: "R1 1776", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] }
1420
+ ];
1421
+ function createPerplexityAdapter(apiKey) {
1422
+ async function makeRequest(path2, body, method = "POST") {
1423
+ const res = await fetch(`${PERPLEXITY_API_BASE}${path2}`, {
1424
+ method,
1425
+ headers: {
1426
+ "Content-Type": "application/json",
1427
+ "Authorization": `Bearer ${apiKey}`
1428
+ },
1429
+ body: body ? JSON.stringify(body) : void 0
1430
+ });
1431
+ if (!res.ok) {
1432
+ let errorBody;
1433
+ try {
1434
+ errorBody = await res.json();
1435
+ } catch {
1436
+ errorBody = { message: res.statusText };
1437
+ }
1438
+ const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
1439
+ throw new AnyModelError(mapErrorCode(res.status), msg, {
1440
+ provider_name: "perplexity",
1441
+ raw: errorBody
1442
+ });
1443
+ }
1444
+ return res;
1445
+ }
1446
+ function mapErrorCode(status) {
1447
+ if (status === 401 || status === 403) return 401;
1448
+ if (status === 429) return 429;
1449
+ if (status === 400 || status === 422) return 400;
1450
+ if (status >= 500) return 502;
1451
+ return status;
1452
+ }
1453
+ function rePrefixId(id) {
1454
+ if (id && id.startsWith("chatcmpl-")) {
1455
+ return `gen-${id.substring(9)}`;
1456
+ }
1457
+ return id.startsWith("gen-") ? id : `gen-${id}`;
1458
+ }
1459
+ function buildRequestBody(request) {
1460
+ const body = {
1461
+ model: request.model,
1462
+ messages: request.messages
1463
+ };
1464
+ if (request.temperature !== void 0) body.temperature = request.temperature;
1465
+ if (request.max_tokens !== void 0) body.max_tokens = request.max_tokens;
1466
+ if (request.top_p !== void 0) body.top_p = request.top_p;
1467
+ if (request.frequency_penalty !== void 0) body.frequency_penalty = request.frequency_penalty;
1468
+ if (request.presence_penalty !== void 0) body.presence_penalty = request.presence_penalty;
1469
+ if (request.stop !== void 0) body.stop = request.stop;
1470
+ if (request.stream !== void 0) body.stream = request.stream;
1471
+ if (request.response_format !== void 0) body.response_format = request.response_format;
1472
+ if (request.tools !== void 0) body.tools = request.tools;
1473
+ if (request.tool_choice !== void 0) body.tool_choice = request.tool_choice;
1474
+ return body;
1475
+ }
1476
+ const adapter = {
1477
+ name: "perplexity",
1478
+ translateRequest(request) {
1479
+ return buildRequestBody(request);
1480
+ },
1481
+ translateResponse(response) {
1482
+ const r = response;
1483
+ const result = {
1484
+ id: rePrefixId(r.id),
1485
+ object: "chat.completion",
1486
+ created: r.created,
1487
+ model: `perplexity/${r.model}`,
1488
+ choices: r.choices,
1489
+ usage: r.usage
1490
+ };
1491
+ if (r.citations && result.choices?.[0]?.message) {
1492
+ result.citations = r.citations;
1493
+ }
1494
+ return result;
1495
+ },
1496
+ async *translateStream(stream) {
1497
+ const reader = stream.getReader();
1498
+ const decoder = new TextDecoder();
1499
+ let buffer = "";
1500
+ try {
1501
+ while (true) {
1502
+ const { done, value } = await reader.read();
1503
+ if (done) break;
1504
+ buffer += decoder.decode(value, { stream: true });
1505
+ const lines = buffer.split("\n");
1506
+ buffer = lines.pop() || "";
1507
+ for (const line of lines) {
1508
+ const trimmed = line.trim();
1509
+ if (!trimmed || trimmed.startsWith(":")) continue;
1510
+ if (trimmed === "data: [DONE]") return;
1511
+ if (trimmed.startsWith("data: ")) {
1512
+ const json = JSON.parse(trimmed.substring(6));
1513
+ json.id = rePrefixId(json.id);
1514
+ json.model = `perplexity/${json.model}`;
1515
+ yield json;
1516
+ }
1517
+ }
1518
+ }
1519
+ } finally {
1520
+ reader.releaseLock();
1521
+ }
1522
+ },
1523
+ translateError(error) {
1524
+ if (error instanceof AnyModelError) {
1525
+ return { code: error.code, message: error.message, metadata: error.metadata };
1526
+ }
1527
+ const err = error;
1528
+ const status = err?.status || err?.code || 500;
1529
+ return {
1530
+ code: mapErrorCode(status),
1531
+ message: err?.message || "Unknown Perplexity error",
1532
+ metadata: { provider_name: "perplexity", raw: error }
1533
+ };
1534
+ },
1535
+ async listModels() {
1536
+ return MODELS.map((m) => ({
1537
+ id: `perplexity/${m.id}`,
1538
+ name: m.name,
1539
+ created: 0,
1540
+ description: "",
1541
+ context_length: m.context,
1542
+ pricing: { prompt: "0", completion: "0" },
1543
+ architecture: {
1544
+ modality: m.modality,
1545
+ input_modalities: m.inputModalities,
1546
+ output_modalities: ["text"],
1547
+ tokenizer: "unknown"
1548
+ },
1549
+ top_provider: {
1550
+ context_length: m.context,
1551
+ max_completion_tokens: m.maxOutput,
1552
+ is_moderated: false
1553
+ },
1554
+ supported_parameters: Array.from(SUPPORTED_PARAMS4)
1555
+ }));
1556
+ },
1557
+ supportsParameter(param) {
1558
+ return SUPPORTED_PARAMS4.has(param);
1559
+ },
1560
+ supportsBatch() {
1561
+ return false;
1562
+ },
1563
+ async sendRequest(request) {
1564
+ const body = buildRequestBody(request);
1565
+ const res = await makeRequest("/chat/completions", body);
1566
+ const json = await res.json();
1567
+ return adapter.translateResponse(json);
1568
+ },
1569
+ async sendStreamingRequest(request) {
1570
+ const body = buildRequestBody({ ...request, stream: true });
1571
+ const res = await makeRequest("/chat/completions", body);
1572
+ if (!res.body) {
1573
+ throw new AnyModelError(502, "No response body for streaming request", {
1574
+ provider_name: "perplexity"
1575
+ });
1576
+ }
1577
+ return adapter.translateStream(res.body);
1578
+ }
1579
+ };
1580
+ return adapter;
1581
+ }
1582
+
1345
1583
  // src/providers/custom.ts
1346
1584
  function createCustomAdapter(name, config) {
1347
1585
  const openaiAdapter = createOpenAIAdapter(config.apiKey || "", config.baseURL);
1348
1586
  return {
1349
1587
  ...openaiAdapter,
1350
1588
  name,
1589
+ supportsBatch() {
1590
+ return false;
1591
+ },
1351
1592
  async listModels() {
1352
1593
  if (config.models && config.models.length > 0) {
1353
1594
  return config.models.map((modelId) => ({
@@ -1413,10 +1654,10 @@ function interpolateDeep(obj) {
1413
1654
  }
1414
1655
  return obj;
1415
1656
  }
1416
- function loadJsonFile(path) {
1417
- if (!(0, import_node_fs.existsSync)(path)) return null;
1657
+ function loadJsonFile(path2) {
1658
+ if (!(0, import_node_fs.existsSync)(path2)) return null;
1418
1659
  try {
1419
- const raw = (0, import_node_fs.readFileSync)(path, "utf-8");
1660
+ const raw = (0, import_node_fs.readFileSync)(path2, "utf-8");
1420
1661
  const parsed = JSON.parse(raw);
1421
1662
  return interpolateDeep(parsed);
1422
1663
  } catch {
@@ -1517,93 +1758,237 @@ var GenerationStatsStore = class {
1517
1758
  }
1518
1759
  };
1519
1760
 
1520
- // src/batch/store.ts
1761
+ // src/utils/fs-io.ts
1762
+ var import_promises = require("fs/promises");
1521
1763
  var import_node_fs2 = require("fs");
1522
- var import_node_path2 = require("path");
1523
- var import_node_os2 = require("os");
1524
- var DEFAULT_BATCH_DIR = (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".anymodel", "batches");
1764
+ var import_node_path2 = __toESM(require("path"), 1);
1765
+ var import_p_queue = __toESM(require("p-queue"), 1);
1766
+ var writeQueue = new import_p_queue.default({ concurrency: 10 });
1767
+ var readQueue = new import_p_queue.default({ concurrency: 20 });
1768
+ function configureFsIO(options) {
1769
+ if (options.readConcurrency !== void 0) {
1770
+ readQueue.concurrency = options.readConcurrency;
1771
+ }
1772
+ if (options.writeConcurrency !== void 0) {
1773
+ writeQueue.concurrency = options.writeConcurrency;
1774
+ }
1775
+ }
1776
+ var ensuredDirs = /* @__PURE__ */ new Set();
1777
+ var joinPathCache = /* @__PURE__ */ new Map();
1778
+ var dirnameCache = /* @__PURE__ */ new Map();
1779
+ var resolvePathCache = /* @__PURE__ */ new Map();
1780
+ async function ensureDir(dir) {
1781
+ if (!dir) return;
1782
+ if (ensuredDirs.has(dir)) return;
1783
+ await (0, import_promises.mkdir)(dir, { recursive: true });
1784
+ ensuredDirs.add(dir);
1785
+ }
1786
+ async function readFileQueued(filePath, encoding = "utf8") {
1787
+ return readQueue.add(async () => {
1788
+ return (0, import_promises.readFile)(filePath, encoding);
1789
+ });
1790
+ }
1791
+ async function readJsonQueued(filePath) {
1792
+ const raw = await readFileQueued(filePath, "utf8");
1793
+ return JSON.parse(raw);
1794
+ }
1795
+ async function readDirQueued(dirPath) {
1796
+ return readQueue.add(async () => {
1797
+ return (0, import_promises.readdir)(dirPath, { withFileTypes: true });
1798
+ });
1799
+ }
1800
+ async function pathExistsQueued(p) {
1801
+ return readQueue.add(async () => {
1802
+ try {
1803
+ await (0, import_promises.stat)(p);
1804
+ return true;
1805
+ } catch {
1806
+ return false;
1807
+ }
1808
+ });
1809
+ }
1810
+ async function fileExistsQueued(filePath) {
1811
+ return readQueue.add(async () => {
1812
+ try {
1813
+ const s = await (0, import_promises.stat)(filePath);
1814
+ return s.isFile();
1815
+ } catch {
1816
+ return false;
1817
+ }
1818
+ });
1819
+ }
1820
+ async function writeFileQueued(filePath, data) {
1821
+ await writeQueue.add(async () => {
1822
+ const dir = dirnameOf(filePath);
1823
+ await ensureDir(dir);
1824
+ await (0, import_promises.writeFile)(filePath, data);
1825
+ });
1826
+ }
1827
+ async function appendFileQueued(filePath, data) {
1828
+ await writeQueue.add(async () => {
1829
+ const dir = dirnameOf(filePath);
1830
+ await ensureDir(dir);
1831
+ await (0, import_promises.writeFile)(filePath, data, { flag: "a" });
1832
+ });
1833
+ }
1834
+ async function writeFileFlushedQueued(filePath, data) {
1835
+ await writeQueue.add(async () => {
1836
+ const dir = dirnameOf(filePath);
1837
+ await ensureDir(dir);
1838
+ const tmpPath = joinPath(
1839
+ dir,
1840
+ `.${import_node_path2.default.basename(filePath)}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`
1841
+ );
1842
+ const fh = await (0, import_promises.open)(tmpPath, "w");
1843
+ try {
1844
+ await fh.writeFile(data);
1845
+ await fh.sync();
1846
+ } finally {
1847
+ await fh.close();
1848
+ }
1849
+ await (0, import_promises.rename)(tmpPath, filePath);
1850
+ try {
1851
+ const dh = await (0, import_promises.open)(dir, "r");
1852
+ try {
1853
+ await dh.sync();
1854
+ } finally {
1855
+ await dh.close();
1856
+ }
1857
+ } catch {
1858
+ }
1859
+ });
1860
+ }
1861
+ function joinPath(...segments) {
1862
+ const key = segments.join("\0");
1863
+ const cached = joinPathCache.get(key);
1864
+ if (cached !== void 0) return cached;
1865
+ const out = import_node_path2.default.join(...segments);
1866
+ joinPathCache.set(key, out);
1867
+ return out;
1868
+ }
1869
+ function dirnameOf(p) {
1870
+ const cached = dirnameCache.get(p);
1871
+ if (cached !== void 0) return cached;
1872
+ const out = import_node_path2.default.dirname(p);
1873
+ dirnameCache.set(p, out);
1874
+ return out;
1875
+ }
1876
+ function resolvePath(...segments) {
1877
+ const key = segments.join("\0");
1878
+ const cached = resolvePathCache.get(key);
1879
+ if (cached !== void 0) return cached;
1880
+ const out = import_node_path2.default.resolve(...segments);
1881
+ resolvePathCache.set(key, out);
1882
+ return out;
1883
+ }
1884
+ function getFsQueueStatus() {
1885
+ return {
1886
+ read: { size: readQueue.size, pending: readQueue.pending },
1887
+ write: { size: writeQueue.size, pending: writeQueue.pending }
1888
+ };
1889
+ }
1890
+ async function waitForFsQueuesIdle() {
1891
+ await Promise.all([writeQueue.onIdle(), readQueue.onIdle()]);
1892
+ }
1893
+
1894
+ // src/batch/store.ts
1895
+ var DEFAULT_BATCH_DIR = joinPath(process.cwd(), ".anymodel", "batches");
1525
1896
  var BatchStore = class {
1526
1897
  dir;
1898
+ initialized = false;
1527
1899
  constructor(dir) {
1528
- this.dir = (0, import_node_path2.resolve)(dir || DEFAULT_BATCH_DIR);
1529
- (0, import_node_fs2.mkdirSync)(this.dir, { recursive: true });
1900
+ this.dir = resolvePath(dir || DEFAULT_BATCH_DIR);
1901
+ }
1902
+ async init() {
1903
+ if (this.initialized) return;
1904
+ await ensureDir(this.dir);
1905
+ this.initialized = true;
1530
1906
  }
1531
1907
  batchDir(id) {
1532
- return (0, import_node_path2.join)(this.dir, id);
1908
+ return joinPath(this.dir, id);
1533
1909
  }
1534
1910
  /**
1535
1911
  * Create a new batch directory and save initial metadata.
1536
1912
  */
1537
- create(batch) {
1913
+ async create(batch) {
1914
+ await this.init();
1538
1915
  const dir = this.batchDir(batch.id);
1539
- (0, import_node_fs2.mkdirSync)(dir, { recursive: true });
1540
- (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(dir, "meta.json"), JSON.stringify(batch, null, 2));
1916
+ await ensureDir(dir);
1917
+ await writeFileFlushedQueued(joinPath(dir, "meta.json"), JSON.stringify(batch, null, 2));
1541
1918
  }
1542
1919
  /**
1543
- * Update batch metadata.
1920
+ * Update batch metadata (atomic write).
1544
1921
  */
1545
- updateMeta(batch) {
1546
- const dir = this.batchDir(batch.id);
1547
- (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(dir, "meta.json"), JSON.stringify(batch, null, 2));
1922
+ async updateMeta(batch) {
1923
+ await writeFileFlushedQueued(
1924
+ joinPath(this.batchDir(batch.id), "meta.json"),
1925
+ JSON.stringify(batch, null, 2)
1926
+ );
1548
1927
  }
1549
1928
  /**
1550
1929
  * Save requests as JSONL.
1551
1930
  */
1552
- saveRequests(id, requests) {
1553
- const dir = this.batchDir(id);
1931
+ async saveRequests(id, requests) {
1554
1932
  const lines = requests.map((r) => JSON.stringify(r)).join("\n") + "\n";
1555
- (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(dir, "requests.jsonl"), lines);
1933
+ await writeFileQueued(joinPath(this.batchDir(id), "requests.jsonl"), lines);
1556
1934
  }
1557
1935
  /**
1558
1936
  * Append a result to results.jsonl.
1559
1937
  */
1560
- appendResult(id, result) {
1561
- const dir = this.batchDir(id);
1562
- (0, import_node_fs2.appendFileSync)((0, import_node_path2.join)(dir, "results.jsonl"), JSON.stringify(result) + "\n");
1938
+ async appendResult(id, result) {
1939
+ await appendFileQueued(
1940
+ joinPath(this.batchDir(id), "results.jsonl"),
1941
+ JSON.stringify(result) + "\n"
1942
+ );
1563
1943
  }
1564
1944
  /**
1565
1945
  * Save provider-specific state (e.g., provider batch ID).
1566
1946
  */
1567
- saveProviderState(id, state) {
1568
- const dir = this.batchDir(id);
1569
- (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(dir, "provider.json"), JSON.stringify(state, null, 2));
1947
+ async saveProviderState(id, state) {
1948
+ await writeFileFlushedQueued(
1949
+ joinPath(this.batchDir(id), "provider.json"),
1950
+ JSON.stringify(state, null, 2)
1951
+ );
1570
1952
  }
1571
1953
  /**
1572
1954
  * Load provider state.
1573
1955
  */
1574
- loadProviderState(id) {
1575
- const path = (0, import_node_path2.join)(this.batchDir(id), "provider.json");
1576
- if (!(0, import_node_fs2.existsSync)(path)) return null;
1577
- return JSON.parse((0, import_node_fs2.readFileSync)(path, "utf-8"));
1956
+ async loadProviderState(id) {
1957
+ const p = joinPath(this.batchDir(id), "provider.json");
1958
+ if (!await fileExistsQueued(p)) return null;
1959
+ return readJsonQueued(p);
1578
1960
  }
1579
1961
  /**
1580
1962
  * Get batch metadata.
1581
1963
  */
1582
- getMeta(id) {
1583
- const path = (0, import_node_path2.join)(this.batchDir(id), "meta.json");
1584
- if (!(0, import_node_fs2.existsSync)(path)) return null;
1585
- return JSON.parse((0, import_node_fs2.readFileSync)(path, "utf-8"));
1964
+ async getMeta(id) {
1965
+ const p = joinPath(this.batchDir(id), "meta.json");
1966
+ if (!await fileExistsQueued(p)) return null;
1967
+ return readJsonQueued(p);
1586
1968
  }
1587
1969
  /**
1588
1970
  * Get all results for a batch.
1589
1971
  */
1590
- getResults(id) {
1591
- const path = (0, import_node_path2.join)(this.batchDir(id), "results.jsonl");
1592
- if (!(0, import_node_fs2.existsSync)(path)) return [];
1593
- return (0, import_node_fs2.readFileSync)(path, "utf-8").trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
1972
+ async getResults(id) {
1973
+ const p = joinPath(this.batchDir(id), "results.jsonl");
1974
+ if (!await fileExistsQueued(p)) return [];
1975
+ const raw = await readFileQueued(p, "utf8");
1976
+ return raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
1594
1977
  }
1595
1978
  /**
1596
1979
  * List all batch IDs.
1597
1980
  */
1598
- listBatches() {
1599
- if (!(0, import_node_fs2.existsSync)(this.dir)) return [];
1600
- return (0, import_node_fs2.readdirSync)(this.dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
1981
+ async listBatches() {
1982
+ await this.init();
1983
+ if (!await pathExistsQueued(this.dir)) return [];
1984
+ const entries = await readDirQueued(this.dir);
1985
+ return entries.filter((d) => d.isDirectory()).map((d) => d.name).sort();
1601
1986
  }
1602
1987
  /**
1603
1988
  * Check if a batch exists.
1604
1989
  */
1605
- exists(id) {
1606
- return (0, import_node_fs2.existsSync)((0, import_node_path2.join)(this.batchDir(id), "meta.json"));
1990
+ async exists(id) {
1991
+ return fileExistsQueued(joinPath(this.batchDir(id), "meta.json"));
1607
1992
  }
1608
1993
  };
1609
1994
 
@@ -1612,10 +1997,27 @@ var BatchManager = class {
1612
1997
  store;
1613
1998
  router;
1614
1999
  concurrencyLimit;
2000
+ defaultPollInterval;
2001
+ batchAdapters = /* @__PURE__ */ new Map();
1615
2002
  constructor(router, options) {
1616
2003
  this.store = new BatchStore(options?.dir);
1617
2004
  this.router = router;
1618
2005
  this.concurrencyLimit = options?.concurrency ?? 5;
2006
+ this.defaultPollInterval = options?.pollInterval ?? 5e3;
2007
+ }
2008
+ /**
2009
+ * Register a native batch adapter for a provider.
2010
+ */
2011
+ registerBatchAdapter(providerName, adapter) {
2012
+ this.batchAdapters.set(providerName, adapter);
2013
+ }
2014
+ /**
2015
+ * Check if a provider has native batch support.
2016
+ */
2017
+ getNativeBatchAdapter(model) {
2018
+ const providerName = model.split("/")[0];
2019
+ const adapter = this.batchAdapters.get(providerName);
2020
+ return adapter ? { adapter, providerName } : null;
1619
2021
  }
1620
2022
  /**
1621
2023
  * Create a batch and return immediately (no polling).
@@ -1623,13 +2025,16 @@ var BatchManager = class {
1623
2025
  async create(request) {
1624
2026
  const id = generateId("batch");
1625
2027
  const now = (/* @__PURE__ */ new Date()).toISOString();
2028
+ const providerName = request.model.split("/")[0] || "unknown";
2029
+ const native = this.getNativeBatchAdapter(request.model);
2030
+ const batchMode = native ? "native" : "concurrent";
1626
2031
  const batch = {
1627
2032
  id,
1628
2033
  object: "batch",
1629
2034
  status: "pending",
1630
2035
  model: request.model,
1631
- provider_name: request.model.split("/")[0] || "unknown",
1632
- batch_mode: "concurrent",
2036
+ provider_name: providerName,
2037
+ batch_mode: batchMode,
1633
2038
  total: request.requests.length,
1634
2039
  completed: 0,
1635
2040
  failed: 0,
@@ -1637,10 +2042,15 @@ var BatchManager = class {
1637
2042
  completed_at: null,
1638
2043
  expires_at: null
1639
2044
  };
1640
- this.store.create(batch);
1641
- this.store.saveRequests(id, request.requests);
1642
- this.processBatch(id, request).catch(() => {
1643
- });
2045
+ await this.store.create(batch);
2046
+ await this.store.saveRequests(id, request.requests);
2047
+ if (native) {
2048
+ this.processNativeBatch(id, request, native.adapter).catch(() => {
2049
+ });
2050
+ } else {
2051
+ this.processConcurrentBatch(id, request).catch(() => {
2052
+ });
2053
+ }
1644
2054
  return batch;
1645
2055
  }
1646
2056
  /**
@@ -1654,14 +2064,19 @@ var BatchManager = class {
1654
2064
  * Poll an existing batch until completion.
1655
2065
  */
1656
2066
  async poll(id, options = {}) {
1657
- const interval = options.interval ?? 5e3;
2067
+ const interval = options.interval ?? this.defaultPollInterval;
1658
2068
  const timeout = options.timeout ?? 0;
1659
2069
  const startTime = Date.now();
1660
2070
  while (true) {
1661
- const batch = this.store.getMeta(id);
2071
+ let batch = await this.store.getMeta(id);
1662
2072
  if (!batch) {
1663
2073
  throw new AnyModelError(404, `Batch ${id} not found`);
1664
2074
  }
2075
+ if (batch.batch_mode === "native" && batch.status === "processing") {
2076
+ await this.syncNativeBatchStatus(id);
2077
+ batch = await this.store.getMeta(id);
2078
+ if (!batch) throw new AnyModelError(404, `Batch ${id} not found`);
2079
+ }
1665
2080
  if (options.onProgress) {
1666
2081
  options.onProgress(batch);
1667
2082
  }
@@ -1671,24 +2086,24 @@ var BatchManager = class {
1671
2086
  if (timeout > 0 && Date.now() - startTime > timeout) {
1672
2087
  throw new AnyModelError(408, `Batch ${id} timed out after ${timeout}ms`);
1673
2088
  }
1674
- await new Promise((resolve3) => setTimeout(resolve3, interval));
2089
+ await new Promise((resolve2) => setTimeout(resolve2, interval));
1675
2090
  }
1676
2091
  }
1677
2092
  /**
1678
2093
  * Get the current status of a batch.
1679
2094
  */
1680
- get(id) {
2095
+ async get(id) {
1681
2096
  return this.store.getMeta(id);
1682
2097
  }
1683
2098
  /**
1684
2099
  * Get results for a completed batch.
1685
2100
  */
1686
- getResults(id) {
1687
- const batch = this.store.getMeta(id);
2101
+ async getResults(id) {
2102
+ const batch = await this.store.getMeta(id);
1688
2103
  if (!batch) {
1689
2104
  throw new AnyModelError(404, `Batch ${id} not found`);
1690
2105
  }
1691
- const results = this.store.getResults(id);
2106
+ const results = await this.store.getResults(id);
1692
2107
  const usage = {
1693
2108
  total_prompt_tokens: 0,
1694
2109
  total_completion_tokens: 0,
@@ -1710,37 +2125,119 @@ var BatchManager = class {
1710
2125
  /**
1711
2126
  * List all batches.
1712
2127
  */
1713
- list() {
1714
- return this.store.listBatches().map((id) => this.store.getMeta(id)).filter((b) => b !== null);
2128
+ async list() {
2129
+ const ids = await this.store.listBatches();
2130
+ const batches = [];
2131
+ for (const id of ids) {
2132
+ const meta = await this.store.getMeta(id);
2133
+ if (meta) batches.push(meta);
2134
+ }
2135
+ return batches;
1715
2136
  }
1716
2137
  /**
1717
2138
  * Cancel a batch.
1718
2139
  */
1719
- cancel(id) {
1720
- const batch = this.store.getMeta(id);
2140
+ async cancel(id) {
2141
+ const batch = await this.store.getMeta(id);
1721
2142
  if (!batch) {
1722
2143
  throw new AnyModelError(404, `Batch ${id} not found`);
1723
2144
  }
1724
2145
  if (batch.status === "completed" || batch.status === "cancelled") {
1725
2146
  return batch;
1726
2147
  }
2148
+ if (batch.batch_mode === "native") {
2149
+ const providerState = await this.store.loadProviderState(id);
2150
+ const adapter = this.batchAdapters.get(batch.provider_name);
2151
+ if (adapter && providerState?.providerBatchId) {
2152
+ try {
2153
+ await adapter.cancelBatch(providerState.providerBatchId);
2154
+ } catch {
2155
+ }
2156
+ }
2157
+ }
1727
2158
  batch.status = "cancelled";
1728
2159
  batch.completed_at = (/* @__PURE__ */ new Date()).toISOString();
1729
- this.store.updateMeta(batch);
2160
+ await this.store.updateMeta(batch);
1730
2161
  return batch;
1731
2162
  }
1732
2163
  /**
1733
- * Process batch requests concurrently.
2164
+ * Process batch via native provider batch API.
2165
+ */
2166
+ async processNativeBatch(batchId, request, adapter) {
2167
+ const batch = await this.store.getMeta(batchId);
2168
+ if (!batch) return;
2169
+ try {
2170
+ const model = request.model.includes("/") ? request.model.split("/").slice(1).join("/") : request.model;
2171
+ const { providerBatchId, metadata } = await adapter.createBatch(
2172
+ model,
2173
+ request.requests,
2174
+ request.options
2175
+ );
2176
+ await this.store.saveProviderState(batchId, {
2177
+ providerBatchId,
2178
+ providerName: batch.provider_name,
2179
+ ...metadata
2180
+ });
2181
+ batch.status = "processing";
2182
+ await this.store.updateMeta(batch);
2183
+ } catch (err) {
2184
+ batch.status = "failed";
2185
+ batch.completed_at = (/* @__PURE__ */ new Date()).toISOString();
2186
+ await this.store.updateMeta(batch);
2187
+ throw err;
2188
+ }
2189
+ }
2190
+ /**
2191
+ * Sync native batch status from provider.
2192
+ */
2193
+ async syncNativeBatchStatus(batchId) {
2194
+ const batch = await this.store.getMeta(batchId);
2195
+ if (!batch) return;
2196
+ const providerState = await this.store.loadProviderState(batchId);
2197
+ if (!providerState?.providerBatchId) return;
2198
+ const adapter = this.batchAdapters.get(batch.provider_name);
2199
+ if (!adapter) return;
2200
+ try {
2201
+ const status = await adapter.pollBatch(providerState.providerBatchId);
2202
+ batch.total = status.total || batch.total;
2203
+ batch.completed = status.completed;
2204
+ batch.failed = status.failed;
2205
+ if (status.status === "completed" || status.status === "failed" || status.status === "cancelled") {
2206
+ batch.status = status.status;
2207
+ batch.completed_at = (/* @__PURE__ */ new Date()).toISOString();
2208
+ if (status.status === "completed" || status.status === "failed") {
2209
+ try {
2210
+ const results = await adapter.getBatchResults(providerState.providerBatchId);
2211
+ for (const result of results) {
2212
+ await this.store.appendResult(batchId, result);
2213
+ }
2214
+ batch.completed = results.filter((r) => r.status === "success").length;
2215
+ batch.failed = results.filter((r) => r.status === "error").length;
2216
+ } catch {
2217
+ if (batch.status !== "failed") {
2218
+ batch.status = "failed";
2219
+ }
2220
+ }
2221
+ }
2222
+ } else {
2223
+ batch.status = "processing";
2224
+ }
2225
+ await this.store.updateMeta(batch);
2226
+ } catch {
2227
+ }
2228
+ }
2229
+ /**
2230
+ * Process batch requests concurrently (fallback path).
1734
2231
  */
1735
- async processBatch(batchId, request) {
1736
- const batch = this.store.getMeta(batchId);
2232
+ async processConcurrentBatch(batchId, request) {
2233
+ const batch = await this.store.getMeta(batchId);
2234
+ if (!batch) return;
1737
2235
  batch.status = "processing";
1738
- this.store.updateMeta(batch);
2236
+ await this.store.updateMeta(batch);
1739
2237
  const items = request.requests;
1740
- const queue = [...items];
1741
2238
  const active = /* @__PURE__ */ new Set();
1742
2239
  const processItem = async (item) => {
1743
- const current = this.store.getMeta(batchId);
2240
+ const current = await this.store.getMeta(batchId);
1744
2241
  if (current?.status === "cancelled") return;
1745
2242
  const chatRequest = {
1746
2243
  model: request.model,
@@ -1772,17 +2269,19 @@ var BatchManager = class {
1772
2269
  error: { code: error.code, message: error.message }
1773
2270
  };
1774
2271
  }
1775
- this.store.appendResult(batchId, result);
1776
- const meta = this.store.getMeta(batchId);
1777
- if (result.status === "success") {
1778
- meta.completed++;
1779
- } else {
1780
- meta.failed++;
2272
+ await this.store.appendResult(batchId, result);
2273
+ const meta = await this.store.getMeta(batchId);
2274
+ if (meta) {
2275
+ if (result.status === "success") {
2276
+ meta.completed++;
2277
+ } else {
2278
+ meta.failed++;
2279
+ }
2280
+ await this.store.updateMeta(meta);
1781
2281
  }
1782
- this.store.updateMeta(meta);
1783
2282
  };
1784
- for (const item of queue) {
1785
- const current = this.store.getMeta(batchId);
2283
+ for (const item of items) {
2284
+ const current = await this.store.getMeta(batchId);
1786
2285
  if (current?.status === "cancelled") break;
1787
2286
  if (active.size >= this.concurrencyLimit) {
1788
2287
  await Promise.race(active);
@@ -1793,15 +2292,411 @@ var BatchManager = class {
1793
2292
  active.add(promise);
1794
2293
  }
1795
2294
  await Promise.all(active);
1796
- const finalMeta = this.store.getMeta(batchId);
1797
- if (finalMeta.status !== "cancelled") {
2295
+ const finalMeta = await this.store.getMeta(batchId);
2296
+ if (finalMeta && finalMeta.status !== "cancelled") {
1798
2297
  finalMeta.status = finalMeta.failed === finalMeta.total ? "failed" : "completed";
1799
2298
  finalMeta.completed_at = (/* @__PURE__ */ new Date()).toISOString();
1800
- this.store.updateMeta(finalMeta);
2299
+ await this.store.updateMeta(finalMeta);
1801
2300
  }
1802
2301
  }
1803
2302
  };
1804
2303
 
2304
+ // src/providers/openai-batch.ts
2305
+ var OPENAI_API_BASE2 = "https://api.openai.com/v1";
2306
+ function createOpenAIBatchAdapter(apiKey) {
2307
+ async function apiRequest(path2, options = {}) {
2308
+ const headers = {
2309
+ "Authorization": `Bearer ${apiKey}`
2310
+ };
2311
+ let fetchBody;
2312
+ if (options.formData) {
2313
+ fetchBody = options.formData;
2314
+ } else if (options.body) {
2315
+ headers["Content-Type"] = "application/json";
2316
+ fetchBody = JSON.stringify(options.body);
2317
+ }
2318
+ const res = await fetch(`${OPENAI_API_BASE2}${path2}`, {
2319
+ method: options.method || "GET",
2320
+ headers,
2321
+ body: fetchBody
2322
+ });
2323
+ if (!res.ok) {
2324
+ let errorBody;
2325
+ try {
2326
+ errorBody = await res.json();
2327
+ } catch {
2328
+ errorBody = { message: res.statusText };
2329
+ }
2330
+ const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
2331
+ throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
2332
+ provider_name: "openai",
2333
+ raw: errorBody
2334
+ });
2335
+ }
2336
+ return res;
2337
+ }
2338
+ function buildJSONL(model, requests) {
2339
+ return requests.map((req) => {
2340
+ const body = {
2341
+ model,
2342
+ messages: req.messages
2343
+ };
2344
+ if (req.max_tokens !== void 0) body.max_tokens = req.max_tokens;
2345
+ if (req.temperature !== void 0) body.temperature = req.temperature;
2346
+ if (req.top_p !== void 0) body.top_p = req.top_p;
2347
+ if (req.stop !== void 0) body.stop = req.stop;
2348
+ if (req.response_format !== void 0) body.response_format = req.response_format;
2349
+ if (req.tools !== void 0) body.tools = req.tools;
2350
+ if (req.tool_choice !== void 0) body.tool_choice = req.tool_choice;
2351
+ return JSON.stringify({
2352
+ custom_id: req.custom_id,
2353
+ method: "POST",
2354
+ url: "/v1/chat/completions",
2355
+ body
2356
+ });
2357
+ }).join("\n");
2358
+ }
2359
+ function rePrefixId(id) {
2360
+ if (id && id.startsWith("chatcmpl-")) {
2361
+ return `gen-${id.substring(9)}`;
2362
+ }
2363
+ return id.startsWith("gen-") ? id : `gen-${id}`;
2364
+ }
2365
+ function translateOpenAIResponse(body) {
2366
+ return {
2367
+ id: rePrefixId(body.id || generateId()),
2368
+ object: "chat.completion",
2369
+ created: body.created || Math.floor(Date.now() / 1e3),
2370
+ model: `openai/${body.model}`,
2371
+ choices: body.choices,
2372
+ usage: body.usage
2373
+ };
2374
+ }
2375
+ function mapStatus(openaiStatus) {
2376
+ switch (openaiStatus) {
2377
+ case "validating":
2378
+ case "finalizing":
2379
+ return "processing";
2380
+ case "in_progress":
2381
+ return "processing";
2382
+ case "completed":
2383
+ return "completed";
2384
+ case "failed":
2385
+ return "failed";
2386
+ case "expired":
2387
+ return "failed";
2388
+ case "cancelled":
2389
+ case "cancelling":
2390
+ return "cancelled";
2391
+ default:
2392
+ return "pending";
2393
+ }
2394
+ }
2395
+ return {
2396
+ async createBatch(model, requests, options) {
2397
+ const jsonlContent = buildJSONL(model, requests);
2398
+ const blob = new Blob([jsonlContent], { type: "application/jsonl" });
2399
+ const formData = new FormData();
2400
+ formData.append("purpose", "batch");
2401
+ formData.append("file", blob, "batch_input.jsonl");
2402
+ const uploadRes = await apiRequest("/files", { method: "POST", formData });
2403
+ const fileData = await uploadRes.json();
2404
+ const inputFileId = fileData.id;
2405
+ const batchRes = await apiRequest("/batches", {
2406
+ method: "POST",
2407
+ body: {
2408
+ input_file_id: inputFileId,
2409
+ endpoint: "/v1/chat/completions",
2410
+ completion_window: "24h",
2411
+ metadata: options?.metadata
2412
+ }
2413
+ });
2414
+ const batchData = await batchRes.json();
2415
+ return {
2416
+ providerBatchId: batchData.id,
2417
+ metadata: {
2418
+ input_file_id: inputFileId,
2419
+ openai_status: batchData.status
2420
+ }
2421
+ };
2422
+ },
2423
+ async pollBatch(providerBatchId) {
2424
+ const res = await apiRequest(`/batches/${providerBatchId}`);
2425
+ const data = await res.json();
2426
+ const requestCounts = data.request_counts || {};
2427
+ return {
2428
+ status: mapStatus(data.status),
2429
+ total: requestCounts.total || 0,
2430
+ completed: requestCounts.completed || 0,
2431
+ failed: requestCounts.failed || 0
2432
+ };
2433
+ },
2434
+ async getBatchResults(providerBatchId) {
2435
+ const batchRes = await apiRequest(`/batches/${providerBatchId}`);
2436
+ const batchData = await batchRes.json();
2437
+ const results = [];
2438
+ if (batchData.output_file_id) {
2439
+ const outputRes = await apiRequest(`/files/${batchData.output_file_id}/content`);
2440
+ const outputText = await outputRes.text();
2441
+ for (const line of outputText.trim().split("\n")) {
2442
+ if (!line) continue;
2443
+ const item = JSON.parse(line);
2444
+ if (item.response?.status_code === 200) {
2445
+ results.push({
2446
+ custom_id: item.custom_id,
2447
+ status: "success",
2448
+ response: translateOpenAIResponse(item.response.body),
2449
+ error: null
2450
+ });
2451
+ } else {
2452
+ results.push({
2453
+ custom_id: item.custom_id,
2454
+ status: "error",
2455
+ response: null,
2456
+ error: {
2457
+ code: item.response?.status_code || 500,
2458
+ message: item.error?.message || item.response?.body?.error?.message || "Unknown error"
2459
+ }
2460
+ });
2461
+ }
2462
+ }
2463
+ }
2464
+ if (batchData.error_file_id) {
2465
+ const errorRes = await apiRequest(`/files/${batchData.error_file_id}/content`);
2466
+ const errorText = await errorRes.text();
2467
+ for (const line of errorText.trim().split("\n")) {
2468
+ if (!line) continue;
2469
+ const item = JSON.parse(line);
2470
+ const existing = results.find((r) => r.custom_id === item.custom_id);
2471
+ if (!existing) {
2472
+ results.push({
2473
+ custom_id: item.custom_id,
2474
+ status: "error",
2475
+ response: null,
2476
+ error: {
2477
+ code: item.response?.status_code || 500,
2478
+ message: item.error?.message || "Batch item error"
2479
+ }
2480
+ });
2481
+ }
2482
+ }
2483
+ }
2484
+ return results;
2485
+ },
2486
+ async cancelBatch(providerBatchId) {
2487
+ await apiRequest(`/batches/${providerBatchId}/cancel`, { method: "POST" });
2488
+ }
2489
+ };
2490
+ }
2491
+
2492
+ // src/providers/anthropic-batch.ts
2493
+ var ANTHROPIC_API_BASE2 = "https://api.anthropic.com/v1";
2494
+ var ANTHROPIC_VERSION2 = "2023-06-01";
2495
+ var DEFAULT_MAX_TOKENS2 = 4096;
2496
+ function createAnthropicBatchAdapter(apiKey) {
2497
+ async function apiRequest(path2, options = {}) {
2498
+ const headers = {
2499
+ "x-api-key": apiKey,
2500
+ "anthropic-version": ANTHROPIC_VERSION2,
2501
+ "Content-Type": "application/json"
2502
+ };
2503
+ const res = await fetch(`${ANTHROPIC_API_BASE2}${path2}`, {
2504
+ method: options.method || "GET",
2505
+ headers,
2506
+ body: options.body ? JSON.stringify(options.body) : void 0
2507
+ });
2508
+ if (!res.ok) {
2509
+ let errorBody;
2510
+ try {
2511
+ errorBody = await res.json();
2512
+ } catch {
2513
+ errorBody = { message: res.statusText };
2514
+ }
2515
+ const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
2516
+ throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
2517
+ provider_name: "anthropic",
2518
+ raw: errorBody
2519
+ });
2520
+ }
2521
+ return res;
2522
+ }
2523
+ function translateToAnthropicParams(model, req) {
2524
+ const params = {
2525
+ model,
2526
+ max_tokens: req.max_tokens || DEFAULT_MAX_TOKENS2
2527
+ };
2528
+ const systemMessages = req.messages.filter((m) => m.role === "system");
2529
+ const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
2530
+ if (systemMessages.length > 0) {
2531
+ params.system = systemMessages.map((m) => typeof m.content === "string" ? m.content : "").join("\n");
2532
+ }
2533
+ params.messages = nonSystemMessages.map((m) => ({
2534
+ role: m.role === "tool" ? "user" : m.role,
2535
+ content: m.tool_call_id ? [{ type: "tool_result", tool_use_id: m.tool_call_id, content: typeof m.content === "string" ? m.content : "" }] : m.content
2536
+ }));
2537
+ if (req.temperature !== void 0) params.temperature = req.temperature;
2538
+ if (req.top_p !== void 0) params.top_p = req.top_p;
2539
+ if (req.top_k !== void 0) params.top_k = req.top_k;
2540
+ if (req.stop !== void 0) params.stop_sequences = Array.isArray(req.stop) ? req.stop : [req.stop];
2541
+ if (req.tools && req.tools.length > 0) {
2542
+ params.tools = req.tools.map((t) => ({
2543
+ name: t.function.name,
2544
+ description: t.function.description || "",
2545
+ input_schema: t.function.parameters || { type: "object", properties: {} }
2546
+ }));
2547
+ if (req.tool_choice) {
2548
+ if (req.tool_choice === "auto") {
2549
+ params.tool_choice = { type: "auto" };
2550
+ } else if (req.tool_choice === "required") {
2551
+ params.tool_choice = { type: "any" };
2552
+ } else if (req.tool_choice === "none") {
2553
+ delete params.tools;
2554
+ } else if (typeof req.tool_choice === "object") {
2555
+ params.tool_choice = { type: "tool", name: req.tool_choice.function.name };
2556
+ }
2557
+ }
2558
+ }
2559
+ if (req.response_format) {
2560
+ if (req.response_format.type === "json_object" || req.response_format.type === "json_schema") {
2561
+ const jsonInstruction = "Respond with valid JSON only. Do not include any text outside the JSON object.";
2562
+ params.system = params.system ? `${jsonInstruction}
2563
+
2564
+ ${params.system}` : jsonInstruction;
2565
+ }
2566
+ }
2567
+ return params;
2568
+ }
2569
+ function mapStopReason(reason) {
2570
+ switch (reason) {
2571
+ case "end_turn":
2572
+ return "stop";
2573
+ case "max_tokens":
2574
+ return "length";
2575
+ case "tool_use":
2576
+ return "tool_calls";
2577
+ case "stop_sequence":
2578
+ return "stop";
2579
+ default:
2580
+ return "stop";
2581
+ }
2582
+ }
2583
+ function translateAnthropicMessage(msg) {
2584
+ let content = "";
2585
+ const toolCalls = [];
2586
+ for (const block of msg.content || []) {
2587
+ if (block.type === "text") {
2588
+ content += block.text;
2589
+ } else if (block.type === "tool_use") {
2590
+ toolCalls.push({
2591
+ id: block.id,
2592
+ type: "function",
2593
+ function: {
2594
+ name: block.name,
2595
+ arguments: JSON.stringify(block.input)
2596
+ }
2597
+ });
2598
+ }
2599
+ }
2600
+ const message = { role: "assistant", content };
2601
+ if (toolCalls.length > 0) {
2602
+ message.tool_calls = toolCalls;
2603
+ }
2604
+ return {
2605
+ id: generateId(),
2606
+ object: "chat.completion",
2607
+ created: Math.floor(Date.now() / 1e3),
2608
+ model: `anthropic/${msg.model}`,
2609
+ choices: [{
2610
+ index: 0,
2611
+ message,
2612
+ finish_reason: mapStopReason(msg.stop_reason)
2613
+ }],
2614
+ usage: {
2615
+ prompt_tokens: msg.usage?.input_tokens || 0,
2616
+ completion_tokens: msg.usage?.output_tokens || 0,
2617
+ total_tokens: (msg.usage?.input_tokens || 0) + (msg.usage?.output_tokens || 0)
2618
+ }
2619
+ };
2620
+ }
2621
+ return {
2622
+ async createBatch(model, requests, _options) {
2623
+ const batchRequests = requests.map((req) => ({
2624
+ custom_id: req.custom_id,
2625
+ params: translateToAnthropicParams(model, req)
2626
+ }));
2627
+ const res = await apiRequest("/messages/batches", {
2628
+ method: "POST",
2629
+ body: { requests: batchRequests }
2630
+ });
2631
+ const data = await res.json();
2632
+ return {
2633
+ providerBatchId: data.id,
2634
+ metadata: {
2635
+ anthropic_type: data.type,
2636
+ created_at: data.created_at
2637
+ }
2638
+ };
2639
+ },
2640
+ async pollBatch(providerBatchId) {
2641
+ const res = await apiRequest(`/messages/batches/${providerBatchId}`);
2642
+ const data = await res.json();
2643
+ const counts = data.request_counts || {};
2644
+ const total = (counts.processing || 0) + (counts.succeeded || 0) + (counts.errored || 0) + (counts.canceled || 0) + (counts.expired || 0);
2645
+ let status;
2646
+ if (data.processing_status === "ended") {
2647
+ if (counts.succeeded === 0 && (counts.errored > 0 || counts.expired > 0 || counts.canceled > 0)) {
2648
+ status = "failed";
2649
+ } else if (data.cancel_initiated_at) {
2650
+ status = "cancelled";
2651
+ } else {
2652
+ status = "completed";
2653
+ }
2654
+ } else {
2655
+ status = "processing";
2656
+ }
2657
+ return {
2658
+ status,
2659
+ total,
2660
+ completed: counts.succeeded || 0,
2661
+ failed: (counts.errored || 0) + (counts.expired || 0) + (counts.canceled || 0)
2662
+ };
2663
+ },
2664
+ async getBatchResults(providerBatchId) {
2665
+ const res = await apiRequest(`/messages/batches/${providerBatchId}/results`);
2666
+ const text = await res.text();
2667
+ const results = [];
2668
+ for (const line of text.trim().split("\n")) {
2669
+ if (!line) continue;
2670
+ const item = JSON.parse(line);
2671
+ if (item.result?.type === "succeeded") {
2672
+ results.push({
2673
+ custom_id: item.custom_id,
2674
+ status: "success",
2675
+ response: translateAnthropicMessage(item.result.message),
2676
+ error: null
2677
+ });
2678
+ } else {
2679
+ const errorType = item.result?.type || "unknown";
2680
+ const errorMsg = item.result?.error?.message || `Batch item ${errorType}`;
2681
+ results.push({
2682
+ custom_id: item.custom_id,
2683
+ status: "error",
2684
+ response: null,
2685
+ error: {
2686
+ code: errorType === "expired" ? 408 : 500,
2687
+ message: errorMsg
2688
+ }
2689
+ });
2690
+ }
2691
+ }
2692
+ return results;
2693
+ },
2694
+ async cancelBatch(providerBatchId) {
2695
+ await apiRequest(`/messages/batches/${providerBatchId}/cancel`, { method: "POST" });
2696
+ }
2697
+ };
2698
+ }
2699
+
1805
2700
  // src/client.ts
1806
2701
  var AnyModel = class {
1807
2702
  registry;
@@ -1817,6 +2712,9 @@ var AnyModel = class {
1817
2712
  constructor(config = {}) {
1818
2713
  this.config = resolveConfig(config);
1819
2714
  this.registry = new ProviderRegistry();
2715
+ if (this.config.io) {
2716
+ configureFsIO(this.config.io);
2717
+ }
1820
2718
  this.registerProviders();
1821
2719
  this.router = new Router(this.registry, this.config.aliases, this.config);
1822
2720
  this.chat = {
@@ -1866,8 +2764,10 @@ var AnyModel = class {
1866
2764
  };
1867
2765
  this.batchManager = new BatchManager(this.router, {
1868
2766
  dir: this.config.batch?.dir,
1869
- concurrency: this.config.batch?.concurrencyFallback
2767
+ concurrency: this.config.batch?.concurrencyFallback,
2768
+ pollInterval: this.config.batch?.pollInterval
1870
2769
  });
2770
+ this.registerBatchAdapters();
1871
2771
  this.batches = {
1872
2772
  create: (request) => this.batchManager.create(request),
1873
2773
  createAndPoll: (request, options) => this.batchManager.createAndPoll(request, options),
@@ -1892,14 +2792,17 @@ var AnyModel = class {
1892
2792
  if (googleKey) {
1893
2793
  this.registry.register("google", createGoogleAdapter(googleKey));
1894
2794
  }
2795
+ const perplexityKey = config.perplexity?.apiKey || process.env.PERPLEXITY_API_KEY;
2796
+ if (perplexityKey) {
2797
+ this.registry.register("perplexity", createPerplexityAdapter(perplexityKey));
2798
+ }
1895
2799
  const builtinProviders = [
1896
2800
  { name: "mistral", baseURL: "https://api.mistral.ai/v1", configKey: "mistral", envVar: "MISTRAL_API_KEY" },
1897
2801
  { name: "groq", baseURL: "https://api.groq.com/openai/v1", configKey: "groq", envVar: "GROQ_API_KEY" },
1898
2802
  { name: "deepseek", baseURL: "https://api.deepseek.com", configKey: "deepseek", envVar: "DEEPSEEK_API_KEY" },
1899
2803
  { name: "xai", baseURL: "https://api.x.ai/v1", configKey: "xai", envVar: "XAI_API_KEY" },
1900
2804
  { name: "together", baseURL: "https://api.together.xyz/v1", configKey: "together", envVar: "TOGETHER_API_KEY" },
1901
- { name: "fireworks", baseURL: "https://api.fireworks.ai/inference/v1", configKey: "fireworks", envVar: "FIREWORKS_API_KEY" },
1902
- { name: "perplexity", baseURL: "https://api.perplexity.ai", configKey: "perplexity", envVar: "PERPLEXITY_API_KEY" }
2805
+ { name: "fireworks", baseURL: "https://api.fireworks.ai/inference/v1", configKey: "fireworks", envVar: "FIREWORKS_API_KEY" }
1903
2806
  ];
1904
2807
  for (const { name, baseURL, configKey, envVar } of builtinProviders) {
1905
2808
  const providerConfig = config[configKey];
@@ -1919,6 +2822,17 @@ var AnyModel = class {
1919
2822
  }
1920
2823
  }
1921
2824
  }
2825
+ registerBatchAdapters() {
2826
+ const config = this.config;
2827
+ const openaiKey = config.openai?.apiKey || process.env.OPENAI_API_KEY;
2828
+ if (openaiKey) {
2829
+ this.batchManager.registerBatchAdapter("openai", createOpenAIBatchAdapter(openaiKey));
2830
+ }
2831
+ const anthropicKey = config.anthropic?.apiKey || process.env.ANTHROPIC_API_KEY;
2832
+ if (anthropicKey) {
2833
+ this.batchManager.registerBatchAdapter("anthropic", createAnthropicBatchAdapter(anthropicKey));
2834
+ }
2835
+ }
1922
2836
  applyDefaults(request) {
1923
2837
  const defaults = this.config.defaults;
1924
2838
  if (!defaults) return request;
@@ -1947,10 +2861,10 @@ var AnyModel = class {
1947
2861
  // src/server.ts
1948
2862
  var import_node_http = require("http");
1949
2863
  function parseBody(req) {
1950
- return new Promise((resolve3, reject) => {
2864
+ return new Promise((resolve2, reject) => {
1951
2865
  const chunks = [];
1952
2866
  req.on("data", (chunk) => chunks.push(chunk));
1953
- req.on("end", () => resolve3(Buffer.concat(chunks).toString()));
2867
+ req.on("end", () => resolve2(Buffer.concat(chunks).toString()));
1954
2868
  req.on("error", reject);
1955
2869
  });
1956
2870
  }
@@ -1980,7 +2894,7 @@ function createAnyModelServer(options = {}) {
1980
2894
  const basePath = "/api/v1";
1981
2895
  const server = (0, import_node_http.createServer)(async (req, res) => {
1982
2896
  const url = new URL(req.url || "/", `http://${req.headers.host}`);
1983
- const path = url.pathname;
2897
+ const path2 = url.pathname;
1984
2898
  res.setHeader("Access-Control-Allow-Origin", "*");
1985
2899
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
1986
2900
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
@@ -1990,11 +2904,11 @@ function createAnyModelServer(options = {}) {
1990
2904
  return;
1991
2905
  }
1992
2906
  try {
1993
- if (path === "/health" && req.method === "GET") {
2907
+ if (path2 === "/health" && req.method === "GET") {
1994
2908
  sendJSON(res, 200, { status: "ok" });
1995
2909
  return;
1996
2910
  }
1997
- if (path === `${basePath}/chat/completions` && req.method === "POST") {
2911
+ if (path2 === `${basePath}/chat/completions` && req.method === "POST") {
1998
2912
  const body = JSON.parse(await parseBody(req));
1999
2913
  if (body.stream) {
2000
2914
  const stream = await client.chat.completions.create(body);
@@ -2005,14 +2919,14 @@ function createAnyModelServer(options = {}) {
2005
2919
  }
2006
2920
  return;
2007
2921
  }
2008
- if (path === `${basePath}/models` && req.method === "GET") {
2922
+ if (path2 === `${basePath}/models` && req.method === "GET") {
2009
2923
  const provider = url.searchParams.get("provider") || void 0;
2010
2924
  const models = await client.models.list({ provider });
2011
2925
  sendJSON(res, 200, { object: "list", data: models });
2012
2926
  return;
2013
2927
  }
2014
- if (path.startsWith(`${basePath}/generation/`) && req.method === "GET") {
2015
- const id = path.substring(`${basePath}/generation/`.length);
2928
+ if (path2.startsWith(`${basePath}/generation/`) && req.method === "GET") {
2929
+ const id = path2.substring(`${basePath}/generation/`.length);
2016
2930
  const stats = client.generation.get(id);
2017
2931
  if (!stats) {
2018
2932
  sendError(res, 404, `Generation ${id} not found`);
@@ -2021,26 +2935,26 @@ function createAnyModelServer(options = {}) {
2021
2935
  sendJSON(res, 200, stats);
2022
2936
  return;
2023
2937
  }
2024
- if (path === `${basePath}/batches` && req.method === "POST") {
2938
+ if (path2 === `${basePath}/batches` && req.method === "POST") {
2025
2939
  const body = JSON.parse(await parseBody(req));
2026
2940
  const batch = await client.batches.create(body);
2027
2941
  sendJSON(res, 201, batch);
2028
2942
  return;
2029
2943
  }
2030
- if (path === `${basePath}/batches` && req.method === "GET") {
2031
- const batches = client.batches.list();
2944
+ if (path2 === `${basePath}/batches` && req.method === "GET") {
2945
+ const batches = await client.batches.list();
2032
2946
  sendJSON(res, 200, { object: "list", data: batches });
2033
2947
  return;
2034
2948
  }
2035
- if (path.startsWith(`${basePath}/batches/`) && req.method === "GET") {
2036
- const parts = path.substring(`${basePath}/batches/`.length).split("/");
2949
+ if (path2.startsWith(`${basePath}/batches/`) && req.method === "GET") {
2950
+ const parts = path2.substring(`${basePath}/batches/`.length).split("/");
2037
2951
  const id = parts[0];
2038
2952
  if (parts[1] === "results") {
2039
- const results = client.batches.results(id);
2953
+ const results = await client.batches.results(id);
2040
2954
  sendJSON(res, 200, results);
2041
2955
  return;
2042
2956
  }
2043
- const batch = client.batches.get(id);
2957
+ const batch = await client.batches.get(id);
2044
2958
  if (!batch) {
2045
2959
  sendError(res, 404, `Batch ${id} not found`);
2046
2960
  return;
@@ -2048,16 +2962,16 @@ function createAnyModelServer(options = {}) {
2048
2962
  sendJSON(res, 200, batch);
2049
2963
  return;
2050
2964
  }
2051
- if (path.startsWith(`${basePath}/batches/`) && req.method === "POST") {
2052
- const parts = path.substring(`${basePath}/batches/`.length).split("/");
2965
+ if (path2.startsWith(`${basePath}/batches/`) && req.method === "POST") {
2966
+ const parts = path2.substring(`${basePath}/batches/`.length).split("/");
2053
2967
  const id = parts[0];
2054
2968
  if (parts[1] === "cancel") {
2055
- const batch = client.batches.cancel(id);
2969
+ const batch = await client.batches.cancel(id);
2056
2970
  sendJSON(res, 200, batch);
2057
2971
  return;
2058
2972
  }
2059
2973
  }
2060
- sendError(res, 404, `Not found: ${path}`);
2974
+ sendError(res, 404, `Not found: ${path2}`);
2061
2975
  } catch (err) {
2062
2976
  const code = err?.code || 500;
2063
2977
  const message = err?.message || "Internal server error";
@@ -2093,8 +3007,19 @@ function startServer(options = {}) {
2093
3007
  BatchManager,
2094
3008
  BatchStore,
2095
3009
  GenerationStatsStore,
3010
+ appendFileQueued,
3011
+ configureFsIO,
3012
+ createAnthropicBatchAdapter,
2096
3013
  createAnyModelServer,
3014
+ createOpenAIBatchAdapter,
3015
+ ensureDir,
3016
+ getFsQueueStatus,
3017
+ joinPath,
3018
+ readFileQueued,
2097
3019
  resolveConfig,
2098
- startServer
3020
+ startServer,
3021
+ waitForFsQueuesIdle,
3022
+ writeFileFlushedQueued,
3023
+ writeFileQueued
2099
3024
  });
2100
3025
  //# sourceMappingURL=index.cjs.map