diff --git a/e2e/scenarios/ai-sdk-otel-export/__snapshots__/ai-sdk-v5.otel-spans.json b/e2e/scenarios/ai-sdk-otel-export/__snapshots__/ai-sdk-v5.otel-spans.json index acf5e50ae..fd5442920 100644 --- a/e2e/scenarios/ai-sdk-otel-export/__snapshots__/ai-sdk-v5.otel-spans.json +++ b/e2e/scenarios/ai-sdk-otel-export/__snapshots__/ai-sdk-v5.otel-spans.json @@ -1,7 +1,7 @@ [ { "hasParent": false, - "name": "ai.generateText.doGenerate" + "name": "ai.generateText" }, { "hasParent": false, @@ -9,11 +9,11 @@ }, { "hasParent": false, - "name": "ai.streamText.doStream" + "name": "ai.generateText.doGenerate" }, { "hasParent": false, - "name": "ai.streamText" + "name": "ai.generateText.doGenerate" }, { "hasParent": false, @@ -21,7 +21,7 @@ }, { "hasParent": false, - "name": "ai.toolCall" + "name": "ai.generateText.doGenerate" }, { "hasParent": false, @@ -29,11 +29,11 @@ }, { "hasParent": false, - "name": "ai.toolCall" + "name": "ai.streamText" }, { "hasParent": false, - "name": "ai.generateText.doGenerate" + "name": "ai.streamText.doStream" }, { "hasParent": false, @@ -41,7 +41,7 @@ }, { "hasParent": false, - "name": "ai.generateText.doGenerate" + "name": "ai.toolCall" }, { "hasParent": false, @@ -49,6 +49,6 @@ }, { "hasParent": false, - "name": "ai.generateText" + "name": "ai.toolCall" } ] diff --git a/e2e/scenarios/ai-sdk-otel-export/__snapshots__/ai-sdk-v6.otel-spans.json b/e2e/scenarios/ai-sdk-otel-export/__snapshots__/ai-sdk-v6.otel-spans.json index acf5e50ae..fd5442920 100644 --- a/e2e/scenarios/ai-sdk-otel-export/__snapshots__/ai-sdk-v6.otel-spans.json +++ b/e2e/scenarios/ai-sdk-otel-export/__snapshots__/ai-sdk-v6.otel-spans.json @@ -1,7 +1,7 @@ [ { "hasParent": false, - "name": "ai.generateText.doGenerate" + "name": "ai.generateText" }, { "hasParent": false, @@ -9,11 +9,11 @@ }, { "hasParent": false, - "name": "ai.streamText.doStream" + "name": "ai.generateText.doGenerate" }, { "hasParent": false, - "name": "ai.streamText" + "name": "ai.generateText.doGenerate" }, { "hasParent": false, @@ -21,7 +21,7 @@ }, { "hasParent": false, - "name": "ai.toolCall" + "name": "ai.generateText.doGenerate" }, { "hasParent": false, @@ -29,11 +29,11 @@ }, { "hasParent": false, - "name": "ai.toolCall" + "name": "ai.streamText" }, { "hasParent": false, - "name": "ai.generateText.doGenerate" + "name": "ai.streamText.doStream" }, { "hasParent": false, @@ -41,7 +41,7 @@ }, { "hasParent": false, - "name": "ai.generateText.doGenerate" + "name": "ai.toolCall" }, { "hasParent": false, @@ -49,6 +49,6 @@ }, { "hasParent": false, - "name": "ai.generateText" + "name": "ai.toolCall" } ] diff --git a/e2e/scenarios/ai-sdk-otel-export/scenario.test.ts b/e2e/scenarios/ai-sdk-otel-export/scenario.test.ts index 7b230db44..7d81f5eac 100644 --- a/e2e/scenarios/ai-sdk-otel-export/scenario.test.ts +++ b/e2e/scenarios/ai-sdk-otel-export/scenario.test.ts @@ -119,10 +119,17 @@ for (const scenario of scenarios) { // Snapshot the span names and key structure (not full payloads, since // response content and token counts are non-deterministic). - const spanSummary = allSpans.map((span) => ({ - hasParent: !!span.parentSpanId, - name: span.name, - })); + // Sort for determinism — OTel spans arrive in non-deterministic order. + const spanSummary = allSpans + .map((span) => ({ + hasParent: !!span.parentSpanId, + name: span.name, + })) + .sort((a, b) => + a.name !== b.name + ? a.name.localeCompare(b.name) + : Number(a.hasParent) - Number(b.hasParent), + ); await expect(formatJsonFileSnapshot(spanSummary)).toMatchFileSnapshot( resolveFileSnapshotPath( import.meta.url, diff --git a/e2e/scenarios/anthropic-instrumentation/scenario.test.ts b/e2e/scenarios/anthropic-instrumentation/scenario.test.ts index baebbc01d..ce7184bef 100644 --- a/e2e/scenarios/anthropic-instrumentation/scenario.test.ts +++ b/e2e/scenarios/anthropic-instrumentation/scenario.test.ts @@ -9,7 +9,7 @@ import { defineAnthropicInstrumentationAssertions } from "./assertions"; const scenarioDir = await prepareScenarioDir({ scenarioDir: resolveScenarioDir(import.meta.url), }); -const TIMEOUT_MS = 90_000; +const TIMEOUT_MS = 150_000; const anthropicScenarios = await Promise.all( [ { diff --git a/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/log-payloads.json b/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/log-payloads.json index 7a1b005aa..b3852b50a 100644 --- a/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/log-payloads.json +++ b/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/log-payloads.json @@ -126,10 +126,6 @@ "ls_model_type": "chat", "ls_provider": "openai", "ls_temperature": 0, - "max_tokens": 16, - "model": "gpt-4o-mini", - "stream": false, - "temperature": 0, "versions": { "@langchain/core": "", "@langchain/openai": "" @@ -704,10 +700,6 @@ "ls_model_type": "chat", "ls_provider": "openai", "ls_temperature": 0, - "max_tokens": 32, - "model": "gpt-4o-mini", - "stream": false, - "temperature": 0, "versions": { "@langchain/core": "", "@langchain/openai": "" @@ -973,13 +965,6 @@ "ls_model_type": "chat", "ls_provider": "openai", "ls_temperature": 0, - "max_tokens": 32, - "model": "gpt-4o-mini", - "stream": true, - "stream_options": { - "include_usage": true - }, - "temperature": 0, "versions": { "@langchain/core": "", "@langchain/openai": "" @@ -1265,10 +1250,6 @@ "ls_model_type": "chat", "ls_provider": "openai", "ls_temperature": 0, - "max_tokens": 128, - "model": "gpt-4o-mini", - "stream": false, - "temperature": 0, "versions": { "@langchain/core": "", "@langchain/openai": "" @@ -1606,10 +1587,6 @@ "ls_model_type": "chat", "ls_provider": "openai", "ls_temperature": 0, - "max_tokens": 128, - "model": "gpt-4o-mini", - "stream": false, - "temperature": 0, "versions": { "@langchain/core": "", "@langchain/openai": "" @@ -1962,10 +1939,6 @@ "ls_model_type": "chat", "ls_provider": "openai", "ls_temperature": 0, - "max_tokens": 128, - "model": "gpt-4o-mini", - "stream": false, - "temperature": 0, "versions": { "@langchain/core": "", "@langchain/openai": "" diff --git a/e2e/scenarios/wrap-langchain-js-traces/assertions.ts b/e2e/scenarios/wrap-langchain-js-traces/assertions.ts index ff6415220..f5e1ed7dd 100644 --- a/e2e/scenarios/wrap-langchain-js-traces/assertions.ts +++ b/e2e/scenarios/wrap-langchain-js-traces/assertions.ts @@ -137,6 +137,17 @@ function normalizeToolCallIds(obj: unknown): void { } } +// Fields that older @langchain/openai included in the ls_* metadata block but +// newer versions removed. Normalize them out so the snapshot is stable across +// both locked and canary (latest) langchain versions. +const LANGCHAIN_LS_VOLATILE_KEYS = new Set([ + "max_tokens", + "model", + "stream", + "stream_options", + "temperature", +]); + function normalizeLangchainVersions(obj: unknown): void { if (!obj || typeof obj !== "object") return; @@ -161,6 +172,15 @@ function normalizeLangchainVersions(obj: unknown): void { } } + // If this object is the ls_* metadata block (identified by the presence of + // any `ls_` key), remove volatile keys that newer langchain drops. + const hasLsKey = Object.keys(record).some((k) => k.startsWith("ls_")); + if (hasLsKey) { + for (const key of LANGCHAIN_LS_VOLATILE_KEYS) { + delete record[key]; + } + } + for (const value of Object.values(record)) { if (typeof value === "object" && value !== null) { normalizeLangchainVersions(value);