getaiapi 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue.svg)](https://www.typescriptlang.org/)
8
8
 
9
- A unified TypeScript library that wraps 1,930 AI models across 3 providers into a single `generate()` function. One input shape. One output shape. Any model.
9
+ A unified TypeScript library that wraps 1,940+ AI models across 4 providers into a single `generate()` function. One input shape. One output shape. Any model.
10
10
 
11
11
  ## Install
12
12
 
@@ -29,6 +29,31 @@ console.log(result.outputs[0].url)
29
29
 
30
30
  ## More Examples
31
31
 
32
+ **Text generation (LLMs)**
33
+
34
+ ```typescript
35
+ const answer = await generate({
36
+ model: 'claude-sonnet-4-6',
37
+ prompt: 'Explain quantum computing in one paragraph'
38
+ })
39
+
40
+ console.log(answer.outputs[0].content)
41
+ ```
42
+
43
+ With system prompt and parameters:
44
+
45
+ ```typescript
46
+ const reply = await generate({
47
+ model: 'gpt-4o',
48
+ prompt: 'Write a haiku about TypeScript',
49
+ options: {
50
+ system: 'You are a creative poet.',
51
+ temperature: 0.9,
52
+ max_tokens: 100,
53
+ }
54
+ })
55
+ ```
56
+
32
57
  **Text-to-video**
33
58
 
34
59
  ```typescript
@@ -105,6 +130,9 @@ export REPLICATE_API_TOKEN="your-replicate-token"
105
130
 
106
131
  # WaveSpeed (66 models)
107
132
  export WAVESPEED_API_KEY="your-wavespeed-key"
133
+
134
+ # OpenRouter (10+ LLM models — Claude, GPT, Gemini, Llama, etc.)
135
+ export OPENROUTER_API_KEY="your-openrouter-key"
108
136
  ```
109
137
 
110
138
  ### Option 2: Programmatic Configuration
@@ -119,6 +147,7 @@ configure({
119
147
  'fal-ai': process.env.MY_FAL_TOKEN,
120
148
  'replicate': process.env.MY_REPLICATE_TOKEN,
121
149
  'wavespeed': process.env.MY_WAVESPEED_TOKEN,
150
+ 'openrouter': process.env.MY_OPENROUTER_TOKEN,
122
151
  },
123
152
  })
