retrace-sdk 0.5.2 → 0.6.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 +6 -0
- package/dist/interceptors/openai.js +8 -0
- package/dist/recorder.js +12 -10
- package/dist/resume.d.ts +1 -1
- package/dist/resume.js +30 -35
- package/dist/trace.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -111,6 +111,12 @@ configure({ apiKey: "rt_live_...", sampleRate: 0.1 }); // Record 10% of traces
|
|
|
111
111
|
|
|
112
112
|
- **Fixed** — OpenAI interceptor no longer creates dummy client instance to find prototype
|
|
113
113
|
|
|
114
|
+
### 0.6.0
|
|
115
|
+
|
|
116
|
+
- **Token ID capture** — Stores output token IDs + logprobs from OpenAI responses (enables speculative decoding during replay)
|
|
117
|
+
- **SpanData extended** — New `token_ids` and `logprobs` fields on SpanData interface
|
|
118
|
+
- **Shared schema** — SpanInputSchema updated with `token_ids` and `logprobs` optional arrays
|
|
119
|
+
|
|
114
120
|
### 0.2.1
|
|
115
121
|
|
|
116
122
|
- **Offline buffer** — stores up to 1000 messages when WebSocket disconnects, flushes on reconnect
|
|
@@ -188,6 +188,12 @@ function createPatchedCreate() {
|
|
|
188
188
|
const inputTokens = res?.usage?.prompt_tokens || 0;
|
|
189
189
|
const outputTokens = res?.usage?.completion_tokens || 0;
|
|
190
190
|
const output = res?.choices?.[0]?.message?.content || "";
|
|
191
|
+
// Extract token IDs and logprobs if available (requires logprobs: true in request)
|
|
192
|
+
const choiceLogprobs = res?.choices?.[0]?.logprobs?.content;
|
|
193
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
194
|
+
const tokenIds = choiceLogprobs?.map((t) => t.token_id ?? t.top_logprobs?.[0]?.token_id).filter(Boolean);
|
|
195
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
196
|
+
const logprobValues = choiceLogprobs?.map((t) => t.logprob).filter((v) => v !== undefined);
|
|
191
197
|
const span = {
|
|
192
198
|
id: spanId, trace_id: "", parent_id: null,
|
|
193
199
|
span_type: SpanType.LLM_CALL, name: "openai.chat.completions.create", model,
|
|
@@ -196,6 +202,8 @@ function createPatchedCreate() {
|
|
|
196
202
|
input_tokens: inputTokens, output_tokens: outputTokens,
|
|
197
203
|
cost: calcCost(model, inputTokens, outputTokens),
|
|
198
204
|
duration_ms: durationMs, started_at: startedAt, ended_at: nowIso(),
|
|
205
|
+
...(tokenIds?.length ? { token_ids: tokenIds } : {}),
|
|
206
|
+
...(logprobValues?.length ? { logprobs: logprobValues } : {}),
|
|
199
207
|
...(Object.keys(spanMetadata).length ? { metadata: spanMetadata } : {}),
|
|
200
208
|
};
|
|
201
209
|
onSpanCallback?.(span);
|
package/dist/recorder.js
CHANGED
|
@@ -129,16 +129,18 @@ export class TraceRecorder {
|
|
|
129
129
|
export function record(opts) {
|
|
130
130
|
const cfg = getConfig();
|
|
131
131
|
if (!cfg.enabled || !shouldSample(cfg.sampleRate, cfg.sampleSeed, opts?.name)) {
|
|
132
|
-
// Return a typed no-op
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
132
|
+
// Return a properly-typed no-op recorder that satisfies the TraceRecorder interface
|
|
133
|
+
const noop = Object.create(TraceRecorder.prototype);
|
|
134
|
+
Object.defineProperties(noop, {
|
|
135
|
+
traceId: { get: () => "" },
|
|
136
|
+
output: { value: undefined, writable: true },
|
|
137
|
+
});
|
|
138
|
+
noop.start = () => noop;
|
|
139
|
+
noop.end = () => { };
|
|
140
|
+
noop.addSpan = () => { };
|
|
141
|
+
noop.startSpan = (name) => new SpanBuilder(name, "llm_call");
|
|
142
|
+
noop.endSpan = () => { };
|
|
143
|
+
return noop;
|
|
142
144
|
}
|
|
143
145
|
return new TraceRecorder(opts);
|
|
144
146
|
}
|
package/dist/resume.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface ResumeCommand {
|
|
|
17
17
|
}
|
|
18
18
|
export declare function registerResumable(name: string, fn: (...args: unknown[]) => unknown): void;
|
|
19
19
|
export declare function getResumable(name: string): ((...args: unknown[]) => unknown) | undefined;
|
|
20
|
-
export declare function handleResume(command: ResumeCommand): boolean
|
|
20
|
+
export declare function handleResume(command: ResumeCommand): Promise<boolean>;
|
|
21
21
|
export declare function parseResumeMessage(msg: {
|
|
22
22
|
type: string;
|
|
23
23
|
data?: Record<string, unknown>;
|
package/dist/resume.js
CHANGED
|
@@ -15,46 +15,41 @@ export function registerResumable(name, fn) {
|
|
|
15
15
|
export function getResumable(name) {
|
|
16
16
|
return resumableFunctions.get(name);
|
|
17
17
|
}
|
|
18
|
-
export function handleResume(command) {
|
|
18
|
+
export async function handleResume(command) {
|
|
19
19
|
const fn = getResumable(command.traceName);
|
|
20
20
|
if (!fn)
|
|
21
21
|
return false;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
recorder.start(`Fork: ${command.traceName}`, command.modifiedInput);
|
|
42
|
-
// Determine args for re-execution
|
|
43
|
-
let args = command.originalArgs || [];
|
|
44
|
-
if (typeof command.modifiedInput === "string") {
|
|
45
|
-
args = [command.modifiedInput, ...args.slice(1)];
|
|
46
|
-
}
|
|
47
|
-
else if (typeof command.modifiedInput === "object" && !Array.isArray(command.modifiedInput)) {
|
|
48
|
-
args = [command.modifiedInput];
|
|
49
|
-
}
|
|
50
|
-
const result = await Promise.resolve(fn(...args));
|
|
51
|
-
recorder.end(result, TraceStatus.COMPLETED);
|
|
22
|
+
try {
|
|
23
|
+
const { TraceRecorder } = await import("./recorder.js");
|
|
24
|
+
const { TraceStatus } = await import("./trace.js");
|
|
25
|
+
const recorder = new TraceRecorder({
|
|
26
|
+
name: `Fork: ${command.traceName}`,
|
|
27
|
+
input: command.modifiedInput,
|
|
28
|
+
metadata: {
|
|
29
|
+
_fork_id: command.forkId,
|
|
30
|
+
_fork_of: command.traceId,
|
|
31
|
+
_fork_point: command.forkPointSpanId,
|
|
32
|
+
_cascade_replay: true,
|
|
33
|
+
},
|
|
34
|
+
forkPointSpanId: command.forkPointSpanId,
|
|
35
|
+
});
|
|
36
|
+
recorder.start(`Fork: ${command.traceName}`, command.modifiedInput);
|
|
37
|
+
// Determine args for re-execution
|
|
38
|
+
let args = command.originalArgs || [];
|
|
39
|
+
if (typeof command.modifiedInput === "string") {
|
|
40
|
+
args = [command.modifiedInput, ...args.slice(1)];
|
|
52
41
|
}
|
|
53
|
-
|
|
54
|
-
|
|
42
|
+
else if (typeof command.modifiedInput === "object" && !Array.isArray(command.modifiedInput)) {
|
|
43
|
+
args = [command.modifiedInput];
|
|
55
44
|
}
|
|
56
|
-
|
|
57
|
-
|
|
45
|
+
const result = await Promise.resolve(fn(...args));
|
|
46
|
+
recorder.end(result, TraceStatus.COMPLETED);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.error("[retrace] Cascade replay failed:", err);
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
58
53
|
}
|
|
59
54
|
export function parseResumeMessage(msg) {
|
|
60
55
|
if (msg.type !== "resume" || !msg.data)
|
package/dist/trace.d.ts
CHANGED