@macpaw/ai-sdk 0.1.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/CHANGELOG.md +39 -0
- package/LICENSE +21 -0
- package/MIGRATION.md +52 -0
- package/README.md +272 -0
- package/dist/chunk-KGOVQRMH.js +287 -0
- package/dist/chunk-KGOVQRMH.js.map +1 -0
- package/dist/chunk-N26BDEG5.cjs +302 -0
- package/dist/chunk-N26BDEG5.cjs.map +1 -0
- package/dist/gateway-errors-DdgDIyQw.d.cts +181 -0
- package/dist/gateway-errors-DdgDIyQw.d.ts +181 -0
- package/dist/index.cjs +483 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +109 -0
- package/dist/index.d.ts +109 -0
- package/dist/index.js +438 -0
- package/dist/index.js.map +1 -0
- package/dist/nestjs/index.cjs +149 -0
- package/dist/nestjs/index.cjs.map +1 -0
- package/dist/nestjs/index.d.cts +117 -0
- package/dist/nestjs/index.d.ts +117 -0
- package/dist/nestjs/index.js +145 -0
- package/dist/nestjs/index.js.map +1 -0
- package/package.json +128 -0
- package/scripts/setup.mjs +90 -0
- package/templates/AGENTS.md +51 -0
- package/templates/CLAUDE.md +55 -0
- package/templates/cursor/skills/integrate-ai-gateway/SKILL.md +90 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration types and resolver for the AI Gateway SDK.
|
|
3
|
+
*
|
|
4
|
+
* GatewayProviderSettings is the public API — 8 fields.
|
|
5
|
+
* ResolvedConfig is the internal resolved form used by the request pipeline.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Supported gateway environments.
|
|
9
|
+
* Only 'production' is exposed — non-production setups use a direct `baseURL`
|
|
10
|
+
* instead, keeping env-specific URL management out of this library.
|
|
11
|
+
*/
|
|
12
|
+
type Environment = 'production';
|
|
13
|
+
interface RetryConfig {
|
|
14
|
+
/** Max number of attempts (including first). Default 3. */
|
|
15
|
+
maxAttempts?: number;
|
|
16
|
+
/** Initial delay in ms. Default 1000. */
|
|
17
|
+
initialDelayMs?: number;
|
|
18
|
+
/** Max delay in ms. Default 30000. */
|
|
19
|
+
maxDelayMs?: number;
|
|
20
|
+
/** Retry only on these status codes (and network errors). Default: 429, 5xx. */
|
|
21
|
+
retryableStatuses?: number[];
|
|
22
|
+
}
|
|
23
|
+
interface RequestConfig {
|
|
24
|
+
url: string;
|
|
25
|
+
method: string;
|
|
26
|
+
headers: Record<string, string>;
|
|
27
|
+
body?: RequestInit['body'];
|
|
28
|
+
signal?: AbortSignal | undefined;
|
|
29
|
+
}
|
|
30
|
+
type Middleware = (config: RequestConfig, next: (config: RequestConfig) => Promise<Response>) => Promise<Response>;
|
|
31
|
+
/**
|
|
32
|
+
* Configuration for AI Gateway providers and NestJS module.
|
|
33
|
+
*
|
|
34
|
+
* Exactly 8 fields — covers auth, routing, request behaviour and middleware.
|
|
35
|
+
*/
|
|
36
|
+
interface GatewayProviderSettings {
|
|
37
|
+
/** Gateway base URL. Required if env is not set. */
|
|
38
|
+
baseURL?: string;
|
|
39
|
+
/** 'production' uses the default MacPaw Gateway URL. */
|
|
40
|
+
env?: Environment;
|
|
41
|
+
/**
|
|
42
|
+
* Returns Bearer token for auth.
|
|
43
|
+
* Called with `forceRefresh=true` after 401 — provide a fresh token.
|
|
44
|
+
*/
|
|
45
|
+
getAuthToken: (forceRefresh?: boolean) => Promise<string | null>;
|
|
46
|
+
/** Extra headers sent with every request. Do not set Authorization here. */
|
|
47
|
+
headers?: Record<string, string>;
|
|
48
|
+
/** Retry policy. Set to false to disable. */
|
|
49
|
+
retry?: RetryConfig | false;
|
|
50
|
+
/** Request timeout in ms. Default: 60000. */
|
|
51
|
+
timeout?: number;
|
|
52
|
+
/** Request interceptors. Run in order before each request. */
|
|
53
|
+
middleware?: Middleware[];
|
|
54
|
+
/**
|
|
55
|
+
* Custom fetch implementation.
|
|
56
|
+
* Use for testing or low-level request customization.
|
|
57
|
+
*/
|
|
58
|
+
fetch?: typeof fetch;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Error types for the AI Gateway SDK.
|
|
63
|
+
*
|
|
64
|
+
* Combines error classes, API codes, and response shapes in one place.
|
|
65
|
+
* parseErrorResponse throws the SPECIFIC subclass (AuthError, CreditsError, etc.)
|
|
66
|
+
* so that `instanceof` checks work correctly in consumer code.
|
|
67
|
+
*/
|
|
68
|
+
type ObjectValues<T> = T[keyof T];
|
|
69
|
+
/** Raw error codes returned by the AI Gateway HTTP API. */
|
|
70
|
+
declare const GatewayApiCode: {
|
|
71
|
+
readonly BadRequest: "BAD_REQUEST";
|
|
72
|
+
readonly Unauthorized: "UNAUTHORIZED";
|
|
73
|
+
readonly InsufficientCredits: "INSUFFICIENT_CREDITS";
|
|
74
|
+
readonly Forbidden: "FORBIDDEN";
|
|
75
|
+
readonly Validation: "VALIDATION";
|
|
76
|
+
readonly RateLimitExceeded: "RATE_LIMIT_EXCEEDED";
|
|
77
|
+
readonly InternalServerError: "INTERNAL_SERVER_ERROR";
|
|
78
|
+
readonly ServiceUnavailable: "SERVICE_UNAVAILABLE";
|
|
79
|
+
readonly Timeout: "TIMEOUT";
|
|
80
|
+
readonly NotFound: "NOT_FOUND";
|
|
81
|
+
readonly Conflict: "CONFLICT";
|
|
82
|
+
};
|
|
83
|
+
type GatewayApiCode = ObjectValues<typeof GatewayApiCode>;
|
|
84
|
+
/** Normalized error codes for app handling. */
|
|
85
|
+
declare const ErrorCode: {
|
|
86
|
+
readonly AuthRequired: "AUTH_REQUIRED";
|
|
87
|
+
readonly InsufficientCredits: "INSUFFICIENT_CREDITS";
|
|
88
|
+
readonly SubscriptionExpired: "SUBSCRIPTION_EXPIRED";
|
|
89
|
+
readonly ModelNotAllowed: "MODEL_NOT_ALLOWED";
|
|
90
|
+
readonly RateLimited: "RATE_LIMITED";
|
|
91
|
+
readonly BadRequest: "BAD_REQUEST";
|
|
92
|
+
readonly Validation: "VALIDATION";
|
|
93
|
+
readonly Forbidden: "FORBIDDEN";
|
|
94
|
+
readonly InternalServerError: "INTERNAL_SERVER_ERROR";
|
|
95
|
+
readonly ServiceUnavailable: "SERVICE_UNAVAILABLE";
|
|
96
|
+
readonly Timeout: "TIMEOUT";
|
|
97
|
+
readonly NotFound: "NOT_FOUND";
|
|
98
|
+
readonly Conflict: "CONFLICT";
|
|
99
|
+
};
|
|
100
|
+
type ErrorCode = ObjectValues<typeof ErrorCode>;
|
|
101
|
+
interface GatewayApiErrorItem {
|
|
102
|
+
target?: string;
|
|
103
|
+
property?: string;
|
|
104
|
+
constraints?: string[];
|
|
105
|
+
message?: string;
|
|
106
|
+
metadata?: Record<string, unknown>;
|
|
107
|
+
}
|
|
108
|
+
interface GatewayApiErrorResponse {
|
|
109
|
+
statusCode: number;
|
|
110
|
+
message: string;
|
|
111
|
+
timestamp: string;
|
|
112
|
+
code: GatewayApiCode;
|
|
113
|
+
path?: string;
|
|
114
|
+
errors?: GatewayApiErrorItem[];
|
|
115
|
+
request_id?: string;
|
|
116
|
+
}
|
|
117
|
+
interface OpenAIErrorResponse {
|
|
118
|
+
error: {
|
|
119
|
+
message: string;
|
|
120
|
+
type?: string | null;
|
|
121
|
+
code?: string | null;
|
|
122
|
+
param?: string | null;
|
|
123
|
+
};
|
|
124
|
+
request_id?: string;
|
|
125
|
+
}
|
|
126
|
+
interface NormalizedErrorMetadata {
|
|
127
|
+
paymentUrl?: string;
|
|
128
|
+
/** Suggested delay before retrying, in seconds (from server's `Retry-After`). */
|
|
129
|
+
retryAfter?: number;
|
|
130
|
+
requestId?: string;
|
|
131
|
+
path?: string;
|
|
132
|
+
timestamp?: string;
|
|
133
|
+
errors?: GatewayApiErrorItem[];
|
|
134
|
+
}
|
|
135
|
+
declare class AIGatewayError extends Error {
|
|
136
|
+
readonly code: ErrorCode;
|
|
137
|
+
readonly statusCode: number;
|
|
138
|
+
readonly metadata: NormalizedErrorMetadata;
|
|
139
|
+
constructor(message: string, code: ErrorCode, statusCode: number, metadata?: NormalizedErrorMetadata, options?: {
|
|
140
|
+
cause?: unknown;
|
|
141
|
+
});
|
|
142
|
+
get paymentUrl(): string | undefined;
|
|
143
|
+
/** Suggested delay before retrying, in seconds. `undefined` if not provided by the server. */
|
|
144
|
+
get retryAfter(): number | undefined;
|
|
145
|
+
get requestId(): string | undefined;
|
|
146
|
+
toJSON(): Record<string, unknown>;
|
|
147
|
+
}
|
|
148
|
+
declare class AuthError extends AIGatewayError {
|
|
149
|
+
constructor(message: string, statusCode: number, metadata?: NormalizedErrorMetadata, options?: {
|
|
150
|
+
cause?: unknown;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
declare class CreditsError extends AIGatewayError {
|
|
154
|
+
constructor(message: string, statusCode: number, code: typeof ErrorCode.InsufficientCredits | typeof ErrorCode.SubscriptionExpired, metadata?: NormalizedErrorMetadata, options?: {
|
|
155
|
+
cause?: unknown;
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
declare class RateLimitError extends AIGatewayError {
|
|
159
|
+
constructor(message: string, statusCode: number, metadata?: NormalizedErrorMetadata, options?: {
|
|
160
|
+
cause?: unknown;
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
declare class ModelNotAllowedError extends AIGatewayError {
|
|
164
|
+
constructor(message: string, statusCode: number, metadata?: NormalizedErrorMetadata, options?: {
|
|
165
|
+
cause?: unknown;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
declare class GatewayValidationError extends AIGatewayError {
|
|
169
|
+
constructor(message: string, statusCode: number, metadata?: NormalizedErrorMetadata, options?: {
|
|
170
|
+
cause?: unknown;
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/** Type guard: returns true if the value is an AIGatewayError (or any subclass). */
|
|
174
|
+
declare function isAIGatewayError(value: unknown): value is AIGatewayError;
|
|
175
|
+
/**
|
|
176
|
+
* Parse error response body and throw appropriate AIGatewayError subclass.
|
|
177
|
+
* Handles both Gateway API format and OpenAI proxy format.
|
|
178
|
+
*/
|
|
179
|
+
declare function parseErrorResponse(statusCode: number, body: unknown): never;
|
|
180
|
+
|
|
181
|
+
export { AIGatewayError as A, CreditsError as C, ErrorCode as E, type GatewayProviderSettings as G, type Middleware as M, type NormalizedErrorMetadata as N, type OpenAIErrorResponse as O, RateLimitError as R, AuthError as a, GatewayApiCode as b, type GatewayApiErrorItem as c, type GatewayApiErrorResponse as d, GatewayValidationError as e, ModelNotAllowedError as f, type RetryConfig as g, isAIGatewayError as i, parseErrorResponse as p };
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkN26BDEG5_cjs = require('./chunk-N26BDEG5.cjs');
|
|
4
|
+
var openai = require('@ai-sdk/openai');
|
|
5
|
+
|
|
6
|
+
// src/gateway-config.ts
|
|
7
|
+
var DEFAULT_RETRY = {
|
|
8
|
+
maxAttempts: 3,
|
|
9
|
+
initialDelayMs: 1e3,
|
|
10
|
+
maxDelayMs: 3e4,
|
|
11
|
+
retryableStatuses: [429, 500, 502, 503, 504]
|
|
12
|
+
};
|
|
13
|
+
function normalizeRetryConfig(merged) {
|
|
14
|
+
const n = Math.floor(Number(merged.maxAttempts));
|
|
15
|
+
const maxAttempts = Number.isFinite(n) && n >= 1 ? n : DEFAULT_RETRY.maxAttempts;
|
|
16
|
+
return { ...merged, maxAttempts };
|
|
17
|
+
}
|
|
18
|
+
function resolveConfig(config) {
|
|
19
|
+
return {
|
|
20
|
+
baseURL: config.baseURL,
|
|
21
|
+
getAuthToken: config.getAuthToken,
|
|
22
|
+
retry: config.retry === false ? false : normalizeRetryConfig({ ...DEFAULT_RETRY, ...config.retry }),
|
|
23
|
+
middleware: [...config.middleware ?? []],
|
|
24
|
+
headers: config.headers,
|
|
25
|
+
timeout: config.timeout ?? 6e4,
|
|
26
|
+
fetchImpl: config.fetch
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
var DEFAULT_BASE_URLS = {
|
|
30
|
+
production: "https://api.macpaw.com/ai"
|
|
31
|
+
};
|
|
32
|
+
function resolveGatewayBaseURL(baseURL, env, consumerName) {
|
|
33
|
+
const resolved = baseURL ?? (env ? DEFAULT_BASE_URLS[env] : void 0);
|
|
34
|
+
if (!resolved) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`${consumerName} requires baseURL or env (production). For non-production environments, pass baseURL directly.`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return resolved;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/gateway-retry.ts
|
|
43
|
+
function delay(ms, signal) {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
if (signal?.aborted) {
|
|
46
|
+
reject(signal.reason);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const timer = setTimeout(resolve, ms);
|
|
50
|
+
signal?.addEventListener(
|
|
51
|
+
"abort",
|
|
52
|
+
() => {
|
|
53
|
+
clearTimeout(timer);
|
|
54
|
+
reject(signal.reason);
|
|
55
|
+
},
|
|
56
|
+
{ once: true }
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function isRetryableStatus(status, retryableStatuses) {
|
|
61
|
+
return retryableStatuses.includes(status);
|
|
62
|
+
}
|
|
63
|
+
function addJitter(delayMs) {
|
|
64
|
+
const jitter = delayMs * 0.25 * Math.random();
|
|
65
|
+
return delayMs + jitter;
|
|
66
|
+
}
|
|
67
|
+
async function withRetry(fn, options) {
|
|
68
|
+
const { maxAttempts, initialDelayMs, maxDelayMs, retryableStatuses } = options.retryConfig;
|
|
69
|
+
let lastError;
|
|
70
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
71
|
+
try {
|
|
72
|
+
return await fn();
|
|
73
|
+
} catch (err) {
|
|
74
|
+
lastError = err;
|
|
75
|
+
if (attempt === maxAttempts) break;
|
|
76
|
+
const status = err?.statusCode;
|
|
77
|
+
const isRetryable = status != null ? isRetryableStatus(status, retryableStatuses) : options.isNetworkError?.(err) ?? false;
|
|
78
|
+
if (!isRetryable) throw err;
|
|
79
|
+
const retryAfterSeconds = err?.retryAfter;
|
|
80
|
+
let backoff;
|
|
81
|
+
if (retryAfterSeconds != null && retryAfterSeconds > 0) {
|
|
82
|
+
backoff = retryAfterSeconds * 1e3;
|
|
83
|
+
} else {
|
|
84
|
+
backoff = addJitter(Math.min(initialDelayMs * Math.pow(2, attempt - 1), maxDelayMs));
|
|
85
|
+
}
|
|
86
|
+
await delay(backoff, options.signal);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
throw lastError;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/gateway-request.ts
|
|
93
|
+
function anySignal(signals) {
|
|
94
|
+
const any = AbortSignal.any;
|
|
95
|
+
if (typeof any === "function") {
|
|
96
|
+
return any(signals);
|
|
97
|
+
}
|
|
98
|
+
const controller = new AbortController();
|
|
99
|
+
const handlers = [];
|
|
100
|
+
function cleanup() {
|
|
101
|
+
for (const [sig, handler] of handlers) sig.removeEventListener("abort", handler);
|
|
102
|
+
handlers.length = 0;
|
|
103
|
+
}
|
|
104
|
+
for (const signal of signals) {
|
|
105
|
+
if (signal.aborted) {
|
|
106
|
+
controller.abort(signal.reason);
|
|
107
|
+
return controller.signal;
|
|
108
|
+
}
|
|
109
|
+
const handler = () => {
|
|
110
|
+
cleanup();
|
|
111
|
+
controller.abort(signal.reason);
|
|
112
|
+
};
|
|
113
|
+
handlers.push([signal, handler]);
|
|
114
|
+
signal.addEventListener("abort", handler, { once: true });
|
|
115
|
+
}
|
|
116
|
+
controller.signal.addEventListener("abort", cleanup, { once: true });
|
|
117
|
+
return controller.signal;
|
|
118
|
+
}
|
|
119
|
+
var _counter = 0;
|
|
120
|
+
function generateRequestId(prefix = "sdk") {
|
|
121
|
+
if (typeof globalThis.crypto?.randomUUID === "function") {
|
|
122
|
+
return `${prefix}-${globalThis.crypto.randomUUID()}`;
|
|
123
|
+
}
|
|
124
|
+
const timestamp = Date.now().toString(36);
|
|
125
|
+
const count = (_counter++).toString(36);
|
|
126
|
+
const random = Math.random().toString(36).slice(2, 8);
|
|
127
|
+
return `${prefix}-${timestamp}-${count}-${random}`;
|
|
128
|
+
}
|
|
129
|
+
function hasHeaderCaseInsensitive(headers, name) {
|
|
130
|
+
const lower = name.toLowerCase();
|
|
131
|
+
return Object.keys(headers).some((k) => k.toLowerCase() === lower);
|
|
132
|
+
}
|
|
133
|
+
function shouldAutoSetJsonContentType(body, headers) {
|
|
134
|
+
if (body == null || hasHeaderCaseInsensitive(headers, "content-type")) return false;
|
|
135
|
+
const isFormData = typeof FormData !== "undefined" && body instanceof FormData;
|
|
136
|
+
const isBlob = typeof Blob !== "undefined" && body instanceof Blob;
|
|
137
|
+
return !isFormData && !isBlob;
|
|
138
|
+
}
|
|
139
|
+
var NODE_NETWORK_CODES = /* @__PURE__ */ new Set([
|
|
140
|
+
"ECONNREFUSED",
|
|
141
|
+
"ECONNRESET",
|
|
142
|
+
"ENOTFOUND",
|
|
143
|
+
"EPIPE",
|
|
144
|
+
"ETIMEDOUT",
|
|
145
|
+
"ENETUNREACH",
|
|
146
|
+
"EAI_AGAIN",
|
|
147
|
+
"UND_ERR_CONNECT_TIMEOUT"
|
|
148
|
+
]);
|
|
149
|
+
var FETCH_NETWORK_TYPEERROR_HINTS = [
|
|
150
|
+
"failed to fetch",
|
|
151
|
+
"fetch failed",
|
|
152
|
+
"load failed",
|
|
153
|
+
"networkerror",
|
|
154
|
+
"network error when attempting to fetch"
|
|
155
|
+
];
|
|
156
|
+
function hasRetryableNodeErrorCode(err) {
|
|
157
|
+
let cur = err;
|
|
158
|
+
for (let depth = 0; depth < 5 && cur != null; depth++) {
|
|
159
|
+
const code = cur?.code;
|
|
160
|
+
if (typeof code === "string" && NODE_NETWORK_CODES.has(code)) return true;
|
|
161
|
+
cur = cur instanceof Error && cur.cause !== void 0 ? cur.cause : void 0;
|
|
162
|
+
}
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
function isFetchFailureTypeError(err) {
|
|
166
|
+
if (!(err instanceof TypeError)) return false;
|
|
167
|
+
const msg = String(err.message).toLowerCase();
|
|
168
|
+
return FETCH_NETWORK_TYPEERROR_HINTS.some((hint) => msg.includes(hint));
|
|
169
|
+
}
|
|
170
|
+
function isNetworkError(err) {
|
|
171
|
+
return hasRetryableNodeErrorCode(err) || isFetchFailureTypeError(err);
|
|
172
|
+
}
|
|
173
|
+
async function executeFetch(fetchImpl, requestConfig) {
|
|
174
|
+
const impl = fetchImpl ?? globalThis.fetch;
|
|
175
|
+
if (typeof impl === "undefined") {
|
|
176
|
+
throw new Error(
|
|
177
|
+
"@macpaw/ai-sdk requires a global `fetch` implementation. Use Node.js 18+ or install a polyfill like `undici`."
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
return impl(requestConfig.url, {
|
|
181
|
+
method: requestConfig.method,
|
|
182
|
+
headers: requestConfig.headers,
|
|
183
|
+
body: requestConfig.body,
|
|
184
|
+
signal: requestConfig.signal
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
async function executeRequestPipeline(config, request, behavior) {
|
|
188
|
+
const b = {
|
|
189
|
+
includeConfigHeaders: behavior?.includeConfigHeaders ?? true,
|
|
190
|
+
includeAuth: behavior?.includeAuth ?? true,
|
|
191
|
+
includeRequestId: behavior?.includeRequestId ?? true,
|
|
192
|
+
normalizeErrors: behavior?.normalizeErrors ?? true,
|
|
193
|
+
allowAuthRetry: behavior?.allowAuthRetry ?? true,
|
|
194
|
+
requestIdPrefix: behavior?.requestIdPrefix
|
|
195
|
+
};
|
|
196
|
+
return executeWithAuth(config, request, b, false);
|
|
197
|
+
}
|
|
198
|
+
async function executeWithAuth(config, request, behavior, isTokenRetry) {
|
|
199
|
+
try {
|
|
200
|
+
return await executeRequest(config, request, behavior, isTokenRetry);
|
|
201
|
+
} catch (err) {
|
|
202
|
+
if (behavior.allowAuthRetry && !isTokenRetry && err instanceof chunkN26BDEG5_cjs.AuthError) {
|
|
203
|
+
if (typeof ReadableStream !== "undefined" && request.body instanceof ReadableStream) {
|
|
204
|
+
throw err;
|
|
205
|
+
}
|
|
206
|
+
return executeWithAuth(config, request, behavior, true);
|
|
207
|
+
}
|
|
208
|
+
throw err;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async function executeRequest(config, request, behavior, forceRefreshToken) {
|
|
212
|
+
const headers = {
|
|
213
|
+
...behavior.includeConfigHeaders ? config.headers : void 0,
|
|
214
|
+
...request.headers
|
|
215
|
+
};
|
|
216
|
+
if (shouldAutoSetJsonContentType(request.body, headers)) {
|
|
217
|
+
headers["Content-Type"] = "application/json";
|
|
218
|
+
}
|
|
219
|
+
if (behavior.includeRequestId && !hasHeaderCaseInsensitive(headers, "x-request-id")) {
|
|
220
|
+
headers["X-Request-ID"] = generateRequestId(behavior.requestIdPrefix);
|
|
221
|
+
}
|
|
222
|
+
const timeoutMs = config.timeout;
|
|
223
|
+
const userSignal = request.signal;
|
|
224
|
+
async function doRequest() {
|
|
225
|
+
const timeoutController = new AbortController();
|
|
226
|
+
const timeoutId = setTimeout(
|
|
227
|
+
() => timeoutController.abort(new Error(`Request timed out after ${timeoutMs}ms`)),
|
|
228
|
+
timeoutMs
|
|
229
|
+
);
|
|
230
|
+
const signal = userSignal ? anySignal([userSignal, timeoutController.signal]) : timeoutController.signal;
|
|
231
|
+
const requestConfig = {
|
|
232
|
+
url: request.url,
|
|
233
|
+
method: request.method,
|
|
234
|
+
headers: { ...headers },
|
|
235
|
+
body: request.body,
|
|
236
|
+
signal
|
|
237
|
+
};
|
|
238
|
+
const { middleware } = config;
|
|
239
|
+
let index = 0;
|
|
240
|
+
const next = async (currentRequest) => {
|
|
241
|
+
if (index < middleware.length) {
|
|
242
|
+
const middlewareItem = middleware[index++];
|
|
243
|
+
return middlewareItem(currentRequest, next);
|
|
244
|
+
}
|
|
245
|
+
const finalHeaders = { ...currentRequest.headers };
|
|
246
|
+
if (behavior.includeAuth) {
|
|
247
|
+
const token = await config.getAuthToken(forceRefreshToken);
|
|
248
|
+
if (token) {
|
|
249
|
+
finalHeaders.Authorization = `Bearer ${token}`;
|
|
250
|
+
} else {
|
|
251
|
+
for (const key of Object.keys(finalHeaders)) {
|
|
252
|
+
if (key.toLowerCase() === "authorization") delete finalHeaders[key];
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return executeFetch(config.fetchImpl, { ...currentRequest, headers: finalHeaders });
|
|
257
|
+
};
|
|
258
|
+
try {
|
|
259
|
+
const response = await next(requestConfig);
|
|
260
|
+
if (!response.ok) {
|
|
261
|
+
const retryableStatuses = config.retry !== false ? config.retry.retryableStatuses : [];
|
|
262
|
+
const isTransportCritical = behavior.allowAuthRetry && response.status === 401 || config.retry !== false && retryableStatuses.includes(response.status);
|
|
263
|
+
if (behavior.normalizeErrors || isTransportCritical) {
|
|
264
|
+
await chunkN26BDEG5_cjs.parseErrorResponseFromResponse(response);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return response;
|
|
268
|
+
} finally {
|
|
269
|
+
clearTimeout(timeoutId);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (config.retry) {
|
|
273
|
+
return withRetry(doRequest, {
|
|
274
|
+
retryConfig: config.retry,
|
|
275
|
+
signal: userSignal,
|
|
276
|
+
isNetworkError
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
return doRequest();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// src/gateway-fetch.ts
|
|
283
|
+
function resolveRequestUrl(input) {
|
|
284
|
+
if (typeof input === "string") return input;
|
|
285
|
+
if (input instanceof URL) return input.href;
|
|
286
|
+
return input.url;
|
|
287
|
+
}
|
|
288
|
+
function stripPlaceholderAuthorization(headers, placeholder) {
|
|
289
|
+
const auth = headers.get("authorization");
|
|
290
|
+
if (auth === `Bearer ${placeholder}`) {
|
|
291
|
+
headers.delete("authorization");
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function headersToRecord(headers) {
|
|
295
|
+
const record = {};
|
|
296
|
+
for (const [key, value] of headers.entries()) {
|
|
297
|
+
record[key] = value;
|
|
298
|
+
}
|
|
299
|
+
return record;
|
|
300
|
+
}
|
|
301
|
+
function joinBaseUrl(baseURL, path) {
|
|
302
|
+
return `${baseURL}${path.startsWith("/") ? "" : "/"}${path}`;
|
|
303
|
+
}
|
|
304
|
+
function isGatewayUrl(url, gatewayBaseUrl) {
|
|
305
|
+
const gatewayPath = gatewayBaseUrl.pathname.replace(/\/$/, "");
|
|
306
|
+
const requestPath = url.pathname.replace(/\/$/, "");
|
|
307
|
+
return url.origin === gatewayBaseUrl.origin && (requestPath === gatewayPath || requestPath.startsWith(`${gatewayPath}/`));
|
|
308
|
+
}
|
|
309
|
+
var GATEWAY_PLACEHOLDER_API_KEY = "ai-gateway-auth-via-fetch";
|
|
310
|
+
function createGatewayFetch(options) {
|
|
311
|
+
const { baseURL, normalizeErrors = true } = options;
|
|
312
|
+
const base = baseURL.replace(/\/$/, "");
|
|
313
|
+
const gatewayBaseUrl = new URL(base);
|
|
314
|
+
const resolvedConfig = resolveConfig({ ...options, baseURL: base });
|
|
315
|
+
return async function gatewayFetch(input, init) {
|
|
316
|
+
const rawUrl = resolveRequestUrl(input);
|
|
317
|
+
const isAbsolute = rawUrl.startsWith("http://") || rawUrl.startsWith("https://");
|
|
318
|
+
const resolvedUrl = new URL(isAbsolute ? rawUrl : joinBaseUrl(base, rawUrl));
|
|
319
|
+
const isGatewayRequest = isGatewayUrl(resolvedUrl, gatewayBaseUrl);
|
|
320
|
+
const request = typeof Request !== "undefined" && input instanceof Request ? input : void 0;
|
|
321
|
+
const requestClone = request?.clone();
|
|
322
|
+
const headers = new Headers(requestClone?.headers);
|
|
323
|
+
if (init?.headers) {
|
|
324
|
+
for (const [key, value] of new Headers(init.headers).entries()) {
|
|
325
|
+
headers.set(key, value);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (!isGatewayRequest) {
|
|
329
|
+
stripPlaceholderAuthorization(headers, GATEWAY_PLACEHOLDER_API_KEY);
|
|
330
|
+
}
|
|
331
|
+
return executeRequestPipeline(
|
|
332
|
+
resolvedConfig,
|
|
333
|
+
{
|
|
334
|
+
url: resolvedUrl.toString(),
|
|
335
|
+
method: init?.method ?? requestClone?.method ?? "GET",
|
|
336
|
+
headers: headersToRecord(headers),
|
|
337
|
+
body: init?.body ?? requestClone?.body,
|
|
338
|
+
signal: init?.signal ?? requestClone?.signal
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
includeConfigHeaders: isGatewayRequest,
|
|
342
|
+
includeAuth: isGatewayRequest,
|
|
343
|
+
includeRequestId: isGatewayRequest,
|
|
344
|
+
normalizeErrors: isGatewayRequest && normalizeErrors,
|
|
345
|
+
allowAuthRetry: isGatewayRequest,
|
|
346
|
+
requestIdPrefix: "provider"
|
|
347
|
+
}
|
|
348
|
+
);
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// src/gateway-provider.ts
|
|
353
|
+
var DEFAULT_API_VERSION = "v1";
|
|
354
|
+
function createAIGatewayProvider(options) {
|
|
355
|
+
const baseURL = resolveGatewayBaseURL(options.baseURL, options.env, "AIGatewayProvider");
|
|
356
|
+
const customFetch = createGatewayFetch({ ...options, baseURL, normalizeErrors: options.normalizeErrors });
|
|
357
|
+
const openAI = options.createOpenAI ?? openai.createOpenAI;
|
|
358
|
+
return openAI({
|
|
359
|
+
name: options.name,
|
|
360
|
+
organization: options.organization,
|
|
361
|
+
project: options.project,
|
|
362
|
+
baseURL: `${baseURL.replace(/\/$/, "")}/api/${DEFAULT_API_VERSION}`,
|
|
363
|
+
fetch: customFetch,
|
|
364
|
+
apiKey: GATEWAY_PLACEHOLDER_API_KEY
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
var GATEWAY_PROVIDERS = {
|
|
368
|
+
ANTHROPIC: "anthropic",
|
|
369
|
+
GOOGLE: "google",
|
|
370
|
+
XAI: "xai",
|
|
371
|
+
GROQ: "groq",
|
|
372
|
+
MISTRAL: "mistral",
|
|
373
|
+
AMAZON_BEDROCK: "amazon-bedrock",
|
|
374
|
+
AZURE: "azure",
|
|
375
|
+
COHERE: "cohere",
|
|
376
|
+
PERPLEXITY: "perplexity",
|
|
377
|
+
DEEPSEEK: "deepseek",
|
|
378
|
+
TOGETHERAI: "togetherai",
|
|
379
|
+
OPENAI_COMPATIBLE: "openai-compatible"
|
|
380
|
+
};
|
|
381
|
+
var GATEWAY_PROVIDER_DEFAULT_PREFIXES = {
|
|
382
|
+
[GATEWAY_PROVIDERS.ANTHROPIC]: "anthropic",
|
|
383
|
+
[GATEWAY_PROVIDERS.GOOGLE]: "google",
|
|
384
|
+
[GATEWAY_PROVIDERS.XAI]: "xai",
|
|
385
|
+
[GATEWAY_PROVIDERS.GROQ]: "groq",
|
|
386
|
+
[GATEWAY_PROVIDERS.MISTRAL]: "mistral",
|
|
387
|
+
[GATEWAY_PROVIDERS.AMAZON_BEDROCK]: "bedrock",
|
|
388
|
+
[GATEWAY_PROVIDERS.AZURE]: "azure",
|
|
389
|
+
[GATEWAY_PROVIDERS.COHERE]: "cohere",
|
|
390
|
+
[GATEWAY_PROVIDERS.PERPLEXITY]: "perplexity",
|
|
391
|
+
[GATEWAY_PROVIDERS.DEEPSEEK]: "deepseek",
|
|
392
|
+
[GATEWAY_PROVIDERS.TOGETHERAI]: "togetherai"
|
|
393
|
+
};
|
|
394
|
+
function prefixModelId(prefix, modelId) {
|
|
395
|
+
return modelId.includes("/") ? modelId : `${prefix}/${modelId}`;
|
|
396
|
+
}
|
|
397
|
+
function createPrefixedGatewayProvider(defaultPrefix, options) {
|
|
398
|
+
const { modelPrefix, ...providerOptions } = options;
|
|
399
|
+
const prefix = modelPrefix ?? defaultPrefix;
|
|
400
|
+
const provider = createAIGatewayProvider(providerOptions);
|
|
401
|
+
const wrapped = ((modelId) => provider(prefixModelId(prefix, modelId)));
|
|
402
|
+
return new Proxy(wrapped, {
|
|
403
|
+
apply(_target, _thisArg, args) {
|
|
404
|
+
const [modelId, ...rest] = args;
|
|
405
|
+
return provider(prefixModelId(prefix, modelId), ...rest);
|
|
406
|
+
},
|
|
407
|
+
get(_target, prop) {
|
|
408
|
+
const value = Reflect.get(provider, prop, provider);
|
|
409
|
+
if (typeof value === "function") {
|
|
410
|
+
return (...args) => {
|
|
411
|
+
if (typeof args[0] === "string") {
|
|
412
|
+
args[0] = prefixModelId(prefix, args[0]);
|
|
413
|
+
}
|
|
414
|
+
return value.apply(provider, args);
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
return value;
|
|
418
|
+
},
|
|
419
|
+
has(_target, prop) {
|
|
420
|
+
return prop in provider;
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
function createGatewayProvider(provider, options) {
|
|
425
|
+
if (provider === GATEWAY_PROVIDERS.OPENAI_COMPATIBLE) {
|
|
426
|
+
return createPrefixedGatewayProvider(
|
|
427
|
+
options.modelPrefix,
|
|
428
|
+
options
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
return createPrefixedGatewayProvider(
|
|
432
|
+
GATEWAY_PROVIDER_DEFAULT_PREFIXES[provider],
|
|
433
|
+
options
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
Object.defineProperty(exports, "AIGatewayError", {
|
|
438
|
+
enumerable: true,
|
|
439
|
+
get: function () { return chunkN26BDEG5_cjs.AIGatewayError; }
|
|
440
|
+
});
|
|
441
|
+
Object.defineProperty(exports, "AuthError", {
|
|
442
|
+
enumerable: true,
|
|
443
|
+
get: function () { return chunkN26BDEG5_cjs.AuthError; }
|
|
444
|
+
});
|
|
445
|
+
Object.defineProperty(exports, "CreditsError", {
|
|
446
|
+
enumerable: true,
|
|
447
|
+
get: function () { return chunkN26BDEG5_cjs.CreditsError; }
|
|
448
|
+
});
|
|
449
|
+
Object.defineProperty(exports, "ErrorCode", {
|
|
450
|
+
enumerable: true,
|
|
451
|
+
get: function () { return chunkN26BDEG5_cjs.ErrorCode; }
|
|
452
|
+
});
|
|
453
|
+
Object.defineProperty(exports, "GatewayApiCode", {
|
|
454
|
+
enumerable: true,
|
|
455
|
+
get: function () { return chunkN26BDEG5_cjs.GatewayApiCode; }
|
|
456
|
+
});
|
|
457
|
+
Object.defineProperty(exports, "GatewayValidationError", {
|
|
458
|
+
enumerable: true,
|
|
459
|
+
get: function () { return chunkN26BDEG5_cjs.GatewayValidationError; }
|
|
460
|
+
});
|
|
461
|
+
Object.defineProperty(exports, "ModelNotAllowedError", {
|
|
462
|
+
enumerable: true,
|
|
463
|
+
get: function () { return chunkN26BDEG5_cjs.ModelNotAllowedError; }
|
|
464
|
+
});
|
|
465
|
+
Object.defineProperty(exports, "RateLimitError", {
|
|
466
|
+
enumerable: true,
|
|
467
|
+
get: function () { return chunkN26BDEG5_cjs.RateLimitError; }
|
|
468
|
+
});
|
|
469
|
+
Object.defineProperty(exports, "isAIGatewayError", {
|
|
470
|
+
enumerable: true,
|
|
471
|
+
get: function () { return chunkN26BDEG5_cjs.isAIGatewayError; }
|
|
472
|
+
});
|
|
473
|
+
Object.defineProperty(exports, "parseErrorResponse", {
|
|
474
|
+
enumerable: true,
|
|
475
|
+
get: function () { return chunkN26BDEG5_cjs.parseErrorResponse; }
|
|
476
|
+
});
|
|
477
|
+
exports.GATEWAY_PLACEHOLDER_API_KEY = GATEWAY_PLACEHOLDER_API_KEY;
|
|
478
|
+
exports.GATEWAY_PROVIDERS = GATEWAY_PROVIDERS;
|
|
479
|
+
exports.createAIGatewayProvider = createAIGatewayProvider;
|
|
480
|
+
exports.createGatewayFetch = createGatewayFetch;
|
|
481
|
+
exports.createGatewayProvider = createGatewayProvider;
|
|
482
|
+
//# sourceMappingURL=index.cjs.map
|
|
483
|
+
//# sourceMappingURL=index.cjs.map
|