UNPKG

@aws/aws-distro-opentelemetry-node-autoinstrumentation

Version:

This package provides Amazon Web Services distribution of the OpenTelemetry Node Instrumentation, which allows for auto-instrumentation of NodeJS applications.

241 lines 13.7 kB
"use strict"; // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 Object.defineProperty(exports, "__esModule", { value: true }); exports.LangChainInstrumentation = exports.INSTRUMENTATION_SHORT_NAME = exports.INSTRUMENTATION_NAME = void 0; /* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-this-alias */ const api_1 = require("@opentelemetry/api"); const instrumentation_1 = require("@opentelemetry/instrumentation"); const version_1 = require("../../version"); const utils_1 = require("../../utils"); exports.INSTRUMENTATION_NAME = '@aws/aws-distro-opentelemetry-instrumentation-langchain'; exports.INSTRUMENTATION_SHORT_NAME = 'aws_langchain'; const SUPPORTED_VERSIONS = ['>=1.0.0 <2.0.0']; class LangChainInstrumentation extends instrumentation_1.InstrumentationBase { constructor(config = {}) { super(exports.INSTRUMENTATION_NAME, version_1.LIB_VERSION, config); this._patchedCallbackManagers = new Set(); this._patchedChatModelsProtos = new Set(); this._patchedToolsProtos = new Set(); this._wrappedChatProtos = new Set(); this._wrappedToolProtos = new Set(); this._handler = undefined; } setConfig(config = {}) { super.setConfig(Object.assign(Object.assign({}, config), { captureMessageContent: !!config.captureMessageContent })); this._handler = undefined; } enable() { if ((0, utils_1.isInstrumentationDisabled)(exports.INSTRUMENTATION_SHORT_NAME)) { this._diag.debug(`${exports.INSTRUMENTATION_NAME} is disabled`); return; } if (!(0, utils_1.isAgenticInstrumentationOptIn)()) { const conflict = (0, utils_1.detectConflictingInstrumentation)(exports.INSTRUMENTATION_SHORT_NAME); if (conflict) { this._diag.info(`Skipping ${exports.INSTRUMENTATION_NAME}: third-party '${conflict}' detected. ` + 'Set AWS_AGENTIC_INSTRUMENTATION_OPT_IN=true to override.'); return; } } super.enable(); } init() { const moduleFiles = (path, patch, unpatch) => [ new instrumentation_1.InstrumentationNodeModuleFile(`${path}.cjs`, SUPPORTED_VERSIONS, patch, unpatch), new instrumentation_1.InstrumentationNodeModuleFile(`${path}.js`, SUPPORTED_VERSIONS, patch, unpatch), ]; // to ensure these patches work for both CJS and ESM and to ensure we are setting the // proper trace context for downstream instrumentations we MUST monkey-patch _generate() and _call() to wrap // them in context.with(), which makes our callback handler's span the active context in AsyncLocalStorage. Unfortunately, // in js the only way to set the active context is by wrapping the callback itself. // see: https://github.com/open-telemetry/opentelemetry-js/issues/3558 return [ new instrumentation_1.InstrumentationNodeModuleDefinition('@langchain/core', SUPPORTED_VERSIONS, (m) => m, () => { }, [ ...moduleFiles('@langchain/core/dist/callbacks/manager', (m) => { this._patchCallbackManager(m === null || m === void 0 ? void 0 : m.CallbackManager); return m; }, (m) => { this._unpatchCallbackManager(m === null || m === void 0 ? void 0 : m.CallbackManager); return m; }), ...moduleFiles('@langchain/core/dist/language_models/chat_models', (m) => this._patchChatModelsModule(m), (m) => this._unpatchChatModelsModule(m)), ...moduleFiles('@langchain/core/dist/tools/index', (m) => this._patchToolsModule(m), (m) => this._unpatchToolsModule(m)), ]), ]; } _patchCallbackManager(CallbackManager) { if (!CallbackManager || this._patchedCallbackManagers.has(CallbackManager)) return; if (typeof CallbackManager._configureSync !== 'function') return; const langChainInstrumentation = this; this._wrap(CallbackManager, '_configureSync', (original) => { return function (...args) { if (!langChainInstrumentation._handler) { // eslint-disable-next-line @typescript-eslint/no-var-requires const { OpenTelemetryCallbackHandler } = require('./callback-handler'); langChainInstrumentation._handler = new OpenTelemetryCallbackHandler(langChainInstrumentation.tracer, !!langChainInstrumentation.getConfig().captureMessageContent); langChainInstrumentation._diag.debug('Lazily loaded OTel callback handler'); } // OTel handler must be first so that span context is set before // other handlers that are registered are executed so that we can // propagate to downstream instrumentations. // see: https://github.com/aws-observability/aws-otel-python-instrumentation/blob/e729533/aws-opentelemetry-distro/src/amazon/opentelemetry/distro/instrumentation/langchain/callback_handler.py#L78-L82 args[0] = LangChainInstrumentation._injectHandler(args[0], langChainInstrumentation._handler); return original.apply(this, args); }; }); this._patchedCallbackManagers.add(CallbackManager); this._diag.debug('Patched CallbackManager._configureSync'); } _unpatchCallbackManager(CallbackManager) { if (!CallbackManager || !this._patchedCallbackManagers.has(CallbackManager)) return; this._unwrap(CallbackManager, '_configureSync'); this._patchedCallbackManagers.clear(); this._handler = undefined; this._diag.debug('Unpatched CallbackManager'); } _patchChatModelsModule(modExports) { var _a; const proto = (_a = modExports === null || modExports === void 0 ? void 0 : modExports.BaseChatModel) === null || _a === void 0 ? void 0 : _a.prototype; if (!proto || this._patchedChatModelsProtos.has(proto)) return modExports; const langChainInstrumentation = this; this._wrap(proto, '_generateUncached', (original) => { return function (...args) { langChainInstrumentation._propagateContextOnChatProto(Object.getPrototypeOf(this)); return original.apply(this, args); }; }); this._patchedChatModelsProtos.add(proto); this._diag.debug('Patched BaseChatModel.prototype._generateUncached'); return modExports; } _unpatchChatModelsModule(modExports) { var _a; const proto = (_a = modExports === null || modExports === void 0 ? void 0 : modExports.BaseChatModel) === null || _a === void 0 ? void 0 : _a.prototype; if (!proto || !this._patchedChatModelsProtos.has(proto)) return modExports; this._unwrap(proto, '_generateUncached'); for (const p of this._wrappedChatProtos) { if (typeof p._generate === 'function') this._unwrap(p, '_generate'); if (typeof p._streamResponseChunks === 'function') this._unwrap(p, '_streamResponseChunks'); } this._wrappedChatProtos.clear(); this._patchedChatModelsProtos.clear(); this._diag.debug('Unpatched BaseChatModel and all concrete chat model prototypes'); return modExports; } _patchToolsModule(modExports) { var _a; const proto = (_a = modExports === null || modExports === void 0 ? void 0 : modExports.StructuredTool) === null || _a === void 0 ? void 0 : _a.prototype; if (!proto || this._patchedToolsProtos.has(proto)) return modExports; const langChainInstrumentation = this; this._wrap(proto, 'call', (original) => { return function (...args) { langChainInstrumentation._propagateContextOnToolProto(Object.getPrototypeOf(this)); return original.apply(this, args); }; }); this._patchedToolsProtos.add(proto); this._diag.debug('Patched StructuredTool.prototype.call'); return modExports; } _unpatchToolsModule(modExports) { var _a; const proto = (_a = modExports === null || modExports === void 0 ? void 0 : modExports.StructuredTool) === null || _a === void 0 ? void 0 : _a.prototype; if (!proto || !this._patchedToolsProtos.has(proto)) return modExports; this._unwrap(proto, 'call'); for (const p of this._wrappedToolProtos) { if (typeof p._call === 'function') this._unwrap(p, '_call'); } this._wrappedToolProtos.clear(); this._patchedToolsProtos.clear(); this._diag.debug('Unpatched StructuredTool and all concrete tool prototypes'); return modExports; } // for propagating context in non-streaming and streaming calls to LLMs. // These are the base methods all chat classes must implement // see: _generate: https://github.com/langchain-ai/langchainjs/blob/0bf9d7e/libs/langchain-core/src/language_models/chat_models.ts#L896 // see: _streamResponseChunks: https://github.com/langchain-ai/langchainjs/blob/0bf9d7e/libs/langchain-core/src/language_models/chat_models.ts#L145 _propagateContextOnChatProto(concreteProto) { var _a; if (!concreteProto || this._wrappedChatProtos.has(concreteProto)) return; this._wrappedChatProtos.add(concreteProto); const langChainInstrumentation = this; if (typeof concreteProto._generate === 'function') { this._wrap(concreteProto, '_generate', (original) => { return function (...genArgs) { var _a, _b, _c, _d; const spanCtx = (_d = (_b = (_a = langChainInstrumentation._handler) === null || _a === void 0 ? void 0 : _a.runIdToSpanMap) === null || _b === void 0 ? void 0 : _b.get((_c = genArgs[2]) === null || _c === void 0 ? void 0 : _c.runId)) === null || _d === void 0 ? void 0 : _d.context; if (spanCtx) return api_1.context.with(spanCtx, () => original.apply(this, genArgs)); return original.apply(this, genArgs); }; }); } if (typeof concreteProto._streamResponseChunks === 'function') { this._wrap(concreteProto, '_streamResponseChunks', (original) => { return function (...streamArgs) { var _a, _b, _c, _d; const spanCtx = (_d = (_b = (_a = langChainInstrumentation._handler) === null || _a === void 0 ? void 0 : _a.runIdToSpanMap) === null || _b === void 0 ? void 0 : _b.get((_c = streamArgs[2]) === null || _c === void 0 ? void 0 : _c.runId)) === null || _d === void 0 ? void 0 : _d.context; if (!spanCtx) return original.apply(this, streamArgs); const boundOriginal = api_1.context.bind(spanCtx, original.bind(this, ...streamArgs)); return boundOriginal(); }; }); } this._diag.debug(`Wrapped context propagation on ${((_a = concreteProto.constructor) === null || _a === void 0 ? void 0 : _a.name) || 'unknown'} prototype`); } // for propagating context in tool calls // see: _call: https://github.com/langchain-ai/langchainjs/blob/0bf9d7e/libs/langchain-core/src/tools/index.ts#L163 _propagateContextOnToolProto(concreteProto) { var _a; if (!concreteProto || this._wrappedToolProtos.has(concreteProto)) return; this._wrappedToolProtos.add(concreteProto); const langChainInstrumentation = this; if (typeof concreteProto._call === 'function') { this._wrap(concreteProto, '_call', (original) => { return function (...callArgs) { var _a, _b, _c, _d; const spanCtx = (_d = (_b = (_a = langChainInstrumentation._handler) === null || _a === void 0 ? void 0 : _a.runIdToSpanMap) === null || _b === void 0 ? void 0 : _b.get((_c = callArgs[1]) === null || _c === void 0 ? void 0 : _c.runId)) === null || _d === void 0 ? void 0 : _d.context; if (spanCtx) return api_1.context.with(spanCtx, () => original.apply(this, callArgs)); return original.apply(this, callArgs); }; }); } this._diag.debug(`Wrapped context propagation on ${((_a = concreteProto.constructor) === null || _a === void 0 ? void 0 : _a.name) || 'unknown'} tool prototype`); } static _injectHandler(handlersOrManager, handler) { if (Array.isArray(handlersOrManager)) { if (!handlersOrManager.includes(handler)) { handlersOrManager.unshift(handler); } return handlersOrManager; } const manager = handlersOrManager; if (manager && typeof manager === 'object' && Array.isArray(manager.handlers)) { if (!manager.handlers.includes(handler)) { manager.handlers.unshift(handler); if (Array.isArray(manager.inheritableHandlers)) { manager.inheritableHandlers.unshift(handler); } } return manager; } return [handler]; } } exports.LangChainInstrumentation = LangChainInstrumentation; //# sourceMappingURL=instrumentation.js.map