@tokenite/sdk 2.4.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -193,6 +193,36 @@ switch (result.reason) {
193
193
 
194
194
  `expectedState` is optional. When you provide it, a mismatch returns `{ ok: false, reason: 'invalid_state' }` even if a `code` is present — treating a stolen code paired with a forged state as a CSRF attempt.
195
195
 
196
+ ### Vendor-agnostic calls (any provider behind one SDK)
197
+
198
+ The `/agnostic/{flavor}` route lets you keep using your favourite vendor SDK while letting the user's keys decide which provider actually runs the request. You write code once in (say) OpenAI shape; the proxy translates the request and the response so the SDK still sees its own format. The model name in the body is looked up across every provider's catalog, so a Claude model is fine in an OpenAI-shaped call.
199
+
200
+ ```typescript
201
+ import OpenAI from 'openai';
202
+
203
+ const openai = new OpenAI({
204
+ apiKey: accessToken,
205
+ baseURL: tk.agnosticUrl('openai') + '/v1', // ← agnostic, OpenAI wire shape
206
+ });
207
+
208
+ // Same SDK, any model the user has access to — Claude here, served by
209
+ // the user's Anthropic key. Tokenite translates the envelope both ways.
210
+ const r = await openai.chat.completions.create({
211
+ model: 'claude-sonnet-4-6',
212
+ messages: [{ role: 'user', content: 'hi' }],
213
+ max_tokens: 256,
214
+ });
215
+ ```
216
+
217
+ The same works mirrored — Anthropic SDK naming an OpenAI model, etc. Pair it with `tk.getAccessContext()` to render a picker scoped to what the user can actually run (`models[].callableNow`).
218
+
219
+ **Caveats:**
220
+ - Non-streaming only. For `stream: true` keep using `tk.proxyUrl(provider)` (same-provider streaming passthrough).
221
+ - Pricing is the *resolved* provider's pricing (a Claude call via `/agnostic/openai` bills at Anthropic's rate). The proxy records `provider: anthropic` in your usage logs regardless of the URL.
222
+ - The app's `modelStrategy` still applies — a `models`-pinned app limits which slugs are valid; `tier`-pinned limits them by tier.
223
+
224
+ For provider-bound calls keep using `tk.proxyUrl()`. `agnosticUrl()` only opts into cross-provider routing — it's not a default.
225
+
196
226
  ### Streaming responses
197
227
 
198
228
  `tk.call()` is for non-streaming requests. Streaming responses bypass the unified envelope and are forwarded as-is, so use any vendor SDK with `baseURL: tk.proxyUrl(...)`:
@@ -241,6 +271,10 @@ Make an authenticated, non-streaming request through the proxy. Returns a unifie
241
271
 
242
272
  Get the proxy URL for a specific provider. Use as `baseURL` in a vendor SDK for streaming requests, which bypass the unified envelope.
243
273
 
274
+ ### `.agnosticUrl(flavor: InboundFlavor) => string`
275
+
276
+ Base URL for the *agnostic* proxy route — same SDK shape (`flavor`), but the model named in the body is looked up globally across every provider. The user's keys decide which one actually runs the request; the proxy translates the request and response envelopes so your vendor SDK still sees its own shape.
277
+
244
278
  ### `.baseUrl`: `string`
245
279
 
246
280
  The Tokenite dashboard base URL
