aisubs 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.5 - 2026-09-14
4
+
5
+ - Move ChatGPT Responses instructions into the developer input prefix so
6
+ stable subscription prompts participate in prompt caching.
7
+
8
+ ## 0.3.4 - 2026-09-13
9
+
10
+ - Refresh account model catalogs on demand and update ChatGPT compatibility so
11
+ newly available subscription models appear without stale cached listings.
12
+ - Preserve prompt-cache routing and cache read/write usage when translating
13
+ between OpenAI, Anthropic, and Responses-compatible protocols.
14
+
3
15
  ## 0.3.3 - 2026-09-02
4
16
 
5
17
  - Show remaining usage percentages in account meter values and progress bars,
package/dist/auth.d.ts CHANGED
@@ -19,7 +19,7 @@ export interface SubscriptionAccount {
19
19
  fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
20
20
  proxy(path: string, init?: RequestInit): Promise<Response>;
21
21
  getUsage(signal?: AbortSignal): Promise<ProviderUsage | null>;
22
- getModels(signal?: AbortSignal): Promise<ProviderModels | null>;
22
+ getModels(signal?: AbortSignal, force?: boolean): Promise<ProviderModels | null>;
23
23
  credentialSummary(): Promise<CredentialSummary>;
24
24
  details(signal?: AbortSignal): Promise<SubscriptionAccountDetails>;
25
25
  }
@@ -69,7 +69,7 @@ export declare class SubscriptionAuth {
69
69
  /** Build an authorized direct-provider request for transports such as WebSocket. */
70
70
  authorizeProxyRequest(provider: ProviderId, account: string, path: string, init?: RequestInit): Promise<Request>;
71
71
  getUsage(provider: ProviderId, account?: string, callerSignal?: AbortSignal): Promise<ProviderUsage | null>;
72
- getModels(provider: ProviderId, account?: string, callerSignal?: AbortSignal): Promise<ProviderModels | null>;
72
+ getModels(provider: ProviderId, account?: string, callerSignal?: AbortSignal, force?: boolean): Promise<ProviderModels | null>;
73
73
  account(provider: ProviderId, account: string): SubscriptionAccount;
74
74
  }
75
75
  export declare function createSubscriptionAuth(options: {
package/dist/auth.js CHANGED
@@ -459,12 +459,17 @@ export class SubscriptionAuth {
459
459
  return data ? { provider, accountKey, asOf: Date.now(), ...data } : null;
460
460
  });
461
461
  }
