getaiapi 0.4.12 → 1.0.0-alpha.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.
@@ -1,164 +1,21 @@
1
- // src/gateway.ts
2
- import { randomUUID as randomUUID2 } from "crypto";
3
-
4
- // src/errors.ts
5
- var GetAIApiError = class extends Error {
6
- constructor(message) {
7
- super(message);
8
- this.name = "GetAIApiError";
9
- }
10
- };
11
- var AuthError = class extends GetAIApiError {
12
- provider;
13
- envVar;
14
- constructor(provider, envVar) {
15
- super(`Missing or invalid API key for ${provider}. Set the ${envVar} environment variable.`);
16
- this.name = "AuthError";
17
- this.provider = provider;
18
- this.envVar = envVar;
19
- }
20
- };
21
- var ModelNotFoundError = class extends GetAIApiError {
22
- query;
23
- suggestions;
24
- constructor(query, suggestions = []) {
25
- const hint = suggestions.length > 0 ? ` Did you mean: ${suggestions.join(", ")}?` : "";
26
- super(`Model "${query}" not found.${hint}`);
27
- this.name = "ModelNotFoundError";
28
- this.query = query;
29
- this.suggestions = suggestions;
30
- }
31
- };
32
- var NoProviderError = class extends GetAIApiError {
33
- query;
34
- model;
35
- requiredProviders;
36
- availableProviders;
37
- constructor(query, model, requiredProviders, availableProviders) {
38
- const envHints = {
39
- "fal-ai": "FAL_KEY",
40
- replicate: "REPLICATE_API_TOKEN",
41
- wavespeed: "WAVESPEED_API_KEY",
42
- openrouter: "OPENROUTER_API_KEY"
43
- };
44
- const needed = requiredProviders.map((p) => `${p} (${envHints[p] || "unknown"})`).join(" or ");
45
- super(
46
- `Model "${query}" found but requires ${needed}. You have: ${availableProviders.length > 0 ? availableProviders.join(", ") : "none"}.`
47
- );
48
- this.name = "NoProviderError";
49
- this.query = query;
50
- this.model = model;
51
- this.requiredProviders = requiredProviders;
52
- this.availableProviders = availableProviders;
53
- }
54
- };
55
- var ValidationError = class extends GetAIApiError {
56
- field;
57
- constructor(field, message) {
58
- super(`Validation error on "${field}": ${message}`);
59
- this.name = "ValidationError";
60
- this.field = field;
61
- }
62
- };
63
- var ProviderError = class extends GetAIApiError {
64
- provider;
65
- model;
66
- statusCode;
67
- raw;
68
- constructor(provider, model, statusCode, raw) {
69
- super(
70
- `Provider ${provider} returned status ${statusCode} for model "${model}".`
71
- );
72
- this.name = "ProviderError";
73
- this.provider = provider;
74
- this.model = model;
75
- this.statusCode = statusCode;
76
- this.raw = raw;
77
- }
78
- };
79
- var TimeoutError = class extends GetAIApiError {
80
- provider;
81
- model;
82
- timeoutMs;
83
- constructor(provider, model, timeoutMs) {
84
- super(
85
- `Generation timed out after ${timeoutMs}ms for model "${model}" on ${provider}.`
86
- );
87
- this.name = "TimeoutError";
88
- this.provider = provider;
89
- this.model = model;
90
- this.timeoutMs = timeoutMs;
91
- }
92
- };
93
- var RateLimitError = class extends GetAIApiError {
94
- provider;
95
- retryAfterMs;
96
- constructor(provider, retryAfterMs) {
97
- super(
98
- `Rate limited by ${provider}. Retry after ${retryAfterMs}ms.`
99
- );
100
- this.name = "RateLimitError";
101
- this.provider = provider;
102
- this.retryAfterMs = retryAfterMs;
103
- }
104
- };
105
- var StorageError = class extends GetAIApiError {
106
- operation;
107
- statusCode;
108
- constructor(operation, message, statusCode) {
109
- super(`Storage ${operation} failed: ${message}`);
110
- this.name = "StorageError";
111
- this.operation = operation;
112
- this.statusCode = statusCode;
113
- }
114
- };
1
+ import {
2
+ AuthManager,
3
+ ModelNotFoundError,
4
+ NoProviderError,
5
+ ProviderError,
6
+ ValidationError,
7
+ configureAuth,
8
+ configureStorage,
9
+ falAiAdapter,
10
+ openRouterAdapter,
11
+ processParamsForUpload,
12
+ replicateAdapter,
13
+ wavespeedAdapter,
14
+ withRetry
15
+ } from "./chunk-P7XV6JOH.js";
115
16
 
