diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json index 972a016966e0..34dd2a7757da 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json @@ -14,8 +14,8 @@ "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^2.5.1", "@opentelemetry/core": "^2.5.1", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/instrumentation-http": "^0.211.0", + "@opentelemetry/instrumentation": "^0.212.0", + "@opentelemetry/instrumentation-http": "^0.212.0", "@opentelemetry/resources": "^2.5.1", "@opentelemetry/sdk-trace-node": "^2.5.1", "@opentelemetry/semantic-conventions": "^1.39.0", diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json index 16b87251e743..96fe46062bcc 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json @@ -14,13 +14,13 @@ "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^2.5.1", "@opentelemetry/core": "^2.5.1", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/instrumentation-http": "^0.211.0", + "@opentelemetry/instrumentation": "^0.212.0", + "@opentelemetry/instrumentation-http": "^0.212.0", "@opentelemetry/resources": "^2.5.1", "@opentelemetry/sdk-trace-node": "^2.5.1", "@opentelemetry/semantic-conventions": "^1.39.0", - "@opentelemetry/sdk-node": "^0.211.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.211.0", + "@opentelemetry/sdk-node": "^0.212.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.212.0", "@sentry/node-core": "latest || *", "@sentry/opentelemetry": "latest || *", "@types/express": "4.17.17", diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json index c6fe70e91773..210ed53d2733 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json @@ -16,8 +16,8 @@ "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^2.5.1", "@opentelemetry/core": "^2.5.1", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/instrumentation-http": "^0.211.0", + "@opentelemetry/instrumentation": "^0.212.0", + "@opentelemetry/instrumentation-http": "^0.212.0", "@opentelemetry/resources": "^2.5.1", "@opentelemetry/sdk-trace-node": "^2.5.1", "@opentelemetry/semantic-conventions": "^1.39.0", diff --git a/dev-packages/node-core-integration-tests/package.json b/dev-packages/node-core-integration-tests/package.json index 5a89f8303259..76764bcdc5a4 100644 --- a/dev-packages/node-core-integration-tests/package.json +++ b/dev-packages/node-core-integration-tests/package.json @@ -29,8 +29,8 @@ "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^2.5.1", "@opentelemetry/core": "^2.5.1", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/instrumentation-http": "0.211.0", + "@opentelemetry/instrumentation": "^0.212.0", + "@opentelemetry/instrumentation-http": "0.212.0", "@opentelemetry/resources": "^2.5.1", "@opentelemetry/sdk-trace-base": "^2.5.1", "@opentelemetry/semantic-conventions": "^1.39.0", diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/agent-scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/agent-scenario.mjs new file mode 100644 index 000000000000..cef786b8988f --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/agent-scenario.mjs @@ -0,0 +1,70 @@ +import { ChatAnthropic } from '@langchain/anthropic'; +import { HumanMessage, SystemMessage } from '@langchain/core/messages'; +import { createReactAgent } from '@langchain/langgraph/prebuilt'; +import * as Sentry from '@sentry/node'; +import express from 'express'; + +function startMockAnthropicServer() { + const app = express(); + app.use(express.json()); + + app.post('/v1/messages', (req, res) => { + const model = req.body.model; + + // Simulate basic response + res.json({ + id: 'msg_react_agent_123', + type: 'message', + role: 'assistant', + content: [ + { + type: 'text', + text: 'Mock response from Anthropic!', + }, + ], + model: model, + stop_reason: 'end_turn', + stop_sequence: null, + usage: { + input_tokens: 10, + output_tokens: 15, + }, + }); + }); + + return new Promise(resolve => { + const server = app.listen(0, () => { + resolve(server); + }); + }); +} + +async function run() { + const server = await startMockAnthropicServer(); + const baseUrl = `http://localhost:${server.address().port}`; + + await Sentry.startSpan({ op: 'function', name: 'main' }, async () => { + // Create mocked LLM instance + const llm = new ChatAnthropic({ + model: 'claude-3-5-sonnet-20241022', + apiKey: 'mock-api-key', + clientOptions: { + baseURL: baseUrl, + }, + }); + + // Create a simple react agent with no tools + const agent = createReactAgent({ llm, tools: [] }); + + // Test: basic invocation + await agent.invoke({ + messages: [new SystemMessage('You are a helpful assistant.'), new HumanMessage('What is the weather today?')], + }); + }); + + await Sentry.flush(2000); + + server.close(); +} + +run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-with-pii.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-with-pii.mjs index be512ed2f773..cb68a6f7683e 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-with-pii.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-with-pii.mjs @@ -7,4 +7,11 @@ Sentry.init({ tracesSampleRate: 1.0, sendDefaultPii: true, transport: loggingTransport, + beforeSendTransaction: event => { + // Filter out mock express server transactions + if (event.transaction.includes('/v1/messages')) { + return null; + } + return event; + }, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument.mjs index 06cc1a32e93e..b4ce44f3e91a 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument.mjs @@ -7,4 +7,11 @@ Sentry.init({ tracesSampleRate: 1.0, sendDefaultPii: false, transport: loggingTransport, + beforeSendTransaction: event => { + // Filter out mock express server transactions + if (event.transaction.includes('/v1/messages')) { + return null; + } + return event; + }, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts index 0b03e59bbfbf..0258bee817a4 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts @@ -223,6 +223,46 @@ describe('LangGraph integration', () => { }); }); + const EXPECTED_TRANSACTION_REACT_AGENT = { + transaction: 'main', + spans: expect.arrayContaining([ + // create_agent span + expect.objectContaining({ + data: expect.objectContaining({ + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'create_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.create_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.langgraph', + }), + description: expect.stringContaining('create_agent'), + op: 'gen_ai.create_agent', + origin: 'auto.ai.langgraph', + status: 'ok', + }), + // invoke_agent span + expect.objectContaining({ + data: expect.objectContaining({ + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'invoke_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.invoke_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.langgraph', + }), + description: expect.stringContaining('invoke_agent'), + op: 'gen_ai.invoke_agent', + origin: 'auto.ai.langgraph', + status: 'ok', + }), + ]), + }; + + createEsmAndCjsTests(__dirname, 'agent-scenario.mjs', 'instrument.mjs', (createRunner, test) => { + test('should instrument LangGraph createReactAgent with default PII settings', async () => { + await createRunner() + .ignore('event') + .expect({ transaction: EXPECTED_TRANSACTION_REACT_AGENT }) + .start() + .completed(); + }); + }); + // Test for thread_id (conversation ID) support const EXPECTED_TRANSACTION_THREAD_ID = { transaction: 'langgraph-thread-id-test', diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json index 453aa291510e..e0f1e8de720f 100644 --- a/packages/aws-serverless/package.json +++ b/packages/aws-serverless/package.json @@ -66,7 +66,7 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/instrumentation": "^0.212.0", "@opentelemetry/instrumentation-aws-sdk": "0.66.0", "@opentelemetry/semantic-conventions": "^1.39.0", "@sentry/core": "10.39.0", diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 2fd6a4a9c8d5..f6425a2e9a8b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -159,7 +159,7 @@ export type { GoogleGenAIResponse } from './tracing/google-genai/types'; export { createLangChainCallbackHandler } from './tracing/langchain'; export { LANGCHAIN_INTEGRATION_NAME } from './tracing/langchain/constants'; export type { LangChainOptions, LangChainIntegration } from './tracing/langchain/types'; -export { instrumentStateGraphCompile, instrumentLangGraph } from './tracing/langgraph'; +export { instrumentStateGraphCompile, instrumentCreateReactAgent, instrumentLangGraph } from './tracing/langgraph'; export { LANGGRAPH_INTEGRATION_NAME } from './tracing/langgraph/constants'; export type { LangGraphOptions, LangGraphIntegration, CompiledGraph } from './tracing/langgraph/types'; export type { OpenAiClient, OpenAiOptions, InstrumentedMethod } from './tracing/openai/types'; diff --git a/packages/core/src/tracing/langchain/types.ts b/packages/core/src/tracing/langchain/types.ts index 7379de764817..e2aa08f4631c 100644 --- a/packages/core/src/tracing/langchain/types.ts +++ b/packages/core/src/tracing/langchain/types.ts @@ -30,6 +30,14 @@ export interface LangChainSerialized { kwargs?: Record; } +/** + * Subset of the 'llm' param passed to createReactAgent + */ +export interface BaseChatModel { + lc_namespace: string[]; + modelName: string; +} + /** * LangChain message structure * Supports both regular messages and LangChain serialized format diff --git a/packages/core/src/tracing/langgraph/index.ts b/packages/core/src/tracing/langgraph/index.ts index c1e838bd1914..e065bf8e185f 100644 --- a/packages/core/src/tracing/langgraph/index.ts +++ b/packages/core/src/tracing/langgraph/index.ts @@ -10,16 +10,17 @@ import { GEN_AI_OPERATION_NAME_ATTRIBUTE, GEN_AI_PIPELINE_NAME_ATTRIBUTE, GEN_AI_REQUEST_AVAILABLE_TOOLS_ATTRIBUTE, + GEN_AI_REQUEST_MODEL_ATTRIBUTE, GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE, } from '../ai/gen-ai-attributes'; import { truncateGenAiMessages } from '../ai/messageTruncation'; import { extractSystemInstructions } from '../ai/utils'; -import type { LangChainMessage } from '../langchain/types'; +import type { BaseChatModel, LangChainMessage } from '../langchain/types'; import { normalizeLangChainMessages } from '../langchain/utils'; import { startSpan } from '../trace'; import { LANGGRAPH_ORIGIN } from './constants'; import type { CompiledGraph, LangGraphOptions } from './types'; -import { extractToolsFromCompiledGraph, setResponseAttributes } from './utils'; +import { extractLLMFromParams, extractToolsFromCompiledGraph, setResponseAttributes } from './utils'; /** * Instruments StateGraph's compile method to create spans for agent creation and invocation @@ -94,9 +95,11 @@ function instrumentCompiledGraphInvoke( graphInstance: CompiledGraph, compileOptions: Record, options: LangGraphOptions, + llm?: BaseChatModel | null, ): (...args: unknown[]) => Promise { return new Proxy(originalInvoke, { apply(target, thisArg, args: unknown[]): Promise { + const modelName = llm?.modelName; return startSpan( { op: 'gen_ai.invoke_agent', @@ -116,6 +119,9 @@ function instrumentCompiledGraphInvoke( span.setAttribute(GEN_AI_AGENT_NAME_ATTRIBUTE, graphName); span.updateName(`invoke_agent ${graphName}`); } + if (modelName) { + span.setAttribute(GEN_AI_REQUEST_MODEL_ATTRIBUTE, modelName); + } // Extract thread_id from the config (second argument) // LangGraph uses config.configurable.thread_id for conversation/session linking @@ -179,6 +185,60 @@ function instrumentCompiledGraphInvoke( }) as (...args: unknown[]) => Promise; } +/** + * Instruments createReactAgent to create spans for agent creation and invocation + * + * Creates a `gen_ai.create_agent` span when createReactAgent() is called + */ +export function instrumentCreateReactAgent( + originalCreateReactAgent: (...args: unknown[]) => CompiledGraph, + options: LangGraphOptions, +): (...args: unknown[]) => CompiledGraph { + return new Proxy(originalCreateReactAgent, { + apply(target, thisArg, args: unknown[]): CompiledGraph { + const llm = extractLLMFromParams(args); + return startSpan( + { + op: 'gen_ai.create_agent', + name: 'create_agent', + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: LANGGRAPH_ORIGIN, + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.create_agent', + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'create_agent', + }, + }, + span => { + try { + const compiledGraph = Reflect.apply(target, thisArg, args); + const compiledOptions = args.length > 0 ? (args[0] as Record) : {}; + const originalInvoke = compiledGraph.invoke; + if (originalInvoke && typeof originalInvoke === 'function') { + compiledGraph.invoke = instrumentCompiledGraphInvoke( + originalInvoke.bind(compiledGraph) as (...args: unknown[]) => Promise, + compiledGraph, + compiledOptions, + options, + llm, + ) as typeof originalInvoke; + } + + return compiledGraph; + } catch (error) { + span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); + captureException(error, { + mechanism: { + handled: false, + type: 'auto.ai.langgraph.error', + }, + }); + throw error; + } + }, + ); + }, + }) as (...args: unknown[]) => CompiledGraph; +} + /** * Directly instruments a StateGraph instance to add tracing spans * diff --git a/packages/core/src/tracing/langgraph/utils.ts b/packages/core/src/tracing/langgraph/utils.ts index 4b1990058924..f2c64052c071 100644 --- a/packages/core/src/tracing/langgraph/utils.ts +++ b/packages/core/src/tracing/langgraph/utils.ts @@ -8,10 +8,25 @@ import { GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE, GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE, } from '../ai/gen-ai-attributes'; -import type { LangChainMessage } from '../langchain/types'; +import type { BaseChatModel, LangChainMessage } from '../langchain/types'; import { normalizeLangChainMessages } from '../langchain/utils'; import type { CompiledGraph, LangGraphTool } from './types'; +/** + * Extract LLM model object from createReactAgent params + */ +export function extractLLMFromParams(args: unknown[]): null | BaseChatModel { + const arg = args[0]; + return typeof arg === 'object' && + !!arg && + 'llm' in arg && + !!arg.llm && + typeof arg.llm === 'object' && + typeof (arg.llm as BaseChatModel).modelName === 'string' + ? (arg.llm as BaseChatModel) + : null; +} + /** * Extract tool calls from messages */ @@ -139,7 +154,9 @@ export function setResponseAttributes(span: Span, inputMessages: LangChainMessag } // Get new messages (delta between input and output) + /* v8 ignore start - coverage gets confused by this somehow */ const inputCount = inputMessages?.length ?? 0; + /* v8 ignore stop */ const newMessages = outputMessages.length > inputCount ? outputMessages.slice(inputCount) : []; if (newMessages.length === 0) { diff --git a/packages/core/test/lib/utils/langraph-utils.test.ts b/packages/core/test/lib/utils/langraph-utils.test.ts new file mode 100644 index 000000000000..bd8d5c7193ec --- /dev/null +++ b/packages/core/test/lib/utils/langraph-utils.test.ts @@ -0,0 +1,255 @@ +import { beforeEach, describe, expect, it } from 'vitest'; +import type { Span } from '../../../src'; +import { + extractLLMFromParams, + extractModelMetadata, + extractTokenUsageFromMessage, + extractToolCalls, + extractToolsFromCompiledGraph, + setResponseAttributes, +} from '../../../src/tracing/langgraph/utils'; + +describe('extractLLMFromParams', () => { + it('handles invalid args or missing llm object', () => { + // @ts-expect-error should be arguments array, at least. + expect(extractLLMFromParams({})).toBe(null); + expect(extractLLMFromParams([])).toBe(null); + expect(extractLLMFromParams([null])).toBe(null); + expect(extractLLMFromParams([{}])).toBe(null); + expect(extractLLMFromParams([{ llm: false }])).toBe(null); + expect(extractLLMFromParams([{ llm: 123 }])).toBe(null); + expect(extractLLMFromParams([{ llm: {} }])).toBe(null); + }); + it('extracts llm object if found', () => { + expect(extractLLMFromParams([{ llm: { modelName: 'model-name-1' } }])).toStrictEqual({ modelName: 'model-name-1' }); + }); +}); + +describe('extractToolCalls', () => { + it('returns null for missing/empty messages', () => { + expect(extractToolCalls(null)).toBe(null); + expect(extractToolCalls([])).toBe(null); + expect(extractToolCalls([{}])).toBe(null); + expect(extractToolCalls([{ tool_calls: null }])).toBe(null); + expect(extractToolCalls([{ tool_calls: [] }])).toBe(null); + }); + it('extracts tool call from messages array', () => { + expect( + extractToolCalls([ + { tool_calls: [{ name: 'tool a' }] }, + { tool_calls: [{ name: 'tool b' }, { name: 'tool c' }] }, + ]), + ).toStrictEqual([{ name: 'tool a' }, { name: 'tool b' }, { name: 'tool c' }]); + }); +}); + +describe('extractTokenUsageFromMessage', () => { + it('extracts from usage_metadata', () => { + const inputs = [{}, { input_tokens: 10 }]; + const outputs = [{}, { output_tokens: 20 }]; + const totals = [{}, { total_tokens: 30 }]; + for (const i of inputs) { + for (const o of outputs) { + for (const t of totals) { + expect( + extractTokenUsageFromMessage({ + usage_metadata: { + ...i, + ...o, + ...t, + }, + }), + ).toStrictEqual({ + inputTokens: i?.input_tokens ?? 0, + outputTokens: o?.output_tokens ?? 0, + totalTokens: t?.total_tokens ?? 0, + }); + } + } + } + }); + it('falls back to response_metadata', () => { + const inputs = [{}, { promptTokens: 10 }]; + const outputs = [{}, { completionTokens: 20 }]; + const totals = [{}, { totalTokens: 30 }]; + for (const i of inputs) { + for (const o of outputs) { + for (const t of totals) { + expect( + extractTokenUsageFromMessage({ + response_metadata: { + // @ts-expect-error using old tokenUsage field + tokenUsage: { + ...i, + ...o, + ...t, + }, + }, + }), + ).toStrictEqual({ + inputTokens: i?.promptTokens ?? 0, + outputTokens: o?.completionTokens ?? 0, + totalTokens: t?.totalTokens ?? 0, + }); + } + } + } + }); +}); + +describe('extractModelMetadata', () => { + let attributes: Record = {}; + const span = { + setAttribute(key: string, value: unknown) { + attributes[key] = value; + }, + } as unknown as Span; + beforeEach(() => (attributes = {})); + + it('handles lacking metadata ok', () => { + extractModelMetadata(span, {}); + expect(attributes).toStrictEqual({}); + }); + + it('extracts response model name from metadata', () => { + extractModelMetadata(span, { + response_metadata: { + model_name: 'model-name', + finish_reason: 'stop', + }, + }); + expect(attributes).toStrictEqual({ + 'gen_ai.response.model': 'model-name', + 'gen_ai.response.finish_reasons': ['stop'], + }); + }); +}); + +describe('extractToolsFromCompiledGraph', () => { + it('returns null if no tools found', () => { + expect(extractToolsFromCompiledGraph({})).toBe(null); + expect( + extractToolsFromCompiledGraph({ + builder: { nodes: { tools: { runnable: {} } } }, + }), + ).toBe(null); + expect( + extractToolsFromCompiledGraph({ + // @ts-expect-error Wants LangGraphTool[] + builder: { nodes: { tools: { runnable: { tools: 'not an array' } } } }, + }), + ).toBe(null); + expect( + extractToolsFromCompiledGraph({ + builder: { nodes: { tools: { runnable: { tools: [] } } } }, + }), + ).toBe(null); + }); + it('returns the tools found', () => { + expect( + extractToolsFromCompiledGraph({ + builder: { + nodes: { + tools: { + runnable: { + tools: [ + {}, + { lc_kwargs: { name: 'name' } }, + { lc_kwargs: { name: 'name', description: 'desc' } }, + { lc_kwargs: { name: 'name', description: 'desc', schema: 'schema' } }, + ], + }, + }, + }, + }, + }), + ).toStrictEqual([ + { name: undefined, description: undefined, schema: undefined }, + { name: 'name', description: undefined, schema: undefined }, + { name: 'name', description: 'desc', schema: undefined }, + { name: 'name', description: 'desc', schema: 'schema' }, + ]); + }); +}); + +describe('setResponseAttribute', () => { + let attributes: Record = {}; + const span = { + setAttribute(key: string, value: unknown) { + attributes[key] = value; + }, + } as unknown as Span; + beforeEach(() => (attributes = {})); + + it('handles lack of messages', () => { + setResponseAttributes(span, [], undefined); + expect(attributes).toStrictEqual({}); + + setResponseAttributes(span, null, undefined); + expect(attributes).toStrictEqual({}); + + setResponseAttributes(span, null, {}); + expect(attributes).toStrictEqual({}); + + setResponseAttributes(span, null, { messages: null }); + expect(attributes).toStrictEqual({}); + + // no new messages + setResponseAttributes(span, [{}], { messages: [{}] }); + expect(attributes).toStrictEqual({}); + setResponseAttributes(span, [], { messages: [] }); + expect(attributes).toStrictEqual({}); + + // @ts-expect-error cover excessive type safety case + setResponseAttributes(span, { length: undefined }, []); + expect(attributes).toStrictEqual({}); + }); + + it('extracts tool calls', () => { + setResponseAttributes(span, [], { + messages: [{ tool_calls: [{ name: 'tool a' }] }], + }); + expect(attributes).toStrictEqual({ + 'gen_ai.response.text': '[{"role":"user"}]', + 'gen_ai.response.tool_calls': JSON.stringify([{ name: 'tool a' }]), + }); + }); + + it('extracts token usage', () => { + setResponseAttributes(span, [], { + messages: [ + { + usage_metadata: { + input_tokens: 1, + output_tokens: 2, + total_tokens: 3, + }, + }, + ], + }); + expect(attributes).toStrictEqual({ + 'gen_ai.response.text': '[{"role":"user"}]', + 'gen_ai.usage.input_tokens': 1, + 'gen_ai.usage.output_tokens': 2, + 'gen_ai.usage.total_tokens': 3, + }); + }); + + it('extracts model metadata', () => { + setResponseAttributes(span, [], { + messages: [ + { + response_metadata: { + model_name: 'model-name-1', + finish_reason: 'stop', + }, + }, + ], + }); + expect(attributes).toStrictEqual({ + 'gen_ai.response.text': '[{"role":"user"}]', + 'gen_ai.response.model': 'model-name-1', + 'gen_ai.response.finish_reasons': ['stop'], + }); + }); +}); diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index a1ed07c3d81f..59ff95a7a995 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -46,7 +46,7 @@ "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/core": "^2.5.1", - "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/instrumentation": "^0.212.0", "@opentelemetry/instrumentation-nestjs-core": "0.57.0", "@opentelemetry/semantic-conventions": "^1.39.0", "@sentry/core": "10.39.0", diff --git a/packages/node-core/package.json b/packages/node-core/package.json index 2cd2c462eba3..aeae61f8f476 100644 --- a/packages/node-core/package.json +++ b/packages/node-core/package.json @@ -107,7 +107,7 @@ "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^2.5.1", "@opentelemetry/core": "^2.5.1", - "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/instrumentation": "^0.212.0", "@opentelemetry/resources": "^2.5.1", "@opentelemetry/sdk-trace-base": "^2.5.1", "@opentelemetry/semantic-conventions": "^1.39.0", diff --git a/packages/node/package.json b/packages/node/package.json index f9eb8331702e..f48dd4a28380 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -68,7 +68,7 @@ "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^2.5.1", "@opentelemetry/core": "^2.5.1", - "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/instrumentation": "^0.212.0", "@opentelemetry/instrumentation-amqplib": "0.58.0", "@opentelemetry/instrumentation-connect": "0.54.0", "@opentelemetry/instrumentation-dataloader": "0.28.0", @@ -77,7 +77,7 @@ "@opentelemetry/instrumentation-generic-pool": "0.54.0", "@opentelemetry/instrumentation-graphql": "0.58.0", "@opentelemetry/instrumentation-hapi": "0.57.0", - "@opentelemetry/instrumentation-http": "0.211.0", + "@opentelemetry/instrumentation-http": "0.212.0", "@opentelemetry/instrumentation-ioredis": "0.59.0", "@opentelemetry/instrumentation-kafkajs": "0.20.0", "@opentelemetry/instrumentation-knex": "0.55.0", @@ -95,7 +95,6 @@ "@opentelemetry/sdk-trace-base": "^2.5.1", "@opentelemetry/semantic-conventions": "^1.39.0", "@prisma/instrumentation": "7.2.0", - "@fastify/otel": "0.16.0", "@sentry/core": "10.39.0", "@sentry/node-core": "10.39.0", "@sentry/opentelemetry": "10.39.0", diff --git a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts index d275e1b9d39b..85244aa9bc6c 100644 --- a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts +++ b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts @@ -6,7 +6,7 @@ import { InstrumentationNodeModuleFile, } from '@opentelemetry/instrumentation'; import type { CompiledGraph, LangGraphOptions } from '@sentry/core'; -import { getClient, instrumentStateGraphCompile, SDK_VERSION } from '@sentry/core'; +import { getClient, instrumentCreateReactAgent, instrumentStateGraphCompile, SDK_VERSION } from '@sentry/core'; const supportedVersions = ['>=0.0.0 <2.0.0']; @@ -18,6 +18,7 @@ type LangGraphInstrumentationOptions = InstrumentationConfig & LangGraphOptions; interface PatchedModuleExports { [key: string]: unknown; StateGraph?: abstract new (...args: unknown[]) => unknown; + createReactAgent?: (...args: unknown[]) => CompiledGraph; } /** @@ -31,28 +32,72 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase exports, - [ - new InstrumentationNodeModuleFile( - /** - * In CJS, LangGraph packages re-export from dist/index.cjs files. - * Patching only the root module sometimes misses the real implementation or - * gets overwritten when that file is loaded. We add a file-level patch so that - * _patch runs again on the concrete implementation - */ - '@langchain/langgraph/dist/index.cjs', - supportedVersions, - this._patch.bind(this), - exports => exports, - ), - ], - ); - return module; + public init(): InstrumentationModuleDefinition[] { + return [ + new InstrumentationNodeModuleDefinition( + '@langchain/langgraph', + supportedVersions, + this._patch.bind(this), + exports => exports, + [ + new InstrumentationNodeModuleFile( + /** + * In CJS, LangGraph packages re-export from dist/index.cjs files. + * Patching only the root module sometimes misses the real implementation or + * gets overwritten when that file is loaded. We add a file-level patch so that + * _patch runs again on the concrete implementation + */ + '@langchain/langgraph/dist/index.cjs', + supportedVersions, + this._patch.bind(this), + exports => exports, + ), + new InstrumentationNodeModuleFile( + /** + * In CJS, LangGraph packages re-export from dist/prebuilt/index.cjs files. + * Patching only the root module sometimes misses the real implementation or + * gets overwritten when that file is loaded. We add a file-level patch so that + * _patch runs again on the concrete implementation + */ + '@langchain/langgraph/dist/prebuilt/index.cjs', + supportedVersions, + this._patch.bind(this), + exports => exports, + ), + ], + ), + new InstrumentationNodeModuleDefinition( + '@langchain/langgraph/prebuilt', + supportedVersions, + this._patch.bind(this), + exports => exports, + [ + new InstrumentationNodeModuleFile( + /** + * ESM builds use dist/prebuilt/index.js (without .cjs extension) + * This catches ESM imports that resolve through the main package, + * using the package.json submodule export + */ + '@langchain/langgraph/dist/prebuilt/index.js', + supportedVersions, + this._patch.bind(this), + exports => exports, + ), + new InstrumentationNodeModuleFile( + /** + * In CJS, LangGraph packages re-export from dist/prebuilt/index.cjs files. + * Patching only the root module sometimes misses the real implementation or + * gets overwritten when that file is loaded. We add a file-level patch so that + * _patch runs again on the concrete implementation + */ + '@langchain/langgraph/dist/prebuilt/index.cjs', + supportedVersions, + this._patch.bind(this), + exports => exports, + ), + ], + ), + ]; } /** @@ -83,6 +128,17 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase CompiledGraph, options), + writable: true, + enumerable: true, + configurable: true, + }); + } + return exports; } } diff --git a/packages/node/test/sdk/client.test.ts b/packages/node/test/sdk/client.test.ts index ff58698a7931..9a6029826567 100644 --- a/packages/node/test/sdk/client.test.ts +++ b/packages/node/test/sdk/client.test.ts @@ -1,5 +1,5 @@ import { ProxyTracer } from '@opentelemetry/api'; -import * as opentelemetryInstrumentationPackage from '@opentelemetry/instrumentation'; +import type * as opentelemetryInstrumentationPackage from '@opentelemetry/instrumentation'; import type { Event, EventHint, Log } from '@sentry/core'; import { getCurrentScope, getGlobalScope, getIsolationScope, Scope, SDK_VERSION } from '@sentry/core'; import { setOpenTelemetryContextAsyncContextStrategy } from '@sentry/opentelemetry'; @@ -285,18 +285,23 @@ describe('NodeClient', () => { }); it('registers instrumentations provided with `openTelemetryInstrumentations`', () => { - const registerInstrumentationsSpy = vi - .spyOn(opentelemetryInstrumentationPackage, 'registerInstrumentations') - .mockImplementationOnce(() => () => undefined); - const instrumentationsArray = ['foobar'] as unknown as opentelemetryInstrumentationPackage.Instrumentation[]; + const mockInstrumentation = { + setTracerProvider: vi.fn(), + setMeterProvider: vi.fn(), + enable: vi.fn(), + disable: vi.fn(), + getConfig: vi.fn(() => ({})), + setConfig: vi.fn(), + getModuleDefinitions: vi.fn(() => []), + } as unknown as opentelemetryInstrumentationPackage.Instrumentation; + + const instrumentationsArray = [mockInstrumentation]; new NodeClient(getDefaultNodeClientOptions({ openTelemetryInstrumentations: instrumentationsArray })); - expect(registerInstrumentationsSpy).toHaveBeenCalledWith( - expect.objectContaining({ - instrumentations: instrumentationsArray, - }), - ); + // Verify that the instrumentation was registered by checking if its methods were called + /* eslint-disable-next-line @typescript-eslint/unbound-method */ + expect(mockInstrumentation.setTracerProvider).toHaveBeenCalled(); }); describe('log capture', () => { diff --git a/packages/react-router/package.json b/packages/react-router/package.json index 1aee41086bad..cb16ecce3b0c 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -47,14 +47,14 @@ "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/core": "^2.5.1", - "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/instrumentation": "^0.212.0", "@opentelemetry/semantic-conventions": "^1.39.0", "@sentry/browser": "10.39.0", - "@sentry/cli": "^2.58.5", + "@sentry/cli": "^2.58.4", "@sentry/core": "10.39.0", "@sentry/node": "10.39.0", "@sentry/react": "10.39.0", - "@sentry/vite-plugin": "^5.1.0", + "@sentry/vite-plugin": "^4.8.0", "glob": "^13.0.1" }, "devDependencies": { diff --git a/packages/remix/package.json b/packages/remix/package.json index db69ddc77c6a..81644c690ae1 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -65,7 +65,7 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/instrumentation": "^0.212.0", "@opentelemetry/semantic-conventions": "^1.39.0", "@remix-run/router": "^1.23.2", "@sentry/cli": "^2.58.5", diff --git a/yarn.lock b/yarn.lock index 5bd273f6d7a2..22b1708ab86e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4414,16 +4414,6 @@ resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.0.0.tgz#f22824caff3ae506b18207bad4126dbc6ccdb6b8" integrity sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ== -"@fastify/otel@0.16.0": - version "0.16.0" - resolved "https://registry.yarnpkg.com/@fastify/otel/-/otel-0.16.0.tgz#e003c9b81039490af9141a7f1397de6b05baa768" - integrity sha512-2304BdM5Q/kUvQC9qJO1KZq3Zn1WWsw+WWkVmFEaj1UE2hEIiuFqrPeglQOwEtw/ftngisqfQ3v70TWMmwhhHA== - dependencies: - "@opentelemetry/core" "^2.0.0" - "@opentelemetry/instrumentation" "^0.208.0" - "@opentelemetry/semantic-conventions" "^1.28.0" - minimatch "^10.0.3" - "@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" @@ -6194,13 +6184,6 @@ dependencies: "@opentelemetry/api" "^1.3.0" -"@opentelemetry/api-logs@0.208.0": - version "0.208.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.208.0.tgz#56d3891010a1fa1cf600ba8899ed61b43ace511c" - integrity sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg== - dependencies: - "@opentelemetry/api" "^1.3.0" - "@opentelemetry/api-logs@0.211.0": version "0.211.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz#32d9ed98939956a84d4e2ff5e01598cb9d28d744" @@ -6208,6 +6191,13 @@ dependencies: "@opentelemetry/api" "^1.3.0" +"@opentelemetry/api-logs@0.212.0": + version "0.212.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.212.0.tgz#ec66a0951b84b1f082e13fd8a027b9f9d65a3f7a" + integrity sha512-TEEVrLbNROUkYY51sBJGk7lO/OLjuepch8+hmpM6ffMJQ2z/KVCjdHuCFX6fJj8OkJP2zckPjrJzQtXU3IAsFg== + dependencies: + "@opentelemetry/api" "^1.3.0" + "@opentelemetry/api@1.9.0", "@opentelemetry/api@^1.3.0", "@opentelemetry/api@^1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" @@ -6218,13 +6208,6 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-2.5.1.tgz#457b8f9c1e219bf6e22b549d90f773db0a38fe06" integrity sha512-MHbu8XxCHcBn6RwvCt2Vpn1WnLMNECfNKYB14LI5XypcgH4IE0/DiVifVR9tAkwPMyLXN8dOoPJfya3IryLQVw== -"@opentelemetry/core@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-2.5.0.tgz#3b2ac6cf471ed9a85eea836048a4de77a2e549d3" - integrity sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ== - dependencies: - "@opentelemetry/semantic-conventions" "^1.29.0" - "@opentelemetry/core@2.5.1", "@opentelemetry/core@^2.0.0", "@opentelemetry/core@^2.5.1": version "2.5.1" resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-2.5.1.tgz#b5d830ab499bc13e29f6efa88a165630f25d2ad2" @@ -6307,13 +6290,13 @@ "@opentelemetry/instrumentation" "^0.211.0" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-http@0.211.0": - version "0.211.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.211.0.tgz#2f12f83f0c21d37917fd9710fb5b755f28858cf6" - integrity sha512-n0IaQ6oVll9PP84SjbOCwDjaJasWRHi6BLsbMLiT6tNj7QbVOkuA5sk/EfZczwI0j5uTKl1awQPivO/ldVtsqA== +"@opentelemetry/instrumentation-http@0.212.0": + version "0.212.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.212.0.tgz#e9b08e500afb6f8feb0ffeeadf81cf8af77e9457" + integrity sha512-t2nt16Uyv9irgR+tqnX96YeToOStc3X5js7Ljn3EKlI2b4Fe76VhMkTXtsTQ0aId6AsYgefrCRnXSCo/Fn/vww== dependencies: - "@opentelemetry/core" "2.5.0" - "@opentelemetry/instrumentation" "0.211.0" + "@opentelemetry/core" "2.5.1" + "@opentelemetry/instrumentation" "0.212.0" "@opentelemetry/semantic-conventions" "^1.29.0" forwarded-parse "2.1.2" @@ -6440,13 +6423,13 @@ "@opentelemetry/instrumentation" "^0.211.0" "@opentelemetry/semantic-conventions" "^1.24.0" -"@opentelemetry/instrumentation@0.211.0", "@opentelemetry/instrumentation@^0.211.0": - version "0.211.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz#d45e20eafa75b5d3e8a9745a6205332893c55f37" - integrity sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q== +"@opentelemetry/instrumentation@0.212.0", "@opentelemetry/instrumentation@^0.212.0": + version "0.212.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.212.0.tgz#238b6e3e2131217ff4acfe7e8e7b6ce1f0ac0ba0" + integrity sha512-IyXmpNnifNouMOe0I/gX7ENfv2ZCNdYTF0FpCsoBcpbIHzk81Ww9rQTYTnvghszCg7qGrIhNvWC8dhEifgX9Jg== dependencies: - "@opentelemetry/api-logs" "0.211.0" - import-in-the-middle "^2.0.0" + "@opentelemetry/api-logs" "0.212.0" + import-in-the-middle "^2.0.6" require-in-the-middle "^8.0.0" "@opentelemetry/instrumentation@^0.207.0": @@ -6458,12 +6441,12 @@ import-in-the-middle "^2.0.0" require-in-the-middle "^8.0.0" -"@opentelemetry/instrumentation@^0.208.0": - version "0.208.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.208.0.tgz#d764f8e4329dad50804e2e98f010170c14c4ce8f" - integrity sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA== +"@opentelemetry/instrumentation@^0.211.0": + version "0.211.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz#d45e20eafa75b5d3e8a9745a6205332893c55f37" + integrity sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q== dependencies: - "@opentelemetry/api-logs" "0.208.0" + "@opentelemetry/api-logs" "0.211.0" import-in-the-middle "^2.0.0" require-in-the-middle "^8.0.0" @@ -6489,7 +6472,7 @@ "@opentelemetry/resources" "2.5.1" "@opentelemetry/semantic-conventions" "^1.29.0" -"@opentelemetry/semantic-conventions@^1.24.0", "@opentelemetry/semantic-conventions@^1.27.0", "@opentelemetry/semantic-conventions@^1.28.0", "@opentelemetry/semantic-conventions@^1.29.0", "@opentelemetry/semantic-conventions@^1.30.0", "@opentelemetry/semantic-conventions@^1.33.0", "@opentelemetry/semantic-conventions@^1.33.1", "@opentelemetry/semantic-conventions@^1.34.0", "@opentelemetry/semantic-conventions@^1.36.0", "@opentelemetry/semantic-conventions@^1.37.0", "@opentelemetry/semantic-conventions@^1.39.0": +"@opentelemetry/semantic-conventions@^1.24.0", "@opentelemetry/semantic-conventions@^1.27.0", "@opentelemetry/semantic-conventions@^1.29.0", "@opentelemetry/semantic-conventions@^1.30.0", "@opentelemetry/semantic-conventions@^1.33.0", "@opentelemetry/semantic-conventions@^1.33.1", "@opentelemetry/semantic-conventions@^1.34.0", "@opentelemetry/semantic-conventions@^1.36.0", "@opentelemetry/semantic-conventions@^1.37.0", "@opentelemetry/semantic-conventions@^1.39.0": version "1.39.0" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.39.0.tgz#f653b2752171411feb40310b8a8953d7e5c543b7" integrity sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg== @@ -7556,11 +7539,30 @@ fflate "^0.4.4" mitt "^3.0.0" +"@sentry/babel-plugin-component-annotate@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-4.9.1.tgz#76b306cb89ac465946e9328228a20c5e7b83b534" + integrity sha512-0gEoi2Lb54MFYPOmdTfxlNKxI7kCOvNV7gP8lxMXJ7nCazF5OqOOZIVshfWjDLrc0QrSV6XdVvwPV9GDn4wBMg== + "@sentry/babel-plugin-component-annotate@5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-5.1.0.tgz#59a9f203d07f4f17876c9a70ca6604ae28f4ebb0" integrity sha512-deEZGTxPMiVNcHXzYMcKEp2uGGU3Q+055nVH6vPHnzuxGoRNZRe2YZ5B1yP9gFD+LJGku8dJ4y3bs1iJrLGPtQ== +"@sentry/bundler-plugin-core@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-4.9.1.tgz#6a12b555954e5f727df982825ae240d56b1e29f4" + integrity sha512-moii+w7N8k8WdvkX7qCDY9iRBlhgHlhTHTUQwF2FNMhBHuqlNpVcSJJqJMjFUQcjYMBDrZgxhfKV18bt5ixwlQ== + dependencies: + "@babel/core" "^7.18.5" + "@sentry/babel-plugin-component-annotate" "4.9.1" + "@sentry/cli" "^2.57.0" + dotenv "^16.3.1" + find-up "^5.0.0" + glob "^10.5.0" + magic-string "0.30.8" + unplugin "1.0.1" + "@sentry/bundler-plugin-core@5.1.0", "@sentry/bundler-plugin-core@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-5.1.0.tgz#01bef91543eb42cd370288573291b9a02b240e84" @@ -7614,7 +7616,7 @@ resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.58.5.tgz#4937c0821abfd346da50a3cf1ecbd752f75f8ef4" integrity sha512-IZf+XIMiQwj+5NzqbOQfywlOitmCV424Vtf9c+ep61AaVScUFD1TSrQbOcJJv5xGxhlxNOMNgMeZhdexdzrKZg== -"@sentry/cli@^2.58.5": +"@sentry/cli@^2.57.0", "@sentry/cli@^2.58.4", "@sentry/cli@^2.58.5": version "2.58.5" resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.58.5.tgz#160a89235ba2add4c198f666d8c14547a1459ae8" integrity sha512-tavJ7yGUZV+z3Ct2/ZB6mg339i08sAk6HDkgqmSRuQEu2iLS5sl9HIvuXfM6xjv8fwlgFOSy++WNABNAcGHUbg== @@ -7642,6 +7644,14 @@ "@sentry/bundler-plugin-core" "5.1.0" magic-string "0.30.8" +"@sentry/vite-plugin@^4.8.0": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-4.9.1.tgz#1dd392c813f694451fd5c7816c0f15f2300dc581" + integrity sha512-Tlyg2cyFYp/icX58GWvfpvZr9NLdLs2/xyFVyS8pQ0faZWmoXic3FMzoXYHV1gsdMbL1Yy5WQvGJy8j1rS8LGA== + dependencies: + "@sentry/bundler-plugin-core" "4.9.1" + unplugin "1.0.1" + "@sentry/vite-plugin@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-5.1.0.tgz#217e9115ea643b6c92b846576f9eede6c8da2f55" @@ -10805,10 +10815,10 @@ acorn@8.11.3: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== -acorn@^8.0.4, acorn@^8.1.0, acorn@^8.10.0, acorn@^8.11.0, acorn@^8.12.1, acorn@^8.14.0, acorn@^8.14.1, acorn@^8.15.0, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.6.0, acorn@^8.7.0, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: - version "8.15.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" - integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== +acorn@^8.0.4, acorn@^8.1.0, acorn@^8.10.0, acorn@^8.11.0, acorn@^8.12.1, acorn@^8.14.0, acorn@^8.14.1, acorn@^8.15.0, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.6.0, acorn@^8.7.0, acorn@^8.7.1, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" + integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== adjust-sourcemap-loader@^4.0.0: version "4.0.0" @@ -17925,7 +17935,7 @@ glob@8.0.3: minimatch "^5.0.1" once "^1.3.0" -glob@^10.0.0, glob@^10.3.10, glob@^10.3.4, glob@^10.3.7, glob@^10.4.1: +glob@^10.0.0, glob@^10.3.10, glob@^10.3.4, glob@^10.3.7, glob@^10.4.1, glob@^10.5.0: version "10.5.0" resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== @@ -21921,7 +21931,7 @@ minimatch@5.1.0: dependencies: brace-expansion "^2.0.1" -minimatch@^10.0.3, minimatch@^10.2.2: +minimatch@^10.2.2: version "10.2.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.2.tgz#361603ee323cfb83496fea2ae17cc44ea4e1f99f" integrity sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw== @@ -29564,6 +29574,16 @@ unplugin-vue-router@^0.14.0: unplugin-utils "^0.2.4" yaml "^2.8.0" +unplugin@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-1.0.1.tgz#83b528b981cdcea1cad422a12cd02e695195ef3f" + integrity sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA== + dependencies: + acorn "^8.8.1" + chokidar "^3.5.3" + webpack-sources "^3.2.3" + webpack-virtual-modules "^0.5.0" + unplugin@^1.8.3: version "1.14.1" resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-1.14.1.tgz#c76d6155a661e43e6a897bce6b767a1ecc344c1a" @@ -30366,6 +30386,11 @@ webpack-subresource-integrity@5.1.0: dependencies: typed-assert "^1.0.8" +webpack-virtual-modules@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.5.0.tgz#362f14738a56dae107937ab98ea7062e8bdd3b6c" + integrity sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw== + webpack-virtual-modules@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz#057faa9065c8acf48f24cb57ac0e77739ab9a7e8"