462
- async getModels(provider, account = DEFAULT_ACCOUNT, callerSignal) {
462
+ async getModels(provider, account = DEFAULT_ACCOUNT, callerSignal, force = false) {
463
463
  const adapter = this.adapter(provider);
464
464
  if (!adapter.getModels)
465
465
  return null;
466
466
  const accountKey = normalizeAccountKey(account);
467
467
  const scope = credentialKey(provider, accountKey);
468
+ if (force) {
469
+ this.modelsCache.delete(scope);
470
+ this.modelsInflight.delete(scope);
471
+ this.metadataGenerations.set(scope, (this.metadataGenerations.get(scope) ?? 0) + 1);
472
+ }
468
473
  return this.cachedMetadata(this.modelsCache, this.modelsInflight, scope, this.modelsCacheTtlMs, callerSignal, async () => {
469
474
  const credential = await this.credential(provider, accountKey);
470
475
  const timeout = AbortSignal.timeout(this.refreshTimeoutMs);
@@ -490,7 +495,7 @@ export class SubscriptionAuth {
490
495
  fetch: (input, init) => this.fetch(provider, input, init, accountKey),
491
496
  proxy: (path, init) => this.proxy(provider, accountKey, path, init),
492
497
  getUsage: (signal) => this.getUsage(provider, accountKey, signal),
493
- getModels: (signal) => this.getModels(provider, accountKey, signal),
498
+ getModels: (signal, force) => this.getModels(provider, accountKey, signal, force),
494
499
  credentialSummary: () => this.credentialSummary(provider, accountKey),
495
500
  details: (signal) => this.details(provider, accountKey, signal),
496
501
  };
@@ -150,6 +150,7 @@ function parseChat(body) {
150
150
  responseFormat: raw.response_format,
151
151
  metadata: raw.metadata,
152
152
  user: stringValue(raw.user),
153
+ promptCacheKey: stringValue(raw.prompt_cache_key),
153
154
  };
154
155
  }
155
156
  function responseTools(value) {
@@ -243,6 +244,7 @@ function parseResponses(body) {
243
244
  responseFormat,
244
245
  metadata: raw.metadata,
245
246
  user: stringValue(raw.user),
247
+ promptCacheKey: stringValue(raw.prompt_cache_key),
246
248
  };
247
249
  }
248
250
  function anthropicParts(value) {
@@ -472,6 +474,7 @@ function toChat(request) {
472
474
  ...(request.responseFormat != null ? { response_format: request.responseFormat } : {}),
473
475
  ...(request.metadata != null ? { metadata: request.metadata } : {}),
474
476
  ...(request.user ? { user: request.user } : {}),
477
+ ...(request.promptCacheKey ? { prompt_cache_key: request.promptCacheKey } : {}),
475
478
  };
476
479
  }
477
480
  function responseContent(part, role) {
@@ -557,6 +560,7 @@ function toResponses(request) {
557
560
  ...(format != null ? { text: { format } } : {}),
558
561
  ...(request.metadata != null ? { metadata: request.metadata } : {}),
559
562
  ...(request.user ? { user: request.user } : {}),
563
+ ...(request.promptCacheKey ? { prompt_cache_key: request.promptCacheKey } : {}),
560
564
  };
561
565
  }
562
566
  function anthropicContent(message) {
@@ -703,7 +707,7 @@ function toGoogle(request) {
703
707
  },
704
708
  };
705
709
  }
706
- function usage(input, output, cached, reasoning) {
710
+ function usage(input, output, cached, reasoning, cacheWrite) {
707
711
  if (input == null && output == null)
708
712
  return undefined;
709
713
  return {
@@ -711,6 +715,7 @@ function usage(input, output, cached, reasoning) {
711
715
  output: output ?? 0,
712
716
  total: (input ?? 0) + (output ?? 0),
713
717
  ...(cached != null ? { cached } : {}),
718
+ ...(cacheWrite != null ? { cacheWrite } : {}),
714
719
  ...(reasoning != null ? { reasoning } : {}),
715
720
  };
716
721
  }
@@ -735,7 +740,7 @@ function parseChatResult(raw, model) {
735
740
  finishReason: finish === "length" || finish === "tool_calls" || finish === "content_filter"
736
741
  ? finish
737
742
  : "stop",
738
- usage: usage(numberValue(details?.prompt_tokens), numberValue(details?.completion_tokens), numberValue(promptDetails?.cached_tokens), numberValue(completionDetails?.reasoning_tokens)),
743
+ usage: usage(numberValue(details?.prompt_tokens), numberValue(details?.completion_tokens), numberValue(promptDetails?.cached_tokens), numberValue(completionDetails?.reasoning_tokens), numberValue(promptDetails?.cache_write_tokens)),
739
744
  };
740
745
  }
741
746
  function parseResponsesResult(raw, model) {
@@ -785,7 +790,7 @@ function parseResponsesResult(raw, model) {
785
790
  : raw.status === "failed"
786
791
  ? "error"
787
792
  : "stop",
788
- usage: usage(numberValue(details?.input_tokens), numberValue(details?.output_tokens), numberValue(inputDetails?.cached_tokens), numberValue(outputDetails?.reasoning_tokens)),
793
+ usage: usage(numberValue(details?.input_tokens), numberValue(details?.output_tokens), numberValue(inputDetails?.cached_tokens), numberValue(outputDetails?.reasoning_tokens), numberValue(inputDetails?.cache_write_tokens)),
789
794
  };
790
795
  }
791
796
  function parseAnthropicResult(raw, model) {
@@ -805,7 +810,11 @@ function parseAnthropicResult(raw, model) {
805
810
  : stop === "refusal"
806
811
  ? "content_filter"
807
812
  : "stop",
808
- usage: usage(numberValue(rawUsage?.input_tokens), numberValue(rawUsage?.output_tokens), numberValue(rawUsage?.cache_read_input_tokens)),
813
+ usage: usage(rawUsage?.input_tokens != null
814
+ ? (numberValue(rawUsage.input_tokens) ?? 0) +
815
+ (numberValue(rawUsage.cache_read_input_tokens) ?? 0) +
816
+ (numberValue(rawUsage.cache_creation_input_tokens) ?? 0)
817
+ : undefined, numberValue(rawUsage?.output_tokens), numberValue(rawUsage?.cache_read_input_tokens), undefined, numberValue(rawUsage?.cache_creation_input_tokens)),
809
818
  };
810
819
  }
811
820
  function parseGoogleResult(raw, model) {
@@ -876,8 +885,15 @@ function resultToChat(result) {
876
885
  prompt_tokens: result.usage.input,
877
886
  completion_tokens: result.usage.output,
878
887
  total_tokens: result.usage.total,
879
- ...(result.usage.cached != null
880
- ? { prompt_tokens_details: { cached_tokens: result.usage.cached } }
888
+ ...(result.usage.cached != null || result.usage.cacheWrite != null
889
+ ? {
890
+ prompt_tokens_details: {
891
+ ...(result.usage.cached != null ? { cached_tokens: result.usage.cached } : {}),
892
+ ...(result.usage.cacheWrite != null
893
+ ? { cache_write_tokens: result.usage.cacheWrite }
894
+ : {}),
895
+ },
896
+ }
881
897
  : {}),
882
898
  ...(result.usage.reasoning != null
883
899
  ? { completion_tokens_details: { reasoning_tokens: result.usage.reasoning } }
@@ -935,7 +951,16 @@ function resultToResponses(result) {
935
951
  input_tokens: result.usage.input,
936
952
  output_tokens: result.usage.output,
937
953
  total_tokens: result.usage.total,
938
- input_tokens_details: { cached_tokens: result.usage.cached ?? 0 },
954
+ ...(result.usage.cached != null || result.usage.cacheWrite != null
955
+ ? {
956
+ input_tokens_details: {
957
+ ...(result.usage.cached != null ? { cached_tokens: result.usage.cached } : {}),
958
+ ...(result.usage.cacheWrite != null
959
+ ? { cache_write_tokens: result.usage.cacheWrite }
960
+ : {}),
961
+ },
962
+ }
963
+ : {}),
939
964
  output_tokens_details: { reasoning_tokens: result.usage.reasoning ?? 0 },
940
965
  },
941
966
  }
@@ -965,11 +990,14 @@ function resultToAnthropic(result) {
965
990
  ...(result.usage
966
991
  ? {
967
992
  usage: {
968
- input_tokens: result.usage.input,
993
+ input_tokens: Math.max(0, result.usage.input - (result.usage.cached ?? 0) - (result.usage.cacheWrite ?? 0)),
969
994
  output_tokens: result.usage.output,
970
995
  ...(result.usage.cached != null
971
996
  ? { cache_read_input_tokens: result.usage.cached }
972
997
  : {}),
998
+ ...(result.usage.cacheWrite != null
999
+ ? { cache_creation_input_tokens: result.usage.cacheWrite }
1000
+ : {}),
973
1001
  },
974
1002
  }
975
1003
  : {}),
@@ -1269,7 +1297,7 @@ function streamAnthropic(result) {
1269
1297
  ...message,
1270
1298
  content: [],
1271
1299
  stop_reason: null,
1272
- usage: { input_tokens: result.usage?.input ?? 0, output_tokens: 0 },
1300
+ ...(message.usage ? { usage: { ...message.usage, output_tokens: 0 } } : {}),
1273
1301
  },
1274
1302
  });
1275
1303
  for (const [index, block] of message.content.entries()) {
@@ -109,6 +109,25 @@ async function normalizeChatGptRequest(request) {
109
109
  if (!isRecord(raw))
110
110
  return request;
111
111
  const body = { ...raw };
112
+ // The Codex endpoint places the implicit cache boundary after input
113
+ // messages. Move stable top-level instructions into that prefix so they
114
+ // participate in prompt caching for subscription requests.
115
+ if (typeof body.instructions === "string" && body.instructions.length > 0) {
116
+ const input = Array.isArray(body.input)
117
+ ? body.input
118
+ : typeof body.input === "string"
119
+ ? [{ type: "message", role: "user", content: body.input }]
120
+ : [];
121
+ body.input = [
122
+ {
123
+ type: "message",
124
+ role: "developer",
125
+ content: [{ type: "input_text", text: body.instructions }],
126
+ },
127
+ ...input,
128
+ ];
129
+ delete body.instructions;
130
+ }
112
131
  delete body.prompt_cache_options;
113
132
  delete body.prompt_cache_retention;
114
133
  const stripBreakpoints = (value) => Array.isArray(value)
@@ -126,7 +145,7 @@ async function normalizeChatGptRequest(request) {
126
145
  }
127
146
  export function chatGptProvider(options = {}) {
128
147
  const clientId = options.clientId ?? DEFAULT_CLIENT_ID;
129
- const compatibilityVersion = options.compatibilityVersion ?? "0.144.2";
148
+ const compatibilityVersion = options.compatibilityVersion ?? "0.154.0";
130
149
  const fetcher = options.fetch ?? globalThis.fetch;
131
150
  async function startDeviceLogin(signal) {
132
151
  const response = await fetcher(DEVICE_CODE_URL, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aisubs",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Connect AI provider accounts and use those subscriptions from any local tool or as api.",
5
5
  "keywords": [
6
6
  "ai",