@warpmetrics/warp 0.0.19 → 0.0.21
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/package.json +1 -1
- package/src/core/reserve.js +43 -0
- package/src/index.d.ts +29 -0
- package/src/index.js +3 -0
- package/src/providers/anthropic.js +2 -2
- package/src/trace/act.js +44 -5
package/package.json
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Warpmetrics SDK — reserve()
|
|
2
|
+
|
|
3
|
+
import { generateId } from './utils.js';
|
|
4
|
+
import { actRegistry, runRegistry, groupRegistry, outcomeRegistry } from './registry.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Reserve an event ID without queueing.
|
|
8
|
+
*
|
|
9
|
+
* Takes a descriptor (returned by calling an event function without its
|
|
10
|
+
* required target) and assigns a pre-generated ID. The ID is registered
|
|
11
|
+
* as a stub so that ref() can resolve it immediately.
|
|
12
|
+
*
|
|
13
|
+
* @param {{ _descriptor: true, _eventType: string, name: string, opts: Record<string, any> | null }} descriptor
|
|
14
|
+
* @returns {Readonly<{ id: string, _type: string, name: string, opts: Record<string, any> | null }>}
|
|
15
|
+
*/
|
|
16
|
+
export function reserve(descriptor) {
|
|
17
|
+
if (!descriptor || !descriptor._descriptor) {
|
|
18
|
+
throw new Error('reserve() requires a descriptor from act(), run(), etc.');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const prefixMap = { act: 'act', run: 'run', group: 'grp', outcome: 'oc' };
|
|
22
|
+
const prefix = prefixMap[descriptor._eventType];
|
|
23
|
+
if (!prefix) {
|
|
24
|
+
throw new Error(`reserve() — unknown event type: ${descriptor._eventType}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const id = generateId(prefix);
|
|
28
|
+
|
|
29
|
+
// Register as stub so ref() can resolve it
|
|
30
|
+
const registryMap = { act: actRegistry, run: runRegistry, group: groupRegistry, outcome: outcomeRegistry };
|
|
31
|
+
const registry = registryMap[descriptor._eventType];
|
|
32
|
+
if (!registry) {
|
|
33
|
+
throw new Error(`reserve() — no registry for event type: ${descriptor._eventType}`);
|
|
34
|
+
}
|
|
35
|
+
registry.set(id, { ...(registry.get(id) || {}), id, stub: true });
|
|
36
|
+
|
|
37
|
+
return Object.freeze({
|
|
38
|
+
id,
|
|
39
|
+
_type: descriptor._eventType,
|
|
40
|
+
name: descriptor.name,
|
|
41
|
+
opts: descriptor.opts,
|
|
42
|
+
});
|
|
43
|
+
}
|
package/src/index.d.ts
CHANGED
|
@@ -29,6 +29,22 @@ export interface Act {
|
|
|
29
29
|
readonly _type: 'act';
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/** Descriptor returned by event functions called without a target. */
|
|
33
|
+
export interface Descriptor<T extends string> {
|
|
34
|
+
readonly _descriptor: true;
|
|
35
|
+
readonly _eventType: T;
|
|
36
|
+
readonly name: string;
|
|
37
|
+
readonly opts: Record<string, any> | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Reserved event handle with pre-generated ID. */
|
|
41
|
+
export interface ReservedAct {
|
|
42
|
+
readonly id: string;
|
|
43
|
+
readonly _type: 'act';
|
|
44
|
+
readonly name: string;
|
|
45
|
+
readonly opts: Record<string, any> | null;
|
|
46
|
+
}
|
|
47
|
+
|
|
32
48
|
|
|
33
49
|
/**
|
|
34
50
|
* Wrap an LLM client to automatically track every API call.
|
|
@@ -100,6 +116,19 @@ export function act(
|
|
|
100
116
|
opts?: Record<string, any>,
|
|
101
117
|
): Act | undefined;
|
|
102
118
|
|
|
119
|
+
/** Create an act descriptor (no outcome yet). Pass to reserve() to get an ID. */
|
|
120
|
+
export function act(name: string, opts?: Record<string, any>): Descriptor<'act'>;
|
|
121
|
+
|
|
122
|
+
/** Complete a reserved act by providing the outcome. */
|
|
123
|
+
export function act(
|
|
124
|
+
target: Outcome | string,
|
|
125
|
+
reserved: ReservedAct,
|
|
126
|
+
opts?: Record<string, any>,
|
|
127
|
+
): Act | undefined;
|
|
128
|
+
|
|
129
|
+
/** Reserve an event ID without queueing. Returns a handle with pre-generated ID. */
|
|
130
|
+
export function reserve(descriptor: Descriptor<'act'>): ReservedAct;
|
|
131
|
+
|
|
103
132
|
/** Resolve any trackable target to its string ID. */
|
|
104
133
|
export function ref(target: Run | Group | Act | Outcome | object | string): string | undefined;
|
|
105
134
|
|
package/src/index.js
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
// trace(target, data) — manually trace a call (non-SDK tools)
|
|
10
10
|
// outcome(target, name, opts?) — record a result
|
|
11
11
|
// act(target, name, opts?) — record an action, returns act ref
|
|
12
|
+
// act(name, opts?) — create an act descriptor (no queue)
|
|
13
|
+
// reserve(descriptor) — reserve an event ID without queueing
|
|
12
14
|
// ref(target) — get tracking ID
|
|
13
15
|
export { warp } from './core/warp.js';
|
|
14
16
|
export { run } from './trace/run.js';
|
|
@@ -17,5 +19,6 @@ export { call } from './trace/call.js';
|
|
|
17
19
|
export { trace } from './trace/trace.js';
|
|
18
20
|
export { outcome } from './trace/outcome.js';
|
|
19
21
|
export { act } from './trace/act.js';
|
|
22
|
+
export { reserve } from './core/reserve.js';
|
|
20
23
|
export { ref } from './trace/ref.js';
|
|
21
24
|
export { flush } from './core/transport.js';
|
|
@@ -15,7 +15,7 @@ export function extract(result) {
|
|
|
15
15
|
response: Array.isArray(result?.content)
|
|
16
16
|
? result.content.filter(c => c.type === 'text').map(c => c.text).join('')
|
|
17
17
|
: '',
|
|
18
|
-
tokens: { prompt: input, completion: output, total: input + output, cacheWrite, cacheRead },
|
|
18
|
+
tokens: { prompt: input + cacheWrite + cacheRead, completion: output, total: input + output + cacheWrite + cacheRead, cacheWrite, cacheRead },
|
|
19
19
|
toolCalls: null,
|
|
20
20
|
};
|
|
21
21
|
}
|
|
@@ -38,7 +38,7 @@ export function normalizeUsage(usage) {
|
|
|
38
38
|
const completion = usage?.output_tokens || 0;
|
|
39
39
|
const cacheWrite = usage?.cache_creation_input_tokens || 0;
|
|
40
40
|
const cacheRead = usage?.cache_read_input_tokens || 0;
|
|
41
|
-
return { prompt, completion, total: prompt + completion, cacheWrite, cacheRead };
|
|
41
|
+
return { prompt: prompt + cacheWrite + cacheRead, completion, total: prompt + completion + cacheWrite + cacheRead, cacheWrite, cacheRead };
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export function proxy(client, intercept) {
|
package/src/trace/act.js
CHANGED
|
@@ -8,12 +8,51 @@ import { logAct, getConfig } from '../core/transport.js';
|
|
|
8
8
|
/**
|
|
9
9
|
* Record an action taken on an outcome (e.g. acting on feedback).
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* Three modes:
|
|
12
|
+
*
|
|
13
|
+
* 1. Normal: act(outcome, name, opts?) — queue an act event
|
|
14
|
+
* 2. Descriptor: act(name, opts?) — return a descriptor (no queue)
|
|
15
|
+
* 3. Reserved: act(outcome, reserved, opts?) — complete a reserved act
|
|
16
|
+
*
|
|
17
|
+
* @param {{ id: string, _type: 'outcome' } | string} target
|
|
18
|
+
* @param {string | { id: string, _type: 'act', name: string, opts: any }} nameOrReserved
|
|
13
19
|
* @param {Record<string, any>} [opts]
|
|
14
|
-
* @returns {{ readonly id: string, readonly _type: 'act' } | undefined}
|
|
20
|
+
* @returns {{ readonly id: string, readonly _type: 'act' } | { _descriptor: true, _eventType: 'act', name: string, opts: any } | undefined}
|
|
15
21
|
*/
|
|
16
|
-
export function act(target,
|
|
22
|
+
export function act(target, nameOrReserved, opts) {
|
|
23
|
+
// Path A — Descriptor mode: act(name) or act(name, opts)
|
|
24
|
+
// First arg is a string that doesn't look like a wm_ ref
|
|
25
|
+
if (typeof target === 'string' && !target.startsWith('wm_')) {
|
|
26
|
+
return { _descriptor: true, _eventType: 'act', name: target, opts: nameOrReserved || null };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Path B — Complete a reserved act: act(outcome, reserved, extraOpts?)
|
|
30
|
+
if (nameOrReserved && typeof nameOrReserved === 'object' && nameOrReserved._type === 'act' && nameOrReserved.id) {
|
|
31
|
+
const refId = getRef(target);
|
|
32
|
+
|
|
33
|
+
if (!refId) {
|
|
34
|
+
if (getConfig().debug) console.warn('[warpmetrics] act() — target not tracked.');
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!refId.startsWith('wm_oc_')) {
|
|
39
|
+
if (getConfig().debug) console.warn('[warpmetrics] act() — target must be an outcome (wm_oc_*).');
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const id = nameOrReserved.id;
|
|
44
|
+
const mergedOpts = opts
|
|
45
|
+
? { ...(nameOrReserved.opts || {}), ...opts }
|
|
46
|
+
: (nameOrReserved.opts || null);
|
|
47
|
+
|
|
48
|
+
const existing = actRegistry.get(id) || {};
|
|
49
|
+
actRegistry.set(id, { ...existing, id, refId });
|
|
50
|
+
logAct({ id, refId, name: nameOrReserved.name, opts: mergedOpts });
|
|
51
|
+
|
|
52
|
+
return Object.freeze({ id, _type: 'act' });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Path C — Normal: act(outcome, name, opts?)
|
|
17
56
|
const refId = getRef(target);
|
|
18
57
|
|
|
19
58
|
if (!refId) {
|
|
@@ -29,7 +68,7 @@ export function act(target, name, opts) {
|
|
|
29
68
|
const id = generateId('act');
|
|
30
69
|
actRegistry.set(id, { id, refId });
|
|
31
70
|
|
|
32
|
-
logAct({ id, refId, name, opts: opts || null });
|
|
71
|
+
logAct({ id, refId, name: nameOrReserved, opts: opts || null });
|
|
33
72
|
|
|
34
73
|
return Object.freeze({ id, _type: 'act' });
|
|
35
74
|
}
|