agl 40.4.0 → 40.5.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.
Files changed (149) hide show
  1. package/changelog.md +27 -0
  2. package/dist_serve/bundle.js +2228 -2177
  3. package/dist_serve/bundle.js.map +2 -2
  4. package/dist_ts/00_commitinfo_data.js +1 -1
  5. package/dist_ts/aglapi/aglapisurface.document.js +2 -2
  6. package/dist_ts/classes.accountsactions.d.ts +11 -1
  7. package/dist_ts/classes.accountsactions.js +59 -2
  8. package/dist_ts/classes.controller.js +5 -5
  9. package/dist_ts/classes.flexaccountaccess.js +3 -2
  10. package/dist_ts/classes.harnessreleases.d.ts +14 -4
  11. package/dist_ts/classes.harnessreleases.js +28 -4
  12. package/dist_ts/classes.inferencegateway.d.ts +28 -3
  13. package/dist_ts/classes.inferencegateway.js +177 -61
  14. package/dist_ts/classes.inferenceplacement.d.ts +5 -3
  15. package/dist_ts/classes.inferenceplacement.js +2 -2
  16. package/dist_ts/classes.inferencerouter.d.ts +349 -91
  17. package/dist_ts/classes.inferencerouter.js +804 -297
  18. package/dist_ts/classes.inferenceservice.d.ts +14 -2
  19. package/dist_ts/classes.inferenceservice.js +53 -14
  20. package/dist_ts/classes.inferencespend.d.ts +339 -0
  21. package/dist_ts/classes.inferencespend.js +815 -0
  22. package/dist_ts/classes.inferenceusagecache.d.ts +8 -0
  23. package/dist_ts/classes.inferenceusagecache.js +9 -5
  24. package/dist_ts/classes.inferenceusagetap.d.ts +50 -9
  25. package/dist_ts/classes.inferenceusagetap.js +185 -31
  26. package/dist_ts/classes.proxycontrol.d.ts +3 -2
  27. package/dist_ts/classes.proxycontrol.js +1 -2
  28. package/dist_ts/classes.proxydaemon.js +3 -2
  29. package/dist_ts/classes.proxystore.d.ts +6 -0
  30. package/dist_ts/classes.proxystore.js +11 -2
  31. package/dist_ts/fleet/classes.claudeworkers.d.ts +5 -4
  32. package/dist_ts/fleet/classes.claudeworkers.js +10 -9
  33. package/dist_ts/functions.claudecoderelease.d.ts +12 -6
  34. package/dist_ts/functions.claudecoderelease.js +13 -7
  35. package/dist_ts/functions.inferencecli.js +12 -8
  36. package/dist_ts/interfaces.auth.d.ts +1 -1
  37. package/dist_ts/interfaces.auth.js +3 -1
  38. package/dist_ts/plugins.d.ts +3 -1
  39. package/dist_ts/plugins.js +7 -2
  40. package/dist_ts_authswitch/authority-apikeys.d.ts +32 -0
  41. package/dist_ts_authswitch/authority-apikeys.js +65 -0
  42. package/dist_ts_authswitch/authority-contract.d.ts +142 -28
  43. package/dist_ts_authswitch/authority-contract.js +48 -1
  44. package/dist_ts_authswitch/authority-overview.js +8 -1
  45. package/dist_ts_authswitch/authority-placement.d.ts +7 -2
  46. package/dist_ts_authswitch/authority-placement.js +10 -3
  47. package/dist_ts_authswitch/authority-runtime-contract.d.ts +2 -2
  48. package/dist_ts_authswitch/authority-spend.d.ts +103 -0
  49. package/dist_ts_authswitch/authority-spend.js +110 -0
  50. package/dist_ts_authswitch/authority-subscription.d.ts +12 -1
  51. package/dist_ts_authswitch/authority-subscription.js +24 -1
  52. package/dist_ts_authswitch/authority-usagelimit.d.ts +7 -4
  53. package/dist_ts_authswitch/authority-usagelimit.js +89 -39
  54. package/dist_ts_authswitch/authority-usagewindows.d.ts +4 -1
  55. package/dist_ts_authswitch/authority-usagewindows.js +41 -16
  56. package/dist_ts_authswitch/classes.authoritybroker.d.ts +30 -19
  57. package/dist_ts_authswitch/classes.authoritybroker.js +152 -61
  58. package/dist_ts_authswitch/classes.authoritycli.d.ts +9 -4
  59. package/dist_ts_authswitch/classes.authoritycli.js +94 -29
  60. package/dist_ts_authswitch/classes.authorityclient.d.ts +24 -7
  61. package/dist_ts_authswitch/classes.authorityclient.js +31 -11
  62. package/dist_ts_authswitch/classes.authoritydaemon.js +39 -12
  63. package/dist_ts_authswitch/classes.authoritydatabase.d.ts +28 -5
  64. package/dist_ts_authswitch/classes.authoritydatabase.js +171 -9
  65. package/dist_ts_authswitch/classes.authorityimport.js +2 -2
  66. package/dist_ts_authswitch/classes.authoritymodels.d.ts +71 -3
  67. package/dist_ts_authswitch/classes.authoritymodels.js +249 -20
  68. package/dist_ts_authswitch/classes.authorityregistry.js +4 -2
  69. package/dist_ts_authswitch/classes.authorityusage.d.ts +69 -7
  70. package/dist_ts_authswitch/classes.authorityusage.js +118 -4
  71. package/dist_ts_authswitch/classes.claudenative.d.ts +16 -7
  72. package/dist_ts_authswitch/classes.claudenative.js +29 -16
  73. package/dist_ts_authswitch/classes.claudesignin.js +5 -2
  74. package/dist_ts_authswitch/testsupport.d.ts +24 -5
  75. package/dist_ts_authswitch/testsupport.js +56 -12
  76. package/dist_ts_authswitch/ts_migration/0011_account_api_key_fields.d.ts +12 -0
  77. package/dist_ts_authswitch/ts_migration/0011_account_api_key_fields.js +21 -0
  78. package/dist_ts_authswitch/ts_migration/index.js +3 -1
  79. package/dist_ts_interfaces/functions.authorityscope.d.ts +18 -7
  80. package/dist_ts_interfaces/functions.authorityscope.js +35 -8
  81. package/dist_ts_interfaces/interfaces.d.ts +1 -1
  82. package/dist_ts_interfaces/interfaces.js +1 -1
  83. package/dist_ts_interfaces/plugins.d.ts +2 -1
  84. package/dist_ts_interfaces/plugins.js +7 -4
  85. package/dist_ts_interfaces/requests.accounts.d.ts +56 -0
  86. package/dist_ts_interfaces/requests.accounts.js +11 -1
  87. package/dist_ts_interfaces/requests.d.ts +2 -2
  88. package/dist_ts_interfaces/requests.js +1 -1
  89. package/docs/authswitch.md +75 -12
  90. package/package.json +5 -5
  91. package/readme.md +198 -21
  92. package/ts/00_commitinfo_data.ts +1 -1
  93. package/ts/aglapi/aglapisurface.document.ts +2 -2
  94. package/ts/classes.accountsactions.ts +75 -1
  95. package/ts/classes.controller.ts +4 -4
  96. package/ts/classes.flexaccountaccess.ts +2 -1
  97. package/ts/classes.harnessreleases.ts +25 -4
  98. package/ts/classes.inferencegateway.ts +184 -64
  99. package/ts/classes.inferenceplacement.ts +6 -4
  100. package/ts/classes.inferencerouter.ts +1026 -314
  101. package/ts/classes.inferenceservice.ts +56 -13
  102. package/ts/classes.inferencespend.ts +870 -0
  103. package/ts/classes.inferenceusagecache.ts +16 -4
  104. package/ts/classes.inferenceusagetap.ts +159 -34
  105. package/ts/classes.proxycontrol.ts +3 -3
  106. package/ts/classes.proxydaemon.ts +2 -1
  107. package/ts/classes.proxystore.ts +11 -1
  108. package/ts/fleet/classes.claudeworkers.ts +9 -8
  109. package/ts/functions.claudecoderelease.ts +13 -6
  110. package/ts/functions.inferencecli.ts +11 -7
  111. package/ts/interfaces.auth.ts +2 -0
  112. package/ts/plugins.ts +7 -0
  113. package/ts_authswitch/authority-apikeys.ts +85 -0
  114. package/ts_authswitch/authority-contract.ts +167 -29
  115. package/ts_authswitch/authority-overview.ts +7 -0
  116. package/ts_authswitch/authority-placement.ts +12 -4
  117. package/ts_authswitch/authority-runtime-contract.ts +2 -2
  118. package/ts_authswitch/authority-spend.ts +168 -0
  119. package/ts_authswitch/authority-subscription.ts +28 -1
  120. package/ts_authswitch/authority-usagelimit.ts +95 -40
  121. package/ts_authswitch/authority-usagewindows.ts +42 -17
  122. package/ts_authswitch/classes.authoritybroker.ts +161 -68
  123. package/ts_authswitch/classes.authoritycli.ts +108 -32
  124. package/ts_authswitch/classes.authorityclient.ts +41 -10
  125. package/ts_authswitch/classes.authoritydaemon.ts +43 -13
  126. package/ts_authswitch/classes.authoritydatabase.ts +178 -8
  127. package/ts_authswitch/classes.authorityimport.ts +1 -1
  128. package/ts_authswitch/classes.authoritymodels.ts +165 -19
  129. package/ts_authswitch/classes.authorityregistry.ts +3 -0
  130. package/ts_authswitch/classes.authorityusage.ts +171 -10
  131. package/ts_authswitch/classes.claudenative.ts +28 -15
  132. package/ts_authswitch/classes.claudesignin.ts +4 -1
  133. package/ts_authswitch/native-contracts.md +81 -20
  134. package/ts_authswitch/readme.md +1 -1
  135. package/ts_authswitch/testsupport.ts +58 -11
  136. package/ts_authswitch/ts_migration/0011_account_api_key_fields.ts +20 -0
  137. package/ts_authswitch/ts_migration/index.ts +2 -0
  138. package/ts_interfaces/functions.authorityscope.ts +43 -8
  139. package/ts_interfaces/interfaces.ts +1 -1
  140. package/ts_interfaces/plugins.ts +6 -3
  141. package/ts_interfaces/requests.accounts.ts +48 -0
  142. package/ts_interfaces/requests.ts +4 -0
  143. package/ts_web/00_commitinfo_data.ts +1 -1
  144. package/ts_web/accounts.plugins.ts +3 -1
  145. package/ts_web/accounts.templates.ts +20 -11
  146. package/ts_web/classes.accountsreadmodel.ts +322 -70
  147. package/ts_web/elements.accounts.ts +219 -20
  148. package/ts_web/elements.codexconnections.ts +3 -1
  149. package/ts_web/plugins.ts +4 -2
