@warpmetrics/warp 0.0.7 → 0.0.9
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 +1 -1
- package/README.md +0 -9
- package/package.json +2 -1
- package/src/core/registry.js +0 -6
- package/src/core/transport.js +26 -5
- package/src/core/utils.js +0 -43
- package/src/core/warp.js +12 -11
- package/src/index.d.ts +8 -3
- package/src/index.js +2 -3
- package/src/providers/anthropic.js +6 -2
- package/src/providers/openai.js +13 -5
- package/src/trace/act.js +26 -0
- package/src/trace/cost.js +0 -50
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -105,15 +105,6 @@ ref(response) // 'wm_call_01jkx3ndef8mn2q7kpvhc4e9ws'
|
|
|
105
105
|
ref('wm_run_01jkx3ndek0gh4r5tmqp9a3bcv') // pass-through
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
### `cost(target)`
|
|
109
|
-
|
|
110
|
-
Get the estimated cost in USD for any target. Aggregates across all nested calls for runs and groups.
|
|
111
|
-
|
|
112
|
-
```js
|
|
113
|
-
cost(response) // 0.0012
|
|
114
|
-
cost(r) // 0.0036 (sum of all calls in the run)
|
|
115
|
-
```
|
|
116
|
-
|
|
117
108
|
### `flush()`
|
|
118
109
|
|
|
119
110
|
Manually flush pending events. Events are auto-flushed on an interval and on process exit, but you can force it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@warpmetrics/warp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "Measure your agents, not your LLM calls.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"test": "vitest run",
|
|
19
19
|
"test:watch": "vitest",
|
|
20
20
|
"test:coverage": "vitest run --coverage",
|
|
21
|
+
"preversion": "vitest run --coverage",
|
|
21
22
|
"release:patch": "npm version patch && git push origin main --tags",
|
|
22
23
|
"release:minor": "npm version minor && git push origin main --tags"
|
|
23
24
|
},
|
package/src/core/registry.js
CHANGED
|
@@ -9,9 +9,3 @@ export const groupRegistry = new Map();
|
|
|
9
9
|
|
|
10
10
|
/** @type {WeakMap<object, string>} LLM response object → call id */
|
|
11
11
|
export const responseRegistry = new WeakMap();
|
|
12
|
-
|
|
13
|
-
/** @type {WeakMap<object, number>} LLM response object → cost in USD */
|
|
14
|
-
export const costRegistry = new WeakMap();
|
|
15
|
-
|
|
16
|
-
/** @type {Map<string, number>} call id → cost in USD (for aggregation) */
|
|
17
|
-
export const costByCallId = new Map();
|
package/src/core/transport.js
CHANGED
|
@@ -21,6 +21,7 @@ const queue = {
|
|
|
21
21
|
calls: [],
|
|
22
22
|
links: [],
|
|
23
23
|
outcomes: [],
|
|
24
|
+
acts: [],
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
let flushTimeout = null;
|
|
@@ -44,6 +45,7 @@ export function clearQueue() {
|
|
|
44
45
|
queue.calls.length = 0;
|
|
45
46
|
queue.links.length = 0;
|
|
46
47
|
queue.outcomes.length = 0;
|
|
48
|
+
queue.acts.length = 0;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
// ---------------------------------------------------------------------------
|
|
@@ -56,7 +58,7 @@ function enqueue(type, event) {
|
|
|
56
58
|
queue[type].push(event);
|
|
57
59
|
|
|
58
60
|
const total = queue.runs.length + queue.groups.length + queue.calls.length
|
|
59
|
-
+ queue.links.length + queue.outcomes.length;
|
|
61
|
+
+ queue.links.length + queue.outcomes.length + queue.acts.length;
|
|
60
62
|
|
|
61
63
|
if (total >= config.maxBatchSize) {
|
|
62
64
|
flush();
|
|
@@ -81,10 +83,11 @@ export async function flush() {
|
|
|
81
83
|
calls: queue.calls.splice(0),
|
|
82
84
|
links: queue.links.splice(0),
|
|
83
85
|
outcomes: queue.outcomes.splice(0),
|
|
86
|
+
acts: queue.acts.splice(0),
|
|
84
87
|
};
|
|
85
88
|
|
|
86
89
|
const total = batch.runs.length + batch.groups.length + batch.calls.length
|
|
87
|
-
+ batch.links.length + batch.outcomes.length;
|
|
90
|
+
+ batch.links.length + batch.outcomes.length + batch.acts.length;
|
|
88
91
|
|
|
89
92
|
if (total === 0) return;
|
|
90
93
|
|
|
@@ -100,10 +103,17 @@ export async function flush() {
|
|
|
100
103
|
`[warpmetrics] Flushing ${total} events`
|
|
101
104
|
+ ` (runs=${batch.runs.length} groups=${batch.groups.length}`
|
|
102
105
|
+ ` calls=${batch.calls.length} links=${batch.links.length}`
|
|
103
|
-
+ ` outcomes=${batch.outcomes.length})`
|
|
106
|
+
+ ` outcomes=${batch.outcomes.length} acts=${batch.acts.length})`
|
|
104
107
|
);
|
|
105
108
|
}
|
|
106
109
|
|
|
110
|
+
const raw = JSON.stringify(batch);
|
|
111
|
+
const body = JSON.stringify({ d: Buffer.from(raw, 'utf-8').toString('base64') });
|
|
112
|
+
|
|
113
|
+
if (config.debug) {
|
|
114
|
+
console.log(`[warpmetrics] Payload size: ${(raw.length / 1024).toFixed(1)}KB → ${(body.length / 1024).toFixed(1)}KB (encoded)`);
|
|
115
|
+
}
|
|
116
|
+
|
|
107
117
|
try {
|
|
108
118
|
const res = await fetch(`${config.baseUrl}/v1/events`, {
|
|
109
119
|
method: 'POST',
|
|
@@ -112,7 +122,7 @@ export async function flush() {
|
|
|
112
122
|
'Authorization': `Bearer ${config.apiKey}`,
|
|
113
123
|
'X-SDK-Version': SDK_VERSION,
|
|
114
124
|
},
|
|
115
|
-
body
|
|
125
|
+
body,
|
|
116
126
|
});
|
|
117
127
|
|
|
118
128
|
if (!res.ok) {
|
|
@@ -122,7 +132,8 @@ export async function flush() {
|
|
|
122
132
|
|
|
123
133
|
if (config.debug) {
|
|
124
134
|
const result = await res.json();
|
|
125
|
-
|
|
135
|
+
const d = result.data || result;
|
|
136
|
+
console.log(`[warpmetrics] Flush OK — received=${d.received} processed=${d.processed}`);
|
|
126
137
|
}
|
|
127
138
|
} catch (err) {
|
|
128
139
|
if (config.debug) {
|
|
@@ -134,6 +145,7 @@ export async function flush() {
|
|
|
134
145
|
queue.calls.unshift(...batch.calls);
|
|
135
146
|
queue.links.unshift(...batch.links);
|
|
136
147
|
queue.outcomes.unshift(...batch.outcomes);
|
|
148
|
+
queue.acts.unshift(...batch.acts);
|
|
137
149
|
}
|
|
138
150
|
}
|
|
139
151
|
|
|
@@ -185,6 +197,15 @@ export function logOutcome(data) {
|
|
|
185
197
|
});
|
|
186
198
|
}
|
|
187
199
|
|
|
200
|
+
export function logAct(data) {
|
|
201
|
+
enqueue('acts', {
|
|
202
|
+
targetId: data.targetId,
|
|
203
|
+
name: data.name,
|
|
204
|
+
metadata: data.metadata,
|
|
205
|
+
timestamp: new Date().toISOString(),
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
188
209
|
// ---------------------------------------------------------------------------
|
|
189
210
|
// Auto-flush on process exit (Node.js only)
|
|
190
211
|
// ---------------------------------------------------------------------------
|
package/src/core/utils.js
CHANGED
|
@@ -13,46 +13,3 @@ export function generateId(prefix) {
|
|
|
13
13
|
return `wm_${prefix}_${ulid().toLowerCase()}`;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
// Pricing (per 1 M tokens, USD)
|
|
18
|
-
// Best-effort — returns 0 for unknown models.
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
|
|
21
|
-
const PRICING = {
|
|
22
|
-
// OpenAI
|
|
23
|
-
'gpt-4o': { prompt: 2.50, completion: 10.00 },
|
|
24
|
-
'gpt-4o-2024-11-20': { prompt: 2.50, completion: 10.00 },
|
|
25
|
-
'gpt-4o-mini': { prompt: 0.15, completion: 0.60 },
|
|
26
|
-
'gpt-4o-mini-2024-07-18': { prompt: 0.15, completion: 0.60 },
|
|
27
|
-
'gpt-4-turbo': { prompt: 10.00, completion: 30.00 },
|
|
28
|
-
'gpt-4-turbo-preview': { prompt: 10.00, completion: 30.00 },
|
|
29
|
-
'gpt-4': { prompt: 30.00, completion: 60.00 },
|
|
30
|
-
'gpt-3.5-turbo': { prompt: 0.50, completion: 1.50 },
|
|
31
|
-
'o1': { prompt: 15.00, completion: 60.00 },
|
|
32
|
-
'o1-mini': { prompt: 3.00, completion: 12.00 },
|
|
33
|
-
'o3-mini': { prompt: 1.10, completion: 4.40 },
|
|
34
|
-
|
|
35
|
-
// Anthropic
|
|
36
|
-
'claude-sonnet-4-5-20250514': { prompt: 3.00, completion: 15.00 },
|
|
37
|
-
'claude-opus-4-6': { prompt: 15.00, completion: 75.00 },
|
|
38
|
-
'claude-3-5-sonnet-20241022': { prompt: 3.00, completion: 15.00 },
|
|
39
|
-
'claude-3-5-sonnet-latest': { prompt: 3.00, completion: 15.00 },
|
|
40
|
-
'claude-3-5-haiku-20241022': { prompt: 0.80, completion: 4.00 },
|
|
41
|
-
'claude-3-5-haiku-latest': { prompt: 0.80, completion: 4.00 },
|
|
42
|
-
'claude-3-opus-20240229': { prompt: 15.00, completion: 75.00 },
|
|
43
|
-
'claude-3-haiku-20240307': { prompt: 0.25, completion: 1.25 },
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Estimate cost from model name and token counts.
|
|
48
|
-
* Returns 0 for unknown models.
|
|
49
|
-
* @param {string} model
|
|
50
|
-
* @param {{ prompt: number, completion: number }} tokens
|
|
51
|
-
* @returns {number} cost in USD
|
|
52
|
-
*/
|
|
53
|
-
export function calculateCost(model, tokens) {
|
|
54
|
-
const p = PRICING[model];
|
|
55
|
-
if (!p) return 0;
|
|
56
|
-
return (tokens.prompt / 1_000_000) * p.prompt
|
|
57
|
-
+ (tokens.completion / 1_000_000) * p.completion;
|
|
58
|
-
}
|
package/src/core/warp.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Warpmetrics SDK — warp()
|
|
2
2
|
// Wraps an LLM client so every API call is automatically tracked.
|
|
3
3
|
|
|
4
|
-
import { generateId
|
|
5
|
-
import { responseRegistry
|
|
4
|
+
import { generateId } from './utils.js';
|
|
5
|
+
import { responseRegistry } from './registry.js';
|
|
6
6
|
import { logCall, setConfig, getConfig } from './transport.js';
|
|
7
7
|
import * as openai from '../providers/openai.js';
|
|
8
8
|
import * as anthropic from '../providers/anthropic.js';
|
|
@@ -39,21 +39,18 @@ function createInterceptor(originalFn, context, provider) {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const ext = provider.extract(result);
|
|
42
|
-
const cost = calculateCost(model, ext.tokens);
|
|
43
42
|
|
|
44
43
|
logCall({
|
|
45
44
|
id: callId, provider: provider.name, model, messages,
|
|
46
45
|
response: ext.response,
|
|
47
46
|
tools: tools ? tools.map(t => t.function?.name || t.name).filter(Boolean) : null,
|
|
48
47
|
toolCalls: ext.toolCalls,
|
|
49
|
-
tokens: ext.tokens,
|
|
48
|
+
tokens: ext.tokens, latency,
|
|
50
49
|
timestamp: new Date().toISOString(),
|
|
51
50
|
status: 'success',
|
|
52
51
|
});
|
|
53
52
|
|
|
54
53
|
responseRegistry.set(result, callId);
|
|
55
|
-
costRegistry.set(result, cost);
|
|
56
|
-
costByCallId.set(callId, cost);
|
|
57
54
|
|
|
58
55
|
return result;
|
|
59
56
|
} catch (error) {
|
|
@@ -90,21 +87,17 @@ function wrapStream(stream, ctx) {
|
|
|
90
87
|
? ctx.provider.normalizeUsage(usage)
|
|
91
88
|
: { prompt: 0, completion: 0, total: 0 };
|
|
92
89
|
|
|
93
|
-
const cost = calculateCost(ctx.model, tokens);
|
|
94
|
-
|
|
95
90
|
logCall({
|
|
96
91
|
id: ctx.callId, provider: ctx.provider.name, model: ctx.model, messages: ctx.messages,
|
|
97
92
|
response: content,
|
|
98
93
|
tools: ctx.tools ? ctx.tools.map(t => t.function?.name || t.name).filter(Boolean) : null,
|
|
99
|
-
tokens,
|
|
94
|
+
tokens,
|
|
100
95
|
latency: Date.now() - ctx.start,
|
|
101
96
|
timestamp: new Date().toISOString(),
|
|
102
97
|
status: 'success',
|
|
103
98
|
});
|
|
104
99
|
|
|
105
100
|
responseRegistry.set(wrapped, ctx.callId);
|
|
106
|
-
costRegistry.set(wrapped, cost);
|
|
107
|
-
costByCallId.set(ctx.callId, cost);
|
|
108
101
|
},
|
|
109
102
|
};
|
|
110
103
|
return wrapped;
|
|
@@ -140,6 +133,14 @@ export function warp(client, options) {
|
|
|
140
133
|
});
|
|
141
134
|
}
|
|
142
135
|
|
|
136
|
+
const finalCfg = getConfig();
|
|
137
|
+
if (finalCfg.debug) {
|
|
138
|
+
const masked = finalCfg.apiKey
|
|
139
|
+
? finalCfg.apiKey.slice(0, 10) + '...' + finalCfg.apiKey.slice(-4)
|
|
140
|
+
: '(none)';
|
|
141
|
+
console.log(`[warpmetrics] Config: baseUrl=${finalCfg.baseUrl} apiKey=${masked} enabled=${finalCfg.enabled} flushInterval=${finalCfg.flushInterval} maxBatchSize=${finalCfg.maxBatchSize}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
143
144
|
const provider = findProvider(client);
|
|
144
145
|
|
|
145
146
|
if (!provider) {
|
package/src/index.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ export interface OutcomeOptions {
|
|
|
42
42
|
metadata?: Record<string, any>;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
|
|
45
46
|
/**
|
|
46
47
|
* Wrap an LLM client to automatically track every API call.
|
|
47
48
|
* Pass options on the first call to configure the SDK; env vars are used as defaults.
|
|
@@ -64,11 +65,15 @@ export function outcome(
|
|
|
64
65
|
options?: OutcomeOptions,
|
|
65
66
|
): void;
|
|
66
67
|
|
|
68
|
+
/** Record an action taken on a tracked target (e.g. acting on feedback). */
|
|
69
|
+
export function act(
|
|
70
|
+
target: Run | Group | object | string,
|
|
71
|
+
name: string,
|
|
72
|
+
metadata?: Record<string, any>,
|
|
73
|
+
): void;
|
|
74
|
+
|
|
67
75
|
/** Resolve any trackable target to its string ID. */
|
|
68
76
|
export function ref(target: Run | Group | object | string): string | undefined;
|
|
69
77
|
|
|
70
|
-
/** Get the cost in USD for any tracked target. */
|
|
71
|
-
export function cost(target: Run | Group | object | string): number;
|
|
72
|
-
|
|
73
78
|
/** Manually flush pending events to the API. */
|
|
74
79
|
export function flush(): Promise<void>;
|
package/src/index.js
CHANGED
|
@@ -6,14 +6,13 @@
|
|
|
6
6
|
// group(label, options?) — create a group
|
|
7
7
|
// add(target, ...items) — add groups / calls to a run or group
|
|
8
8
|
// outcome(target, name, options?) — record a result
|
|
9
|
+
// act(target, name, options?) — record an action taken on a result
|
|
9
10
|
// ref(target) — get tracking ID
|
|
10
|
-
// cost(target) — get cost in USD
|
|
11
|
-
|
|
12
11
|
export { warp } from './core/warp.js';
|
|
13
12
|
export { run } from './trace/run.js';
|
|
14
13
|
export { group } from './trace/group.js';
|
|
15
14
|
export { add } from './trace/add.js';
|
|
16
15
|
export { outcome } from './trace/outcome.js';
|
|
16
|
+
export { act } from './trace/act.js';
|
|
17
17
|
export { ref } from './trace/ref.js';
|
|
18
|
-
export { cost } from './trace/cost.js';
|
|
19
18
|
export { flush } from './core/transport.js';
|
|
@@ -9,11 +9,13 @@ export function detect(client) {
|
|
|
9
9
|
export function extract(result) {
|
|
10
10
|
const input = result?.usage?.input_tokens || 0;
|
|
11
11
|
const output = result?.usage?.output_tokens || 0;
|
|
12
|
+
const cacheWrite = result?.usage?.cache_creation_input_tokens || 0;
|
|
13
|
+
const cacheRead = result?.usage?.cache_read_input_tokens || 0;
|
|
12
14
|
return {
|
|
13
15
|
response: Array.isArray(result?.content)
|
|
14
16
|
? result.content.filter(c => c.type === 'text').map(c => c.text).join('')
|
|
15
17
|
: '',
|
|
16
|
-
tokens: { prompt: input, completion: output, total: input + output },
|
|
18
|
+
tokens: { prompt: input, completion: output, total: input + output, cacheWrite, cacheRead },
|
|
17
19
|
toolCalls: null,
|
|
18
20
|
};
|
|
19
21
|
}
|
|
@@ -28,7 +30,9 @@ export function extractStreamDelta(chunk) {
|
|
|
28
30
|
export function normalizeUsage(usage) {
|
|
29
31
|
const prompt = usage?.input_tokens || 0;
|
|
30
32
|
const completion = usage?.output_tokens || 0;
|
|
31
|
-
|
|
33
|
+
const cacheWrite = usage?.cache_creation_input_tokens || 0;
|
|
34
|
+
const cacheRead = usage?.cache_read_input_tokens || 0;
|
|
35
|
+
return { prompt, completion, total: prompt + completion, cacheWrite, cacheRead };
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
export function proxy(client, intercept) {
|
package/src/providers/openai.js
CHANGED
|
@@ -16,12 +16,17 @@ function isResponsesAPI(result) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function extractChatCompletions(result) {
|
|
19
|
+
const prompt = result?.usage?.prompt_tokens || 0;
|
|
20
|
+
const completion = result?.usage?.completion_tokens || 0;
|
|
21
|
+
const cachedInput = result?.usage?.prompt_tokens_details?.cached_tokens || 0;
|
|
22
|
+
|
|
19
23
|
return {
|
|
20
24
|
response: result?.choices?.[0]?.message?.content || '',
|
|
21
25
|
tokens: {
|
|
22
|
-
prompt
|
|
23
|
-
completion
|
|
24
|
-
total:
|
|
26
|
+
prompt,
|
|
27
|
+
completion,
|
|
28
|
+
total: result?.usage?.total_tokens || 0,
|
|
29
|
+
cachedInput,
|
|
25
30
|
},
|
|
26
31
|
toolCalls: result?.choices?.[0]?.message?.tool_calls?.map(tc => ({
|
|
27
32
|
id: tc.id,
|
|
@@ -40,12 +45,13 @@ function extractResponses(result) {
|
|
|
40
45
|
|
|
41
46
|
const input = result?.usage?.input_tokens || 0;
|
|
42
47
|
const output = result?.usage?.output_tokens || 0;
|
|
48
|
+
const cachedInput = result?.usage?.input_tokens_details?.cached_tokens || 0;
|
|
43
49
|
|
|
44
50
|
const fnCalls = (result?.output || []).filter(item => item.type === 'function_call');
|
|
45
51
|
|
|
46
52
|
return {
|
|
47
53
|
response: text,
|
|
48
|
-
tokens: { prompt: input, completion: output, total: input + output },
|
|
54
|
+
tokens: { prompt: input, completion: output, total: input + output, cachedInput },
|
|
49
55
|
toolCalls: fnCalls.length > 0
|
|
50
56
|
? fnCalls.map(fc => ({ id: fc.id, name: fc.name, arguments: fc.arguments }))
|
|
51
57
|
: null,
|
|
@@ -79,7 +85,9 @@ export function extractStreamDelta(chunk) {
|
|
|
79
85
|
export function normalizeUsage(usage) {
|
|
80
86
|
const prompt = usage?.prompt_tokens || usage?.input_tokens || 0;
|
|
81
87
|
const completion = usage?.completion_tokens || usage?.output_tokens || 0;
|
|
82
|
-
|
|
88
|
+
const cachedInput = usage?.prompt_tokens_details?.cached_tokens
|
|
89
|
+
|| usage?.input_tokens_details?.cached_tokens || 0;
|
|
90
|
+
return { prompt, completion, total: prompt + completion, cachedInput };
|
|
83
91
|
}
|
|
84
92
|
|
|
85
93
|
// ---------------------------------------------------------------------------
|
package/src/trace/act.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Warpmetrics SDK — act()
|
|
2
|
+
|
|
3
|
+
import { ref as getRef } from './ref.js';
|
|
4
|
+
import { logAct, getConfig } from '../core/transport.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Record an action taken on a tracked target (e.g. acting on feedback).
|
|
8
|
+
*
|
|
9
|
+
* @param {object | string} target — Run, Group, LLM response, or ref string
|
|
10
|
+
* @param {string} name — action name ("improve-section", "refine-prompt")
|
|
11
|
+
* @param {Record<string, any>} [metadata] — arbitrary extra data
|
|
12
|
+
*/
|
|
13
|
+
export function act(target, name, metadata) {
|
|
14
|
+
const targetId = getRef(target);
|
|
15
|
+
|
|
16
|
+
if (!targetId) {
|
|
17
|
+
if (getConfig().debug) console.warn('[warpmetrics] act() — target not tracked.');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
logAct({
|
|
22
|
+
targetId,
|
|
23
|
+
name,
|
|
24
|
+
metadata: metadata || null,
|
|
25
|
+
});
|
|
26
|
+
}
|
package/src/trace/cost.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
// Warpmetrics SDK — cost()
|
|
2
|
-
|
|
3
|
-
import { ref as getRef } from './ref.js';
|
|
4
|
-
import { runRegistry, groupRegistry, costRegistry, costByCallId } from '../core/registry.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Get the cost in USD for any tracked target.
|
|
8
|
-
*
|
|
9
|
-
* - Response object → cost of that single call
|
|
10
|
-
* - Run / Group → sum of all nested calls
|
|
11
|
-
* - Ref string → lookup in registries
|
|
12
|
-
*
|
|
13
|
-
* @param {object | string} target
|
|
14
|
-
* @returns {number} cost in USD
|
|
15
|
-
*/
|
|
16
|
-
export function cost(target) {
|
|
17
|
-
// Response object — direct lookup
|
|
18
|
-
if (typeof target === 'object' && costRegistry.has(target)) {
|
|
19
|
-
return costRegistry.get(target);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const id = getRef(target);
|
|
23
|
-
if (!id) return 0;
|
|
24
|
-
|
|
25
|
-
// Single call by ref string
|
|
26
|
-
if (id.startsWith('wm_call_')) {
|
|
27
|
-
return costByCallId.get(id) || 0;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Run or Group — aggregate
|
|
31
|
-
const container = runRegistry.get(id) || groupRegistry.get(id);
|
|
32
|
-
if (container) return sumCosts(container);
|
|
33
|
-
|
|
34
|
-
return 0;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function sumCosts(container) {
|
|
38
|
-
let total = 0;
|
|
39
|
-
|
|
40
|
-
for (const callId of container.calls) {
|
|
41
|
-
total += costByCallId.get(callId) || 0;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
for (const groupId of container.groups) {
|
|
45
|
-
const g = groupRegistry.get(groupId);
|
|
46
|
-
if (g) total += sumCosts(g);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return total;
|
|
50
|
-
}
|