@kunal_dutt/token-lens 0.0.0-stage → 0.2.17
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 +491 -0
- package/LICENSE +21 -0
- package/PRIVACY.md +36 -0
- package/README.md +80 -2
- package/agent-cost-check.yml +49 -0
- package/check-agent.mjs +3298 -0
- package/cli.mjs +22 -0
- package/gateway.mjs +3365 -0
- package/package.json +45 -4
- package/sdk.mjs +207719 -0
- package/server.mjs +3392 -0
- package/token-lens.mcpb +0 -0
- package/types/src/core/advice.d.ts +27 -0
- package/types/src/core/budget.d.ts +28 -0
- package/types/src/core/compress.d.ts +29 -0
- package/types/src/core/cost.d.ts +32 -0
- package/types/src/core/guard.d.ts +20 -0
- package/types/src/core/hotspots.d.ts +124 -0
- package/types/src/core/index.d.ts +74 -0
- package/types/src/core/media.d.ts +29 -0
- package/types/src/core/models.d.ts +81 -0
- package/types/src/core/plans.d.ts +88 -0
- package/types/src/core/redos.d.ts +32 -0
- package/types/src/core/review.d.ts +46 -0
- package/types/src/core/rules.d.ts +47 -0
- package/types/src/core/tasks.d.ts +44 -0
- package/types/src/core/tokens.d.ts +19 -0
- package/types/src/core/verify.d.ts +19 -0
- package/types/src/gateway/config.d.ts +217 -0
- package/types/src/gateway/guard.d.ts +56 -0
- package/types/src/gateway/images.d.ts +40 -0
- package/types/src/gateway/metrics.d.ts +287 -0
- package/types/src/gateway/optimize.d.ts +102 -0
- package/types/src/gateway/protocol.d.ts +68 -0
- package/types/src/gateway/runs.d.ts +182 -0
- package/types/src/gateway/shared.d.ts +57 -0
- package/types/src/gateway/signals.d.ts +38 -0
- package/types/src/gateway/storage.d.ts +88 -0
- package/types/src/gateway/verify.d.ts +51 -0
- package/types/src/sdk/index.d.ts +228 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Lens for developers who don't run the gateway: the gateway's fixes,
|
|
3
|
+
* applied inside your own process, with the same savings report.
|
|
4
|
+
*
|
|
5
|
+
* import OpenAI from "openai";
|
|
6
|
+
* import { withTokenLens } from "@kunal_dutt/token-lens/sdk";
|
|
7
|
+
* const client = withTokenLens(new OpenAI(), { agent: "support-bot" });
|
|
8
|
+
* ...
|
|
9
|
+
* console.log(client.tokenLens.summary());
|
|
10
|
+
*
|
|
11
|
+
* It hooks the SDK's `fetch`, so the same hook also works for anything that
|
|
12
|
+
* takes a fetch function: LangChain (`configuration: { fetch }` /
|
|
13
|
+
* `clientOptions: { fetch }`), the Vercel AI SDK (`createOpenAI({ fetch })`),
|
|
14
|
+
* LlamaIndex and others.
|
|
15
|
+
*
|
|
16
|
+
* Nothing leaves your process except the calls you were already making, to
|
|
17
|
+
* the provider you already call. The model is never changed.
|
|
18
|
+
*
|
|
19
|
+
* On by default, because the request means exactly the same thing:
|
|
20
|
+
* minify-json pretty-printed JSON in tool results -> compact JSON
|
|
21
|
+
* collapse-repeats runs of identical lines in tool results -> one line and a count
|
|
22
|
+
* dedupe-tool-outputs a tool result identical to an earlier one is sent once
|
|
23
|
+
* prompt-caching cache breakpoints (Anthropic) or a cache key (OpenAI) once a prefix repeats
|
|
24
|
+
* dedupe-retries an identical temperature-0 request is answered from the earlier response
|
|
25
|
+
*
|
|
26
|
+
* Opt-in, because it rewrites text the model reads:
|
|
27
|
+
* rewritePrompts the core prompt optimizer on user-written messages. It starts on
|
|
28
|
+
* probation, as opt-in fixes do in the gateway (guard.ts): for the
|
|
29
|
+
* first `probation` calls the original request goes too, and the
|
|
30
|
+
* app gets the rewritten call's answer only when the two match.
|
|
31
|
+
* Streamed calls aren't rewritten until probation is passed. After
|
|
32
|
+
* that it is checked on at least a quarter of calls. The first
|
|
33
|
+
* different answer switches it off for good.
|
|
34
|
+
*
|
|
35
|
+
* A failed output check also switches off the safe fixes that were in play,
|
|
36
|
+
* so a fix is never kept once a check has seen it change an answer.
|
|
37
|
+
*/
|
|
38
|
+
import { type ModelPricing, type ModelProfile } from "../core";
|
|
39
|
+
import { type SafeOptimizations } from "../gateway/config";
|
|
40
|
+
import { type Action } from "../gateway/optimize";
|
|
41
|
+
import { type Protocol, type Usage } from "../gateway/protocol";
|
|
42
|
+
import { type Check } from "../gateway/verify";
|
|
43
|
+
export { registerModel, formatUsd, type CustomModel, type ModelPricing } from "../core";
|
|
44
|
+
type Fetch = typeof globalThis.fetch;
|
|
45
|
+
export type SafeFix = Exclude<keyof SafeOptimizations, "prompt">;
|
|
46
|
+
export interface TokenLensOptions {
|
|
47
|
+
/** Name for this agent or app in the report. */
|
|
48
|
+
agent?: string;
|
|
49
|
+
/** Run id; calls in one run share a prompt cache. Default: one per TokenLens instance. */
|
|
50
|
+
run?: string;
|
|
51
|
+
/** "observe" reports cost without changing any request. Default "optimize". */
|
|
52
|
+
mode?: "optimize" | "observe";
|
|
53
|
+
/** Turn individual safe fixes off, e.g. `{ dedupeRetries: false }`. */
|
|
54
|
+
fixes?: Partial<Record<SafeFix, boolean>>;
|
|
55
|
+
/** Opt-in: rewrite user-written messages with the core prompt optimizer (checked, see above). */
|
|
56
|
+
rewritePrompts?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Share of optimized, non-streamed calls (0..1) to check by also sending the
|
|
59
|
+
* original request to the same model and comparing the answers. Off by
|
|
60
|
+
* default because each check is one extra call.
|
|
61
|
+
*/
|
|
62
|
+
verify?: number;
|
|
63
|
+
/** Rewritten calls that must match the original's answer before the rewrite is trusted. Default 5. */
|
|
64
|
+
probation?: number;
|
|
65
|
+
/** Text similarity at or above which two answers count as the same; tool calls must match exactly. Default 0.8. */
|
|
66
|
+
minSimilarity?: number;
|
|
67
|
+
/** Your own price, USD per 1M tokens, for every model; default is each model's API list price. */
|
|
68
|
+
pricing?: ModelPricing;
|
|
69
|
+
/** Seconds an identical temperature-0 request is answered from the earlier response. Default 120. */
|
|
70
|
+
retryWindowSeconds?: number;
|
|
71
|
+
/** Called after each call with what Token Lens did and what it saved. */
|
|
72
|
+
onCall?: (call: CallRecord) => void;
|
|
73
|
+
}
|
|
74
|
+
export interface CallRecord {
|
|
75
|
+
protocol: Protocol;
|
|
76
|
+
model: string;
|
|
77
|
+
/** Token Lens profile the model was priced with. */
|
|
78
|
+
profile: string;
|
|
79
|
+
status: number;
|
|
80
|
+
stream: boolean;
|
|
81
|
+
/** Input tokens removed from the request. */
|
|
82
|
+
savedInputTokens: number;
|
|
83
|
+
fixes: Action[];
|
|
84
|
+
usage: Usage;
|
|
85
|
+
/** Answered from an earlier identical request, so the call was not made. */
|
|
86
|
+
replayed: boolean;
|
|
87
|
+
costUsd: number;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The model's prices for a request of `inputTokens` input tokens: past a
|
|
91
|
+
* long-context threshold, the whole request is billed at the higher rate.
|
|
92
|
+
*/
|
|
93
|
+
export declare function pricedFor(profile: ModelProfile, inputTokens: number): ModelProfile;
|
|
94
|
+
export declare class TokenLens {
|
|
95
|
+
readonly options: TokenLensOptions;
|
|
96
|
+
private readonly counter;
|
|
97
|
+
private readonly prefixes;
|
|
98
|
+
private readonly rows;
|
|
99
|
+
private readonly byFix;
|
|
100
|
+
private readonly checks;
|
|
101
|
+
private checkCostUsd;
|
|
102
|
+
private readonly replays;
|
|
103
|
+
private readonly pending;
|
|
104
|
+
private readonly runId;
|
|
105
|
+
/** Set when an output check found a different answer with prompt rewriting on. */
|
|
106
|
+
private rewriteStopped?;
|
|
107
|
+
/** Rewritten calls whose answer matched the original's during probation. */
|
|
108
|
+
private rewriteMatched;
|
|
109
|
+
/** Safe fixes switched off because a check found a different answer while they were in play. */
|
|
110
|
+
private readonly disabled;
|
|
111
|
+
constructor(options?: TokenLensOptions);
|
|
112
|
+
/** A fetch that applies Token Lens to OpenAI and Anthropic API calls and passes everything else through. */
|
|
113
|
+
fetch(base?: Fetch): Fetch;
|
|
114
|
+
/** LangChain callback handler: reports tokens and cost for any LangChain chat model (no request changes). */
|
|
115
|
+
langchain(): {
|
|
116
|
+
name: string;
|
|
117
|
+
handleChatModelStart: (llm: any, _m: unknown, runId: string, _p?: string, extra?: any) => any;
|
|
118
|
+
handleLLMStart: (llm: any, _p: unknown, runId: string, _pr?: string, extra?: any) => any;
|
|
119
|
+
handleLLMEnd: (output: any, runId: string) => void;
|
|
120
|
+
};
|
|
121
|
+
/** Wait for background work (output checks, streams still being read). */
|
|
122
|
+
flush(): Promise<void>;
|
|
123
|
+
report(): {
|
|
124
|
+
agent: string;
|
|
125
|
+
run: string;
|
|
126
|
+
total: {
|
|
127
|
+
costUsd: number;
|
|
128
|
+
costBeforeUsd: number;
|
|
129
|
+
checkCostUsd: number;
|
|
130
|
+
savedTokens: number;
|
|
131
|
+
savedTokensPct: number;
|
|
132
|
+
savedUsd: number;
|
|
133
|
+
savedUsdPct: number;
|
|
134
|
+
netSavedUsd: number;
|
|
135
|
+
outputUnchangedPct: number;
|
|
136
|
+
unpricedRequests: number;
|
|
137
|
+
requests: number;
|
|
138
|
+
errors: number;
|
|
139
|
+
replayed: number;
|
|
140
|
+
inputTokens: number;
|
|
141
|
+
inputTokensBefore: number;
|
|
142
|
+
outputTokens: number;
|
|
143
|
+
outputTokensAvoided: number;
|
|
144
|
+
cachedInputTokens: number;
|
|
145
|
+
reasoningTokens: number;
|
|
146
|
+
savedInputTokens: number;
|
|
147
|
+
pricedRequests: number;
|
|
148
|
+
checks: number;
|
|
149
|
+
checksMatched: number;
|
|
150
|
+
};
|
|
151
|
+
byModel: {
|
|
152
|
+
costUsd: number;
|
|
153
|
+
costBeforeUsd: number;
|
|
154
|
+
checkCostUsd: number;
|
|
155
|
+
savedTokens: number;
|
|
156
|
+
savedTokensPct: number;
|
|
157
|
+
savedUsd: number;
|
|
158
|
+
savedUsdPct: number;
|
|
159
|
+
netSavedUsd: number;
|
|
160
|
+
outputUnchangedPct: number;
|
|
161
|
+
unpricedRequests: number;
|
|
162
|
+
requests: number;
|
|
163
|
+
errors: number;
|
|
164
|
+
replayed: number;
|
|
165
|
+
inputTokens: number;
|
|
166
|
+
inputTokensBefore: number;
|
|
167
|
+
outputTokens: number;
|
|
168
|
+
outputTokensAvoided: number;
|
|
169
|
+
cachedInputTokens: number;
|
|
170
|
+
reasoningTokens: number;
|
|
171
|
+
savedInputTokens: number;
|
|
172
|
+
pricedRequests: number;
|
|
173
|
+
checks: number;
|
|
174
|
+
checksMatched: number;
|
|
175
|
+
model: string;
|
|
176
|
+
}[];
|
|
177
|
+
byFix: {
|
|
178
|
+
fix: string;
|
|
179
|
+
calls: number;
|
|
180
|
+
tokens: number;
|
|
181
|
+
usd: number;
|
|
182
|
+
}[];
|
|
183
|
+
checks: {
|
|
184
|
+
recent: Check[];
|
|
185
|
+
checked: number;
|
|
186
|
+
matched: number;
|
|
187
|
+
sameToolCalls: number;
|
|
188
|
+
averageSimilarity: number;
|
|
189
|
+
savedOnChecked: number;
|
|
190
|
+
};
|
|
191
|
+
rewritePrompts: string;
|
|
192
|
+
switchedOff: SafeFix[];
|
|
193
|
+
};
|
|
194
|
+
/** The report in a few plain lines. */
|
|
195
|
+
summary(): string;
|
|
196
|
+
private profile;
|
|
197
|
+
private probation;
|
|
198
|
+
private onProbation;
|
|
199
|
+
private config;
|
|
200
|
+
private prepare;
|
|
201
|
+
private send;
|
|
202
|
+
/**
|
|
203
|
+
* Probation: send the rewritten and the original request together, and hand
|
|
204
|
+
* back the rewritten call's answer only when the two match. Otherwise the app
|
|
205
|
+
* gets the original's answer and rewriting is switched off.
|
|
206
|
+
*/
|
|
207
|
+
private probationCall;
|
|
208
|
+
private addCheck;
|
|
209
|
+
/** Send the caller's original request too, in the background, and compare the two answers. */
|
|
210
|
+
private maybeCheck;
|
|
211
|
+
private base;
|
|
212
|
+
private record;
|
|
213
|
+
}
|
|
214
|
+
type WithOptions = {
|
|
215
|
+
withOptions(options: any): unknown;
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* Wrap an OpenAI or Anthropic client (any SDK with `withOptions({ fetch })`):
|
|
219
|
+
* every call through the returned client gets Token Lens, and
|
|
220
|
+
* `client.tokenLens` holds the report.
|
|
221
|
+
*/
|
|
222
|
+
export declare function withTokenLens<T extends WithOptions>(client: T, options?: TokenLensOptions | TokenLens): T & {
|
|
223
|
+
tokenLens: TokenLens;
|
|
224
|
+
};
|
|
225
|
+
/** Shorthand for `new TokenLens(options).fetch()`, for LangChain, the Vercel AI SDK and other fetch-based clients. */
|
|
226
|
+
export declare function tokenLensFetch(options?: TokenLensOptions | TokenLens, base?: Fetch): Fetch & {
|
|
227
|
+
tokenLens: TokenLens;
|
|
228
|
+
};
|