116
- // src/auth.ts
117
- var ENV_MAP = {
118
- "fal-ai": "FAL_KEY",
119
- replicate: "REPLICATE_API_TOKEN",
120
- wavespeed: "WAVESPEED_API_KEY",
121
- openrouter: "OPENROUTER_API_KEY"
122
- };
123
- var keyOverrides = /* @__PURE__ */ new Map();
124
- function configureAuth(keys) {
125
- for (const [provider, key] of Object.entries(keys)) {
126
- if (key?.trim()) {
127
- keyOverrides.set(provider, key.trim());
128
- }
129
- }
130
- }
131
- var AuthManager = class {
132
- keys;
133
- constructor() {
134
- this.keys = /* @__PURE__ */ new Map();
135
- for (const [provider, key] of keyOverrides) {
136
- this.keys.set(provider, key);
137
- }
138
- for (const [provider, envVar] of Object.entries(ENV_MAP)) {
139
- if (!this.keys.has(provider)) {
140
- const key = process.env[envVar]?.trim();
141
- if (key) this.keys.set(provider, key);
142
- }
143
- }
144
- }
145
- availableProviders() {
146
- return [...this.keys.keys()];
147
- }
148
- getKey(provider) {
149
- const key = this.keys.get(provider);
150
- if (!key) {
151
- throw new AuthError(provider, ENV_MAP[provider]);
152
- }
153
- return key;
154
- }
155
- canAccess(model) {
156
- return model.providers.some((p) => this.keys.has(p.provider));
157
- }
158
- listAvailableModels(registry) {
159
- return registry.filter((m) => this.canAccess(m));
160
- }
161
- };
17
+ // src/gateway.ts
18
+ import { randomUUID } from "crypto";
162
19
 
163
20
  // src/resolver.ts
164
21
  import { readFileSync } from "fs";
@@ -1503,869 +1360,6 @@ function getCategoryTemplate(category) {
1503
1360
  return templates[category];
1504
1361
  }
1505
1362
 
