tolvyn 1.0.7 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -50,10 +50,10 @@ const { OpenAI } = require("tolvyn");
50
50
  const client = new OpenAI({ tolvynApiKey: process.env.TOLVYN_API_KEY, team: "backend" });
51
51
  ```
52
52
 
53
- ## All three providers
53
+ ## All four providers
54
54
 
55
55
  ```typescript
56
- import { OpenAI, Anthropic, Google } from "tolvyn";
56
+ import { OpenAI, Anthropic, Google, DeepSeek } from "tolvyn";
57
57
 
58
58
  // OpenAI
59
59
  const oai = new OpenAI({
@@ -70,6 +70,16 @@ const anth = new Anthropic({
70
70
  // Google (requires @google/generative-ai peer dep)
71
71
  const goog = new Google({ tolvynApiKey: "tlv_live_..." });
72
72
  const model = goog.getGenerativeModel({ model: "gemini-1.5-flash" });
73
+
74
+ // DeepSeek (OpenAI-compatible API)
75
+ const ds = new DeepSeek({
76
+ tolvynApiKey: "tlv_live_...",
77
+ deepSeekApiKey: "sk-...", // optional — enables fail-open fallback
78
+ });
79
+ const dsResponse = await ds.chat.completions.create({
80
+ model: "deepseek-chat",
81
+ messages: [{ role: "user", content: "Hello" }],
82
+ });
73
83
  ```
74
84
 
75
85
  ## Attribution headers
@@ -88,11 +98,11 @@ const client = new OpenAI({
88
98
  });
89
99
  ```
90
100
 
91
- The TOLVYN proxy strips all six headers before forwarding the request upstream — they never reach OpenAI/Anthropic/Google.
101
+ The TOLVYN proxy strips all six headers before forwarding the request upstream — they never reach OpenAI/Anthropic/Google/DeepSeek.
92
102
 
93
103
  ## Fail-open behavior
94
104
 
95
- If TOLVYN's proxy is unreachable, the SDK automatically retries the request directly against the provider (requires `openAIApiKey` / `anthropicApiKey` / `googleApiKey` to be set). Disable with `failOpen: false`.
105
+ If TOLVYN's proxy is unreachable, the SDK automatically retries the request directly against the provider (requires `openAIApiKey` / `anthropicApiKey` / `googleApiKey` / `deepSeekApiKey` to be set). Disable with `failOpen: false`.
96
106
 
97
107
  Triggers on: connection refused, timeout, DNS failure, HTTP 503.
98
108
  Does NOT trigger on: 4xx errors (auth failures, rate limits, bad requests).
@@ -106,7 +116,8 @@ Requests that fail open bypass the proxy and are not metered for that call.
106
116
  | `TOLVYN_API_KEY` | Yes (unless `tolvynApiKey` option is passed) | Your TOLVYN API key (`tlv_live_...`) |
107
117
  | `OPENAI_API_KEY` | For fail-open | Fallback OpenAI key if proxy unreachable |
108
118
  | `ANTHROPIC_API_KEY` | For fail-open | Fallback Anthropic key if proxy unreachable |
109
- | `GOOGLE_API_KEY` | For fail-open | Reserved; Google fail-open is implemented in v1.0.6+ |
119
+ | `GOOGLE_API_KEY` | For fail-open | Fallback Google key if proxy unreachable (`generateContent` in v1.0.6+; `generateContentStream`/`countTokens`/`embedContent`/`batchEmbedContents` in v1.0.10+) |
120
+ | `DEEPSEEK_API_KEY` | For fail-open | Fallback DeepSeek key if proxy unreachable (v1.0.9+) |
110
121
  | `TOLVYN_PROXY_URL` | No | Override proxy URL |
111
122
 
112
123
  ## API keys
@@ -114,7 +125,7 @@ Requests that fail open bypass the proxy and are not metered for that call.
114
125
  - Production keys start with `tlv_live_`
115
126
  - Test keys start with `tlv_test_` (use these in CI / staging)
116
127
  - Get your key at [app.tolvyn.io](https://app.tolvyn.io) → API Keys
117
- - **Provider keys** (OpenAI / Anthropic / Google) go in the dashboard under **Account → Provider Keys** — never in code. They are stored encrypted server-side.
128
+ - **Provider keys** (OpenAI / Anthropic / Google / DeepSeek) go in the dashboard under **Account → Provider Keys** — never in code. They are stored encrypted server-side.
118
129
 
119
130
  ## TypeScript
120
131
 
@@ -125,6 +136,7 @@ import type {
125
136
  TolvynOpenAIOptions,
126
137
  TolvynAnthropicOptions,
127
138
  TolvynGoogleOptions,
139
+ TolvynDeepSeekOptions,
128
140
  } from "tolvyn";
129
141
  ```
130
142
 
@@ -0,0 +1,28 @@
1
+ /**
2
+ * TOLVYN DeepSeek wrapper — thin drop-in over the official openai package.
3
+ *
4
+ * DeepSeek exposes an OpenAI-compatible API, so this wraps the same `openai`
5
+ * base client as the OpenAI wrapper but points the proxy/fallback at DeepSeek
6
+ * and falls back with the DeepSeek key. Fail-open routes direct to
7
+ * https://api.deepseek.com/v1 (Bearer auth) when the TOLVYN proxy is unreachable.
8
+ */
9
+ import OpenAIBase, { ClientOptions } from 'openai';
10
+ export interface TolvynDeepSeekOptions extends Omit<ClientOptions, 'apiKey' | 'baseURL'> {
11
+ tolvynApiKey?: string;
12
+ proxyUrl?: string;
13
+ team?: string;
14
+ service?: string;
15
+ feature?: string;
16
+ agent?: string;
17
+ user?: string;
18
+ endCustomer?: string;
19
+ failOpen?: boolean;
20
+ deepSeekApiKey?: string;
21
+ }
22
+ export declare class DeepSeek extends OpenAIBase {
23
+ readonly _tolvynFailOpen: boolean;
24
+ readonly _tolvynFallbackKey: string | undefined;
25
+ readonly _tolvynProxyUrl: string;
26
+ constructor(options?: TolvynDeepSeekOptions);
27
+ }
28
+ //# sourceMappingURL=deepseek.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepseek.d.ts","sourceRoot":"","sources":["../../src/deepseek.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,UAAU,EAAE,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAMnD,MAAM,WAAW,qBACf,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,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,QAAS,SAAQ,UAAU;IACtC,SAAgB,eAAe,EAAE,OAAO,CAAC;IACzC,SAAgB,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IACvD,SAAgB,eAAe,EAAE,MAAM,CAAC;gBAE5B,OAAO,GAAE,qBAA0B;CAgDhD"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DeepSeek = void 0;
7
+ /**
8
+ * TOLVYN DeepSeek wrapper — thin drop-in over the official openai package.
9
+ *
10
+ * DeepSeek exposes an OpenAI-compatible API, so this wraps the same `openai`
11
+ * base client as the OpenAI wrapper but points the proxy/fallback at DeepSeek
12
+ * and falls back with the DeepSeek key. Fail-open routes direct to
13
+ * https://api.deepseek.com/v1 (Bearer auth) when the TOLVYN proxy is unreachable.
14
+ */
15
+ const openai_1 = __importDefault(require("openai"));
16
+ const failopen_1 = require("./failopen");
17
+ const DEEPSEEK_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/deepseek/';
18
+ const DEEPSEEK_DIRECT_URL = 'https://api.deepseek.com/v1';
19
+ class DeepSeek extends openai_1.default {
20
+ constructor(options = {}) {
21
+ const tolvynApiKey = options.tolvynApiKey ?? process.env['TOLVYN_API_KEY'];
22
+ if (!tolvynApiKey) {
23
+ throw new Error('tolvynApiKey required. Set TOLVYN_API_KEY env var or pass tolvynApiKey.');
24
+ }
25
+ const proxyUrl = options.proxyUrl ??
26
+ process.env['TOLVYN_PROXY_URL'] ??
27
+ DEEPSEEK_DEFAULT_PROXY_URL;
28
+ const defaultHeaders = {};
29
+ if (options.team)
30
+ defaultHeaders['X-Tolvyn-Team'] = options.team;
31
+ if (options.service)
32
+ defaultHeaders['X-Tolvyn-Service'] = options.service;
33
+ if (options.feature)
34
+ defaultHeaders['X-Tolvyn-Feature'] = options.feature;
35
+ if (options.agent)
36
+ defaultHeaders['X-Tolvyn-Agent'] = options.agent;
37
+ if (options.user)
38
+ defaultHeaders['X-Tolvyn-User'] = options.user;
39
+ if (options.endCustomer)
40
+ defaultHeaders['X-Tolvyn-End-Customer'] = options.endCustomer;
41
+ const fallbackKey = options.deepSeekApiKey ?? process.env['DEEPSEEK_API_KEY'];
42
+ const failOpen = options.failOpen ?? true;
43
+ const { tolvynApiKey: _tk, proxyUrl: _pu, team: _t, service: _sv, feature: _f, agent: _a, user: _u, endCustomer: _ec, failOpen: _fo, deepSeekApiKey: _dsk, ...rest } = options;
44
+ const superOptions = {
45
+ ...rest,
46
+ apiKey: tolvynApiKey,
47
+ baseURL: proxyUrl,
48
+ defaultHeaders,
49
+ };
50
+ if (failOpen && fallbackKey && !superOptions.fetch) {
51
+ superOptions.fetch = (0, failopen_1.makeFailOpenFetch)(fallbackKey, DEEPSEEK_DIRECT_URL, 'DeepSeek');
52
+ }
53
+ super(superOptions);
54
+ this._tolvynFailOpen = failOpen;
55
+ this._tolvynFallbackKey = fallbackKey;
56
+ this._tolvynProxyUrl = proxyUrl;
57
+ }
58
+ }
59
+ exports.DeepSeek = DeepSeek;
60
+ //# sourceMappingURL=deepseek.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepseek.js","sourceRoot":"","sources":["../../src/deepseek.ts"],"names":[],"mappings":";;;;;;AAAA;;;;;;;GAOG;AACH,oDAAmD;AACnD,yCAA+C;AAE/C,MAAM,0BAA0B,GAAG,4CAA4C,CAAC;AAChF,MAAM,mBAAmB,GAAG,6BAA6B,CAAC;AAgB1D,MAAa,QAAS,SAAQ,gBAAU;IAKtC,YAAY,UAAiC,EAAE;QAC7C,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,0BAA0B,CAAC;QAE7B,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,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAC9E,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,cAAc,EAAE,IAAI,EACnC,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,mBAAmB,EAAE,UAAU,CAAC,CAAC;QACvF,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,4BAqDC"}
@@ -21,5 +21,14 @@ export declare function shouldNotFailOpen(error: unknown): boolean;
21
21
  * → https://api.anthropic.com/v1/messages
22
22
  */
23
23
  export declare function buildFallbackUrl(originalUrl: URL, fallbackBaseUrl: string): URL;
24
+ /**
25
+ * Rewrite `headers` in place to authenticate a direct provider call.
26
+ *
27
+ * Strips every inbound auth header (Authorization/x-api-key/x-goog-api-key —
28
+ * each may carry the TOLVYN key) and sets the single header the provider's
29
+ * direct API expects, with the provider's own key. Fixes ND-09 (Bearer sent to
30
+ * Anthropic → 401) and ND-11 (the TOLVYN key in x-api-key leaking on fallback).
31
+ */
32
+ export declare function applyFallbackAuth(headers: Headers, provider: string, fallbackKey: string): void;
24
33
  export declare function makeFailOpenFetch(fallbackKey: string, directUrl: string, provider: string): typeof globalThis.fetch;
25
34
  //# sourceMappingURL=failopen.d.ts.map
@@ -1 +1 @@
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"}
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;AAaD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,IAAI,CAUN;AAED,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,UAAU,CAAC,KAAK,CAgCzB"}
@@ -6,8 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.isProxyError = isProxyError;
7
7
  exports.shouldNotFailOpen = shouldNotFailOpen;
8
8
  exports.buildFallbackUrl = buildFallbackUrl;
9
+ exports.applyFallbackAuth = applyFallbackAuth;
9
10
  exports.makeFailOpenFetch = makeFailOpenFetch;
10
- const PROXY_PREFIX_RE = /^\/v1\/proxy\/(?:openai|anthropic|google)\//;
11
+ const PROXY_PREFIX_RE = /^\/v1\/proxy\/(?:openai|anthropic|google|deepseek)\//;
11
12
  function isProxyError(error) {
12
13
  if (!error || typeof error !== 'object')
13
14
  return false;
@@ -79,6 +80,36 @@ function buildFallbackUrl(originalUrl, fallbackBaseUrl) {
79
80
  newUrl.pathname = finalPath;
80
81
  return newUrl;
81
82
  }
83
+ // The header each provider's DIRECT API reads the API key from. The TOLVYN
84
+ // proxy accepts Authorization/x-api-key/x-goog-api-key interchangeably, but the
85
+ // providers themselves do NOT: Anthropic authenticates only via x-api-key and
86
+ // Google only via x-goog-api-key — sending Bearer to them 401s.
87
+ const PROVIDER_AUTH_HEADER = {
88
+ openai: 'Authorization',
89
+ anthropic: 'x-api-key',
90
+ google: 'x-goog-api-key',
91
+ deepseek: 'Authorization', // OpenAI-compatible; Bearer auth (same as the default)
92
+ };
93
+ /**
94
+ * Rewrite `headers` in place to authenticate a direct provider call.
95
+ *
96
+ * Strips every inbound auth header (Authorization/x-api-key/x-goog-api-key —
97
+ * each may carry the TOLVYN key) and sets the single header the provider's
98
+ * direct API expects, with the provider's own key. Fixes ND-09 (Bearer sent to
99
+ * Anthropic → 401) and ND-11 (the TOLVYN key in x-api-key leaking on fallback).
100
+ */
101
+ function applyFallbackAuth(headers, provider, fallbackKey) {
102
+ headers.delete('Authorization');
103
+ headers.delete('x-api-key');
104
+ headers.delete('x-goog-api-key');
105
+ const header = PROVIDER_AUTH_HEADER[provider.toLowerCase()] ?? 'Authorization';
106
+ if (header === 'Authorization') {
107
+ headers.set('Authorization', `Bearer ${fallbackKey}`);
108
+ }
109
+ else {
110
+ headers.set(header, fallbackKey);
111
+ }
112
+ }
82
113
  function makeFailOpenFetch(fallbackKey, directUrl, provider) {
83
114
  return async function failOpenFetch(input, init) {
84
115
  try {
@@ -100,7 +131,7 @@ function makeFailOpenFetch(fallbackKey, directUrl, provider) {
100
131
  const url = buildFallbackUrl(new URL(originalUrl), directUrl);
101
132
  const newInit = { ...(init ?? {}) };
102
133
  const headers = new Headers(init?.headers ?? {});
103
- headers.set('Authorization', `Bearer ${fallbackKey}`);
134
+ applyFallbackAuth(headers, provider, fallbackKey);
104
135
  newInit.headers = headers;
105
136
  return fetch(url.toString(), newInit);
106
137
  }
@@ -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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"}
@@ -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;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"}
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;AAW/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;CAwDlC"}
@@ -8,7 +8,11 @@ const generative_ai_1 = require("@google/generative-ai");
8
8
  const failopen_1 = require("./failopen");
9
9
  // The proxy base URL is prepended to Google API paths (/v1beta/models/...).
10
10
  // The TOLVYN proxy strips /v1/proxy/google and forwards the remainder to Google.
11
- const GOOGLE_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/google/';
11
+ // ND-10: NO trailing slash. @google/generative-ai builds `${baseUrl}/${apiVersion}/...`
12
+ // WITHOUT collapsing a double slash (unlike the OpenAI/Anthropic SDKs), so a
13
+ // trailing slash here produces `.../proxy/google//v1beta/...`. This intentionally
14
+ // reverses ND-07's trailing-slash "consistency" for the Google provider only.
15
+ const GOOGLE_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/google';
12
16
  // ND-05: renamed from TolvynGoogle → Google so stack traces and constructor.name
13
17
  // match the public API. Old name remains re-exportable via index.ts alias.
14
18
  class Google extends generative_ai_1.GoogleGenerativeAI {
@@ -45,34 +49,53 @@ class Google extends generative_ai_1.GoogleGenerativeAI {
45
49
  }
46
50
  getGenerativeModel(modelParams, requestOptions) {
47
51
  const mergedOptions = {
48
- baseUrl: this._tolvynProxyUrl,
52
+ // ND-10: strip any trailing slash so an env/option override can't
53
+ // reintroduce the double-slash the Google SDK won't collapse.
54
+ baseUrl: this._tolvynProxyUrl.replace(/\/+$/, ''),
49
55
  customHeaders: this._tolvynHeaders,
50
56
  ...requestOptions,
51
57
  };
52
58
  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.
59
+ // ND-01 + BE-91: wrap every GenerativeModel network method so a
60
+ // proxy-unreachable error retries against the real Google API using the
61
+ // fallback key. Google's SDK exposes no fetch hook (unlike the
62
+ // OpenAI/Anthropic wrappers), so we rebuild a direct client — with the
63
+ // ORIGINAL requestOptions, i.e. WITHOUT the proxy baseUrl, so it hits the
64
+ // real generativelanguage.googleapis.com — and re-invoke the same method.
65
+ // (ND-01 covered only generateContent; BE-91 extends to stream/count/embed.
66
+ // startChat returns a ChatSession with separate plumbing — not yet covered.)
55
67
  if (!this._tolvynFailOpen || !this._tolvynFallbackKey) {
56
68
  return model;
57
69
  }
58
70
  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
- };
71
+ const failOpenMethods = [
72
+ 'generateContent',
73
+ 'generateContentStream',
74
+ 'countTokens',
75
+ 'embedContent',
76
+ 'batchEmbedContents',
77
+ ];
78
+ for (const method of failOpenMethods) {
79
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
80
+ const original = model[method];
81
+ if (typeof original !== 'function')
82
+ continue;
83
+ const boundOriginal = original.bind(model);
84
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
85
+ model[method] = async (...args) => {
86
+ try {
87
+ return await boundOriginal(...args);
88
+ }
89
+ catch (e) {
90
+ if (!(0, failopen_1.isProxyError)(e))
91
+ throw e;
92
+ console.error('TOLVYN proxy unreachable — routing direct to Google (fail-open)');
93
+ const fallbackModel = new generative_ai_1.GoogleGenerativeAI(fallbackKey).getGenerativeModel(modelParams, requestOptions);
94
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
95
+ return fallbackModel[method](...args);
96
+ }
97
+ };
98
+ }
76
99
  return model;
77
100
  }
78
101
  }
@@ -1 +1 @@
1
- {"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/google.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/google.ts"],"names":[],"mappings":"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"}
@@ -4,4 +4,6 @@ export { Anthropic } from './anthropic';
4
4
  export type { TolvynAnthropicOptions } from './anthropic';
5
5
  export { Google } from './google';
6
6
  export type { TolvynGoogleOptions } from './google';
7
+ export { DeepSeek } from './deepseek';
8
+ export type { TolvynDeepSeekOptions } from './deepseek';
7
9
  //# sourceMappingURL=index.d.ts.map
@@ -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,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC"}
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;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC"}
package/dist/cjs/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Google = exports.Anthropic = exports.OpenAI = void 0;
3
+ exports.DeepSeek = exports.Google = exports.Anthropic = exports.OpenAI = void 0;
4
4
  var client_1 = require("./client");
5
5
  Object.defineProperty(exports, "OpenAI", { enumerable: true, get: function () { return client_1.OpenAI; } });
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
9
  Object.defineProperty(exports, "Google", { enumerable: true, get: function () { return google_1.Google; } });
10
+ var deepseek_1 = require("./deepseek");
11
+ Object.defineProperty(exports, "DeepSeek", { enumerable: true, get: function () { return deepseek_1.DeepSeek; } });
10
12
  //# sourceMappingURL=index.js.map
@@ -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,mCAAkC;AAAzB,gGAAA,MAAM,OAAA"}
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;AAEf,uCAAsC;AAA7B,oGAAA,QAAQ,OAAA"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * TOLVYN DeepSeek wrapper — thin drop-in over the official openai package.
3
+ *
4
+ * DeepSeek exposes an OpenAI-compatible API, so this wraps the same `openai`
5
+ * base client as the OpenAI wrapper but points the proxy/fallback at DeepSeek
6
+ * and falls back with the DeepSeek key. Fail-open routes direct to
7
+ * https://api.deepseek.com/v1 (Bearer auth) when the TOLVYN proxy is unreachable.
8
+ */
9
+ import OpenAIBase, { ClientOptions } from 'openai';
10
+ export interface TolvynDeepSeekOptions extends Omit<ClientOptions, 'apiKey' | 'baseURL'> {
11
+ tolvynApiKey?: string;
12
+ proxyUrl?: string;
13
+ team?: string;
14
+ service?: string;
15
+ feature?: string;
16
+ agent?: string;
17
+ user?: string;
18
+ endCustomer?: string;
19
+ failOpen?: boolean;
20
+ deepSeekApiKey?: string;
21
+ }
22
+ export declare class DeepSeek extends OpenAIBase {
23
+ readonly _tolvynFailOpen: boolean;
24
+ readonly _tolvynFallbackKey: string | undefined;
25
+ readonly _tolvynProxyUrl: string;
26
+ constructor(options?: TolvynDeepSeekOptions);
27
+ }
28
+ //# sourceMappingURL=deepseek.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepseek.d.ts","sourceRoot":"","sources":["../../src/deepseek.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,UAAU,EAAE,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAMnD,MAAM,WAAW,qBACf,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,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,QAAS,SAAQ,UAAU;IACtC,SAAgB,eAAe,EAAE,OAAO,CAAC;IACzC,SAAgB,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IACvD,SAAgB,eAAe,EAAE,MAAM,CAAC;gBAE5B,OAAO,GAAE,qBAA0B;CAgDhD"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * TOLVYN DeepSeek wrapper — thin drop-in over the official openai package.
3
+ *
4
+ * DeepSeek exposes an OpenAI-compatible API, so this wraps the same `openai`
5
+ * base client as the OpenAI wrapper but points the proxy/fallback at DeepSeek
6
+ * and falls back with the DeepSeek key. Fail-open routes direct to
7
+ * https://api.deepseek.com/v1 (Bearer auth) when the TOLVYN proxy is unreachable.
8
+ */
9
+ import OpenAIBase from 'openai';
10
+ import { makeFailOpenFetch } from './failopen';
11
+ const DEEPSEEK_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/deepseek/';
12
+ const DEEPSEEK_DIRECT_URL = 'https://api.deepseek.com/v1';
13
+ export class DeepSeek extends OpenAIBase {
14
+ constructor(options = {}) {
15
+ const tolvynApiKey = options.tolvynApiKey ?? process.env['TOLVYN_API_KEY'];
16
+ if (!tolvynApiKey) {
17
+ throw new Error('tolvynApiKey required. Set TOLVYN_API_KEY env var or pass tolvynApiKey.');
18
+ }
19
+ const proxyUrl = options.proxyUrl ??
20
+ process.env['TOLVYN_PROXY_URL'] ??
21
+ DEEPSEEK_DEFAULT_PROXY_URL;
22
+ const defaultHeaders = {};
23
+ if (options.team)
24
+ defaultHeaders['X-Tolvyn-Team'] = options.team;
25
+ if (options.service)
26
+ defaultHeaders['X-Tolvyn-Service'] = options.service;
27
+ if (options.feature)
28
+ defaultHeaders['X-Tolvyn-Feature'] = options.feature;
29
+ if (options.agent)
30
+ defaultHeaders['X-Tolvyn-Agent'] = options.agent;
31
+ if (options.user)
32
+ defaultHeaders['X-Tolvyn-User'] = options.user;
33
+ if (options.endCustomer)
34
+ defaultHeaders['X-Tolvyn-End-Customer'] = options.endCustomer;
35
+ const fallbackKey = options.deepSeekApiKey ?? process.env['DEEPSEEK_API_KEY'];
36
+ const failOpen = options.failOpen ?? true;
37
+ const { tolvynApiKey: _tk, proxyUrl: _pu, team: _t, service: _sv, feature: _f, agent: _a, user: _u, endCustomer: _ec, failOpen: _fo, deepSeekApiKey: _dsk, ...rest } = options;
38
+ const superOptions = {
39
+ ...rest,
40
+ apiKey: tolvynApiKey,
41
+ baseURL: proxyUrl,
42
+ defaultHeaders,
43
+ };
44
+ if (failOpen && fallbackKey && !superOptions.fetch) {
45
+ superOptions.fetch = makeFailOpenFetch(fallbackKey, DEEPSEEK_DIRECT_URL, 'DeepSeek');
46
+ }
47
+ super(superOptions);
48
+ this._tolvynFailOpen = failOpen;
49
+ this._tolvynFallbackKey = fallbackKey;
50
+ this._tolvynProxyUrl = proxyUrl;
51
+ }
52
+ }
53
+ //# sourceMappingURL=deepseek.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepseek.js","sourceRoot":"","sources":["../../src/deepseek.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,UAA6B,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,0BAA0B,GAAG,4CAA4C,CAAC;AAChF,MAAM,mBAAmB,GAAG,6BAA6B,CAAC;AAgB1D,MAAM,OAAO,QAAS,SAAQ,UAAU;IAKtC,YAAY,UAAiC,EAAE;QAC7C,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,0BAA0B,CAAC;QAE7B,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,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAC9E,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,cAAc,EAAE,IAAI,EACnC,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,mBAAmB,EAAE,UAAU,CAAC,CAAC;QACvF,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"}
@@ -21,5 +21,14 @@ export declare function shouldNotFailOpen(error: unknown): boolean;
21
21
  * → https://api.anthropic.com/v1/messages
22
22
  */
23
23
  export declare function buildFallbackUrl(originalUrl: URL, fallbackBaseUrl: string): URL;
24
+ /**
25
+ * Rewrite `headers` in place to authenticate a direct provider call.
26
+ *
27
+ * Strips every inbound auth header (Authorization/x-api-key/x-goog-api-key —
28
+ * each may carry the TOLVYN key) and sets the single header the provider's
29
+ * direct API expects, with the provider's own key. Fixes ND-09 (Bearer sent to
30
+ * Anthropic → 401) and ND-11 (the TOLVYN key in x-api-key leaking on fallback).
31
+ */
32
+ export declare function applyFallbackAuth(headers: Headers, provider: string, fallbackKey: string): void;
24
33
  export declare function makeFailOpenFetch(fallbackKey: string, directUrl: string, provider: string): typeof globalThis.fetch;
25
34
  //# sourceMappingURL=failopen.d.ts.map
@@ -1 +1 @@
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"}
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;AAaD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,IAAI,CAUN;AAED,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,UAAU,CAAC,KAAK,CAgCzB"}
@@ -1,7 +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
+ const PROXY_PREFIX_RE = /^\/v1\/proxy\/(?:openai|anthropic|google|deepseek)\//;
5
5
  export function isProxyError(error) {
6
6
  if (!error || typeof error !== 'object')
7
7
  return false;
@@ -73,6 +73,36 @@ export function buildFallbackUrl(originalUrl, fallbackBaseUrl) {
73
73
  newUrl.pathname = finalPath;
74
74
  return newUrl;
75
75
  }
76
+ // The header each provider's DIRECT API reads the API key from. The TOLVYN
77
+ // proxy accepts Authorization/x-api-key/x-goog-api-key interchangeably, but the
78
+ // providers themselves do NOT: Anthropic authenticates only via x-api-key and
79
+ // Google only via x-goog-api-key — sending Bearer to them 401s.
80
+ const PROVIDER_AUTH_HEADER = {
81
+ openai: 'Authorization',
82
+ anthropic: 'x-api-key',
83
+ google: 'x-goog-api-key',
84
+ deepseek: 'Authorization', // OpenAI-compatible; Bearer auth (same as the default)
85
+ };
86
+ /**
87
+ * Rewrite `headers` in place to authenticate a direct provider call.
88
+ *
89
+ * Strips every inbound auth header (Authorization/x-api-key/x-goog-api-key —
90
+ * each may carry the TOLVYN key) and sets the single header the provider's
91
+ * direct API expects, with the provider's own key. Fixes ND-09 (Bearer sent to
92
+ * Anthropic → 401) and ND-11 (the TOLVYN key in x-api-key leaking on fallback).
93
+ */
94
+ export function applyFallbackAuth(headers, provider, fallbackKey) {
95
+ headers.delete('Authorization');
96
+ headers.delete('x-api-key');
97
+ headers.delete('x-goog-api-key');
98
+ const header = PROVIDER_AUTH_HEADER[provider.toLowerCase()] ?? 'Authorization';
99
+ if (header === 'Authorization') {
100
+ headers.set('Authorization', `Bearer ${fallbackKey}`);
101
+ }
102
+ else {
103
+ headers.set(header, fallbackKey);
104
+ }
105
+ }
76
106
  export function makeFailOpenFetch(fallbackKey, directUrl, provider) {
77
107
  return async function failOpenFetch(input, init) {
78
108
  try {
@@ -94,7 +124,7 @@ export function makeFailOpenFetch(fallbackKey, directUrl, provider) {
94
124
  const url = buildFallbackUrl(new URL(originalUrl), directUrl);
95
125
  const newInit = { ...(init ?? {}) };
96
126
  const headers = new Headers(init?.headers ?? {});
97
- headers.set('Authorization', `Bearer ${fallbackKey}`);
127
+ applyFallbackAuth(headers, provider, fallbackKey);
98
128
  newInit.headers = headers;
99
129
  return fetch(url.toString(), newInit);
100
130
  }
@@ -1 +1 @@
1
- {"version":3,"file":"failopen.js","sourceRoot":"","sources":["../../src/failopen.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,eAAe,GAAG,6CAA6C,CAAC;AAEtE,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,GAAG,GAAG,KAAgC,CAAC;IAE7C,4DAA4D;IAC5D,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAC9B,IAAI,KAAK,cAAc;QACvB,IAAI,KAAK,YAAY;QACrB,IAAI,KAAK,WAAW;QACpB,IAAI,KAAK,WAAW;QACpB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CACxB,EAAE,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,sBAAsB;IACtB,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;IAClD,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAEhC,qBAAqB;IACrB,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACzE,IACE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;QAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;QAC7B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;QAChC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EACxC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,sCAAsC;IACtC,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3B,IAAI,KAAK,IAAI,YAAY,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAE9C,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,uEAAuE;IACvE,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;IAClD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QAClF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAAgB,EAAE,eAAuB;IACxE,MAAM,EAAE,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc;IAEjE,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;IACpE,MAAM,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IAExC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC/C,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IAC9B,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IAC9B,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC;IACtB,MAAM,CAAC,QAAQ,GAAG,SAAS,CAAC;IAC5B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,WAAmB,EACnB,SAAiB,EACjB,QAAgB;IAEhB,OAAO,KAAK,UAAU,aAAa,CACjC,KAAwB,EACxB,IAAkB;QAElB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YACrC,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACvB,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YACpE,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC;gBAAE,MAAM,GAAG,CAAC;YAC5D,OAAO,CAAC,KAAK,CACX,gDAAgD,QAAQ,cAAc,CACvE,CAAC;YACF,MAAM,WAAW,GACf,OAAO,KAAK,KAAK,QAAQ;gBACvB,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,KAAK,YAAY,GAAG;oBACtB,CAAC,CAAC,KAAK,CAAC,IAAI;oBACZ,CAAC,CAAE,KAAiB,CAAC,GAAG,CAAC;YAC7B,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,GAAG,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC,CAAC;YAE9D,MAAM,OAAO,GAAgB,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;YACjD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAE,IAAI,EAAE,OAAuB,IAAI,EAAE,CAAC,CAAC;YAClE,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,WAAW,EAAE,CAAC,CAAC;YACtD,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;YAE1B,OAAO,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,OAAO,CAAC,CAAC;QACxC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"failopen.js","sourceRoot":"","sources":["../../src/failopen.ts"],"names":[],"mappings":"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"}
@@ -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;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"}
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;AAW/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;CAwDlC"}
@@ -5,7 +5,11 @@ import { GoogleGenerativeAI, } from '@google/generative-ai';
5
5
  import { isProxyError } from './failopen';
6
6
  // The proxy base URL is prepended to Google API paths (/v1beta/models/...).
7
7
  // The TOLVYN proxy strips /v1/proxy/google and forwards the remainder to Google.
8
- const GOOGLE_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/google/';
8
+ // ND-10: NO trailing slash. @google/generative-ai builds `${baseUrl}/${apiVersion}/...`
9
+ // WITHOUT collapsing a double slash (unlike the OpenAI/Anthropic SDKs), so a
10
+ // trailing slash here produces `.../proxy/google//v1beta/...`. This intentionally
11
+ // reverses ND-07's trailing-slash "consistency" for the Google provider only.
12
+ const GOOGLE_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/google';
9
13
  // ND-05: renamed from TolvynGoogle → Google so stack traces and constructor.name
10
14
  // match the public API. Old name remains re-exportable via index.ts alias.
11
15
  export class Google extends GoogleGenerativeAI {
@@ -42,34 +46,53 @@ export class Google extends GoogleGenerativeAI {
42
46
  }
43
47
  getGenerativeModel(modelParams, requestOptions) {
44
48
  const mergedOptions = {
45
- baseUrl: this._tolvynProxyUrl,
49
+ // ND-10: strip any trailing slash so an env/option override can't
50
+ // reintroduce the double-slash the Google SDK won't collapse.
51
+ baseUrl: this._tolvynProxyUrl.replace(/\/+$/, ''),
46
52
  customHeaders: this._tolvynHeaders,
47
53
  ...requestOptions,
48
54
  };
49
55
  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.
56
+ // ND-01 + BE-91: wrap every GenerativeModel network method so a
57
+ // proxy-unreachable error retries against the real Google API using the
58
+ // fallback key. Google's SDK exposes no fetch hook (unlike the
59
+ // OpenAI/Anthropic wrappers), so we rebuild a direct client — with the
60
+ // ORIGINAL requestOptions, i.e. WITHOUT the proxy baseUrl, so it hits the
61
+ // real generativelanguage.googleapis.com — and re-invoke the same method.
62
+ // (ND-01 covered only generateContent; BE-91 extends to stream/count/embed.
63
+ // startChat returns a ChatSession with separate plumbing — not yet covered.)
52
64
  if (!this._tolvynFailOpen || !this._tolvynFallbackKey) {
53
65
  return model;
54
66
  }
55
67
  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
- };
68
+ const failOpenMethods = [
69
+ 'generateContent',
70
+ 'generateContentStream',
71
+ 'countTokens',
72
+ 'embedContent',
73
+ 'batchEmbedContents',
74
+ ];
75
+ for (const method of failOpenMethods) {
76
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
+ const original = model[method];
78
+ if (typeof original !== 'function')
79
+ continue;
80
+ const boundOriginal = original.bind(model);
81
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
82
+ model[method] = async (...args) => {
83
+ try {
84
+ return await boundOriginal(...args);
85
+ }
86
+ catch (e) {
87
+ if (!isProxyError(e))
88
+ throw e;
89
+ console.error('TOLVYN proxy unreachable — routing direct to Google (fail-open)');
90
+ const fallbackModel = new GoogleGenerativeAI(fallbackKey).getGenerativeModel(modelParams, requestOptions);
91
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
92
+ return fallbackModel[method](...args);
93
+ }
94
+ };
95
+ }
73
96
  return model;
74
97
  }
75
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/google.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/google.ts"],"names":[],"mappings":"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"}
@@ -4,4 +4,6 @@ export { Anthropic } from './anthropic';
4
4
  export type { TolvynAnthropicOptions } from './anthropic';
5
5
  export { Google } from './google';
6
6
  export type { TolvynGoogleOptions } from './google';
7
+ export { DeepSeek } from './deepseek';
8
+ export type { TolvynDeepSeekOptions } from './deepseek';
7
9
  //# sourceMappingURL=index.d.ts.map
@@ -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,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC"}
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;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC"}
package/dist/esm/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { OpenAI } from './client';
2
2
  export { Anthropic } from './anthropic';
3
3
  export { Google } from './google';
4
+ export { DeepSeek } from './deepseek';
4
5
  //# sourceMappingURL=index.js.map
@@ -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,MAAM,EAAE,MAAM,UAAU,CAAC"}
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;AAElC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tolvyn",
3
- "version": "1.0.7",
4
- "description": "Drop-in replacement for the OpenAI/Anthropic SDK — routes through TOLVYN for cost attribution, budget enforcement, and audit logging.",
3
+ "version": "1.0.10",
4
+ "description": "Drop-in replacement for the OpenAI/Anthropic/Google/DeepSeek SDKs — 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",
7
7
  "types": "./dist/cjs/index.d.ts",
@@ -0,0 +1,82 @@
1
+ /**
2
+ * TOLVYN DeepSeek wrapper — thin drop-in over the official openai package.
3
+ *
4
+ * DeepSeek exposes an OpenAI-compatible API, so this wraps the same `openai`
5
+ * base client as the OpenAI wrapper but points the proxy/fallback at DeepSeek
6
+ * and falls back with the DeepSeek key. Fail-open routes direct to
7
+ * https://api.deepseek.com/v1 (Bearer auth) when the TOLVYN proxy is unreachable.
8
+ */
9
+ import OpenAIBase, { ClientOptions } from 'openai';
10
+ import { makeFailOpenFetch } from './failopen';
11
+
12
+ const DEEPSEEK_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/deepseek/';
13
+ const DEEPSEEK_DIRECT_URL = 'https://api.deepseek.com/v1';
14
+
15
+ export interface TolvynDeepSeekOptions
16
+ extends Omit<ClientOptions, 'apiKey' | 'baseURL'> {
17
+ tolvynApiKey?: string;
18
+ proxyUrl?: string;
19
+ team?: string;
20
+ service?: string;
21
+ feature?: string;
22
+ agent?: string;
23
+ user?: string;
24
+ endCustomer?: string;
25
+ failOpen?: boolean;
26
+ deepSeekApiKey?: string;
27
+ }
28
+
29
+ export class DeepSeek extends OpenAIBase {
30
+ public readonly _tolvynFailOpen: boolean;
31
+ public readonly _tolvynFallbackKey: string | undefined;
32
+ public readonly _tolvynProxyUrl: string;
33
+
34
+ constructor(options: TolvynDeepSeekOptions = {}) {
35
+ const tolvynApiKey = options.tolvynApiKey ?? process.env['TOLVYN_API_KEY'];
36
+ if (!tolvynApiKey) {
37
+ throw new Error(
38
+ 'tolvynApiKey required. Set TOLVYN_API_KEY env var or pass tolvynApiKey.'
39
+ );
40
+ }
41
+
42
+ const proxyUrl =
43
+ options.proxyUrl ??
44
+ process.env['TOLVYN_PROXY_URL'] ??
45
+ DEEPSEEK_DEFAULT_PROXY_URL;
46
+
47
+ const defaultHeaders: Record<string, string> = {};
48
+ if (options.team) defaultHeaders['X-Tolvyn-Team'] = options.team;
49
+ if (options.service) defaultHeaders['X-Tolvyn-Service'] = options.service;
50
+ if (options.feature) defaultHeaders['X-Tolvyn-Feature'] = options.feature;
51
+ if (options.agent) defaultHeaders['X-Tolvyn-Agent'] = options.agent;
52
+ if (options.user) defaultHeaders['X-Tolvyn-User'] = options.user;
53
+ if (options.endCustomer) defaultHeaders['X-Tolvyn-End-Customer'] = options.endCustomer;
54
+
55
+ const fallbackKey = options.deepSeekApiKey ?? process.env['DEEPSEEK_API_KEY'];
56
+ const failOpen = options.failOpen ?? true;
57
+
58
+ const {
59
+ tolvynApiKey: _tk, proxyUrl: _pu, team: _t, service: _sv,
60
+ feature: _f, agent: _a, user: _u, endCustomer: _ec,
61
+ failOpen: _fo, deepSeekApiKey: _dsk,
62
+ ...rest
63
+ } = options;
64
+
65
+ const superOptions: ClientOptions = {
66
+ ...rest,
67
+ apiKey: tolvynApiKey,
68
+ baseURL: proxyUrl,
69
+ defaultHeaders,
70
+ };
71
+
72
+ if (failOpen && fallbackKey && !superOptions.fetch) {
73
+ superOptions.fetch = makeFailOpenFetch(fallbackKey, DEEPSEEK_DIRECT_URL, 'DeepSeek');
74
+ }
75
+
76
+ super(superOptions);
77
+
78
+ this._tolvynFailOpen = failOpen;
79
+ this._tolvynFallbackKey = fallbackKey;
80
+ this._tolvynProxyUrl = proxyUrl;
81
+ }
82
+ }
package/src/failopen.ts CHANGED
@@ -2,7 +2,7 @@
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)\//;
5
+ const PROXY_PREFIX_RE = /^\/v1\/proxy\/(?:openai|anthropic|google|deepseek)\//;
6
6
 
7
7
  export function isProxyError(error: unknown): boolean {
8
8
  if (!error || typeof error !== 'object') return false;
@@ -85,6 +85,41 @@ export function buildFallbackUrl(originalUrl: URL, fallbackBaseUrl: string): URL
85
85
  return newUrl;
86
86
  }
87
87
 
88
+ // The header each provider's DIRECT API reads the API key from. The TOLVYN
89
+ // proxy accepts Authorization/x-api-key/x-goog-api-key interchangeably, but the
90
+ // providers themselves do NOT: Anthropic authenticates only via x-api-key and
91
+ // Google only via x-goog-api-key — sending Bearer to them 401s.
92
+ const PROVIDER_AUTH_HEADER: Record<string, string> = {
93
+ openai: 'Authorization',
94
+ anthropic: 'x-api-key',
95
+ google: 'x-goog-api-key',
96
+ deepseek: 'Authorization', // OpenAI-compatible; Bearer auth (same as the default)
97
+ };
98
+
99
+ /**
100
+ * Rewrite `headers` in place to authenticate a direct provider call.
101
+ *
102
+ * Strips every inbound auth header (Authorization/x-api-key/x-goog-api-key —
103
+ * each may carry the TOLVYN key) and sets the single header the provider's
104
+ * direct API expects, with the provider's own key. Fixes ND-09 (Bearer sent to
105
+ * Anthropic → 401) and ND-11 (the TOLVYN key in x-api-key leaking on fallback).
106
+ */
107
+ export function applyFallbackAuth(
108
+ headers: Headers,
109
+ provider: string,
110
+ fallbackKey: string,
111
+ ): void {
112
+ headers.delete('Authorization');
113
+ headers.delete('x-api-key');
114
+ headers.delete('x-goog-api-key');
115
+ const header = PROVIDER_AUTH_HEADER[provider.toLowerCase()] ?? 'Authorization';
116
+ if (header === 'Authorization') {
117
+ headers.set('Authorization', `Bearer ${fallbackKey}`);
118
+ } else {
119
+ headers.set(header, fallbackKey);
120
+ }
121
+ }
122
+
88
123
  export function makeFailOpenFetch(
89
124
  fallbackKey: string,
90
125
  directUrl: string,
@@ -115,7 +150,7 @@ export function makeFailOpenFetch(
115
150
 
116
151
  const newInit: RequestInit = { ...(init ?? {}) };
117
152
  const headers = new Headers((init?.headers as HeadersInit) ?? {});
118
- headers.set('Authorization', `Bearer ${fallbackKey}`);
153
+ applyFallbackAuth(headers, provider, fallbackKey);
119
154
  newInit.headers = headers;
120
155
 
121
156
  return fetch(url.toString(), newInit);
package/src/google.ts CHANGED
@@ -10,7 +10,11 @@ import { isProxyError } from './failopen';
10
10
 
11
11
  // The proxy base URL is prepended to Google API paths (/v1beta/models/...).
12
12
  // The TOLVYN proxy strips /v1/proxy/google and forwards the remainder to Google.
13
- const GOOGLE_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/google/';
13
+ // ND-10: NO trailing slash. @google/generative-ai builds `${baseUrl}/${apiVersion}/...`
14
+ // WITHOUT collapsing a double slash (unlike the OpenAI/Anthropic SDKs), so a
15
+ // trailing slash here produces `.../proxy/google//v1beta/...`. This intentionally
16
+ // reverses ND-07's trailing-slash "consistency" for the Google provider only.
17
+ const GOOGLE_DEFAULT_PROXY_URL = 'https://proxy.tolvyn.io/v1/proxy/google';
14
18
 
15
19
  export interface TolvynGoogleOptions {
16
20
  tolvynApiKey?: string;
@@ -70,35 +74,57 @@ export class Google extends GoogleGenerativeAI {
70
74
  requestOptions?: RequestOptions
71
75
  ) {
72
76
  const mergedOptions: RequestOptions = {
73
- baseUrl: this._tolvynProxyUrl,
77
+ // ND-10: strip any trailing slash so an env/option override can't
78
+ // reintroduce the double-slash the Google SDK won't collapse.
79
+ baseUrl: this._tolvynProxyUrl.replace(/\/+$/, ''),
74
80
  customHeaders: this._tolvynHeaders,
75
81
  ...requestOptions,
76
82
  };
77
83
  const model = super.getGenerativeModel(modelParams, mergedOptions);
78
84
 
79
- // ND-01: wrap generateContent so a proxy-unreachable error retries against
80
- // the real Google API using the fallback key.
85
+ // ND-01 + BE-91: wrap every GenerativeModel network method so a
86
+ // proxy-unreachable error retries against the real Google API using the
87
+ // fallback key. Google's SDK exposes no fetch hook (unlike the
88
+ // OpenAI/Anthropic wrappers), so we rebuild a direct client — with the
89
+ // ORIGINAL requestOptions, i.e. WITHOUT the proxy baseUrl, so it hits the
90
+ // real generativelanguage.googleapis.com — and re-invoke the same method.
91
+ // (ND-01 covered only generateContent; BE-91 extends to stream/count/embed.
92
+ // startChat returns a ChatSession with separate plumbing — not yet covered.)
81
93
  if (!this._tolvynFailOpen || !this._tolvynFallbackKey) {
82
94
  return model;
83
95
  }
84
96
  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
- };
97
+ const failOpenMethods = [
98
+ 'generateContent',
99
+ 'generateContentStream',
100
+ 'countTokens',
101
+ 'embedContent',
102
+ 'batchEmbedContents',
103
+ ] as const;
104
+
105
+ for (const method of failOpenMethods) {
106
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
+ const original = (model as any)[method];
108
+ if (typeof original !== 'function') continue;
109
+ const boundOriginal = original.bind(model);
110
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
+ (model as any)[method] = async (...args: unknown[]) => {
112
+ try {
113
+ return await boundOriginal(...args);
114
+ } catch (e) {
115
+ if (!isProxyError(e)) throw e;
116
+ console.error(
117
+ 'TOLVYN proxy unreachable — routing direct to Google (fail-open)',
118
+ );
119
+ const fallbackModel = new GoogleGenerativeAI(fallbackKey).getGenerativeModel(
120
+ modelParams,
121
+ requestOptions,
122
+ );
123
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
124
+ return (fallbackModel as any)[method](...args);
125
+ }
126
+ };
127
+ }
102
128
  return model;
103
129
  }
104
130
  }
package/src/index.ts CHANGED
@@ -4,3 +4,5 @@ export { Anthropic } from './anthropic';
4
4
  export type { TolvynAnthropicOptions } from './anthropic';
5
5
  export { Google } from './google';
6
6
  export type { TolvynGoogleOptions } from './google';
7
+ export { DeepSeek } from './deepseek';
8
+ export type { TolvynDeepSeekOptions } from './deepseek';