@gajae-code/ai 0.17.2 → 0.17.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 +118 -0
- package/dist/types/auth-gateway/server.d.ts +23 -1
- package/dist/types/auth-storage.d.ts +12 -1
- package/dist/types/providers/anthropic.d.ts +1 -1
- package/dist/types/providers/cursor.d.ts +10 -0
- package/dist/types/providers/openai-completions.d.ts +9 -1
- package/dist/types/types.d.ts +16 -0
- package/dist/types/utils/discovery/openai-compatible.d.ts +10 -0
- package/dist/types/utils/fallback-transport.d.ts +4 -0
- package/dist/types/utils/h2-fetch.d.ts +8 -7
- package/dist/types/utils/stream-repetition-guard.d.ts +107 -0
- package/dist/types/utils/tool-call-healing.d.ts +4 -0
- package/dist/types/utils/tool-fence-strip.d.ts +27 -0
- package/package.json +3 -3
- package/src/auth-gateway/server.ts +48 -9
- package/src/auth-storage.ts +194 -52
- package/src/model-pricing.ts +22 -0
- package/src/model-thinking.ts +40 -4
- package/src/models.json +10060 -3164
- package/src/providers/anthropic.d.ts +1 -1
- package/src/providers/anthropic.ts +1 -1
- package/src/providers/cursor.d.ts +10 -0
- package/src/providers/cursor.ts +144 -24
- package/src/providers/kiro-api-key.ts +7 -0
- package/src/providers/openai-completions.d.ts +9 -1
- package/src/providers/openai-completions.ts +410 -150
- package/src/providers/transform-messages.ts +54 -1
- package/src/stream.ts +7 -0
- package/src/types.d.ts +16 -0
- package/src/types.ts +17 -0
- package/src/utils/discovery/openai-compatible.ts +16 -2
- package/src/utils/fallback-transport.d.ts +4 -0
- package/src/utils/fallback-transport.ts +11 -0
- package/src/utils/h2-fetch.ts +65 -26
- package/src/utils/http-inspector.ts +2 -0
- package/src/utils/idle-iterator.ts +109 -96
- package/src/utils/json-parse.ts +12 -4
- package/src/utils/stream-repetition-guard.d.ts +107 -0
- package/src/utils/stream-repetition-guard.ts +290 -0
- package/src/utils/tool-call-healing.d.ts +4 -0
- package/src/utils/tool-call-healing.ts +4 -0
- package/src/utils/tool-fence-strip.d.ts +27 -0
- package/src/utils/tool-fence-strip.ts +64 -0
package/src/auth-storage.ts
CHANGED
|
@@ -481,6 +481,10 @@ export interface AuthCredentialStore {
|
|
|
481
481
|
deleteAuthCredentialsForProvider(provider: string, disabledCause: string): void;
|
|
482
482
|
getCache(key: string, options?: { includeExpired?: boolean }): string | null;
|
|
483
483
|
setCache(key: string, value: string, expiresAtSec: number): void;
|
|
484
|
+
/** Atomically claim a cross-process usage poll lease for a bounded interval. */
|
|
485
|
+
tryAcquireUsageFetchLease?(key: string, owner: string, nowMs: number, leaseMs: number): boolean | undefined;
|
|
486
|
+
/** Release a usage poll lease owned by this process. */
|
|
487
|
+
releaseUsageFetchLease?(key: string, owner: string): void;
|
|
484
488
|
/** Atomically allocate a durable sequence for broker restart epochs. */
|
|
485
489
|
allocateMonotonicSequence(key: string, expiresAtSec: number): number;
|
|
486
490
|
deleteCachePrefix?(prefix: string): void;
|
|
@@ -929,6 +933,21 @@ const DEFAULT_RANKING_STRATEGIES = new Map<Provider, CredentialRankingStrategy>(
|
|
|
929
933
|
]);
|
|
930
934
|
|
|
931
935
|
const USAGE_CACHE_PREFIX = "usage_cache:";
|
|
936
|
+
function hashUsageDiagnosticValue(value: unknown): string | undefined {
|
|
937
|
+
if (typeof value !== "string" || value.length === 0) return undefined;
|
|
938
|
+
return Bun.hash(value).toString(16);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
function redactUsageDiagnosticUrl(value: string | undefined): string | undefined {
|
|
942
|
+
if (!value) return undefined;
|
|
943
|
+
try {
|
|
944
|
+
const parsed = new URL(value);
|
|
945
|
+
return `${parsed.protocol}//${parsed.host}`;
|
|
946
|
+
} catch {
|
|
947
|
+
return "[invalid-url]";
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
932
951
|
// 5 min stale tolerance. Anthropic / OpenAI rate-limit /usage hard at the IP
|
|
933
952
|
// level so we can't fetch all N credentials every cycle; with a long cache
|
|
934
953
|
// each credential's last-known value sticks visible while peers retry. UI
|
|
@@ -945,6 +964,9 @@ const USAGE_FAILURE_BACKOFF_MS = 10_000;
|
|
|
945
964
|
// Bumped from 3s — Anthropic model usage retries up to 3 times with exponential backoff
|
|
946
965
|
// (~3.5s total worst case); a tight per-request budget aborts retries mid-cycle.
|
|
947
966
|
const DEFAULT_USAGE_REQUEST_TIMEOUT_MS = 10_000;
|
|
967
|
+
/** Grace period after the configured provider timeout before peers retry a poll. */
|
|
968
|
+
const USAGE_FETCH_LEASE_GRACE_MS = 5_000;
|
|
969
|
+
const USAGE_FETCH_WAIT_POLL_MS = 25;
|
|
948
970
|
const DEFAULT_OAUTH_REFRESH_TIMEOUT_MS = 10_000;
|
|
949
971
|
/** Maximum provider ownership window; expiry recovers a crashed process without indefinite blocking. */
|
|
950
972
|
const OAUTH_REFRESH_LEASE_MS = DEFAULT_OAUTH_REFRESH_TIMEOUT_MS + 5_000;
|
|
@@ -1407,6 +1429,7 @@ export class AuthStorage {
|
|
|
1407
1429
|
#usageReportsInFlight: Map<string, Promise<UsageReport[] | null>> = new Map();
|
|
1408
1430
|
#usageFetch: typeof fetch;
|
|
1409
1431
|
#usageRequestTimeoutMs: number;
|
|
1432
|
+
#usageFetchLeaseMs: number;
|
|
1410
1433
|
#credentialRankingMode: CredentialRankingMode = "balanced";
|
|
1411
1434
|
#usageLogger?: UsageLogger;
|
|
1412
1435
|
#fallbackResolver?: (provider: string) => string | undefined;
|
|
@@ -1459,6 +1482,13 @@ export class AuthStorage {
|
|
|
1459
1482
|
this.#usageCache = new AuthStorageUsageCache(this.#store);
|
|
1460
1483
|
this.#usageFetch = options.usageFetch ?? fetch;
|
|
1461
1484
|
this.#usageRequestTimeoutMs = options.usageRequestTimeoutMs ?? DEFAULT_USAGE_REQUEST_TIMEOUT_MS;
|
|
1485
|
+
const usageTimeoutForLease =
|
|
1486
|
+
typeof this.#usageRequestTimeoutMs === "number" &&
|
|
1487
|
+
Number.isFinite(this.#usageRequestTimeoutMs) &&
|
|
1488
|
+
this.#usageRequestTimeoutMs > 0
|
|
1489
|
+
? this.#usageRequestTimeoutMs
|
|
1490
|
+
: DEFAULT_USAGE_REQUEST_TIMEOUT_MS;
|
|
1491
|
+
this.#usageFetchLeaseMs = usageTimeoutForLease + USAGE_FETCH_LEASE_GRACE_MS;
|
|
1462
1492
|
this.#credentialRankingMode = options.credentialRankingMode ?? "balanced";
|
|
1463
1493
|
this.#refreshOAuthCredentialOverride = options.refreshOAuthCredential;
|
|
1464
1494
|
this.#fetchUsageReportsOverride = options.fetchUsageReports;
|
|
@@ -1548,8 +1578,15 @@ export class AuthStorage {
|
|
|
1548
1578
|
getProviderEvidenceGeneration(provider: string, resolvedApiKey?: string, owner?: object): string {
|
|
1549
1579
|
const storageProvider = resolveOAuthStorageProvider(provider);
|
|
1550
1580
|
provider = storageProvider;
|
|
1551
|
-
const evidenceApiKey = resolvedApiKey;
|
|
1552
1581
|
const configOverride = this.#configOverrideRegistration(storageProvider, owner);
|
|
1582
|
+
const runtimeOverride = this.#runtimeOverrides.get(storageProvider);
|
|
1583
|
+
const environmentOverride = runtimeOverride || configOverride?.apiKey ? undefined : getEnvApiKey(storageProvider);
|
|
1584
|
+
// Discovery callers may fingerprint the provider before resolving its
|
|
1585
|
+
// already-effective override or environment key, then pass that resolved
|
|
1586
|
+
// key on the live path. Use the same effective key for the omitted form
|
|
1587
|
+
// so both paths share one credential-sensitive generation.
|
|
1588
|
+
const evidenceApiKey =
|
|
1589
|
+
resolvedApiKey ?? (runtimeOverride || configOverride?.apiKey || environmentOverride || undefined);
|
|
1553
1590
|
const storedLiteral =
|
|
1554
1591
|
evidenceApiKey === undefined || this.#runtimeOverrides.has(storageProvider) || configOverride !== undefined
|
|
1555
1592
|
? undefined
|
|
@@ -1585,9 +1622,7 @@ export class AuthStorage {
|
|
|
1585
1622
|
credential.type === "oauth" && Number.isFinite(credential.expires) && credential.expires > Date.now(),
|
|
1586
1623
|
);
|
|
1587
1624
|
const effectiveEnvKey =
|
|
1588
|
-
|
|
1589
|
-
? undefined
|
|
1590
|
-
: getEnvApiKey(provider);
|
|
1625
|
+
runtimeOverride || configOverride?.apiKey || hasApiKey || hasUsableOAuth ? undefined : getEnvApiKey(provider);
|
|
1591
1626
|
const storedApiKeyFingerprint = credentials
|
|
1592
1627
|
.filter(
|
|
1593
1628
|
(credential): credential is Extract<AuthCredential, { type: "api_key" }> => credential.type === "api_key",
|
|
@@ -2009,6 +2044,7 @@ export class AuthStorage {
|
|
|
2009
2044
|
this.#usageRequestInFlight.clear();
|
|
2010
2045
|
this.#usageReportsInFlight.clear();
|
|
2011
2046
|
this.#usageCache.deletePrefix?.(`report:${storageProvider}:`);
|
|
2047
|
+
this.#usageCache.deletePrefix?.("reports:");
|
|
2012
2048
|
for (const [scopeId, selectors] of this.#sessionCredentialSelectors) {
|
|
2013
2049
|
const selector = selectors.get(storageProvider);
|
|
2014
2050
|
const selected = selector
|
|
@@ -2422,6 +2458,7 @@ export class AuthStorage {
|
|
|
2422
2458
|
this.#data.set(provider, credentials);
|
|
2423
2459
|
}
|
|
2424
2460
|
if (identityOrderChanged) this.#resetProviderAssignments(storageProvider);
|
|
2461
|
+
if (!tokenRotationOnly) this.#invalidateUsageCacheForProvider(storageProvider);
|
|
2425
2462
|
if (tokenRotationOnly) this.#bumpGeneration("oauth-token-rotation");
|
|
2426
2463
|
else this.#bumpGeneration("credentials", provider);
|
|
2427
2464
|
}
|
|
@@ -3111,6 +3148,7 @@ export class AuthStorage {
|
|
|
3111
3148
|
this.#usageRequestInFlight.clear();
|
|
3112
3149
|
this.#usageReportsInFlight.clear();
|
|
3113
3150
|
this.#usageCache.deletePrefix?.(`report:${provider}:`);
|
|
3151
|
+
this.#usageCache.deletePrefix?.("reports:");
|
|
3114
3152
|
}
|
|
3115
3153
|
|
|
3116
3154
|
/**
|
|
@@ -4037,6 +4075,8 @@ export class AuthStorage {
|
|
|
4037
4075
|
logDetails: boolean = true,
|
|
4038
4076
|
): Promise<UsageReport | null> {
|
|
4039
4077
|
const cacheKey = this.#buildUsageReportCacheKey(request);
|
|
4078
|
+
const provider = resolveOAuthStorageProvider(request.provider);
|
|
4079
|
+
const credentialGeneration = this.#getProviderGeneration(provider);
|
|
4040
4080
|
const now = Date.now();
|
|
4041
4081
|
const cached = this.#usageCache.get<UsageReport | null>(cacheKey);
|
|
4042
4082
|
// Fresh cache hit: return whatever's there (success or null fallback).
|
|
@@ -4049,6 +4089,7 @@ export class AuthStorage {
|
|
|
4049
4089
|
|
|
4050
4090
|
const promise = (async () => {
|
|
4051
4091
|
const report = await this.#fetchUsageUncached(request, timeoutMs, logDetails);
|
|
4092
|
+
if (this.#getProviderGeneration(provider) !== credentialGeneration) return null;
|
|
4052
4093
|
const ttlJitter = USAGE_REPORT_TTL_MS * (Math.random() * 0.5 - 0.25);
|
|
4053
4094
|
if (report !== null) {
|
|
4054
4095
|
// Success: stagger per-credential cache expiry so all accounts don't
|
|
@@ -4080,6 +4121,51 @@ export class AuthStorage {
|
|
|
4080
4121
|
return promise;
|
|
4081
4122
|
}
|
|
4082
4123
|
|
|
4124
|
+
#captureUsageProviderGenerations(requests: ReadonlyArray<UsageRequestDescriptor>): Map<string, number> {
|
|
4125
|
+
const generations = new Map<string, number>();
|
|
4126
|
+
for (const request of requests) {
|
|
4127
|
+
const provider = resolveOAuthStorageProvider(request.provider);
|
|
4128
|
+
if (!generations.has(provider)) generations.set(provider, this.#getProviderGeneration(provider));
|
|
4129
|
+
}
|
|
4130
|
+
return generations;
|
|
4131
|
+
}
|
|
4132
|
+
|
|
4133
|
+
#usageProviderGenerationsMatch(generations: ReadonlyMap<string, number>): boolean {
|
|
4134
|
+
for (const [provider, generation] of generations) {
|
|
4135
|
+
if (this.#getProviderGeneration(provider) !== generation) return false;
|
|
4136
|
+
}
|
|
4137
|
+
return true;
|
|
4138
|
+
}
|
|
4139
|
+
|
|
4140
|
+
#readAggregateUsageCache(cacheKey: string): UsageReport[] | undefined {
|
|
4141
|
+
const cached = this.#usageCache.get<UsageReport[]>(cacheKey);
|
|
4142
|
+
if (!cached || cached.expiresAt <= Date.now() || !Array.isArray(cached.value)) return undefined;
|
|
4143
|
+
return cached.value;
|
|
4144
|
+
}
|
|
4145
|
+
|
|
4146
|
+
#tryAcquireUsageFetchLease(cacheKey: string): boolean | undefined {
|
|
4147
|
+
const claim = this.#store.tryAcquireUsageFetchLease;
|
|
4148
|
+
if (!claim) return undefined;
|
|
4149
|
+
return claim.call(this.#store, cacheKey, this.#oauthRefreshLeaseOwner, Date.now(), this.#usageFetchLeaseMs);
|
|
4150
|
+
}
|
|
4151
|
+
|
|
4152
|
+
async #waitForAggregateUsagePoll(
|
|
4153
|
+
cacheKey: string,
|
|
4154
|
+
): Promise<{ cached: UsageReport[] | undefined; leaseAcquired: boolean }> {
|
|
4155
|
+
const deadline = Date.now() + this.#usageFetchLeaseMs;
|
|
4156
|
+
while (Date.now() < deadline) {
|
|
4157
|
+
const cached = this.#readAggregateUsageCache(cacheKey);
|
|
4158
|
+
if (cached !== undefined) return { cached, leaseAcquired: false };
|
|
4159
|
+
if (this.#tryAcquireUsageFetchLease(cacheKey) === true) return { cached: undefined, leaseAcquired: true };
|
|
4160
|
+
await Bun.sleep(USAGE_FETCH_WAIT_POLL_MS);
|
|
4161
|
+
}
|
|
4162
|
+
return { cached: this.#readAggregateUsageCache(cacheKey), leaseAcquired: false };
|
|
4163
|
+
}
|
|
4164
|
+
|
|
4165
|
+
#releaseUsageFetchLease(cacheKey: string): void {
|
|
4166
|
+
this.#store.releaseUsageFetchLease?.(cacheKey, this.#oauthRefreshLeaseOwner);
|
|
4167
|
+
}
|
|
4168
|
+
|
|
4083
4169
|
#collectUsageRequests(options?: {
|
|
4084
4170
|
provider?: Provider;
|
|
4085
4171
|
baseUrlResolver?: (provider: Provider) => string | undefined;
|
|
@@ -4297,10 +4383,15 @@ export class AuthStorage {
|
|
|
4297
4383
|
async fetchUsageReports(options?: {
|
|
4298
4384
|
provider?: Provider;
|
|
4299
4385
|
baseUrlResolver?: (provider: Provider) => string | undefined;
|
|
4300
|
-
/** Caller
|
|
4386
|
+
/** Caller’s cancel signal; only rejects this caller, never the shared upstream fetch. */
|
|
4301
4387
|
signal?: AbortSignal;
|
|
4302
4388
|
/** Disable provider/account/error logging for secret-safe control surfaces. */
|
|
4303
4389
|
logDetails?: boolean;
|
|
4390
|
+
/**
|
|
4391
|
+
* Enable identity correlation diagnostics. Values are one-way hashed and
|
|
4392
|
+
* URLs are reduced to their origin; the default logs provider/type/count only.
|
|
4393
|
+
*/
|
|
4394
|
+
logIdentity?: boolean;
|
|
4304
4395
|
}): Promise<UsageReport[] | null> {
|
|
4305
4396
|
// Caller override > store-level hook > local per-credential fan-out.
|
|
4306
4397
|
// `RemoteAuthCredentialStore` implements the store hook so a gateway
|
|
@@ -4340,63 +4431,87 @@ export class AuthStorage {
|
|
|
4340
4431
|
const requests = this.#collectUsageRequests(options);
|
|
4341
4432
|
if (requests.length === 0) return [];
|
|
4342
4433
|
|
|
4343
|
-
if (options?.logDetails !== false) {
|
|
4344
|
-
this.#usageLogger?.debug("Usage fetch requested", {
|
|
4345
|
-
providers: [...new Set(requests.map(request => request.provider))].sort(),
|
|
4346
|
-
});
|
|
4347
|
-
}
|
|
4348
|
-
|
|
4349
4434
|
// Per-credential caching with jitter lives in #fetchUsageCached, so we
|
|
4350
|
-
//
|
|
4351
|
-
//
|
|
4352
|
-
//
|
|
4353
|
-
// aggregate cache freezes whichever set landed first).
|
|
4435
|
+
// also retain the completed aggregate in the durable store. The aggregate
|
|
4436
|
+
// cache is what lets another process reuse this poll instead of starting a
|
|
4437
|
+
// second provider fan-out while the first process is still collecting rows.
|
|
4354
4438
|
const cacheKey = this.#buildUsageReportsCacheKey(requests);
|
|
4355
|
-
|
|
4439
|
+
const providerGenerations = this.#captureUsageProviderGenerations(requests);
|
|
4356
4440
|
const inFlight = this.#usageReportsInFlight.get(cacheKey);
|
|
4357
4441
|
if (inFlight) return raceUsageWithSignal(inFlight, options?.signal);
|
|
4358
4442
|
|
|
4359
4443
|
const promise = (async () => {
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4444
|
+
let leaseOwned = false;
|
|
4445
|
+
try {
|
|
4446
|
+
const initialLeaseClaim = this.#tryAcquireUsageFetchLease(cacheKey);
|
|
4447
|
+
const leaseSupported = initialLeaseClaim !== undefined;
|
|
4448
|
+
if (leaseSupported) {
|
|
4449
|
+
leaseOwned = initialLeaseClaim === true;
|
|
4450
|
+
if (!leaseOwned) {
|
|
4451
|
+
const shared = await this.#waitForAggregateUsagePoll(cacheKey);
|
|
4452
|
+
if (shared.cached !== undefined) return shared.cached;
|
|
4453
|
+
leaseOwned = shared.leaseAcquired || this.#tryAcquireUsageFetchLease(cacheKey) === true;
|
|
4454
|
+
}
|
|
4455
|
+
}
|
|
4456
|
+
|
|
4457
|
+
if (options?.logDetails !== false) {
|
|
4458
|
+
this.#usageLogger?.debug("Usage fetch requested", {
|
|
4459
|
+
providers: [...new Set(requests.map(request => request.provider))].sort(),
|
|
4460
|
+
credentialTypes: [...new Set(requests.map(request => request.credential.type))].sort(),
|
|
4461
|
+
credentials: requests.length,
|
|
4368
4462
|
});
|
|
4463
|
+
if (options?.logIdentity === true) {
|
|
4464
|
+
for (const request of requests) {
|
|
4465
|
+
this.#usageLogger?.debug("Usage fetch queued", {
|
|
4466
|
+
provider: request.provider,
|
|
4467
|
+
credentialType: request.credential.type,
|
|
4468
|
+
baseUrl: redactUsageDiagnosticUrl(request.baseUrl),
|
|
4469
|
+
accountId: hashUsageDiagnosticValue(request.credential.accountId),
|
|
4470
|
+
email: hashUsageDiagnosticValue(request.credential.email),
|
|
4471
|
+
});
|
|
4472
|
+
}
|
|
4473
|
+
}
|
|
4369
4474
|
}
|
|
4370
|
-
}
|
|
4371
4475
|
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4476
|
+
const results = await Promise.all(
|
|
4477
|
+
requests.map(request =>
|
|
4478
|
+
this.#fetchUsageCached(request, this.#usageRequestTimeoutMs, options?.logDetails !== false),
|
|
4479
|
+
),
|
|
4480
|
+
);
|
|
4481
|
+
const reports = results.filter((report): report is UsageReport => report !== null);
|
|
4482
|
+
const resolved = this.#dedupeUsageReports(reports);
|
|
4483
|
+
if (this.#usageProviderGenerationsMatch(providerGenerations)) {
|
|
4484
|
+
// This is a lease-scoped handoff for concurrent processes, not a
|
|
4485
|
+
// long-lived aggregate cache: per-credential jitter remains active
|
|
4486
|
+
// on the next poll.
|
|
4487
|
+
this.#usageCache.set(cacheKey, { value: resolved, expiresAt: Date.now() + this.#usageFetchLeaseMs });
|
|
4488
|
+
}
|
|
4489
|
+
if (options?.logDetails !== false) {
|
|
4490
|
+
this.#usageLogger?.debug("Usage fetch resolved", {
|
|
4491
|
+
reports:
|
|
4492
|
+
options?.logIdentity === true
|
|
4493
|
+
? resolved.map(report => ({
|
|
4494
|
+
provider: report.provider,
|
|
4495
|
+
limits: report.limits.length,
|
|
4496
|
+
account: hashUsageDiagnosticValue(
|
|
4497
|
+
this.#getUsageReportMetadataValue(report, "email") ??
|
|
4498
|
+
this.#getUsageReportMetadataValue(report, "accountId") ??
|
|
4499
|
+
this.#getUsageReportMetadataValue(report, "account") ??
|
|
4500
|
+
this.#getUsageReportMetadataValue(report, "user") ??
|
|
4501
|
+
this.#getUsageReportMetadataValue(report, "username") ??
|
|
4502
|
+
this.#getUsageReportScopeAccountId(report),
|
|
4503
|
+
),
|
|
4504
|
+
}))
|
|
4505
|
+
: {
|
|
4506
|
+
count: resolved.length,
|
|
4507
|
+
providers: [...new Set(resolved.map(report => report.provider))].sort(),
|
|
4508
|
+
},
|
|
4509
|
+
});
|
|
4510
|
+
}
|
|
4511
|
+
return resolved;
|
|
4512
|
+
} finally {
|
|
4513
|
+
if (leaseOwned) this.#releaseUsageFetchLease(cacheKey);
|
|
4398
4514
|
}
|
|
4399
|
-
return resolved;
|
|
4400
4515
|
})().finally(() => {
|
|
4401
4516
|
if (this.#usageReportsInFlight.get(cacheKey) === promise) {
|
|
4402
4517
|
this.#usageReportsInFlight.delete(cacheKey);
|
|
@@ -6669,6 +6784,8 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
6669
6784
|
#getCacheStmt: Statement;
|
|
6670
6785
|
#getCacheIncludingExpiredStmt: Statement;
|
|
6671
6786
|
#upsertCacheStmt: Statement;
|
|
6787
|
+
#claimUsageFetchLeaseStmt: Statement;
|
|
6788
|
+
#releaseUsageFetchLeaseStmt: Statement;
|
|
6672
6789
|
#deleteCachePrefixStmt: Statement;
|
|
6673
6790
|
#deleteExpiredCacheStmt: Statement;
|
|
6674
6791
|
#closed = false;
|
|
@@ -6718,6 +6835,10 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
6718
6835
|
this.#upsertCacheStmt = this.#db.prepare(
|
|
6719
6836
|
"INSERT INTO cache (key, value, expires_at) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, expires_at = excluded.expires_at",
|
|
6720
6837
|
);
|
|
6838
|
+
this.#claimUsageFetchLeaseStmt = this.#db.prepare(
|
|
6839
|
+
"INSERT INTO cache (key, value, expires_at) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, expires_at = excluded.expires_at WHERE cache.expires_at <= ?",
|
|
6840
|
+
);
|
|
6841
|
+
this.#releaseUsageFetchLeaseStmt = this.#db.prepare("DELETE FROM cache WHERE key = ? AND value = ?");
|
|
6721
6842
|
this.#deleteCachePrefixStmt = this.#db.prepare("DELETE FROM cache WHERE substr(key, 1, ?) = ?");
|
|
6722
6843
|
this.#deleteExpiredCacheStmt = this.#db.prepare(`DELETE FROM cache WHERE expires_at <= ${SQLITE_NOW_EPOCH}`);
|
|
6723
6844
|
}
|
|
@@ -7431,6 +7552,27 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
7431
7552
|
}
|
|
7432
7553
|
}
|
|
7433
7554
|
|
|
7555
|
+
tryAcquireUsageFetchLease(key: string, owner: string, nowMs: number, leaseMs: number): boolean | undefined {
|
|
7556
|
+
try {
|
|
7557
|
+
const nowSec = Math.floor(nowMs / 1000);
|
|
7558
|
+
const expiresAtSec = Math.ceil((nowMs + leaseMs) / 1000);
|
|
7559
|
+
const result = this.#claimUsageFetchLeaseStmt.run(`usage_fetch_lease:${key}`, owner, expiresAtSec, nowSec) as {
|
|
7560
|
+
changes: number;
|
|
7561
|
+
};
|
|
7562
|
+
return result.changes === 1;
|
|
7563
|
+
} catch {
|
|
7564
|
+
return undefined;
|
|
7565
|
+
}
|
|
7566
|
+
}
|
|
7567
|
+
|
|
7568
|
+
releaseUsageFetchLease(key: string, owner: string): void {
|
|
7569
|
+
try {
|
|
7570
|
+
this.#releaseUsageFetchLeaseStmt.run(`usage_fetch_lease:${key}`, owner);
|
|
7571
|
+
} catch {
|
|
7572
|
+
// Ignore cache lease cleanup failures; the bounded expiry recovers it.
|
|
7573
|
+
}
|
|
7574
|
+
}
|
|
7575
|
+
|
|
7434
7576
|
allocateMonotonicSequence(key: string, expiresAtSec: number): number {
|
|
7435
7577
|
const allocate = this.#db.transaction(() => {
|
|
7436
7578
|
const row = this.#getCacheIncludingExpiredStmt.get(key) as { value?: string } | undefined;
|
package/src/model-pricing.ts
CHANGED
|
@@ -25,9 +25,31 @@ const GPT_6_ASTRA_PRICING: TieredPricing = {
|
|
|
25
25
|
},
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
// GPT-6 Sol: $2/$10 standard, cache read $0.20, cache write $2.50; inputs past
|
|
29
|
+
// 272K apply 2x to input/cache and 1.5x to output.
|
|
30
|
+
const GPT_6_SOL_PRICING: TieredPricing = {
|
|
31
|
+
cost: { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 },
|
|
32
|
+
longContextPricing: {
|
|
33
|
+
threshold: LONG_CONTEXT_THRESHOLD,
|
|
34
|
+
cost: { input: 4, output: 15, cacheRead: 0.4, cacheWrite: 5 },
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// GPT-6 Luna: $0.10/$0.50 standard, cache read $0.01, cache write $0.125;
|
|
39
|
+
// inputs past 272K apply 2x to input/cache and 1.5x to output.
|
|
40
|
+
const GPT_6_LUNA_PRICING: TieredPricing = {
|
|
41
|
+
cost: { input: 0.1, output: 0.5, cacheRead: 0.01, cacheWrite: 0.125 },
|
|
42
|
+
longContextPricing: {
|
|
43
|
+
threshold: LONG_CONTEXT_THRESHOLD,
|
|
44
|
+
cost: { input: 0.2, output: 0.75, cacheRead: 0.02, cacheWrite: 0.25 },
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
28
48
|
// OpenAI Standard pricing: https://developers.openai.com/api/docs/pricing
|
|
29
49
|
const OPENAI_GPT_5_6_PRICING: ReadonlyMap<string, TieredPricing> = new Map([
|
|
30
50
|
["gpt-6-astra", GPT_6_ASTRA_PRICING],
|
|
51
|
+
["gpt-6-sol", GPT_6_SOL_PRICING],
|
|
52
|
+
["gpt-6-luna", GPT_6_LUNA_PRICING],
|
|
31
53
|
["gpt-5.6", GPT_5_6_SOL_PRICING],
|
|
32
54
|
["gpt-5.6-sol", GPT_5_6_SOL_PRICING],
|
|
33
55
|
[
|
package/src/model-thinking.ts
CHANGED
|
@@ -66,6 +66,17 @@ const GPT_5_6_PLUS_EFFORTS: readonly Effort[] = [Effort.Low, Effort.Medium, Effo
|
|
|
66
66
|
const GPT_5_5_DEFAULT_EFFORT = Effort.XHigh;
|
|
67
67
|
const KIMI_K3_EFFORTS: readonly Effort[] = [Effort.Low, Effort.High, Effort.Max];
|
|
68
68
|
const DEEPSEEK_V4_FLASH_0731_EFFORTS: readonly Effort[] = [Effort.Low, Effort.High, Effort.Max];
|
|
69
|
+
const ALIBABA_GLM_53_EFFORTS: readonly Effort[] = [Effort.Low, Effort.High, Effort.Max];
|
|
70
|
+
const ALIBABA_DEEPSEEK_V4_PINNED_IDS = new Set([
|
|
71
|
+
"deepseek-v4-flash-0731",
|
|
72
|
+
"deepseek-v4-pro-0813",
|
|
73
|
+
"deepseek-v4.1-flash",
|
|
74
|
+
]);
|
|
75
|
+
const ALIBABA_DEEPSEEK_V4_PINNED_NAMES: Record<string, string> = {
|
|
76
|
+
"deepseek-v4-flash-0731": "DeepSeek V4 Flash 0731",
|
|
77
|
+
"deepseek-v4-pro-0813": "DeepSeek V4 Pro 0813",
|
|
78
|
+
"deepseek-v4.1-flash": "DeepSeek V4.1 Flash",
|
|
79
|
+
};
|
|
69
80
|
const GROK_4_5_EFFORTS: readonly Effort[] = [Effort.Low, Effort.Medium, Effort.High];
|
|
70
81
|
const GROK_4_6_EFFORTS: readonly Effort[] = [Effort.Low, Effort.Medium, Effort.High, Effort.XHigh];
|
|
71
82
|
const GROK_4_20_EFFORTS: readonly Effort[] = [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High];
|
|
@@ -320,9 +331,13 @@ export function applyGeneratedModelPolicies(models: ApiModel<Api>[]): void {
|
|
|
320
331
|
if (source.provider === "xai" && (source.id === "grok-4.5" || source.id === "grok-4.6")) {
|
|
321
332
|
source.reasoning = true;
|
|
322
333
|
}
|
|
323
|
-
if (source.provider === "alibaba-token-plan" && source.id
|
|
334
|
+
if (source.provider === "alibaba-token-plan" && ALIBABA_DEEPSEEK_V4_PINNED_IDS.has(source.id)) {
|
|
335
|
+
source.reasoning = true;
|
|
336
|
+
source.name = ALIBABA_DEEPSEEK_V4_PINNED_NAMES[source.id] ?? source.name;
|
|
337
|
+
}
|
|
338
|
+
if (source.provider === "alibaba-token-plan" && source.id === "glm-5.3") {
|
|
324
339
|
source.reasoning = true;
|
|
325
|
-
source.name = "
|
|
340
|
+
source.name = "GLM-5.3";
|
|
326
341
|
}
|
|
327
342
|
if (source.id.split("/").at(-1)?.toLowerCase() === "muse-spark-1.2") {
|
|
328
343
|
source.reasoning = true;
|
|
@@ -669,7 +684,7 @@ function applyGeneratedModelPolicy(model: ApiModel<Api>): void {
|
|
|
669
684
|
levels: [Effort.Low, Effort.High, Effort.Max],
|
|
670
685
|
};
|
|
671
686
|
}
|
|
672
|
-
if (model.provider === "alibaba-token-plan" && model.id
|
|
687
|
+
if (model.provider === "alibaba-token-plan" && ALIBABA_DEEPSEEK_V4_PINNED_IDS.has(model.id)) {
|
|
673
688
|
model.contextWindow = 1_000_000;
|
|
674
689
|
model.maxTokens = 384_000;
|
|
675
690
|
model.compat = {
|
|
@@ -680,6 +695,24 @@ function applyGeneratedModelPolicy(model: ApiModel<Api>): void {
|
|
|
680
695
|
requiresReasoningContentForToolCalls: true,
|
|
681
696
|
};
|
|
682
697
|
}
|
|
698
|
+
if (model.provider === "alibaba-token-plan" && model.id === "glm-5.3") {
|
|
699
|
+
model.contextWindow = 1_000_000;
|
|
700
|
+
model.maxTokens = 131_072;
|
|
701
|
+
model.compat = {
|
|
702
|
+
...(model.compat ?? {}),
|
|
703
|
+
supportsDeveloperRole: false,
|
|
704
|
+
supportsReasoningEffort: true,
|
|
705
|
+
reasoningContentField: "reasoning_content",
|
|
706
|
+
requiresReasoningContentForToolCalls: true,
|
|
707
|
+
};
|
|
708
|
+
model.thinking = {
|
|
709
|
+
mode: "effort",
|
|
710
|
+
minLevel: Effort.Low,
|
|
711
|
+
maxLevel: Effort.Max,
|
|
712
|
+
defaultLevel: Effort.Max,
|
|
713
|
+
levels: [Effort.Low, Effort.High, Effort.Max],
|
|
714
|
+
};
|
|
715
|
+
}
|
|
683
716
|
if (model.provider === "xai" && (model.id === "grok-4.5" || model.id === "grok-4.6")) {
|
|
684
717
|
model.maxTokens = Math.min(model.maxTokens, 64_000);
|
|
685
718
|
}
|
|
@@ -898,9 +931,12 @@ function inferSupportedEfforts<TApi extends Api>(parsedModel: ParsedModel, model
|
|
|
898
931
|
if (model.provider === "kimi-code" && model.id === "k3") {
|
|
899
932
|
return KIMI_K3_EFFORTS;
|
|
900
933
|
}
|
|
901
|
-
if (model.provider === "alibaba-token-plan" && model.id
|
|
934
|
+
if (model.provider === "alibaba-token-plan" && ALIBABA_DEEPSEEK_V4_PINNED_IDS.has(model.id)) {
|
|
902
935
|
return DEEPSEEK_V4_FLASH_0731_EFFORTS;
|
|
903
936
|
}
|
|
937
|
+
if (model.provider === "alibaba-token-plan" && model.id === "glm-5.3") {
|
|
938
|
+
return ALIBABA_GLM_53_EFFORTS;
|
|
939
|
+
}
|
|
904
940
|
switch (parsedModel.family) {
|
|
905
941
|
case "openai":
|
|
906
942
|
return inferOpenAISupportedEfforts(parsedModel);
|