@@ -2,9 +2,10 @@ import * as plugins from './plugins.js';
2
2
  import { AuthorityState } from './classes.authoritystate.js';
3
3
  import type { IAuthoritySocketPaths } from './classes.authorityclient.js';
4
4
  import { FlexAccountAccess } from './classes.flexaccountaccess.js';
5
- import { InferenceRouter, InferenceRoutingError, type IInferenceAccount, type IInferenceAccountStatus,
6
- type IInferenceRoutingPolicy } from './classes.inferencerouter.js';
5
+ import { InferenceRouter, InferenceRoutingError, inferencePaymentReportOptions, type IInferenceAccount,
6
+ type IInferenceAccountStatus, type IInferenceRoutingPolicy } from './classes.inferencerouter.js';
7
7
  import { InferenceUsageCache } from './classes.inferenceusagecache.js';
8
+ import { InferenceSpendOutbox, type IInferenceSpendStore } from './classes.inferencespend.js';
8
9
  import { InferenceUsageObserver, type IInferenceUsageObserverStatus } from './classes.inferenceusageobserver.js';
9
10
  import { InferenceGateway, type IInferenceGatewayClientOptions, type IInferenceGatewayGrant, type IInferenceGatewayLease,
10
11
  type IInferenceGatewayLimits, type IInferenceGatewayRestoredLease, type IInferenceGatewayStatus } from './classes.inferencegateway.js';
