diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/anthropic/init.js b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/anthropic/init.js deleted file mode 100644 index d90a3acf6157..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/anthropic/init.js +++ /dev/null @@ -1,9 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - tracesSampleRate: 1, - debug: true, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/anthropic/mocks.js b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/anthropic/mocks.js deleted file mode 100644 index 01c6c31ce596..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/anthropic/mocks.js +++ /dev/null @@ -1,55 +0,0 @@ -// Mock Anthropic client for browser testing -export class MockAnthropic { - constructor(config) { - this.apiKey = config.apiKey; - - // Main focus: messages.create functionality - this.messages = { - create: async (...args) => { - const params = args[0]; - // Simulate processing time - await new Promise(resolve => setTimeout(resolve, 10)); - - if (params.model === 'error-model') { - const error = new Error('Model not found'); - error.status = 404; - error.headers = { 'x-request-id': 'mock-request-123' }; - throw error; - } - - const response = { - id: 'msg_mock123', - type: 'message', - role: 'assistant', - model: params.model, - content: [ - { - type: 'text', - text: 'Hello from Anthropic mock!', - }, - ], - stop_reason: 'end_turn', - stop_sequence: null, - usage: { - input_tokens: 10, - output_tokens: 15, - cache_creation_input_tokens: 0, - cache_read_input_tokens: 0, - }, - }; - return response; - }, - countTokens: async (..._args) => ({ id: 'mock', type: 'model', model: 'mock', input_tokens: 0 }), - }; - - // Minimal implementations for required interface compliance - this.models = { - list: async (..._args) => ({ id: 'mock', type: 'model', model: 'mock' }), - get: async (..._args) => ({ id: 'mock', type: 'model', model: 'mock' }), - }; - - this.completions = { - create: async (..._args) => ({ id: 'mock', type: 'completion', model: 'mock' }), - }; - } -} diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/anthropic/subject.js b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/anthropic/subject.js deleted file mode 100644 index febfe938139e..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/anthropic/subject.js +++ /dev/null @@ -1,19 +0,0 @@ -import { instrumentAnthropicAiClient } from '@sentry/browser'; -import { MockAnthropic } from './mocks.js'; - -const mockClient = new MockAnthropic({ - apiKey: 'mock-api-key', -}); - -const client = instrumentAnthropicAiClient(mockClient); - -// Test that manual instrumentation doesn't crash the browser -// The instrumentation automatically creates spans -const response = await client.messages.create({ - model: 'claude-3-haiku-20240307', - messages: [{ role: 'user', content: 'What is the capital of France?' }], - temperature: 0.7, - max_tokens: 100, -}); - -console.log('Received response', response); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/anthropic/test.ts b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/anthropic/test.ts deleted file mode 100644 index 8f14f0318456..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/anthropic/test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { envelopeRequestParser, waitForTransactionRequest } from '../../../../utils/helpers'; - -// These tests are not exhaustive because the instrumentation is -// already tested in the node integration tests and we merely -// want to test that the instrumentation does not crash in the browser -// and that gen_ai transactions are sent. - -sentryTest('manual Anthropic instrumentation sends gen_ai transactions', async ({ getLocalTestUrl, page }) => { - const transactionPromise = waitForTransactionRequest(page, event => { - return !!event.transaction?.includes('claude-3-haiku-20240307'); - }); - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - - const req = await transactionPromise; - - const eventData = envelopeRequestParser(req); - - // Verify it's a gen_ai transaction - expect(eventData.transaction).toBe('chat claude-3-haiku-20240307'); - expect(eventData.contexts?.trace?.op).toBe('gen_ai.chat'); - expect(eventData.contexts?.trace?.origin).toBe('auto.ai.anthropic'); - expect(eventData.contexts?.trace?.data).toMatchObject({ - 'gen_ai.operation.name': 'chat', - 'gen_ai.system': 'anthropic', - 'gen_ai.request.model': 'claude-3-haiku-20240307', - 'gen_ai.request.temperature': 0.7, - 'gen_ai.response.model': 'claude-3-haiku-20240307', - 'gen_ai.response.id': 'msg_mock123', - 'gen_ai.usage.input_tokens': 10, - 'gen_ai.usage.output_tokens': 15, - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/init.js b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/init.js deleted file mode 100644 index d90a3acf6157..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/init.js +++ /dev/null @@ -1,9 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - tracesSampleRate: 1, - debug: true, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/mocks.js b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/mocks.js deleted file mode 100644 index d33f5dfbb285..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/mocks.js +++ /dev/null @@ -1,136 +0,0 @@ -// Mock Google GenAI client for browser testing -export class MockGoogleGenAI { - constructor(config) { - this.apiKey = config.apiKey; - - // models.generateContent functionality - this.models = { - generateContent: async (...args) => { - const params = args[0]; - // Simulate processing time - await new Promise(resolve => setTimeout(resolve, 10)); - - if (params.model === 'error-model') { - const error = new Error('Model not found'); - error.status = 404; - error.headers = { 'x-request-id': 'mock-request-123' }; - throw error; - } - - return { - candidates: [ - { - content: { - parts: [ - { - text: 'Hello from Google GenAI mock!', - }, - ], - role: 'model', - }, - finishReason: 'stop', - index: 0, - }, - ], - usageMetadata: { - promptTokenCount: 8, - candidatesTokenCount: 12, - totalTokenCount: 20, - }, - }; - }, - embedContent: async (...args) => { - const params = args[0]; - await new Promise(resolve => setTimeout(resolve, 10)); - - if (params.model === 'error-model') { - const error = new Error('Model not found'); - error.status = 404; - throw error; - } - - return { - embeddings: [ - { - values: [0.1, 0.2, 0.3, 0.4, 0.5], - }, - ], - }; - }, - generateContentStream: async () => { - // Return a promise that resolves to an async generator - return (async function* () { - yield { - candidates: [ - { - content: { - parts: [{ text: 'Streaming response' }], - role: 'model', - }, - finishReason: 'stop', - index: 0, - }, - ], - }; - })(); - }, - }; - - // chats.create implementation - this.chats = { - create: (...args) => { - const params = args[0]; - const model = params.model; - - return { - modelVersion: model, - sendMessage: async (..._messageArgs) => { - // Simulate processing time - await new Promise(resolve => setTimeout(resolve, 10)); - - const response = { - candidates: [ - { - content: { - parts: [ - { - text: 'This is a joke from the chat!', - }, - ], - role: 'model', - }, - finishReason: 'stop', - index: 0, - }, - ], - usageMetadata: { - promptTokenCount: 8, - candidatesTokenCount: 12, - totalTokenCount: 20, - }, - modelVersion: model, // Include model version in response - }; - return response; - }, - sendMessageStream: async () => { - // Return a promise that resolves to an async generator - return (async function* () { - yield { - candidates: [ - { - content: { - parts: [{ text: 'Streaming chat response' }], - role: 'model', - }, - finishReason: 'stop', - index: 0, - }, - ], - }; - })(); - }, - }; - }, - }; - } -} diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/subject.js b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/subject.js deleted file mode 100644 index b506ec52195b..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/subject.js +++ /dev/null @@ -1,40 +0,0 @@ -import { instrumentGoogleGenAIClient } from '@sentry/browser'; -import { MockGoogleGenAI } from './mocks.js'; - -const mockClient = new MockGoogleGenAI({ - apiKey: 'mock-api-key', -}); - -const client = instrumentGoogleGenAIClient(mockClient); - -// Test that manual instrumentation doesn't crash the browser -// The instrumentation automatically creates spans -// Test both chats and models APIs -const chat = client.chats.create({ - model: 'gemini-1.5-pro', - config: { - temperature: 0.8, - topP: 0.9, - maxOutputTokens: 150, - }, - history: [ - { - role: 'user', - parts: [{ text: 'Hello, how are you?' }], - }, - ], -}); - -const response = await chat.sendMessage({ - message: 'Tell me a joke', -}); - -console.log('Received response', response); - -// Test embedContent -const embedResponse = await client.models.embedContent({ - model: 'text-embedding-004', - contents: 'Hello world', -}); - -console.log('Received embed response', embedResponse); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/test.ts b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/test.ts deleted file mode 100644 index c6c9001b3e45..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { envelopeRequestParser, waitForTransactionRequest } from '../../../../utils/helpers'; - -// These tests are not exhaustive because the instrumentation is -// already tested in the node integration tests and we merely -// want to test that the instrumentation does not crash in the browser -// and that gen_ai transactions are sent. - -sentryTest('manual Google GenAI instrumentation sends gen_ai transactions', async ({ getLocalTestUrl, page }) => { - const transactionPromise = waitForTransactionRequest(page, event => { - return !!event.transaction?.includes('gemini-1.5-pro'); - }); - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - - const req = await transactionPromise; - - const eventData = envelopeRequestParser(req); - - // Verify it's a gen_ai transaction - expect(eventData.transaction).toBe('chat gemini-1.5-pro'); - expect(eventData.contexts?.trace?.op).toBe('gen_ai.chat'); - expect(eventData.contexts?.trace?.origin).toBe('auto.ai.google_genai'); - expect(eventData.contexts?.trace?.data).toMatchObject({ - 'gen_ai.operation.name': 'chat', - 'gen_ai.system': 'google_genai', - 'gen_ai.request.model': 'gemini-1.5-pro', - }); -}); - -sentryTest('manual Google GenAI instrumentation sends embeddings transactions', async ({ getLocalTestUrl, page }) => { - const transactionPromise = waitForTransactionRequest(page, event => { - return !!event.transaction?.includes('text-embedding-004'); - }); - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - - const req = await transactionPromise; - - const eventData = envelopeRequestParser(req); - - // Verify it's a gen_ai embeddings transaction - expect(eventData.transaction).toBe('embeddings text-embedding-004'); - expect(eventData.contexts?.trace?.op).toBe('gen_ai.embeddings'); - expect(eventData.contexts?.trace?.origin).toBe('auto.ai.google_genai'); - expect(eventData.contexts?.trace?.data).toMatchObject({ - 'gen_ai.operation.name': 'embeddings', - 'gen_ai.system': 'google_genai', - 'gen_ai.request.model': 'text-embedding-004', - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langchain/init.js b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langchain/init.js deleted file mode 100644 index d90a3acf6157..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langchain/init.js +++ /dev/null @@ -1,9 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - tracesSampleRate: 1, - debug: true, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langchain/mocks.js b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langchain/mocks.js deleted file mode 100644 index 4661e66f5652..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langchain/mocks.js +++ /dev/null @@ -1,94 +0,0 @@ -// Mock LangChain Embeddings for browser testing -export class MockOpenAIEmbeddings { - constructor(params) { - this.model = params.model; - this.dimensions = params.dimensions; - } - - async embedQuery(_text) { - await new Promise(resolve => setTimeout(resolve, 10)); - return [0.1, 0.2, 0.3]; - } - - async embedDocuments(documents) { - await new Promise(resolve => setTimeout(resolve, 10)); - return documents.map(() => [0.1, 0.2, 0.3]); - } -} - -// Mock LangChain Chat Model for browser testing -export class MockChatAnthropic { - constructor(params) { - this._model = params.model; - this._temperature = params.temperature; - this._maxTokens = params.maxTokens; - } - - async invoke(messages, config = { callbacks: [] }) { - const callbacks = config.callbacks; - const runId = 'mock-run-id-123'; - - const invocationParams = { - model: this._model, - temperature: this._temperature, - max_tokens: this._maxTokens, - }; - - const serialized = { - lc: 1, - type: 'constructor', - id: ['langchain', 'anthropic', 'anthropic'], - kwargs: invocationParams, - }; - - // Call handleChatModelStart - for (const callback of callbacks) { - if (callback.handleChatModelStart) { - await callback.handleChatModelStart( - serialized, - messages, - runId, - undefined, - undefined, - { invocation_params: invocationParams }, - { ls_model_name: this._model, ls_provider: 'anthropic' }, - ); - } - } - - // Simulate processing time - await new Promise(resolve => setTimeout(resolve, 10)); - - // Create mock result - const result = { - generations: [ - [ - { - text: 'Mock response from Anthropic!', - generationInfo: { - finish_reason: 'stop', - }, - }, - ], - ], - llmOutput: { - tokenUsage: { - promptTokens: 10, - completionTokens: 15, - totalTokens: 25, - }, - model_name: this._model, - id: 'msg_mock123', - }, - }; - - // Call handleLLMEnd - for (const callback of callbacks) { - if (callback.handleLLMEnd) { - await callback.handleLLMEnd(result, runId); - } - } - - return result; - } -} diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langchain/subject.js b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langchain/subject.js deleted file mode 100644 index 73e62ab18516..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langchain/subject.js +++ /dev/null @@ -1,31 +0,0 @@ -import { createLangChainCallbackHandler } from '@sentry/browser'; -import { instrumentLangChainEmbeddings } from '@sentry/browser'; -import { MockChatAnthropic, MockOpenAIEmbeddings } from './mocks.js'; - -const callbackHandler = createLangChainCallbackHandler({ - recordInputs: false, - recordOutputs: false, -}); - -const chatModel = new MockChatAnthropic({ - model: 'claude-3-haiku-20240307', - temperature: 0.7, - maxTokens: 100, -}); - -// Test that manual instrumentation doesn't crash the browser -// The instrumentation automatically creates spans -// We can provide callbacks in the config object:https://docs.langchain.com/oss/python/langchain/models#invocation-config -const response = await chatModel.invoke('What is the capital of France?', { - callbacks: [callbackHandler], -}); - -console.log('Received response', response); - -// Test embeddings instrumentation -const embeddings = instrumentLangChainEmbeddings( - new MockOpenAIEmbeddings({ model: 'text-embedding-3-small', dimensions: 1536 }), -); - -const embedding = await embeddings.embedQuery('Hello world'); -console.log('Received embedding', embedding); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langchain/test.ts b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langchain/test.ts deleted file mode 100644 index e60f53b063a2..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langchain/test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { envelopeRequestParser, waitForTransactionRequest } from '../../../../utils/helpers'; - -// These tests are not exhaustive because the instrumentation is -// already tested in the node integration tests and we merely -// want to test that the instrumentation does not crash in the browser -// and that gen_ai transactions are sent. - -sentryTest('manual LangChain instrumentation sends gen_ai transactions', async ({ getLocalTestUrl, page }) => { - const transactionPromise = waitForTransactionRequest(page, event => { - return !!event.transaction?.includes('claude-3-haiku-20240307'); - }); - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - - const req = await transactionPromise; - - const eventData = envelopeRequestParser(req); - - // Verify it's a gen_ai transaction - expect(eventData.transaction).toBe('chat claude-3-haiku-20240307'); - expect(eventData.contexts?.trace?.op).toBe('gen_ai.chat'); - expect(eventData.contexts?.trace?.origin).toBe('auto.ai.langchain'); - expect(eventData.contexts?.trace?.data).toMatchObject({ - 'gen_ai.operation.name': 'chat', - 'gen_ai.system': 'anthropic', - 'gen_ai.request.model': 'claude-3-haiku-20240307', - 'gen_ai.request.temperature': 0.7, - 'gen_ai.response.model': 'claude-3-haiku-20240307', - 'gen_ai.response.id': 'msg_mock123', - 'gen_ai.usage.input_tokens': 10, - 'gen_ai.usage.output_tokens': 15, - 'gen_ai.usage.total_tokens': 25, - }); -}); - -sentryTest( - 'manual LangChain embeddings instrumentation sends gen_ai transactions', - async ({ getLocalTestUrl, page }) => { - const transactionPromise = waitForTransactionRequest(page, event => { - return !!event.transaction?.includes('text-embedding-3-small'); - }); - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - - const req = await transactionPromise; - - const eventData = envelopeRequestParser(req); - - expect(eventData.transaction).toBe('embeddings text-embedding-3-small'); - expect(eventData.contexts?.trace?.op).toBe('gen_ai.embeddings'); - expect(eventData.contexts?.trace?.origin).toBe('auto.ai.langchain'); - expect(eventData.contexts?.trace?.data).toMatchObject({ - 'gen_ai.operation.name': 'embeddings', - 'gen_ai.system': 'openai', - 'gen_ai.request.model': 'text-embedding-3-small', - 'gen_ai.request.dimensions': 1536, - }); - }, -); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langgraph/init.js b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langgraph/init.js deleted file mode 100644 index d90a3acf6157..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langgraph/init.js +++ /dev/null @@ -1,9 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - tracesSampleRate: 1, - debug: true, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langgraph/mocks.js b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langgraph/mocks.js deleted file mode 100644 index 54792b827a43..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langgraph/mocks.js +++ /dev/null @@ -1,29 +0,0 @@ -// Mock LangGraph graph for browser testing -export class MockStateGraph { - compile(options = {}) { - const compiledGraph = { - name: options.name, - graph_name: options.name, - lc_kwargs: { - name: options.name, - }, - builder: { - nodes: {}, - }, - invoke: async input => { - const messages = input?.messages; - return { - messages: [ - ...messages, - { - role: 'assistant', - content: 'Mock response from LangGraph', - }, - ], - }; - }, - }; - - return compiledGraph; - } -} diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langgraph/subject.js b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langgraph/subject.js deleted file mode 100644 index 70741f5d111f..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langgraph/subject.js +++ /dev/null @@ -1,16 +0,0 @@ -import { MockStateGraph } from './mocks.js'; -import { instrumentLangGraph } from '@sentry/browser'; - -// Test that manual instrumentation doesn't crash the browser -// The instrumentation automatically creates spans -// Test both agent creation and invocation - -const graph = new MockStateGraph(); -instrumentLangGraph(graph, { recordInputs: false, recordOutputs: false }); -const compiledGraph = graph.compile({ name: 'mock-graph' }); - -const response = await compiledGraph.invoke({ - messages: [{ role: 'user', content: 'What is the capital of France?' }], -}); - -console.log('Received response', response); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langgraph/test.ts b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langgraph/test.ts deleted file mode 100644 index 1feabd48c8d2..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/langgraph/test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { envelopeRequestParser, waitForTransactionRequest } from '../../../../utils/helpers'; - -// These tests are not exhaustive because the instrumentation is -// already tested in the node integration tests and we merely -// want to test that the instrumentation does not crash in the browser -// and that gen_ai transactions are sent. - -sentryTest('manual LangGraph instrumentation sends gen_ai transactions', async ({ getLocalTestUrl, page }) => { - const createTransactionPromise = waitForTransactionRequest(page, event => { - return !!event.transaction?.includes('create_agent mock-graph'); - }); - - const invokeTransactionPromise = waitForTransactionRequest(page, event => { - return !!event.transaction?.includes('invoke_agent mock-graph'); - }); - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - - const createReq = await createTransactionPromise; - const invokeReq = await invokeTransactionPromise; - - const createEventData = envelopeRequestParser(createReq); - const invokeEventData = envelopeRequestParser(invokeReq); - - // Verify create_agent transaction - expect(createEventData.transaction).toBe('create_agent mock-graph'); - expect(createEventData.contexts?.trace?.op).toBe('gen_ai.create_agent'); - expect(createEventData.contexts?.trace?.origin).toBe('auto.ai.langgraph'); - expect(createEventData.contexts?.trace?.data).toMatchObject({ - 'gen_ai.operation.name': 'create_agent', - 'gen_ai.agent.name': 'mock-graph', - }); - - // Verify invoke_agent transaction - expect(invokeEventData.transaction).toBe('invoke_agent mock-graph'); - expect(invokeEventData.contexts?.trace?.op).toBe('gen_ai.invoke_agent'); - expect(invokeEventData.contexts?.trace?.origin).toBe('auto.ai.langgraph'); - expect(invokeEventData.contexts?.trace?.data).toMatchObject({ - 'gen_ai.operation.name': 'invoke_agent', - 'gen_ai.agent.name': 'mock-graph', - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/openai/init.js b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/openai/init.js deleted file mode 100644 index d90a3acf6157..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/openai/init.js +++ /dev/null @@ -1,9 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - tracesSampleRate: 1, - debug: true, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/openai/mocks.js b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/openai/mocks.js deleted file mode 100644 index a1fe56dd30c2..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/openai/mocks.js +++ /dev/null @@ -1,47 +0,0 @@ -// Mock OpenAI client for browser testing -export class MockOpenAi { - constructor(config) { - this.apiKey = config.apiKey; - - this.chat = { - completions: { - create: async (...args) => { - const params = args[0]; - // Simulate processing time - await new Promise(resolve => setTimeout(resolve, 10)); - - if (params.model === 'error-model') { - const error = new Error('Model not found'); - error.status = 404; - error.headers = { 'x-request-id': 'mock-request-123' }; - throw error; - } - - const response = { - id: 'chatcmpl-mock123', - object: 'chat.completion', - created: 1677652288, - model: params.model, - system_fingerprint: 'fp_44709d6fcb', - choices: [ - { - index: 0, - message: { - role: 'assistant', - content: 'Hello from OpenAI mock!', - }, - finish_reason: 'stop', - }, - ], - usage: { - prompt_tokens: 10, - completion_tokens: 15, - total_tokens: 25, - }, - }; - return response; - }, - }, - }; - } -} diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/openai/subject.js b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/openai/subject.js deleted file mode 100644 index aadc2864ceee..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/openai/subject.js +++ /dev/null @@ -1,22 +0,0 @@ -import { instrumentOpenAiClient } from '@sentry/browser'; -import { MockOpenAi } from './mocks.js'; - -const mockClient = new MockOpenAi({ - apiKey: 'mock-api-key', -}); - -const client = instrumentOpenAiClient(mockClient); - -// Test that manual instrumentation doesn't crash the browser -// The instrumentation automatically creates spans -const response = await client.chat.completions.create({ - model: 'gpt-3.5-turbo', - messages: [ - { role: 'system', content: 'You are a helpful assistant.' }, - { role: 'user', content: 'What is the capital of France?' }, - ], - temperature: 0.7, - max_tokens: 100, -}); - -console.log('Received response', response); diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/openai/test.ts b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/openai/test.ts deleted file mode 100644 index c71c0786ff96..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/openai/test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { envelopeRequestParser, waitForTransactionRequest } from '../../../../utils/helpers'; - -// These tests are not exhaustive because the instrumentation is -// already tested in the node integration tests and we merely -// want to test that the instrumentation does not crash in the browser -// and that gen_ai transactions are sent. - -sentryTest('manual OpenAI instrumentation sends gen_ai transactions', async ({ getLocalTestUrl, page }) => { - const transactionPromise = waitForTransactionRequest(page, event => { - return !!event.transaction?.includes('gpt-3.5-turbo'); - }); - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - - const req = await transactionPromise; - - const eventData = envelopeRequestParser(req); - - // Verify it's a gen_ai transaction - expect(eventData.transaction).toBe('chat gpt-3.5-turbo'); - expect(eventData.contexts?.trace?.op).toBe('gen_ai.chat'); - expect(eventData.contexts?.trace?.origin).toBe('auto.ai.openai'); - expect(eventData.contexts?.trace?.data).toMatchObject({ - 'gen_ai.operation.name': 'chat', - 'gen_ai.system': 'openai', - 'gen_ai.request.model': 'gpt-3.5-turbo', - 'gen_ai.request.temperature': 0.7, - 'gen_ai.response.model': 'gpt-3.5-turbo', - 'gen_ai.response.id': 'chatcmpl-mock123', - 'gen_ai.usage.input_tokens': 10, - 'gen_ai.usage.output_tokens': 15, - 'gen_ai.usage.total_tokens': 25, - }); -}); diff --git a/dev-packages/browser-integration-tests/utils/generatePlugin.ts b/dev-packages/browser-integration-tests/utils/generatePlugin.ts index 61ccbf6a39d8..084629a9d437 100644 --- a/dev-packages/browser-integration-tests/utils/generatePlugin.ts +++ b/dev-packages/browser-integration-tests/utils/generatePlugin.ts @@ -37,12 +37,6 @@ const IMPORTED_INTEGRATION_CDN_BUNDLE_PATHS: Record = { moduleMetadataIntegration: 'modulemetadata', graphqlClientIntegration: 'graphqlclient', browserProfilingIntegration: 'browserprofiling', - instrumentAnthropicAiClient: 'instrumentanthropicaiclient', - instrumentOpenAiClient: 'instrumentopenaiclient', - instrumentGoogleGenAIClient: 'instrumentgooglegenaiclient', - instrumentLangGraph: 'instrumentlanggraph', - createLangChainCallbackHandler: 'createlangchaincallbackhandler', - instrumentLangChainEmbeddings: 'instrumentlangchainembeddings', // technically, this is not an integration, but let's add it anyway for simplicity makeMultiplexedTransport: 'multiplexedtransport', }; diff --git a/packages/browser/rollup.bundle.config.mjs b/packages/browser/rollup.bundle.config.mjs index 2a70d25dac77..3306278fe2ae 100644 --- a/packages/browser/rollup.bundle.config.mjs +++ b/packages/browser/rollup.bundle.config.mjs @@ -13,12 +13,6 @@ const reexportedPluggableIntegrationFiles = [ 'modulemetadata', 'graphqlclient', 'spotlight', - 'instrumentanthropicaiclient', - 'instrumentopenaiclient', - 'instrumentgooglegenaiclient', - 'instrumentlanggraph', - 'createlangchaincallbackhandler', - 'instrumentlangchainembeddings', ]; browserPluggableIntegrationFiles.forEach(integrationName => { diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index a9e7b568ea97..46fec05bbb0a 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -72,13 +72,6 @@ export { zodErrorsIntegration, thirdPartyErrorFilterIntegration, featureFlagsIntegration, - instrumentAnthropicAiClient, - instrumentOpenAiClient, - instrumentGoogleGenAIClient, - instrumentLangGraph, - instrumentCreateReactAgent, - createLangChainCallbackHandler, - instrumentLangChainEmbeddings, logger, } from '@sentry/core/browser'; export type { Span, FeatureFlagsIntegration } from '@sentry/core/browser'; diff --git a/packages/browser/src/integrations-bundle/index.createlangchaincallbackhandler.ts b/packages/browser/src/integrations-bundle/index.createlangchaincallbackhandler.ts deleted file mode 100644 index a7bdee8b6693..000000000000 --- a/packages/browser/src/integrations-bundle/index.createlangchaincallbackhandler.ts +++ /dev/null @@ -1 +0,0 @@ -export { createLangChainCallbackHandler } from '@sentry/core/browser'; diff --git a/packages/browser/src/integrations-bundle/index.instrumentanthropicaiclient.ts b/packages/browser/src/integrations-bundle/index.instrumentanthropicaiclient.ts deleted file mode 100644 index ab7b3157953a..000000000000 --- a/packages/browser/src/integrations-bundle/index.instrumentanthropicaiclient.ts +++ /dev/null @@ -1 +0,0 @@ -export { instrumentAnthropicAiClient } from '@sentry/core/browser'; diff --git a/packages/browser/src/integrations-bundle/index.instrumentgooglegenaiclient.ts b/packages/browser/src/integrations-bundle/index.instrumentgooglegenaiclient.ts deleted file mode 100644 index 9e8316dc7e43..000000000000 --- a/packages/browser/src/integrations-bundle/index.instrumentgooglegenaiclient.ts +++ /dev/null @@ -1 +0,0 @@ -export { instrumentGoogleGenAIClient } from '@sentry/core/browser'; diff --git a/packages/browser/src/integrations-bundle/index.instrumentlangchainembeddings.ts b/packages/browser/src/integrations-bundle/index.instrumentlangchainembeddings.ts deleted file mode 100644 index b8b733fc9907..000000000000 --- a/packages/browser/src/integrations-bundle/index.instrumentlangchainembeddings.ts +++ /dev/null @@ -1 +0,0 @@ -export { instrumentLangChainEmbeddings } from '@sentry/core/browser'; diff --git a/packages/browser/src/integrations-bundle/index.instrumentlanggraph.ts b/packages/browser/src/integrations-bundle/index.instrumentlanggraph.ts deleted file mode 100644 index e54333eed24a..000000000000 --- a/packages/browser/src/integrations-bundle/index.instrumentlanggraph.ts +++ /dev/null @@ -1 +0,0 @@ -export { instrumentLangGraph } from '@sentry/core/browser'; diff --git a/packages/browser/src/integrations-bundle/index.instrumentopenaiclient.ts b/packages/browser/src/integrations-bundle/index.instrumentopenaiclient.ts deleted file mode 100644 index 813ad7b2a9fb..000000000000 --- a/packages/browser/src/integrations-bundle/index.instrumentopenaiclient.ts +++ /dev/null @@ -1 +0,0 @@ -export { instrumentOpenAiClient } from '@sentry/core/browser'; diff --git a/packages/browser/src/utils/lazyLoadIntegration.ts b/packages/browser/src/utils/lazyLoadIntegration.ts index f348681adba5..6b93c3f3a00b 100644 --- a/packages/browser/src/utils/lazyLoadIntegration.ts +++ b/packages/browser/src/utils/lazyLoadIntegration.ts @@ -23,12 +23,6 @@ const LAZY_LOADABLE_NAMES = [ 'rewriteFramesIntegration', 'browserProfilingIntegration', 'moduleMetadataIntegration', - 'instrumentAnthropicAiClient', - 'instrumentOpenAiClient', - 'instrumentGoogleGenAIClient', - 'instrumentLangGraph', - 'createLangChainCallbackHandler', - 'instrumentLangChainEmbeddings', ] as const; type ElementOf = T[number]; diff --git a/packages/core/src/server-exports.ts b/packages/core/src/server-exports.ts index 21469232c101..63a7b1b0398f 100644 --- a/packages/core/src/server-exports.ts +++ b/packages/core/src/server-exports.ts @@ -53,3 +53,77 @@ export type { HttpServerResponse, HttpModuleExport, } from './integrations/http/types'; + +// AI instrumentation is only supported in server runtimes, so these exports are kept out of the browser entry to +// avoid shipping the AI tracing code in browser bundles. +export { addVercelAiProcessors, getProviderMetadataAttributes } from './tracing/vercel-ai'; +export { getTruncatedJsonString, shouldEnableTruncation, resolveAIRecordingOptions } from './tracing/ai/utils'; +export { + GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, + GEN_AI_REQUEST_MODEL_ATTRIBUTE, + GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE, +} from './tracing/ai/gen-ai-attributes'; +export { _INTERNAL_getSpanContextForToolCallId, _INTERNAL_cleanupToolCallSpanContext } from './tracing/vercel-ai/utils'; +export { toolCallSpanContextMap as _INTERNAL_toolCallSpanContextMap } from './tracing/vercel-ai/constants'; +export { + instrumentOpenAiClient, + extractRequestAttributes as extractOpenAiRequestAttributes, + addRequestAttributes as addOpenAiRequestAttributes, +} from './tracing/openai'; +export { + addResponseAttributes as addOpenAiResponseAttributes, + extractRequestParameters as extractOpenAiRequestParameters, +} from './tracing/openai/utils'; +export { instrumentStream as instrumentOpenAiStream } from './tracing/openai/streaming'; +export { OPENAI_INTEGRATION_NAME } from './tracing/openai/constants'; +export { + instrumentAnthropicAiClient, + extractRequestAttributes as extractAnthropicRequestAttributes, + addPrivateRequestAttributes as addAnthropicRequestAttributes, + addResponseAttributes as addAnthropicResponseAttributes, +} from './tracing/anthropic-ai'; +export { instrumentAsyncIterableStream, instrumentMessageStream } from './tracing/anthropic-ai/streaming'; +export { ANTHROPIC_AI_INTEGRATION_NAME } from './tracing/anthropic-ai/constants'; +export { + instrumentGoogleGenAIClient, + extractRequestAttributes as extractGoogleGenAIRequestAttributes, + addPrivateRequestAttributes as addGoogleGenAIRequestAttributes, + addResponseAttributes as addGoogleGenAIResponseAttributes, +} from './tracing/google-genai'; +export { instrumentStream as instrumentGoogleGenAIStream } from './tracing/google-genai/streaming'; +export { GOOGLE_GENAI_INTEGRATION_NAME } from './tracing/google-genai/constants'; +export type { GoogleGenAIResponse } from './tracing/google-genai/types'; +export { createLangChainCallbackHandler, instrumentLangChainEmbeddings } from './tracing/langchain'; +export { _INTERNAL_getLangChainEmbeddingsSpanOptions } from './tracing/langchain/embeddings'; +export { _INTERNAL_mergeLangChainCallbackHandler } from './tracing/langchain/utils'; +export { LANGCHAIN_INTEGRATION_NAME } from './tracing/langchain/constants'; +export type { LangChainOptions, LangChainIntegration } from './tracing/langchain/types'; +export { + instrumentStateGraphCompile, + instrumentCreateReactAgent, + instrumentLangGraph, + instrumentCompiledGraphInvoke, + _INTERNAL_getLangGraphCreateAgentSpanOptions, +} from './tracing/langgraph'; +export { wrapToolsWithSpans, extractLLMFromParams, extractAgentNameFromParams } from './tracing/langgraph/utils'; +export { LANGGRAPH_INTEGRATION_NAME } from './tracing/langgraph/constants'; +export type { LangGraphOptions, LangGraphIntegration, CompiledGraph } from './tracing/langgraph/types'; +export { instrumentWorkersAiClient } from './tracing/workers-ai'; +export type { WorkersAiClient, WorkersAiOptions } from './tracing/workers-ai/types'; +// eslint-disable-next-line typescript/no-deprecated +export type { OpenAiClient, OpenAiOptions, InstrumentedMethod } from './tracing/openai/types'; +export type { + AnthropicAiClient, + AnthropicAiOptions, + // eslint-disable-next-line typescript/no-deprecated + AnthropicAiInstrumentedMethod, + AnthropicAiResponse, +} from './tracing/anthropic-ai/types'; +export type { + GoogleGenAIClient, + GoogleGenAIChat, + GoogleGenAIOptions, + GoogleGenAIInstrumentedMethod, +} from './tracing/google-genai/types'; +// eslint-disable-next-line typescript/no-deprecated +export type { GoogleGenAIIstrumentedMethod } from './tracing/google-genai/types'; diff --git a/packages/core/src/shared-exports.ts b/packages/core/src/shared-exports.ts index 983367c882f9..eae0361ee6dd 100644 --- a/packages/core/src/shared-exports.ts +++ b/packages/core/src/shared-exports.ts @@ -178,77 +178,6 @@ export { export * as metrics from './metrics/public-api'; export type { MetricOptions } from './metrics/public-api'; export { createConsolaReporter } from './integrations/consola'; -export { addVercelAiProcessors, getProviderMetadataAttributes } from './tracing/vercel-ai'; -export { getTruncatedJsonString, shouldEnableTruncation, resolveAIRecordingOptions } from './tracing/ai/utils'; -export { - GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, - GEN_AI_REQUEST_MODEL_ATTRIBUTE, - GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE, -} from './tracing/ai/gen-ai-attributes'; -export { _INTERNAL_getSpanContextForToolCallId, _INTERNAL_cleanupToolCallSpanContext } from './tracing/vercel-ai/utils'; -export { toolCallSpanContextMap as _INTERNAL_toolCallSpanContextMap } from './tracing/vercel-ai/constants'; -export { - instrumentOpenAiClient, - extractRequestAttributes as extractOpenAiRequestAttributes, - addRequestAttributes as addOpenAiRequestAttributes, -} from './tracing/openai'; -export { - addResponseAttributes as addOpenAiResponseAttributes, - extractRequestParameters as extractOpenAiRequestParameters, -} from './tracing/openai/utils'; -export { instrumentStream as instrumentOpenAiStream } from './tracing/openai/streaming'; -export { OPENAI_INTEGRATION_NAME } from './tracing/openai/constants'; -export { - instrumentAnthropicAiClient, - extractRequestAttributes as extractAnthropicRequestAttributes, - addPrivateRequestAttributes as addAnthropicRequestAttributes, - addResponseAttributes as addAnthropicResponseAttributes, -} from './tracing/anthropic-ai'; -export { instrumentAsyncIterableStream, instrumentMessageStream } from './tracing/anthropic-ai/streaming'; -export { ANTHROPIC_AI_INTEGRATION_NAME } from './tracing/anthropic-ai/constants'; -export { - instrumentGoogleGenAIClient, - extractRequestAttributes as extractGoogleGenAIRequestAttributes, - addPrivateRequestAttributes as addGoogleGenAIRequestAttributes, - addResponseAttributes as addGoogleGenAIResponseAttributes, -} from './tracing/google-genai'; -export { instrumentStream as instrumentGoogleGenAIStream } from './tracing/google-genai/streaming'; -export { GOOGLE_GENAI_INTEGRATION_NAME } from './tracing/google-genai/constants'; -export type { GoogleGenAIResponse } from './tracing/google-genai/types'; -export { createLangChainCallbackHandler, instrumentLangChainEmbeddings } from './tracing/langchain'; -export { _INTERNAL_getLangChainEmbeddingsSpanOptions } from './tracing/langchain/embeddings'; -export { _INTERNAL_mergeLangChainCallbackHandler } from './tracing/langchain/utils'; -export { LANGCHAIN_INTEGRATION_NAME } from './tracing/langchain/constants'; -export type { LangChainOptions, LangChainIntegration } from './tracing/langchain/types'; -export { - instrumentStateGraphCompile, - instrumentCreateReactAgent, - instrumentLangGraph, - instrumentCompiledGraphInvoke, - _INTERNAL_getLangGraphCreateAgentSpanOptions, -} from './tracing/langgraph'; -export { wrapToolsWithSpans, extractLLMFromParams, extractAgentNameFromParams } from './tracing/langgraph/utils'; -export { LANGGRAPH_INTEGRATION_NAME } from './tracing/langgraph/constants'; -export type { LangGraphOptions, LangGraphIntegration, CompiledGraph } from './tracing/langgraph/types'; -export { instrumentWorkersAiClient } from './tracing/workers-ai'; -export type { WorkersAiClient, WorkersAiOptions } from './tracing/workers-ai/types'; -// eslint-disable-next-line typescript/no-deprecated -export type { OpenAiClient, OpenAiOptions, InstrumentedMethod } from './tracing/openai/types'; -export type { - AnthropicAiClient, - AnthropicAiOptions, - // eslint-disable-next-line typescript/no-deprecated - AnthropicAiInstrumentedMethod, - AnthropicAiResponse, -} from './tracing/anthropic-ai/types'; -export type { - GoogleGenAIClient, - GoogleGenAIChat, - GoogleGenAIOptions, - GoogleGenAIInstrumentedMethod, -} from './tracing/google-genai/types'; -// eslint-disable-next-line typescript/no-deprecated -export type { GoogleGenAIIstrumentedMethod } from './tracing/google-genai/types'; export { SpanBuffer } from './tracing/spans/spanBuffer'; export { hasSpanStreamingEnabled } from './tracing/spans/hasSpanStreamingEnabled'; export { spanStreamingIntegration } from './integrations/spanStreaming';