From 0ed41282a20ad7a9fd789454acb25cd0b2f365c7 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 18 Sep 2025 11:55:43 +0200 Subject: [PATCH] ref(core): Avoid looking up openai integration options --- packages/core/src/utils/openai/index.ts | 44 ++++++++----------- .../src/integrations/tracing/openai/index.ts | 6 +-- .../tracing/openai/instrumentation.ts | 30 ++++--------- 3 files changed, 29 insertions(+), 51 deletions(-) diff --git a/packages/core/src/utils/openai/index.ts b/packages/core/src/utils/openai/index.ts index d296df840112..aeb0c215298a 100644 --- a/packages/core/src/utils/openai/index.ts +++ b/packages/core/src/utils/openai/index.ts @@ -1,4 +1,4 @@ -import { getCurrentScope } from '../../currentScopes'; +import { getClient } from '../../currentScopes'; import { captureException } from '../../exports'; import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../semanticAttributes'; import { SPAN_STATUS_ERROR } from '../../tracing'; @@ -19,13 +19,11 @@ import { GEN_AI_RESPONSE_TOOL_CALLS_ATTRIBUTE, GEN_AI_SYSTEM_ATTRIBUTE, } from '../ai/gen-ai-attributes'; -import { OPENAI_INTEGRATION_NAME } from './constants'; import { instrumentStream } from './streaming'; import type { ChatCompletionChunk, InstrumentedMethod, OpenAiChatCompletionObject, - OpenAiIntegration, OpenAiOptions, OpenAiResponse, OpenAIResponseObject, @@ -198,18 +196,6 @@ function addRequestAttributes(span: Span, params: Record): void } } -function getOptionsFromIntegration(): OpenAiOptions { - const scope = getCurrentScope(); - const client = scope.getClient(); - const integration = client?.getIntegrationByName(OPENAI_INTEGRATION_NAME) as OpenAiIntegration | undefined; - const shouldRecordInputsAndOutputs = integration ? Boolean(client?.getOptions().sendDefaultPii) : false; - - return { - recordInputs: integration?.options?.recordInputs ?? shouldRecordInputsAndOutputs, - recordOutputs: integration?.options?.recordOutputs ?? shouldRecordInputsAndOutputs, - }; -} - /** * Instrument a method with Sentry spans * Following Sentry AI Agents Manual Instrumentation conventions @@ -219,10 +205,9 @@ function instrumentMethod( originalMethod: (...args: T) => Promise, methodPath: InstrumentedMethod, context: unknown, - options?: OpenAiOptions, + options: OpenAiOptions, ): (...args: T) => Promise { return async function instrumentedMethod(...args: T): Promise { - const finalOptions = options || getOptionsFromIntegration(); const requestAttributes = extractRequestAttributes(args, methodPath); const model = (requestAttributes[GEN_AI_REQUEST_MODEL_ATTRIBUTE] as string) || 'unknown'; const operationName = getOperationName(methodPath); @@ -240,8 +225,8 @@ function instrumentMethod( }, async (span: Span) => { try { - if (finalOptions.recordInputs && args[0] && typeof args[0] === 'object') { - addRequestAttributes(span, args[0] as Record); + if (options.recordInputs && params) { + addRequestAttributes(span, params); } const result = await originalMethod.apply(context, args); @@ -249,7 +234,7 @@ function instrumentMethod( return instrumentStream( result as OpenAIStream, span, - finalOptions.recordOutputs ?? false, + options.recordOutputs ?? false, ) as unknown as R; } catch (error) { // For streaming requests that fail before stream creation, we still want to record @@ -279,12 +264,12 @@ function instrumentMethod( }, async (span: Span) => { try { - if (finalOptions.recordInputs && args[0] && typeof args[0] === 'object') { - addRequestAttributes(span, args[0] as Record); + if (options.recordInputs && params) { + addRequestAttributes(span, params); } const result = await originalMethod.apply(context, args); - addResponseAttributes(span, result, finalOptions.recordOutputs); + addResponseAttributes(span, result, options.recordOutputs); return result; } catch (error) { captureException(error, { @@ -307,7 +292,7 @@ function instrumentMethod( /** * Create a deep proxy for OpenAI client instrumentation */ -function createDeepProxy(target: T, currentPath = '', options?: OpenAiOptions): T { +function createDeepProxy(target: T, currentPath = '', options: OpenAiOptions): T { return new Proxy(target, { get(obj: object, prop: string): unknown { const value = (obj as Record)[prop]; @@ -336,6 +321,13 @@ function createDeepProxy(target: T, currentPath = '', options? * Instrument an OpenAI client with Sentry tracing * Can be used across Node.js, Cloudflare Workers, and Vercel Edge */ -export function instrumentOpenAiClient(client: T, options?: OpenAiOptions): T { - return createDeepProxy(client, '', options); +export function instrumentOpenAiClient(openAiClient: T, options?: OpenAiOptions): T { + const sendDefaultPii = Boolean(getClient()?.getOptions().sendDefaultPii); + + const _options = { + recordInputs: sendDefaultPii, + recordOutputs: sendDefaultPii, + ...options, + }; + return createDeepProxy(openAiClient, '', _options); } diff --git a/packages/node/src/integrations/tracing/openai/index.ts b/packages/node/src/integrations/tracing/openai/index.ts index 0e88d2b315cc..f0531dfa23f4 100644 --- a/packages/node/src/integrations/tracing/openai/index.ts +++ b/packages/node/src/integrations/tracing/openai/index.ts @@ -3,9 +3,9 @@ import { defineIntegration, OPENAI_INTEGRATION_NAME } from '@sentry/core'; import { generateInstrumentOnce } from '@sentry/node-core'; import { SentryOpenAiInstrumentation } from './instrumentation'; -export const instrumentOpenAi = generateInstrumentOnce( +export const instrumentOpenAi = generateInstrumentOnce( OPENAI_INTEGRATION_NAME, - () => new SentryOpenAiInstrumentation({}), + options => new SentryOpenAiInstrumentation(options), ); const _openAiIntegration = ((options: OpenAiOptions = {}) => { @@ -13,7 +13,7 @@ const _openAiIntegration = ((options: OpenAiOptions = {}) => { name: OPENAI_INTEGRATION_NAME, options, setupOnce() { - instrumentOpenAi(); + instrumentOpenAi(options); }, }; }) satisfies IntegrationFn; diff --git a/packages/node/src/integrations/tracing/openai/instrumentation.ts b/packages/node/src/integrations/tracing/openai/instrumentation.ts index 23df5bb66c35..1bbffc1d1eec 100644 --- a/packages/node/src/integrations/tracing/openai/instrumentation.ts +++ b/packages/node/src/integrations/tracing/openai/instrumentation.ts @@ -4,14 +4,12 @@ import { InstrumentationBase, InstrumentationNodeModuleDefinition, } from '@opentelemetry/instrumentation'; -import type { Integration, OpenAiClient, OpenAiOptions } from '@sentry/core'; -import { getClient, instrumentOpenAiClient, OPENAI_INTEGRATION_NAME, SDK_VERSION } from '@sentry/core'; +import type { OpenAiClient, OpenAiOptions } from '@sentry/core'; +import { getClient, instrumentOpenAiClient, SDK_VERSION } from '@sentry/core'; const supportedVersions = ['>=4.0.0 <6']; -export interface OpenAiIntegration extends Integration { - options: OpenAiOptions; -} +type OpenAiInstrumentationOptions = InstrumentationConfig & OpenAiOptions; /** * Represents the patched shape of the OpenAI module export. @@ -21,23 +19,11 @@ interface PatchedModuleExports { OpenAI: abstract new (...args: unknown[]) => OpenAiClient; } -/** - * Determines telemetry recording settings. - */ -function determineRecordingSettings( - integrationOptions: OpenAiOptions | undefined, - defaultEnabled: boolean, -): { recordInputs: boolean; recordOutputs: boolean } { - const recordInputs = integrationOptions?.recordInputs ?? defaultEnabled; - const recordOutputs = integrationOptions?.recordOutputs ?? defaultEnabled; - return { recordInputs, recordOutputs }; -} - /** * Sentry OpenAI instrumentation using OpenTelemetry. */ -export class SentryOpenAiInstrumentation extends InstrumentationBase { - public constructor(config: InstrumentationConfig = {}) { +export class SentryOpenAiInstrumentation extends InstrumentationBase { + public constructor(config: OpenAiInstrumentationOptions = {}) { super('@sentry/instrumentation-openai', SDK_VERSION, config); } @@ -54,15 +40,15 @@ export class SentryOpenAiInstrumentation extends InstrumentationBase(OPENAI_INTEGRATION_NAME); - const integrationOpts = integration?.options; const defaultPii = Boolean(client?.getOptions().sendDefaultPii); - const { recordInputs, recordOutputs } = determineRecordingSettings(integrationOpts, defaultPii); + const recordInputs = config.recordInputs ?? defaultPii; + const recordOutputs = config.recordOutputs ?? defaultPii; return instrumentOpenAiClient(instance as OpenAiClient, { recordInputs,