124
153
  ```
@@ -197,6 +226,7 @@ const model = getModel('flux-schnell')
197
226
  | `video-to-audio` | video | audio | 17 |
198
227
  | `upscale-video` | video | video | 15 |
199
228
  | `video-to-video` | video | video | 15 |
229
+ | `text-generation` | text | text | 10 |
200
230
  | `moderation` | text/image/video | text | 8 |
201
231
  | `audio-to-video` | audio | video | 4 |
202
232
  | `audio-edit` | audio | audio | 2 |
@@ -209,9 +239,12 @@ const model = getModel('flux-schnell')
209
239
  | fal-ai | 1,201 | `FAL_KEY` | Native fetch |
210
240
  | Replicate | 687 | `REPLICATE_API_TOKEN` | Native fetch |
211
241
  | WaveSpeed | 66 | `WAVESPEED_API_KEY` | Native fetch |
242
+ | OpenRouter | 10 | `OPENROUTER_API_KEY` | Native fetch |
212
243
 
213
244
  Zero external dependencies -- all provider communication uses native `fetch`.
214
245
 
246
+ See the full [Model Directory](docs/MODELS.md) for all 1,940 models with provider availability.
247
+
215
248
  ## API Reference
216
249
 
217
250
  ### `generate(request: GenerateRequest): Promise<GenerateResponse>`
@@ -256,12 +289,16 @@ interface GenerateResponse {
256
289
  inference_time_ms?: number
257
290
  cost?: number
258
291
  safety_flagged?: boolean
292
+ tokens?: number // total tokens (LLM only)
293
+ prompt_tokens?: number // input tokens (LLM only)
294
+ completion_tokens?: number // output tokens (LLM only)
259
295
  }
260
296
  }
261
297
 
262
298
  interface OutputItem {
263
299
  type: 'image' | 'video' | 'audio' | 'text' | '3d' | 'segmentation'
264
- url: string
300
+ url?: string // URL for media outputs
301
+ content?: string // text content for LLM outputs
265
302
  content_type: string
266
303
  size_bytes?: number
267
304
  }
@@ -94,7 +94,8 @@ var StorageError = class extends GetAIApiError {
94
94
  var ENV_MAP = {
95
95
  "fal-ai": "FAL_KEY",
96
96
  replicate: "REPLICATE_API_TOKEN",
97
- wavespeed: "WAVESPEED_API_KEY"
97
+ wavespeed: "WAVESPEED_API_KEY",
98
+ openrouter: "OPENROUTER_API_KEY"
98
99
  };
99
100
  var keyOverrides = /* @__PURE__ */ new Map();
100
101
  function configureAuth(keys) {
@@ -381,6 +382,9 @@ function genericExtract(data, path, type, contentType) {
381
382
  current = current[seg];
382
383
  }
383
384
  if (typeof current === "string") {
385
+ if (type === "text") {
386
+ return [{ type, content: current, content_type: contentType }];
387
+ }
384
388
  return [{ type, url: current, content_type: contentType }];
385
389
  }
386
390
  return [];
@@ -504,6 +508,14 @@ var imageEditTemplate = {
504
508
  },
505
509
  required: true
506
510
  },
511
+ {
512
+ universal: "images",
513
+ providers: {
514
+ "fal-ai": "image_urls",
515
+ "replicate": "image_urls",
516
+ "wavespeed": "image_urls"
517
+ }
518
+ },
507
519
  {
508
520
  universal: "prompt",
509
521
  providers: {
@@ -897,6 +909,25 @@ var removeBackgroundTemplate = {
897
909
  default_timeout_ms: 6e4
898
910
  };
899
911
 
912
+ // src/categories/text-generation.ts
913
+ var textGenerationTemplate = {
914
+ category: "text-generation",
915
+ input_mappings: [
916
+ {
917
+ universal: "prompt",
918
+ providers: {
919
+ openrouter: "prompt"
920
+ },
921
+ required: true
922
+ }
923
+ ],
924
+ output_type: "text",
925
+ output_extract: {
926
+ openrouter: "choices[0].message.content"
927
+ },
928
+ default_timeout_ms: 12e4
929
+ };
930
+
900
931
  // src/categories/index.ts
901
932
  var templates = {
902
933
  "text-to-image": textToImageTemplate,
@@ -906,7 +937,8 @@ var templates = {
906
937
  "upscale-image": upscaleImageTemplate,
907
938
  "text-to-audio": textToAudioTemplate,
908
939
  "audio-to-text": audioToTextTemplate,
909
- "remove-background": removeBackgroundTemplate
940
+ "remove-background": removeBackgroundTemplate,
941
+ "text-generation": textGenerationTemplate
910
942
  };
911
943
  function getCategoryTemplate(category) {
912
944
  return templates[category];
@@ -914,6 +946,11 @@ function getCategoryTemplate(category) {
914
946
 
915
947
  // src/adapters/fal-ai.ts
916
948
  var BASE_URL = "https://queue.fal.run";
949
+ function getBaseEndpoint(endpoint) {
950
+ const parts = endpoint.split("/");
951
+ if (parts.length <= 2) return endpoint;
952
+ return `${parts[0]}/${parts[1]}`;
953
+ }
917
954
  async function handleHttpErrors(response, endpoint) {
918
955
  if (response.ok) return;
919
956
  const status = response.status;
@@ -959,7 +996,8 @@ var falAiAdapter = {
959
996
  if (!endpoint) {
960
997
  throw new ProviderError("fal-ai", "unknown", 400, "endpoint is required for polling");
961
998
  }
962
- const statusUrl = `${BASE_URL}/${endpoint}/requests/${taskId}/status`;
999
+ const baseEndpoint = getBaseEndpoint(endpoint);
1000
+ const statusUrl = `${BASE_URL}/${baseEndpoint}/requests/${taskId}/status`;
963
1001
  const statusResponse = await fetch(statusUrl, {
964
1002
  headers: { Authorization: `Key ${auth}` }
965
1003
  });
@@ -978,7 +1016,7 @@ var falAiAdapter = {
978
1016
  status: "processing"
979
1017
  };
980
1018
  }
981
- const resultUrl = `${BASE_URL}/${endpoint}/requests/${taskId}`;
1019
+ const resultUrl = `${BASE_URL}/${baseEndpoint}/requests/${taskId}`;
982
1020
  const resultResponse = await fetch(resultUrl, {
983
1021
  headers: { Authorization: `Key ${auth}` }
984
1022
  });
@@ -1002,6 +1040,17 @@ var falAiAdapter = {
1002
1040
  content_type: img.content_type ?? outputMapping.content_type ?? "image/jpeg"
1003
1041
  }));
1004
1042
  }
1043
+ if (path === "image.url") {
1044
+ const image = data.image;
1045
+ if (!image?.url) return [];
1046
+ return [
1047
+ {
1048
+ type: outputMapping.type,
1049
+ url: image.url,
1050
+ content_type: image.content_type ?? outputMapping.content_type ?? "image/png"
1051
+ }
1052
+ ];
1053
+ }
1005
1054
  if (path === "video.url") {
1006
1055
  const video = data.video;
1007
1056
  if (!video?.url) return [];
@@ -1024,6 +1073,41 @@ var falAiAdapter = {
1024
1073
  }
1025
1074
  ];
1026
1075
  }
1076
+ if (path === "audio_file.url") {
1077
+ const audioFile = data.audio_file;
1078
+ if (!audioFile?.url) return [];
1079
+ return [
1080
+ {
1081
+ type: outputMapping.type,
1082
+ url: audioFile.url,
1083
+ content_type: audioFile.content_type ?? outputMapping.content_type ?? "audio/mpeg"
1084
+ }
1085
+ ];
1086
+ }
1087
+ if (path === "audio_url") {
1088
+ const audioUrl = data.audio_url;
1089
+ if (!audioUrl) return [];
1090
+ const url = typeof audioUrl === "string" ? audioUrl : audioUrl.url;
1091
+ if (!url) return [];
1092
+ return [
1093
+ {
1094
+ type: outputMapping.type,
1095
+ url,
1096
+ content_type: (typeof audioUrl === "object" ? audioUrl.content_type : void 0) ?? outputMapping.content_type ?? "audio/mpeg"
1097
+ }
1098
+ ];
1099
+ }
1100
+ if (path === "video_url") {
1101
+ const videoUrl = data.video_url;
1102
+ if (!videoUrl) return [];
1103
+ return [
1104
+ {
1105
+ type: outputMapping.type,
1106
+ url: videoUrl,
1107
+ content_type: outputMapping.content_type ?? "video/mp4"
1108
+ }
1109
+ ];
1110
+ }
1027
1111
  return [];
1028
1112
  }
1029
1113
  };
@@ -1221,6 +1305,87 @@ var wavespeedAdapter = {
1221
1305
  }
1222
1306
  };
1223
1307
 
1308
+ // src/adapters/openrouter.ts
1309
+ var BASE_URL4 = "https://openrouter.ai/api/v1";
1310
+ async function handleHttpErrors4(response, endpoint) {
1311
+ if (response.ok) return;
1312
+ const status = response.status;
1313
+ if (status === 401) {
1314
+ throw new AuthError("openrouter", "OPENROUTER_API_KEY");
1315
+ }
1316
+ if (status === 429) {
1317
+ const retryAfter = response.headers.get("retry-after");
1318
+ const retryMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : 6e4;
1319
+ throw new RateLimitError("openrouter", retryMs);
1320
+ }
1321
+ let raw;
1322
+ try {
1323
+ raw = await response.json();
1324
+ } catch {
1325
+ raw = await response.text().catch(() => null);
1326
+ }
1327
+ throw new ProviderError("openrouter", endpoint, status, raw);
1328
+ }
1329
+ function authHeaders4(auth) {
1330
+ return {
1331
+ Authorization: `Bearer ${auth}`,
1332
+ "Content-Type": "application/json",
1333
+ "X-Title": "getaiapi"
1334
+ };
1335
+ }
1336
+ var openRouterAdapter = {
1337
+ name: "openrouter",
1338
+ async submit(endpoint, params, auth) {
1339
+ const { prompt, system, temperature, max_tokens, top_p, ...rest } = params;
1340
+ const messages = [];
1341
+ if (system && typeof system === "string") {
1342
+ messages.push({ role: "system", content: system });
1343
+ }
1344
+ messages.push({ role: "user", content: prompt ?? "" });
1345
+ const body = {
1346
+ model: endpoint,
1347
+ messages,
1348
+ ...rest
1349
+ };
1350
+ if (temperature !== void 0) body.temperature = temperature;
1351
+ if (max_tokens !== void 0) body.max_tokens = max_tokens;
1352
+ if (top_p !== void 0) body.top_p = top_p;
1353
+ const url = `${BASE_URL4}/chat/completions`;
1354
+ const response = await fetch(url, {
1355
+ method: "POST",
1356
+ headers: authHeaders4(auth),
1357
+ body: JSON.stringify(body)
1358
+ });
1359
+ await handleHttpErrors4(response, endpoint);
1360
+ const data = await response.json();
1361
+ return {
1362
+ id: data.id,
1363
+ status: "completed",
1364
+ output: data
1365
+ };
1366
+ },
1367
+ async poll(taskId) {
1368
+ return {
1369
+ id: taskId,
1370
+ status: "completed"
1371
+ };
1372
+ },
1373
+ parseOutput(raw, outputMapping) {
1374
+ const data = raw;
1375
+ const choices = data.choices;
1376
+ if (!Array.isArray(choices) || choices.length === 0) return [];
1377
+ const content = choices[0].message?.content;
1378
+ if (typeof content !== "string") return [];
1379
+ return [
1380
+ {
1381
+ type: outputMapping.type,
1382
+ content,
1383
+ content_type: outputMapping.content_type ?? "text/plain"
1384
+ }
1385
+ ];
1386
+ }
1387
+ };
1388
+
1224
1389
  // src/retry.ts
1225
1390
  var DEFAULT_OPTIONS = {
1226
1391
  maxRetries: 3,
@@ -1614,7 +1779,8 @@ async function processParamsForUpload(params, options) {
1614
1779
  var adapters = {
1615
1780
  "fal-ai": falAiAdapter,
1616
1781
  "replicate": replicateAdapter,
1617
- "wavespeed": wavespeedAdapter
1782
+ "wavespeed": wavespeedAdapter,
1783
+ "openrouter": openRouterAdapter
1618
1784
  };
1619
1785
  async function generate(request) {
1620
1786
  const startTime = Date.now();
@@ -1660,17 +1826,25 @@ async function generate(request) {
1660
1826
  );
1661
1827
  }
1662
1828
  const outputs = mapOutput(result.output, binding.output_map);
1829
+ const rawOutput = typeof result.output === "object" && result.output !== null ? result.output : void 0;
1830
+ const metadata = {
1831
+ inference_time_ms: Date.now() - startTime,
1832
+ seed: rawOutput?.seed,
1833
+ safety_flagged: rawOutput ? Array.isArray(rawOutput.has_nsfw_concepts) ? rawOutput.has_nsfw_concepts.some((v) => v) : void 0 : void 0
1834
+ };
1835
+ if (rawOutput?.usage) {
1836
+ const usage = rawOutput.usage;
1837
+ metadata.tokens = usage.total_tokens;
1838
+ metadata.prompt_tokens = usage.prompt_tokens;
1839
+ metadata.completion_tokens = usage.completion_tokens;
1840
+ }
1663
1841
  return {
1664
1842
  id: randomUUID2(),
1665
1843
  model: model.canonical_name,
1666
1844
  provider: binding.provider,
1667
1845
  status: "completed",
1668
1846
  outputs,
1669
- metadata: {
1670
- inference_time_ms: Date.now() - startTime,
1671
- seed: typeof result.output === "object" && result.output !== null ? result.output.seed : void 0,
1672
- safety_flagged: typeof result.output === "object" && result.output !== null ? Array.isArray(result.output.has_nsfw_concepts) ? result.output.has_nsfw_concepts.some((v) => v) : void 0 : void 0
1673
- }
1847
+ metadata
1674
1848
  };
1675
1849
  }
1676
1850
 
@@ -1730,4 +1904,4 @@ export {
1730
1904
  listModels,
1731
1905
  getModel
1732
1906
  };
1733
- //# sourceMappingURL=chunk-RC2KZLI2.js.map
1907
+ //# sourceMappingURL=chunk-7UTLP4YN.js.map