@dexcost/sdk 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/LICENSE +21 -0
- package/README.md +210 -0
- package/dist/adapters/_netbytes.d.ts +31 -0
- package/dist/adapters/_netbytes.d.ts.map +1 -0
- package/dist/adapters/_netbytes.js +154 -0
- package/dist/adapters/_netbytes.js.map +1 -0
- package/dist/adapters/aws-lambda.d.ts +41 -0
- package/dist/adapters/aws-lambda.d.ts.map +1 -0
- package/dist/adapters/aws-lambda.js +65 -0
- package/dist/adapters/aws-lambda.js.map +1 -0
- package/dist/adapters/browser.d.ts +52 -0
- package/dist/adapters/browser.d.ts.map +1 -0
- package/dist/adapters/browser.js +127 -0
- package/dist/adapters/browser.js.map +1 -0
- package/dist/adapters/compute-wrap.d.ts +33 -0
- package/dist/adapters/compute-wrap.d.ts.map +1 -0
- package/dist/adapters/compute-wrap.js +188 -0
- package/dist/adapters/compute-wrap.js.map +1 -0
- package/dist/adapters/data/aws_lambda_pricing.json +61 -0
- package/dist/adapters/gpu-wrap.d.ts +31 -0
- package/dist/adapters/gpu-wrap.d.ts.map +1 -0
- package/dist/adapters/gpu-wrap.js +147 -0
- package/dist/adapters/gpu-wrap.js.map +1 -0
- package/dist/adapters/http.d.ts +58 -0
- package/dist/adapters/http.d.ts.map +1 -0
- package/dist/adapters/http.js +769 -0
- package/dist/adapters/http.js.map +1 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +12 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/network-accountant.d.ts +63 -0
- package/dist/adapters/network-accountant.d.ts.map +1 -0
- package/dist/adapters/network-accountant.js +153 -0
- package/dist/adapters/network-accountant.js.map +1 -0
- package/dist/cli/index.d.ts +13 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +225 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/scanner.d.ts +39 -0
- package/dist/cli/scanner.d.ts.map +1 -0
- package/dist/cli/scanner.js +480 -0
- package/dist/cli/scanner.js.map +1 -0
- package/dist/clients.d.ts +54 -0
- package/dist/clients.d.ts.map +1 -0
- package/dist/clients.js +240 -0
- package/dist/clients.js.map +1 -0
- package/dist/cloud-detect.d.ts +96 -0
- package/dist/cloud-detect.d.ts.map +1 -0
- package/dist/cloud-detect.js +545 -0
- package/dist/cloud-detect.js.map +1 -0
- package/dist/core/auto-task.d.ts +20 -0
- package/dist/core/auto-task.d.ts.map +1 -0
- package/dist/core/auto-task.js +34 -0
- package/dist/core/auto-task.js.map +1 -0
- package/dist/core/cgroup-reader.d.ts +45 -0
- package/dist/core/cgroup-reader.d.ts.map +1 -0
- package/dist/core/cgroup-reader.js +124 -0
- package/dist/core/cgroup-reader.js.map +1 -0
- package/dist/core/cgroup-walker.d.ts +60 -0
- package/dist/core/cgroup-walker.d.ts.map +1 -0
- package/dist/core/cgroup-walker.js +166 -0
- package/dist/core/cgroup-walker.js.map +1 -0
- package/dist/core/compute-accountant.d.ts +51 -0
- package/dist/core/compute-accountant.d.ts.map +1 -0
- package/dist/core/compute-accountant.js +179 -0
- package/dist/core/compute-accountant.js.map +1 -0
- package/dist/core/compute-runtime.d.ts +42 -0
- package/dist/core/compute-runtime.d.ts.map +1 -0
- package/dist/core/compute-runtime.js +80 -0
- package/dist/core/compute-runtime.js.map +1 -0
- package/dist/core/config.d.ts +44 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +66 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/context.d.ts +76 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +91 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/fargate-metadata.d.ts +27 -0
- package/dist/core/fargate-metadata.d.ts.map +1 -0
- package/dist/core/fargate-metadata.js +102 -0
- package/dist/core/fargate-metadata.js.map +1 -0
- package/dist/core/gpu-accountant.d.ts +104 -0
- package/dist/core/gpu-accountant.d.ts.map +1 -0
- package/dist/core/gpu-accountant.js +383 -0
- package/dist/core/gpu-accountant.js.map +1 -0
- package/dist/core/gpu-runtime.d.ts +58 -0
- package/dist/core/gpu-runtime.d.ts.map +1 -0
- package/dist/core/gpu-runtime.js +131 -0
- package/dist/core/gpu-runtime.js.map +1 -0
- package/dist/core/heuristics.d.ts +74 -0
- package/dist/core/heuristics.d.ts.map +1 -0
- package/dist/core/heuristics.js +182 -0
- package/dist/core/heuristics.js.map +1 -0
- package/dist/core/models.d.ts +149 -0
- package/dist/core/models.d.ts.map +1 -0
- package/dist/core/models.js +226 -0
- package/dist/core/models.js.map +1 -0
- package/dist/core/nvml-reader.d.ts +114 -0
- package/dist/core/nvml-reader.d.ts.map +1 -0
- package/dist/core/nvml-reader.js +323 -0
- package/dist/core/nvml-reader.js.map +1 -0
- package/dist/core/session.d.ts +48 -0
- package/dist/core/session.d.ts.map +1 -0
- package/dist/core/session.js +123 -0
- package/dist/core/session.js.map +1 -0
- package/dist/core/tracker.d.ts +364 -0
- package/dist/core/tracker.d.ts.map +1 -0
- package/dist/core/tracker.js +1073 -0
- package/dist/core/tracker.js.map +1 -0
- package/dist/data/compute_prices.json +180 -0
- package/dist/data/egress_prices.json +418 -0
- package/dist/data/gpu_prices.json +412 -0
- package/dist/data/service_prices.json +2595 -0
- package/dist/dev-console.d.ts +12 -0
- package/dist/dev-console.d.ts.map +1 -0
- package/dist/dev-console.js +60 -0
- package/dist/dev-console.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/instruments/anthropic.d.ts +26 -0
- package/dist/instruments/anthropic.d.ts.map +1 -0
- package/dist/instruments/anthropic.js +242 -0
- package/dist/instruments/anthropic.js.map +1 -0
- package/dist/instruments/bedrock.d.ts +29 -0
- package/dist/instruments/bedrock.d.ts.map +1 -0
- package/dist/instruments/bedrock.js +215 -0
- package/dist/instruments/bedrock.js.map +1 -0
- package/dist/instruments/cohere.d.ts +29 -0
- package/dist/instruments/cohere.d.ts.map +1 -0
- package/dist/instruments/cohere.js +237 -0
- package/dist/instruments/cohere.js.map +1 -0
- package/dist/instruments/gemini.d.ts +30 -0
- package/dist/instruments/gemini.d.ts.map +1 -0
- package/dist/instruments/gemini.js +247 -0
- package/dist/instruments/gemini.js.map +1 -0
- package/dist/instruments/index.d.ts +35 -0
- package/dist/instruments/index.d.ts.map +1 -0
- package/dist/instruments/index.js +54 -0
- package/dist/instruments/index.js.map +1 -0
- package/dist/instruments/mcp.d.ts +24 -0
- package/dist/instruments/mcp.d.ts.map +1 -0
- package/dist/instruments/mcp.js +459 -0
- package/dist/instruments/mcp.js.map +1 -0
- package/dist/instruments/openai.d.ts +26 -0
- package/dist/instruments/openai.d.ts.map +1 -0
- package/dist/instruments/openai.js +221 -0
- package/dist/instruments/openai.js.map +1 -0
- package/dist/instruments/vercel-ai.d.ts +28 -0
- package/dist/instruments/vercel-ai.d.ts.map +1 -0
- package/dist/instruments/vercel-ai.js +192 -0
- package/dist/instruments/vercel-ai.js.map +1 -0
- package/dist/integrations/langchain.d.ts +65 -0
- package/dist/integrations/langchain.d.ts.map +1 -0
- package/dist/integrations/langchain.js +165 -0
- package/dist/integrations/langchain.js.map +1 -0
- package/dist/middleware/express.d.ts +55 -0
- package/dist/middleware/express.d.ts.map +1 -0
- package/dist/middleware/express.js +101 -0
- package/dist/middleware/express.js.map +1 -0
- package/dist/middleware/index.d.ts +6 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +5 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/pricing/compute-pricing.d.ts +57 -0
- package/dist/pricing/compute-pricing.d.ts.map +1 -0
- package/dist/pricing/compute-pricing.js +627 -0
- package/dist/pricing/compute-pricing.js.map +1 -0
- package/dist/pricing/cost_map.json +37665 -0
- package/dist/pricing/egress-pricing.d.ts +55 -0
- package/dist/pricing/egress-pricing.d.ts.map +1 -0
- package/dist/pricing/egress-pricing.js +226 -0
- package/dist/pricing/egress-pricing.js.map +1 -0
- package/dist/pricing/engine.d.ts +24 -0
- package/dist/pricing/engine.d.ts.map +1 -0
- package/dist/pricing/engine.js +148 -0
- package/dist/pricing/engine.js.map +1 -0
- package/dist/pricing/gpu-pricing.d.ts +63 -0
- package/dist/pricing/gpu-pricing.d.ts.map +1 -0
- package/dist/pricing/gpu-pricing.js +484 -0
- package/dist/pricing/gpu-pricing.js.map +1 -0
- package/dist/pricing/rates.d.ts +17 -0
- package/dist/pricing/rates.d.ts.map +1 -0
- package/dist/pricing/rates.js +102 -0
- package/dist/pricing/rates.js.map +1 -0
- package/dist/pricing/service-catalog.d.ts +87 -0
- package/dist/pricing/service-catalog.d.ts.map +1 -0
- package/dist/pricing/service-catalog.js +406 -0
- package/dist/pricing/service-catalog.js.map +1 -0
- package/dist/schema/dexcost-event.v1.json +111 -0
- package/dist/schema/dexcost-task.v1.json +160 -0
- package/dist/schema/validate.d.ts +15 -0
- package/dist/schema/validate.d.ts.map +1 -0
- package/dist/schema/validate.js +87 -0
- package/dist/schema/validate.js.map +1 -0
- package/dist/security/redaction.d.ts +55 -0
- package/dist/security/redaction.d.ts.map +1 -0
- package/dist/security/redaction.js +144 -0
- package/dist/security/redaction.js.map +1 -0
- package/dist/transport/buffer.d.ts +117 -0
- package/dist/transport/buffer.d.ts.map +1 -0
- package/dist/transport/buffer.js +759 -0
- package/dist/transport/buffer.js.map +1 -0
- package/dist/transport/pusher.d.ts +89 -0
- package/dist/transport/pusher.d.ts.map +1 -0
- package/dist/transport/pusher.js +323 -0
- package/dist/transport/pusher.js.map +1 -0
- package/package.json +93 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI auto-instrumentation for dexcost TypeScript SDK.
|
|
3
|
+
*
|
|
4
|
+
* Monkey-patches `OpenAI.Chat.Completions.prototype.create` to automatically
|
|
5
|
+
* record cost events and aggregate token usage on the active task context.
|
|
6
|
+
*
|
|
7
|
+
* Supports both non-streaming and streaming responses.
|
|
8
|
+
*/
|
|
9
|
+
import { randomUUID } from "node:crypto";
|
|
10
|
+
import { createCostEvent } from "../core/models.js";
|
|
11
|
+
import { getCurrentTask } from "../core/context.js";
|
|
12
|
+
import { createAutoTask } from "../core/auto-task.js";
|
|
13
|
+
import { registerInstrument } from "./index.js";
|
|
14
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
15
|
+
let _patched = false;
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
17
|
+
let _original = null;
|
|
18
|
+
let _completionsClass = null;
|
|
19
|
+
let _buffer = null;
|
|
20
|
+
let _pricing = null;
|
|
21
|
+
/** Test helper: inject a mock Completions class so tests avoid importing openai. */
|
|
22
|
+
export function _setCompletionsClass(cls) {
|
|
23
|
+
_completionsClass = cls;
|
|
24
|
+
}
|
|
25
|
+
/** Test helper: reset to real module resolution. */
|
|
26
|
+
export function _resetCompletionsClass() {
|
|
27
|
+
_completionsClass = null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Patch `OpenAI.Chat.Completions.prototype.create` to record cost events.
|
|
31
|
+
*
|
|
32
|
+
* If `openai` is not installed and no mock class is injected, the dynamic
|
|
33
|
+
* import will throw and the function will reject.
|
|
34
|
+
*/
|
|
35
|
+
export async function instrumentOpenai(pricing, buffer) {
|
|
36
|
+
if (_patched)
|
|
37
|
+
return;
|
|
38
|
+
let CompletionsProto;
|
|
39
|
+
if (_completionsClass) {
|
|
40
|
+
CompletionsProto = _completionsClass.prototype;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// openai is an optional peer dependency; the dynamic import only
|
|
44
|
+
// succeeds at runtime if the user has installed it.
|
|
45
|
+
// @ts-ignore -- openai is an optional peer dependency
|
|
46
|
+
const openai = await import("openai");
|
|
47
|
+
const OpenAI = openai.default ?? openai;
|
|
48
|
+
CompletionsProto = OpenAI.Chat.Completions.prototype;
|
|
49
|
+
}
|
|
50
|
+
_original = CompletionsProto.create;
|
|
51
|
+
_buffer = buffer;
|
|
52
|
+
_pricing = pricing;
|
|
53
|
+
CompletionsProto.create = async function (body, options) {
|
|
54
|
+
let task = getCurrentTask();
|
|
55
|
+
// Auto-create a task when no explicit task is active so LLM costs
|
|
56
|
+
// are never silently lost (mirrors Python create_auto_task).
|
|
57
|
+
if (!task) {
|
|
58
|
+
task = createAutoTask("openai.chat");
|
|
59
|
+
_buffer?.upsertTask(task);
|
|
60
|
+
}
|
|
61
|
+
const startTime = performance.now();
|
|
62
|
+
if (body?.stream) {
|
|
63
|
+
const rawStream = await _original.call(this, body, options);
|
|
64
|
+
return wrapStream(rawStream, task, startTime);
|
|
65
|
+
}
|
|
66
|
+
const response = await _original.call(this, body, options);
|
|
67
|
+
try {
|
|
68
|
+
const latencyMs = Math.round(performance.now() - startTime);
|
|
69
|
+
recordEvent(response, task, latencyMs);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// dexcost errors must never crash user code
|
|
73
|
+
}
|
|
74
|
+
return response;
|
|
75
|
+
};
|
|
76
|
+
_patched = true;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Remove the monkey-patch and restore the original `create` method.
|
|
80
|
+
*/
|
|
81
|
+
export function uninstrumentOpenai() {
|
|
82
|
+
if (!_patched || !_original)
|
|
83
|
+
return;
|
|
84
|
+
if (_completionsClass) {
|
|
85
|
+
_completionsClass.prototype.create = _original;
|
|
86
|
+
}
|
|
87
|
+
_original = null;
|
|
88
|
+
_buffer = null;
|
|
89
|
+
_pricing = null;
|
|
90
|
+
_patched = false;
|
|
91
|
+
}
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Internal helpers
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
function recordEvent(response, task, latencyMs) {
|
|
96
|
+
if (!_buffer || !_pricing)
|
|
97
|
+
return;
|
|
98
|
+
const model = response?.model ?? "unknown";
|
|
99
|
+
const usage = response?.usage;
|
|
100
|
+
const hasUsage = usage != null;
|
|
101
|
+
const inputTokens = usage?.prompt_tokens ?? 0;
|
|
102
|
+
const outputTokens = usage?.completion_tokens ?? 0;
|
|
103
|
+
const cachedTokens = usage?.prompt_tokens_details?.cached_tokens ?? 0;
|
|
104
|
+
let costUsd = 0;
|
|
105
|
+
let costConfidence = "estimated";
|
|
106
|
+
let pricingSource = "unknown";
|
|
107
|
+
if (hasUsage) {
|
|
108
|
+
const result = _pricing.getCost(model, inputTokens, outputTokens, cachedTokens);
|
|
109
|
+
costUsd = result.costUsd;
|
|
110
|
+
costConfidence = result.costConfidence;
|
|
111
|
+
pricingSource = result.pricingSource;
|
|
112
|
+
}
|
|
113
|
+
const event = createCostEvent({
|
|
114
|
+
eventId: randomUUID(),
|
|
115
|
+
taskId: task.taskId,
|
|
116
|
+
eventType: "llm_call",
|
|
117
|
+
costUsd,
|
|
118
|
+
costConfidence,
|
|
119
|
+
pricingSource,
|
|
120
|
+
provider: "openai",
|
|
121
|
+
model,
|
|
122
|
+
inputTokens,
|
|
123
|
+
outputTokens,
|
|
124
|
+
cachedTokens,
|
|
125
|
+
latencyMs,
|
|
126
|
+
isRetry: false,
|
|
127
|
+
});
|
|
128
|
+
_buffer.addEvent(event);
|
|
129
|
+
task.llmCostUsd += costUsd;
|
|
130
|
+
task.totalCostUsd += costUsd;
|
|
131
|
+
task.totalInputTokens += inputTokens;
|
|
132
|
+
task.totalOutputTokens += outputTokens;
|
|
133
|
+
task.totalCachedTokens += cachedTokens;
|
|
134
|
+
_buffer.upsertTask(task);
|
|
135
|
+
}
|
|
136
|
+
function wrapStream(rawStream, task, startTime) {
|
|
137
|
+
let model = "unknown";
|
|
138
|
+
let inputTokens = 0;
|
|
139
|
+
let outputTokens = 0;
|
|
140
|
+
let cachedTokens = 0;
|
|
141
|
+
let hasUsage = false;
|
|
142
|
+
let finalized = false;
|
|
143
|
+
return {
|
|
144
|
+
[Symbol.asyncIterator]() {
|
|
145
|
+
const iter = rawStream[Symbol.asyncIterator]();
|
|
146
|
+
return {
|
|
147
|
+
async next() {
|
|
148
|
+
const result = await iter.next();
|
|
149
|
+
if (result.done) {
|
|
150
|
+
if (finalized)
|
|
151
|
+
return result;
|
|
152
|
+
finalized = true;
|
|
153
|
+
try {
|
|
154
|
+
const latencyMs = Math.round(performance.now() - startTime);
|
|
155
|
+
if (hasUsage && _pricing && _buffer) {
|
|
156
|
+
const costResult = _pricing.getCost(model, inputTokens, outputTokens, cachedTokens);
|
|
157
|
+
const event = createCostEvent({
|
|
158
|
+
eventId: randomUUID(),
|
|
159
|
+
taskId: task.taskId,
|
|
160
|
+
eventType: "llm_call",
|
|
161
|
+
costUsd: costResult.costUsd,
|
|
162
|
+
costConfidence: costResult.costConfidence,
|
|
163
|
+
pricingSource: costResult.pricingSource,
|
|
164
|
+
provider: "openai",
|
|
165
|
+
model,
|
|
166
|
+
inputTokens,
|
|
167
|
+
outputTokens,
|
|
168
|
+
cachedTokens,
|
|
169
|
+
latencyMs,
|
|
170
|
+
isRetry: false,
|
|
171
|
+
});
|
|
172
|
+
_buffer.addEvent(event);
|
|
173
|
+
task.llmCostUsd += costResult.costUsd;
|
|
174
|
+
task.totalCostUsd += costResult.costUsd;
|
|
175
|
+
task.totalInputTokens += inputTokens;
|
|
176
|
+
task.totalOutputTokens += outputTokens;
|
|
177
|
+
task.totalCachedTokens += cachedTokens;
|
|
178
|
+
_buffer.upsertTask(task);
|
|
179
|
+
}
|
|
180
|
+
else if (_buffer) {
|
|
181
|
+
const event = createCostEvent({
|
|
182
|
+
eventId: randomUUID(),
|
|
183
|
+
taskId: task.taskId,
|
|
184
|
+
eventType: "llm_call",
|
|
185
|
+
costUsd: 0,
|
|
186
|
+
costConfidence: "estimated",
|
|
187
|
+
pricingSource: "unknown",
|
|
188
|
+
provider: "openai",
|
|
189
|
+
model,
|
|
190
|
+
inputTokens: 0,
|
|
191
|
+
outputTokens: 0,
|
|
192
|
+
latencyMs,
|
|
193
|
+
isRetry: false,
|
|
194
|
+
});
|
|
195
|
+
_buffer.addEvent(event);
|
|
196
|
+
_buffer.upsertTask(task);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
// dexcost errors must never crash user code
|
|
201
|
+
}
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
const chunk = result.value;
|
|
205
|
+
if (chunk?.model)
|
|
206
|
+
model = chunk.model;
|
|
207
|
+
if (chunk?.usage) {
|
|
208
|
+
hasUsage = true;
|
|
209
|
+
inputTokens = chunk.usage.prompt_tokens ?? inputTokens;
|
|
210
|
+
outputTokens = chunk.usage.completion_tokens ?? outputTokens;
|
|
211
|
+
cachedTokens = chunk.usage.prompt_tokens_details?.cached_tokens ?? cachedTokens;
|
|
212
|
+
}
|
|
213
|
+
return result;
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
// Self-register so importing this module is enough to make the instrument available.
|
|
220
|
+
registerInstrument("openai", instrumentOpenai, uninstrumentOpenai);
|
|
221
|
+
//# sourceMappingURL=openai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/instruments/openai.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vercel AI SDK auto-instrumentation for dexcost TypeScript SDK.
|
|
3
|
+
*
|
|
4
|
+
* Monkey-patches `generateText` and `streamText` from the `ai` package to
|
|
5
|
+
* automatically record cost events and aggregate token usage on the active
|
|
6
|
+
* task context.
|
|
7
|
+
*
|
|
8
|
+
* The `ai` package is an optional peer dependency; the instrument gracefully
|
|
9
|
+
* no-ops if the package is not installed.
|
|
10
|
+
*/
|
|
11
|
+
import type { EventBuffer } from "../transport/buffer.js";
|
|
12
|
+
import type { PricingEngine } from "../pricing/engine.js";
|
|
13
|
+
/** Test helper: inject a mock ai module so tests avoid importing ai. */
|
|
14
|
+
export declare function _setAiModule(mod: any): void;
|
|
15
|
+
/** Test helper: reset to real module resolution. */
|
|
16
|
+
export declare function _resetAiModule(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Patch `generateText` and `streamText` from the `ai` module to record cost events.
|
|
19
|
+
*
|
|
20
|
+
* If `ai` is not installed and no mock module is injected, the dynamic
|
|
21
|
+
* import will throw and the function will reject.
|
|
22
|
+
*/
|
|
23
|
+
export declare function instrumentVercelAi(pricing: PricingEngine, buffer: EventBuffer): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Remove the monkey-patches and restore the original functions.
|
|
26
|
+
*/
|
|
27
|
+
export declare function uninstrumentVercelAi(): void;
|
|
28
|
+
//# sourceMappingURL=vercel-ai.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel-ai.d.ts","sourceRoot":"","sources":["../../src/instruments/vercel-ai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,sBAAsB,CAAC;AActE,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAE3C;AAED,oDAAoD;AACpD,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAgED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,IAAI,CAAC,CAmEf;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAW3C"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vercel AI SDK auto-instrumentation for dexcost TypeScript SDK.
|
|
3
|
+
*
|
|
4
|
+
* Monkey-patches `generateText` and `streamText` from the `ai` package to
|
|
5
|
+
* automatically record cost events and aggregate token usage on the active
|
|
6
|
+
* task context.
|
|
7
|
+
*
|
|
8
|
+
* The `ai` package is an optional peer dependency; the instrument gracefully
|
|
9
|
+
* no-ops if the package is not installed.
|
|
10
|
+
*/
|
|
11
|
+
import { randomUUID } from "node:crypto";
|
|
12
|
+
import { createCostEvent } from "../core/models.js";
|
|
13
|
+
import { getCurrentTask } from "../core/context.js";
|
|
14
|
+
import { createAutoTask } from "../core/auto-task.js";
|
|
15
|
+
import { registerInstrument } from "./index.js";
|
|
16
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
17
|
+
let _patched = false;
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
19
|
+
let _originalGenerateText = null;
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
21
|
+
let _originalStreamText = null;
|
|
22
|
+
let _aiModule = null;
|
|
23
|
+
let _buffer = null;
|
|
24
|
+
let _pricing = null;
|
|
25
|
+
/** Test helper: inject a mock ai module so tests avoid importing ai. */
|
|
26
|
+
export function _setAiModule(mod) {
|
|
27
|
+
_aiModule = mod;
|
|
28
|
+
}
|
|
29
|
+
/** Test helper: reset to real module resolution. */
|
|
30
|
+
export function _resetAiModule() {
|
|
31
|
+
_aiModule = null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Extract the model identifier from Vercel AI SDK options.
|
|
35
|
+
*
|
|
36
|
+
* The `ai` package accepts model as either a string or an object with
|
|
37
|
+
* a `modelId` property (e.g., `openai("gpt-4o")` returns an object).
|
|
38
|
+
*/
|
|
39
|
+
function extractModel(opts) {
|
|
40
|
+
if (typeof opts?.model === "string")
|
|
41
|
+
return opts.model;
|
|
42
|
+
if (opts?.model?.modelId)
|
|
43
|
+
return String(opts.model.modelId);
|
|
44
|
+
return "unknown";
|
|
45
|
+
}
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Internal helpers
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
function recordEvent(model, inputTokens, outputTokens, task, latencyMs) {
|
|
50
|
+
if (!_buffer || !_pricing)
|
|
51
|
+
return;
|
|
52
|
+
const hasUsage = inputTokens > 0 || outputTokens > 0;
|
|
53
|
+
let costUsd = 0;
|
|
54
|
+
let costConfidence = "estimated";
|
|
55
|
+
let pricingSource = "unknown";
|
|
56
|
+
if (hasUsage) {
|
|
57
|
+
const result = _pricing.getCost(model, inputTokens, outputTokens);
|
|
58
|
+
costUsd = result.costUsd;
|
|
59
|
+
costConfidence = result.costConfidence;
|
|
60
|
+
pricingSource = result.pricingSource;
|
|
61
|
+
}
|
|
62
|
+
const event = createCostEvent({
|
|
63
|
+
eventId: randomUUID(),
|
|
64
|
+
taskId: task.taskId,
|
|
65
|
+
eventType: "llm_call",
|
|
66
|
+
costUsd,
|
|
67
|
+
costConfidence,
|
|
68
|
+
pricingSource,
|
|
69
|
+
provider: "vercel-ai",
|
|
70
|
+
model,
|
|
71
|
+
inputTokens,
|
|
72
|
+
outputTokens,
|
|
73
|
+
latencyMs,
|
|
74
|
+
isRetry: false,
|
|
75
|
+
});
|
|
76
|
+
_buffer.addEvent(event);
|
|
77
|
+
task.llmCostUsd += costUsd;
|
|
78
|
+
task.totalCostUsd += costUsd;
|
|
79
|
+
task.totalInputTokens += inputTokens;
|
|
80
|
+
task.totalOutputTokens += outputTokens;
|
|
81
|
+
_buffer.upsertTask(task);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Patch `generateText` and `streamText` from the `ai` module to record cost events.
|
|
85
|
+
*
|
|
86
|
+
* If `ai` is not installed and no mock module is injected, the dynamic
|
|
87
|
+
* import will throw and the function will reject.
|
|
88
|
+
*/
|
|
89
|
+
export async function instrumentVercelAi(pricing, buffer) {
|
|
90
|
+
if (_patched)
|
|
91
|
+
return;
|
|
92
|
+
if (!_aiModule) {
|
|
93
|
+
// ai is an optional peer dependency; the dynamic import only
|
|
94
|
+
// succeeds at runtime if the user has installed it.
|
|
95
|
+
// @ts-ignore -- ai is an optional peer dependency
|
|
96
|
+
_aiModule = await import("ai");
|
|
97
|
+
}
|
|
98
|
+
_originalGenerateText = _aiModule.generateText;
|
|
99
|
+
_originalStreamText = _aiModule.streamText;
|
|
100
|
+
_buffer = buffer;
|
|
101
|
+
_pricing = pricing;
|
|
102
|
+
_aiModule.generateText = async function patchedGenerateText(opts) {
|
|
103
|
+
let task = getCurrentTask();
|
|
104
|
+
// Auto-create a task when no explicit task is active so LLM costs
|
|
105
|
+
// are never silently lost (mirrors Python create_auto_task).
|
|
106
|
+
if (!task) {
|
|
107
|
+
task = createAutoTask("vercel-ai.generateText");
|
|
108
|
+
_buffer?.upsertTask(task);
|
|
109
|
+
}
|
|
110
|
+
const startTime = performance.now();
|
|
111
|
+
const result = await _originalGenerateText.call(this, opts);
|
|
112
|
+
const latencyMs = Math.round(performance.now() - startTime);
|
|
113
|
+
const model = extractModel(opts);
|
|
114
|
+
const inputTokens = result?.usage?.promptTokens ?? 0;
|
|
115
|
+
const outputTokens = result?.usage?.completionTokens ?? 0;
|
|
116
|
+
recordEvent(model, inputTokens, outputTokens, task, latencyMs);
|
|
117
|
+
return result;
|
|
118
|
+
};
|
|
119
|
+
_aiModule.streamText = function patchedStreamText(opts) {
|
|
120
|
+
let task = getCurrentTask();
|
|
121
|
+
// Auto-create a task when no explicit task is active so LLM costs
|
|
122
|
+
// are never silently lost (mirrors Python create_auto_task).
|
|
123
|
+
if (!task) {
|
|
124
|
+
task = createAutoTask("vercel-ai.streamText");
|
|
125
|
+
_buffer?.upsertTask(task);
|
|
126
|
+
}
|
|
127
|
+
const startTime = performance.now();
|
|
128
|
+
const streamResult = _originalStreamText.call(this, opts);
|
|
129
|
+
// Wrap the stream to capture usage after iteration completes.
|
|
130
|
+
// The Vercel AI SDK streamText returns an object with an async iterator
|
|
131
|
+
// for text chunks and a `usage` promise that resolves when done.
|
|
132
|
+
if (streamResult && typeof streamResult[Symbol.asyncIterator] === "function") {
|
|
133
|
+
return wrapStream(streamResult, opts, task, startTime);
|
|
134
|
+
}
|
|
135
|
+
return streamResult;
|
|
136
|
+
};
|
|
137
|
+
_patched = true;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Remove the monkey-patches and restore the original functions.
|
|
141
|
+
*/
|
|
142
|
+
export function uninstrumentVercelAi() {
|
|
143
|
+
if (!_patched || !_aiModule)
|
|
144
|
+
return;
|
|
145
|
+
if (_originalGenerateText)
|
|
146
|
+
_aiModule.generateText = _originalGenerateText;
|
|
147
|
+
if (_originalStreamText)
|
|
148
|
+
_aiModule.streamText = _originalStreamText;
|
|
149
|
+
_originalGenerateText = null;
|
|
150
|
+
_originalStreamText = null;
|
|
151
|
+
_buffer = null;
|
|
152
|
+
_pricing = null;
|
|
153
|
+
_patched = false;
|
|
154
|
+
}
|
|
155
|
+
function wrapStream(rawStream, opts, task, startTime) {
|
|
156
|
+
let recorded = false;
|
|
157
|
+
// Preserve all properties of the original stream result
|
|
158
|
+
const wrapped = Object.create(rawStream);
|
|
159
|
+
wrapped[Symbol.asyncIterator] = function () {
|
|
160
|
+
const iter = rawStream[Symbol.asyncIterator]();
|
|
161
|
+
return {
|
|
162
|
+
async next() {
|
|
163
|
+
const result = await iter.next();
|
|
164
|
+
if (result.done && !recorded) {
|
|
165
|
+
recorded = true;
|
|
166
|
+
const latencyMs = Math.round(performance.now() - startTime);
|
|
167
|
+
// After stream completes, try to read usage from the stream result.
|
|
168
|
+
// Vercel AI SDK exposes usage as a property or promise on the result.
|
|
169
|
+
let inputTokens = 0;
|
|
170
|
+
let outputTokens = 0;
|
|
171
|
+
try {
|
|
172
|
+
const usage = rawStream.usage ?? (await rawStream.usage);
|
|
173
|
+
if (usage) {
|
|
174
|
+
inputTokens = usage.promptTokens ?? 0;
|
|
175
|
+
outputTokens = usage.completionTokens ?? 0;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// usage not available
|
|
180
|
+
}
|
|
181
|
+
const model = extractModel(opts);
|
|
182
|
+
recordEvent(model, inputTokens, outputTokens, task, latencyMs);
|
|
183
|
+
}
|
|
184
|
+
return result;
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
return wrapped;
|
|
189
|
+
}
|
|
190
|
+
// Self-register so importing this module is enough to make the instrument available.
|
|
191
|
+
registerInstrument("vercel-ai", instrumentVercelAi, uninstrumentVercelAi);
|
|
192
|
+
//# sourceMappingURL=vercel-ai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel-ai.js","sourceRoot":"","sources":["../../src/instruments/vercel-ai.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DexcostCallbackHandler — duck-typed LangChain callback handler.
|
|
3
|
+
*
|
|
4
|
+
* Works with any LangChain version without importing `@langchain/core`.
|
|
5
|
+
* Attach to a LangChain chain/agent as a callback to automatically track
|
|
6
|
+
* LLM call costs inside dexcost tasks.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* const handler = new DexcostCallbackHandler(tracker);
|
|
10
|
+
* const chain = new LLMChain({ llm, prompt, callbacks: [handler] });
|
|
11
|
+
*/
|
|
12
|
+
import type { CostTracker } from "../core/tracker.js";
|
|
13
|
+
/**
|
|
14
|
+
* A duck-typed LangChain callback handler that records cost events for every
|
|
15
|
+
* LLM call that occurs inside a dexcost tracked task.
|
|
16
|
+
*
|
|
17
|
+
* No `@langchain/core` dependency — the handler implements the subset of the
|
|
18
|
+
* BaseCallbackHandler interface that LangChain calls for LLM lifecycle events.
|
|
19
|
+
*/
|
|
20
|
+
export declare class DexcostCallbackHandler {
|
|
21
|
+
private _pricing;
|
|
22
|
+
private _buffer;
|
|
23
|
+
private _pending;
|
|
24
|
+
constructor(tracker: CostTracker);
|
|
25
|
+
/**
|
|
26
|
+
* Called by LangChain at the start of an LLM invocation.
|
|
27
|
+
*
|
|
28
|
+
* @param serialized - Serialized LLM object. Model name is extracted from
|
|
29
|
+
* `serialized.kwargs.model_name` (preferred) or the last element of
|
|
30
|
+
* `serialized.id` (fallback).
|
|
31
|
+
* @param prompts - The input prompt strings (unused beyond signalling start).
|
|
32
|
+
* @param runId - Unique identifier for this run, used to correlate start/end.
|
|
33
|
+
*/
|
|
34
|
+
handleLLMStart(serialized: Record<string, unknown>, _prompts: string[], runId: string): void;
|
|
35
|
+
/**
|
|
36
|
+
* Called by LangChain when an LLM invocation completes successfully.
|
|
37
|
+
*
|
|
38
|
+
* Records a `llm_call` cost event into the active task (if any).
|
|
39
|
+
*
|
|
40
|
+
* @param output - LLM output object. Token counts are read from
|
|
41
|
+
* `output.llmOutput.tokenUsage.{promptTokens, completionTokens}`.
|
|
42
|
+
* @param runId - Matches the runId from the corresponding handleLLMStart.
|
|
43
|
+
*/
|
|
44
|
+
handleLLMEnd(output: Record<string, unknown>, runId: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* Called by LangChain when an LLM invocation fails.
|
|
47
|
+
*
|
|
48
|
+
* Records a failure `llm_call` event (cost 0, `error_type` in details)
|
|
49
|
+
* when a task context is active, mirroring the Python SDK's
|
|
50
|
+
* `on_llm_error`. Never throws.
|
|
51
|
+
*
|
|
52
|
+
* @param error - The error that occurred.
|
|
53
|
+
* @param runId - Matches the runId from the corresponding handleLLMStart.
|
|
54
|
+
*/
|
|
55
|
+
handleLLMError(error: Error, runId: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* Extract the model identifier from a serialized LangChain LLM object.
|
|
58
|
+
*
|
|
59
|
+
* Preference order:
|
|
60
|
+
* 1. `serialized.kwargs.model_name` — set by most ChatModel subclasses
|
|
61
|
+
* 2. last element of `serialized.id` — the class name (e.g. "ChatOpenAI")
|
|
62
|
+
*/
|
|
63
|
+
private _extractModel;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=langchain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"langchain.d.ts","sourceRoot":"","sources":["../../src/integrations/langchain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAetD;;;;;;GAMG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,QAAQ,CAAsC;gBAE1C,OAAO,EAAE,WAAW;IAKhC;;;;;;;;OAQG;IACH,cAAc,CACZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,QAAQ,EAAE,MAAM,EAAE,EAClB,KAAK,EAAE,MAAM,GACZ,IAAI;IAKP;;;;;;;;OAQG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAqDlE;;;;;;;;;OASG;IACH,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAuCjD;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;CActB"}
|