@@ -394,6 +428,14 @@ export type TokenResponse = {
394
428
 
395
429
  export type Provider = 'anthropic' | 'openai' | 'google' | 'grok' | 'bedrock';
396
430
 
431
+ /**
432
+ * The wire flavor a vendor SDK speaks — pick the one matching the SDK
433
+ * you're using. Used by `tk.agnosticUrl(flavor)` to declare "I want the
434
+ * shape of flavor X, but I don't care which vendor actually runs the
435
+ * model I name."
436
+ */
437
+ export type InboundFlavor = 'anthropic' | 'openai' | 'gemini';
438
+
397
439
  export type ProxyCallOptions = {
398
440
  /** The user's Tokenite access token (returned by `tk.exchangeCode()`) */
399
441
  readonly accessToken: string;
@@ -549,6 +591,8 @@ export type ModelInfo = {
549
591
  readonly displayName: string;
550
592
  /** The lab that built the model */
551
593
  readonly creator: 'anthropic' | 'openai' | 'google' | 'grok';
594
+ /** Absolute URL to the creator's logo — use it as the model's icon in a picker */
595
+ readonly creatorLogoUrl: string;
552
596
  /** Capability tiers this model satisfies (cheap / fast / smart / reasoning) */
553
597
  readonly tiers: readonly string[];
554
598
  /** Feature capabilities, e.g. "vision", "tools", "thinking" */
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { TokeniteConfig, AuthorizeOptions, PopupOptions, PopupResult, TokenResponse, Provider, ProxyCallOptions, ProxyResponse, AccessContext, TopUpOptions, TopUpResult, CallWithRecoveryOptions } from './types.js';
1
+ import type { TokeniteConfig, AuthorizeOptions, PopupOptions, PopupResult, TokenResponse, Provider, ProxyCallOptions, ProxyResponse, AccessContext, InboundFlavor, TopUpOptions, TopUpResult, CallWithRecoveryOptions } from './types.js';
2
2
  /**
3
3
  * Tokenite client.
4
4
  *
@@ -134,6 +134,23 @@ export declare const Tokenite: (config: TokeniteConfig) => {
134
134
  * bypass the unified envelope.
135
135
  */
136
136
  proxyUrl: (provider: Provider) => string;
137
+ /**
138
+ * Base URL for the *agnostic* proxy route — same SDK shape (`flavor`),
139
+ * but the model named in the body is looked up globally across every
140
+ * provider. The user's keys decide which one actually runs the request;
141
+ * the proxy translates the request and response envelopes so your
142
+ * vendor SDK still sees its own shape.
143
+ *
144
+ * Useful when you want to write code once against, say, the OpenAI SDK,
145
+ * but accept any model the user has access to. Point the SDK at:
146
+ *
147
+ * - `agnosticUrl('anthropic')` for `@anthropic-ai/sdk`
148
+ * - `agnosticUrl('openai') + '/v1'` for `openai`
149
+ * - `agnosticUrl('gemini')` for `@google/genai`
150
+ *
151
+ * Non-streaming only. For provider-bound calls keep using `proxyUrl()`.
152
+ */
153
+ agnosticUrl: (flavor: InboundFlavor) => string;
137
154
  /**
138
155
  * Open a Tokenite-hosted "raise budget" popup for this app.
139
156
  *
package/dist/client.js CHANGED
@@ -235,6 +235,10 @@ export const Tokenite = (config) => {
235
235
  ...p,
236
236
  logoUrl: absoluteUrl(p.logoUrl, baseUrl),
237
237
  })),
238
+ models: data.models.map((m) => ({
239
+ ...m,
240
+ creatorLogoUrl: absoluteUrl(m.creatorLogoUrl, baseUrl),
241
+ })),
238
242
  };
239
243
  },
240
244
  /**
@@ -243,6 +247,23 @@ export const Tokenite = (config) => {
243
247
  * bypass the unified envelope.
244
248
  */
245
249
  proxyUrl: (provider) => `${proxyBase}/${provider}`,
250
+ /**
251
+ * Base URL for the *agnostic* proxy route — same SDK shape (`flavor`),
252
+ * but the model named in the body is looked up globally across every
253
+ * provider. The user's keys decide which one actually runs the request;
254
+ * the proxy translates the request and response envelopes so your
255
+ * vendor SDK still sees its own shape.
256
+ *
257
+ * Useful when you want to write code once against, say, the OpenAI SDK,
258
+ * but accept any model the user has access to. Point the SDK at:
259
+ *
260
+ * - `agnosticUrl('anthropic')` for `@anthropic-ai/sdk`
261
+ * - `agnosticUrl('openai') + '/v1'` for `openai`
262
+ * - `agnosticUrl('gemini')` for `@google/genai`
263
+ *
264
+ * Non-streaming only. For provider-bound calls keep using `proxyUrl()`.
265
+ */
266
+ agnosticUrl: (flavor) => `${proxyBase}/agnostic/${flavor}`,
246
267
  /**
247
268
  * Open a Tokenite-hosted "raise budget" popup for this app.
248
269
  *
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { Tokenite } from './client.js';
2
- export type { TokeniteConfig, AuthorizeOptions, OAuthPrompt, PopupOptions, PopupResult, TokenResponse, Provider, ProxyCallOptions, ProxyUsage, ProxySuccess, ProxyError, ProxyResponse, ErrorSource, ProviderInfo, ModelInfo, TierInfo, AppInfo, UserInfo, AccessContext, TopUpOptions, TopUpResult, CallWithRecoveryOptions, } from './types.js';
2
+ export type { TokeniteConfig, AuthorizeOptions, OAuthPrompt, PopupOptions, PopupResult, TokenResponse, Provider, ProxyCallOptions, ProxyUsage, ProxySuccess, ProxyError, ProxyResponse, ErrorSource, ProviderInfo, ModelInfo, TierInfo, InboundFlavor, AppInfo, UserInfo, AccessContext, TopUpOptions, TopUpResult, CallWithRecoveryOptions, } from './types.js';
3
3
  export { isProxyError, isProxySuccess } from './types.js';
4
4
  export { parseCallback } from './parse-callback.js';
5
5
  export type { CallbackResult, CallbackSuccess, CallbackError, CallbackReason, ParseCallbackOptions, } from './parse-callback.js';
package/dist/types.d.ts CHANGED
@@ -133,6 +133,13 @@ export type TokenResponse = {
133
133
  readonly token_type: string;
134
134
  };
135
135
  export type Provider = 'anthropic' | 'openai' | 'google' | 'grok' | 'bedrock';
136
+ /**
137
+ * The wire flavor a vendor SDK speaks — pick the one matching the SDK
138
+ * you're using. Used by `tk.agnosticUrl(flavor)` to declare "I want the
139
+ * shape of flavor X, but I don't care which vendor actually runs the
140
+ * model I name."
141
+ */
142
+ export type InboundFlavor = 'anthropic' | 'openai' | 'gemini';
136
143
  export type ProxyCallOptions = {
137
144
  /** The user's Tokenite access token (returned by `tk.exchangeCode()`) */
138
145
  readonly accessToken: string;
@@ -268,6 +275,8 @@ export type ModelInfo = {
268
275
  readonly displayName: string;
269
276
  /** The lab that built the model */
270
277
  readonly creator: 'anthropic' | 'openai' | 'google' | 'grok';
278
+ /** Absolute URL to the creator's logo — use it as the model's icon in a picker */
279
+ readonly creatorLogoUrl: string;
271
280
  /** Capability tiers this model satisfies (cheap / fast / smart / reasoning) */
272
281
  readonly tiers: readonly string[];
273
282
  /** Feature capabilities, e.g. "vision", "tools", "thinking" */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenite/sdk",
3
- "version": "2.4.0",
3
+ "version": "2.6.0",
4
4
  "description": "SDK for integrating \"Login with Tokenite\" into your app. Your users bring their own AI tokens — you pay nothing.",
5
5
  "type": "module",
6
6
  "exports": {