1506
- // src/adapters/fal-ai.ts
1507
- var BASE_URL = "https://queue.fal.run";
1508
- function getBaseEndpoint(endpoint) {
1509
- const parts = endpoint.split("/");
1510
- if (parts.length <= 2) return endpoint;
1511
- return `${parts[0]}/${parts[1]}`;
1512
- }
1513
- async function handleHttpErrors(response, endpoint) {
1514
- if (response.ok) return;
1515
- const status = response.status;
1516
- if (status === 401) {
1517
- throw new AuthError("fal-ai", "FAL_KEY");
1518
- }
1519
- if (status === 429) {
1520
- const retryAfter = response.headers.get("retry-after");
1521
- const retryMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : 6e4;
1522
- throw new RateLimitError("fal-ai", retryMs);
1523
- }
1524
- let raw;
1525
- try {
1526
- raw = await response.json();
1527
- } catch {
1528
- raw = await response.text().catch(() => null);
1529
- }
1530
- throw new ProviderError("fal-ai", endpoint, status, raw);
1531
- }
1532
- function authHeaders(auth) {
1533
- return {
1534
- Authorization: `Key ${auth}`,
1535
- "Content-Type": "application/json"
1536
- };
1537
- }
1538
- var falAiAdapter = {
1539
- name: "fal-ai",
1540
- async submit(endpoint, params, auth) {
1541
- const url = `${BASE_URL}/${endpoint}`;
1542
- const response = await fetch(url, {
1543
- method: "POST",
1544
- headers: authHeaders(auth),
1545
- body: JSON.stringify(params)
1546
- });
1547
- await handleHttpErrors(response, endpoint);
1548
- const data = await response.json();
1549
- return {
1550
- id: data.request_id,
1551
- status: "pending"
1552
- };
1553
- },
1554
- async poll(taskId, auth, endpoint) {
1555
- if (!endpoint) {
1556
- throw new ProviderError("fal-ai", "unknown", 400, "endpoint is required for polling");
1557
- }
1558
- const baseEndpoint = getBaseEndpoint(endpoint);
1559
- const statusUrl = `${BASE_URL}/${baseEndpoint}/requests/${taskId}/status`;
1560
- const statusResponse = await fetch(statusUrl, {
1561
- headers: { Authorization: `Key ${auth}` }
1562
- });
1563
- await handleHttpErrors(statusResponse, endpoint);
1564
- const statusData = await statusResponse.json();
1565
- if (statusData.status === "FAILED") {
1566
- return {
1567
- id: taskId,
1568
- status: "failed",
1569
- error: statusData.error ?? "Unknown error"
1570
- };
1571
- }
1572
- if (statusData.status !== "COMPLETED") {
1573
- return {
1574
- id: taskId,
1575
- status: "processing"
1576
- };
1577
- }
1578
- const resultUrl = `${BASE_URL}/${baseEndpoint}/requests/${taskId}`;
1579
- const resultResponse = await fetch(resultUrl, {
1580
- headers: { Authorization: `Key ${auth}` }
1581
- });
1582
- await handleHttpErrors(resultResponse, endpoint);
1583
- const output = await resultResponse.json();
1584
- return {
1585
- id: taskId,
1586
- status: "completed",
1587
- output
1588
- };
1589
- },
1590
- parseOutput(raw, outputMapping) {
1591
- const data = raw;
1592
- const path = outputMapping.extract_path;
1593
- if (path === "images[].url") {
1594
- const images = data.images;
1595
- if (!Array.isArray(images)) return [];
1596
- return images.map((img) => ({
1597
- type: outputMapping.type,
1598
- url: img.url,
1599
- content_type: img.content_type ?? outputMapping.content_type ?? "image/jpeg"
1600
- }));
1601
- }
1602
- if (path === "image.url") {
1603
- const image = data.image;
1604
- if (!image?.url) return [];
1605
- return [
1606
- {
1607
- type: outputMapping.type,
1608
- url: image.url,
1609
- content_type: image.content_type ?? outputMapping.content_type ?? "image/png"
1610
- }
1611
- ];
1612
- }
1613
- if (path === "video.url") {
1614
- const video = data.video;
1615
- if (!video?.url) return [];
1616
- return [
1617
- {
1618
- type: outputMapping.type,
1619
- url: video.url,
1620
- content_type: video.content_type ?? outputMapping.content_type ?? "video/mp4"
1621
- }
1622
- ];
1623
- }
1624
- if (path === "audio.url") {
1625
- const audio = data.audio;
1626
- if (!audio?.url) return [];
1627
- return [
1628
- {
1629
- type: outputMapping.type,
1630
- url: audio.url,
1631
- content_type: audio.content_type ?? outputMapping.content_type ?? "audio/mpeg"
1632
- }
1633
- ];
1634
- }
1635
- if (path === "audio_file.url") {
1636
- const audioFile = data.audio_file;
1637
- if (!audioFile?.url) return [];
1638
- return [
1639
- {
1640
- type: outputMapping.type,
1641
- url: audioFile.url,
1642
- content_type: audioFile.content_type ?? outputMapping.content_type ?? "audio/mpeg"
1643
- }
1644
- ];
1645
- }
1646
- if (path === "audio_url") {
1647
- const audioUrl = data.audio_url;
1648
- if (!audioUrl) return [];
1649
- const url = typeof audioUrl === "string" ? audioUrl : audioUrl.url;
1650
- if (!url) return [];
1651
- return [
1652
- {
1653
- type: outputMapping.type,
1654
- url,
1655
- content_type: (typeof audioUrl === "object" ? audioUrl.content_type : void 0) ?? outputMapping.content_type ?? "audio/mpeg"
1656
- }
1657
- ];
1658
- }
1659
- if (path === "video_url") {
1660
- const videoUrl = data.video_url;
1661
- if (!videoUrl) return [];
1662
- return [
1663
- {
1664
- type: outputMapping.type,
1665
- url: videoUrl,
1666
- content_type: outputMapping.content_type ?? "video/mp4"
1667
- }
1668
- ];
1669
- }
1670
- return [];
1671
- }
1672
- };
1673
-
1674
- // src/adapters/replicate.ts
1675
- var BASE_URL2 = "https://api.replicate.com/v1";
1676
- async function handleHttpErrors2(response, endpoint) {
1677
- if (response.ok) return;
1678
- const status = response.status;
1679
- if (status === 401) {
1680
- throw new AuthError("replicate", "REPLICATE_API_TOKEN");
1681
- }
1682
- if (status === 429) {
1683
- const retryAfter = response.headers.get("retry-after");
1684
- const retryMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : 6e4;
1685
- throw new RateLimitError("replicate", retryMs);
1686
- }
1687
- let raw;
1688
- try {
1689
- raw = await response.json();
1690
- } catch {
1691
- raw = await response.text().catch(() => null);
1692
- }
1693
- throw new ProviderError("replicate", endpoint, status, raw);
1694
- }
1695
- function authHeaders2(auth) {
1696
- return {
1697
- Authorization: `Bearer ${auth}`,
1698
- "Content-Type": "application/json"
1699
- };
1700
- }
1701
- function inferContentType(url) {
1702
- const lower = url.toLowerCase();
1703
- if (lower.includes(".png")) return "image/png";
1704
- if (lower.includes(".jpg") || lower.includes(".jpeg")) return "image/jpeg";
1705
- if (lower.includes(".webp")) return "image/webp";
1706
- if (lower.includes(".mp4")) return "video/mp4";
1707
- if (lower.includes(".mp3")) return "audio/mpeg";
1708
- if (lower.includes(".wav")) return "audio/wav";
1709
- return "image/jpeg";
1710
- }
1711
- var versionCache = /* @__PURE__ */ new Map();
1712
- var legacyEndpoints = /* @__PURE__ */ new Set();
1713
- async function fetchLatestVersion(endpoint, auth) {
1714
- const cached = versionCache.get(endpoint);
1715
- if (cached) return cached;
1716
- const url = `${BASE_URL2}/models/${endpoint}`;
1717
- const response = await fetch(url, {
1718
- headers: { Authorization: `Bearer ${auth}` }
1719
- });
1720
- await handleHttpErrors2(response, endpoint);
1721
- const data = await response.json();
1722
- if (!data.latest_version?.id) {
1723
- throw new ProviderError("replicate", endpoint, 404, "No version found for model");
1724
- }
1725
- versionCache.set(endpoint, data.latest_version.id);
1726
- return data.latest_version.id;
1727
- }
1728
- async function submitWithVersion(endpoint, params, auth) {
1729
- const version = await fetchLatestVersion(endpoint, auth);
1730
- const response = await fetch(`${BASE_URL2}/predictions`, {
1731
- method: "POST",
1732
- headers: authHeaders2(auth),
1733
- body: JSON.stringify({ version, input: params })
1734
- });
1735
- await handleHttpErrors2(response, endpoint);
1736
- const data = await response.json();
1737
- return { id: data.id, status: "pending" };
1738
- }
1739
- var replicateAdapter = {
1740
- name: "replicate",
1741
- async submit(endpoint, params, auth) {
1742
- if (legacyEndpoints.has(endpoint)) {
1743
- return submitWithVersion(endpoint, params, auth);
1744
- }
1745
- const modelsUrl = `${BASE_URL2}/models/${endpoint}/predictions`;
1746
- const response = await fetch(modelsUrl, {
1747
- method: "POST",
1748
- headers: authHeaders2(auth),
1749
- body: JSON.stringify({ input: params })
1750
- });
1751
- if (response.status !== 404) {
1752
- await handleHttpErrors2(response, endpoint);
1753
- const data = await response.json();
1754
- return { id: data.id, status: "pending" };
1755
- }
1756
- legacyEndpoints.add(endpoint);
1757
- return submitWithVersion(endpoint, params, auth);
1758
- },
1759
- async poll(taskId, auth) {
1760
- const url = `${BASE_URL2}/predictions/${taskId}`;
1761
- const response = await fetch(url, {
1762
- headers: { Authorization: `Bearer ${auth}` }
1763
- });
1764
- await handleHttpErrors2(response, taskId);
1765
- const data = await response.json();
1766
- if (data.status === "succeeded") {
1767
- return {
1768
- id: data.id,
1769
- status: "completed",
1770
- output: data.output
1771
- };
1772
- }
1773
- if (data.status === "failed" || data.status === "canceled") {
1774
- return {
1775
- id: data.id,
1776
- status: "failed",
1777
- error: data.error ?? `Prediction ${data.status}`
1778
- };
1779
- }
1780
- return {
1781
- id: data.id,
1782
- status: "processing"
1783
- };
1784
- },
1785
- parseOutput(raw, outputMapping) {
1786
- if (!Array.isArray(raw)) return [];
1787
- return raw.map((url) => ({
1788
- type: outputMapping.type,
1789
- url,
1790
- content_type: outputMapping.content_type ?? inferContentType(url)
1791
- }));
1792
- }
1793
- };
1794
-
1795
- // src/adapters/wavespeed.ts
1796
- var BASE_URL3 = "https://api.wavespeed.ai/api/v3";
1797
- async function handleHttpErrors3(response, endpoint) {
1798
- if (response.ok) return;
1799
- const status = response.status;
1800
- if (status === 401) {
1801
- throw new AuthError("wavespeed", "WAVESPEED_API_KEY");
1802
- }
1803
- if (status === 429) {
1804
- const retryAfter = response.headers.get("retry-after");
1805
- const retryMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : 6e4;
1806
- throw new RateLimitError("wavespeed", retryMs);
1807
- }
1808
- let raw;
1809
- try {
1810
- raw = await response.json();
1811
- } catch {
1812
- raw = await response.text().catch(() => null);
1813
- }
1814
- throw new ProviderError("wavespeed", endpoint, status, raw);
1815
- }
1816
- function authHeaders3(auth) {
1817
- return {
1818
- Authorization: `Bearer ${auth}`,
1819
- "Content-Type": "application/json"
1820
- };
1821
- }
1822
- function inferContentType2(url) {
1823
- const ext = url.split(".").pop()?.toLowerCase()?.split("?")[0];
1824
- switch (ext) {
1825
- case "png":
1826
- return "image/png";
1827
- case "jpg":
1828
- case "jpeg":
1829
- return "image/jpeg";
1830
- case "webp":
1831
- return "image/webp";
1832
- case "gif":
1833
- return "image/gif";
1834
- case "mp4":
1835
- return "video/mp4";
1836
- case "mp3":
1837
- return "audio/mpeg";
1838
- case "wav":
1839
- return "audio/wav";
1840
- default:
1841
- return "application/octet-stream";
1842
- }
1843
- }
1844
- var wavespeedAdapter = {
1845
- name: "wavespeed",
1846
- async submit(endpoint, params, auth) {
1847
- const url = `${BASE_URL3}/${endpoint}`;
1848
- const response = await fetch(url, {
1849
- method: "POST",
1850
- headers: authHeaders3(auth),
1851
- body: JSON.stringify(params)
1852
- });
1853
- await handleHttpErrors3(response, endpoint);
1854
- const json = await response.json();
1855
- return {
1856
- id: json.data.id,
1857
- status: "pending"
1858
- };
1859
- },
1860
- async poll(taskId, auth) {
1861
- const url = `${BASE_URL3}/predictions/${taskId}/result`;
1862
- const response = await fetch(url, {
1863
- headers: { Authorization: `Bearer ${auth}` }
1864
- });
1865
- await handleHttpErrors3(response, `predictions/${taskId}/result`);
1866
- const json = await response.json();
1867
- const { data } = json;
1868
- if (data.status === "failed") {
1869
- return {
1870
- id: taskId,
1871
- status: "failed",
1872
- error: data.error ?? "Unknown error"
1873
- };
1874
- }
1875
- if (data.status === "completed") {
1876
- return {
1877
- id: taskId,
1878
- status: "completed",
1879
- output: data
1880
- };
1881
- }
1882
- return {
1883
- id: taskId,
1884
- status: "processing"
1885
- };
1886
- },
1887
- parseOutput(raw, outputMapping) {
1888
- const data = raw;
1889
- const outputs = data.outputs;
1890
- if (!Array.isArray(outputs)) return [];
1891
- return outputs.map((url) => ({
1892
- type: outputMapping.type,
1893
- url,
1894
- content_type: outputMapping.content_type ?? inferContentType2(url)
1895
- }));
1896
- }
1897
- };
1898
-
1899
- // src/adapters/openrouter.ts
1900
- var BASE_URL4 = "https://openrouter.ai/api/v1";
1901
- async function handleHttpErrors4(response, endpoint) {
1902
- if (response.ok) return;
1903
- const status = response.status;
1904
- if (status === 401) {
1905
- throw new AuthError("openrouter", "OPENROUTER_API_KEY");
1906
- }
1907
- if (status === 429) {
1908
- const retryAfter = response.headers.get("retry-after");
1909
- const retryMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : 6e4;
1910
- throw new RateLimitError("openrouter", retryMs);
1911
- }
1912
- let raw;
1913
- try {
1914
- raw = await response.json();
1915
- } catch {
1916
- raw = await response.text().catch(() => null);
1917
- }
1918
- throw new ProviderError("openrouter", endpoint, status, raw);
1919
- }
1920
- function authHeaders4(auth) {
1921
- return {
1922
- Authorization: `Bearer ${auth}`,
1923
- "Content-Type": "application/json",
1924
- "X-Title": "getaiapi"
1925
- };
1926
- }
1927
- var openRouterAdapter = {
1928
- name: "openrouter",
1929
- async submit(endpoint, params, auth) {
1930
- const { prompt, system, temperature, max_tokens, top_p, ...rest } = params;
1931
- const messages = [];
1932
- if (system && typeof system === "string") {
1933
- messages.push({ role: "system", content: system });
1934
- }
1935
- messages.push({ role: "user", content: prompt ?? "" });
1936
- const body = {
1937
- model: endpoint,
1938
- messages,
1939
- ...rest
1940
- };
1941
- if (temperature !== void 0) body.temperature = temperature;
1942
- if (max_tokens !== void 0) body.max_tokens = max_tokens;
1943
- if (top_p !== void 0) body.top_p = top_p;
1944
- const url = `${BASE_URL4}/chat/completions`;
1945
- const response = await fetch(url, {
1946
- method: "POST",
1947
- headers: authHeaders4(auth),
1948
- body: JSON.stringify(body)
1949
- });
1950
- await handleHttpErrors4(response, endpoint);
1951
- const data = await response.json();
1952
- return {
1953
- id: data.id,
1954
- status: "completed",
1955
- output: data
1956
- };
1957
- },
1958
- async poll(taskId) {
1959
- return {
1960
- id: taskId,
1961
- status: "completed"
1962
- };
1963
- },
1964
- parseOutput(raw, outputMapping) {
1965
- const data = raw;
1966
- const choices = data.choices;
1967
- if (!Array.isArray(choices) || choices.length === 0) return [];
1968
- const content = choices[0].message?.content;
1969
- if (typeof content !== "string") return [];
1970
- return [
1971
- {
1972
- type: outputMapping.type,
1973
- content,
1974
- content_type: outputMapping.content_type ?? "text/plain"
1975
- }
1976
- ];
1977
- }
1978
- };
1979
-
1980
- // src/retry.ts
1981
- var DEFAULT_OPTIONS = {
1982
- maxRetries: 3,
1983
- initialDelayMs: 1e3,
1984
- maxDelayMs: 1e4,
1985
- timeoutMs: 3e5
1986
- };
1987
- function sleep(ms) {
1988
- return new Promise((resolve2) => setTimeout(resolve2, ms));
1989
- }
1990
- function isRetryable(error) {
1991
- if (error instanceof AuthError || error instanceof ValidationError || error instanceof ModelNotFoundError) {
1992
- return false;
1993
- }
1994
- if (error instanceof RateLimitError) {
1995
- return true;
1996
- }
1997
- if (error instanceof ProviderError) {
1998
- return error.statusCode >= 500;
1999
- }
2000
- if (error instanceof TypeError) {
2001
- return true;
2002
- }
2003
- return false;
2004
- }
2005
- function getDelayMs(error, attempt, options) {
2006
- if (error instanceof RateLimitError) {
2007
- return error.retryAfterMs;
2008
- }
2009
- const jitter = Math.random() * options.initialDelayMs * 0.5;
2010
- const delay = options.initialDelayMs * Math.pow(2, attempt) + jitter;
2011
- return Math.min(delay, options.maxDelayMs);
2012
- }
2013
- async function withRetry(fn, options) {
2014
- const opts = { ...DEFAULT_OPTIONS, ...options };
2015
- const startTime = Date.now();
2016
- let lastError;
2017
- for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
2018
- if (attempt > 0) {
2019
- const elapsed = Date.now() - startTime;
2020
- if (elapsed >= opts.timeoutMs) {
2021
- throw new TimeoutError("unknown", "unknown", opts.timeoutMs);
2022
- }
2023
- }
2024
- try {
2025
- return await fn();
2026
- } catch (error) {
2027
- lastError = error;
2028
- if (!isRetryable(error)) {
2029
- throw error;
2030
- }
2031
- if (attempt >= opts.maxRetries) {
2032
- throw error;
2033
- }
2034
- const delay = getDelayMs(error, attempt, opts);
2035
- const elapsed = Date.now() - startTime;
2036
- if (elapsed + delay >= opts.timeoutMs) {
2037
- throw new TimeoutError("unknown", "unknown", opts.timeoutMs);
2038
- }
2039
- await sleep(delay);
2040
- }
2041
- }
2042
- throw lastError;
2043
- }
2044
-
2045
- // src/storage.ts
2046
- import { randomUUID } from "crypto";
2047
-
2048
- // src/s3-signer.ts
2049
- import { createHmac, createHash } from "crypto";
2050
- function sha256(data) {
2051
- return createHash("sha256").update(data).digest("hex");
2052
- }
2053
- function hmacSha256(key, data) {
2054
- return createHmac("sha256", key).update(data).digest();
2055
- }
2056
- function getSigningKey(secretKey, dateStamp, region, service) {
2057
- const kDate = hmacSha256(`AWS4${secretKey}`, dateStamp);
2058
- const kRegion = hmacSha256(kDate, region);
2059
- const kService = hmacSha256(kRegion, service);
2060
- return hmacSha256(kService, "aws4_request");
2061
- }
2062
- function toAmzDate(date) {
2063
- const iso = date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
2064
- return {
2065
- amzDate: iso,
2066
- dateStamp: iso.slice(0, 8)
2067
- };
2068
- }
2069
- function signS3Request(method, url, headers, body, credentials) {
2070
- const region = credentials.region ?? "auto";
2071
- const service = "s3";
2072
- const parsedUrl = new URL(url);
2073
- const now = /* @__PURE__ */ new Date();
2074
- const { amzDate, dateStamp } = toAmzDate(now);
2075
- const payloadHash = body ? sha256(body) : sha256("");
2076
- const allHeaders = {
2077
- ...headers,
2078
- host: parsedUrl.host,
2079
- "x-amz-date": amzDate,
2080
- "x-amz-content-sha256": payloadHash
2081
- };
2082
- const lcHeaders = Object.fromEntries(
2083
- Object.entries(allHeaders).map(([k, v]) => [k.toLowerCase(), v.trim()])
2084
- );
2085
- const sortedKeys = Object.keys(lcHeaders).sort();
2086
- const canonicalHeaders = sortedKeys.map((k) => `${k}:${lcHeaders[k]}`).join("\n") + "\n";
2087
- const signedHeaders = sortedKeys.join(";");
2088
- const canonicalRequest = [
2089
- method,
2090
- parsedUrl.pathname,
2091
- parsedUrl.searchParams.toString(),
2092
- canonicalHeaders,
2093
- signedHeaders,
2094
- payloadHash
2095
- ].join("\n");
2096
- const scope = `${dateStamp}/${region}/${service}/aws4_request`;
2097
- const stringToSign = [
2098
- "AWS4-HMAC-SHA256",
2099
- amzDate,
2100
- scope,
2101
- sha256(canonicalRequest)
2102
- ].join("\n");
2103
- const signingKey = getSigningKey(credentials.secretAccessKey, dateStamp, region, service);
2104
- const signature = createHmac("sha256", signingKey).update(stringToSign).digest("hex");
2105
- const authorization = `AWS4-HMAC-SHA256 Credential=${credentials.accessKeyId}/${scope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
2106
- return {
2107
- url,
2108
- headers: {
2109
- ...lcHeaders,
2110
- authorization
2111
- }
2112
- };
2113
- }
2114
- function presignS3Url(url, credentials, options) {
2115
- const region = credentials.region ?? "auto";
2116
- const service = "s3";
2117
- const parsedUrl = new URL(url);
2118
- const now = /* @__PURE__ */ new Date();
2119
- const { amzDate, dateStamp } = toAmzDate(now);
2120
- const expiresIn = options?.expiresIn ?? 3600;
2121
- const host = parsedUrl.host;
2122
- const signedHeaders = "host";
2123
- const scope = `${dateStamp}/${region}/${service}/aws4_request`;
2124
- const credential = `${credentials.accessKeyId}/${scope}`;
2125
- const queryParams = new URLSearchParams({
2126
- "X-Amz-Algorithm": "AWS4-HMAC-SHA256",
2127
- "X-Amz-Credential": credential,
2128
- "X-Amz-Date": amzDate,
2129
- "X-Amz-Expires": String(expiresIn),
2130
- "X-Amz-SignedHeaders": signedHeaders
2131
- });
2132
- const canonicalRequest = [
2133
- "GET",
2134
- parsedUrl.pathname,
2135
- queryParams.toString(),
2136
- `host:${host}
2137
- `,
2138
- signedHeaders,
2139
- "UNSIGNED-PAYLOAD"
2140
- ].join("\n");
2141
- const stringToSign = [
2142
- "AWS4-HMAC-SHA256",
2143
- amzDate,
2144
- scope,
2145
- sha256(canonicalRequest)
2146
- ].join("\n");
2147
- const signingKey = getSigningKey(credentials.secretAccessKey, dateStamp, region, service);
2148
- const signature = createHmac("sha256", signingKey).update(stringToSign).digest("hex");
2149
- queryParams.set("X-Amz-Signature", signature);
2150
- return `${parsedUrl.origin}${parsedUrl.pathname}?${queryParams.toString()}`;
2151
- }
2152
-
2153
- // src/storage.ts
2154
- var MAX_PRESIGN_EXPIRES = 604800;
2155
- var storageConfig = null;
2156
- function parseExpiresIn(value) {
2157
- if (!value) return void 0;
2158
- const parsed = parseInt(value, 10);
2159
- if (Number.isNaN(parsed) || parsed <= 0) {
2160
- throw new StorageError("config", `Invalid R2_PRESIGN_EXPIRES_IN: "${value}". Must be a positive integer.`);
2161
- }
2162
- return parsed;
2163
- }
2164
- function configureStorage(config) {
2165
- if (config) {
2166
- storageConfig = config;
2167
- return;
2168
- }
2169
- const accountId = process.env.R2_ACCOUNT_ID;
2170
- const bucketName = process.env.R2_BUCKET_NAME;
2171
- const accessKeyId = process.env.R2_ACCESS_KEY_ID;
2172
- const secretAccessKey = process.env.R2_SECRET_ACCESS_KEY;
2173
- if (!accountId || !bucketName || !accessKeyId || !secretAccessKey) {
2174
- throw new StorageError(
2175
- "config",
2176
- "Missing R2 credentials. Provide a config object or set R2_ACCOUNT_ID, R2_BUCKET_NAME, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY environment variables."
2177
- );
2178
- }
2179
- storageConfig = {
2180
- accountId,
2181
- bucketName,
2182
- accessKeyId,
2183
- secretAccessKey,
2184
- publicUrlBase: process.env.R2_PUBLIC_URL,
2185
- autoUpload: false,
2186
- mode: process.env.R2_STORAGE_MODE === "presigned" ? "presigned" : void 0,
2187
- presignExpiresIn: parseExpiresIn(process.env.R2_PRESIGN_EXPIRES_IN)
2188
- };
2189
- }
2190
- function getStorageConfig() {
2191
- return storageConfig;
2192
- }
2193
- function getConfig() {
2194
- if (!storageConfig) {
2195
- throw new StorageError("config", "Storage not configured. Call configureStorage() first.");
2196
- }
2197
- return storageConfig;
2198
- }
2199
- function buildR2Url(config, key) {
2200
- return `https://${config.accountId}.r2.cloudflarestorage.com/${config.bucketName}/${key}`;
2201
- }
2202
- function buildPublicUrl(config, key) {
2203
- if (config.publicUrlBase) {
2204
- const base = config.publicUrlBase.replace(/\/$/, "");
2205
- return `${base}/${key}`;
2206
- }
2207
- return buildR2Url(config, key);
2208
- }
2209
- function detectContentType(input) {
2210
- if (input instanceof File) return input.type || "application/octet-stream";
2211
- if (input instanceof Blob) return input.type || "application/octet-stream";
2212
- return "application/octet-stream";
2213
- }
2214
- async function toBuffer(input) {
2215
- if (Buffer.isBuffer(input)) return input;
2216
- if (input instanceof ArrayBuffer) return Buffer.from(input);
2217
- if (input instanceof Blob) return Buffer.from(await input.arrayBuffer());
2218
- return Buffer.from(input);
2219
- }
2220
- async function uploadAsset(input, options) {
2221
- const config = getConfig();
2222
- const buffer = await toBuffer(input);
2223
- const maxBytes = options?.maxBytes ?? DEFAULT_MAX_BYTES;
2224
- if (buffer.length > maxBytes) {
2225
- throw new StorageError(
2226
- "upload",
2227
- `Asset too large (${buffer.length} bytes, max ${maxBytes})`
2228
- );
2229
- }
2230
- const contentType = options?.contentType ?? detectContentType(input);
2231
- const prefix = options?.prefix ? `${options.prefix.replace(/\/$/, "")}/` : "";
2232
- const key = options?.key ?? `${prefix}${randomUUID()}`;
2233
- const r2Url = buildR2Url(config, key);
2234
- const signed = signS3Request(
2235
- "PUT",
2236
- r2Url,
2237
- { "Content-Type": contentType, "Content-Length": String(buffer.length) },
2238
- buffer,
2239
- {
2240
- accessKeyId: config.accessKeyId,
2241
- secretAccessKey: config.secretAccessKey
2242
- }
2243
- );
2244
- const response = await fetch(signed.url, {
2245
- method: "PUT",
2246
- headers: signed.headers,
2247
- body: new Uint8Array(buffer)
2248
- });
2249
- if (!response.ok) {
2250
- const body = await response.text().catch(() => "");
2251
- throw new StorageError("upload", `R2 returned ${response.status}: ${body}`, response.status);
2252
- }
2253
- const url = config.mode === "presigned" ? validatedPresign(config, key) : buildPublicUrl(config, key);
2254
- return {
2255
- url,
2256
- key,
2257
- size_bytes: buffer.length,
2258
- content_type: contentType
2259
- };
2260
- }
2261
- function validatedPresign(config, key, expiresIn) {
2262
- const ttl = expiresIn ?? config.presignExpiresIn ?? 3600;
2263
- if (ttl > MAX_PRESIGN_EXPIRES) {
2264
- throw new StorageError(
2265
- "config",
2266
- `Presign expiry ${ttl}s exceeds maximum of ${MAX_PRESIGN_EXPIRES}s (7 days).`
2267
- );
2268
- }
2269
- return presignS3Url(buildR2Url(config, key), {
2270
- accessKeyId: config.accessKeyId,
2271
- secretAccessKey: config.secretAccessKey
2272
- }, { expiresIn: ttl });
2273
- }
2274
- function presignAsset(key, options) {
2275
- const config = getConfig();
2276
- return validatedPresign(config, key, options?.expiresIn);
2277
- }
2278
- async function deleteAsset(key) {
2279
- const config = getConfig();
2280
- const r2Url = buildR2Url(config, key);
2281
- const signed = signS3Request(
2282
- "DELETE",
2283
- r2Url,
2284
- {},
2285
- null,
2286
- {
2287
- accessKeyId: config.accessKeyId,
2288
- secretAccessKey: config.secretAccessKey
2289
- }
2290
- );
2291
- const response = await fetch(signed.url, {
2292
- method: "DELETE",
2293
- headers: signed.headers
2294
- });
2295
- if (!response.ok && response.status !== 404) {
2296
- const body = await response.text().catch(() => "");
2297
- throw new StorageError("delete", `R2 returned ${response.status}: ${body}`, response.status);
2298
- }
2299
- }
2300
- var DEFAULT_PREFIX = "getaiapi-tmp";
2301
- var DEFAULT_MAX_BYTES = 500 * 1024 * 1024;
2302
- function isBinaryValue(value) {
2303
- return Buffer.isBuffer(value) || value instanceof File || value instanceof Blob || value instanceof ArrayBuffer;
2304
- }
2305
- function isUrl(value) {
2306
- return typeof value === "string" && (value.startsWith("http://") || value.startsWith("https://"));
2307
- }
2308
- async function fetchAndReupload(url, maxBytes) {
2309
- const response = await fetch(url);
2310
- if (!response.ok) {
2311
- throw new StorageError("upload", `Failed to fetch URL for re-upload: ${url}`);
2312
- }
2313
- const contentLength = response.headers.get("content-length");
2314
- if (contentLength && parseInt(contentLength, 10) > maxBytes) {
2315
- throw new StorageError(
2316
- "upload",
2317
- `URL content too large (${contentLength} bytes, max ${maxBytes}): ${url}`
2318
- );
2319
- }
2320
- const buffer = Buffer.from(await response.arrayBuffer());
2321
- if (buffer.length > maxBytes) {
2322
- throw new StorageError(
2323
- "upload",
2324
- `URL content too large (${buffer.length} bytes, max ${maxBytes}): ${url}`
2325
- );
2326
- }
2327
- const contentType = response.headers.get("content-type") ?? "application/octet-stream";
2328
- const uploaded = await uploadAsset(buffer, { contentType, prefix: DEFAULT_PREFIX });
2329
- return uploaded.url;
2330
- }
2331
- async function processValue(value, shouldReupload, maxBytes) {
2332
- if (isBinaryValue(value)) {
2333
- const uploaded = await uploadAsset(value, { prefix: DEFAULT_PREFIX });
2334
- return uploaded.url;
2335
- }
2336
- if (isUrl(value) && shouldReupload) {
2337
- return fetchAndReupload(value, maxBytes);
2338
- }
2339
- if (Array.isArray(value)) {
2340
- const results = await Promise.all(
2341
- value.map((item) => processValue(item, shouldReupload, maxBytes))
2342
- );
2343
- return results;
2344
- }
2345
- if (value !== null && typeof value === "object" && !(value instanceof Date)) {
2346
- return processRecord(
2347
- value,
2348
- shouldReupload,
2349
- maxBytes
2350
- );
2351
- }
2352
- return value;
2353
- }
2354
- async function processRecord(obj, shouldReupload, maxBytes) {
2355
- const result = {};
2356
- for (const [key, value] of Object.entries(obj)) {
2357
- result[key] = await processValue(value, shouldReupload, maxBytes);
2358
- }
2359
- return result;
2360
- }
2361
- async function processParamsForUpload(params, options) {
2362
- const config = getStorageConfig();
2363
- if (!config) return params;
2364
- const shouldReupload = options?.reupload || config.autoUpload || false;
2365
- const maxBytes = options?.maxBytes ?? DEFAULT_MAX_BYTES;
2366
- return processRecord(params, shouldReupload, maxBytes);
2367
- }
2368
-
2369
1363
  // src/gateway.ts
