Skip to content

Commit 174a50e

Browse files
Preserve nested trace metadata values
1 parent 74212fc commit 174a50e

3 files changed

Lines changed: 47 additions & 2 deletions

File tree

packages/core/src/otel.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,8 @@ function langfuseAttributes(attributes: Record<string, unknown>): {
349349
const traceMetadata: Record<string, unknown> = {};
350350
for (const [key, value] of Object.entries(attributes)) {
351351
if (key.startsWith("langfuse.trace.metadata.")) {
352-
traceMetadata[key.slice("langfuse.trace.metadata.".length)] = value;
352+
traceMetadata[key.slice("langfuse.trace.metadata.".length)] =
353+
parseJsonMetadataValue(value);
353354
}
354355
}
355356

@@ -367,3 +368,7 @@ function langfuseAttributes(attributes: Record<string, unknown>): {
367368
tracePublic: booleanValue(attributes["langfuse.trace.public"]),
368369
};
369370
}
371+
372+
function parseJsonMetadataValue(value: unknown): unknown {
373+
return typeof value === "string" ? parseJsonString(value) : value;
374+
}

packages/perf/src/load.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@ async function generateChildObservation(
129129
return;
130130
}
131131

132-
const updateSpanLikeObservation = async (span: LangfuseSpan | LangfuseTool) => {
132+
const updateSpanLikeObservation = async (
133+
span: LangfuseSpan | LangfuseTool,
134+
) => {
133135
span.update({
134136
input: {
135137
step: childIndex,

tests/cli.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,44 @@ test("CLI default trace ids are OTEL trace ids", () => {
142142
assert.match(createOtelTraceId(), /^[0-9a-f]{32}$/);
143143
});
144144

145+
test("CLI trace creation preserves nested metadata values", async () => {
146+
const dbPath = join(
147+
mkdtempSync(join(tmpdir(), "agentpond-cli-")),
148+
"cache.duckdb",
149+
);
150+
const resourceSpans = manualTraceResourceSpans(
151+
{
152+
flags: {
153+
metadata: '{"details":{"tier":"pro"},"tags":["a","b"],"plain":"ok"}',
154+
},
155+
positionals: [],
156+
},
157+
"0123456789abcdef0123456789abcdef",
158+
"2026-06-14T11:03:19.419Z",
159+
);
160+
const store = new MemoryObjectStore();
161+
await store.putJson(
162+
"otel/default-project/2026/06/14/11/03/batch-1.json",
163+
resourceSpans,
164+
);
165+
const db = new AgentPondDuckDb(dbPath);
166+
await db.syncFromStore({
167+
store,
168+
projectId: "default-project",
169+
prefix: "",
170+
});
171+
const traces = await db.query<{ metadata_json: string }>(
172+
"SELECT metadata_json FROM traces WHERE id = '0123456789abcdef0123456789abcdef'",
173+
);
174+
await db.close();
175+
176+
assert.deepEqual(JSON.parse(traces[0].metadata_json), {
177+
details: { tier: "pro" },
178+
tags: ["a", "b"],
179+
plain: "ok",
180+
});
181+
});
182+
145183
test("CLI-created scores are immediately visible to score list queries", async () => {
146184
const store = new MemoryObjectStore();
147185
const dbPath = join(

0 commit comments

Comments
 (0)