openlit
Version:
OpenTelemetry-native Auto instrumentation library for monitoring LLM Applications, facilitating the integration of observability into your GenAI-driven projects
743 lines • 40.5 kB
JavaScript
"use strict";
/**
* OpenLIT LlamaIndex Wrapper
*
* Mirrors Python SDK: sdk/python/src/openlit/instrumentation/llamaindex/
* Uses the same OPERATION_MAP and span semantics as the Python implementation.
*
* LLM operations get full provider-style telemetry (attributes, events, metrics).
* Framework operations (query engine, retriever, index, etc.) get framework-level spans.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const api_1 = require("@opentelemetry/api");
const config_1 = __importDefault(require("../../config"));
const helpers_1 = __importStar(require("../../helpers"));
const semantic_convention_1 = __importDefault(require("../../semantic-convention"));
const base_wrapper_1 = __importDefault(require("../base-wrapper"));
/**
* Operation mapping matching Python SDK's OPERATION_MAP in
* sdk/python/src/openlit/instrumentation/llamaindex/utils.py
*/
const OPERATION_MAP = {
// Document Loading & Processing
document_load: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_RETRIEVE,
document_transform: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK,
document_split: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK,
// Index Construction & Management
index_construct: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK,
index_insert: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK,
index_delete: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK,
index_build: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK,
// Query Engine Operations
query_engine_create: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK,
query_engine_query: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_RETRIEVE,
// Retriever Operations
retriever_create: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK,
retriever_retrieve: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_RETRIEVE,
// LLM Operations
llm_chat: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT,
llm_complete: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT,
// Embedding Operations
embedding_generate: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_EMBEDDING,
// Response Synthesis
response_synthesize: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT,
// Text Processing Components
text_splitter_split: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK,
node_parser_parse: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK,
// Vector Store Components
vector_store_add: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK,
vector_store_delete: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK,
vector_store_query: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_RETRIEVE,
// Postprocessor
postprocessor_process: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK,
};
class LlamaIndexWrapper {
// ---------------------------------------------------------------------------
// Helpers (mirrors Python set_server_address_and_port / model extraction)
// ---------------------------------------------------------------------------
static _extractModel(instance) {
return instance?.model
|| instance?.modelName
|| instance?.metadata?.model
|| instance?.llm?.model
|| instance?._llm?.model
|| instance?._responseSynthesizer?.llm?.model
|| instance?._responseSynthesizer?._llm?.model
|| 'unknown';
}
static _extractServerInfo(instance) {
const candidates = [
instance?._client?.baseURL,
instance?.session?.openai?.baseURL,
instance?.clientOptions?.baseURL,
instance?.llm?._client?.baseURL,
instance?._llm?._client?.baseURL,
instance?.llm?.session?.openai?.baseURL,
instance?._llm?.session?.openai?.baseURL,
];
for (const rawUrl of candidates) {
if (rawUrl) {
try {
const parsed = new URL(rawUrl);
return {
address: parsed.hostname,
port: parsed.port
? parseInt(parsed.port, 10)
: (parsed.protocol === 'https:' ? 443 : 80),
};
}
catch { /* try next */ }
}
}
return { address: 'localhost', port: 8000 };
}
// ---------------------------------------------------------------------------
// LLM chat patch — full provider-style telemetry + frameworkLlmActive
// Mirrors Python: LLM.chat -> operation_type "chat"
// ---------------------------------------------------------------------------
static _patchLLMChat(tracer) {
const endpoint = 'llm_chat';
const operationType = OPERATION_MAP[endpoint];
return (originalMethod) => {
return function (...args) {
const requestModel = LlamaIndexWrapper._extractModel(this);
const { address, port } = LlamaIndexWrapper._extractServerInfo(this);
const spanName = `${operationType} ${requestModel}`;
const span = tracer.startSpan(spanName, {
kind: api_1.SpanKind.CLIENT,
attributes: {
[semantic_convention_1.default.GEN_AI_OPERATION]: operationType,
[semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL]: LlamaIndexWrapper.aiSystem,
[semantic_convention_1.default.GEN_AI_REQUEST_MODEL]: requestModel,
[semantic_convention_1.default.SERVER_ADDRESS]: address,
[semantic_convention_1.default.SERVER_PORT]: port,
},
});
return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), () => {
const startTime = Date.now();
const onSuccess = (response) => {
try {
LlamaIndexWrapper._processLLMResponse(span, response, requestModel, address, port, startTime, args, 'chat');
}
catch { /* swallow telemetry errors */ }
return response;
};
const onError = (e) => {
helpers_1.default.handleException(span, e);
base_wrapper_1.default.recordMetrics(span, {
genAIEndpoint: endpoint,
model: requestModel,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
errorType: e?.constructor?.name || '_OTHER',
});
span.end();
throw e;
};
try {
const result = (0, helpers_1.runWithFrameworkLlm)(() => originalMethod.apply(this, args));
if (result && typeof result.then === 'function') {
return result.then(onSuccess).catch(onError);
}
return onSuccess(result);
}
catch (e) {
return onError(e);
}
});
};
};
}
// ---------------------------------------------------------------------------
// LLM complete patch — full provider-style telemetry + frameworkLlmActive
// Mirrors Python: LLM.complete -> operation_type "chat"
// ---------------------------------------------------------------------------
static _patchLLMComplete(tracer) {
const endpoint = 'llm_complete';
const operationType = OPERATION_MAP[endpoint];
return (originalMethod) => {
return function (...args) {
const requestModel = LlamaIndexWrapper._extractModel(this);
const { address, port } = LlamaIndexWrapper._extractServerInfo(this);
const spanName = `${operationType} ${requestModel}`;
const span = tracer.startSpan(spanName, {
kind: api_1.SpanKind.CLIENT,
attributes: {
[semantic_convention_1.default.GEN_AI_OPERATION]: operationType,
[semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL]: LlamaIndexWrapper.aiSystem,
[semantic_convention_1.default.GEN_AI_REQUEST_MODEL]: requestModel,
[semantic_convention_1.default.SERVER_ADDRESS]: address,
[semantic_convention_1.default.SERVER_PORT]: port,
},
});
return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), () => {
const startTime = Date.now();
const onSuccess = (response) => {
try {
LlamaIndexWrapper._processLLMResponse(span, response, requestModel, address, port, startTime, args, 'complete');
}
catch { /* swallow */ }
return response;
};
const onError = (e) => {
helpers_1.default.handleException(span, e);
base_wrapper_1.default.recordMetrics(span, {
genAIEndpoint: endpoint,
model: requestModel,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
errorType: e?.constructor?.name || '_OTHER',
});
span.end();
throw e;
};
try {
const result = (0, helpers_1.runWithFrameworkLlm)(() => originalMethod.apply(this, args));
if (result && typeof result.then === 'function') {
return result.then(onSuccess).catch(onError);
}
return onSuccess(result);
}
catch (e) {
return onError(e);
}
});
};
};
}
// ---------------------------------------------------------------------------
// Shared LLM response processor — attributes, events, metrics
// ---------------------------------------------------------------------------
static _processLLMResponse(span, response, requestModel, serverAddress, serverPort, startTime, args, mode) {
const endpoint = mode === 'chat' ? 'llm_chat' : 'llm_complete';
const duration = (Date.now() - startTime) / 1000;
const rawUsage = response?.raw?.usage || response?.usage || {};
const inputTokens = rawUsage.prompt_tokens || rawUsage.input_tokens || 0;
const outputTokens = rawUsage.completion_tokens || rawUsage.output_tokens || 0;
const finishReason = response?.raw?.choices?.[0]?.finish_reason || 'stop';
const responseModel = response?.raw?.model || requestModel;
const responseId = response?.raw?.id || '';
const pricingInfo = config_1.default.pricingInfo || {};
const cost = helpers_1.default.getChatModelCost(requestModel, pricingInfo, inputTokens, outputTokens);
base_wrapper_1.default.setBaseSpanAttributes(span, {
genAIEndpoint: endpoint,
model: requestModel,
cost,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress,
serverPort,
});
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IS_STREAM, false);
if (responseModel) {
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_MODEL, responseModel);
}
if (responseId) {
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_ID, responseId);
}
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_FINISH_REASON, [finishReason]);
if (inputTokens) {
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS, inputTokens);
}
if (outputTokens) {
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS, outputTokens);
}
span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_OPERATION_DURATION, duration);
let inputMessagesJson = '';
let outputMessagesJson = '';
if (config_1.default.captureMessageContent) {
if (mode === 'chat') {
const messages = args[0]?.messages || (Array.isArray(args[0]) ? args[0] : []);
const formatted = (Array.isArray(messages) ? messages : [messages]).map((m) => ({
role: m.role || 'user',
content: typeof m.content === 'string' ? m.content : JSON.stringify(m.content || ''),
}));
inputMessagesJson = helpers_1.default.buildInputMessages(formatted);
const text = response?.message?.content || response?.text || '';
outputMessagesJson = helpers_1.default.buildOutputMessages(text, finishReason);
}
else {
const prompt = typeof args[0] === 'string' ? args[0] : args[0]?.prompt || '';
inputMessagesJson = helpers_1.default.buildInputMessages([{ role: 'user', content: prompt }]);
const text = response?.text || '';
outputMessagesJson = helpers_1.default.buildOutputMessages(text, finishReason);
}
span.setAttribute(semantic_convention_1.default.GEN_AI_INPUT_MESSAGES, inputMessagesJson);
span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES, outputMessagesJson);
}
const eventAttrs = {
[semantic_convention_1.default.GEN_AI_OPERATION]: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT,
[semantic_convention_1.default.GEN_AI_REQUEST_MODEL]: requestModel,
[semantic_convention_1.default.GEN_AI_RESPONSE_MODEL]: responseModel,
[semantic_convention_1.default.SERVER_ADDRESS]: serverAddress,
[semantic_convention_1.default.SERVER_PORT]: serverPort,
[semantic_convention_1.default.GEN_AI_RESPONSE_ID]: responseId,
[semantic_convention_1.default.GEN_AI_RESPONSE_FINISH_REASON]: [finishReason],
[semantic_convention_1.default.GEN_AI_OUTPUT_TYPE]: semantic_convention_1.default.GEN_AI_OUTPUT_TYPE_TEXT,
[semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS]: inputTokens,
[semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS]: outputTokens,
};
if (config_1.default.captureMessageContent) {
eventAttrs[semantic_convention_1.default.GEN_AI_INPUT_MESSAGES] = inputMessagesJson;
eventAttrs[semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES] = outputMessagesJson;
}
helpers_1.default.emitInferenceEvent(span, eventAttrs);
base_wrapper_1.default.recordMetrics(span, {
genAIEndpoint: endpoint,
model: requestModel,
cost,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress,
serverPort,
});
span.end();
}
// ---------------------------------------------------------------------------
// Query engine query patch — retrieval span with source nodes
// Mirrors Python: RetrieverQueryEngine.query -> operation_type "retrieval"
// ---------------------------------------------------------------------------
static _patchQueryEngineQuery(tracer) {
const endpoint = 'query_engine_query';
const operationType = OPERATION_MAP[endpoint];
return (originalMethod) => {
return function (...args) {
const requestModel = LlamaIndexWrapper._extractModel(this);
const { address, port } = LlamaIndexWrapper._extractServerInfo(this?.llm || this?._llm || this);
const spanName = `${operationType} ${endpoint}`;
const span = tracer.startSpan(spanName, {
kind: api_1.SpanKind.CLIENT,
attributes: {
[semantic_convention_1.default.GEN_AI_OPERATION]: operationType,
[semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL]: LlamaIndexWrapper.aiSystem,
},
});
return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), () => {
const startTime = Date.now();
const onSuccess = (response) => {
try {
const duration = (Date.now() - startTime) / 1000;
base_wrapper_1.default.setBaseSpanAttributes(span, {
genAIEndpoint: endpoint,
model: requestModel,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
});
span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_OPERATION_DURATION, duration);
const sourceNodes = response?.sourceNodes || response?.source_nodes || [];
if (sourceNodes.length > 0) {
span.setAttribute(semantic_convention_1.default.GEN_AI_RETRIEVAL_SOURCE, JSON.stringify(sourceNodes.slice(0, 5).map((n) => ({
id: n.node?.id_ || n.id_ || '',
score: n.score,
text: n.node?.text?.slice(0, 200) || '',
}))));
}
if (config_1.default.captureMessageContent) {
const queryStr = typeof args[0] === 'string'
? args[0]
: args[0]?.query || args[0]?.queryStr || '';
span.setAttribute(semantic_convention_1.default.GEN_AI_INPUT_MESSAGES, helpers_1.default.buildInputMessages([{ role: 'user', content: queryStr }]));
const responseText = typeof response?.response === 'string'
? response.response
: response?.message?.content || response?.toString?.() || '';
span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES, helpers_1.default.buildOutputMessages(responseText, 'stop'));
}
base_wrapper_1.default.recordMetrics(span, {
genAIEndpoint: endpoint,
model: requestModel,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
});
}
catch { /* swallow */ }
span.end();
return response;
};
const onError = (e) => {
helpers_1.default.handleException(span, e);
base_wrapper_1.default.recordMetrics(span, {
genAIEndpoint: endpoint,
model: requestModel,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
errorType: e?.constructor?.name || '_OTHER',
});
span.end();
throw e;
};
try {
const result = originalMethod.apply(this, args);
if (result && typeof result.then === 'function') {
return result.then(onSuccess).catch(onError);
}
return onSuccess(result);
}
catch (e) {
return onError(e);
}
});
};
};
}
// ---------------------------------------------------------------------------
// Chat engine chat patch — framework span with chat content
// Mirrors Python: chat engine operations -> operation_type "invoke_workflow"
// ---------------------------------------------------------------------------
static _patchChatEngineChat(tracer) {
const endpoint = 'chat_engine_chat';
const operationType = semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK;
return (originalMethod) => {
return function (...args) {
const requestModel = LlamaIndexWrapper._extractModel(this);
const { address, port } = LlamaIndexWrapper._extractServerInfo(this?.llm || this?._llm || this);
const workflowName = this?.constructor?.name || 'chat_engine';
const spanName = `${operationType} ${workflowName}`;
const span = tracer.startSpan(spanName, {
kind: api_1.SpanKind.INTERNAL,
attributes: {
[semantic_convention_1.default.GEN_AI_OPERATION]: operationType,
[semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL]: LlamaIndexWrapper.aiSystem,
[semantic_convention_1.default.GEN_AI_WORKFLOW_NAME]: workflowName,
},
});
return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), () => {
const startTime = Date.now();
const onSuccess = (response) => {
try {
const duration = (Date.now() - startTime) / 1000;
base_wrapper_1.default.setBaseSpanAttributes(span, {
genAIEndpoint: endpoint,
model: requestModel,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
});
span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_OPERATION_DURATION, duration);
if (config_1.default.captureMessageContent) {
const messageInput = args[0];
const message = typeof messageInput === 'string'
? messageInput
: messageInput?.message || messageInput?.content || '';
span.setAttribute(semantic_convention_1.default.GEN_AI_INPUT_MESSAGES, helpers_1.default.buildInputMessages([{ role: 'user', content: message }]));
const responseContent = response?.message?.content
|| (typeof response?.response === 'string' ? response.response : '')
|| response?.toString?.() || '';
span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES, helpers_1.default.buildOutputMessages(responseContent, 'stop'));
}
base_wrapper_1.default.recordMetrics(span, {
genAIEndpoint: endpoint,
model: requestModel,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
});
}
catch { /* swallow */ }
span.end();
return response;
};
const onError = (e) => {
helpers_1.default.handleException(span, e);
base_wrapper_1.default.recordMetrics(span, {
genAIEndpoint: endpoint,
model: requestModel,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
errorType: e?.constructor?.name || '_OTHER',
});
span.end();
throw e;
};
try {
const result = originalMethod.apply(this, args);
if (result && typeof result.then === 'function') {
return result.then(onSuccess).catch(onError);
}
return onSuccess(result);
}
catch (e) {
return onError(e);
}
});
};
};
}
// ---------------------------------------------------------------------------
// Retriever retrieve patch — retrieval span
// Mirrors Python: BaseRetriever.retrieve -> operation_type "retrieval"
// ---------------------------------------------------------------------------
static _patchRetrieverRetrieve(tracer) {
const endpoint = 'retriever_retrieve';
const operationType = OPERATION_MAP[endpoint];
return (originalMethod) => {
return function (...args) {
const { address, port } = LlamaIndexWrapper._extractServerInfo(this);
const spanName = `${operationType} ${endpoint}`;
const span = tracer.startSpan(spanName, {
kind: api_1.SpanKind.CLIENT,
attributes: {
[semantic_convention_1.default.GEN_AI_OPERATION]: operationType,
[semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL]: LlamaIndexWrapper.aiSystem,
},
});
return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), () => {
const startTime = Date.now();
const model = LlamaIndexWrapper._extractModel(this);
const onSuccess = (response) => {
try {
const duration = (Date.now() - startTime) / 1000;
base_wrapper_1.default.setBaseSpanAttributes(span, {
genAIEndpoint: endpoint,
model,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
});
span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_OPERATION_DURATION, duration);
if (Array.isArray(response) && response.length > 0) {
span.setAttribute(semantic_convention_1.default.GEN_AI_RETRIEVAL_SOURCE, JSON.stringify(response.slice(0, 5).map((n) => ({
id: n.node?.id_ || n.id_ || '',
score: n.score,
text: n.node?.text?.slice(0, 200) || n.text?.slice(0, 200) || '',
}))));
}
if (config_1.default.captureMessageContent) {
const queryStr = typeof args[0] === 'string'
? args[0]
: args[0]?.query || args[0]?.queryStr || '';
span.setAttribute(semantic_convention_1.default.GEN_AI_INPUT_MESSAGES, helpers_1.default.buildInputMessages([{ role: 'user', content: queryStr }]));
}
base_wrapper_1.default.recordMetrics(span, {
genAIEndpoint: endpoint,
model,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
});
}
catch { /* swallow */ }
span.end();
return response;
};
const onError = (e) => {
helpers_1.default.handleException(span, e);
base_wrapper_1.default.recordMetrics(span, {
genAIEndpoint: endpoint,
model,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
errorType: e?.constructor?.name || '_OTHER',
});
span.end();
throw e;
};
try {
const result = originalMethod.apply(this, args);
if (result && typeof result.then === 'function') {
return result.then(onSuccess).catch(onError);
}
return onSuccess(result);
}
catch (e) {
return onError(e);
}
});
};
};
}
// ---------------------------------------------------------------------------
// Embedding patch — embeddings span
// Mirrors Python: BaseEmbedding.get_text_embedding_batch -> "embeddings"
// ---------------------------------------------------------------------------
static _patchEmbedding(tracer, endpoint = 'embedding_generate') {
const operationType = OPERATION_MAP[endpoint] || semantic_convention_1.default.GEN_AI_OPERATION_TYPE_EMBEDDING;
return (originalMethod) => {
return function (...args) {
const model = LlamaIndexWrapper._extractModel(this);
const { address, port } = LlamaIndexWrapper._extractServerInfo(this);
const spanName = `${operationType} ${endpoint}`;
const span = tracer.startSpan(spanName, {
kind: api_1.SpanKind.CLIENT,
attributes: {
[semantic_convention_1.default.GEN_AI_OPERATION]: operationType,
[semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL]: LlamaIndexWrapper.aiSystem,
[semantic_convention_1.default.GEN_AI_REQUEST_MODEL]: model,
[semantic_convention_1.default.SERVER_ADDRESS]: address,
[semantic_convention_1.default.SERVER_PORT]: port,
},
});
return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), () => {
const startTime = Date.now();
const onSuccess = (response) => {
try {
const duration = (Date.now() - startTime) / 1000;
base_wrapper_1.default.setBaseSpanAttributes(span, {
genAIEndpoint: endpoint,
model,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
});
span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_OPERATION_DURATION, duration);
if (Array.isArray(response) && response.length > 0) {
if (Array.isArray(response[0])) {
span.setAttribute(semantic_convention_1.default.GEN_AI_EMBEDDINGS_DIMENSION_COUNT, response[0].length);
}
else if (typeof response[0] === 'number') {
span.setAttribute(semantic_convention_1.default.GEN_AI_EMBEDDINGS_DIMENSION_COUNT, response.length);
}
}
base_wrapper_1.default.recordMetrics(span, {
genAIEndpoint: endpoint,
model,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
});
}
catch { /* swallow */ }
span.end();
return response;
};
const onError = (e) => {
helpers_1.default.handleException(span, e);
base_wrapper_1.default.recordMetrics(span, {
genAIEndpoint: endpoint,
model,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
errorType: e?.constructor?.name || '_OTHER',
});
span.end();
throw e;
};
try {
const result = (0, helpers_1.runWithFrameworkLlm)(() => originalMethod.apply(this, args));
if (result && typeof result.then === 'function') {
return result.then(onSuccess).catch(onError);
}
return onSuccess(result);
}
catch (e) {
return onError(e);
}
});
};
};
}
// ---------------------------------------------------------------------------
// Generic framework method patch — for index, document, synthesizer, etc.
// Mirrors Python: common_llamaindex_logic with framework-level attributes
// ---------------------------------------------------------------------------
static _patchFrameworkMethod(tracer, endpoint) {
const operationType = OPERATION_MAP[endpoint] || semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK;
return (originalMethod) => {
return function (...args) {
const { address, port } = LlamaIndexWrapper._extractServerInfo(this);
const spanName = `${operationType} ${endpoint}`;
const span = tracer.startSpan(spanName, {
kind: api_1.SpanKind.CLIENT,
attributes: {
[semantic_convention_1.default.GEN_AI_OPERATION]: operationType,
[semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL]: LlamaIndexWrapper.aiSystem,
},
});
return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), () => {
const startTime = Date.now();
const model = LlamaIndexWrapper._extractModel(this);
const onSuccess = (response) => {
try {
const duration = (Date.now() - startTime) / 1000;
base_wrapper_1.default.setBaseSpanAttributes(span, {
genAIEndpoint: endpoint,
model,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
});
span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_OPERATION_DURATION, duration);
base_wrapper_1.default.recordMetrics(span, {
genAIEndpoint: endpoint,
model,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
});
}
catch { /* swallow */ }
span.end();
return response;
};
const onError = (e) => {
helpers_1.default.handleException(span, e);
base_wrapper_1.default.recordMetrics(span, {
genAIEndpoint: endpoint,
model,
aiSystem: LlamaIndexWrapper.aiSystem,
serverAddress: address,
serverPort: port,
errorType: e?.constructor?.name || '_OTHER',
});
span.end();
throw e;
};
try {
const result = originalMethod.apply(this, args);
if (result && typeof result.then === 'function') {
return result.then(onSuccess).catch(onError);
}
return onSuccess(result);
}
catch (e) {
return onError(e);
}
});
};
};
}
}
LlamaIndexWrapper.aiSystem = semantic_convention_1.default.GEN_AI_SYSTEM_LLAMAINDEX;
exports.default = LlamaIndexWrapper;
//# sourceMappingURL=wrapper.js.map