@kunal_dutt/token-lens 0.2.17 → 0.2.19
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/GATEWAY.md +113 -5
- package/README.md +15 -4
- package/check-agent.mjs +5 -5
- package/gateway.mjs +66 -63
- package/package.json +1 -1
- package/sdk.mjs +461 -93
- package/server.mjs +27 -27
- package/token-lens.mcpb +0 -0
- package/types/src/gateway/config.d.ts +29 -1
- package/types/src/gateway/customers.d.ts +168 -0
- package/types/src/gateway/metrics.d.ts +2 -1
- package/types/src/gateway/runs.d.ts +6 -3
- package/types/src/gateway/shared.d.ts +5 -0
- package/types/src/gateway/storage.d.ts +7 -0
- package/types/src/sdk/index.d.ts +51 -0
package/token-lens.mcpb
CHANGED
|
Binary file
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* "env:NAME" is read from the environment variable NAME.
|
|
7
7
|
*/
|
|
8
8
|
import type { CustomModel, Level } from "../core";
|
|
9
|
+
import { type CustomersConfig } from "./customers";
|
|
9
10
|
import type { StorageConfig } from "./shared";
|
|
10
11
|
export type UpstreamType = "openai" | "anthropic";
|
|
11
12
|
export interface UpstreamConfig {
|
|
@@ -183,6 +184,16 @@ export interface GatewayConfig {
|
|
|
183
184
|
models: CustomModel[];
|
|
184
185
|
/** Keys callers must present. Empty: no gateway auth (only for private networks). */
|
|
185
186
|
clientKeys: ClientKey[];
|
|
187
|
+
/**
|
|
188
|
+
* End customers (B2B2C) and limits per customer, plan and team. A call names its
|
|
189
|
+
* customer with x-token-lens-customer; see customers.ts.
|
|
190
|
+
*/
|
|
191
|
+
customers: CustomersConfig;
|
|
192
|
+
/**
|
|
193
|
+
* Each client key sees only its own team's reports, runs, customers and metrics;
|
|
194
|
+
* the admin key sees all. For a gateway shared by several businesses.
|
|
195
|
+
*/
|
|
196
|
+
isolateTeams: boolean;
|
|
186
197
|
/** Key for the admin endpoints (registering models at runtime). */
|
|
187
198
|
adminKey?: string;
|
|
188
199
|
maxBodyBytes: number;
|
|
@@ -205,13 +216,30 @@ export interface PriceTier {
|
|
|
205
216
|
export declare const DEFAULTS: GatewayConfig;
|
|
206
217
|
type Env = Record<string, string | undefined>;
|
|
207
218
|
/** The config file: every field optional, nested sections merged with the defaults. */
|
|
208
|
-
export type ConfigFile = Partial<Omit<GatewayConfig, "safe" | "optIn" | "verify" | "batch" | "storage">> & {
|
|
219
|
+
export type ConfigFile = Partial<Omit<GatewayConfig, "safe" | "optIn" | "verify" | "batch" | "storage" | "customers">> & {
|
|
220
|
+
customers?: Partial<CustomersConfig>;
|
|
209
221
|
safe?: Partial<SafeOptimizations>;
|
|
210
222
|
optIn?: Partial<OptInOptimizations>;
|
|
211
223
|
verify?: Partial<VerifyConfig>;
|
|
212
224
|
batch?: Partial<GatewayConfig["batch"]>;
|
|
213
225
|
storage?: Partial<StorageConfig>;
|
|
214
226
|
};
|
|
227
|
+
/**
|
|
228
|
+
* Providers the gateway sets up from their usual environment variable alone,
|
|
229
|
+
* each through its OpenAI-compatible API (Anthropic through its own). `models`
|
|
230
|
+
* are the model-id globs routed to it; providers that host many vendors' models
|
|
231
|
+
* under overlapping names (Groq, Together, Fireworks, Cerebras, local servers)
|
|
232
|
+
* get no automatic route: name them in TOKEN_LENS_DEFAULT_UPSTREAM or in routes.
|
|
233
|
+
*/
|
|
234
|
+
export declare const PROVIDERS: Array<{
|
|
235
|
+
name: string;
|
|
236
|
+
type: UpstreamType;
|
|
237
|
+
key?: string[];
|
|
238
|
+
baseUrlEnv?: string;
|
|
239
|
+
baseUrl?: string;
|
|
240
|
+
models?: string[];
|
|
241
|
+
authHeader?: UpstreamConfig["authHeader"];
|
|
242
|
+
}>;
|
|
215
243
|
export declare function buildConfig(partial?: ConfigFile, env?: Env): GatewayConfig;
|
|
216
244
|
export declare function loadConfig(env?: Env): GatewayConfig;
|
|
217
245
|
export {};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Customers: for businesses that run Token Lens inside their own product and
|
|
3
|
+
* serve their own customers through it (B2B2C), and for companies that give
|
|
4
|
+
* each team or client business its own limits (B2B).
|
|
5
|
+
*
|
|
6
|
+
* A call names the end customer it was made for with `x-token-lens-customer`
|
|
7
|
+
* (or, when the header is absent, the API's own end-user field). Savings and
|
|
8
|
+
* spend are kept per team and customer, optional limits per day or month stop
|
|
9
|
+
* a customer's (or a team's) calls once reached, and caches are kept apart per
|
|
10
|
+
* customer so one customer's calls are never answered from another's.
|
|
11
|
+
*
|
|
12
|
+
* Token Lens itself still collects nothing: the ledger lives in the gateway or
|
|
13
|
+
* library the business runs, and in its own shared storage when that is on.
|
|
14
|
+
*/
|
|
15
|
+
import { type CallMetrics, type Totals } from "./metrics";
|
|
16
|
+
/** Spending limits; every field optional. Days and months are UTC. */
|
|
17
|
+
export interface Budget {
|
|
18
|
+
dailyUsd?: number;
|
|
19
|
+
monthlyUsd?: number;
|
|
20
|
+
dailyTokens?: number;
|
|
21
|
+
monthlyTokens?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface CustomersConfig {
|
|
24
|
+
/**
|
|
25
|
+
* When `x-token-lens-customer` is absent, read the customer from the API's own
|
|
26
|
+
* end-user field: OpenAI's `safety_identifier` or `user`, Anthropic's
|
|
27
|
+
* `metadata.user_id`. Off by default, so nothing from a request body shows up
|
|
28
|
+
* in reports unless the business asks for it.
|
|
29
|
+
*/
|
|
30
|
+
fromBody: boolean;
|
|
31
|
+
/** Keep customer ids only as a short hash. Lookups hash the id asked for, so the business can still query its own ids. */
|
|
32
|
+
hashIds: boolean;
|
|
33
|
+
/** Customers whose totals are kept in this gateway's memory; the least recently seen go first. Shared storage keeps them all. */
|
|
34
|
+
maxCustomers: number;
|
|
35
|
+
/** Default limits for every customer. Off when empty. */
|
|
36
|
+
budget: Budget;
|
|
37
|
+
/** Limits by plan, picked per call with `x-token-lens-plan`, e.g. { "free": { "dailyUsd": 0.05 } }. */
|
|
38
|
+
plans: Record<string, Budget>;
|
|
39
|
+
/** Limits for a whole team (client key), all its customers together; "*" applies to every team without its own entry. */
|
|
40
|
+
teamBudgets: Record<string, Budget>;
|
|
41
|
+
}
|
|
42
|
+
export declare const CUSTOMER_DEFAULTS: CustomersConfig;
|
|
43
|
+
export interface Spend {
|
|
44
|
+
usd: number;
|
|
45
|
+
tokens: number;
|
|
46
|
+
}
|
|
47
|
+
export type Period = "day" | "month";
|
|
48
|
+
/** UTC period keys, e.g. "d2026-10-14" and "m2026-10". */
|
|
49
|
+
export declare function periodKey(period: Period, now: Date): string;
|
|
50
|
+
/** Seconds a period's spend is worth keeping in shared storage: the period plus a margin. */
|
|
51
|
+
export declare const periodSeconds: (period: Period) => number;
|
|
52
|
+
export declare const hashId: (id: string) => string;
|
|
53
|
+
/** The customer a call was made for, as stored (hashed when hashIds is on). */
|
|
54
|
+
export declare function customerOf(headerValue: string | undefined, body: any, cfg: CustomersConfig): string | undefined;
|
|
55
|
+
/** How an id the business asks about is stored. */
|
|
56
|
+
export declare const storedId: (id: string, cfg: CustomersConfig) => string;
|
|
57
|
+
/** Keep only valid limits. */
|
|
58
|
+
export declare function cleanBudget(b: Partial<Record<keyof Budget, unknown>> | undefined): Budget;
|
|
59
|
+
export declare class UnknownPlanError extends Error {
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A customer's limits for one call: the default, then its plan's, then any set
|
|
63
|
+
* on the call itself (`x-token-lens-customer-daily-usd`, `-monthly-usd`,
|
|
64
|
+
* `-daily-tokens`, `-monthly-tokens`).
|
|
65
|
+
*/
|
|
66
|
+
export declare function customerBudget(cfg: CustomersConfig, plan: string | undefined, header: (name: string) => string | undefined): Budget;
|
|
67
|
+
export declare function teamBudget(cfg: CustomersConfig, app: string): Budget;
|
|
68
|
+
export declare const hasLimits: (b: Budget) => boolean;
|
|
69
|
+
export interface OverBudget {
|
|
70
|
+
who: "customer" | "team";
|
|
71
|
+
limit: keyof Budget;
|
|
72
|
+
cap: number;
|
|
73
|
+
spent: number;
|
|
74
|
+
}
|
|
75
|
+
export interface Remaining {
|
|
76
|
+
/** The tightest dollar limit left, if any applies. */
|
|
77
|
+
usd?: number;
|
|
78
|
+
/** The tightest token limit left, if any applies. */
|
|
79
|
+
tokens?: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Totals per team and customer, and spend in the current UTC day and month per
|
|
83
|
+
* customer and per team, for limits.
|
|
84
|
+
*/
|
|
85
|
+
export declare class CustomerLedger {
|
|
86
|
+
private maxCustomers;
|
|
87
|
+
/** key(app, customer) -> totals. Map order is least recently seen first. */
|
|
88
|
+
private rows;
|
|
89
|
+
/** key(period, app, customer | TEAM) -> spend recorded by this copy. */
|
|
90
|
+
private local;
|
|
91
|
+
/** What of `local` was already written to shared storage. */
|
|
92
|
+
private flushed;
|
|
93
|
+
/** Spend of every copy, as last read from shared storage (includes what this copy flushed). */
|
|
94
|
+
private shared;
|
|
95
|
+
private periods;
|
|
96
|
+
constructor(maxCustomers: number);
|
|
97
|
+
private prune;
|
|
98
|
+
/** One call: the same figures as the savings report, for the customer and its team. */
|
|
99
|
+
record(app: string, customer: string | undefined, m: Omit<CallMetrics, "app" | "agent" | "run">, now?: Date): void;
|
|
100
|
+
/** Spend in the current day or month, across every copy when shared storage is on. */
|
|
101
|
+
spent(app: string, customer: string | undefined, period: Period, now?: Date): Spend;
|
|
102
|
+
/** The first limit already reached, the customer's checked before the team's. */
|
|
103
|
+
over(app: string, customer: string | undefined, budgets: {
|
|
104
|
+
customer: Budget;
|
|
105
|
+
team: Budget;
|
|
106
|
+
}, now?: Date): OverBudget | undefined;
|
|
107
|
+
/** What is left under the tightest limits that apply, for the response headers. */
|
|
108
|
+
remaining(app: string, customer: string | undefined, budgets: {
|
|
109
|
+
customer: Budget;
|
|
110
|
+
team: Budget;
|
|
111
|
+
}, now?: Date): Remaining;
|
|
112
|
+
/** Raw totals rows (key: team and customer), for shared storage. */
|
|
113
|
+
snapshot(): Map<string, Totals>;
|
|
114
|
+
lastSeen(app: string, customer: string): string | undefined;
|
|
115
|
+
/**
|
|
116
|
+
* One row per customer with savings, spend today and this month; `rows` from
|
|
117
|
+
* shared storage when given, else this copy's own. `app` limits it to a team.
|
|
118
|
+
*/
|
|
119
|
+
list(app: string | undefined, rows?: Map<string, Totals>, now?: Date): any[];
|
|
120
|
+
view(team: string, customer: string, t?: Totals, now?: Date): {
|
|
121
|
+
lastSeen: string;
|
|
122
|
+
spentToday: {
|
|
123
|
+
usd: number;
|
|
124
|
+
tokens: number;
|
|
125
|
+
};
|
|
126
|
+
spentThisMonth: {
|
|
127
|
+
usd: number;
|
|
128
|
+
tokens: number;
|
|
129
|
+
};
|
|
130
|
+
costUsd: number;
|
|
131
|
+
costBeforeUsd: number;
|
|
132
|
+
checkCostUsd: number;
|
|
133
|
+
savedTokens: number;
|
|
134
|
+
savedTokensPct: number;
|
|
135
|
+
savedUsd: number;
|
|
136
|
+
savedUsdPct: number;
|
|
137
|
+
netSavedUsd: number;
|
|
138
|
+
outputUnchangedPct: number;
|
|
139
|
+
unpricedRequests: number;
|
|
140
|
+
requests: number;
|
|
141
|
+
errors: number;
|
|
142
|
+
replayed: number;
|
|
143
|
+
inputTokens: number;
|
|
144
|
+
inputTokensBefore: number;
|
|
145
|
+
outputTokens: number;
|
|
146
|
+
outputTokensAvoided: number;
|
|
147
|
+
cachedInputTokens: number;
|
|
148
|
+
reasoningTokens: number;
|
|
149
|
+
savedInputTokens: number;
|
|
150
|
+
pricedRequests: number;
|
|
151
|
+
checks: number;
|
|
152
|
+
checksMatched: number;
|
|
153
|
+
team: string;
|
|
154
|
+
customer: string;
|
|
155
|
+
};
|
|
156
|
+
/** Spend recorded since the last flush, by period key and field; marks it flushed. */
|
|
157
|
+
takeSpendDeltas(now?: Date): Array<{
|
|
158
|
+
period: string;
|
|
159
|
+
field: string;
|
|
160
|
+
by: number;
|
|
161
|
+
}>;
|
|
162
|
+
/** Every copy's spend for one period, as read from shared storage (fields: team, customer, usd|tokens). */
|
|
163
|
+
setSharedSpend(period: string, fields: Record<string, string>): void;
|
|
164
|
+
/** Periods to read back from shared storage. */
|
|
165
|
+
currentPeriods(now?: Date): string[];
|
|
166
|
+
}
|
|
167
|
+
/** CSV of the customers list. */
|
|
168
|
+
export declare function customersCsv(rows: ReturnType<CustomerLedger["list"]>): string;
|
|
@@ -253,7 +253,8 @@ export declare class Metrics {
|
|
|
253
253
|
checksMatched: number;
|
|
254
254
|
}[];
|
|
255
255
|
};
|
|
256
|
-
|
|
256
|
+
/** Prometheus text; `team` limits it to one team's rows. */
|
|
257
|
+
prometheus(team?: string): string;
|
|
257
258
|
}
|
|
258
259
|
/** A totals row with the derived savings figures. */
|
|
259
260
|
export declare function view(t: Totals): {
|
|
@@ -78,6 +78,8 @@ export interface Run {
|
|
|
78
78
|
/** The agent or workflow, from the x-token-lens-agent header ("-" when not given). */
|
|
79
79
|
agent: string;
|
|
80
80
|
named: boolean;
|
|
81
|
+
/** The business's end customer this run was for (x-token-lens-customer), when named. */
|
|
82
|
+
customer?: string;
|
|
81
83
|
firstSeen: string;
|
|
82
84
|
lastSeen: string;
|
|
83
85
|
calls: CallRecord[];
|
|
@@ -153,9 +155,6 @@ export declare function findings(run: Run): Finding[];
|
|
|
153
155
|
/** The part that grew most across the run, for the per-call header. */
|
|
154
156
|
export declare function fastestGrowing(run: Run): Part | undefined;
|
|
155
157
|
export declare function runSummary(run: Run): {
|
|
156
|
-
id: string;
|
|
157
|
-
app: string;
|
|
158
|
-
agent: string;
|
|
159
158
|
named: boolean;
|
|
160
159
|
firstSeen: string;
|
|
161
160
|
lastSeen: string;
|
|
@@ -179,4 +178,8 @@ export declare function runSummary(run: Run): {
|
|
|
179
178
|
retriesReplayed: number;
|
|
180
179
|
stoppedByBudget: boolean;
|
|
181
180
|
outputCheck: import("./verify").CheckSummary;
|
|
181
|
+
customer?: string;
|
|
182
|
+
id: string;
|
|
183
|
+
app: string;
|
|
184
|
+
agent: string;
|
|
182
185
|
};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* across gateway copies, run reports, and the fixes switched off to keep
|
|
4
4
|
* answers unchanged.
|
|
5
5
|
*/
|
|
6
|
+
import { type CustomerLedger } from "./customers";
|
|
6
7
|
import type { OutputGuard } from "./guard";
|
|
7
8
|
import { type Metrics, type Totals } from "./metrics";
|
|
8
9
|
import type { Run } from "./runs";
|
|
@@ -24,6 +25,7 @@ export declare class SharedReports {
|
|
|
24
25
|
private cfg;
|
|
25
26
|
private deps;
|
|
26
27
|
private flushed;
|
|
28
|
+
private flushedCustomers;
|
|
27
29
|
private dirtyRuns;
|
|
28
30
|
private timer?;
|
|
29
31
|
private busy?;
|
|
@@ -31,6 +33,7 @@ export declare class SharedReports {
|
|
|
31
33
|
readonly kind: "redis" | "file";
|
|
32
34
|
constructor(kv: KV, cfg: StorageConfig, deps: {
|
|
33
35
|
metrics: Metrics;
|
|
36
|
+
ledger?: CustomerLedger;
|
|
34
37
|
guard: OutputGuard;
|
|
35
38
|
runView(run: Run): object;
|
|
36
39
|
log(line: string): void;
|
|
@@ -49,6 +52,8 @@ export declare class SharedReports {
|
|
|
49
52
|
rows: Map<string, Totals>;
|
|
50
53
|
since: string;
|
|
51
54
|
}>;
|
|
55
|
+
/** Customer totals of every copy, by team and customer. */
|
|
56
|
+
customerTotals(): Promise<Map<string, Totals>>;
|
|
52
57
|
recentRuns(limit: number): Promise<any[]>;
|
|
53
58
|
run(id: string): Promise<any | undefined>;
|
|
54
59
|
suspend(list: SwitchedOff[]): Promise<void>;
|
|
@@ -25,6 +25,8 @@ export interface KV {
|
|
|
25
25
|
zrevrange(key: string, start: number, stop: number): Promise<string[]>;
|
|
26
26
|
zremrangebyscore(key: string, max: number): Promise<void>;
|
|
27
27
|
setnx(key: string, value: string): Promise<void>;
|
|
28
|
+
/** Drop a key after `seconds`. */
|
|
29
|
+
expire(key: string, seconds: number): Promise<void>;
|
|
28
30
|
close(): void;
|
|
29
31
|
}
|
|
30
32
|
type Reply = string | number | null | Reply[] | Error;
|
|
@@ -52,6 +54,7 @@ export declare class RedisKV implements KV {
|
|
|
52
54
|
zrevrange(key: string, start: number, stop: number): Promise<string[]>;
|
|
53
55
|
zremrangebyscore(key: string, max: number): Promise<void>;
|
|
54
56
|
setnx(key: string, value: string): Promise<void>;
|
|
57
|
+
expire(key: string, seconds: number): Promise<void>;
|
|
55
58
|
close(): void;
|
|
56
59
|
}
|
|
57
60
|
/** Parses one RESP2 reply starting at `at`; undefined when more bytes are needed. */
|
|
@@ -65,6 +68,8 @@ export declare class FileKV implements KV {
|
|
|
65
68
|
private hashes;
|
|
66
69
|
private strings;
|
|
67
70
|
private zsets;
|
|
71
|
+
/** Hashes with an expiry time (ms). */
|
|
72
|
+
private expiries;
|
|
68
73
|
private dirty;
|
|
69
74
|
constructor(path: string);
|
|
70
75
|
private hash;
|
|
@@ -72,6 +77,8 @@ export declare class FileKV implements KV {
|
|
|
72
77
|
hgetall(key: string): Promise<{
|
|
73
78
|
[k: string]: string;
|
|
74
79
|
}>;
|
|
80
|
+
private dropExpired;
|
|
81
|
+
expire(key: string, seconds: number): Promise<void>;
|
|
75
82
|
hset(key: string, field: string, value: string): Promise<void>;
|
|
76
83
|
hdel(key: string, field: string): Promise<void>;
|
|
77
84
|
setex(key: string, seconds: number, value: string): Promise<void>;
|
package/types/src/sdk/index.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ import { type SafeOptimizations } from "../gateway/config";
|
|
|
40
40
|
import { type Action } from "../gateway/optimize";
|
|
41
41
|
import { type Protocol, type Usage } from "../gateway/protocol";
|
|
42
42
|
import { type Check } from "../gateway/verify";
|
|
43
|
+
import { type CustomersConfig } from "../gateway/customers";
|
|
43
44
|
export { registerModel, formatUsd, type CustomModel, type ModelPricing } from "../core";
|
|
44
45
|
type Fetch = typeof globalThis.fetch;
|
|
45
46
|
export type SafeFix = Exclude<keyof SafeOptimizations, "prompt">;
|
|
@@ -70,9 +71,20 @@ export interface TokenLensOptions {
|
|
|
70
71
|
retryWindowSeconds?: number;
|
|
71
72
|
/** Called after each call with what Token Lens did and what it saved. */
|
|
72
73
|
onCall?: (call: CallRecord) => void;
|
|
74
|
+
/**
|
|
75
|
+
* For apps serving their own customers: a call names its customer with the
|
|
76
|
+
* `x-token-lens-customer` header (or the API's own end-user field), and its
|
|
77
|
+
* plan with `x-token-lens-plan`. Savings are kept per customer, limits per day
|
|
78
|
+
* or month stop a customer's calls once reached, and retries are never answered
|
|
79
|
+
* from another customer's call. Token Lens strips its headers before the call
|
|
80
|
+
* goes out.
|
|
81
|
+
*/
|
|
82
|
+
customers?: Partial<Omit<CustomersConfig, "teamBudgets">>;
|
|
73
83
|
}
|
|
74
84
|
export interface CallRecord {
|
|
75
85
|
protocol: Protocol;
|
|
86
|
+
/** The app's end customer the call was for, when named. */
|
|
87
|
+
customer?: string;
|
|
76
88
|
model: string;
|
|
77
89
|
/** Token Lens profile the model was priced with. */
|
|
78
90
|
profile: string;
|
|
@@ -108,6 +120,8 @@ export declare class TokenLens {
|
|
|
108
120
|
private rewriteMatched;
|
|
109
121
|
/** Safe fixes switched off because a check found a different answer while they were in play. */
|
|
110
122
|
private readonly disabled;
|
|
123
|
+
private readonly customers;
|
|
124
|
+
private readonly ledger;
|
|
111
125
|
constructor(options?: TokenLensOptions);
|
|
112
126
|
/** A fetch that applies Token Lens to OpenAI and Anthropic API calls and passes everything else through. */
|
|
113
127
|
fetch(base?: Fetch): Fetch;
|
|
@@ -190,6 +204,43 @@ export declare class TokenLens {
|
|
|
190
204
|
};
|
|
191
205
|
rewritePrompts: string;
|
|
192
206
|
switchedOff: SafeFix[];
|
|
207
|
+
byCustomer: any[];
|
|
208
|
+
};
|
|
209
|
+
/** One customer's savings, and spend today and this month (limits count these), or undefined before its first call. */
|
|
210
|
+
customer(id: string): {
|
|
211
|
+
lastSeen: string;
|
|
212
|
+
spentToday: {
|
|
213
|
+
usd: number;
|
|
214
|
+
tokens: number;
|
|
215
|
+
};
|
|
216
|
+
spentThisMonth: {
|
|
217
|
+
usd: number;
|
|
218
|
+
tokens: number;
|
|
219
|
+
};
|
|
220
|
+
costUsd: number;
|
|
221
|
+
costBeforeUsd: number;
|
|
222
|
+
checkCostUsd: number;
|
|
223
|
+
savedTokens: number;
|
|
224
|
+
savedTokensPct: number;
|
|
225
|
+
savedUsd: number;
|
|
226
|
+
savedUsdPct: number;
|
|
227
|
+
netSavedUsd: number;
|
|
228
|
+
outputUnchangedPct: number;
|
|
229
|
+
unpricedRequests: number;
|
|
230
|
+
requests: number;
|
|
231
|
+
errors: number;
|
|
232
|
+
replayed: number;
|
|
233
|
+
inputTokens: number;
|
|
234
|
+
inputTokensBefore: number;
|
|
235
|
+
outputTokens: number;
|
|
236
|
+
outputTokensAvoided: number;
|
|
237
|
+
cachedInputTokens: number;
|
|
238
|
+
reasoningTokens: number;
|
|
239
|
+
savedInputTokens: number;
|
|
240
|
+
pricedRequests: number;
|
|
241
|
+
checks: number;
|
|
242
|
+
checksMatched: number;
|
|
243
|
+
customer: string;
|
|
193
244
|
};
|
|
194
245
|
/** The report in a few plain lines. */
|
|
195
246
|
summary(): string;
|