UNPKG

openlit

Version:

OpenTelemetry-native Auto instrumentation library for monitoring LLM Applications, facilitating the integration of observability into your GenAI-driven projects

221 lines 11.5 kB
"use strict"; /** * OpenLIT LlamaIndex Instrumentation * * Monkey-patches LlamaIndex JS classes to emit OTel-compliant telemetry. * Mirrors the Python SDK: sdk/python/src/openlit/instrumentation/llamaindex/__init__.py * * Targets the `llamaindex` npm package (>=0.3.0). * Patches: LLM classes, query engines, chat engines, retrievers, embeddings, * index construction, document loaders, splitters, and synthesizers. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const instrumentation_1 = require("@opentelemetry/instrumentation"); const constant_1 = require("../../constant"); const wrapper_1 = __importDefault(require("./wrapper")); class OpenlitLlamaIndexInstrumentation extends instrumentation_1.InstrumentationBase { constructor(config = {}) { super(`${constant_1.INSTRUMENTATION_PREFIX}/instrumentation-llamaindex`, '1.0.0', config); } init() { const mainModule = new instrumentation_1.InstrumentationNodeModuleDefinition('llamaindex', ['>=0.3.0'], (moduleExports) => { this._patch(moduleExports); return moduleExports; }, (moduleExports) => { if (moduleExports) this._unpatch(moduleExports); }); return [mainModule]; } manualPatch(llamaindex) { this._patch(llamaindex); } // --------------------------------------------------------------------------- // Patch helpers // --------------------------------------------------------------------------- /** * Wrap an instance method on the first class found that exposes it. * Silently skips classes that aren't exported or don't have the method. */ _patchProto(moduleExports, classNames, method, wrapper) { for (const name of classNames) { const Cls = moduleExports[name]; if (Cls?.prototype?.[method] && !(0, instrumentation_1.isWrapped)(Cls.prototype[method])) { try { this._wrap(Cls.prototype, method, wrapper); } catch { /* skip silently */ } } } } /** * Wrap a static method on a class. */ _patchStatic(Cls, method, wrapper) { if (typeof Cls?.[method] === 'function' && !(0, instrumentation_1.isWrapped)(Cls[method])) { try { this._wrap(Cls, method, wrapper); } catch { /* skip silently */ } } } /** * Unwrap an instance method if it's wrapped. */ _unwrapProto(moduleExports, classNames, method) { for (const name of classNames) { const Cls = moduleExports[name]; if (Cls?.prototype?.[method] && (0, instrumentation_1.isWrapped)(Cls.prototype[method])) { try { this._unwrap(Cls.prototype, method); } catch { /* ignore */ } } } } /** * Unwrap a static method if it's wrapped. */ _unwrapStatic(Cls, method) { if (Cls && typeof Cls[method] === 'function' && (0, instrumentation_1.isWrapped)(Cls[method])) { try { this._unwrap(Cls, method); } catch { /* ignore */ } } } // --------------------------------------------------------------------------- // Main patch — mirrors Python _instrument() operation lists // --------------------------------------------------------------------------- _patch(m) { try { const tracer = this.tracer; // === LLM OPERATIONS (chat spans) === // Mirrors Python: LLM.chat, LLM.complete const llmClasses = [ 'OpenAI', 'Anthropic', 'Ollama', 'Gemini', 'Groq', 'HuggingFaceInference', 'Replicate', 'DeepSeek', 'Portkey', 'BaseLLM', 'LLM', ]; this._patchProto(m, llmClasses, 'chat', wrapper_1.default._patchLLMChat(tracer)); this._patchProto(m, llmClasses, 'complete', wrapper_1.default._patchLLMComplete(tracer)); // === QUERY ENGINE OPERATIONS (retrieval spans) === // Mirrors Python: RetrieverQueryEngine.query, TransformQueryEngine.query const queryEngineClasses = [ 'RetrieverQueryEngine', 'TransformQueryEngine', 'SubQuestionQueryEngine', 'BaseQueryEngine', 'QueryEngine', ]; this._patchProto(m, queryEngineClasses, 'query', wrapper_1.default._patchQueryEngineQuery(tracer)); // === CHAT ENGINE OPERATIONS (invoke_workflow spans) === const chatEngineClasses = [ 'ContextChatEngine', 'SimpleChatEngine', 'CondenseQuestionChatEngine', 'BaseChatEngine', 'ChatEngine', ]; this._patchProto(m, chatEngineClasses, 'chat', wrapper_1.default._patchChatEngineChat(tracer)); // === RETRIEVER OPERATIONS (retrieval spans) === // Mirrors Python: VectorIndexRetriever.retrieve, BaseRetriever.retrieve const retrieverClasses = [ 'VectorIndexRetriever', 'BaseRetriever', 'Retriever', ]; this._patchProto(m, retrieverClasses, 'retrieve', wrapper_1.default._patchRetrieverRetrieve(tracer)); // === EMBEDDING OPERATIONS (embeddings spans) === // Mirrors Python: BaseEmbedding.get_text_embedding_batch const embeddingClasses = [ 'OpenAIEmbedding', 'BaseEmbedding', 'HuggingFaceEmbedding', ]; this._patchProto(m, embeddingClasses, 'getTextEmbedding', wrapper_1.default._patchEmbedding(tracer, 'embedding_generate')); this._patchProto(m, embeddingClasses, 'getQueryEmbedding', wrapper_1.default._patchEmbedding(tracer, 'embedding_generate')); this._patchProto(m, embeddingClasses, 'getTextEmbeddingsBatch', wrapper_1.default._patchEmbedding(tracer, 'embedding_generate')); // === INDEX OPERATIONS (invoke_workflow spans) === // Mirrors Python: VectorStoreIndex.from_documents, from_vector_store const indexClasses = ['VectorStoreIndex', 'ListIndex', 'TreeIndex']; for (const name of indexClasses) { const Cls = m[name]; if (Cls) { this._patchStatic(Cls, 'fromDocuments', wrapper_1.default._patchFrameworkMethod(tracer, 'index_construct')); this._patchStatic(Cls, 'fromVectorStore', wrapper_1.default._patchFrameworkMethod(tracer, 'index_construct')); } } // Mirrors Python: BaseIndex.as_query_engine, BaseIndex.as_retriever const indexProtoClasses = [ 'VectorStoreIndex', 'ListIndex', 'TreeIndex', 'BaseIndex', ]; this._patchProto(m, indexProtoClasses, 'asQueryEngine', wrapper_1.default._patchFrameworkMethod(tracer, 'query_engine_create')); this._patchProto(m, indexProtoClasses, 'asRetriever', wrapper_1.default._patchFrameworkMethod(tracer, 'retriever_create')); // === DOCUMENT OPERATIONS (framework / retrieval spans) === // Mirrors Python: SimpleDirectoryReader.load_data this._patchProto(m, ['SimpleDirectoryReader'], 'loadData', wrapper_1.default._patchFrameworkMethod(tracer, 'document_load')); // Mirrors Python: SentenceSplitter.get_nodes_from_documents this._patchProto(m, ['SentenceSplitter', 'NodeParser'], 'getNodesFromDocuments', wrapper_1.default._patchFrameworkMethod(tracer, 'document_split')); // Mirrors Python: SentenceSplitter.split_text this._patchProto(m, ['SentenceSplitter'], 'splitText', wrapper_1.default._patchFrameworkMethod(tracer, 'text_splitter_split')); // === SYNTHESIZER OPERATIONS (chat spans) === // Mirrors Python: BaseSynthesizer.synthesize const synthesizerClasses = [ 'ResponseSynthesizer', 'CompactAndRefine', 'TreeSummarize', 'BaseSynthesizer', ]; this._patchProto(m, synthesizerClasses, 'synthesize', wrapper_1.default._patchFrameworkMethod(tracer, 'response_synthesize')); // === POSTPROCESSOR OPERATIONS (framework spans) === this._patchProto(m, ['BaseNodePostprocessor', 'NodePostprocessor'], 'postprocessNodes', wrapper_1.default._patchFrameworkMethod(tracer, 'postprocessor_process')); } catch { /* graceful degradation — do not break the application */ } } // --------------------------------------------------------------------------- // Unpatch // --------------------------------------------------------------------------- _unpatch(m) { try { const llmClasses = [ 'OpenAI', 'Anthropic', 'Ollama', 'Gemini', 'Groq', 'HuggingFaceInference', 'Replicate', 'DeepSeek', 'Portkey', 'BaseLLM', 'LLM', ]; this._unwrapProto(m, llmClasses, 'chat'); this._unwrapProto(m, llmClasses, 'complete'); const queryEngineClasses = [ 'RetrieverQueryEngine', 'TransformQueryEngine', 'SubQuestionQueryEngine', 'BaseQueryEngine', 'QueryEngine', ]; this._unwrapProto(m, queryEngineClasses, 'query'); const chatEngineClasses = [ 'ContextChatEngine', 'SimpleChatEngine', 'CondenseQuestionChatEngine', 'BaseChatEngine', 'ChatEngine', ]; this._unwrapProto(m, chatEngineClasses, 'chat'); const retrieverClasses = ['VectorIndexRetriever', 'BaseRetriever', 'Retriever']; this._unwrapProto(m, retrieverClasses, 'retrieve'); const embeddingClasses = ['OpenAIEmbedding', 'BaseEmbedding', 'HuggingFaceEmbedding']; this._unwrapProto(m, embeddingClasses, 'getTextEmbedding'); this._unwrapProto(m, embeddingClasses, 'getQueryEmbedding'); this._unwrapProto(m, embeddingClasses, 'getTextEmbeddingsBatch'); const indexClasses = ['VectorStoreIndex', 'ListIndex', 'TreeIndex']; for (const name of indexClasses) { const Cls = m[name]; if (Cls) { this._unwrapStatic(Cls, 'fromDocuments'); this._unwrapStatic(Cls, 'fromVectorStore'); } } const indexProtoClasses = [ 'VectorStoreIndex', 'ListIndex', 'TreeIndex', 'BaseIndex', ]; this._unwrapProto(m, indexProtoClasses, 'asQueryEngine'); this._unwrapProto(m, indexProtoClasses, 'asRetriever'); this._unwrapProto(m, ['SimpleDirectoryReader'], 'loadData'); this._unwrapProto(m, ['SentenceSplitter', 'NodeParser'], 'getNodesFromDocuments'); this._unwrapProto(m, ['SentenceSplitter'], 'splitText'); const synthesizerClasses = [ 'ResponseSynthesizer', 'CompactAndRefine', 'TreeSummarize', 'BaseSynthesizer', ]; this._unwrapProto(m, synthesizerClasses, 'synthesize'); this._unwrapProto(m, ['BaseNodePostprocessor', 'NodePostprocessor'], 'postprocessNodes'); } catch { /* ignore */ } } } exports.default = OpenlitLlamaIndexInstrumentation; //# sourceMappingURL=index.js.map