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 +39 -2
- package/dist/{chunk-RC2KZLI2.js → chunk-7UTLP4YN.js} +185 -11
- package/dist/chunk-7UTLP4YN.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.js +1 -1
- package/package.json +5 -1
- package/registry/registry.json +305 -0
- package/dist/chunk-RC2KZLI2.js.map +0 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](https://www.typescriptlang.org/)
|
|
8
8
|
|
|
9
|
-
A unified TypeScript library that wraps 1,
|
|
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
|
|
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
|
|
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}/${
|
|
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-
|
|
1907
|
+
//# sourceMappingURL=chunk-7UTLP4YN.js.map
|