@@ -12,7 +13,8 @@ import type { IInferenceAffinityStore } from './classes.inferenceaffinity.js';
12
13
  import type { IInferencePlacementJournal, IInferencePlacementStatus } from './classes.inferenceplacement.js';
13
14
  import type { IInferenceUsageLedger } from './classes.inferenceusage.js';
14
15
  import type { TInferenceProvider } from './functions.proxycontract.js';
15
- import { findAuthorityGatewayLogin, findInferencePool, isAuthorityInstallationId, parseAuthorityScope } from '../ts_interfaces/index.js';
16
+ import { findAuthorityApiKeyLogin, findAuthorityGatewayLogin, findInferencePool, isAuthorityInstallationId,
17
+ parseAuthorityScope } from '../ts_interfaces/index.js';
16
18
 
17
19
  export interface IInferenceServiceOptions {
18
20
  authorityPaths: IAuthoritySocketPaths;
@@ -25,6 +27,11 @@ export interface IInferenceServiceOptions {
25
27
  placements: IInferencePlacementJournal;
26
28
  /** Where the tokens of every answer are recorded for the usage metrics. */
27
29
  usageLedger: IInferenceUsageLedger;
30
+ /**
31
+ * Where the spend of the answers relayed on API keys is kept until the account service acknowledged it: the proxy's
32
+ * own database (`ProxyStore.spend`).
33
+ */
34
+ spendStore: IInferenceSpendStore;
28
35
  providerFetch?: typeof fetch;
29
36
  limits?: IInferenceGatewayLimits;
30
37
  }
@@ -45,6 +52,11 @@ export class InferenceService {
45
52
  * to the authority, whose answer the cache holds.
46
53
  */
47
54
  private readonly observer: InferenceUsageObserver;
55
+ /**
56
+ * The spend of the answers relayed on API keys until the account service acknowledged it: counted against each key's
57
+ * limit in the readings the cache hands out, and reported to the account service.
58
+ */
59
+ private readonly spend: InferenceSpendOutbox;
48
60
  private readonly abort = new AbortController();
49
61
  private ready = false;
50
62
  private started = false;
@@ -66,6 +78,21 @@ export class InferenceService {
66
78
  // Routing needs a reading: an account added a moment ago is routed on once its first read landed.
67
79
  return client.getUsage(accountIdArg, login.id, signalArg, { waitForFirstRead: true });
68
80
  },
81
+ // A key's reading counts the answers whose spend the account service has not acknowledged yet.
82
+ view: (accountIdArg, snapshotArg) => this.spend.judged(accountIdArg, snapshotArg),
83
+ });
84
+ this.spend = new InferenceSpendOutbox({
85
+ store: options.spendStore,
86
+ // Reported on the login each answer was served on, which the outbox keeps with it: also once the key was removed.
87
+ report: async (accountIdArg, loginIdArg, answersArg, signalArg) => {
88
+ const client = this.authority.readyClient();
89
+ if (!this.ready || !client) throw new Error('The account service is unavailable.');
90
+ return client.recordSpend(accountIdArg, loginIdArg, answersArg, signalArg);
91
+ },
92
+ // The account service's answer counts the report's answers: it replaces the held reading as they leave the outbox.
93
+ acknowledged: (accountIdArg, usageArg) => this.usage.accept(accountIdArg, usageArg),
94
+ // An answer recorded without a login is reported on the key's live login, or the removed key's.
95
+ attribute: (accountIdArg) => findAuthorityApiKeyLogin(this.authority.getProjection(), accountIdArg)?.id,
69
96
  });
