agentv 3.11.0 → 3.12.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.
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
2
2
 
3
- // ../../packages/core/dist/chunk-HMXZ2AX4.js
3
+ // ../../packages/core/dist/chunk-3G2KXH7N.js
4
4
  import { createWriteStream } from "node:fs";
5
5
  import { mkdir } from "node:fs/promises";
6
6
  import { dirname } from "node:path";
@@ -10,6 +10,7 @@ var SimpleTraceFileExporter = class {
10
10
  streamReady = null;
11
11
  pendingWrites = [];
12
12
  _shuttingDown = false;
13
+ spansByTraceId = /* @__PURE__ */ new Map();
13
14
  constructor(filePath) {
14
15
  this.filePath = filePath;
15
16
  }
@@ -28,25 +29,27 @@ var SimpleTraceFileExporter = class {
28
29
  resultCallback({ code: 0 });
29
30
  return;
30
31
  }
31
- const spanMap = /* @__PURE__ */ new Map();
32
- const childMap = /* @__PURE__ */ new Map();
32
+ const rootSpans = [];
33
33
  for (const span of spans) {
34
- spanMap.set(span.spanContext().spanId, span);
35
- const parentId = span.parentSpanId;
36
- if (parentId) {
37
- if (!childMap.has(parentId)) childMap.set(parentId, []);
38
- childMap.get(parentId)?.push(span);
34
+ const traceId = span.spanContext().traceId;
35
+ const existing = this.spansByTraceId.get(traceId) ?? [];
36
+ existing.push(span);
37
+ this.spansByTraceId.set(traceId, existing);
38
+ if (span.name === "agentv.eval") {
39
+ rootSpans.push(span);
39
40
  }
40
41
  }
41
- const rootSpans = spans.filter(
42
- (s) => !s.parentSpanId || !spanMap.has(s.parentSpanId)
43
- );
44
42
  const writePromise = this.ensureStream().then((stream) => {
45
43
  for (const root of rootSpans) {
46
- const children = this.collectChildren(root.spanContext().spanId, childMap);
44
+ const traceId = root.spanContext().traceId;
45
+ const traceSpans = this.spansByTraceId.get(traceId) ?? [root];
46
+ const children = traceSpans.filter(
47
+ (span) => span.spanContext().spanId !== root.spanContext().spanId
48
+ );
47
49
  const record = this.buildSimpleRecord(root, children);
48
50
  stream.write(`${JSON.stringify(record)}
49
51
  `);
52
+ this.spansByTraceId.delete(traceId);
50
53
  }
51
54
  });
52
55
  this.pendingWrites.push(writePromise);
@@ -56,6 +59,7 @@ var SimpleTraceFileExporter = class {
56
59
  this._shuttingDown = true;
57
60
  await Promise.all(this.pendingWrites);
58
61
  this.pendingWrites = [];
62
+ this.spansByTraceId.clear();
59
63
  return new Promise((resolve) => {
60
64
  if (this.stream) {
61
65
  this.stream.end(() => resolve());
@@ -68,17 +72,9 @@ var SimpleTraceFileExporter = class {
68
72
  await Promise.all(this.pendingWrites);
69
73
  this.pendingWrites = [];
70
74
  }
71
- collectChildren(spanId, childMap) {
72
- const direct = childMap.get(spanId) || [];
73
- const all = [...direct];
74
- for (const child of direct) {
75
- all.push(...this.collectChildren(child.spanContext().spanId, childMap));
76
- }
77
- return all;
78
- }
79
75
  buildSimpleRecord(root, children) {
80
76
  const attrs = root.attributes || {};
81
- const durationMs = hrTimeDiffMs(root.startTime, root.endTime);
77
+ const durationMs = typeof attrs["agentv.trace.duration_ms"] === "number" ? attrs["agentv.trace.duration_ms"] : hrTimeDiffMs(root.startTime, root.endTime);
82
78
  let inputTokens = 0;
83
79
  let outputTokens = 0;
84
80
  for (const child of children) {
@@ -86,6 +82,14 @@ var SimpleTraceFileExporter = class {
86
82
  if (ca["gen_ai.usage.input_tokens"]) inputTokens += ca["gen_ai.usage.input_tokens"];
87
83
  if (ca["gen_ai.usage.output_tokens"]) outputTokens += ca["gen_ai.usage.output_tokens"];
88
84
  }
85
+ const rootInputTokens = typeof attrs["agentv.trace.token_input"] === "number" ? attrs["agentv.trace.token_input"] : 0;
86
+ const rootOutputTokens = typeof attrs["agentv.trace.token_output"] === "number" ? attrs["agentv.trace.token_output"] : 0;
87
+ const rootCachedTokens = typeof attrs["agentv.trace.token_cached"] === "number" ? attrs["agentv.trace.token_cached"] : void 0;
88
+ const llmSpans = children.filter((s) => s.attributes?.["gen_ai.operation.name"] === "chat").map((s) => ({
89
+ type: "llm",
90
+ name: s.name,
91
+ duration_ms: hrTimeDiffMs(s.startTime, s.endTime)
92
+ }));
89
93
  const toolSpans = children.filter((s) => s.attributes?.["gen_ai.tool.name"]).map((s) => ({
90
94
  type: "tool",
91
95
  name: s.attributes["gen_ai.tool.name"],
@@ -97,8 +101,12 @@ var SimpleTraceFileExporter = class {
97
101
  score: attrs["agentv.score"],
98
102
  duration_ms: durationMs,
99
103
  cost_usd: attrs["agentv.trace.cost_usd"],
100
- token_usage: inputTokens || outputTokens ? { input: inputTokens, output: outputTokens } : void 0,
101
- spans: toolSpans.length > 0 ? toolSpans : void 0
104
+ token_usage: inputTokens || outputTokens || rootInputTokens || rootOutputTokens || rootCachedTokens ? {
105
+ input: inputTokens || rootInputTokens,
106
+ output: outputTokens || rootOutputTokens,
107
+ ...rootCachedTokens ? { cached: rootCachedTokens } : {}
108
+ } : void 0,
109
+ spans: [...llmSpans, ...toolSpans].length > 0 ? [...llmSpans, ...toolSpans] : void 0
102
110
  };
103
111
  }
104
112
  };
@@ -111,4 +119,4 @@ function hrTimeDiffMs(start, end) {
111
119
  export {
112
120
  SimpleTraceFileExporter
113
121
  };
114
- //# sourceMappingURL=chunk-JK6V4KVD.js.map
122
+ //# sourceMappingURL=chunk-NR7QVL75.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../packages/core/src/observability/simple-trace-file-exporter.ts"],"sourcesContent":["import { type WriteStream, createWriteStream } from 'node:fs';\nimport { mkdir } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\n// biome-ignore lint/suspicious/noExplicitAny: OTel ReadableSpan loaded dynamically\ntype ReadableSpan = any;\n\n/**\n * SpanExporter that writes human-readable JSONL (one line per root span).\n * Designed for quick debugging and analysis without OTel tooling.\n */\nexport class SimpleTraceFileExporter {\n private stream: WriteStream | null = null;\n private filePath: string;\n private streamReady: Promise<WriteStream> | null = null;\n private pendingWrites: Promise<void>[] = [];\n private _shuttingDown = false;\n private spansByTraceId = new Map<string, ReadableSpan[]>();\n\n constructor(filePath: string) {\n this.filePath = filePath;\n }\n\n private async ensureStream(): Promise<WriteStream> {\n if (!this.streamReady) {\n this.streamReady = (async () => {\n await mkdir(dirname(this.filePath), { recursive: true });\n this.stream = createWriteStream(this.filePath, { flags: 'w' });\n return this.stream;\n })();\n }\n return this.streamReady;\n }\n\n export(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {\n if (this._shuttingDown) {\n resultCallback({ code: 0 });\n return;\n }\n const rootSpans: ReadableSpan[] = [];\n for (const span of spans) {\n const traceId = span.spanContext().traceId;\n const existing = this.spansByTraceId.get(traceId) ?? [];\n existing.push(span);\n this.spansByTraceId.set(traceId, existing);\n if (span.name === 'agentv.eval') {\n rootSpans.push(span);\n }\n }\n\n const writePromise = this.ensureStream().then((stream) => {\n for (const root of rootSpans) {\n const traceId = root.spanContext().traceId;\n const traceSpans = this.spansByTraceId.get(traceId) ?? [root];\n const children = traceSpans.filter(\n (span) => span.spanContext().spanId !== root.spanContext().spanId,\n );\n const record = this.buildSimpleRecord(root, children);\n stream.write(`${JSON.stringify(record)}\\n`);\n this.spansByTraceId.delete(traceId);\n }\n });\n this.pendingWrites.push(writePromise);\n\n resultCallback({ code: 0 });\n }\n\n async shutdown(): Promise<void> {\n this._shuttingDown = true;\n await Promise.all(this.pendingWrites);\n this.pendingWrites = [];\n this.spansByTraceId.clear();\n return new Promise((resolve) => {\n if (this.stream) {\n this.stream.end(() => resolve());\n } else {\n resolve();\n }\n });\n }\n\n async forceFlush(): Promise<void> {\n await Promise.all(this.pendingWrites);\n this.pendingWrites = [];\n }\n\n private buildSimpleRecord(root: ReadableSpan, children: ReadableSpan[]): Record<string, unknown> {\n const attrs = root.attributes || {};\n const durationMs =\n typeof attrs['agentv.trace.duration_ms'] === 'number'\n ? attrs['agentv.trace.duration_ms']\n : hrTimeDiffMs(root.startTime, root.endTime);\n\n let inputTokens = 0;\n let outputTokens = 0;\n for (const child of children) {\n const ca = child.attributes || {};\n if (ca['gen_ai.usage.input_tokens']) inputTokens += ca['gen_ai.usage.input_tokens'];\n if (ca['gen_ai.usage.output_tokens']) outputTokens += ca['gen_ai.usage.output_tokens'];\n }\n const rootInputTokens =\n typeof attrs['agentv.trace.token_input'] === 'number' ? attrs['agentv.trace.token_input'] : 0;\n const rootOutputTokens =\n typeof attrs['agentv.trace.token_output'] === 'number'\n ? attrs['agentv.trace.token_output']\n : 0;\n const rootCachedTokens =\n typeof attrs['agentv.trace.token_cached'] === 'number'\n ? attrs['agentv.trace.token_cached']\n : undefined;\n\n const llmSpans = children\n .filter((s: ReadableSpan) => s.attributes?.['gen_ai.operation.name'] === 'chat')\n .map((s: ReadableSpan) => ({\n type: 'llm' as const,\n name: s.name,\n duration_ms: hrTimeDiffMs(s.startTime, s.endTime),\n }));\n\n const toolSpans = children\n .filter((s: ReadableSpan) => s.attributes?.['gen_ai.tool.name'])\n .map((s: ReadableSpan) => ({\n type: 'tool' as const,\n name: s.attributes['gen_ai.tool.name'],\n duration_ms: hrTimeDiffMs(s.startTime, s.endTime),\n }));\n\n return {\n test_id: attrs['agentv.test_id'],\n target: attrs['agentv.target'],\n score: attrs['agentv.score'],\n duration_ms: durationMs,\n cost_usd: attrs['agentv.trace.cost_usd'],\n token_usage:\n inputTokens || outputTokens || rootInputTokens || rootOutputTokens || rootCachedTokens\n ? {\n input: inputTokens || rootInputTokens,\n output: outputTokens || rootOutputTokens,\n ...(rootCachedTokens ? { cached: rootCachedTokens } : {}),\n }\n : undefined,\n spans: [...llmSpans, ...toolSpans].length > 0 ? [...llmSpans, ...toolSpans] : undefined,\n };\n }\n}\n\nfunction hrTimeDiffMs(start: [number, number], end: [number, number]): number {\n const diffSec = end[0] - start[0];\n const diffNano = end[1] - start[1];\n return Math.round(diffSec * 1000 + diffNano / 1_000_000);\n}\n"],"mappings":"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","names":[]}