From 405ab1f3baf183804e2ec804e6e66cbe604aff01 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Mon, 3 Aug 2026 13:27:06 +0200 Subject: [PATCH] ref(anthropic)!: Move Anthropic AI integration to `@sentry/server-utils` Move the Anthropic gen-ai instrumentation from `@sentry/core` to `@sentry/server-utils/src/ai/anthropic-ai`. `instrumentAnthropicAiClient` is re-exported by node/cloudflare/vercel-edge from `@sentry/server-utils`, so the platform SDK public API is unchanged. Also makes `SpanStatusType` public on `@sentry/core` (the Anthropic integration is the only AI integration that consumes it). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../suites/tracing/anthropic/test.ts | 2 +- packages/cloudflare/src/index.ts | 3 +-- packages/core/src/server-exports.ts | 15 --------------- packages/core/src/shared-exports.ts | 2 +- .../test/lib/utils/ai/providerSkip.test.ts | 7 ++++--- packages/node/src/index.ts | 3 +-- .../src/ai}/anthropic-ai/constants.ts | 2 +- .../src/ai}/anthropic-ai/index.ts | 19 +++++++++++-------- .../src/ai}/anthropic-ai/streaming.ts | 7 +++---- .../src/ai}/anthropic-ai/types.ts | 0 .../src/ai}/anthropic-ai/utils.ts | 9 +++------ packages/server-utils/src/ai/index.ts | 1 + .../integrations/tracing-channel/anthropic.ts | 19 ++++++++----------- .../integrations/tracing-channel/langchain.ts | 2 +- .../ai}/lib/utils/anthropic-utils.test.ts | 4 ++-- packages/vercel-edge/src/index.ts | 3 +-- 16 files changed, 39 insertions(+), 59 deletions(-) rename packages/{core/src/tracing => server-utils/src/ai}/anthropic-ai/constants.ts (90%) rename packages/{core/src/tracing => server-utils/src/ai}/anthropic-ai/index.ts (96%) rename packages/{core/src/tracing => server-utils/src/ai}/anthropic-ai/streaming.ts (97%) rename packages/{core/src/tracing => server-utils/src/ai}/anthropic-ai/types.ts (100%) rename packages/{core/src/tracing => server-utils/src/ai}/anthropic-ai/utils.ts (90%) rename packages/{core/test => server-utils/test/ai}/lib/utils/anthropic-utils.test.ts (97%) diff --git a/dev-packages/node-integration-tests/suites/tracing/anthropic/test.ts b/dev-packages/node-integration-tests/suites/tracing/anthropic/test.ts index b332f982d997..ebd66be60989 100644 --- a/dev-packages/node-integration-tests/suites/tracing/anthropic/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/anthropic/test.ts @@ -18,7 +18,7 @@ import { GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_TOTAL_TOKENS, } from '@sentry/conventions/attributes'; -import { GEN_AI_REQUEST_STREAM_ATTRIBUTE } from '../../../../../packages/core/src/tracing/ai/gen-ai-attributes'; +import { GEN_AI_REQUEST_STREAM_ATTRIBUTE } from '../../../../../packages/server-utils/src/ai/core/gen-ai-attributes'; import { getStringAttributeValue, isOrchestrionEnabled } from '../../../utils'; import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts index cd56bbdb5b21..72cb9ff94c02 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/index.ts @@ -77,7 +77,6 @@ export { // eslint-disable-next-line typescript/no-deprecated inboundFiltersIntegration, instrumentGoogleGenAIClient, - instrumentAnthropicAiClient, eventFiltersIntegration, linkedErrorsIntegration, requestDataIntegration, @@ -127,7 +126,7 @@ export { httpServerIntegration } from './integrations/httpServer'; export { fetchIntegration } from './integrations/fetch'; export { spotlightIntegration } from './integrations/spotlight'; export { vercelAIIntegration } from './integrations/tracing/vercelai'; -export { prismaIntegration, instrumentOpenAiClient } from '@sentry/server-utils'; +export { prismaIntegration, instrumentOpenAiClient, instrumentAnthropicAiClient } from '@sentry/server-utils'; // eslint-disable-next-line typescript/no-deprecated export { instrumentD1WithSentry } from './instrumentations/worker/instrumentD1'; diff --git a/packages/core/src/server-exports.ts b/packages/core/src/server-exports.ts index 2d14997d1449..67a3c9f2fc9b 100644 --- a/packages/core/src/server-exports.ts +++ b/packages/core/src/server-exports.ts @@ -60,14 +60,6 @@ export { addVercelAiProcessors, getProviderMetadataAttributes } from './tracing/ export { getTruncatedJsonString, shouldEnableTruncation, resolveAIRecordingOptions } from './tracing/ai/utils'; export { _INTERNAL_getSpanContextForToolCallId, _INTERNAL_cleanupToolCallSpanContext } from './tracing/vercel-ai/utils'; export { toolCallSpanContextMap as _INTERNAL_toolCallSpanContextMap } from './tracing/vercel-ai/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, @@ -93,13 +85,6 @@ 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'; -export type { - AnthropicAiClient, - AnthropicAiOptions, - // eslint-disable-next-line typescript/no-deprecated - AnthropicAiInstrumentedMethod, - AnthropicAiResponse, -} from './tracing/anthropic-ai/types'; export type { GoogleGenAIClient, GoogleGenAIChat, diff --git a/packages/core/src/shared-exports.ts b/packages/core/src/shared-exports.ts index 03dc9635ff11..4034ff73d37d 100644 --- a/packages/core/src/shared-exports.ts +++ b/packages/core/src/shared-exports.ts @@ -466,7 +466,7 @@ export type { SerializedStreamedSpanContainer, StreamedSpanJSON, } from './types/span'; -export type { SpanStatus } from './types/spanStatus'; +export type { SpanStatus, SpanStatusType } from './types/spanStatus'; export type { Log, LogSeverityLevel } from './types/log'; export type { SpanLink } from './types/link'; export type { diff --git a/packages/core/test/lib/utils/ai/providerSkip.test.ts b/packages/core/test/lib/utils/ai/providerSkip.test.ts index 99ec76c970d6..de39778a2b5e 100644 --- a/packages/core/test/lib/utils/ai/providerSkip.test.ts +++ b/packages/core/test/lib/utils/ai/providerSkip.test.ts @@ -3,11 +3,12 @@ import { _INTERNAL_clearAiProviderSkips, _INTERNAL_shouldSkipAiProviderWrapping, _INTERNAL_skipAiProviderWrapping, - ANTHROPIC_AI_INTEGRATION_NAME, - GOOGLE_GENAI_INTEGRATION_NAME, - OPENAI_INTEGRATION_NAME, } from '../../../../src/index'; +const OPENAI_INTEGRATION_NAME = 'OpenAI'; +const ANTHROPIC_AI_INTEGRATION_NAME = 'Anthropic_AI'; +const GOOGLE_GENAI_INTEGRATION_NAME = 'Google_GenAI'; + describe('AI Provider Skip', () => { beforeEach(() => { _INTERNAL_clearAiProviderSkips(); diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 1860a0e4f1b4..a1bdec11735c 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -30,7 +30,7 @@ export { vercelAiIntegration as vercelAIIntegration, } from '@sentry/server-utils/orchestrion'; export { redisIntegration } from './integrations/tracing/redis'; -export { prismaIntegration, instrumentOpenAiClient } from '@sentry/server-utils'; +export { prismaIntegration, instrumentOpenAiClient, instrumentAnthropicAiClient } from '@sentry/server-utils'; export { setupHapiErrorHandler } from './integrations/tracing/hapi'; export { setupKoaErrorHandler } from './integrations/tracing/koa'; export { @@ -133,7 +133,6 @@ export { updateSpanName, supabaseIntegration, instrumentSupabaseClient, - instrumentAnthropicAiClient, instrumentGoogleGenAIClient, zodErrorsIntegration, profiler, diff --git a/packages/core/src/tracing/anthropic-ai/constants.ts b/packages/server-utils/src/ai/anthropic-ai/constants.ts similarity index 90% rename from packages/core/src/tracing/anthropic-ai/constants.ts rename to packages/server-utils/src/ai/anthropic-ai/constants.ts index c07258373163..3488f495a167 100644 --- a/packages/core/src/tracing/anthropic-ai/constants.ts +++ b/packages/server-utils/src/ai/anthropic-ai/constants.ts @@ -1,4 +1,4 @@ -import type { InstrumentedMethodRegistry } from '../ai/utils'; +import type { InstrumentedMethodRegistry } from '../core/utils'; export const ANTHROPIC_AI_INTEGRATION_NAME = 'Anthropic_AI' as const; diff --git a/packages/core/src/tracing/anthropic-ai/index.ts b/packages/server-utils/src/ai/anthropic-ai/index.ts similarity index 96% rename from packages/core/src/tracing/anthropic-ai/index.ts rename to packages/server-utils/src/ai/anthropic-ai/index.ts index 00e638c6e516..d16d02cf8d4e 100644 --- a/packages/core/src/tracing/anthropic-ai/index.ts +++ b/packages/server-utils/src/ai/anthropic-ai/index.ts @@ -1,9 +1,12 @@ /* eslint-disable typescript-eslint/no-deprecated */ -import { captureException } from '../../exports'; -import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../semanticAttributes'; -import { SPAN_STATUS_ERROR } from '../../tracing'; -import { startSpan, startSpanManual } from '../../tracing/trace'; -import type { Span, SpanAttributeValue } from '../../types/span'; +import { + captureException, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, + SPAN_STATUS_ERROR, + startSpan, + startSpanManual, +} from '@sentry/core'; +import type { Span, SpanAttributeValue } from '@sentry/core'; import { GEN_AI_OPERATION_NAME, GEN_AI_PROMPT, @@ -20,14 +23,14 @@ import { GEN_AI_RESPONSE_TOOL_CALLS, GEN_AI_SYSTEM, } from '@sentry/conventions/attributes'; -import { GEN_AI_REQUEST_STREAM_ATTRIBUTE } from '../ai/gen-ai-attributes'; -import type { InstrumentedMethodEntry } from '../ai/utils'; +import { GEN_AI_REQUEST_STREAM_ATTRIBUTE } from '../core/gen-ai-attributes'; +import type { InstrumentedMethodEntry } from '../core/utils'; import { resolveAIRecordingOptions, setTokenUsageAttributes, shouldEnableTruncation, wrapPromiseWithMethods, -} from '../ai/utils'; +} from '../core/utils'; import { ANTHROPIC_METHOD_REGISTRY } from './constants'; import { instrumentAsyncIterableStream, instrumentMessageStream } from './streaming'; import type { AnthropicAiOptions, AnthropicAiResponse, AnthropicAiStreamingEvent, ContentBlock } from './types'; diff --git a/packages/core/src/tracing/anthropic-ai/streaming.ts b/packages/server-utils/src/ai/anthropic-ai/streaming.ts similarity index 97% rename from packages/core/src/tracing/anthropic-ai/streaming.ts rename to packages/server-utils/src/ai/anthropic-ai/streaming.ts index 68bc9a281001..fcbafa1a456f 100644 --- a/packages/core/src/tracing/anthropic-ai/streaming.ts +++ b/packages/server-utils/src/ai/anthropic-ai/streaming.ts @@ -1,7 +1,6 @@ -import { captureException } from '../../exports'; -import { SPAN_STATUS_ERROR } from '../../tracing'; -import type { Span } from '../../types/span'; -import { endStreamSpan } from '../ai/utils'; +import { captureException, SPAN_STATUS_ERROR } from '@sentry/core'; +import type { Span } from '@sentry/core'; +import { endStreamSpan } from '../core/utils'; import type { AnthropicAiStreamingEvent } from './types'; import { mapAnthropicErrorToStatusMessage } from './utils'; diff --git a/packages/core/src/tracing/anthropic-ai/types.ts b/packages/server-utils/src/ai/anthropic-ai/types.ts similarity index 100% rename from packages/core/src/tracing/anthropic-ai/types.ts rename to packages/server-utils/src/ai/anthropic-ai/types.ts diff --git a/packages/core/src/tracing/anthropic-ai/utils.ts b/packages/server-utils/src/ai/anthropic-ai/utils.ts similarity index 90% rename from packages/core/src/tracing/anthropic-ai/utils.ts rename to packages/server-utils/src/ai/anthropic-ai/utils.ts index 98a1a7f9033f..f1c20787ee59 100644 --- a/packages/core/src/tracing/anthropic-ai/utils.ts +++ b/packages/server-utils/src/ai/anthropic-ai/utils.ts @@ -1,10 +1,7 @@ -import { captureException } from '../../exports'; -import { SPAN_STATUS_ERROR } from '../../tracing'; -import type { Span } from '../../types/span'; -import type { SpanStatusType } from '../../types/spanStatus'; +import { captureException, SPAN_STATUS_ERROR, stringify } from '@sentry/core'; +import type { Span, SpanStatusType } from '@sentry/core'; import { GEN_AI_INPUT_MESSAGES, GEN_AI_SYSTEM_INSTRUCTIONS } from '@sentry/conventions/attributes'; -import { extractSystemInstructions, getTruncatedJsonString } from '../ai/utils'; -import { stringify } from '../../utils/string'; +import { extractSystemInstructions, getTruncatedJsonString } from '../core/utils'; import type { AnthropicAiResponse } from './types'; /** diff --git a/packages/server-utils/src/ai/index.ts b/packages/server-utils/src/ai/index.ts index 6866cffe0669..3931cb532c6e 100644 --- a/packages/server-utils/src/ai/index.ts +++ b/packages/server-utils/src/ai/index.ts @@ -5,3 +5,4 @@ */ export { instrumentOpenAiClient } from './openai'; +export { instrumentAnthropicAiClient } from './anthropic-ai'; diff --git a/packages/server-utils/src/integrations/tracing-channel/anthropic.ts b/packages/server-utils/src/integrations/tracing-channel/anthropic.ts index 99ac98c5c51e..04a62ecd2286 100644 --- a/packages/server-utils/src/integrations/tracing-channel/anthropic.ts +++ b/packages/server-utils/src/integrations/tracing-channel/anthropic.ts @@ -1,19 +1,16 @@ import { GEN_AI_REQUEST_MODEL } from '@sentry/conventions/attributes'; import * as diagnosticsChannel from 'node:diagnostics_channel'; -import type { AnthropicAiOptions, AnthropicAiResponse, IntegrationFn, Span, SpanAttributeValue } from '@sentry/core'; +import type { IntegrationFn, Span, SpanAttributeValue } from '@sentry/core'; import { _INTERNAL_shouldSkipAiProviderWrapping, - addAnthropicRequestAttributes, - addAnthropicResponseAttributes, defineIntegration, - extractAnthropicRequestAttributes, - instrumentAsyncIterableStream, - instrumentMessageStream, - resolveAIRecordingOptions, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, - shouldEnableTruncation, startInactiveSpan, } from '@sentry/core'; +import { resolveAIRecordingOptions, shouldEnableTruncation } from '../../ai/core/utils'; +import { addPrivateRequestAttributes, addResponseAttributes, extractRequestAttributes } from '../../ai/anthropic-ai'; +import { instrumentAsyncIterableStream, instrumentMessageStream } from '../../ai/anthropic-ai/streaming'; +import type { AnthropicAiOptions, AnthropicAiResponse } from '../../ai/anthropic-ai/types'; import { CHANNELS } from '../../orchestrion/channels'; import { bindTracingChannelToSpan } from '../../tracing-channel'; import { anthropicAiModuleNames } from '../../orchestrion/config/anthropic-ai'; @@ -60,7 +57,7 @@ function instrumentAnthropic(options: AnthropicAiOptions): void { data => createGenAiSpan(data, operation, methodPath, options), { beforeSpanEnd: (span, data) => { - addAnthropicResponseAttributes( + addResponseAttributes( span, data.result as AnthropicAiResponse, resolveAIRecordingOptions(options).recordOutputs, @@ -103,7 +100,7 @@ function createGenAiSpan( const { recordInputs } = resolveAIRecordingOptions(options); const enableTruncation = shouldEnableTruncation(options.enableTruncation); - const attributes = extractAnthropicRequestAttributes(args, methodPath, operation); + const attributes = extractRequestAttributes(args, methodPath, operation); const model = (attributes[GEN_AI_REQUEST_MODEL] as string) || 'unknown'; attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = ORIGIN; @@ -114,7 +111,7 @@ function createGenAiSpan( }); if (recordInputs && params) { - addAnthropicRequestAttributes(span, params, enableTruncation); + addPrivateRequestAttributes(span, params, enableTruncation); } return span; diff --git a/packages/server-utils/src/integrations/tracing-channel/langchain.ts b/packages/server-utils/src/integrations/tracing-channel/langchain.ts index 7beaf7e699ca..3a0b05321299 100644 --- a/packages/server-utils/src/integrations/tracing-channel/langchain.ts +++ b/packages/server-utils/src/integrations/tracing-channel/langchain.ts @@ -4,13 +4,13 @@ import { _INTERNAL_getLangChainEmbeddingsSpanOptions, _INTERNAL_mergeLangChainCallbackHandler, _INTERNAL_skipAiProviderWrapping, - ANTHROPIC_AI_INTEGRATION_NAME, createLangChainCallbackHandler, defineIntegration, GOOGLE_GENAI_INTEGRATION_NAME, LANGCHAIN_INTEGRATION_NAME, startInactiveSpan, } from '@sentry/core'; +import { ANTHROPIC_AI_INTEGRATION_NAME } from '../../ai/anthropic-ai/constants'; import { OPENAI_INTEGRATION_NAME } from '../../ai/openai/constants'; import { CHANNELS } from '../../orchestrion/channels'; import { langchainEmbeddingsChannels } from '../../orchestrion/config/langchain'; diff --git a/packages/core/test/lib/utils/anthropic-utils.test.ts b/packages/server-utils/test/ai/lib/utils/anthropic-utils.test.ts similarity index 97% rename from packages/core/test/lib/utils/anthropic-utils.test.ts rename to packages/server-utils/test/ai/lib/utils/anthropic-utils.test.ts index 59b6dd63fad2..0d674fc240a7 100644 --- a/packages/core/test/lib/utils/anthropic-utils.test.ts +++ b/packages/server-utils/test/ai/lib/utils/anthropic-utils.test.ts @@ -3,8 +3,8 @@ import { mapAnthropicErrorToStatusMessage, messagesFromParams, setMessagesAttribute, -} from '../../../src/tracing/anthropic-ai/utils'; -import type { Span } from '../../../src/types/span'; +} from '../../../../src/ai/anthropic-ai/utils'; +import type { Span } from '@sentry/core'; describe('anthropic-ai-utils', () => { describe('mapAnthropicErrorToStatusMessage', () => { diff --git a/packages/vercel-edge/src/index.ts b/packages/vercel-edge/src/index.ts index f2d99fe656d2..880df4543286 100644 --- a/packages/vercel-edge/src/index.ts +++ b/packages/vercel-edge/src/index.ts @@ -75,7 +75,6 @@ export { inboundFiltersIntegration, instrumentStateGraph, instrumentGoogleGenAIClient, - instrumentAnthropicAiClient, eventFiltersIntegration, linkedErrorsIntegration, requestDataIntegration, @@ -107,7 +106,7 @@ export { withStreamedSpan, spanStreamingIntegration, } from '@sentry/core'; -export { instrumentOpenAiClient } from '@sentry/server-utils/no-diagnostic-channels'; +export { instrumentOpenAiClient, instrumentAnthropicAiClient } from '@sentry/server-utils/no-diagnostic-channels'; export { VercelEdgeClient } from './client'; export { getDefaultIntegrations, init } from './sdk';