@tollgateai/sdk 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -10
- package/dist/index.cjs +166 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +73 -4
- package/dist/index.d.ts +73 -4
- package/dist/index.js +165 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,12 @@ Track **real** LLM model usage and compute live gross margin with
|
|
|
4
4
|
[Tollgate](https://tollgateai.vercel.app). The SDK reads the actual `usage`
|
|
5
5
|
object off each provider response — you never hand-count tokens.
|
|
6
6
|
|
|
7
|
-
Published on npm: [@tollgateai/sdk](https://www.npmjs.com/package/@tollgateai/sdk) (v0.
|
|
7
|
+
Published on npm: [@tollgateai/sdk](https://www.npmjs.com/package/@tollgateai/sdk) (v0.2.0).
|
|
8
|
+
|
|
9
|
+
Works with **OpenAI**, **Anthropic**, **AWS Bedrock**, and **every OpenAI-compatible
|
|
10
|
+
gateway** (Vercel AI Gateway, OpenRouter, Groq, Together, Nebius, local vLLM, …) —
|
|
11
|
+
streaming and non-streaming. Cost is computed server-side from the token counts the
|
|
12
|
+
wrappers capture, so no provider has to return a dollar figure.
|
|
8
13
|
|
|
9
14
|
```bash
|
|
10
15
|
npm install @tollgateai/sdk
|
|
@@ -30,19 +35,39 @@ import Anthropic from '@anthropic-ai/sdk';
|
|
|
30
35
|
import { createTollgateClient, wrapAnthropic } from '@tollgateai/sdk';
|
|
31
36
|
|
|
32
37
|
const tollgate = createTollgateClient(); // reads TOLLGATE_API_KEY
|
|
38
|
+
|
|
39
|
+
// Pin a runId so every call in this run is grouped and reports cost only.
|
|
40
|
+
const runId = 'ticket_8842';
|
|
33
41
|
const anthropic = wrapAnthropic(new Anthropic(), tollgate, {
|
|
34
42
|
customerId: 'cust_A', // your end customer
|
|
35
|
-
|
|
43
|
+
runId,
|
|
36
44
|
});
|
|
37
45
|
|
|
38
46
|
// Use the client normally — usage is tracked automatically.
|
|
39
47
|
await anthropic.messages.create({
|
|
40
48
|
model: 'claude-sonnet-4-6',
|
|
41
49
|
max_tokens: 512,
|
|
42
|
-
messages: [{ role: 'user', content: '
|
|
50
|
+
messages: [{ role: 'user', content: 'Resolve this ticket…' }],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Book revenue once, when the run finishes — "no outcome, no charge".
|
|
54
|
+
await tollgate.resolve({
|
|
55
|
+
runId,
|
|
56
|
+
customerId: 'cust_A',
|
|
57
|
+
outcome: 'resolved', // 'resolved' | 'escalated' | 'failed'
|
|
58
|
+
revenueUnitCents: 50, // charge for this resolved unit ($0.50)
|
|
43
59
|
});
|
|
44
60
|
```
|
|
45
61
|
|
|
62
|
+
### Outcome-based pricing
|
|
63
|
+
|
|
64
|
+
Under per-resolution / outcome pricing, only a **resolved** run earns revenue —
|
|
65
|
+
an `escalated`/`failed` run earns $0 but its provider cost still counts against
|
|
66
|
+
you. Wrap your client to meter cost on every call, then call `resolve()` once at
|
|
67
|
+
the end of the run to book the outcome (and, if resolved, its revenue). For
|
|
68
|
+
simple per-call billing you can instead pass `revenueUnitCents` in the wrap
|
|
69
|
+
options and skip `resolve()`.
|
|
70
|
+
|
|
46
71
|
### OpenAI
|
|
47
72
|
|
|
48
73
|
```ts
|
|
@@ -61,9 +86,55 @@ await openai.chat.completions.create({
|
|
|
61
86
|
`revenueUnitCents` may also be a function of the response, e.g.
|
|
62
87
|
`revenueUnitCents: (res) => res.someField ? 50 : 0`.
|
|
63
88
|
|
|
89
|
+
### OpenAI-compatible gateways
|
|
90
|
+
|
|
91
|
+
Point the OpenAI SDK at any compatible endpoint and set `provider:
|
|
92
|
+
'openai_compatible'` so the server prices it from the gateway-echoed model name:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
const openai = new OpenAI({ apiKey: process.env.GROQ_API_KEY, baseURL: 'https://api.groq.com/openai/v1' });
|
|
96
|
+
const client = wrapOpenAI(openai, tollgate, {
|
|
97
|
+
customerId: 'cust_A',
|
|
98
|
+
provider: 'openai_compatible', // Groq / OpenRouter / Together / Nebius / vLLM …
|
|
99
|
+
});
|
|
100
|
+
await client.chat.completions.create({ model: 'llama-3.3-70b-versatile', messages: [...] });
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Streaming
|
|
104
|
+
|
|
105
|
+
Streaming is captured automatically. For **OpenAI / compatible**, pass
|
|
106
|
+
`stream_options: { include_usage: true }` (required for a final usage chunk); for
|
|
107
|
+
**Anthropic** no flag is needed. Just iterate the stream as usual:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
const stream = await client.chat.completions.create({
|
|
111
|
+
model: 'gpt-4o', stream: true, stream_options: { include_usage: true },
|
|
112
|
+
messages: [{ role: 'user', content: 'Hello' }],
|
|
113
|
+
});
|
|
114
|
+
for await (const chunk of stream) { /* … */ } // usage is reported when the stream ends
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### AWS Bedrock
|
|
118
|
+
|
|
119
|
+
Wrap a `BedrockRuntimeClient` so `ConverseCommand` / `ConverseStreamCommand`
|
|
120
|
+
auto-report usage (the model id is read from the command):
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { BedrockRuntimeClient, ConverseCommand } from '@aws-sdk/client-bedrock-runtime';
|
|
124
|
+
import { wrapBedrock } from '@tollgateai/sdk';
|
|
125
|
+
|
|
126
|
+
const bedrock = wrapBedrock(new BedrockRuntimeClient({ region: 'us-east-1' }), tollgate, { customerId: 'cust_A' });
|
|
127
|
+
await bedrock.send(new ConverseCommand({ modelId: 'anthropic.claude-3-5-sonnet-20241022-v2:0', messages: [...] }));
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Already have an exact cost?
|
|
131
|
+
|
|
132
|
+
Pass `providerCostCents` (a number or a function of the response) and the server
|
|
133
|
+
uses it verbatim, skipping the rate card entirely.
|
|
134
|
+
|
|
64
135
|
## Manual tracking
|
|
65
136
|
|
|
66
|
-
For
|
|
137
|
+
For full control or unusual providers:
|
|
67
138
|
|
|
68
139
|
```ts
|
|
69
140
|
import { createTollgateClient } from '@tollgateai/sdk';
|
|
@@ -89,17 +160,25 @@ await tollgate.track({
|
|
|
89
160
|
- **Idempotent.** Events are deduplicated on `idempotencyKey` (auto-set to the
|
|
90
161
|
provider response id by the wrappers), so retries never double-count.
|
|
91
162
|
- **No prompt content is ever sent** — only token counts and metadata.
|
|
92
|
-
- **Streaming
|
|
93
|
-
|
|
163
|
+
- **Streaming is auto-tracked** (OpenAI needs `stream_options.include_usage`).
|
|
164
|
+
- **Cost from tokens.** The server prices every event from token counts × a rate
|
|
165
|
+
card that auto-syncs daily from the public LiteLLM registry — unknown models are
|
|
166
|
+
priced at $0 and flagged in logs. See [docs/PRICING.md](../../docs/PRICING.md).
|
|
94
167
|
- **Non-blocking.** Auto-instrumented tracking runs in the background; failures
|
|
95
168
|
are passed to `onError` (default `console.warn`) and never break your call.
|
|
96
169
|
|
|
97
170
|
## API
|
|
98
171
|
|
|
99
|
-
- `createTollgateClient(options?)` → `{ track(event) }`
|
|
172
|
+
- `createTollgateClient(options?)` → `{ track(event), resolve(input) }`
|
|
173
|
+
- `resolve({ runId, customerId, outcome, revenueUnitCents? })` → close a run with
|
|
174
|
+
its outcome; books revenue once, only when `outcome` is `'resolved'`
|
|
100
175
|
- `wrapAnthropic(client, tollgate, options)` → instrumented Anthropic client
|
|
101
|
-
- `wrapOpenAI(client, tollgate, options)` → instrumented OpenAI client
|
|
102
|
-
- `
|
|
103
|
-
|
|
176
|
+
- `wrapOpenAI(client, tollgate, options)` → instrumented OpenAI / compatible client
|
|
177
|
+
- `wrapBedrock(client, tollgate, options)` → instrumented Bedrock Runtime client
|
|
178
|
+
- `anthropicEventFrom` / `openAIEventFrom` / `bedrockEventFrom` → build a track
|
|
179
|
+
payload manually from a provider response
|
|
180
|
+
|
|
181
|
+
`options` accepts `customerId`, `agentId`, `runId`, `revenueUnitCents`,
|
|
182
|
+
`provider` (override; e.g. `'openai_compatible'`), `providerCostCents`, and `onError`.
|
|
104
183
|
|
|
105
184
|
Licensed for use with Tollgate. Not open source.
|
package/dist/index.cjs
CHANGED
|
@@ -64,7 +64,23 @@ function createTollgateClient(opts = {}) {
|
|
|
64
64
|
}
|
|
65
65
|
throw lastErr instanceof Error ? lastErr : new TollgateError("Tollgate track failed after retries");
|
|
66
66
|
}
|
|
67
|
-
|
|
67
|
+
function resolve(input) {
|
|
68
|
+
return track({
|
|
69
|
+
customerId: input.customerId,
|
|
70
|
+
runId: input.runId,
|
|
71
|
+
agentId: input.agentId,
|
|
72
|
+
// A resolution isn't a provider call; zero usage ⇒ zero cost.
|
|
73
|
+
provider: input.provider ?? "anthropic",
|
|
74
|
+
model: input.model ?? "resolution",
|
|
75
|
+
tokensIn: 0,
|
|
76
|
+
tokensOut: 0,
|
|
77
|
+
outcome: input.outcome,
|
|
78
|
+
revenueUnitCents: input.revenueUnitCents,
|
|
79
|
+
idempotencyKey: input.idempotencyKey ?? `${input.runId}#resolve`,
|
|
80
|
+
ts: input.ts
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return { track, resolve };
|
|
68
84
|
}
|
|
69
85
|
|
|
70
86
|
// src/instrument.ts
|
|
@@ -80,31 +96,105 @@ function resolveRunId(opts, responseId) {
|
|
|
80
96
|
function resolveRevenue(opts, response) {
|
|
81
97
|
return typeof opts.revenueUnitCents === "function" ? opts.revenueUnitCents(response) : opts.revenueUnitCents;
|
|
82
98
|
}
|
|
99
|
+
function resolveCost(opts, response) {
|
|
100
|
+
return typeof opts.providerCostCents === "function" ? opts.providerCostCents(response) : opts.providerCostCents;
|
|
101
|
+
}
|
|
102
|
+
function withCost(event, opts, response) {
|
|
103
|
+
const cost = resolveCost(opts, response);
|
|
104
|
+
if (cost !== void 0) event.providerCostCents = cost;
|
|
105
|
+
return event;
|
|
106
|
+
}
|
|
83
107
|
function fireAndForget(p, onError) {
|
|
84
108
|
p.catch((err) => (onError ?? ((e) => console.warn("[tollgate] track failed:", e)))(err));
|
|
85
109
|
}
|
|
110
|
+
function isAsyncIterable(x) {
|
|
111
|
+
return x != null && typeof x[Symbol.asyncIterator] === "function";
|
|
112
|
+
}
|
|
113
|
+
function instrumentStream(stream, onChunk, onDone) {
|
|
114
|
+
let finished = false;
|
|
115
|
+
const finish = () => {
|
|
116
|
+
if (finished) return;
|
|
117
|
+
finished = true;
|
|
118
|
+
onDone();
|
|
119
|
+
};
|
|
120
|
+
return new Proxy(stream, {
|
|
121
|
+
get(target, prop, recv) {
|
|
122
|
+
if (prop === Symbol.asyncIterator) {
|
|
123
|
+
return function instrumentedIterator() {
|
|
124
|
+
const inner = target[Symbol.asyncIterator]();
|
|
125
|
+
return {
|
|
126
|
+
async next(...a) {
|
|
127
|
+
const r = await inner.next(...a);
|
|
128
|
+
if (r.done) finish();
|
|
129
|
+
else onChunk(r.value);
|
|
130
|
+
return r;
|
|
131
|
+
},
|
|
132
|
+
async return(v) {
|
|
133
|
+
finish();
|
|
134
|
+
return inner.return ? inner.return(v) : { done: true, value: v };
|
|
135
|
+
},
|
|
136
|
+
async throw(e) {
|
|
137
|
+
finish();
|
|
138
|
+
if (inner.throw) return inner.throw(e);
|
|
139
|
+
throw e;
|
|
140
|
+
},
|
|
141
|
+
[Symbol.asyncIterator]() {
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return Reflect.get(target, prop, recv);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
86
151
|
function anthropicEventFrom(msg, opts) {
|
|
87
152
|
const usage = msg?.usage;
|
|
88
153
|
if (!usage) return null;
|
|
89
154
|
const runId = resolveRunId(opts, msg.id);
|
|
90
|
-
|
|
155
|
+
const fivem = usage.cache_creation?.ephemeral_5m_input_tokens;
|
|
156
|
+
const oneh = usage.cache_creation?.ephemeral_1h_input_tokens;
|
|
157
|
+
const hasSplit = fivem !== void 0 || oneh !== void 0;
|
|
158
|
+
const event = {
|
|
91
159
|
customerId: opts.customerId,
|
|
92
160
|
agentId: opts.agentId,
|
|
93
161
|
runId,
|
|
94
|
-
provider: "anthropic",
|
|
162
|
+
provider: opts.provider ?? "anthropic",
|
|
95
163
|
model: msg.model ?? "unknown",
|
|
96
164
|
tokensIn: usage.input_tokens ?? 0,
|
|
97
165
|
tokensOut: usage.output_tokens ?? 0,
|
|
98
166
|
cachedTokens: usage.cache_read_input_tokens ?? 0,
|
|
167
|
+
cacheWrite5mTokens: hasSplit ? fivem ?? 0 : usage.cache_creation_input_tokens ?? 0,
|
|
168
|
+
cacheWrite1hTokens: hasSplit ? oneh ?? 0 : 0,
|
|
99
169
|
revenueUnitCents: resolveRevenue(opts, msg),
|
|
100
170
|
idempotencyKey: msg.id ?? `${runId}#${randomId()}`
|
|
101
171
|
};
|
|
172
|
+
return withCost(event, opts, msg);
|
|
102
173
|
}
|
|
103
174
|
function wrapAnthropic(client, tollgate, opts) {
|
|
104
175
|
const messages = client.messages;
|
|
105
176
|
const original = messages.create.bind(messages);
|
|
106
177
|
const create = async (...args) => {
|
|
107
178
|
const result = await original(...args);
|
|
179
|
+
if (isAsyncIterable(result)) {
|
|
180
|
+
const msg = {};
|
|
181
|
+
return instrumentStream(
|
|
182
|
+
result,
|
|
183
|
+
(ev) => {
|
|
184
|
+
if (ev.type === "message_start" && ev.message) {
|
|
185
|
+
msg.id = ev.message.id;
|
|
186
|
+
msg.model = ev.message.model;
|
|
187
|
+
msg.usage = { ...ev.message.usage };
|
|
188
|
+
} else if (ev.type === "message_delta" && ev.usage) {
|
|
189
|
+
msg.usage = { ...msg.usage ?? {}, output_tokens: ev.usage.output_tokens };
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
() => {
|
|
193
|
+
const event2 = anthropicEventFrom(msg, opts);
|
|
194
|
+
if (event2) fireAndForget(tollgate.track(event2), opts.onError);
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
}
|
|
108
198
|
const event = anthropicEventFrom(result, opts);
|
|
109
199
|
if (event) fireAndForget(tollgate.track(event), opts.onError);
|
|
110
200
|
return result;
|
|
@@ -124,11 +214,11 @@ function openAIEventFrom(completion, opts) {
|
|
|
124
214
|
const usage = completion?.usage;
|
|
125
215
|
if (!usage) return null;
|
|
126
216
|
const runId = resolveRunId(opts, completion.id);
|
|
127
|
-
|
|
217
|
+
const event = {
|
|
128
218
|
customerId: opts.customerId,
|
|
129
219
|
agentId: opts.agentId,
|
|
130
220
|
runId,
|
|
131
|
-
provider: "openai",
|
|
221
|
+
provider: opts.provider ?? "openai",
|
|
132
222
|
model: completion.model ?? "unknown",
|
|
133
223
|
tokensIn: usage.prompt_tokens ?? 0,
|
|
134
224
|
tokensOut: usage.completion_tokens ?? 0,
|
|
@@ -137,12 +227,31 @@ function openAIEventFrom(completion, opts) {
|
|
|
137
227
|
revenueUnitCents: resolveRevenue(opts, completion),
|
|
138
228
|
idempotencyKey: completion.id ?? `${runId}#${randomId()}`
|
|
139
229
|
};
|
|
230
|
+
return withCost(event, opts, completion);
|
|
140
231
|
}
|
|
141
232
|
function wrapOpenAI(client, tollgate, opts) {
|
|
142
233
|
const completions = client.chat.completions;
|
|
143
234
|
const original = completions.create.bind(completions);
|
|
144
235
|
const create = async (...args) => {
|
|
145
236
|
const result = await original(...args);
|
|
237
|
+
if (isAsyncIterable(result)) {
|
|
238
|
+
let id;
|
|
239
|
+
let model;
|
|
240
|
+
let usage;
|
|
241
|
+
return instrumentStream(
|
|
242
|
+
result,
|
|
243
|
+
(chunk) => {
|
|
244
|
+
if (chunk.id) id = chunk.id;
|
|
245
|
+
if (chunk.model) model = chunk.model;
|
|
246
|
+
if (chunk.usage) usage = chunk.usage;
|
|
247
|
+
},
|
|
248
|
+
() => {
|
|
249
|
+
if (!usage) return;
|
|
250
|
+
const event2 = openAIEventFrom({ id, model, usage }, opts);
|
|
251
|
+
if (event2) fireAndForget(tollgate.track(event2), opts.onError);
|
|
252
|
+
}
|
|
253
|
+
);
|
|
254
|
+
}
|
|
146
255
|
const event = openAIEventFrom(result, opts);
|
|
147
256
|
if (event) fireAndForget(tollgate.track(event), opts.onError);
|
|
148
257
|
return result;
|
|
@@ -161,12 +270,64 @@ function wrapOpenAI(client, tollgate, opts) {
|
|
|
161
270
|
}
|
|
162
271
|
});
|
|
163
272
|
}
|
|
273
|
+
function bedrockEventFrom(usage, model, opts, response = void 0) {
|
|
274
|
+
if (!usage) return null;
|
|
275
|
+
const runId = resolveRunId(opts, void 0);
|
|
276
|
+
const event = {
|
|
277
|
+
customerId: opts.customerId,
|
|
278
|
+
agentId: opts.agentId,
|
|
279
|
+
runId,
|
|
280
|
+
provider: opts.provider ?? "bedrock",
|
|
281
|
+
model,
|
|
282
|
+
tokensIn: usage.inputTokens ?? 0,
|
|
283
|
+
tokensOut: usage.outputTokens ?? 0,
|
|
284
|
+
cachedTokens: usage.cacheReadInputTokens ?? 0,
|
|
285
|
+
cacheWrite5mTokens: usage.cacheWriteInputTokens ?? 0,
|
|
286
|
+
revenueUnitCents: resolveRevenue(opts, response),
|
|
287
|
+
idempotencyKey: `${runId}#${randomId()}`
|
|
288
|
+
};
|
|
289
|
+
return withCost(event, opts, response);
|
|
290
|
+
}
|
|
291
|
+
function wrapBedrock(client, tollgate, opts) {
|
|
292
|
+
const originalSend = client.send.bind(client);
|
|
293
|
+
const send = async (command, ...rest) => {
|
|
294
|
+
const result = await originalSend(command, ...rest);
|
|
295
|
+
const model = command?.input?.modelId ?? "unknown";
|
|
296
|
+
if (result?.stream && isAsyncIterable(result.stream)) {
|
|
297
|
+
let usage;
|
|
298
|
+
result.stream = instrumentStream(
|
|
299
|
+
result.stream,
|
|
300
|
+
(ev) => {
|
|
301
|
+
if (ev.metadata?.usage) usage = ev.metadata.usage;
|
|
302
|
+
},
|
|
303
|
+
() => {
|
|
304
|
+
const event = bedrockEventFrom(usage, model, opts, result);
|
|
305
|
+
if (event) fireAndForget(tollgate.track(event), opts.onError);
|
|
306
|
+
}
|
|
307
|
+
);
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
310
|
+
if (result?.usage) {
|
|
311
|
+
const event = bedrockEventFrom(result.usage, model, opts, result);
|
|
312
|
+
if (event) fireAndForget(tollgate.track(event), opts.onError);
|
|
313
|
+
}
|
|
314
|
+
return result;
|
|
315
|
+
};
|
|
316
|
+
return new Proxy(client, {
|
|
317
|
+
get(target, prop, recv) {
|
|
318
|
+
if (prop === "send") return send;
|
|
319
|
+
return Reflect.get(target, prop, recv);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
}
|
|
164
323
|
|
|
165
324
|
exports.TollgateError = TollgateError;
|
|
166
325
|
exports.anthropicEventFrom = anthropicEventFrom;
|
|
326
|
+
exports.bedrockEventFrom = bedrockEventFrom;
|
|
167
327
|
exports.createTollgateClient = createTollgateClient;
|
|
168
328
|
exports.openAIEventFrom = openAIEventFrom;
|
|
169
329
|
exports.wrapAnthropic = wrapAnthropic;
|
|
330
|
+
exports.wrapBedrock = wrapBedrock;
|
|
170
331
|
exports.wrapOpenAI = wrapOpenAI;
|
|
171
332
|
//# sourceMappingURL=index.cjs.map
|
|
172
333
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/instrument.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { TrackEventInput, TrackResult } from './types';\n\nexport interface TollgateClientOptions {\n /** Account API key (`tg_live_…`). Falls back to `process.env.TOLLGATE_API_KEY`. */\n apiKey?: string;\n /** Base URL of your Tollgate deployment. Defaults to `TOLLGATE_BASE_URL` or production. */\n baseUrl?: string;\n /** Per-request timeout in ms. Default 10_000. */\n timeoutMs?: number;\n /** Retry attempts on network error / 5xx / 429. Default 2. */\n maxRetries?: number;\n /** Custom fetch (for testing or non-standard runtimes). Defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nexport class TollgateError extends Error {\n constructor(\n message: string,\n readonly status?: number,\n readonly body?: unknown,\n ) {\n super(message);\n this.name = 'TollgateError';\n }\n}\n\nconst DEFAULT_BASE_URL = 'https://tollgateai.vercel.app';\n\nexport interface TollgateClient {\n /** Report a single usage event. Idempotent on `idempotencyKey`. */\n track(event: TrackEventInput): Promise<TrackResult>;\n}\n\nfunction getEnv(key: string): string | undefined {\n // Guarded so the SDK works in browsers/edge runtimes without `process`.\n return typeof process !== 'undefined' ? process.env?.[key] : undefined;\n}\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\nexport function createTollgateClient(opts: TollgateClientOptions = {}): TollgateClient {\n const apiKey = opts.apiKey ?? getEnv('TOLLGATE_API_KEY');\n const baseUrl = (opts.baseUrl ?? getEnv('TOLLGATE_BASE_URL') ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n const timeoutMs = opts.timeoutMs ?? 10_000;\n const maxRetries = opts.maxRetries ?? 2;\n const doFetch = opts.fetch ?? globalThis.fetch;\n\n if (typeof doFetch !== 'function') {\n throw new TollgateError('No fetch implementation available — pass `fetch` in options.');\n }\n\n async function track(event: TrackEventInput): Promise<TrackResult> {\n if (!apiKey) {\n throw new TollgateError('Missing API key — set opts.apiKey or TOLLGATE_API_KEY.');\n }\n\n let lastErr: unknown;\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await doFetch(`${baseUrl}/api/track`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(event),\n signal: controller.signal,\n });\n\n // 200 = idempotent duplicate, 201 = created. Both are success.\n if (res.ok) {\n return (await res.json()) as TrackResult;\n }\n\n // Retry transient failures; fail fast on 4xx (except 429).\n if (res.status >= 500 || res.status === 429) {\n lastErr = new TollgateError(`Tollgate track failed (${res.status})`, res.status);\n } else {\n const body = await res.json().catch(() => ({}));\n throw new TollgateError(`Tollgate track failed (${res.status})`, res.status, body);\n }\n } catch (err) {\n // Don't retry deterministic client errors.\n if (err instanceof TollgateError && err.status && err.status < 500 && err.status !== 429) {\n throw err;\n }\n lastErr = err;\n } finally {\n clearTimeout(timer);\n }\n\n if (attempt < maxRetries) {\n await sleep(2 ** attempt * 200); // 200ms, 400ms, …\n }\n }\n\n throw lastErr instanceof Error\n ? lastErr\n : new TollgateError('Tollgate track failed after retries');\n }\n\n return { track };\n}\n","// Auto-instrumentation: wrap a provider client so every completion reports its\n// REAL usage to Tollgate — no manual token counting. Wrappers are structurally\n// typed, so this package never has to depend on the provider SDKs.\n\nimport type { TollgateClient } from './client';\nimport type { TrackEventInput } from './types';\n\nexport interface InstrumentOptions {\n /** Your end customer's stable id. Required for margin attribution. */\n customerId: string;\n /** Optional agent/workflow id. */\n agentId?: string;\n /** Revenue per call in cents (or a function of the response). */\n revenueUnitCents?: number | ((response: unknown) => number | undefined);\n /** Override the run id; defaults to the provider response id. */\n runId?: string | (() => string);\n /** Called if a background track() fails. Defaults to console.warn. */\n onError?: (err: unknown) => void;\n}\n\nfunction randomId(): string {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (c?.randomUUID) return c.randomUUID();\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction resolveRunId(opts: InstrumentOptions, responseId?: string): string {\n if (typeof opts.runId === 'function') return opts.runId();\n return opts.runId ?? responseId ?? randomId();\n}\n\nfunction resolveRevenue(opts: InstrumentOptions, response: unknown): number | undefined {\n return typeof opts.revenueUnitCents === 'function'\n ? opts.revenueUnitCents(response)\n : opts.revenueUnitCents;\n}\n\nfunction fireAndForget(p: Promise<unknown>, onError?: InstrumentOptions['onError']): void {\n p.catch((err) => (onError ?? ((e) => console.warn('[tollgate] track failed:', e)))(err));\n}\n\n// --- Anthropic ------------------------------------------------------------\n\ninterface AnthropicUsage {\n input_tokens?: number;\n output_tokens?: number;\n cache_read_input_tokens?: number;\n}\ninterface AnthropicMessage {\n id?: string;\n model?: string;\n usage?: AnthropicUsage;\n}\n\n/** Map a non-streaming Anthropic message to a track payload (or null if no usage). */\nexport function anthropicEventFrom(\n msg: AnthropicMessage,\n opts: InstrumentOptions,\n): TrackEventInput | null {\n const usage = msg?.usage;\n if (!usage) return null;\n const runId = resolveRunId(opts, msg.id);\n return {\n customerId: opts.customerId,\n agentId: opts.agentId,\n runId,\n provider: 'anthropic',\n model: msg.model ?? 'unknown',\n tokensIn: usage.input_tokens ?? 0,\n tokensOut: usage.output_tokens ?? 0,\n cachedTokens: usage.cache_read_input_tokens ?? 0,\n revenueUnitCents: resolveRevenue(opts, msg),\n idempotencyKey: msg.id ?? `${runId}#${randomId()}`,\n };\n}\n\ninterface AnthropicLike {\n messages: { create: (...args: never[]) => Promise<unknown> };\n}\n\n/** Wrap an Anthropic client so `messages.create` auto-reports usage. */\nexport function wrapAnthropic<T extends AnthropicLike>(\n client: T,\n tollgate: TollgateClient,\n opts: InstrumentOptions,\n): T {\n const messages = client.messages;\n const original = messages.create.bind(messages) as (...a: never[]) => Promise<unknown>;\n\n const create = async (...args: never[]): Promise<unknown> => {\n const result = await original(...args);\n const event = anthropicEventFrom(result as AnthropicMessage, opts);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n return result;\n };\n\n return new Proxy(client, {\n get(target, prop, recv) {\n if (prop === 'messages') {\n return new Proxy(messages, {\n get: (m, p, r) => (p === 'create' ? create : Reflect.get(m, p, r)),\n });\n }\n return Reflect.get(target, prop, recv);\n },\n });\n}\n\n// --- OpenAI ---------------------------------------------------------------\n\ninterface OpenAIUsage {\n prompt_tokens?: number;\n completion_tokens?: number;\n completion_tokens_details?: { reasoning_tokens?: number };\n prompt_tokens_details?: { cached_tokens?: number };\n}\ninterface OpenAICompletion {\n id?: string;\n model?: string;\n usage?: OpenAIUsage;\n}\n\n/** Map a non-streaming OpenAI chat completion to a track payload (or null). */\nexport function openAIEventFrom(\n completion: OpenAICompletion,\n opts: InstrumentOptions,\n): TrackEventInput | null {\n const usage = completion?.usage;\n if (!usage) return null;\n const runId = resolveRunId(opts, completion.id);\n return {\n customerId: opts.customerId,\n agentId: opts.agentId,\n runId,\n provider: 'openai',\n model: completion.model ?? 'unknown',\n tokensIn: usage.prompt_tokens ?? 0,\n tokensOut: usage.completion_tokens ?? 0,\n reasoningTokens: usage.completion_tokens_details?.reasoning_tokens ?? 0,\n cachedTokens: usage.prompt_tokens_details?.cached_tokens ?? 0,\n revenueUnitCents: resolveRevenue(opts, completion),\n idempotencyKey: completion.id ?? `${runId}#${randomId()}`,\n };\n}\n\ninterface OpenAILike {\n chat: { completions: { create: (...args: never[]) => Promise<unknown> } };\n}\n\n/** Wrap an OpenAI client so `chat.completions.create` auto-reports usage. */\nexport function wrapOpenAI<T extends OpenAILike>(\n client: T,\n tollgate: TollgateClient,\n opts: InstrumentOptions,\n): T {\n const completions = client.chat.completions;\n const original = completions.create.bind(completions) as (...a: never[]) => Promise<unknown>;\n\n const create = async (...args: never[]): Promise<unknown> => {\n const result = await original(...args);\n const event = openAIEventFrom(result as OpenAICompletion, opts);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n return result;\n };\n\n return new Proxy(client, {\n get(target, prop, recv) {\n if (prop === 'chat') {\n const chat = Reflect.get(target, prop, recv) as OpenAILike['chat'];\n return new Proxy(chat, {\n get: (c, p, r) =>\n p === 'completions'\n ? new Proxy(completions, {\n get: (co, pp, rr) => (pp === 'create' ? create : Reflect.get(co, pp, rr)),\n })\n : Reflect.get(c, p, r),\n });\n }\n return Reflect.get(target, prop, recv);\n },\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/instrument.ts"],"names":["event"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Provider, RunOutcome, TrackEventInput, TrackResult } from './types';\n\nexport interface TollgateClientOptions {\n /** Account API key (`tg_live_…`). Falls back to `process.env.TOLLGATE_API_KEY`. */\n apiKey?: string;\n /** Base URL of your Tollgate deployment. Defaults to `TOLLGATE_BASE_URL` or production. */\n baseUrl?: string;\n /** Per-request timeout in ms. Default 10_000. */\n timeoutMs?: number;\n /** Retry attempts on network error / 5xx / 429. Default 2. */\n maxRetries?: number;\n /** Custom fetch (for testing or non-standard runtimes). Defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nexport class TollgateError extends Error {\n constructor(\n message: string,\n readonly status?: number,\n readonly body?: unknown,\n ) {\n super(message);\n this.name = 'TollgateError';\n }\n}\n\nconst DEFAULT_BASE_URL = 'https://tollgateai.vercel.app';\n\n/** Close out a run with its outcome (and, if resolved, the revenue it earns).\n * A resolution carries no provider usage, so `provider`/`model` are optional. */\nexport interface ResolveInput {\n /** The run being closed (must match the runId your usage events used). */\n runId: string;\n customerId: string;\n /** 'resolved' books revenue; 'escalated'/'failed' book none (cost still counts). */\n outcome: RunOutcome;\n /** Revenue in cents for a resolved run (e.g. 50 for $0.50). Ignored if not resolved. */\n revenueUnitCents?: number;\n agentId?: string;\n /** Idempotency key for the closing event. Defaults to `${runId}#resolve`. */\n idempotencyKey?: string;\n ts?: string;\n /** Rarely needed — a resolution isn't a provider call. Default 'anthropic'/'resolution'. */\n provider?: Provider;\n model?: string;\n}\n\nexport interface TollgateClient {\n /** Report a single usage event. Idempotent on `idempotencyKey`. */\n track(event: TrackEventInput): Promise<TrackResult>;\n /** Close a run with its outcome — book revenue once, only if resolved.\n * Convenience over `track()`: sends a zero-usage terminal event. */\n resolve(input: ResolveInput): Promise<TrackResult>;\n}\n\nfunction getEnv(key: string): string | undefined {\n // Guarded so the SDK works in browsers/edge runtimes without `process`.\n return typeof process !== 'undefined' ? process.env?.[key] : undefined;\n}\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\nexport function createTollgateClient(opts: TollgateClientOptions = {}): TollgateClient {\n const apiKey = opts.apiKey ?? getEnv('TOLLGATE_API_KEY');\n const baseUrl = (opts.baseUrl ?? getEnv('TOLLGATE_BASE_URL') ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n const timeoutMs = opts.timeoutMs ?? 10_000;\n const maxRetries = opts.maxRetries ?? 2;\n const doFetch = opts.fetch ?? globalThis.fetch;\n\n if (typeof doFetch !== 'function') {\n throw new TollgateError('No fetch implementation available — pass `fetch` in options.');\n }\n\n async function track(event: TrackEventInput): Promise<TrackResult> {\n if (!apiKey) {\n throw new TollgateError('Missing API key — set opts.apiKey or TOLLGATE_API_KEY.');\n }\n\n let lastErr: unknown;\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await doFetch(`${baseUrl}/api/track`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(event),\n signal: controller.signal,\n });\n\n // 200 = idempotent duplicate, 201 = created. Both are success.\n if (res.ok) {\n return (await res.json()) as TrackResult;\n }\n\n // Retry transient failures; fail fast on 4xx (except 429).\n if (res.status >= 500 || res.status === 429) {\n lastErr = new TollgateError(`Tollgate track failed (${res.status})`, res.status);\n } else {\n const body = await res.json().catch(() => ({}));\n throw new TollgateError(`Tollgate track failed (${res.status})`, res.status, body);\n }\n } catch (err) {\n // Don't retry deterministic client errors.\n if (err instanceof TollgateError && err.status && err.status < 500 && err.status !== 429) {\n throw err;\n }\n lastErr = err;\n } finally {\n clearTimeout(timer);\n }\n\n if (attempt < maxRetries) {\n await sleep(2 ** attempt * 200); // 200ms, 400ms, …\n }\n }\n\n throw lastErr instanceof Error\n ? lastErr\n : new TollgateError('Tollgate track failed after retries');\n }\n\n function resolve(input: ResolveInput): Promise<TrackResult> {\n return track({\n customerId: input.customerId,\n runId: input.runId,\n agentId: input.agentId,\n // A resolution isn't a provider call; zero usage ⇒ zero cost.\n provider: input.provider ?? 'anthropic',\n model: input.model ?? 'resolution',\n tokensIn: 0,\n tokensOut: 0,\n outcome: input.outcome,\n revenueUnitCents: input.revenueUnitCents,\n idempotencyKey: input.idempotencyKey ?? `${input.runId}#resolve`,\n ts: input.ts,\n });\n }\n\n return { track, resolve };\n}\n","// Auto-instrumentation: wrap a provider client so every completion reports its\n// REAL usage to Tollgate — no manual token counting. Wrappers are structurally\n// typed, so this package never has to depend on the provider SDKs.\n//\n// Coverage is universal: OpenAI + Anthropic native, every OpenAI-compatible\n// gateway (set `provider: 'openai_compatible'`), and AWS Bedrock — each in both\n// non-streaming and streaming modes. Cost is always derived server-side from the\n// token counts these wrappers capture, so no provider needs to return a dollar\n// figure (pass `providerCostCents` only if you already have one).\n\nimport type { TollgateClient } from './client';\nimport type { Provider, TrackEventInput } from './types';\n\nexport interface InstrumentOptions {\n /** Your end customer's stable id. Required for margin attribution. */\n customerId: string;\n /** Optional agent/workflow id. */\n agentId?: string;\n /** Override the reported provider. Defaults per wrapper ('openai' /\n * 'anthropic' / 'bedrock'). Set to 'openai_compatible' when the client points\n * at an OpenAI-shaped gateway (Vercel AI Gateway, OpenRouter, Groq, Together,\n * Nebius, local vLLM, …) so the server prices it by the gateway-echoed model. */\n provider?: Provider;\n /** Revenue per call in cents (or a function of the response). */\n revenueUnitCents?: number | ((response: unknown) => number | undefined);\n /** Provider/gateway-reported cost in cents (or a function of the response).\n * When present and > 0, the server uses it verbatim and skips the rate card —\n * the authoritative escape hatch when you already have an exact cost. */\n providerCostCents?: number | ((response: unknown) => number | undefined);\n /** Override the run id; defaults to the provider response id. */\n runId?: string | (() => string);\n /** Called if a background track() fails. Defaults to console.warn. */\n onError?: (err: unknown) => void;\n}\n\nfunction randomId(): string {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (c?.randomUUID) return c.randomUUID();\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction resolveRunId(opts: InstrumentOptions, responseId?: string): string {\n if (typeof opts.runId === 'function') return opts.runId();\n return opts.runId ?? responseId ?? randomId();\n}\n\nfunction resolveRevenue(opts: InstrumentOptions, response: unknown): number | undefined {\n return typeof opts.revenueUnitCents === 'function'\n ? opts.revenueUnitCents(response)\n : opts.revenueUnitCents;\n}\n\nfunction resolveCost(opts: InstrumentOptions, response: unknown): number | undefined {\n return typeof opts.providerCostCents === 'function'\n ? opts.providerCostCents(response)\n : opts.providerCostCents;\n}\n\n/** Attach providerCostCents to an event only when a (non-undefined) value resolves. */\nfunction withCost(\n event: TrackEventInput,\n opts: InstrumentOptions,\n response: unknown,\n): TrackEventInput {\n const cost = resolveCost(opts, response);\n if (cost !== undefined) event.providerCostCents = cost;\n return event;\n}\n\nfunction fireAndForget(p: Promise<unknown>, onError?: InstrumentOptions['onError']): void {\n p.catch((err) => (onError ?? ((e) => console.warn('[tollgate] track failed:', e)))(err));\n}\n\nfunction isAsyncIterable(x: unknown): x is AsyncIterable<unknown> {\n return x != null && typeof (x as Record<symbol, unknown>)[Symbol.asyncIterator] === 'function';\n}\n\n/**\n * Wrap an async iterable (a provider stream) so each chunk is observed and a\n * finalizer runs once the stream is exhausted — without disturbing the stream's\n * other methods (`.tee()`, `.controller`, …), which are proxied through. If the\n * consumer abandons the stream early the finalizer still fires on `.return()`.\n */\nfunction instrumentStream<TChunk>(\n stream: AsyncIterable<TChunk>,\n onChunk: (chunk: TChunk) => void,\n onDone: () => void,\n): AsyncIterable<TChunk> {\n let finished = false;\n const finish = () => {\n if (finished) return;\n finished = true;\n onDone();\n };\n return new Proxy(stream, {\n get(target, prop, recv) {\n if (prop === Symbol.asyncIterator) {\n return function instrumentedIterator() {\n const inner = (target as AsyncIterable<TChunk>)[Symbol.asyncIterator]();\n return {\n async next(...a: []) {\n const r = await inner.next(...a);\n if (r.done) finish();\n else onChunk(r.value);\n return r;\n },\n async return(v?: unknown) {\n finish();\n return inner.return ? inner.return(v) : { done: true, value: v as TChunk };\n },\n async throw(e?: unknown) {\n finish();\n if (inner.throw) return inner.throw(e);\n throw e;\n },\n [Symbol.asyncIterator]() {\n return this;\n },\n };\n };\n }\n return Reflect.get(target, prop, recv);\n },\n });\n}\n\n// --- Anthropic ------------------------------------------------------------\n\ninterface AnthropicUsage {\n input_tokens?: number;\n output_tokens?: number;\n cache_read_input_tokens?: number;\n // Cache CREATION (writes) bill above the input rate. Newer responses break the\n // total down by TTL; older ones only return the aggregate cache_creation count.\n cache_creation_input_tokens?: number;\n cache_creation?: {\n ephemeral_5m_input_tokens?: number;\n ephemeral_1h_input_tokens?: number;\n };\n}\ninterface AnthropicMessage {\n id?: string;\n model?: string;\n usage?: AnthropicUsage;\n}\n// Streaming event shapes we read usage from (message_start carries inputs +\n// cache, message_delta carries the cumulative output token count).\ninterface AnthropicStreamEvent {\n type?: string;\n message?: AnthropicMessage;\n usage?: AnthropicUsage;\n}\n\n/** Map a non-streaming Anthropic message to a track payload (or null if no usage). */\nexport function anthropicEventFrom(\n msg: AnthropicMessage,\n opts: InstrumentOptions,\n): TrackEventInput | null {\n const usage = msg?.usage;\n if (!usage) return null;\n const runId = resolveRunId(opts, msg.id);\n // Split cache-creation tokens by TTL when the response provides the breakdown;\n // otherwise attribute the whole cache_creation total to the default 5-minute TTL.\n const fivem = usage.cache_creation?.ephemeral_5m_input_tokens;\n const oneh = usage.cache_creation?.ephemeral_1h_input_tokens;\n const hasSplit = fivem !== undefined || oneh !== undefined;\n const event: TrackEventInput = {\n customerId: opts.customerId,\n agentId: opts.agentId,\n runId,\n provider: opts.provider ?? 'anthropic',\n model: msg.model ?? 'unknown',\n tokensIn: usage.input_tokens ?? 0,\n tokensOut: usage.output_tokens ?? 0,\n cachedTokens: usage.cache_read_input_tokens ?? 0,\n cacheWrite5mTokens: hasSplit ? fivem ?? 0 : usage.cache_creation_input_tokens ?? 0,\n cacheWrite1hTokens: hasSplit ? oneh ?? 0 : 0,\n revenueUnitCents: resolveRevenue(opts, msg),\n idempotencyKey: msg.id ?? `${runId}#${randomId()}`,\n };\n return withCost(event, opts, msg);\n}\n\ninterface AnthropicLike {\n messages: { create: (...args: never[]) => Promise<unknown> };\n}\n\n/** Wrap an Anthropic client so `messages.create` auto-reports usage (streaming\n * and non-streaming). */\nexport function wrapAnthropic<T extends AnthropicLike>(\n client: T,\n tollgate: TollgateClient,\n opts: InstrumentOptions,\n): T {\n const messages = client.messages;\n const original = messages.create.bind(messages) as (...a: never[]) => Promise<unknown>;\n\n const create = async (...args: never[]): Promise<unknown> => {\n const result = await original(...args);\n if (isAsyncIterable(result)) {\n // Reconstruct a message from the event stream, then reuse the non-stream\n // mapper. input/cache tokens arrive in message_start; the final cumulative\n // output token count arrives in the last message_delta.\n const msg: AnthropicMessage = {};\n return instrumentStream(\n result as AsyncIterable<AnthropicStreamEvent>,\n (ev) => {\n if (ev.type === 'message_start' && ev.message) {\n msg.id = ev.message.id;\n msg.model = ev.message.model;\n msg.usage = { ...ev.message.usage };\n } else if (ev.type === 'message_delta' && ev.usage) {\n msg.usage = { ...(msg.usage ?? {}), output_tokens: ev.usage.output_tokens };\n }\n },\n () => {\n const event = anthropicEventFrom(msg, opts);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n },\n );\n }\n const event = anthropicEventFrom(result as AnthropicMessage, opts);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n return result;\n };\n\n return new Proxy(client, {\n get(target, prop, recv) {\n if (prop === 'messages') {\n return new Proxy(messages, {\n get: (m, p, r) => (p === 'create' ? create : Reflect.get(m, p, r)),\n });\n }\n return Reflect.get(target, prop, recv);\n },\n });\n}\n\n// --- OpenAI (and OpenAI-compatible gateways) ------------------------------\n\ninterface OpenAIUsage {\n prompt_tokens?: number;\n completion_tokens?: number;\n completion_tokens_details?: { reasoning_tokens?: number };\n prompt_tokens_details?: { cached_tokens?: number };\n}\ninterface OpenAICompletion {\n id?: string;\n model?: string;\n usage?: OpenAIUsage;\n}\n\n/** Map a non-streaming OpenAI chat completion to a track payload (or null). */\nexport function openAIEventFrom(\n completion: OpenAICompletion,\n opts: InstrumentOptions,\n): TrackEventInput | null {\n const usage = completion?.usage;\n if (!usage) return null;\n const runId = resolveRunId(opts, completion.id);\n const event: TrackEventInput = {\n customerId: opts.customerId,\n agentId: opts.agentId,\n runId,\n provider: opts.provider ?? 'openai',\n model: completion.model ?? 'unknown',\n tokensIn: usage.prompt_tokens ?? 0,\n tokensOut: usage.completion_tokens ?? 0,\n reasoningTokens: usage.completion_tokens_details?.reasoning_tokens ?? 0,\n cachedTokens: usage.prompt_tokens_details?.cached_tokens ?? 0,\n revenueUnitCents: resolveRevenue(opts, completion),\n idempotencyKey: completion.id ?? `${runId}#${randomId()}`,\n };\n return withCost(event, opts, completion);\n}\n\ninterface OpenAILike {\n chat: { completions: { create: (...args: never[]) => Promise<unknown> } };\n}\n\n/** Wrap an OpenAI (or OpenAI-compatible) client so `chat.completions.create`\n * auto-reports usage. Streaming works when the caller sets\n * `stream_options: { include_usage: true }` (required for OpenAI to emit a final\n * usage chunk); without it there are no token counts to report and the call is\n * passed through untouched. */\nexport function wrapOpenAI<T extends OpenAILike>(\n client: T,\n tollgate: TollgateClient,\n opts: InstrumentOptions,\n): T {\n const completions = client.chat.completions;\n const original = completions.create.bind(completions) as (...a: never[]) => Promise<unknown>;\n\n const create = async (...args: never[]): Promise<unknown> => {\n const result = await original(...args);\n if (isAsyncIterable(result)) {\n let id: string | undefined;\n let model: string | undefined;\n let usage: OpenAIUsage | undefined;\n return instrumentStream(\n result as AsyncIterable<OpenAICompletion>,\n (chunk) => {\n if (chunk.id) id = chunk.id;\n if (chunk.model) model = chunk.model;\n if (chunk.usage) usage = chunk.usage; // only the final chunk carries it\n },\n () => {\n if (!usage) return; // caller didn't request include_usage — nothing to report\n const event = openAIEventFrom({ id, model, usage }, opts);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n },\n );\n }\n const event = openAIEventFrom(result as OpenAICompletion, opts);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n return result;\n };\n\n return new Proxy(client, {\n get(target, prop, recv) {\n if (prop === 'chat') {\n const chat = Reflect.get(target, prop, recv) as OpenAILike['chat'];\n return new Proxy(chat, {\n get: (c, p, r) =>\n p === 'completions'\n ? new Proxy(completions, {\n get: (co, pp, rr) => (pp === 'create' ? create : Reflect.get(co, pp, rr)),\n })\n : Reflect.get(c, p, r),\n });\n }\n return Reflect.get(target, prop, recv);\n },\n });\n}\n\n// --- AWS Bedrock ----------------------------------------------------------\n// Bedrock's Converse API is command-based: the model id lives on the REQUEST,\n// not the response, so we read it from the command input. Usage is reported in\n// camelCase (inputTokens/outputTokens) plus optional cache token counts.\n\ninterface BedrockUsage {\n inputTokens?: number;\n outputTokens?: number;\n cacheReadInputTokens?: number;\n cacheWriteInputTokens?: number;\n}\ninterface BedrockConverseResponse {\n usage?: BedrockUsage;\n // ConverseStream returns an async-iterable `stream` of events; the final\n // `metadata` event carries the usage totals.\n stream?: AsyncIterable<{ metadata?: { usage?: BedrockUsage } }>;\n}\n\n/** Map a Bedrock Converse response (model from the request) to a track payload. */\nexport function bedrockEventFrom(\n usage: BedrockUsage | undefined,\n model: string,\n opts: InstrumentOptions,\n response: unknown = undefined,\n): TrackEventInput | null {\n if (!usage) return null;\n const runId = resolveRunId(opts, undefined);\n const event: TrackEventInput = {\n customerId: opts.customerId,\n agentId: opts.agentId,\n runId,\n provider: opts.provider ?? 'bedrock',\n model,\n tokensIn: usage.inputTokens ?? 0,\n tokensOut: usage.outputTokens ?? 0,\n cachedTokens: usage.cacheReadInputTokens ?? 0,\n cacheWrite5mTokens: usage.cacheWriteInputTokens ?? 0,\n revenueUnitCents: resolveRevenue(opts, response),\n idempotencyKey: `${runId}#${randomId()}`,\n };\n return withCost(event, opts, response);\n}\n\ninterface BedrockLike {\n send: (command: unknown, ...rest: never[]) => Promise<unknown>;\n}\n\n/** Wrap a Bedrock Runtime client so `send(ConverseCommand)` /\n * `send(ConverseStreamCommand)` auto-report usage. Non-Converse commands (no\n * usage in the response) pass through untouched. */\nexport function wrapBedrock<T extends BedrockLike>(\n client: T,\n tollgate: TollgateClient,\n opts: InstrumentOptions,\n): T {\n const originalSend = client.send.bind(client) as BedrockLike['send'];\n\n const send = async (command: unknown, ...rest: never[]): Promise<unknown> => {\n const result = (await originalSend(command, ...rest)) as BedrockConverseResponse;\n const model =\n ((command as { input?: { modelId?: string } })?.input?.modelId) ?? 'unknown';\n\n if (result?.stream && isAsyncIterable(result.stream)) {\n let usage: BedrockUsage | undefined;\n result.stream = instrumentStream(\n result.stream,\n (ev) => {\n if (ev.metadata?.usage) usage = ev.metadata.usage;\n },\n () => {\n const event = bedrockEventFrom(usage, model, opts, result);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n },\n );\n return result;\n }\n\n if (result?.usage) {\n const event = bedrockEventFrom(result.usage, model, opts, result);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n }\n return result;\n };\n\n return new Proxy(client, {\n get(target, prop, recv) {\n if (prop === 'send') return send;\n return Reflect.get(target, prop, recv);\n },\n });\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
type Provider = 'anthropic' | 'openai' | 'bedrock';
|
|
1
|
+
type Provider = 'anthropic' | 'openai' | 'openai_compatible' | 'bedrock';
|
|
2
2
|
type EventType = 'llm' | 'tool' | 'retrieval';
|
|
3
|
+
/** Terminal outcome of a run, reported on its closing event (see `resolve()`).
|
|
4
|
+
* Under outcome-based pricing only a `resolved` run books revenue — an
|
|
5
|
+
* `escalated`/`failed` run earns nothing but its cost still counts. */
|
|
6
|
+
type RunOutcome = 'resolved' | 'escalated' | 'failed';
|
|
3
7
|
/** The payload accepted by `POST /api/track`. Money is in integer cents. */
|
|
4
8
|
interface TrackEventInput {
|
|
5
9
|
/** Your end customer's stable id (e.g. "cust_A"). Used for margin attribution. */
|
|
@@ -18,10 +22,20 @@ interface TrackEventInput {
|
|
|
18
22
|
reasoningTokens?: number;
|
|
19
23
|
/** Cached / cache-read input tokens. */
|
|
20
24
|
cachedTokens?: number;
|
|
25
|
+
/** Cache-creation (write) tokens, 5-minute TTL — billed above the input rate. */
|
|
26
|
+
cacheWrite5mTokens?: number;
|
|
27
|
+
/** Cache-creation (write) tokens, 1-hour TTL — billed at the highest rate. */
|
|
28
|
+
cacheWrite1hTokens?: number;
|
|
21
29
|
toolCalls?: number;
|
|
22
30
|
toolName?: string;
|
|
31
|
+
/** Terminal run outcome, set on the run's closing event only (omit on
|
|
32
|
+
* intermediate steps). Gates outcome-priced revenue. See `resolve()`. */
|
|
33
|
+
outcome?: RunOutcome;
|
|
23
34
|
/** Per-event revenue contribution in cents (e.g. 50 for a $0.50 unit). */
|
|
24
35
|
revenueUnitCents?: number;
|
|
36
|
+
/** Provider/gateway-reported cost in cents. When present and > 0 the server
|
|
37
|
+
* uses it verbatim and skips the rate card (fractional cents accepted). */
|
|
38
|
+
providerCostCents?: number;
|
|
25
39
|
/** Required for exactly-once ingestion (e.g. "run_12345#step_1"). */
|
|
26
40
|
idempotencyKey: string;
|
|
27
41
|
/** ISO timestamp; defaults to server receive time. */
|
|
@@ -49,9 +63,30 @@ declare class TollgateError extends Error {
|
|
|
49
63
|
readonly body?: unknown | undefined;
|
|
50
64
|
constructor(message: string, status?: number | undefined, body?: unknown | undefined);
|
|
51
65
|
}
|
|
66
|
+
/** Close out a run with its outcome (and, if resolved, the revenue it earns).
|
|
67
|
+
* A resolution carries no provider usage, so `provider`/`model` are optional. */
|
|
68
|
+
interface ResolveInput {
|
|
69
|
+
/** The run being closed (must match the runId your usage events used). */
|
|
70
|
+
runId: string;
|
|
71
|
+
customerId: string;
|
|
72
|
+
/** 'resolved' books revenue; 'escalated'/'failed' book none (cost still counts). */
|
|
73
|
+
outcome: RunOutcome;
|
|
74
|
+
/** Revenue in cents for a resolved run (e.g. 50 for $0.50). Ignored if not resolved. */
|
|
75
|
+
revenueUnitCents?: number;
|
|
76
|
+
agentId?: string;
|
|
77
|
+
/** Idempotency key for the closing event. Defaults to `${runId}#resolve`. */
|
|
78
|
+
idempotencyKey?: string;
|
|
79
|
+
ts?: string;
|
|
80
|
+
/** Rarely needed — a resolution isn't a provider call. Default 'anthropic'/'resolution'. */
|
|
81
|
+
provider?: Provider;
|
|
82
|
+
model?: string;
|
|
83
|
+
}
|
|
52
84
|
interface TollgateClient {
|
|
53
85
|
/** Report a single usage event. Idempotent on `idempotencyKey`. */
|
|
54
86
|
track(event: TrackEventInput): Promise<TrackResult>;
|
|
87
|
+
/** Close a run with its outcome — book revenue once, only if resolved.
|
|
88
|
+
* Convenience over `track()`: sends a zero-usage terminal event. */
|
|
89
|
+
resolve(input: ResolveInput): Promise<TrackResult>;
|
|
55
90
|
}
|
|
56
91
|
declare function createTollgateClient(opts?: TollgateClientOptions): TollgateClient;
|
|
57
92
|
|
|
@@ -60,8 +95,17 @@ interface InstrumentOptions {
|
|
|
60
95
|
customerId: string;
|
|
61
96
|
/** Optional agent/workflow id. */
|
|
62
97
|
agentId?: string;
|
|
98
|
+
/** Override the reported provider. Defaults per wrapper ('openai' /
|
|
99
|
+
* 'anthropic' / 'bedrock'). Set to 'openai_compatible' when the client points
|
|
100
|
+
* at an OpenAI-shaped gateway (Vercel AI Gateway, OpenRouter, Groq, Together,
|
|
101
|
+
* Nebius, local vLLM, …) so the server prices it by the gateway-echoed model. */
|
|
102
|
+
provider?: Provider;
|
|
63
103
|
/** Revenue per call in cents (or a function of the response). */
|
|
64
104
|
revenueUnitCents?: number | ((response: unknown) => number | undefined);
|
|
105
|
+
/** Provider/gateway-reported cost in cents (or a function of the response).
|
|
106
|
+
* When present and > 0, the server uses it verbatim and skips the rate card —
|
|
107
|
+
* the authoritative escape hatch when you already have an exact cost. */
|
|
108
|
+
providerCostCents?: number | ((response: unknown) => number | undefined);
|
|
65
109
|
/** Override the run id; defaults to the provider response id. */
|
|
66
110
|
runId?: string | (() => string);
|
|
67
111
|
/** Called if a background track() fails. Defaults to console.warn. */
|
|
@@ -71,6 +115,11 @@ interface AnthropicUsage {
|
|
|
71
115
|
input_tokens?: number;
|
|
72
116
|
output_tokens?: number;
|
|
73
117
|
cache_read_input_tokens?: number;
|
|
118
|
+
cache_creation_input_tokens?: number;
|
|
119
|
+
cache_creation?: {
|
|
120
|
+
ephemeral_5m_input_tokens?: number;
|
|
121
|
+
ephemeral_1h_input_tokens?: number;
|
|
122
|
+
};
|
|
74
123
|
}
|
|
75
124
|
interface AnthropicMessage {
|
|
76
125
|
id?: string;
|
|
@@ -84,7 +133,8 @@ interface AnthropicLike {
|
|
|
84
133
|
create: (...args: never[]) => Promise<unknown>;
|
|
85
134
|
};
|
|
86
135
|
}
|
|
87
|
-
/** Wrap an Anthropic client so `messages.create` auto-reports usage
|
|
136
|
+
/** Wrap an Anthropic client so `messages.create` auto-reports usage (streaming
|
|
137
|
+
* and non-streaming). */
|
|
88
138
|
declare function wrapAnthropic<T extends AnthropicLike>(client: T, tollgate: TollgateClient, opts: InstrumentOptions): T;
|
|
89
139
|
interface OpenAIUsage {
|
|
90
140
|
prompt_tokens?: number;
|
|
@@ -110,7 +160,26 @@ interface OpenAILike {
|
|
|
110
160
|
};
|
|
111
161
|
};
|
|
112
162
|
}
|
|
113
|
-
/** Wrap an OpenAI client so `chat.completions.create`
|
|
163
|
+
/** Wrap an OpenAI (or OpenAI-compatible) client so `chat.completions.create`
|
|
164
|
+
* auto-reports usage. Streaming works when the caller sets
|
|
165
|
+
* `stream_options: { include_usage: true }` (required for OpenAI to emit a final
|
|
166
|
+
* usage chunk); without it there are no token counts to report and the call is
|
|
167
|
+
* passed through untouched. */
|
|
114
168
|
declare function wrapOpenAI<T extends OpenAILike>(client: T, tollgate: TollgateClient, opts: InstrumentOptions): T;
|
|
169
|
+
interface BedrockUsage {
|
|
170
|
+
inputTokens?: number;
|
|
171
|
+
outputTokens?: number;
|
|
172
|
+
cacheReadInputTokens?: number;
|
|
173
|
+
cacheWriteInputTokens?: number;
|
|
174
|
+
}
|
|
175
|
+
/** Map a Bedrock Converse response (model from the request) to a track payload. */
|
|
176
|
+
declare function bedrockEventFrom(usage: BedrockUsage | undefined, model: string, opts: InstrumentOptions, response?: unknown): TrackEventInput | null;
|
|
177
|
+
interface BedrockLike {
|
|
178
|
+
send: (command: unknown, ...rest: never[]) => Promise<unknown>;
|
|
179
|
+
}
|
|
180
|
+
/** Wrap a Bedrock Runtime client so `send(ConverseCommand)` /
|
|
181
|
+
* `send(ConverseStreamCommand)` auto-report usage. Non-Converse commands (no
|
|
182
|
+
* usage in the response) pass through untouched. */
|
|
183
|
+
declare function wrapBedrock<T extends BedrockLike>(client: T, tollgate: TollgateClient, opts: InstrumentOptions): T;
|
|
115
184
|
|
|
116
|
-
export { type EventType, type InstrumentOptions, type Provider, type TollgateClient, type TollgateClientOptions, TollgateError, type TrackEventInput, type TrackResult, anthropicEventFrom, createTollgateClient, openAIEventFrom, wrapAnthropic, wrapOpenAI };
|
|
185
|
+
export { type EventType, type InstrumentOptions, type Provider, type ResolveInput, type RunOutcome, type TollgateClient, type TollgateClientOptions, TollgateError, type TrackEventInput, type TrackResult, anthropicEventFrom, bedrockEventFrom, createTollgateClient, openAIEventFrom, wrapAnthropic, wrapBedrock, wrapOpenAI };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
type Provider = 'anthropic' | 'openai' | 'bedrock';
|
|
1
|
+
type Provider = 'anthropic' | 'openai' | 'openai_compatible' | 'bedrock';
|
|
2
2
|
type EventType = 'llm' | 'tool' | 'retrieval';
|
|
3
|
+
/** Terminal outcome of a run, reported on its closing event (see `resolve()`).
|
|
4
|
+
* Under outcome-based pricing only a `resolved` run books revenue — an
|
|
5
|
+
* `escalated`/`failed` run earns nothing but its cost still counts. */
|
|
6
|
+
type RunOutcome = 'resolved' | 'escalated' | 'failed';
|
|
3
7
|
/** The payload accepted by `POST /api/track`. Money is in integer cents. */
|
|
4
8
|
interface TrackEventInput {
|
|
5
9
|
/** Your end customer's stable id (e.g. "cust_A"). Used for margin attribution. */
|
|
@@ -18,10 +22,20 @@ interface TrackEventInput {
|
|
|
18
22
|
reasoningTokens?: number;
|
|
19
23
|
/** Cached / cache-read input tokens. */
|
|
20
24
|
cachedTokens?: number;
|
|
25
|
+
/** Cache-creation (write) tokens, 5-minute TTL — billed above the input rate. */
|
|
26
|
+
cacheWrite5mTokens?: number;
|
|
27
|
+
/** Cache-creation (write) tokens, 1-hour TTL — billed at the highest rate. */
|
|
28
|
+
cacheWrite1hTokens?: number;
|
|
21
29
|
toolCalls?: number;
|
|
22
30
|
toolName?: string;
|
|
31
|
+
/** Terminal run outcome, set on the run's closing event only (omit on
|
|
32
|
+
* intermediate steps). Gates outcome-priced revenue. See `resolve()`. */
|
|
33
|
+
outcome?: RunOutcome;
|
|
23
34
|
/** Per-event revenue contribution in cents (e.g. 50 for a $0.50 unit). */
|
|
24
35
|
revenueUnitCents?: number;
|
|
36
|
+
/** Provider/gateway-reported cost in cents. When present and > 0 the server
|
|
37
|
+
* uses it verbatim and skips the rate card (fractional cents accepted). */
|
|
38
|
+
providerCostCents?: number;
|
|
25
39
|
/** Required for exactly-once ingestion (e.g. "run_12345#step_1"). */
|
|
26
40
|
idempotencyKey: string;
|
|
27
41
|
/** ISO timestamp; defaults to server receive time. */
|
|
@@ -49,9 +63,30 @@ declare class TollgateError extends Error {
|
|
|
49
63
|
readonly body?: unknown | undefined;
|
|
50
64
|
constructor(message: string, status?: number | undefined, body?: unknown | undefined);
|
|
51
65
|
}
|
|
66
|
+
/** Close out a run with its outcome (and, if resolved, the revenue it earns).
|
|
67
|
+
* A resolution carries no provider usage, so `provider`/`model` are optional. */
|
|
68
|
+
interface ResolveInput {
|
|
69
|
+
/** The run being closed (must match the runId your usage events used). */
|
|
70
|
+
runId: string;
|
|
71
|
+
customerId: string;
|
|
72
|
+
/** 'resolved' books revenue; 'escalated'/'failed' book none (cost still counts). */
|
|
73
|
+
outcome: RunOutcome;
|
|
74
|
+
/** Revenue in cents for a resolved run (e.g. 50 for $0.50). Ignored if not resolved. */
|
|
75
|
+
revenueUnitCents?: number;
|
|
76
|
+
agentId?: string;
|
|
77
|
+
/** Idempotency key for the closing event. Defaults to `${runId}#resolve`. */
|
|
78
|
+
idempotencyKey?: string;
|
|
79
|
+
ts?: string;
|
|
80
|
+
/** Rarely needed — a resolution isn't a provider call. Default 'anthropic'/'resolution'. */
|
|
81
|
+
provider?: Provider;
|
|
82
|
+
model?: string;
|
|
83
|
+
}
|
|
52
84
|
interface TollgateClient {
|
|
53
85
|
/** Report a single usage event. Idempotent on `idempotencyKey`. */
|
|
54
86
|
track(event: TrackEventInput): Promise<TrackResult>;
|
|
87
|
+
/** Close a run with its outcome — book revenue once, only if resolved.
|
|
88
|
+
* Convenience over `track()`: sends a zero-usage terminal event. */
|
|
89
|
+
resolve(input: ResolveInput): Promise<TrackResult>;
|
|
55
90
|
}
|
|
56
91
|
declare function createTollgateClient(opts?: TollgateClientOptions): TollgateClient;
|
|
57
92
|
|
|
@@ -60,8 +95,17 @@ interface InstrumentOptions {
|
|
|
60
95
|
customerId: string;
|
|
61
96
|
/** Optional agent/workflow id. */
|
|
62
97
|
agentId?: string;
|
|
98
|
+
/** Override the reported provider. Defaults per wrapper ('openai' /
|
|
99
|
+
* 'anthropic' / 'bedrock'). Set to 'openai_compatible' when the client points
|
|
100
|
+
* at an OpenAI-shaped gateway (Vercel AI Gateway, OpenRouter, Groq, Together,
|
|
101
|
+
* Nebius, local vLLM, …) so the server prices it by the gateway-echoed model. */
|
|
102
|
+
provider?: Provider;
|
|
63
103
|
/** Revenue per call in cents (or a function of the response). */
|
|
64
104
|
revenueUnitCents?: number | ((response: unknown) => number | undefined);
|
|
105
|
+
/** Provider/gateway-reported cost in cents (or a function of the response).
|
|
106
|
+
* When present and > 0, the server uses it verbatim and skips the rate card —
|
|
107
|
+
* the authoritative escape hatch when you already have an exact cost. */
|
|
108
|
+
providerCostCents?: number | ((response: unknown) => number | undefined);
|
|
65
109
|
/** Override the run id; defaults to the provider response id. */
|
|
66
110
|
runId?: string | (() => string);
|
|
67
111
|
/** Called if a background track() fails. Defaults to console.warn. */
|
|
@@ -71,6 +115,11 @@ interface AnthropicUsage {
|
|
|
71
115
|
input_tokens?: number;
|
|
72
116
|
output_tokens?: number;
|
|
73
117
|
cache_read_input_tokens?: number;
|
|
118
|
+
cache_creation_input_tokens?: number;
|
|
119
|
+
cache_creation?: {
|
|
120
|
+
ephemeral_5m_input_tokens?: number;
|
|
121
|
+
ephemeral_1h_input_tokens?: number;
|
|
122
|
+
};
|
|
74
123
|
}
|
|
75
124
|
interface AnthropicMessage {
|
|
76
125
|
id?: string;
|
|
@@ -84,7 +133,8 @@ interface AnthropicLike {
|
|
|
84
133
|
create: (...args: never[]) => Promise<unknown>;
|
|
85
134
|
};
|
|
86
135
|
}
|
|
87
|
-
/** Wrap an Anthropic client so `messages.create` auto-reports usage
|
|
136
|
+
/** Wrap an Anthropic client so `messages.create` auto-reports usage (streaming
|
|
137
|
+
* and non-streaming). */
|
|
88
138
|
declare function wrapAnthropic<T extends AnthropicLike>(client: T, tollgate: TollgateClient, opts: InstrumentOptions): T;
|
|
89
139
|
interface OpenAIUsage {
|
|
90
140
|
prompt_tokens?: number;
|
|
@@ -110,7 +160,26 @@ interface OpenAILike {
|
|
|
110
160
|
};
|
|
111
161
|
};
|
|
112
162
|
}
|
|
113
|
-
/** Wrap an OpenAI client so `chat.completions.create`
|
|
163
|
+
/** Wrap an OpenAI (or OpenAI-compatible) client so `chat.completions.create`
|
|
164
|
+
* auto-reports usage. Streaming works when the caller sets
|
|
165
|
+
* `stream_options: { include_usage: true }` (required for OpenAI to emit a final
|
|
166
|
+
* usage chunk); without it there are no token counts to report and the call is
|
|
167
|
+
* passed through untouched. */
|
|
114
168
|
declare function wrapOpenAI<T extends OpenAILike>(client: T, tollgate: TollgateClient, opts: InstrumentOptions): T;
|
|
169
|
+
interface BedrockUsage {
|
|
170
|
+
inputTokens?: number;
|
|
171
|
+
outputTokens?: number;
|
|
172
|
+
cacheReadInputTokens?: number;
|
|
173
|
+
cacheWriteInputTokens?: number;
|
|
174
|
+
}
|
|
175
|
+
/** Map a Bedrock Converse response (model from the request) to a track payload. */
|
|
176
|
+
declare function bedrockEventFrom(usage: BedrockUsage | undefined, model: string, opts: InstrumentOptions, response?: unknown): TrackEventInput | null;
|
|
177
|
+
interface BedrockLike {
|
|
178
|
+
send: (command: unknown, ...rest: never[]) => Promise<unknown>;
|
|
179
|
+
}
|
|
180
|
+
/** Wrap a Bedrock Runtime client so `send(ConverseCommand)` /
|
|
181
|
+
* `send(ConverseStreamCommand)` auto-report usage. Non-Converse commands (no
|
|
182
|
+
* usage in the response) pass through untouched. */
|
|
183
|
+
declare function wrapBedrock<T extends BedrockLike>(client: T, tollgate: TollgateClient, opts: InstrumentOptions): T;
|
|
115
184
|
|
|
116
|
-
export { type EventType, type InstrumentOptions, type Provider, type TollgateClient, type TollgateClientOptions, TollgateError, type TrackEventInput, type TrackResult, anthropicEventFrom, createTollgateClient, openAIEventFrom, wrapAnthropic, wrapOpenAI };
|
|
185
|
+
export { type EventType, type InstrumentOptions, type Provider, type ResolveInput, type RunOutcome, type TollgateClient, type TollgateClientOptions, TollgateError, type TrackEventInput, type TrackResult, anthropicEventFrom, bedrockEventFrom, createTollgateClient, openAIEventFrom, wrapAnthropic, wrapBedrock, wrapOpenAI };
|
package/dist/index.js
CHANGED
|
@@ -62,7 +62,23 @@ function createTollgateClient(opts = {}) {
|
|
|
62
62
|
}
|
|
63
63
|
throw lastErr instanceof Error ? lastErr : new TollgateError("Tollgate track failed after retries");
|
|
64
64
|
}
|
|
65
|
-
|
|
65
|
+
function resolve(input) {
|
|
66
|
+
return track({
|
|
67
|
+
customerId: input.customerId,
|
|
68
|
+
runId: input.runId,
|
|
69
|
+
agentId: input.agentId,
|
|
70
|
+
// A resolution isn't a provider call; zero usage ⇒ zero cost.
|
|
71
|
+
provider: input.provider ?? "anthropic",
|
|
72
|
+
model: input.model ?? "resolution",
|
|
73
|
+
tokensIn: 0,
|
|
74
|
+
tokensOut: 0,
|
|
75
|
+
outcome: input.outcome,
|
|
76
|
+
revenueUnitCents: input.revenueUnitCents,
|
|
77
|
+
idempotencyKey: input.idempotencyKey ?? `${input.runId}#resolve`,
|
|
78
|
+
ts: input.ts
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return { track, resolve };
|
|
66
82
|
}
|
|
67
83
|
|
|
68
84
|
// src/instrument.ts
|
|
@@ -78,31 +94,105 @@ function resolveRunId(opts, responseId) {
|
|
|
78
94
|
function resolveRevenue(opts, response) {
|
|
79
95
|
return typeof opts.revenueUnitCents === "function" ? opts.revenueUnitCents(response) : opts.revenueUnitCents;
|
|
80
96
|
}
|
|
97
|
+
function resolveCost(opts, response) {
|
|
98
|
+
return typeof opts.providerCostCents === "function" ? opts.providerCostCents(response) : opts.providerCostCents;
|
|
99
|
+
}
|
|
100
|
+
function withCost(event, opts, response) {
|
|
101
|
+
const cost = resolveCost(opts, response);
|
|
102
|
+
if (cost !== void 0) event.providerCostCents = cost;
|
|
103
|
+
return event;
|
|
104
|
+
}
|
|
81
105
|
function fireAndForget(p, onError) {
|
|
82
106
|
p.catch((err) => (onError ?? ((e) => console.warn("[tollgate] track failed:", e)))(err));
|
|
83
107
|
}
|
|
108
|
+
function isAsyncIterable(x) {
|
|
109
|
+
return x != null && typeof x[Symbol.asyncIterator] === "function";
|
|
110
|
+
}
|
|
111
|
+
function instrumentStream(stream, onChunk, onDone) {
|
|
112
|
+
let finished = false;
|
|
113
|
+
const finish = () => {
|
|
114
|
+
if (finished) return;
|
|
115
|
+
finished = true;
|
|
116
|
+
onDone();
|
|
117
|
+
};
|
|
118
|
+
return new Proxy(stream, {
|
|
119
|
+
get(target, prop, recv) {
|
|
120
|
+
if (prop === Symbol.asyncIterator) {
|
|
121
|
+
return function instrumentedIterator() {
|
|
122
|
+
const inner = target[Symbol.asyncIterator]();
|
|
123
|
+
return {
|
|
124
|
+
async next(...a) {
|
|
125
|
+
const r = await inner.next(...a);
|
|
126
|
+
if (r.done) finish();
|
|
127
|
+
else onChunk(r.value);
|
|
128
|
+
return r;
|
|
129
|
+
},
|
|
130
|
+
async return(v) {
|
|
131
|
+
finish();
|
|
132
|
+
return inner.return ? inner.return(v) : { done: true, value: v };
|
|
133
|
+
},
|
|
134
|
+
async throw(e) {
|
|
135
|
+
finish();
|
|
136
|
+
if (inner.throw) return inner.throw(e);
|
|
137
|
+
throw e;
|
|
138
|
+
},
|
|
139
|
+
[Symbol.asyncIterator]() {
|
|
140
|
+
return this;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return Reflect.get(target, prop, recv);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
84
149
|
function anthropicEventFrom(msg, opts) {
|
|
85
150
|
const usage = msg?.usage;
|
|
86
151
|
if (!usage) return null;
|
|
87
152
|
const runId = resolveRunId(opts, msg.id);
|
|
88
|
-
|
|
153
|
+
const fivem = usage.cache_creation?.ephemeral_5m_input_tokens;
|
|
154
|
+
const oneh = usage.cache_creation?.ephemeral_1h_input_tokens;
|
|
155
|
+
const hasSplit = fivem !== void 0 || oneh !== void 0;
|
|
156
|
+
const event = {
|
|
89
157
|
customerId: opts.customerId,
|
|
90
158
|
agentId: opts.agentId,
|
|
91
159
|
runId,
|
|
92
|
-
provider: "anthropic",
|
|
160
|
+
provider: opts.provider ?? "anthropic",
|
|
93
161
|
model: msg.model ?? "unknown",
|
|
94
162
|
tokensIn: usage.input_tokens ?? 0,
|
|
95
163
|
tokensOut: usage.output_tokens ?? 0,
|
|
96
164
|
cachedTokens: usage.cache_read_input_tokens ?? 0,
|
|
165
|
+
cacheWrite5mTokens: hasSplit ? fivem ?? 0 : usage.cache_creation_input_tokens ?? 0,
|
|
166
|
+
cacheWrite1hTokens: hasSplit ? oneh ?? 0 : 0,
|
|
97
167
|
revenueUnitCents: resolveRevenue(opts, msg),
|
|
98
168
|
idempotencyKey: msg.id ?? `${runId}#${randomId()}`
|
|
99
169
|
};
|
|
170
|
+
return withCost(event, opts, msg);
|
|
100
171
|
}
|
|
101
172
|
function wrapAnthropic(client, tollgate, opts) {
|
|
102
173
|
const messages = client.messages;
|
|
103
174
|
const original = messages.create.bind(messages);
|
|
104
175
|
const create = async (...args) => {
|
|
105
176
|
const result = await original(...args);
|
|
177
|
+
if (isAsyncIterable(result)) {
|
|
178
|
+
const msg = {};
|
|
179
|
+
return instrumentStream(
|
|
180
|
+
result,
|
|
181
|
+
(ev) => {
|
|
182
|
+
if (ev.type === "message_start" && ev.message) {
|
|
183
|
+
msg.id = ev.message.id;
|
|
184
|
+
msg.model = ev.message.model;
|
|
185
|
+
msg.usage = { ...ev.message.usage };
|
|
186
|
+
} else if (ev.type === "message_delta" && ev.usage) {
|
|
187
|
+
msg.usage = { ...msg.usage ?? {}, output_tokens: ev.usage.output_tokens };
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
() => {
|
|
191
|
+
const event2 = anthropicEventFrom(msg, opts);
|
|
192
|
+
if (event2) fireAndForget(tollgate.track(event2), opts.onError);
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
}
|
|
106
196
|
const event = anthropicEventFrom(result, opts);
|
|
107
197
|
if (event) fireAndForget(tollgate.track(event), opts.onError);
|
|
108
198
|
return result;
|
|
@@ -122,11 +212,11 @@ function openAIEventFrom(completion, opts) {
|
|
|
122
212
|
const usage = completion?.usage;
|
|
123
213
|
if (!usage) return null;
|
|
124
214
|
const runId = resolveRunId(opts, completion.id);
|
|
125
|
-
|
|
215
|
+
const event = {
|
|
126
216
|
customerId: opts.customerId,
|
|
127
217
|
agentId: opts.agentId,
|
|
128
218
|
runId,
|
|
129
|
-
provider: "openai",
|
|
219
|
+
provider: opts.provider ?? "openai",
|
|
130
220
|
model: completion.model ?? "unknown",
|
|
131
221
|
tokensIn: usage.prompt_tokens ?? 0,
|
|
132
222
|
tokensOut: usage.completion_tokens ?? 0,
|
|
@@ -135,12 +225,31 @@ function openAIEventFrom(completion, opts) {
|
|
|
135
225
|
revenueUnitCents: resolveRevenue(opts, completion),
|
|
136
226
|
idempotencyKey: completion.id ?? `${runId}#${randomId()}`
|
|
137
227
|
};
|
|
228
|
+
return withCost(event, opts, completion);
|
|
138
229
|
}
|
|
139
230
|
function wrapOpenAI(client, tollgate, opts) {
|
|
140
231
|
const completions = client.chat.completions;
|
|
141
232
|
const original = completions.create.bind(completions);
|
|
142
233
|
const create = async (...args) => {
|
|
143
234
|
const result = await original(...args);
|
|
235
|
+
if (isAsyncIterable(result)) {
|
|
236
|
+
let id;
|
|
237
|
+
let model;
|
|
238
|
+
let usage;
|
|
239
|
+
return instrumentStream(
|
|
240
|
+
result,
|
|
241
|
+
(chunk) => {
|
|
242
|
+
if (chunk.id) id = chunk.id;
|
|
243
|
+
if (chunk.model) model = chunk.model;
|
|
244
|
+
if (chunk.usage) usage = chunk.usage;
|
|
245
|
+
},
|
|
246
|
+
() => {
|
|
247
|
+
if (!usage) return;
|
|
248
|
+
const event2 = openAIEventFrom({ id, model, usage }, opts);
|
|
249
|
+
if (event2) fireAndForget(tollgate.track(event2), opts.onError);
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
}
|
|
144
253
|
const event = openAIEventFrom(result, opts);
|
|
145
254
|
if (event) fireAndForget(tollgate.track(event), opts.onError);
|
|
146
255
|
return result;
|
|
@@ -159,7 +268,57 @@ function wrapOpenAI(client, tollgate, opts) {
|
|
|
159
268
|
}
|
|
160
269
|
});
|
|
161
270
|
}
|
|
271
|
+
function bedrockEventFrom(usage, model, opts, response = void 0) {
|
|
272
|
+
if (!usage) return null;
|
|
273
|
+
const runId = resolveRunId(opts, void 0);
|
|
274
|
+
const event = {
|
|
275
|
+
customerId: opts.customerId,
|
|
276
|
+
agentId: opts.agentId,
|
|
277
|
+
runId,
|
|
278
|
+
provider: opts.provider ?? "bedrock",
|
|
279
|
+
model,
|
|
280
|
+
tokensIn: usage.inputTokens ?? 0,
|
|
281
|
+
tokensOut: usage.outputTokens ?? 0,
|
|
282
|
+
cachedTokens: usage.cacheReadInputTokens ?? 0,
|
|
283
|
+
cacheWrite5mTokens: usage.cacheWriteInputTokens ?? 0,
|
|
284
|
+
revenueUnitCents: resolveRevenue(opts, response),
|
|
285
|
+
idempotencyKey: `${runId}#${randomId()}`
|
|
286
|
+
};
|
|
287
|
+
return withCost(event, opts, response);
|
|
288
|
+
}
|
|
289
|
+
function wrapBedrock(client, tollgate, opts) {
|
|
290
|
+
const originalSend = client.send.bind(client);
|
|
291
|
+
const send = async (command, ...rest) => {
|
|
292
|
+
const result = await originalSend(command, ...rest);
|
|
293
|
+
const model = command?.input?.modelId ?? "unknown";
|
|
294
|
+
if (result?.stream && isAsyncIterable(result.stream)) {
|
|
295
|
+
let usage;
|
|
296
|
+
result.stream = instrumentStream(
|
|
297
|
+
result.stream,
|
|
298
|
+
(ev) => {
|
|
299
|
+
if (ev.metadata?.usage) usage = ev.metadata.usage;
|
|
300
|
+
},
|
|
301
|
+
() => {
|
|
302
|
+
const event = bedrockEventFrom(usage, model, opts, result);
|
|
303
|
+
if (event) fireAndForget(tollgate.track(event), opts.onError);
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
return result;
|
|
307
|
+
}
|
|
308
|
+
if (result?.usage) {
|
|
309
|
+
const event = bedrockEventFrom(result.usage, model, opts, result);
|
|
310
|
+
if (event) fireAndForget(tollgate.track(event), opts.onError);
|
|
311
|
+
}
|
|
312
|
+
return result;
|
|
313
|
+
};
|
|
314
|
+
return new Proxy(client, {
|
|
315
|
+
get(target, prop, recv) {
|
|
316
|
+
if (prop === "send") return send;
|
|
317
|
+
return Reflect.get(target, prop, recv);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
}
|
|
162
321
|
|
|
163
|
-
export { TollgateError, anthropicEventFrom, createTollgateClient, openAIEventFrom, wrapAnthropic, wrapOpenAI };
|
|
322
|
+
export { TollgateError, anthropicEventFrom, bedrockEventFrom, createTollgateClient, openAIEventFrom, wrapAnthropic, wrapBedrock, wrapOpenAI };
|
|
164
323
|
//# sourceMappingURL=index.js.map
|
|
165
324
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/instrument.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { TrackEventInput, TrackResult } from './types';\n\nexport interface TollgateClientOptions {\n /** Account API key (`tg_live_…`). Falls back to `process.env.TOLLGATE_API_KEY`. */\n apiKey?: string;\n /** Base URL of your Tollgate deployment. Defaults to `TOLLGATE_BASE_URL` or production. */\n baseUrl?: string;\n /** Per-request timeout in ms. Default 10_000. */\n timeoutMs?: number;\n /** Retry attempts on network error / 5xx / 429. Default 2. */\n maxRetries?: number;\n /** Custom fetch (for testing or non-standard runtimes). Defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nexport class TollgateError extends Error {\n constructor(\n message: string,\n readonly status?: number,\n readonly body?: unknown,\n ) {\n super(message);\n this.name = 'TollgateError';\n }\n}\n\nconst DEFAULT_BASE_URL = 'https://tollgateai.vercel.app';\n\nexport interface TollgateClient {\n /** Report a single usage event. Idempotent on `idempotencyKey`. */\n track(event: TrackEventInput): Promise<TrackResult>;\n}\n\nfunction getEnv(key: string): string | undefined {\n // Guarded so the SDK works in browsers/edge runtimes without `process`.\n return typeof process !== 'undefined' ? process.env?.[key] : undefined;\n}\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\nexport function createTollgateClient(opts: TollgateClientOptions = {}): TollgateClient {\n const apiKey = opts.apiKey ?? getEnv('TOLLGATE_API_KEY');\n const baseUrl = (opts.baseUrl ?? getEnv('TOLLGATE_BASE_URL') ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n const timeoutMs = opts.timeoutMs ?? 10_000;\n const maxRetries = opts.maxRetries ?? 2;\n const doFetch = opts.fetch ?? globalThis.fetch;\n\n if (typeof doFetch !== 'function') {\n throw new TollgateError('No fetch implementation available — pass `fetch` in options.');\n }\n\n async function track(event: TrackEventInput): Promise<TrackResult> {\n if (!apiKey) {\n throw new TollgateError('Missing API key — set opts.apiKey or TOLLGATE_API_KEY.');\n }\n\n let lastErr: unknown;\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await doFetch(`${baseUrl}/api/track`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(event),\n signal: controller.signal,\n });\n\n // 200 = idempotent duplicate, 201 = created. Both are success.\n if (res.ok) {\n return (await res.json()) as TrackResult;\n }\n\n // Retry transient failures; fail fast on 4xx (except 429).\n if (res.status >= 500 || res.status === 429) {\n lastErr = new TollgateError(`Tollgate track failed (${res.status})`, res.status);\n } else {\n const body = await res.json().catch(() => ({}));\n throw new TollgateError(`Tollgate track failed (${res.status})`, res.status, body);\n }\n } catch (err) {\n // Don't retry deterministic client errors.\n if (err instanceof TollgateError && err.status && err.status < 500 && err.status !== 429) {\n throw err;\n }\n lastErr = err;\n } finally {\n clearTimeout(timer);\n }\n\n if (attempt < maxRetries) {\n await sleep(2 ** attempt * 200); // 200ms, 400ms, …\n }\n }\n\n throw lastErr instanceof Error\n ? lastErr\n : new TollgateError('Tollgate track failed after retries');\n }\n\n return { track };\n}\n","// Auto-instrumentation: wrap a provider client so every completion reports its\n// REAL usage to Tollgate — no manual token counting. Wrappers are structurally\n// typed, so this package never has to depend on the provider SDKs.\n\nimport type { TollgateClient } from './client';\nimport type { TrackEventInput } from './types';\n\nexport interface InstrumentOptions {\n /** Your end customer's stable id. Required for margin attribution. */\n customerId: string;\n /** Optional agent/workflow id. */\n agentId?: string;\n /** Revenue per call in cents (or a function of the response). */\n revenueUnitCents?: number | ((response: unknown) => number | undefined);\n /** Override the run id; defaults to the provider response id. */\n runId?: string | (() => string);\n /** Called if a background track() fails. Defaults to console.warn. */\n onError?: (err: unknown) => void;\n}\n\nfunction randomId(): string {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (c?.randomUUID) return c.randomUUID();\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction resolveRunId(opts: InstrumentOptions, responseId?: string): string {\n if (typeof opts.runId === 'function') return opts.runId();\n return opts.runId ?? responseId ?? randomId();\n}\n\nfunction resolveRevenue(opts: InstrumentOptions, response: unknown): number | undefined {\n return typeof opts.revenueUnitCents === 'function'\n ? opts.revenueUnitCents(response)\n : opts.revenueUnitCents;\n}\n\nfunction fireAndForget(p: Promise<unknown>, onError?: InstrumentOptions['onError']): void {\n p.catch((err) => (onError ?? ((e) => console.warn('[tollgate] track failed:', e)))(err));\n}\n\n// --- Anthropic ------------------------------------------------------------\n\ninterface AnthropicUsage {\n input_tokens?: number;\n output_tokens?: number;\n cache_read_input_tokens?: number;\n}\ninterface AnthropicMessage {\n id?: string;\n model?: string;\n usage?: AnthropicUsage;\n}\n\n/** Map a non-streaming Anthropic message to a track payload (or null if no usage). */\nexport function anthropicEventFrom(\n msg: AnthropicMessage,\n opts: InstrumentOptions,\n): TrackEventInput | null {\n const usage = msg?.usage;\n if (!usage) return null;\n const runId = resolveRunId(opts, msg.id);\n return {\n customerId: opts.customerId,\n agentId: opts.agentId,\n runId,\n provider: 'anthropic',\n model: msg.model ?? 'unknown',\n tokensIn: usage.input_tokens ?? 0,\n tokensOut: usage.output_tokens ?? 0,\n cachedTokens: usage.cache_read_input_tokens ?? 0,\n revenueUnitCents: resolveRevenue(opts, msg),\n idempotencyKey: msg.id ?? `${runId}#${randomId()}`,\n };\n}\n\ninterface AnthropicLike {\n messages: { create: (...args: never[]) => Promise<unknown> };\n}\n\n/** Wrap an Anthropic client so `messages.create` auto-reports usage. */\nexport function wrapAnthropic<T extends AnthropicLike>(\n client: T,\n tollgate: TollgateClient,\n opts: InstrumentOptions,\n): T {\n const messages = client.messages;\n const original = messages.create.bind(messages) as (...a: never[]) => Promise<unknown>;\n\n const create = async (...args: never[]): Promise<unknown> => {\n const result = await original(...args);\n const event = anthropicEventFrom(result as AnthropicMessage, opts);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n return result;\n };\n\n return new Proxy(client, {\n get(target, prop, recv) {\n if (prop === 'messages') {\n return new Proxy(messages, {\n get: (m, p, r) => (p === 'create' ? create : Reflect.get(m, p, r)),\n });\n }\n return Reflect.get(target, prop, recv);\n },\n });\n}\n\n// --- OpenAI ---------------------------------------------------------------\n\ninterface OpenAIUsage {\n prompt_tokens?: number;\n completion_tokens?: number;\n completion_tokens_details?: { reasoning_tokens?: number };\n prompt_tokens_details?: { cached_tokens?: number };\n}\ninterface OpenAICompletion {\n id?: string;\n model?: string;\n usage?: OpenAIUsage;\n}\n\n/** Map a non-streaming OpenAI chat completion to a track payload (or null). */\nexport function openAIEventFrom(\n completion: OpenAICompletion,\n opts: InstrumentOptions,\n): TrackEventInput | null {\n const usage = completion?.usage;\n if (!usage) return null;\n const runId = resolveRunId(opts, completion.id);\n return {\n customerId: opts.customerId,\n agentId: opts.agentId,\n runId,\n provider: 'openai',\n model: completion.model ?? 'unknown',\n tokensIn: usage.prompt_tokens ?? 0,\n tokensOut: usage.completion_tokens ?? 0,\n reasoningTokens: usage.completion_tokens_details?.reasoning_tokens ?? 0,\n cachedTokens: usage.prompt_tokens_details?.cached_tokens ?? 0,\n revenueUnitCents: resolveRevenue(opts, completion),\n idempotencyKey: completion.id ?? `${runId}#${randomId()}`,\n };\n}\n\ninterface OpenAILike {\n chat: { completions: { create: (...args: never[]) => Promise<unknown> } };\n}\n\n/** Wrap an OpenAI client so `chat.completions.create` auto-reports usage. */\nexport function wrapOpenAI<T extends OpenAILike>(\n client: T,\n tollgate: TollgateClient,\n opts: InstrumentOptions,\n): T {\n const completions = client.chat.completions;\n const original = completions.create.bind(completions) as (...a: never[]) => Promise<unknown>;\n\n const create = async (...args: never[]): Promise<unknown> => {\n const result = await original(...args);\n const event = openAIEventFrom(result as OpenAICompletion, opts);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n return result;\n };\n\n return new Proxy(client, {\n get(target, prop, recv) {\n if (prop === 'chat') {\n const chat = Reflect.get(target, prop, recv) as OpenAILike['chat'];\n return new Proxy(chat, {\n get: (c, p, r) =>\n p === 'completions'\n ? new Proxy(completions, {\n get: (co, pp, rr) => (pp === 'create' ? create : Reflect.get(co, pp, rr)),\n })\n : Reflect.get(c, p, r),\n });\n }\n return Reflect.get(target, prop, recv);\n },\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/instrument.ts"],"names":["event"],"mappings":"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","file":"index.js","sourcesContent":["import type { Provider, RunOutcome, TrackEventInput, TrackResult } from './types';\n\nexport interface TollgateClientOptions {\n /** Account API key (`tg_live_…`). Falls back to `process.env.TOLLGATE_API_KEY`. */\n apiKey?: string;\n /** Base URL of your Tollgate deployment. Defaults to `TOLLGATE_BASE_URL` or production. */\n baseUrl?: string;\n /** Per-request timeout in ms. Default 10_000. */\n timeoutMs?: number;\n /** Retry attempts on network error / 5xx / 429. Default 2. */\n maxRetries?: number;\n /** Custom fetch (for testing or non-standard runtimes). Defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nexport class TollgateError extends Error {\n constructor(\n message: string,\n readonly status?: number,\n readonly body?: unknown,\n ) {\n super(message);\n this.name = 'TollgateError';\n }\n}\n\nconst DEFAULT_BASE_URL = 'https://tollgateai.vercel.app';\n\n/** Close out a run with its outcome (and, if resolved, the revenue it earns).\n * A resolution carries no provider usage, so `provider`/`model` are optional. */\nexport interface ResolveInput {\n /** The run being closed (must match the runId your usage events used). */\n runId: string;\n customerId: string;\n /** 'resolved' books revenue; 'escalated'/'failed' book none (cost still counts). */\n outcome: RunOutcome;\n /** Revenue in cents for a resolved run (e.g. 50 for $0.50). Ignored if not resolved. */\n revenueUnitCents?: number;\n agentId?: string;\n /** Idempotency key for the closing event. Defaults to `${runId}#resolve`. */\n idempotencyKey?: string;\n ts?: string;\n /** Rarely needed — a resolution isn't a provider call. Default 'anthropic'/'resolution'. */\n provider?: Provider;\n model?: string;\n}\n\nexport interface TollgateClient {\n /** Report a single usage event. Idempotent on `idempotencyKey`. */\n track(event: TrackEventInput): Promise<TrackResult>;\n /** Close a run with its outcome — book revenue once, only if resolved.\n * Convenience over `track()`: sends a zero-usage terminal event. */\n resolve(input: ResolveInput): Promise<TrackResult>;\n}\n\nfunction getEnv(key: string): string | undefined {\n // Guarded so the SDK works in browsers/edge runtimes without `process`.\n return typeof process !== 'undefined' ? process.env?.[key] : undefined;\n}\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\nexport function createTollgateClient(opts: TollgateClientOptions = {}): TollgateClient {\n const apiKey = opts.apiKey ?? getEnv('TOLLGATE_API_KEY');\n const baseUrl = (opts.baseUrl ?? getEnv('TOLLGATE_BASE_URL') ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n const timeoutMs = opts.timeoutMs ?? 10_000;\n const maxRetries = opts.maxRetries ?? 2;\n const doFetch = opts.fetch ?? globalThis.fetch;\n\n if (typeof doFetch !== 'function') {\n throw new TollgateError('No fetch implementation available — pass `fetch` in options.');\n }\n\n async function track(event: TrackEventInput): Promise<TrackResult> {\n if (!apiKey) {\n throw new TollgateError('Missing API key — set opts.apiKey or TOLLGATE_API_KEY.');\n }\n\n let lastErr: unknown;\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await doFetch(`${baseUrl}/api/track`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(event),\n signal: controller.signal,\n });\n\n // 200 = idempotent duplicate, 201 = created. Both are success.\n if (res.ok) {\n return (await res.json()) as TrackResult;\n }\n\n // Retry transient failures; fail fast on 4xx (except 429).\n if (res.status >= 500 || res.status === 429) {\n lastErr = new TollgateError(`Tollgate track failed (${res.status})`, res.status);\n } else {\n const body = await res.json().catch(() => ({}));\n throw new TollgateError(`Tollgate track failed (${res.status})`, res.status, body);\n }\n } catch (err) {\n // Don't retry deterministic client errors.\n if (err instanceof TollgateError && err.status && err.status < 500 && err.status !== 429) {\n throw err;\n }\n lastErr = err;\n } finally {\n clearTimeout(timer);\n }\n\n if (attempt < maxRetries) {\n await sleep(2 ** attempt * 200); // 200ms, 400ms, …\n }\n }\n\n throw lastErr instanceof Error\n ? lastErr\n : new TollgateError('Tollgate track failed after retries');\n }\n\n function resolve(input: ResolveInput): Promise<TrackResult> {\n return track({\n customerId: input.customerId,\n runId: input.runId,\n agentId: input.agentId,\n // A resolution isn't a provider call; zero usage ⇒ zero cost.\n provider: input.provider ?? 'anthropic',\n model: input.model ?? 'resolution',\n tokensIn: 0,\n tokensOut: 0,\n outcome: input.outcome,\n revenueUnitCents: input.revenueUnitCents,\n idempotencyKey: input.idempotencyKey ?? `${input.runId}#resolve`,\n ts: input.ts,\n });\n }\n\n return { track, resolve };\n}\n","// Auto-instrumentation: wrap a provider client so every completion reports its\n// REAL usage to Tollgate — no manual token counting. Wrappers are structurally\n// typed, so this package never has to depend on the provider SDKs.\n//\n// Coverage is universal: OpenAI + Anthropic native, every OpenAI-compatible\n// gateway (set `provider: 'openai_compatible'`), and AWS Bedrock — each in both\n// non-streaming and streaming modes. Cost is always derived server-side from the\n// token counts these wrappers capture, so no provider needs to return a dollar\n// figure (pass `providerCostCents` only if you already have one).\n\nimport type { TollgateClient } from './client';\nimport type { Provider, TrackEventInput } from './types';\n\nexport interface InstrumentOptions {\n /** Your end customer's stable id. Required for margin attribution. */\n customerId: string;\n /** Optional agent/workflow id. */\n agentId?: string;\n /** Override the reported provider. Defaults per wrapper ('openai' /\n * 'anthropic' / 'bedrock'). Set to 'openai_compatible' when the client points\n * at an OpenAI-shaped gateway (Vercel AI Gateway, OpenRouter, Groq, Together,\n * Nebius, local vLLM, …) so the server prices it by the gateway-echoed model. */\n provider?: Provider;\n /** Revenue per call in cents (or a function of the response). */\n revenueUnitCents?: number | ((response: unknown) => number | undefined);\n /** Provider/gateway-reported cost in cents (or a function of the response).\n * When present and > 0, the server uses it verbatim and skips the rate card —\n * the authoritative escape hatch when you already have an exact cost. */\n providerCostCents?: number | ((response: unknown) => number | undefined);\n /** Override the run id; defaults to the provider response id. */\n runId?: string | (() => string);\n /** Called if a background track() fails. Defaults to console.warn. */\n onError?: (err: unknown) => void;\n}\n\nfunction randomId(): string {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (c?.randomUUID) return c.randomUUID();\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction resolveRunId(opts: InstrumentOptions, responseId?: string): string {\n if (typeof opts.runId === 'function') return opts.runId();\n return opts.runId ?? responseId ?? randomId();\n}\n\nfunction resolveRevenue(opts: InstrumentOptions, response: unknown): number | undefined {\n return typeof opts.revenueUnitCents === 'function'\n ? opts.revenueUnitCents(response)\n : opts.revenueUnitCents;\n}\n\nfunction resolveCost(opts: InstrumentOptions, response: unknown): number | undefined {\n return typeof opts.providerCostCents === 'function'\n ? opts.providerCostCents(response)\n : opts.providerCostCents;\n}\n\n/** Attach providerCostCents to an event only when a (non-undefined) value resolves. */\nfunction withCost(\n event: TrackEventInput,\n opts: InstrumentOptions,\n response: unknown,\n): TrackEventInput {\n const cost = resolveCost(opts, response);\n if (cost !== undefined) event.providerCostCents = cost;\n return event;\n}\n\nfunction fireAndForget(p: Promise<unknown>, onError?: InstrumentOptions['onError']): void {\n p.catch((err) => (onError ?? ((e) => console.warn('[tollgate] track failed:', e)))(err));\n}\n\nfunction isAsyncIterable(x: unknown): x is AsyncIterable<unknown> {\n return x != null && typeof (x as Record<symbol, unknown>)[Symbol.asyncIterator] === 'function';\n}\n\n/**\n * Wrap an async iterable (a provider stream) so each chunk is observed and a\n * finalizer runs once the stream is exhausted — without disturbing the stream's\n * other methods (`.tee()`, `.controller`, …), which are proxied through. If the\n * consumer abandons the stream early the finalizer still fires on `.return()`.\n */\nfunction instrumentStream<TChunk>(\n stream: AsyncIterable<TChunk>,\n onChunk: (chunk: TChunk) => void,\n onDone: () => void,\n): AsyncIterable<TChunk> {\n let finished = false;\n const finish = () => {\n if (finished) return;\n finished = true;\n onDone();\n };\n return new Proxy(stream, {\n get(target, prop, recv) {\n if (prop === Symbol.asyncIterator) {\n return function instrumentedIterator() {\n const inner = (target as AsyncIterable<TChunk>)[Symbol.asyncIterator]();\n return {\n async next(...a: []) {\n const r = await inner.next(...a);\n if (r.done) finish();\n else onChunk(r.value);\n return r;\n },\n async return(v?: unknown) {\n finish();\n return inner.return ? inner.return(v) : { done: true, value: v as TChunk };\n },\n async throw(e?: unknown) {\n finish();\n if (inner.throw) return inner.throw(e);\n throw e;\n },\n [Symbol.asyncIterator]() {\n return this;\n },\n };\n };\n }\n return Reflect.get(target, prop, recv);\n },\n });\n}\n\n// --- Anthropic ------------------------------------------------------------\n\ninterface AnthropicUsage {\n input_tokens?: number;\n output_tokens?: number;\n cache_read_input_tokens?: number;\n // Cache CREATION (writes) bill above the input rate. Newer responses break the\n // total down by TTL; older ones only return the aggregate cache_creation count.\n cache_creation_input_tokens?: number;\n cache_creation?: {\n ephemeral_5m_input_tokens?: number;\n ephemeral_1h_input_tokens?: number;\n };\n}\ninterface AnthropicMessage {\n id?: string;\n model?: string;\n usage?: AnthropicUsage;\n}\n// Streaming event shapes we read usage from (message_start carries inputs +\n// cache, message_delta carries the cumulative output token count).\ninterface AnthropicStreamEvent {\n type?: string;\n message?: AnthropicMessage;\n usage?: AnthropicUsage;\n}\n\n/** Map a non-streaming Anthropic message to a track payload (or null if no usage). */\nexport function anthropicEventFrom(\n msg: AnthropicMessage,\n opts: InstrumentOptions,\n): TrackEventInput | null {\n const usage = msg?.usage;\n if (!usage) return null;\n const runId = resolveRunId(opts, msg.id);\n // Split cache-creation tokens by TTL when the response provides the breakdown;\n // otherwise attribute the whole cache_creation total to the default 5-minute TTL.\n const fivem = usage.cache_creation?.ephemeral_5m_input_tokens;\n const oneh = usage.cache_creation?.ephemeral_1h_input_tokens;\n const hasSplit = fivem !== undefined || oneh !== undefined;\n const event: TrackEventInput = {\n customerId: opts.customerId,\n agentId: opts.agentId,\n runId,\n provider: opts.provider ?? 'anthropic',\n model: msg.model ?? 'unknown',\n tokensIn: usage.input_tokens ?? 0,\n tokensOut: usage.output_tokens ?? 0,\n cachedTokens: usage.cache_read_input_tokens ?? 0,\n cacheWrite5mTokens: hasSplit ? fivem ?? 0 : usage.cache_creation_input_tokens ?? 0,\n cacheWrite1hTokens: hasSplit ? oneh ?? 0 : 0,\n revenueUnitCents: resolveRevenue(opts, msg),\n idempotencyKey: msg.id ?? `${runId}#${randomId()}`,\n };\n return withCost(event, opts, msg);\n}\n\ninterface AnthropicLike {\n messages: { create: (...args: never[]) => Promise<unknown> };\n}\n\n/** Wrap an Anthropic client so `messages.create` auto-reports usage (streaming\n * and non-streaming). */\nexport function wrapAnthropic<T extends AnthropicLike>(\n client: T,\n tollgate: TollgateClient,\n opts: InstrumentOptions,\n): T {\n const messages = client.messages;\n const original = messages.create.bind(messages) as (...a: never[]) => Promise<unknown>;\n\n const create = async (...args: never[]): Promise<unknown> => {\n const result = await original(...args);\n if (isAsyncIterable(result)) {\n // Reconstruct a message from the event stream, then reuse the non-stream\n // mapper. input/cache tokens arrive in message_start; the final cumulative\n // output token count arrives in the last message_delta.\n const msg: AnthropicMessage = {};\n return instrumentStream(\n result as AsyncIterable<AnthropicStreamEvent>,\n (ev) => {\n if (ev.type === 'message_start' && ev.message) {\n msg.id = ev.message.id;\n msg.model = ev.message.model;\n msg.usage = { ...ev.message.usage };\n } else if (ev.type === 'message_delta' && ev.usage) {\n msg.usage = { ...(msg.usage ?? {}), output_tokens: ev.usage.output_tokens };\n }\n },\n () => {\n const event = anthropicEventFrom(msg, opts);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n },\n );\n }\n const event = anthropicEventFrom(result as AnthropicMessage, opts);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n return result;\n };\n\n return new Proxy(client, {\n get(target, prop, recv) {\n if (prop === 'messages') {\n return new Proxy(messages, {\n get: (m, p, r) => (p === 'create' ? create : Reflect.get(m, p, r)),\n });\n }\n return Reflect.get(target, prop, recv);\n },\n });\n}\n\n// --- OpenAI (and OpenAI-compatible gateways) ------------------------------\n\ninterface OpenAIUsage {\n prompt_tokens?: number;\n completion_tokens?: number;\n completion_tokens_details?: { reasoning_tokens?: number };\n prompt_tokens_details?: { cached_tokens?: number };\n}\ninterface OpenAICompletion {\n id?: string;\n model?: string;\n usage?: OpenAIUsage;\n}\n\n/** Map a non-streaming OpenAI chat completion to a track payload (or null). */\nexport function openAIEventFrom(\n completion: OpenAICompletion,\n opts: InstrumentOptions,\n): TrackEventInput | null {\n const usage = completion?.usage;\n if (!usage) return null;\n const runId = resolveRunId(opts, completion.id);\n const event: TrackEventInput = {\n customerId: opts.customerId,\n agentId: opts.agentId,\n runId,\n provider: opts.provider ?? 'openai',\n model: completion.model ?? 'unknown',\n tokensIn: usage.prompt_tokens ?? 0,\n tokensOut: usage.completion_tokens ?? 0,\n reasoningTokens: usage.completion_tokens_details?.reasoning_tokens ?? 0,\n cachedTokens: usage.prompt_tokens_details?.cached_tokens ?? 0,\n revenueUnitCents: resolveRevenue(opts, completion),\n idempotencyKey: completion.id ?? `${runId}#${randomId()}`,\n };\n return withCost(event, opts, completion);\n}\n\ninterface OpenAILike {\n chat: { completions: { create: (...args: never[]) => Promise<unknown> } };\n}\n\n/** Wrap an OpenAI (or OpenAI-compatible) client so `chat.completions.create`\n * auto-reports usage. Streaming works when the caller sets\n * `stream_options: { include_usage: true }` (required for OpenAI to emit a final\n * usage chunk); without it there are no token counts to report and the call is\n * passed through untouched. */\nexport function wrapOpenAI<T extends OpenAILike>(\n client: T,\n tollgate: TollgateClient,\n opts: InstrumentOptions,\n): T {\n const completions = client.chat.completions;\n const original = completions.create.bind(completions) as (...a: never[]) => Promise<unknown>;\n\n const create = async (...args: never[]): Promise<unknown> => {\n const result = await original(...args);\n if (isAsyncIterable(result)) {\n let id: string | undefined;\n let model: string | undefined;\n let usage: OpenAIUsage | undefined;\n return instrumentStream(\n result as AsyncIterable<OpenAICompletion>,\n (chunk) => {\n if (chunk.id) id = chunk.id;\n if (chunk.model) model = chunk.model;\n if (chunk.usage) usage = chunk.usage; // only the final chunk carries it\n },\n () => {\n if (!usage) return; // caller didn't request include_usage — nothing to report\n const event = openAIEventFrom({ id, model, usage }, opts);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n },\n );\n }\n const event = openAIEventFrom(result as OpenAICompletion, opts);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n return result;\n };\n\n return new Proxy(client, {\n get(target, prop, recv) {\n if (prop === 'chat') {\n const chat = Reflect.get(target, prop, recv) as OpenAILike['chat'];\n return new Proxy(chat, {\n get: (c, p, r) =>\n p === 'completions'\n ? new Proxy(completions, {\n get: (co, pp, rr) => (pp === 'create' ? create : Reflect.get(co, pp, rr)),\n })\n : Reflect.get(c, p, r),\n });\n }\n return Reflect.get(target, prop, recv);\n },\n });\n}\n\n// --- AWS Bedrock ----------------------------------------------------------\n// Bedrock's Converse API is command-based: the model id lives on the REQUEST,\n// not the response, so we read it from the command input. Usage is reported in\n// camelCase (inputTokens/outputTokens) plus optional cache token counts.\n\ninterface BedrockUsage {\n inputTokens?: number;\n outputTokens?: number;\n cacheReadInputTokens?: number;\n cacheWriteInputTokens?: number;\n}\ninterface BedrockConverseResponse {\n usage?: BedrockUsage;\n // ConverseStream returns an async-iterable `stream` of events; the final\n // `metadata` event carries the usage totals.\n stream?: AsyncIterable<{ metadata?: { usage?: BedrockUsage } }>;\n}\n\n/** Map a Bedrock Converse response (model from the request) to a track payload. */\nexport function bedrockEventFrom(\n usage: BedrockUsage | undefined,\n model: string,\n opts: InstrumentOptions,\n response: unknown = undefined,\n): TrackEventInput | null {\n if (!usage) return null;\n const runId = resolveRunId(opts, undefined);\n const event: TrackEventInput = {\n customerId: opts.customerId,\n agentId: opts.agentId,\n runId,\n provider: opts.provider ?? 'bedrock',\n model,\n tokensIn: usage.inputTokens ?? 0,\n tokensOut: usage.outputTokens ?? 0,\n cachedTokens: usage.cacheReadInputTokens ?? 0,\n cacheWrite5mTokens: usage.cacheWriteInputTokens ?? 0,\n revenueUnitCents: resolveRevenue(opts, response),\n idempotencyKey: `${runId}#${randomId()}`,\n };\n return withCost(event, opts, response);\n}\n\ninterface BedrockLike {\n send: (command: unknown, ...rest: never[]) => Promise<unknown>;\n}\n\n/** Wrap a Bedrock Runtime client so `send(ConverseCommand)` /\n * `send(ConverseStreamCommand)` auto-report usage. Non-Converse commands (no\n * usage in the response) pass through untouched. */\nexport function wrapBedrock<T extends BedrockLike>(\n client: T,\n tollgate: TollgateClient,\n opts: InstrumentOptions,\n): T {\n const originalSend = client.send.bind(client) as BedrockLike['send'];\n\n const send = async (command: unknown, ...rest: never[]): Promise<unknown> => {\n const result = (await originalSend(command, ...rest)) as BedrockConverseResponse;\n const model =\n ((command as { input?: { modelId?: string } })?.input?.modelId) ?? 'unknown';\n\n if (result?.stream && isAsyncIterable(result.stream)) {\n let usage: BedrockUsage | undefined;\n result.stream = instrumentStream(\n result.stream,\n (ev) => {\n if (ev.metadata?.usage) usage = ev.metadata.usage;\n },\n () => {\n const event = bedrockEventFrom(usage, model, opts, result);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n },\n );\n return result;\n }\n\n if (result?.usage) {\n const event = bedrockEventFrom(result.usage, model, opts, result);\n if (event) fireAndForget(tollgate.track(event), opts.onError);\n }\n return result;\n };\n\n return new Proxy(client, {\n get(target, prop, recv) {\n if (prop === 'send') return send;\n return Reflect.get(target, prop, recv);\n },\n });\n}\n"]}
|