70
97
  this.observer = new InferenceUsageObserver({
71
98
  send: async (accountIdArg, reportArg, signalArg) => {
@@ -89,15 +116,22 @@ export class InferenceService {
89
116
  },
90
117
  usage: (accountIdArg, signalArg) => this.usage.get(accountIdArg, signalArg),
91
118
  // The account service holds a payment problem on the account's usage, which the cache then routes on.
92
- reportPaymentProblem: async (accountIdArg, observedAtArg) => {
119
+ reportPaymentProblem: async (accountIdArg, observedAtArg, causeArg) => {
93
120
  const client = this.authority.readyClient();
94
121
  const login = findAuthorityGatewayLogin(this.authority.getProjection(), accountIdArg);
95
122
  if (!this.ready || !client || typeof login === 'string') throw new Error('The account login is unavailable.');
96
- const usage = await client.reportPaymentProblem(accountIdArg, login.id, observedAtArg, this.abort.signal);
123
+ const usage = await client.reportPaymentProblem(accountIdArg, login.id, observedAtArg, this.abort.signal,
124
+ inferencePaymentReportOptions(causeArg));
97
125
  this.usage.accept(accountIdArg, usage);
98
126
  },
99
127
  resolveAccess: (accountIdArg, signalArg, rejectedGenerationArg) => this.access.resolveGateway(
100
128
  accountIdArg, AbortSignal.any([signalArg, this.abort.signal]), rejectedGenerationArg),
129
+ // Recorded on the login the route was served on, as the pool was read when the request was placed.
130
+ spend: {
131
+ record: (accountIdArg, loginIdArg, recordArg) => this.spend.record(accountIdArg, loginIdArg, recordArg),
132
+ held: (accountIdArg) => this.spend.held(accountIdArg),
133
+ uncounted: (accountIdArg, modelArg) => this.spend.uncounted(accountIdArg, modelArg),
134
+ },
101
135
  });
102
136
  this.gateway = new InferenceGateway({ router: this.router, signatureKey: options.signatureKey,
103
137
  affinity: options.affinity, placements: options.placements, usageLedger: options.usageLedger, port: options.port,
@@ -114,8 +148,10 @@ export class InferenceService {
114
148
  if (!this.ready || projection.state !== 'ready' || !projection.snapshot) return undefined;
115
149
  return projection.snapshot.accounts.flatMap((account) => {
116
150
  const pool = findInferencePool(projection, account.id);
151
+ const login = findAuthorityGatewayLogin(projection, account.id);
117
152
  return pool ? [{ id: account.id, label: account.label, revision: String(account.revision), providerId: pool.provider,
118
- credential: pool.credential, maxUsagePercent: account.maxUsagePercent, reserveLiftUntil: account.reserveLiftUntil, lapsesAt: account.lapsesAt }] : [];
153
+ credential: pool.credential, maxUsagePercent: account.maxUsagePercent, reserveLiftUntil: account.reserveLiftUntil, lapsesAt: account.lapsesAt,
154
+ spendLimitMicroUsd: account.spendLimitMicroUsd, loginId: typeof login === 'string' ? null : login.id }] : [];
119
155
  });
120
156
  }
121
157
 
@@ -143,6 +179,8 @@ export class InferenceService {
143
179
  incarnationId: binding.incarnationId, expectedRevision: binding.revision }, signal);
144
180
  }
