tolvyn 1.0.4 → 1.0.6
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/dist/cjs/client.d.ts.map +1 -1
- package/dist/cjs/client.js +1 -33
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/failopen.d.ts +18 -0
- package/dist/cjs/failopen.d.ts.map +1 -1
- package/dist/cjs/failopen.js +32 -5
- package/dist/cjs/failopen.js.map +1 -1
- package/dist/cjs/google.d.ts +1 -1
- package/dist/cjs/google.d.ts.map +1 -1
- package/dist/cjs/google.js +36 -7
- package/dist/cjs/google.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/client.js +1 -33
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/failopen.d.ts +18 -0
- package/dist/esm/failopen.d.ts.map +1 -1
- package/dist/esm/failopen.js +31 -5
- package/dist/esm/failopen.js.map +1 -1
- package/dist/esm/google.d.ts +1 -1
- package/dist/esm/google.d.ts.map +1 -1
- package/dist/esm/google.js +34 -5
- package/dist/esm/google.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/package.json +9 -2
- package/src/client.ts +1 -44
- package/src/failopen.ts +38 -8
- package/src/google.ts +35 -5
- package/src/index.ts +1 -1
package/dist/cjs/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,UAAU,EAAE,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAMnD,MAAM,WAAW,mBACf,SAAQ,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,SAAS,CAAC;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,UAAU,EAAE,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAMnD,MAAM,WAAW,mBACf,SAAQ,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,SAAS,CAAC;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,MAAO,SAAQ,UAAU;IACpC,SAAgB,eAAe,EAAE,OAAO,CAAC;IACzC,SAAgB,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IACvD,SAAgB,eAAe,EAAE,MAAM,CAAC;gBAE5B,OAAO,GAAE,mBAAwB;CAgD9C"}
|
package/dist/cjs/client.js
CHANGED
|
@@ -11,38 +11,6 @@ const openai_1 = __importDefault(require("openai"));
|
|
|
11
11
|
const failopen_1 = require("./failopen");
|
|
12
12
|
const OPENAI_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/openai/';
|
|
13
13
|
const OPENAI_DIRECT_URL = 'https://api.openai.com/v1';
|
|
14
|
-
function makeFailOpenFetch(fallbackKey, directUrl, provider) {
|
|
15
|
-
return async function failOpenFetch(input, init) {
|
|
16
|
-
try {
|
|
17
|
-
const res = await fetch(input, init);
|
|
18
|
-
if (res.status === 503) {
|
|
19
|
-
throw Object.assign(new Error('503 from proxy'), { status: 503 });
|
|
20
|
-
}
|
|
21
|
-
return res;
|
|
22
|
-
}
|
|
23
|
-
catch (err) {
|
|
24
|
-
if ((0, failopen_1.shouldNotFailOpen)(err) || !(0, failopen_1.isProxyError)(err))
|
|
25
|
-
throw err;
|
|
26
|
-
console.error(`TOLVYN proxy unreachable — routing direct to ${provider} (fail-open)`);
|
|
27
|
-
const originalUrl = typeof input === 'string'
|
|
28
|
-
? input
|
|
29
|
-
: input instanceof URL
|
|
30
|
-
? input.href
|
|
31
|
-
: input.url;
|
|
32
|
-
const url = new URL(originalUrl);
|
|
33
|
-
const directBase = new URL(directUrl);
|
|
34
|
-
url.hostname = directBase.hostname;
|
|
35
|
-
url.protocol = directBase.protocol;
|
|
36
|
-
url.port = directBase.port;
|
|
37
|
-
url.pathname = url.pathname; // keep path intact
|
|
38
|
-
const newInit = { ...(init ?? {}) };
|
|
39
|
-
const headers = new Headers(init?.headers ?? {});
|
|
40
|
-
headers.set('Authorization', `Bearer ${fallbackKey}`);
|
|
41
|
-
newInit.headers = headers;
|
|
42
|
-
return fetch(url.toString(), newInit);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
14
|
class OpenAI extends openai_1.default {
|
|
47
15
|
constructor(options = {}) {
|
|
48
16
|
const tolvynApiKey = options.tolvynApiKey ?? process.env['TOLVYN_API_KEY'];
|
|
@@ -75,7 +43,7 @@ class OpenAI extends openai_1.default {
|
|
|
75
43
|
defaultHeaders,
|
|
76
44
|
};
|
|
77
45
|
if (failOpen && fallbackKey && !superOptions.fetch) {
|
|
78
|
-
superOptions.fetch = makeFailOpenFetch(fallbackKey, OPENAI_DIRECT_URL, 'OpenAI');
|
|
46
|
+
superOptions.fetch = (0, failopen_1.makeFailOpenFetch)(fallbackKey, OPENAI_DIRECT_URL, 'OpenAI');
|
|
79
47
|
}
|
|
80
48
|
super(superOptions);
|
|
81
49
|
this._tolvynFailOpen = failOpen;
|
package/dist/cjs/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":";;;;;;AAAA;;GAEG;AACH,oDAAmD;AACnD,
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":";;;;;;AAAA;;GAEG;AACH,oDAAmD;AACnD,yCAA+C;AAE/C,MAAM,wBAAwB,GAAG,0CAA0C,CAAC;AAC5E,MAAM,iBAAiB,GAAG,2BAA2B,CAAC;AAgBtD,MAAa,MAAO,SAAQ,gBAAU;IAKpC,YAAY,UAA+B,EAAE;QAC3C,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC3E,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GACZ,OAAO,CAAC,QAAQ;YAChB,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;YAC/B,wBAAwB,CAAC;QAE3B,MAAM,cAAc,GAA2B,EAAE,CAAC;QAClD,IAAI,OAAO,CAAC,IAAI;YAAS,cAAc,CAAC,eAAe,CAAC,GAAW,OAAO,CAAC,IAAI,CAAC;QAChF,IAAI,OAAO,CAAC,OAAO;YAAM,cAAc,CAAC,kBAAkB,CAAC,GAAQ,OAAO,CAAC,OAAO,CAAC;QACnF,IAAI,OAAO,CAAC,OAAO;YAAM,cAAc,CAAC,kBAAkB,CAAC,GAAQ,OAAO,CAAC,OAAO,CAAC;QACnF,IAAI,OAAO,CAAC,KAAK;YAAQ,cAAc,CAAC,gBAAgB,CAAC,GAAU,OAAO,CAAC,KAAK,CAAC;QACjF,IAAI,OAAO,CAAC,IAAI;YAAS,cAAc,CAAC,eAAe,CAAC,GAAW,OAAO,CAAC,IAAI,CAAC;QAChF,IAAI,OAAO,CAAC,WAAW;YAAE,cAAc,CAAC,uBAAuB,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC;QAEvF,MAAM,WAAW,GAAG,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC1E,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC;QAE1C,MAAM,EACJ,YAAY,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EACxD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,GAAG,EAClD,QAAQ,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EACjC,GAAG,IAAI,EACR,GAAG,OAAO,CAAC;QAEZ,MAAM,YAAY,GAAkB;YAClC,GAAG,IAAI;YACP,MAAM,EAAE,YAAY;YACpB,OAAO,EAAE,QAAQ;YACjB,cAAc;SACf,CAAC;QAEF,IAAI,QAAQ,IAAI,WAAW,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;YACnD,YAAY,CAAC,KAAK,GAAG,IAAA,4BAAiB,EAAC,WAAW,EAAE,iBAAiB,EAAE,QAAQ,CAAC,CAAC;QACnF,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,CAAC;QAEpB,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;QAChC,IAAI,CAAC,kBAAkB,GAAG,WAAW,CAAC;QACtC,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;IAClC,CAAC;CACF;AArDD,wBAqDC"}
|
package/dist/cjs/failopen.d.ts
CHANGED
|
@@ -3,5 +3,23 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export declare function isProxyError(error: unknown): boolean;
|
|
5
5
|
export declare function shouldNotFailOpen(error: unknown): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Build the direct-provider URL from a proxy request URL.
|
|
8
|
+
*
|
|
9
|
+
* Strips the /v1/proxy/{provider}/ prefix from the request path, then prepends
|
|
10
|
+
* the fallback base URL's path (e.g. "/v1" for OpenAI, "" for Anthropic) so
|
|
11
|
+
* the final URL hits the real provider endpoint.
|
|
12
|
+
*
|
|
13
|
+
* Example (OpenAI):
|
|
14
|
+
* originalUrl: https://proxy.tolvyn.io/v1/proxy/openai/chat/completions
|
|
15
|
+
* fallbackBase: https://api.openai.com/v1
|
|
16
|
+
* → https://api.openai.com/v1/chat/completions
|
|
17
|
+
*
|
|
18
|
+
* Example (Anthropic):
|
|
19
|
+
* originalUrl: https://proxy.tolvyn.io/v1/proxy/anthropic/v1/messages
|
|
20
|
+
* fallbackBase: https://api.anthropic.com
|
|
21
|
+
* → https://api.anthropic.com/v1/messages
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildFallbackUrl(originalUrl: URL, fallbackBaseUrl: string): URL;
|
|
6
24
|
export declare function makeFailOpenFetch(fallbackKey: string, directUrl: string, provider: string): typeof globalThis.fetch;
|
|
7
25
|
//# sourceMappingURL=failopen.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failopen.d.ts","sourceRoot":"","sources":["../../src/failopen.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"failopen.d.ts","sourceRoot":"","sources":["../../src/failopen.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAoCpD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CASzD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,GAAG,GAAG,CAa/E;AAED,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,UAAU,CAAC,KAAK,CAgCzB"}
|
package/dist/cjs/failopen.js
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.isProxyError = isProxyError;
|
|
7
7
|
exports.shouldNotFailOpen = shouldNotFailOpen;
|
|
8
|
+
exports.buildFallbackUrl = buildFallbackUrl;
|
|
8
9
|
exports.makeFailOpenFetch = makeFailOpenFetch;
|
|
10
|
+
const PROXY_PREFIX_RE = /^\/v1\/proxy\/(?:openai|anthropic|google)\//;
|
|
9
11
|
function isProxyError(error) {
|
|
10
12
|
if (!error || typeof error !== 'object')
|
|
11
13
|
return false;
|
|
@@ -48,6 +50,35 @@ function shouldNotFailOpen(error) {
|
|
|
48
50
|
}
|
|
49
51
|
return false;
|
|
50
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Build the direct-provider URL from a proxy request URL.
|
|
55
|
+
*
|
|
56
|
+
* Strips the /v1/proxy/{provider}/ prefix from the request path, then prepends
|
|
57
|
+
* the fallback base URL's path (e.g. "/v1" for OpenAI, "" for Anthropic) so
|
|
58
|
+
* the final URL hits the real provider endpoint.
|
|
59
|
+
*
|
|
60
|
+
* Example (OpenAI):
|
|
61
|
+
* originalUrl: https://proxy.tolvyn.io/v1/proxy/openai/chat/completions
|
|
62
|
+
* fallbackBase: https://api.openai.com/v1
|
|
63
|
+
* → https://api.openai.com/v1/chat/completions
|
|
64
|
+
*
|
|
65
|
+
* Example (Anthropic):
|
|
66
|
+
* originalUrl: https://proxy.tolvyn.io/v1/proxy/anthropic/v1/messages
|
|
67
|
+
* fallbackBase: https://api.anthropic.com
|
|
68
|
+
* → https://api.anthropic.com/v1/messages
|
|
69
|
+
*/
|
|
70
|
+
function buildFallbackUrl(originalUrl, fallbackBaseUrl) {
|
|
71
|
+
const fb = new URL(fallbackBaseUrl);
|
|
72
|
+
const fbBasePath = fb.pathname.replace(/\/$/, ''); // "/v1" or ""
|
|
73
|
+
const stripped = originalUrl.pathname.replace(PROXY_PREFIX_RE, '/');
|
|
74
|
+
const finalPath = fbBasePath + stripped;
|
|
75
|
+
const newUrl = new URL(originalUrl.toString());
|
|
76
|
+
newUrl.protocol = fb.protocol;
|
|
77
|
+
newUrl.hostname = fb.hostname;
|
|
78
|
+
newUrl.port = fb.port;
|
|
79
|
+
newUrl.pathname = finalPath;
|
|
80
|
+
return newUrl;
|
|
81
|
+
}
|
|
51
82
|
function makeFailOpenFetch(fallbackKey, directUrl, provider) {
|
|
52
83
|
return async function failOpenFetch(input, init) {
|
|
53
84
|
try {
|
|
@@ -66,11 +97,7 @@ function makeFailOpenFetch(fallbackKey, directUrl, provider) {
|
|
|
66
97
|
: input instanceof URL
|
|
67
98
|
? input.href
|
|
68
99
|
: input.url;
|
|
69
|
-
const url = new URL(originalUrl);
|
|
70
|
-
const directBase = new URL(directUrl);
|
|
71
|
-
url.hostname = directBase.hostname;
|
|
72
|
-
url.protocol = directBase.protocol;
|
|
73
|
-
url.port = directBase.port;
|
|
100
|
+
const url = buildFallbackUrl(new URL(originalUrl), directUrl);
|
|
74
101
|
const newInit = { ...(init ?? {}) };
|
|
75
102
|
const headers = new Headers(init?.headers ?? {});
|
|
76
103
|
headers.set('Authorization', `Bearer ${fallbackKey}`);
|
package/dist/cjs/failopen.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failopen.js","sourceRoot":"","sources":["../../src/failopen.ts"],"names":[],"mappings":";AAAA;;GAEG;;
|
|
1
|
+
{"version":3,"file":"failopen.js","sourceRoot":"","sources":["../../src/failopen.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/google.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface TolvynGoogleOptions {
|
|
|
14
14
|
failOpen?: boolean;
|
|
15
15
|
googleApiKey?: string;
|
|
16
16
|
}
|
|
17
|
-
export declare class
|
|
17
|
+
export declare class Google extends GoogleGenerativeAI {
|
|
18
18
|
readonly _tolvynFailOpen: boolean;
|
|
19
19
|
readonly _tolvynFallbackKey: string | undefined;
|
|
20
20
|
private readonly _tolvynProxyUrl;
|
package/dist/cjs/google.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/google.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,cAAc,EACf,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/google.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,cAAc,EACf,MAAM,uBAAuB,CAAC;AAO/B,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAID,qBAAa,MAAO,SAAQ,kBAAkB;IAC5C,SAAgB,eAAe,EAAE,OAAO,CAAC;IACzC,SAAgB,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IACvD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;gBAE5C,OAAO,GAAE,mBAAwB;IAgCpC,kBAAkB,CACzB,WAAW,EAAE,WAAW,EACxB,cAAc,CAAC,EAAE,cAAc;CAkClC"}
|
package/dist/cjs/google.js
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.Google = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* TOLVYN Google wrapper — thin drop-in over @google/generative-ai.
|
|
6
6
|
*/
|
|
7
7
|
const generative_ai_1 = require("@google/generative-ai");
|
|
8
|
+
const failopen_1 = require("./failopen");
|
|
8
9
|
// The proxy base URL is prepended to Google API paths (/v1beta/models/...).
|
|
9
10
|
// The TOLVYN proxy strips /v1/proxy/google and forwards the remainder to Google.
|
|
10
|
-
const GOOGLE_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/google';
|
|
11
|
-
|
|
11
|
+
const GOOGLE_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/google/';
|
|
12
|
+
// ND-05: renamed from TolvynGoogle → Google so stack traces and constructor.name
|
|
13
|
+
// match the public API. Old name remains re-exportable via index.ts alias.
|
|
14
|
+
class Google extends generative_ai_1.GoogleGenerativeAI {
|
|
12
15
|
constructor(options = {}) {
|
|
13
16
|
const tolvynApiKey = options.tolvynApiKey ?? process.env['TOLVYN_API_KEY'];
|
|
14
17
|
if (!tolvynApiKey) {
|
|
15
18
|
throw new Error('tolvynApiKey required. Set TOLVYN_API_KEY env var or pass tolvynApiKey.');
|
|
16
19
|
}
|
|
17
|
-
//
|
|
18
|
-
//
|
|
20
|
+
// ND-06: The TOLVYN API key is passed as the GoogleGenerativeAI api key.
|
|
21
|
+
// Google's SDK sends this as `x-goog-api-key`; the TOLVYN proxy reads
|
|
22
|
+
// `x-goog-api-key` as a Bearer-auth fallback for Google requests.
|
|
23
|
+
// This is intentional — keep this comment if you refactor.
|
|
19
24
|
super(tolvynApiKey);
|
|
20
25
|
this._tolvynProxyUrl =
|
|
21
26
|
options.proxyUrl ??
|
|
@@ -44,8 +49,32 @@ class TolvynGoogle extends generative_ai_1.GoogleGenerativeAI {
|
|
|
44
49
|
customHeaders: this._tolvynHeaders,
|
|
45
50
|
...requestOptions,
|
|
46
51
|
};
|
|
47
|
-
|
|
52
|
+
const model = super.getGenerativeModel(modelParams, mergedOptions);
|
|
53
|
+
// ND-01: wrap generateContent so a proxy-unreachable error retries against
|
|
54
|
+
// the real Google API using the fallback key.
|
|
55
|
+
if (!this._tolvynFailOpen || !this._tolvynFallbackKey) {
|
|
56
|
+
return model;
|
|
57
|
+
}
|
|
58
|
+
const fallbackKey = this._tolvynFallbackKey;
|
|
59
|
+
const originalGenerate = model.generateContent.bind(model);
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
61
|
+
model.generateContent = async (...args) => {
|
|
62
|
+
try {
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
64
|
+
return await originalGenerate(...args);
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
if (!(0, failopen_1.isProxyError)(e))
|
|
68
|
+
throw e;
|
|
69
|
+
console.error('TOLVYN proxy unreachable — routing direct to Google (fail-open)');
|
|
70
|
+
const fallbackAI = new generative_ai_1.GoogleGenerativeAI(fallbackKey);
|
|
71
|
+
const fallbackModel = fallbackAI.getGenerativeModel(modelParams, requestOptions);
|
|
72
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
73
|
+
return fallbackModel.generateContent(...args);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
return model;
|
|
48
77
|
}
|
|
49
78
|
}
|
|
50
|
-
exports.
|
|
79
|
+
exports.Google = Google;
|
|
51
80
|
//# sourceMappingURL=google.js.map
|
package/dist/cjs/google.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/google.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,yDAI+B;
|
|
1
|
+
{"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/google.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ export { OpenAI } from './client';
|
|
|
2
2
|
export type { TolvynOpenAIOptions } from './client';
|
|
3
3
|
export { Anthropic } from './anthropic';
|
|
4
4
|
export type { TolvynAnthropicOptions } from './anthropic';
|
|
5
|
-
export {
|
|
5
|
+
export { Google } from './google';
|
|
6
6
|
export type { TolvynGoogleOptions } from './google';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/cjs/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/cjs/index.js
CHANGED
|
@@ -6,5 +6,5 @@ Object.defineProperty(exports, "OpenAI", { enumerable: true, get: function () {
|
|
|
6
6
|
var anthropic_1 = require("./anthropic");
|
|
7
7
|
Object.defineProperty(exports, "Anthropic", { enumerable: true, get: function () { return anthropic_1.Anthropic; } });
|
|
8
8
|
var google_1 = require("./google");
|
|
9
|
-
Object.defineProperty(exports, "Google", { enumerable: true, get: function () { return google_1.
|
|
9
|
+
Object.defineProperty(exports, "Google", { enumerable: true, get: function () { return google_1.Google; } });
|
|
10
10
|
//# sourceMappingURL=index.js.map
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;AAAA,mCAAkC;AAAzB,gGAAA,MAAM,OAAA;AAEf,yCAAwC;AAA/B,sGAAA,SAAS,OAAA;AAElB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;AAAA,mCAAkC;AAAzB,gGAAA,MAAM,OAAA;AAEf,yCAAwC;AAA/B,sGAAA,SAAS,OAAA;AAElB,mCAAkC;AAAzB,gGAAA,MAAM,OAAA"}
|
package/dist/esm/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,UAAU,EAAE,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAMnD,MAAM,WAAW,mBACf,SAAQ,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,SAAS,CAAC;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,UAAU,EAAE,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAMnD,MAAM,WAAW,mBACf,SAAQ,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,SAAS,CAAC;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,MAAO,SAAQ,UAAU;IACpC,SAAgB,eAAe,EAAE,OAAO,CAAC;IACzC,SAAgB,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IACvD,SAAgB,eAAe,EAAE,MAAM,CAAC;gBAE5B,OAAO,GAAE,mBAAwB;CAgD9C"}
|
package/dist/esm/client.js
CHANGED
|
@@ -2,41 +2,9 @@
|
|
|
2
2
|
* TOLVYN OpenAI wrapper — thin drop-in over the official openai package.
|
|
3
3
|
*/
|
|
4
4
|
import OpenAIBase from 'openai';
|
|
5
|
-
import {
|
|
5
|
+
import { makeFailOpenFetch } from './failopen';
|
|
6
6
|
const OPENAI_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/openai/';
|
|
7
7
|
const OPENAI_DIRECT_URL = 'https://api.openai.com/v1';
|
|
8
|
-
function makeFailOpenFetch(fallbackKey, directUrl, provider) {
|
|
9
|
-
return async function failOpenFetch(input, init) {
|
|
10
|
-
try {
|
|
11
|
-
const res = await fetch(input, init);
|
|
12
|
-
if (res.status === 503) {
|
|
13
|
-
throw Object.assign(new Error('503 from proxy'), { status: 503 });
|
|
14
|
-
}
|
|
15
|
-
return res;
|
|
16
|
-
}
|
|
17
|
-
catch (err) {
|
|
18
|
-
if (shouldNotFailOpen(err) || !isProxyError(err))
|
|
19
|
-
throw err;
|
|
20
|
-
console.error(`TOLVYN proxy unreachable — routing direct to ${provider} (fail-open)`);
|
|
21
|
-
const originalUrl = typeof input === 'string'
|
|
22
|
-
? input
|
|
23
|
-
: input instanceof URL
|
|
24
|
-
? input.href
|
|
25
|
-
: input.url;
|
|
26
|
-
const url = new URL(originalUrl);
|
|
27
|
-
const directBase = new URL(directUrl);
|
|
28
|
-
url.hostname = directBase.hostname;
|
|
29
|
-
url.protocol = directBase.protocol;
|
|
30
|
-
url.port = directBase.port;
|
|
31
|
-
url.pathname = url.pathname; // keep path intact
|
|
32
|
-
const newInit = { ...(init ?? {}) };
|
|
33
|
-
const headers = new Headers(init?.headers ?? {});
|
|
34
|
-
headers.set('Authorization', `Bearer ${fallbackKey}`);
|
|
35
|
-
newInit.headers = headers;
|
|
36
|
-
return fetch(url.toString(), newInit);
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
8
|
export class OpenAI extends OpenAIBase {
|
|
41
9
|
constructor(options = {}) {
|
|
42
10
|
const tolvynApiKey = options.tolvynApiKey ?? process.env['TOLVYN_API_KEY'];
|
package/dist/esm/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,UAA6B,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,UAA6B,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,wBAAwB,GAAG,0CAA0C,CAAC;AAC5E,MAAM,iBAAiB,GAAG,2BAA2B,CAAC;AAgBtD,MAAM,OAAO,MAAO,SAAQ,UAAU;IAKpC,YAAY,UAA+B,EAAE;QAC3C,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC3E,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GACZ,OAAO,CAAC,QAAQ;YAChB,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;YAC/B,wBAAwB,CAAC;QAE3B,MAAM,cAAc,GAA2B,EAAE,CAAC;QAClD,IAAI,OAAO,CAAC,IAAI;YAAS,cAAc,CAAC,eAAe,CAAC,GAAW,OAAO,CAAC,IAAI,CAAC;QAChF,IAAI,OAAO,CAAC,OAAO;YAAM,cAAc,CAAC,kBAAkB,CAAC,GAAQ,OAAO,CAAC,OAAO,CAAC;QACnF,IAAI,OAAO,CAAC,OAAO;YAAM,cAAc,CAAC,kBAAkB,CAAC,GAAQ,OAAO,CAAC,OAAO,CAAC;QACnF,IAAI,OAAO,CAAC,KAAK;YAAQ,cAAc,CAAC,gBAAgB,CAAC,GAAU,OAAO,CAAC,KAAK,CAAC;QACjF,IAAI,OAAO,CAAC,IAAI;YAAS,cAAc,CAAC,eAAe,CAAC,GAAW,OAAO,CAAC,IAAI,CAAC;QAChF,IAAI,OAAO,CAAC,WAAW;YAAE,cAAc,CAAC,uBAAuB,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC;QAEvF,MAAM,WAAW,GAAG,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC1E,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC;QAE1C,MAAM,EACJ,YAAY,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EACxD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,GAAG,EAClD,QAAQ,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EACjC,GAAG,IAAI,EACR,GAAG,OAAO,CAAC;QAEZ,MAAM,YAAY,GAAkB;YAClC,GAAG,IAAI;YACP,MAAM,EAAE,YAAY;YACpB,OAAO,EAAE,QAAQ;YACjB,cAAc;SACf,CAAC;QAEF,IAAI,QAAQ,IAAI,WAAW,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;YACnD,YAAY,CAAC,KAAK,GAAG,iBAAiB,CAAC,WAAW,EAAE,iBAAiB,EAAE,QAAQ,CAAC,CAAC;QACnF,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,CAAC;QAEpB,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;QAChC,IAAI,CAAC,kBAAkB,GAAG,WAAW,CAAC;QACtC,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;IAClC,CAAC;CACF"}
|
package/dist/esm/failopen.d.ts
CHANGED
|
@@ -3,5 +3,23 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export declare function isProxyError(error: unknown): boolean;
|
|
5
5
|
export declare function shouldNotFailOpen(error: unknown): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Build the direct-provider URL from a proxy request URL.
|
|
8
|
+
*
|
|
9
|
+
* Strips the /v1/proxy/{provider}/ prefix from the request path, then prepends
|
|
10
|
+
* the fallback base URL's path (e.g. "/v1" for OpenAI, "" for Anthropic) so
|
|
11
|
+
* the final URL hits the real provider endpoint.
|
|
12
|
+
*
|
|
13
|
+
* Example (OpenAI):
|
|
14
|
+
* originalUrl: https://proxy.tolvyn.io/v1/proxy/openai/chat/completions
|
|
15
|
+
* fallbackBase: https://api.openai.com/v1
|
|
16
|
+
* → https://api.openai.com/v1/chat/completions
|
|
17
|
+
*
|
|
18
|
+
* Example (Anthropic):
|
|
19
|
+
* originalUrl: https://proxy.tolvyn.io/v1/proxy/anthropic/v1/messages
|
|
20
|
+
* fallbackBase: https://api.anthropic.com
|
|
21
|
+
* → https://api.anthropic.com/v1/messages
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildFallbackUrl(originalUrl: URL, fallbackBaseUrl: string): URL;
|
|
6
24
|
export declare function makeFailOpenFetch(fallbackKey: string, directUrl: string, provider: string): typeof globalThis.fetch;
|
|
7
25
|
//# sourceMappingURL=failopen.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failopen.d.ts","sourceRoot":"","sources":["../../src/failopen.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"failopen.d.ts","sourceRoot":"","sources":["../../src/failopen.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAoCpD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CASzD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,GAAG,GAAG,CAa/E;AAED,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,UAAU,CAAC,KAAK,CAgCzB"}
|
package/dist/esm/failopen.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Fail-open helpers: detect proxy unreachability and retry direct.
|
|
3
3
|
*/
|
|
4
|
+
const PROXY_PREFIX_RE = /^\/v1\/proxy\/(?:openai|anthropic|google)\//;
|
|
4
5
|
export function isProxyError(error) {
|
|
5
6
|
if (!error || typeof error !== 'object')
|
|
6
7
|
return false;
|
|
@@ -43,6 +44,35 @@ export function shouldNotFailOpen(error) {
|
|
|
43
44
|
}
|
|
44
45
|
return false;
|
|
45
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Build the direct-provider URL from a proxy request URL.
|
|
49
|
+
*
|
|
50
|
+
* Strips the /v1/proxy/{provider}/ prefix from the request path, then prepends
|
|
51
|
+
* the fallback base URL's path (e.g. "/v1" for OpenAI, "" for Anthropic) so
|
|
52
|
+
* the final URL hits the real provider endpoint.
|
|
53
|
+
*
|
|
54
|
+
* Example (OpenAI):
|
|
55
|
+
* originalUrl: https://proxy.tolvyn.io/v1/proxy/openai/chat/completions
|
|
56
|
+
* fallbackBase: https://api.openai.com/v1
|
|
57
|
+
* → https://api.openai.com/v1/chat/completions
|
|
58
|
+
*
|
|
59
|
+
* Example (Anthropic):
|
|
60
|
+
* originalUrl: https://proxy.tolvyn.io/v1/proxy/anthropic/v1/messages
|
|
61
|
+
* fallbackBase: https://api.anthropic.com
|
|
62
|
+
* → https://api.anthropic.com/v1/messages
|
|
63
|
+
*/
|
|
64
|
+
export function buildFallbackUrl(originalUrl, fallbackBaseUrl) {
|
|
65
|
+
const fb = new URL(fallbackBaseUrl);
|
|
66
|
+
const fbBasePath = fb.pathname.replace(/\/$/, ''); // "/v1" or ""
|
|
67
|
+
const stripped = originalUrl.pathname.replace(PROXY_PREFIX_RE, '/');
|
|
68
|
+
const finalPath = fbBasePath + stripped;
|
|
69
|
+
const newUrl = new URL(originalUrl.toString());
|
|
70
|
+
newUrl.protocol = fb.protocol;
|
|
71
|
+
newUrl.hostname = fb.hostname;
|
|
72
|
+
newUrl.port = fb.port;
|
|
73
|
+
newUrl.pathname = finalPath;
|
|
74
|
+
return newUrl;
|
|
75
|
+
}
|
|
46
76
|
export function makeFailOpenFetch(fallbackKey, directUrl, provider) {
|
|
47
77
|
return async function failOpenFetch(input, init) {
|
|
48
78
|
try {
|
|
@@ -61,11 +91,7 @@ export function makeFailOpenFetch(fallbackKey, directUrl, provider) {
|
|
|
61
91
|
: input instanceof URL
|
|
62
92
|
? input.href
|
|
63
93
|
: input.url;
|
|
64
|
-
const url = new URL(originalUrl);
|
|
65
|
-
const directBase = new URL(directUrl);
|
|
66
|
-
url.hostname = directBase.hostname;
|
|
67
|
-
url.protocol = directBase.protocol;
|
|
68
|
-
url.port = directBase.port;
|
|
94
|
+
const url = buildFallbackUrl(new URL(originalUrl), directUrl);
|
|
69
95
|
const newInit = { ...(init ?? {}) };
|
|
70
96
|
const headers = new Headers(init?.headers ?? {});
|
|
71
97
|
headers.set('Authorization', `Bearer ${fallbackKey}`);
|
package/dist/esm/failopen.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failopen.js","sourceRoot":"","sources":["../../src/failopen.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"failopen.js","sourceRoot":"","sources":["../../src/failopen.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/google.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface TolvynGoogleOptions {
|
|
|
14
14
|
failOpen?: boolean;
|
|
15
15
|
googleApiKey?: string;
|
|
16
16
|
}
|
|
17
|
-
export declare class
|
|
17
|
+
export declare class Google extends GoogleGenerativeAI {
|
|
18
18
|
readonly _tolvynFailOpen: boolean;
|
|
19
19
|
readonly _tolvynFallbackKey: string | undefined;
|
|
20
20
|
private readonly _tolvynProxyUrl;
|
package/dist/esm/google.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/google.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,cAAc,EACf,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/google.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,cAAc,EACf,MAAM,uBAAuB,CAAC;AAO/B,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAID,qBAAa,MAAO,SAAQ,kBAAkB;IAC5C,SAAgB,eAAe,EAAE,OAAO,CAAC;IACzC,SAAgB,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IACvD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;gBAE5C,OAAO,GAAE,mBAAwB;IAgCpC,kBAAkB,CACzB,WAAW,EAAE,WAAW,EACxB,cAAc,CAAC,EAAE,cAAc;CAkClC"}
|
package/dist/esm/google.js
CHANGED
|
@@ -2,17 +2,22 @@
|
|
|
2
2
|
* TOLVYN Google wrapper — thin drop-in over @google/generative-ai.
|
|
3
3
|
*/
|
|
4
4
|
import { GoogleGenerativeAI, } from '@google/generative-ai';
|
|
5
|
+
import { isProxyError } from './failopen';
|
|
5
6
|
// The proxy base URL is prepended to Google API paths (/v1beta/models/...).
|
|
6
7
|
// The TOLVYN proxy strips /v1/proxy/google and forwards the remainder to Google.
|
|
7
|
-
const GOOGLE_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/google';
|
|
8
|
-
|
|
8
|
+
const GOOGLE_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/google/';
|
|
9
|
+
// ND-05: renamed from TolvynGoogle → Google so stack traces and constructor.name
|
|
10
|
+
// match the public API. Old name remains re-exportable via index.ts alias.
|
|
11
|
+
export class Google extends GoogleGenerativeAI {
|
|
9
12
|
constructor(options = {}) {
|
|
10
13
|
const tolvynApiKey = options.tolvynApiKey ?? process.env['TOLVYN_API_KEY'];
|
|
11
14
|
if (!tolvynApiKey) {
|
|
12
15
|
throw new Error('tolvynApiKey required. Set TOLVYN_API_KEY env var or pass tolvynApiKey.');
|
|
13
16
|
}
|
|
14
|
-
//
|
|
15
|
-
//
|
|
17
|
+
// ND-06: The TOLVYN API key is passed as the GoogleGenerativeAI api key.
|
|
18
|
+
// Google's SDK sends this as `x-goog-api-key`; the TOLVYN proxy reads
|
|
19
|
+
// `x-goog-api-key` as a Bearer-auth fallback for Google requests.
|
|
20
|
+
// This is intentional — keep this comment if you refactor.
|
|
16
21
|
super(tolvynApiKey);
|
|
17
22
|
this._tolvynProxyUrl =
|
|
18
23
|
options.proxyUrl ??
|
|
@@ -41,7 +46,31 @@ export class TolvynGoogle extends GoogleGenerativeAI {
|
|
|
41
46
|
customHeaders: this._tolvynHeaders,
|
|
42
47
|
...requestOptions,
|
|
43
48
|
};
|
|
44
|
-
|
|
49
|
+
const model = super.getGenerativeModel(modelParams, mergedOptions);
|
|
50
|
+
// ND-01: wrap generateContent so a proxy-unreachable error retries against
|
|
51
|
+
// the real Google API using the fallback key.
|
|
52
|
+
if (!this._tolvynFailOpen || !this._tolvynFallbackKey) {
|
|
53
|
+
return model;
|
|
54
|
+
}
|
|
55
|
+
const fallbackKey = this._tolvynFallbackKey;
|
|
56
|
+
const originalGenerate = model.generateContent.bind(model);
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
+
model.generateContent = async (...args) => {
|
|
59
|
+
try {
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
61
|
+
return await originalGenerate(...args);
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
if (!isProxyError(e))
|
|
65
|
+
throw e;
|
|
66
|
+
console.error('TOLVYN proxy unreachable — routing direct to Google (fail-open)');
|
|
67
|
+
const fallbackAI = new GoogleGenerativeAI(fallbackKey);
|
|
68
|
+
const fallbackModel = fallbackAI.getGenerativeModel(modelParams, requestOptions);
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
|
+
return fallbackModel.generateContent(...args);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
return model;
|
|
45
74
|
}
|
|
46
75
|
}
|
|
47
76
|
//# sourceMappingURL=google.js.map
|
package/dist/esm/google.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/google.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,kBAAkB,GAGnB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/google.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ export { OpenAI } from './client';
|
|
|
2
2
|
export type { TolvynOpenAIOptions } from './client';
|
|
3
3
|
export { Anthropic } from './anthropic';
|
|
4
4
|
export type { TolvynAnthropicOptions } from './anthropic';
|
|
5
|
-
export {
|
|
5
|
+
export { Google } from './google';
|
|
6
6
|
export type { TolvynGoogleOptions } from './google';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/esm/index.js
CHANGED
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tolvyn",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Drop-in replacement for the OpenAI/Anthropic SDK — routes through TOLVYN for cost attribution, budget enforcement, and audit logging.",
|
|
5
5
|
"main": "./dist/cjs/index.js",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -23,9 +23,16 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@anthropic-ai/sdk": "^0.20.0",
|
|
26
|
-
"@google/generative-ai": "^0.21.0",
|
|
27
26
|
"openai": "^4.0.0"
|
|
28
27
|
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@google/generative-ai": "^0.21.0"
|
|
30
|
+
},
|
|
31
|
+
"peerDependenciesMeta": {
|
|
32
|
+
"@google/generative-ai": {
|
|
33
|
+
"optional": true
|
|
34
|
+
}
|
|
35
|
+
},
|
|
29
36
|
"devDependencies": {
|
|
30
37
|
"@types/jest": "^29.5.0",
|
|
31
38
|
"@types/node": "^20.0.0",
|
package/src/client.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* TOLVYN OpenAI wrapper — thin drop-in over the official openai package.
|
|
3
3
|
*/
|
|
4
4
|
import OpenAIBase, { ClientOptions } from 'openai';
|
|
5
|
-
import {
|
|
5
|
+
import { makeFailOpenFetch } from './failopen';
|
|
6
6
|
|
|
7
7
|
const OPENAI_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/openai/';
|
|
8
8
|
const OPENAI_DIRECT_URL = 'https://api.openai.com/v1';
|
|
@@ -21,49 +21,6 @@ export interface TolvynOpenAIOptions
|
|
|
21
21
|
openAIApiKey?: string;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
function makeFailOpenFetch(
|
|
25
|
-
fallbackKey: string,
|
|
26
|
-
directUrl: string,
|
|
27
|
-
provider: string
|
|
28
|
-
): typeof globalThis.fetch {
|
|
29
|
-
return async function failOpenFetch(
|
|
30
|
-
input: RequestInfo | URL,
|
|
31
|
-
init?: RequestInit
|
|
32
|
-
): Promise<Response> {
|
|
33
|
-
try {
|
|
34
|
-
const res = await fetch(input, init);
|
|
35
|
-
if (res.status === 503) {
|
|
36
|
-
throw Object.assign(new Error('503 from proxy'), { status: 503 });
|
|
37
|
-
}
|
|
38
|
-
return res;
|
|
39
|
-
} catch (err: unknown) {
|
|
40
|
-
if (shouldNotFailOpen(err) || !isProxyError(err)) throw err;
|
|
41
|
-
console.error(
|
|
42
|
-
`TOLVYN proxy unreachable — routing direct to ${provider} (fail-open)`
|
|
43
|
-
);
|
|
44
|
-
const originalUrl =
|
|
45
|
-
typeof input === 'string'
|
|
46
|
-
? input
|
|
47
|
-
: input instanceof URL
|
|
48
|
-
? input.href
|
|
49
|
-
: (input as Request).url;
|
|
50
|
-
const url = new URL(originalUrl);
|
|
51
|
-
const directBase = new URL(directUrl);
|
|
52
|
-
url.hostname = directBase.hostname;
|
|
53
|
-
url.protocol = directBase.protocol;
|
|
54
|
-
url.port = directBase.port;
|
|
55
|
-
url.pathname = url.pathname; // keep path intact
|
|
56
|
-
|
|
57
|
-
const newInit: RequestInit = { ...(init ?? {}) };
|
|
58
|
-
const headers = new Headers((init?.headers as HeadersInit) ?? {});
|
|
59
|
-
headers.set('Authorization', `Bearer ${fallbackKey}`);
|
|
60
|
-
newInit.headers = headers;
|
|
61
|
-
|
|
62
|
-
return fetch(url.toString(), newInit);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
24
|
export class OpenAI extends OpenAIBase {
|
|
68
25
|
public readonly _tolvynFailOpen: boolean;
|
|
69
26
|
public readonly _tolvynFallbackKey: string | undefined;
|
package/src/failopen.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Fail-open helpers: detect proxy unreachability and retry direct.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
const PROXY_PREFIX_RE = /^\/v1\/proxy\/(?:openai|anthropic|google)\//;
|
|
6
|
+
|
|
5
7
|
export function isProxyError(error: unknown): boolean {
|
|
6
8
|
if (!error || typeof error !== 'object') return false;
|
|
7
9
|
const err = error as Record<string, unknown>;
|
|
@@ -51,14 +53,46 @@ export function shouldNotFailOpen(error: unknown): boolean {
|
|
|
51
53
|
return false;
|
|
52
54
|
}
|
|
53
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Build the direct-provider URL from a proxy request URL.
|
|
58
|
+
*
|
|
59
|
+
* Strips the /v1/proxy/{provider}/ prefix from the request path, then prepends
|
|
60
|
+
* the fallback base URL's path (e.g. "/v1" for OpenAI, "" for Anthropic) so
|
|
61
|
+
* the final URL hits the real provider endpoint.
|
|
62
|
+
*
|
|
63
|
+
* Example (OpenAI):
|
|
64
|
+
* originalUrl: https://proxy.tolvyn.io/v1/proxy/openai/chat/completions
|
|
65
|
+
* fallbackBase: https://api.openai.com/v1
|
|
66
|
+
* → https://api.openai.com/v1/chat/completions
|
|
67
|
+
*
|
|
68
|
+
* Example (Anthropic):
|
|
69
|
+
* originalUrl: https://proxy.tolvyn.io/v1/proxy/anthropic/v1/messages
|
|
70
|
+
* fallbackBase: https://api.anthropic.com
|
|
71
|
+
* → https://api.anthropic.com/v1/messages
|
|
72
|
+
*/
|
|
73
|
+
export function buildFallbackUrl(originalUrl: URL, fallbackBaseUrl: string): URL {
|
|
74
|
+
const fb = new URL(fallbackBaseUrl);
|
|
75
|
+
const fbBasePath = fb.pathname.replace(/\/$/, ''); // "/v1" or ""
|
|
76
|
+
|
|
77
|
+
const stripped = originalUrl.pathname.replace(PROXY_PREFIX_RE, '/');
|
|
78
|
+
const finalPath = fbBasePath + stripped;
|
|
79
|
+
|
|
80
|
+
const newUrl = new URL(originalUrl.toString());
|
|
81
|
+
newUrl.protocol = fb.protocol;
|
|
82
|
+
newUrl.hostname = fb.hostname;
|
|
83
|
+
newUrl.port = fb.port;
|
|
84
|
+
newUrl.pathname = finalPath;
|
|
85
|
+
return newUrl;
|
|
86
|
+
}
|
|
87
|
+
|
|
54
88
|
export function makeFailOpenFetch(
|
|
55
89
|
fallbackKey: string,
|
|
56
90
|
directUrl: string,
|
|
57
|
-
provider: string
|
|
91
|
+
provider: string,
|
|
58
92
|
): typeof globalThis.fetch {
|
|
59
93
|
return async function failOpenFetch(
|
|
60
94
|
input: RequestInfo | URL,
|
|
61
|
-
init?: RequestInit
|
|
95
|
+
init?: RequestInit,
|
|
62
96
|
): Promise<Response> {
|
|
63
97
|
try {
|
|
64
98
|
const res = await fetch(input, init);
|
|
@@ -69,7 +103,7 @@ export function makeFailOpenFetch(
|
|
|
69
103
|
} catch (err: unknown) {
|
|
70
104
|
if (shouldNotFailOpen(err) || !isProxyError(err)) throw err;
|
|
71
105
|
console.error(
|
|
72
|
-
`TOLVYN proxy unreachable — routing direct to ${provider} (fail-open)
|
|
106
|
+
`TOLVYN proxy unreachable — routing direct to ${provider} (fail-open)`,
|
|
73
107
|
);
|
|
74
108
|
const originalUrl =
|
|
75
109
|
typeof input === 'string'
|
|
@@ -77,11 +111,7 @@ export function makeFailOpenFetch(
|
|
|
77
111
|
: input instanceof URL
|
|
78
112
|
? input.href
|
|
79
113
|
: (input as Request).url;
|
|
80
|
-
const url = new URL(originalUrl);
|
|
81
|
-
const directBase = new URL(directUrl);
|
|
82
|
-
url.hostname = directBase.hostname;
|
|
83
|
-
url.protocol = directBase.protocol;
|
|
84
|
-
url.port = directBase.port;
|
|
114
|
+
const url = buildFallbackUrl(new URL(originalUrl), directUrl);
|
|
85
115
|
|
|
86
116
|
const newInit: RequestInit = { ...(init ?? {}) };
|
|
87
117
|
const headers = new Headers((init?.headers as HeadersInit) ?? {});
|
package/src/google.ts
CHANGED
|
@@ -6,10 +6,11 @@ import {
|
|
|
6
6
|
ModelParams,
|
|
7
7
|
RequestOptions,
|
|
8
8
|
} from '@google/generative-ai';
|
|
9
|
+
import { isProxyError } from './failopen';
|
|
9
10
|
|
|
10
11
|
// The proxy base URL is prepended to Google API paths (/v1beta/models/...).
|
|
11
12
|
// The TOLVYN proxy strips /v1/proxy/google and forwards the remainder to Google.
|
|
12
|
-
const GOOGLE_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/google';
|
|
13
|
+
const GOOGLE_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/google/';
|
|
13
14
|
|
|
14
15
|
export interface TolvynGoogleOptions {
|
|
15
16
|
tolvynApiKey?: string;
|
|
@@ -24,7 +25,9 @@ export interface TolvynGoogleOptions {
|
|
|
24
25
|
googleApiKey?: string;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
// ND-05: renamed from TolvynGoogle → Google so stack traces and constructor.name
|
|
29
|
+
// match the public API. Old name remains re-exportable via index.ts alias.
|
|
30
|
+
export class Google extends GoogleGenerativeAI {
|
|
28
31
|
public readonly _tolvynFailOpen: boolean;
|
|
29
32
|
public readonly _tolvynFallbackKey: string | undefined;
|
|
30
33
|
private readonly _tolvynProxyUrl: string;
|
|
@@ -38,8 +41,10 @@ export class TolvynGoogle extends GoogleGenerativeAI {
|
|
|
38
41
|
);
|
|
39
42
|
}
|
|
40
43
|
|
|
41
|
-
//
|
|
42
|
-
//
|
|
44
|
+
// ND-06: The TOLVYN API key is passed as the GoogleGenerativeAI api key.
|
|
45
|
+
// Google's SDK sends this as `x-goog-api-key`; the TOLVYN proxy reads
|
|
46
|
+
// `x-goog-api-key` as a Bearer-auth fallback for Google requests.
|
|
47
|
+
// This is intentional — keep this comment if you refactor.
|
|
43
48
|
super(tolvynApiKey);
|
|
44
49
|
|
|
45
50
|
this._tolvynProxyUrl =
|
|
@@ -69,6 +74,31 @@ export class TolvynGoogle extends GoogleGenerativeAI {
|
|
|
69
74
|
customHeaders: this._tolvynHeaders,
|
|
70
75
|
...requestOptions,
|
|
71
76
|
};
|
|
72
|
-
|
|
77
|
+
const model = super.getGenerativeModel(modelParams, mergedOptions);
|
|
78
|
+
|
|
79
|
+
// ND-01: wrap generateContent so a proxy-unreachable error retries against
|
|
80
|
+
// the real Google API using the fallback key.
|
|
81
|
+
if (!this._tolvynFailOpen || !this._tolvynFallbackKey) {
|
|
82
|
+
return model;
|
|
83
|
+
}
|
|
84
|
+
const fallbackKey = this._tolvynFallbackKey;
|
|
85
|
+
const originalGenerate = model.generateContent.bind(model);
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
87
|
+
(model as any).generateContent = async (...args: unknown[]) => {
|
|
88
|
+
try {
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
90
|
+
return await (originalGenerate as any)(...args);
|
|
91
|
+
} catch (e) {
|
|
92
|
+
if (!isProxyError(e)) throw e;
|
|
93
|
+
console.error(
|
|
94
|
+
'TOLVYN proxy unreachable — routing direct to Google (fail-open)',
|
|
95
|
+
);
|
|
96
|
+
const fallbackAI = new GoogleGenerativeAI(fallbackKey);
|
|
97
|
+
const fallbackModel = fallbackAI.getGenerativeModel(modelParams, requestOptions);
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
99
|
+
return (fallbackModel.generateContent as any)(...args);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
return model;
|
|
73
103
|
}
|
|
74
104
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,5 +2,5 @@ export { OpenAI } from './client';
|
|
|
2
2
|
export type { TolvynOpenAIOptions } from './client';
|
|
3
3
|
export { Anthropic } from './anthropic';
|
|
4
4
|
export type { TolvynAnthropicOptions } from './anthropic';
|
|
5
|
-
export {
|
|
5
|
+
export { Google } from './google';
|
|
6
6
|
export type { TolvynGoogleOptions } from './google';
|