@omnicross/core 0.1.8 → 0.1.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/dist/auth/GeminiCodeAssistProjectResolver.cjs +5 -3
- package/dist/auth/GeminiCodeAssistProjectResolver.js +4 -2
- package/dist/{chunk-N5T4M4GU.js → chunk-2UTGX6FM.js} +2 -2
- package/dist/{chunk-32O35CIF.js → chunk-45FHEOKW.js} +22 -7
- package/dist/{chunk-LVUCAWMK.js → chunk-55FMGZ3L.js} +13 -5
- package/dist/{chunk-OF3BECU2.cjs → chunk-56QD3YW6.cjs} +18 -5
- package/dist/{chunk-V4TXMEJS.cjs → chunk-5LNYGCIW.cjs} +15 -7
- package/dist/{chunk-EPN67EGP.cjs → chunk-5UK3KLE2.cjs} +9 -1
- package/dist/chunk-6D4W22P4.js +150 -0
- package/dist/chunk-77USGNRW.cjs +37 -0
- package/dist/chunk-AOFWNWLT.cjs +203 -0
- package/dist/{chunk-UAPBLNN2.js → chunk-AQDON6MB.js} +8 -0
- package/dist/{chunk-LB6EBS6Y.js → chunk-CCVRRJOX.js} +17 -4
- package/dist/{chunk-OVQBNVSV.cjs → chunk-CMVXX7ON.cjs} +42 -27
- package/dist/{chunk-QYC54PMG.js → chunk-FEBAQI5A.js} +255 -92
- package/dist/{chunk-7C7DET6S.js → chunk-GXEZ2R3E.js} +156 -1
- package/dist/{chunk-PEGTIPB3.js → chunk-HKB656B4.js} +37 -43
- package/dist/chunk-N2EIM2XJ.js +203 -0
- package/dist/{chunk-MYYR3HZL.js → chunk-NGYOO5TO.js} +5 -5
- package/dist/{chunk-OKMJLEXX.cjs → chunk-OC6DOOFX.cjs} +5 -5
- package/dist/{chunk-2Z4XM3H5.cjs → chunk-P7EM6BND.cjs} +10 -10
- package/dist/chunk-QUSEZNYS.cjs +150 -0
- package/dist/{chunk-KBM2W6UY.cjs → chunk-RICPANBY.cjs} +41 -24
- package/dist/{chunk-TOVPMMHM.js → chunk-S3DYFEBE.js} +37 -20
- package/dist/{chunk-26K6T2YS.cjs → chunk-TIT74RIL.cjs} +407 -244
- package/dist/{chunk-XPIOJQS2.cjs → chunk-UWDW6PN3.cjs} +159 -4
- package/dist/chunk-WYVGULTQ.js +37 -0
- package/dist/{chunk-OXHNSZ53.cjs → chunk-ZQQUD66M.cjs} +54 -60
- package/dist/completion/CompletionService.cjs +15 -13
- package/dist/completion/CompletionService.js +14 -12
- package/dist/completion.cjs +15 -13
- package/dist/completion.js +14 -12
- package/dist/index.cjs +15 -13
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +14 -12
- package/dist/outbound-api/auditCapture.cjs +2 -2
- package/dist/outbound-api/auditCapture.d.cts +6 -0
- package/dist/outbound-api/auditCapture.d.ts +6 -0
- package/dist/outbound-api/auditCapture.js +1 -1
- package/dist/outbound-api/routeResolver.d.cts +1 -1
- package/dist/outbound-api/routeResolver.d.ts +1 -1
- package/dist/outbound-api/types.d.cts +1 -1
- package/dist/outbound-api/types.d.ts +1 -1
- package/dist/outbound-api.cjs +15 -13
- package/dist/outbound-api.d.cts +3 -2
- package/dist/outbound-api.d.ts +3 -2
- package/dist/outbound-api.js +14 -12
- package/dist/pipeline/AccountAllowanceScheduling.d.cts +1 -1
- package/dist/pipeline/AccountAllowanceScheduling.d.ts +1 -1
- package/dist/pipeline/executeProviderCall.d.cts +2 -0
- package/dist/pipeline/executeProviderCall.d.ts +2 -0
- package/dist/pipeline/resolveProviderChain.d.cts +1 -0
- package/dist/pipeline/resolveProviderChain.d.ts +1 -0
- package/dist/pipeline/resolveSubscriptionChain.d.cts +2 -0
- package/dist/pipeline/resolveSubscriptionChain.d.ts +2 -0
- package/dist/ports/provider-config-source.d.cts +1 -0
- package/dist/ports/provider-config-source.d.ts +1 -0
- package/dist/ports.d.cts +1 -1
- package/dist/ports.d.ts +1 -1
- package/dist/provider-proxy/ProviderProxy.cjs +15 -13
- package/dist/provider-proxy/ProviderProxy.js +14 -12
- package/dist/provider-proxy/ingress/providerProxyShared.cjs +15 -13
- package/dist/provider-proxy/ingress/providerProxyShared.js +14 -12
- package/dist/provider-proxy.cjs +15 -13
- package/dist/provider-proxy.js +14 -12
- package/dist/transformer/TransformerChainExecutor.d.cts +2 -0
- package/dist/transformer/TransformerChainExecutor.d.ts +2 -0
- package/dist/transformer/TransformerService.d.cts +2 -0
- package/dist/transformer/TransformerService.d.ts +2 -0
- package/dist/transformer/transformers/AnthropicTransformer.cjs +4 -2
- package/dist/transformer/transformers/AnthropicTransformer.d.cts +7 -2
- package/dist/transformer/transformers/AnthropicTransformer.d.ts +7 -2
- package/dist/transformer/transformers/AnthropicTransformer.js +3 -1
- package/dist/transformer/transformers/GeminiCodeAssistTransformer.cjs +5 -3
- package/dist/transformer/transformers/GeminiCodeAssistTransformer.d.cts +2 -0
- package/dist/transformer/transformers/GeminiCodeAssistTransformer.d.ts +2 -0
- package/dist/transformer/transformers/GeminiCodeAssistTransformer.js +4 -2
- package/dist/transformer/transformers/GeminiTransformer.cjs +5 -3
- package/dist/transformer/transformers/GeminiTransformer.d.cts +4 -2
- package/dist/transformer/transformers/GeminiTransformer.d.ts +4 -2
- package/dist/transformer/transformers/GeminiTransformer.js +4 -2
- package/dist/transformer/transformers/OpenAIResponseTransformer.cjs +3 -2
- package/dist/transformer/transformers/OpenAIResponseTransformer.d.cts +4 -2
- package/dist/transformer/transformers/OpenAIResponseTransformer.d.ts +4 -2
- package/dist/transformer/transformers/OpenAIResponseTransformer.js +2 -1
- package/dist/transformer/transformers/OpenAITransformer.cjs +3 -2
- package/dist/transformer/transformers/OpenAITransformer.d.cts +3 -1
- package/dist/transformer/transformers/OpenAITransformer.d.ts +3 -1
- package/dist/transformer/transformers/OpenAITransformer.js +2 -1
- package/dist/transformer/transformers.cjs +10 -8
- package/dist/transformer/transformers.d.cts +2 -0
- package/dist/transformer/transformers.d.ts +2 -0
- package/dist/transformer/transformers.js +9 -7
- package/dist/transformer/types.d.cts +16 -5
- package/dist/transformer/types.d.ts +16 -5
- package/dist/transformer.cjs +28 -9
- package/dist/transformer.d.cts +76 -1
- package/dist/transformer.d.ts +76 -1
- package/dist/transformer.js +28 -9
- package/dist/usage/usage-recorder.cjs +2 -2
- package/dist/usage/usage-recorder.d.cts +12 -2
- package/dist/usage/usage-recorder.d.ts +12 -2
- package/dist/usage/usage-recorder.js +1 -1
- package/dist/usage.cjs +20 -3
- package/dist/usage.d.cts +149 -0
- package/dist/usage.d.ts +149 -0
- package/dist/usage.js +20 -3
- package/package.json +2 -2
- package/dist/chunk-7VU7V2E4.js +0 -0
- package/dist/chunk-FZ46M34D.cjs +0 -1
- /package/dist/{chunk-EYZYXJTJ.cjs → chunk-QVOB2DNX.cjs} +0 -0
- /package/dist/{chunk-46RLHI7D.js → chunk-WTQMX2Q6.js} +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { UsageCacheKeySource, UsageDateRange, UsageTotals, ModelUsageRow, ApiKeyUsageRow, UsageTimeBucket, UsageTimeSeriesBucket, MessageUsageRow, SessionCacheStats } from '@omnicross/contracts/usage-stats-types';
|
|
1
|
+
import { UsageCacheKeySource, UsageEventInput, UsageDateRange, UsageTotals, ModelUsageRow, ApiKeyUsageRow, UsageTimeBucket, UsageTimeSeriesBucket, MessageUsageRow, SessionCacheStats } from '@omnicross/contracts/usage-stats-types';
|
|
3
2
|
import { UsageEngineOrigin, UsageTokens } from '@omnicross/contracts/usage-types';
|
|
4
3
|
import { L as Logger } from '../logger-4GvQNzhE.js';
|
|
5
4
|
import { UsageEventStore } from '../ports/usage-event-store.js';
|
|
5
|
+
import { PricingEngine } from './pricing-engine.js';
|
|
6
6
|
import '@omnicross/contracts/pricing-types';
|
|
7
7
|
import '../ports/pricing-store.js';
|
|
8
8
|
|
|
@@ -69,6 +69,15 @@ interface UsageRecorderOptions {
|
|
|
69
69
|
* Absent ⇒ no-op (byte-identical for hosts/tests that do not wire it).
|
|
70
70
|
*/
|
|
71
71
|
onRecord?: (apiKeyId: string, costUsd: number, now: number) => void;
|
|
72
|
+
/**
|
|
73
|
+
* OPTIONAL per-request hook (live-throughput). Called synchronously with the
|
|
74
|
+
* FINISHED row, immediately before the store insert, for EVERY recorded
|
|
75
|
+
* request — attributed or not. Distinct from `onRecord`, which is gated on
|
|
76
|
+
* `apiKeyId` and carries only the cost: a rate metric must see subscription-
|
|
77
|
+
* account traffic too, and needs the token counts. Absent ⇒ no-op
|
|
78
|
+
* (byte-identical for hosts/tests that do not wire it).
|
|
79
|
+
*/
|
|
80
|
+
onEvent?: (row: UsageEventInput, at: number) => void;
|
|
72
81
|
}
|
|
73
82
|
declare class UsageRecorder {
|
|
74
83
|
private store;
|
|
@@ -76,6 +85,7 @@ declare class UsageRecorder {
|
|
|
76
85
|
private logger;
|
|
77
86
|
private defer;
|
|
78
87
|
private onRecord?;
|
|
88
|
+
private onEvent?;
|
|
79
89
|
constructor(store: UsageEventStore, pricing: PricingEngine, logger: Logger, options?: UsageRecorderOptions);
|
|
80
90
|
/**
|
|
81
91
|
* Record one LLM request. Returns immediately — the actual store insert is
|
package/dist/usage.cjs
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
var _chunkQUSEZNYScjs = require('./chunk-QUSEZNYS.cjs');
|
|
2
11
|
|
|
3
12
|
|
|
4
13
|
var _chunk2E42SAW3cjs = require('./chunk-2E42SAW3.cjs');
|
|
5
14
|
|
|
6
15
|
|
|
7
|
-
var
|
|
16
|
+
var _chunk5UK3KLE2cjs = require('./chunk-5UK3KLE2.cjs');
|
|
8
17
|
require('./chunk-XSVNC2UD.cjs');
|
|
9
18
|
|
|
10
19
|
|
|
11
20
|
|
|
12
|
-
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
exports.PricingEngine = _chunk2E42SAW3cjs.PricingEngine; exports.THROUGHPUT_BUCKET_COUNT = _chunkQUSEZNYScjs.THROUGHPUT_BUCKET_COUNT; exports.THROUGHPUT_RETENTION_MS = _chunkQUSEZNYScjs.THROUGHPUT_RETENTION_MS; exports.THROUGHPUT_SAMPLE_LIMIT = _chunkQUSEZNYScjs.THROUGHPUT_SAMPLE_LIMIT; exports.THROUGHPUT_WINDOWS_MS = _chunkQUSEZNYScjs.THROUGHPUT_WINDOWS_MS; exports.UsageRecorder = _chunk5UK3KLE2cjs.UsageRecorder; exports.UsageThroughputTracker = _chunkQUSEZNYScjs.UsageThroughputTracker; exports.__resetSharedUsageThroughputTrackerForTests = _chunkQUSEZNYScjs.__resetSharedUsageThroughputTrackerForTests; exports.getSharedUsageThroughputTracker = _chunkQUSEZNYScjs.getSharedUsageThroughputTracker; exports.setSharedUsageThroughputTracker = _chunkQUSEZNYScjs.setSharedUsageThroughputTracker;
|
package/dist/usage.d.cts
CHANGED
|
@@ -6,3 +6,152 @@ import './logger-4GvQNzhE.cjs';
|
|
|
6
6
|
import './ports/pricing-store.cjs';
|
|
7
7
|
import '@omnicross/contracts/usage-stats-types';
|
|
8
8
|
import './ports/usage-event-store.cjs';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* throughputTracker — a bounded, in-memory sliding window of recently recorded
|
|
12
|
+
* usage events, answering "how fast is the gateway serving RIGHT NOW".
|
|
13
|
+
*
|
|
14
|
+
* WHY a dedicated in-memory store (not a `UsageEventStore` range query): the
|
|
15
|
+
* daemon's `JsonlUsageEventStore.getTotals()` reads and parses the ENTIRE
|
|
16
|
+
* `usage-events.jsonl` synchronously on every call (a deliberate simplification
|
|
17
|
+
* documented there). That is fine for an on-demand stats page, but a live rate
|
|
18
|
+
* widget polls every few seconds, and a periodic synchronous full-file scan
|
|
19
|
+
* would stall the same event loop that is relaying gateway requests. This
|
|
20
|
+
* tracker is fed O(1) per recorded request from `UsageRecorder`'s `onEvent`
|
|
21
|
+
* hook and read in O(samples-in-window) — it never touches disk.
|
|
22
|
+
*
|
|
23
|
+
* Shaped like `pipeline/ServerOverloadCounter`: process-local, cleared on
|
|
24
|
+
* restart, an injectable `now` clock, no external deps. It carries ONLY token
|
|
25
|
+
* counts, costs, and epoch timestamps — never a prompt, header, model secret,
|
|
26
|
+
* or key id — so no snapshot of it can leak one.
|
|
27
|
+
*
|
|
28
|
+
* TWO MEASUREMENT DECISIONS worth stating explicitly, because both are easy to
|
|
29
|
+
* get subtly wrong:
|
|
30
|
+
*
|
|
31
|
+
* 1. `totalTokens` deliberately EXCLUDES `reasoningTokens`. Providers report
|
|
32
|
+
* reasoning tokens as a SUBSET of the completion/output tokens, so adding
|
|
33
|
+
* them would double-count. They are still reported separately.
|
|
34
|
+
*
|
|
35
|
+
* 2. Rates always divide by the FULL requested window, never by "how long this
|
|
36
|
+
* process has been up". A daemon that was down served no traffic, so the
|
|
37
|
+
* honest rate over that wall-clock window really is lower; after a restart
|
|
38
|
+
* the number climbs back from zero on its own. `startedAt` is reported so a
|
|
39
|
+
* reader can say "this process started less than a window ago" without the
|
|
40
|
+
* rate itself being fudged.
|
|
41
|
+
*
|
|
42
|
+
* @module usage/throughputTracker
|
|
43
|
+
*/
|
|
44
|
+
/** Longest window a snapshot can answer; also the sample-retention horizon. */
|
|
45
|
+
declare const THROUGHPUT_RETENTION_MS: number;
|
|
46
|
+
/**
|
|
47
|
+
* Hard memory ceiling on retained samples (~20k x a few dozen bytes). Reaching
|
|
48
|
+
* it means sustained >22 req/s for a full 15 minutes; past that the OLDEST
|
|
49
|
+
* samples are evicted and the affected windows report `complete: false` rather
|
|
50
|
+
* than quietly under-reporting.
|
|
51
|
+
*/
|
|
52
|
+
declare const THROUGHPUT_SAMPLE_LIMIT = 20000;
|
|
53
|
+
/** Windows every snapshot reports, shortest first. The last must be <= retention. */
|
|
54
|
+
declare const THROUGHPUT_WINDOWS_MS: readonly [60000, 300000, 900000];
|
|
55
|
+
/** Trend-series resolution: 30 buckets across the retention horizon (30s each). */
|
|
56
|
+
declare const THROUGHPUT_BUCKET_COUNT = 30;
|
|
57
|
+
/**
|
|
58
|
+
* One recorded request's contribution. Structurally satisfied by the recorder's
|
|
59
|
+
* `UsageEventInput` row, so the daemon can forward it verbatim.
|
|
60
|
+
*/
|
|
61
|
+
interface UsageThroughputInput {
|
|
62
|
+
inputTokens: number;
|
|
63
|
+
outputTokens: number;
|
|
64
|
+
cacheReadTokens: number;
|
|
65
|
+
cacheCreationTokens: number;
|
|
66
|
+
reasoningTokens: number;
|
|
67
|
+
costUsd: number;
|
|
68
|
+
/** Epoch-ms of the event. Omit to use the `now` argument. */
|
|
69
|
+
ts?: number;
|
|
70
|
+
}
|
|
71
|
+
/** Aggregates + rates for one trailing window. */
|
|
72
|
+
interface UsageThroughputWindow {
|
|
73
|
+
/** Trailing window this row covers, in ms. */
|
|
74
|
+
windowMs: number;
|
|
75
|
+
requests: number;
|
|
76
|
+
inputTokens: number;
|
|
77
|
+
outputTokens: number;
|
|
78
|
+
cacheReadTokens: number;
|
|
79
|
+
cacheCreationTokens: number;
|
|
80
|
+
reasoningTokens: number;
|
|
81
|
+
/** input + output + cacheRead + cacheCreation (reasoning is a subset of output). */
|
|
82
|
+
totalTokens: number;
|
|
83
|
+
costUsd: number;
|
|
84
|
+
requestsPerMinute: number;
|
|
85
|
+
/** The headline TPM: `totalTokens` per minute over `windowMs`. */
|
|
86
|
+
tokensPerMinute: number;
|
|
87
|
+
inputTokensPerMinute: number;
|
|
88
|
+
outputTokensPerMinute: number;
|
|
89
|
+
costUsdPerMinute: number;
|
|
90
|
+
/**
|
|
91
|
+
* False when the sample cap evicted events that fall inside this window, so
|
|
92
|
+
* every figure above is a LOWER BOUND rather than an exact count.
|
|
93
|
+
*/
|
|
94
|
+
complete: boolean;
|
|
95
|
+
}
|
|
96
|
+
/** One fixed-width bucket of the trend series. */
|
|
97
|
+
interface UsageThroughputBucket {
|
|
98
|
+
/** Epoch-ms of the bucket's (grid-aligned) start. */
|
|
99
|
+
startTs: number;
|
|
100
|
+
requests: number;
|
|
101
|
+
tokens: number;
|
|
102
|
+
}
|
|
103
|
+
/** The `GET /admin/api/usage/throughput` payload. */
|
|
104
|
+
interface UsageThroughputSnapshot {
|
|
105
|
+
/** Always true here; the UI's fallback shape uses `false` for older daemons. */
|
|
106
|
+
available: true;
|
|
107
|
+
collectedAt: number;
|
|
108
|
+
/** Epoch-ms this tracker began observing (effectively the daemon's start). */
|
|
109
|
+
startedAt: number;
|
|
110
|
+
retentionMs: number;
|
|
111
|
+
/** Width of one `buckets` entry, in ms. */
|
|
112
|
+
bucketMs: number;
|
|
113
|
+
/** One row per {@link THROUGHPUT_WINDOWS_MS} entry, shortest window first. */
|
|
114
|
+
windows: UsageThroughputWindow[];
|
|
115
|
+
/**
|
|
116
|
+
* Oldest to newest, exactly {@link THROUGHPUT_BUCKET_COUNT} entries covering
|
|
117
|
+
* `retentionMs` on a `bucketMs` grid. Empty buckets are present and zeroed;
|
|
118
|
+
* the final bucket is still in progress. Grid alignment (rather than
|
|
119
|
+
* "now minus retention") keeps boundaries stable between polls so a sparkline
|
|
120
|
+
* does not shimmer.
|
|
121
|
+
*/
|
|
122
|
+
buckets: UsageThroughputBucket[];
|
|
123
|
+
}
|
|
124
|
+
declare class UsageThroughputTracker {
|
|
125
|
+
private samples;
|
|
126
|
+
/** Index of the oldest live sample; slots before it are consumed. */
|
|
127
|
+
private head;
|
|
128
|
+
private readonly startedAt;
|
|
129
|
+
/** Newest ts dropped by the sample CAP (not by retention). Null when none. */
|
|
130
|
+
private evictedThroughTs;
|
|
131
|
+
constructor(now?: number);
|
|
132
|
+
/** Record one served request. O(1) amortised; never touches disk. */
|
|
133
|
+
record(input: UsageThroughputInput, now?: number): void;
|
|
134
|
+
/** Aggregate every reported window plus the shared trend series. */
|
|
135
|
+
snapshot(now?: number): UsageThroughputSnapshot;
|
|
136
|
+
/** Drop all samples (tests / teardown). `startedAt` is unchanged. */
|
|
137
|
+
clear(): void;
|
|
138
|
+
/**
|
|
139
|
+
* Drop samples past the retention horizon, then enforce the sample cap.
|
|
140
|
+
*
|
|
141
|
+
* Retention pruning uses the CALLER's `now`, which is never later than a
|
|
142
|
+
* subsequent snapshot's `now`, so it can only ever keep MORE than the widest
|
|
143
|
+
* window needs — it can never shave data off the 15-minute window.
|
|
144
|
+
*/
|
|
145
|
+
private prune;
|
|
146
|
+
private window;
|
|
147
|
+
private buckets;
|
|
148
|
+
}
|
|
149
|
+
declare function getSharedUsageThroughputTracker(): UsageThroughputTracker;
|
|
150
|
+
/**
|
|
151
|
+
* TEST SEAM — swap the singleton (e.g. with a fixed-clock instance). Mirrors the
|
|
152
|
+
* `setShared…`/`__reset…ForTests` discipline of the sibling in-memory stores.
|
|
153
|
+
*/
|
|
154
|
+
declare function setSharedUsageThroughputTracker(instance: UsageThroughputTracker): void;
|
|
155
|
+
declare function __resetSharedUsageThroughputTrackerForTests(): void;
|
|
156
|
+
|
|
157
|
+
export { THROUGHPUT_BUCKET_COUNT, THROUGHPUT_RETENTION_MS, THROUGHPUT_SAMPLE_LIMIT, THROUGHPUT_WINDOWS_MS, type UsageThroughputBucket, type UsageThroughputInput, type UsageThroughputSnapshot, UsageThroughputTracker, type UsageThroughputWindow, __resetSharedUsageThroughputTrackerForTests, getSharedUsageThroughputTracker, setSharedUsageThroughputTracker };
|
package/dist/usage.d.ts
CHANGED
|
@@ -6,3 +6,152 @@ import './logger-4GvQNzhE.js';
|
|
|
6
6
|
import './ports/pricing-store.js';
|
|
7
7
|
import '@omnicross/contracts/usage-stats-types';
|
|
8
8
|
import './ports/usage-event-store.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* throughputTracker — a bounded, in-memory sliding window of recently recorded
|
|
12
|
+
* usage events, answering "how fast is the gateway serving RIGHT NOW".
|
|
13
|
+
*
|
|
14
|
+
* WHY a dedicated in-memory store (not a `UsageEventStore` range query): the
|
|
15
|
+
* daemon's `JsonlUsageEventStore.getTotals()` reads and parses the ENTIRE
|
|
16
|
+
* `usage-events.jsonl` synchronously on every call (a deliberate simplification
|
|
17
|
+
* documented there). That is fine for an on-demand stats page, but a live rate
|
|
18
|
+
* widget polls every few seconds, and a periodic synchronous full-file scan
|
|
19
|
+
* would stall the same event loop that is relaying gateway requests. This
|
|
20
|
+
* tracker is fed O(1) per recorded request from `UsageRecorder`'s `onEvent`
|
|
21
|
+
* hook and read in O(samples-in-window) — it never touches disk.
|
|
22
|
+
*
|
|
23
|
+
* Shaped like `pipeline/ServerOverloadCounter`: process-local, cleared on
|
|
24
|
+
* restart, an injectable `now` clock, no external deps. It carries ONLY token
|
|
25
|
+
* counts, costs, and epoch timestamps — never a prompt, header, model secret,
|
|
26
|
+
* or key id — so no snapshot of it can leak one.
|
|
27
|
+
*
|
|
28
|
+
* TWO MEASUREMENT DECISIONS worth stating explicitly, because both are easy to
|
|
29
|
+
* get subtly wrong:
|
|
30
|
+
*
|
|
31
|
+
* 1. `totalTokens` deliberately EXCLUDES `reasoningTokens`. Providers report
|
|
32
|
+
* reasoning tokens as a SUBSET of the completion/output tokens, so adding
|
|
33
|
+
* them would double-count. They are still reported separately.
|
|
34
|
+
*
|
|
35
|
+
* 2. Rates always divide by the FULL requested window, never by "how long this
|
|
36
|
+
* process has been up". A daemon that was down served no traffic, so the
|
|
37
|
+
* honest rate over that wall-clock window really is lower; after a restart
|
|
38
|
+
* the number climbs back from zero on its own. `startedAt` is reported so a
|
|
39
|
+
* reader can say "this process started less than a window ago" without the
|
|
40
|
+
* rate itself being fudged.
|
|
41
|
+
*
|
|
42
|
+
* @module usage/throughputTracker
|
|
43
|
+
*/
|
|
44
|
+
/** Longest window a snapshot can answer; also the sample-retention horizon. */
|
|
45
|
+
declare const THROUGHPUT_RETENTION_MS: number;
|
|
46
|
+
/**
|
|
47
|
+
* Hard memory ceiling on retained samples (~20k x a few dozen bytes). Reaching
|
|
48
|
+
* it means sustained >22 req/s for a full 15 minutes; past that the OLDEST
|
|
49
|
+
* samples are evicted and the affected windows report `complete: false` rather
|
|
50
|
+
* than quietly under-reporting.
|
|
51
|
+
*/
|
|
52
|
+
declare const THROUGHPUT_SAMPLE_LIMIT = 20000;
|
|
53
|
+
/** Windows every snapshot reports, shortest first. The last must be <= retention. */
|
|
54
|
+
declare const THROUGHPUT_WINDOWS_MS: readonly [60000, 300000, 900000];
|
|
55
|
+
/** Trend-series resolution: 30 buckets across the retention horizon (30s each). */
|
|
56
|
+
declare const THROUGHPUT_BUCKET_COUNT = 30;
|
|
57
|
+
/**
|
|
58
|
+
* One recorded request's contribution. Structurally satisfied by the recorder's
|
|
59
|
+
* `UsageEventInput` row, so the daemon can forward it verbatim.
|
|
60
|
+
*/
|
|
61
|
+
interface UsageThroughputInput {
|
|
62
|
+
inputTokens: number;
|
|
63
|
+
outputTokens: number;
|
|
64
|
+
cacheReadTokens: number;
|
|
65
|
+
cacheCreationTokens: number;
|
|
66
|
+
reasoningTokens: number;
|
|
67
|
+
costUsd: number;
|
|
68
|
+
/** Epoch-ms of the event. Omit to use the `now` argument. */
|
|
69
|
+
ts?: number;
|
|
70
|
+
}
|
|
71
|
+
/** Aggregates + rates for one trailing window. */
|
|
72
|
+
interface UsageThroughputWindow {
|
|
73
|
+
/** Trailing window this row covers, in ms. */
|
|
74
|
+
windowMs: number;
|
|
75
|
+
requests: number;
|
|
76
|
+
inputTokens: number;
|
|
77
|
+
outputTokens: number;
|
|
78
|
+
cacheReadTokens: number;
|
|
79
|
+
cacheCreationTokens: number;
|
|
80
|
+
reasoningTokens: number;
|
|
81
|
+
/** input + output + cacheRead + cacheCreation (reasoning is a subset of output). */
|
|
82
|
+
totalTokens: number;
|
|
83
|
+
costUsd: number;
|
|
84
|
+
requestsPerMinute: number;
|
|
85
|
+
/** The headline TPM: `totalTokens` per minute over `windowMs`. */
|
|
86
|
+
tokensPerMinute: number;
|
|
87
|
+
inputTokensPerMinute: number;
|
|
88
|
+
outputTokensPerMinute: number;
|
|
89
|
+
costUsdPerMinute: number;
|
|
90
|
+
/**
|
|
91
|
+
* False when the sample cap evicted events that fall inside this window, so
|
|
92
|
+
* every figure above is a LOWER BOUND rather than an exact count.
|
|
93
|
+
*/
|
|
94
|
+
complete: boolean;
|
|
95
|
+
}
|
|
96
|
+
/** One fixed-width bucket of the trend series. */
|
|
97
|
+
interface UsageThroughputBucket {
|
|
98
|
+
/** Epoch-ms of the bucket's (grid-aligned) start. */
|
|
99
|
+
startTs: number;
|
|
100
|
+
requests: number;
|
|
101
|
+
tokens: number;
|
|
102
|
+
}
|
|
103
|
+
/** The `GET /admin/api/usage/throughput` payload. */
|
|
104
|
+
interface UsageThroughputSnapshot {
|
|
105
|
+
/** Always true here; the UI's fallback shape uses `false` for older daemons. */
|
|
106
|
+
available: true;
|
|
107
|
+
collectedAt: number;
|
|
108
|
+
/** Epoch-ms this tracker began observing (effectively the daemon's start). */
|
|
109
|
+
startedAt: number;
|
|
110
|
+
retentionMs: number;
|
|
111
|
+
/** Width of one `buckets` entry, in ms. */
|
|
112
|
+
bucketMs: number;
|
|
113
|
+
/** One row per {@link THROUGHPUT_WINDOWS_MS} entry, shortest window first. */
|
|
114
|
+
windows: UsageThroughputWindow[];
|
|
115
|
+
/**
|
|
116
|
+
* Oldest to newest, exactly {@link THROUGHPUT_BUCKET_COUNT} entries covering
|
|
117
|
+
* `retentionMs` on a `bucketMs` grid. Empty buckets are present and zeroed;
|
|
118
|
+
* the final bucket is still in progress. Grid alignment (rather than
|
|
119
|
+
* "now minus retention") keeps boundaries stable between polls so a sparkline
|
|
120
|
+
* does not shimmer.
|
|
121
|
+
*/
|
|
122
|
+
buckets: UsageThroughputBucket[];
|
|
123
|
+
}
|
|
124
|
+
declare class UsageThroughputTracker {
|
|
125
|
+
private samples;
|
|
126
|
+
/** Index of the oldest live sample; slots before it are consumed. */
|
|
127
|
+
private head;
|
|
128
|
+
private readonly startedAt;
|
|
129
|
+
/** Newest ts dropped by the sample CAP (not by retention). Null when none. */
|
|
130
|
+
private evictedThroughTs;
|
|
131
|
+
constructor(now?: number);
|
|
132
|
+
/** Record one served request. O(1) amortised; never touches disk. */
|
|
133
|
+
record(input: UsageThroughputInput, now?: number): void;
|
|
134
|
+
/** Aggregate every reported window plus the shared trend series. */
|
|
135
|
+
snapshot(now?: number): UsageThroughputSnapshot;
|
|
136
|
+
/** Drop all samples (tests / teardown). `startedAt` is unchanged. */
|
|
137
|
+
clear(): void;
|
|
138
|
+
/**
|
|
139
|
+
* Drop samples past the retention horizon, then enforce the sample cap.
|
|
140
|
+
*
|
|
141
|
+
* Retention pruning uses the CALLER's `now`, which is never later than a
|
|
142
|
+
* subsequent snapshot's `now`, so it can only ever keep MORE than the widest
|
|
143
|
+
* window needs — it can never shave data off the 15-minute window.
|
|
144
|
+
*/
|
|
145
|
+
private prune;
|
|
146
|
+
private window;
|
|
147
|
+
private buckets;
|
|
148
|
+
}
|
|
149
|
+
declare function getSharedUsageThroughputTracker(): UsageThroughputTracker;
|
|
150
|
+
/**
|
|
151
|
+
* TEST SEAM — swap the singleton (e.g. with a fixed-clock instance). Mirrors the
|
|
152
|
+
* `setShared…`/`__reset…ForTests` discipline of the sibling in-memory stores.
|
|
153
|
+
*/
|
|
154
|
+
declare function setSharedUsageThroughputTracker(instance: UsageThroughputTracker): void;
|
|
155
|
+
declare function __resetSharedUsageThroughputTrackerForTests(): void;
|
|
156
|
+
|
|
157
|
+
export { THROUGHPUT_BUCKET_COUNT, THROUGHPUT_RETENTION_MS, THROUGHPUT_SAMPLE_LIMIT, THROUGHPUT_WINDOWS_MS, type UsageThroughputBucket, type UsageThroughputInput, type UsageThroughputSnapshot, UsageThroughputTracker, type UsageThroughputWindow, __resetSharedUsageThroughputTrackerForTests, getSharedUsageThroughputTracker, setSharedUsageThroughputTracker };
|
package/dist/usage.js
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
THROUGHPUT_BUCKET_COUNT,
|
|
3
|
+
THROUGHPUT_RETENTION_MS,
|
|
4
|
+
THROUGHPUT_SAMPLE_LIMIT,
|
|
5
|
+
THROUGHPUT_WINDOWS_MS,
|
|
6
|
+
UsageThroughputTracker,
|
|
7
|
+
__resetSharedUsageThroughputTrackerForTests,
|
|
8
|
+
getSharedUsageThroughputTracker,
|
|
9
|
+
setSharedUsageThroughputTracker
|
|
10
|
+
} from "./chunk-6D4W22P4.js";
|
|
2
11
|
import {
|
|
3
12
|
PricingEngine
|
|
4
13
|
} from "./chunk-FXR6N3SR.js";
|
|
5
14
|
import {
|
|
6
15
|
UsageRecorder
|
|
7
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-AQDON6MB.js";
|
|
8
17
|
import "./chunk-OZFM4X3S.js";
|
|
9
18
|
export {
|
|
10
19
|
PricingEngine,
|
|
11
|
-
|
|
20
|
+
THROUGHPUT_BUCKET_COUNT,
|
|
21
|
+
THROUGHPUT_RETENTION_MS,
|
|
22
|
+
THROUGHPUT_SAMPLE_LIMIT,
|
|
23
|
+
THROUGHPUT_WINDOWS_MS,
|
|
24
|
+
UsageRecorder,
|
|
25
|
+
UsageThroughputTracker,
|
|
26
|
+
__resetSharedUsageThroughputTrackerForTests,
|
|
27
|
+
getSharedUsageThroughputTracker,
|
|
28
|
+
setSharedUsageThroughputTracker
|
|
12
29
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnicross/core",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Omnicross LLM serving core
|
|
3
|
+
"version": "0.1.10",
|
|
4
|
+
"description": "Omnicross LLM serving core — provider dispatch, completion pipeline, transformers, and the provider proxy.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Sayo (https://github.com/Dumoedss)",
|
|
7
7
|
"homepage": "https://github.com/Dumoedss/omnicross#readme",
|
package/dist/chunk-7VU7V2E4.js
DELETED
|
File without changes
|
package/dist/chunk-FZ46M34D.cjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
File without changes
|
|
File without changes
|