2370
1364
  var adapters = {
2371
1365
  "fal-ai": falAiAdapter,
@@ -2430,7 +1424,7 @@ async function generate(request) {
2430
1424
  metadata.completion_tokens = usage.completion_tokens;
2431
1425
  }
2432
1426
  return {
2433
- id: randomUUID2(),
1427
+ id: randomUUID(),
2434
1428
  model: model.canonical_name,
2435
1429
  provider: binding.provider,
2436
1430
  status: "completed",
@@ -2477,23 +1471,9 @@ function getModel(name) {
2477
1471
  }
2478
1472
 
2479
1473
  export {
2480
- GetAIApiError,
2481
- AuthError,
2482
- ModelNotFoundError,
2483
- NoProviderError,
2484
- ValidationError,
2485
- ProviderError,
2486
- TimeoutError,
2487
- RateLimitError,
2488
- StorageError,
2489
- configureAuth,
2490
- configureStorage,
2491
- uploadAsset,
2492
- presignAsset,
2493
- deleteAsset,
2494
1474
  generate,
2495
1475
  configure,
2496
1476
  listModels,
2497
1477
  getModel
2498
1478
  };
2499
- //# sourceMappingURL=chunk-LZV7KTSS.js.map
1479
+ //# sourceMappingURL=chunk-LHDSEV4H.js.map