"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LangChainInstrumentation = void 0;
exports.isPatched = isPatched;
const instrumentation_1 = require("@opentelemetry/instrumentation");
const version_1 = require("./version");
const api_1 = require("@opentelemetry/api");
const instrumentationUtils_1 = require("./instrumentationUtils");
const openinference_core_1 = require("@arizeai/openinference-core");
const MODULE_NAME = "@langchain/core/callbacks";
/**
 * Flag to check if the openai module has been patched
 * Note: This is a fallback in case the module is made immutable (e.x. Deno, webpack, etc.)
 */
let _isOpenInferencePatched = false;
/**
 * function to check if instrumentation is enabled / disabled
 */
function isPatched() {
    return _isOpenInferencePatched;
}
/**
 * An auto instrumentation class for LangChain that creates {@link https://github.com/Arize-ai/openinference/blob/main/spec/semantic_conventions.md|OpenInference} Compliant spans for LangChain
 * @param instrumentationConfig The config for the instrumentation @see {@link InstrumentationConfig}
 * @param traceConfig The OpenInference trace configuration. Can be used to mask or redact sensitive information on spans. @see {@link TraceConfigOptions}
 */
class LangChainInstrumentation extends instrumentation_1.InstrumentationBase {
    constructor({ instrumentationConfig, traceConfig, } = {}) {
        super("@arizeai/openinference-instrumentation-langchain", version_1.VERSION, Object.assign({}, instrumentationConfig));
        this.oiTracer = new openinference_core_1.OITracer({
            tracer: this.tracer,
            traceConfig,
        });
    }
    manuallyInstrument(module) {
        api_1.diag.debug(`Manually instrumenting ${MODULE_NAME}`);
        this.patch(module);
    }
    init() {
        const module = new instrumentation_1.InstrumentationNodeModuleDefinition("@langchain/core/dist/callbacks/manager.cjs", ["^0.1.0", "^0.2.0"], this.patch.bind(this), this.unpatch.bind(this));
        return module;
    }
    patch(module, moduleVersion) {
        api_1.diag.debug(`Applying patch for ${MODULE_NAME}${moduleVersion != null ? `@${moduleVersion}` : ""}`);
        if ((module === null || module === void 0 ? void 0 : module.openInferencePatched) || _isOpenInferencePatched) {
            return module;
        }
        // eslint-disable-next-line @typescript-eslint/no-this-alias
        const instrumentation = this;
        /**
         * _configureSync is only available in v0.2.0 and above
         * It was added as a replacement to the configure method which is marked as soon to be deprecated
         * In v0.2.0 and above, the configure method is a wrapper around _configureSync
         * However, configure is not always called, where as _configureSync is always called
         * so we want to patch only configure sync if it's available
         * and only configure if _configureSync is not available so we don't get duplicate traces
         */
        if ("_configureSync" in module.CallbackManager) {
            this._wrap(module.CallbackManager, "_configureSync", (original) => {
                return function (...args) {
                    const inheritableHandlers = args[0];
                    const newInheritableHandlers = (0, instrumentationUtils_1.addTracerToHandlers)(instrumentation.oiTracer, inheritableHandlers);
                    args[0] = newInheritableHandlers;
                    return original.apply(this, args);
                };
            });
        }
        else {
            this._wrap(module.CallbackManager, "configure", (original) => {
                return function (...args) {
                    const handlers = args[0];
                    const newHandlers = (0, instrumentationUtils_1.addTracerToHandlers)(instrumentation.oiTracer, handlers);
                    args[0] = newHandlers;
                    return original.apply(this, args);
                };
            });
        }
        _isOpenInferencePatched = true;
        try {
            // This can fail if the module is made immutable via the runtime or bundler
            module.openInferencePatched = true;
        }
        catch (e) {
            api_1.diag.debug(`Failed to set ${MODULE_NAME} patched flag on the module`, e);
        }
        return module;
    }
    unpatch(module, moduleVersion) {
        if (module == null) {
            return;
        }
        api_1.diag.debug(`Removing patch for ${MODULE_NAME}${moduleVersion != null ? `@${moduleVersion}` : ""}`);
        if ((0, instrumentation_1.isWrapped)(module.CallbackManager.configure)) {
            this._unwrap(module.CallbackManager, "configure");
        }
        /**
         * _configureSync is only available in v0.2.0 and above
         * Thus we only want to unwrap it if it's available and has been wrapped
         */
        if ("_configureSync" in module.CallbackManager &&
            (0, instrumentation_1.isWrapped)(module.CallbackManager._configureSync)) {
            this._unwrap(module.CallbackManager, "_configureSync");
        }
        _isOpenInferencePatched = false;
        try {
            // This can fail if the module is made immutable via the runtime or bundler
            module.openInferencePatched = false;
        }
        catch (e) {
            api_1.diag.warn(`Failed to unset ${MODULE_NAME} patched flag on the module`, e);
        }
        return module;
    }
}
exports.LangChainInstrumentation = LangChainInstrumentation;
//# sourceMappingURL=instrumentation.js.map