145
181
  this.ready = true;
182
+ // The answers a run before left unacknowledged count from the start, before any request is placed, and are sent.
183
+ await this.spend.start(signal);
146
184
  return this.baseUrl;
147
185
  } catch (errorArg) {
148
186
  await this.stop();
@@ -165,20 +203,21 @@ export class InferenceService {
165
203
  /**
166
204
  * The gateway's state, each pool account judged on the usage reading a request would route on now -- the account
167
205
  * service's answer from its own cache (`InferenceUsageCache.current`), so a pool no request used since the proxy
168
- * started is judged as its next request would judge it -- the subscription account each provider's next new client
169
- * session goes to, and the latest placements the journal holds.
206
+ * started is judged as its next request would judge it -- the account each provider's next new session of a client
207
+ * that names no credential goes to (a subscription while one takes it, else an API key), and the latest placements the
208
+ * journal holds.
170
209
  */
171
210
  public async status(signalArg: AbortSignal): Promise<IInferenceGatewayStatus & { baseUrl: string;
172
211
  authority: 'ready' | 'unavailable'; accounts: IInferenceAccountStatus[]; usageObservations: IInferenceUsageObserverStatus;
173
212
  nextPlacement: Record<TInferenceProvider, string | null>; recentPlacements: IInferencePlacementStatus[] }> {
174
213
  const pool = this.poolAccounts() ?? [];
175
- const readings = new Map(await Promise.all(pool.filter((accountArg) => accountArg.credential === 'subscription')
214
+ // A key's usage is its month's spend, with the payment problem the account service holds on it.
215
+ const readings = new Map(await Promise.all(pool
176
216
  .map(async (accountArg) => [accountArg.id, await this.usage.current(accountArg.id, signalArg)] as const)));
177
217
  const accounts = this.router.accountStatus(pool, (accountIdArg) => readings.get(accountIdArg));
178
218
  const recent = await this.options.placements.recent(recentPlacementCount, signalArg);
179
219
  const next = (providerArg: TInferenceProvider): string | null =>
180
- accounts.find((accountArg) => accountArg.provider === providerArg && accountArg.credential === 'subscription'
181
- && accountArg.placementRank === 1)?.id ?? null;
220
+ accounts.find((accountArg) => accountArg.provider === providerArg && accountArg.placementRank === 1)?.id ?? null;
182
221
  return { ...this.gateway.status(), baseUrl: this.baseUrl ?? '', usageObservations: this.observer.status(),
183
222
  authority: this.ready && this.authority.getProjection().state === 'ready' ? 'ready' : 'unavailable',
184
223
  accounts,
@@ -193,9 +232,13 @@ export class InferenceService {
193
232
  this.usage.stop();
194
233
  try { await this.gateway.stop(); }
195
234
  finally {
196
- // Hand-overs in flight end before the authority connection closes under them.
235
+ // Hand-overs in flight end before the authority connection closes under them; the answers the gateway ended are
236
+ // in the outbox, which keeps what it holds for the next start.
197
237
  try { await this.observer.stop(); }
198
- finally { try { await this.access.stop(); } finally { await this.authority.stop(); } }
238
+ finally {
239
+ try { await this.spend.stop(); }
240
+ finally { try { await this.access.stop(); } finally { await this.authority.stop(); } }
241
+ }
199
242
  }
200
243
  })();
201
244
  }