openlit
Version:
OpenTelemetry-native Auto instrumentation library for monitoring LLM Applications, facilitating the integration of observability into your GenAI-driven projects
436 lines • 23.5 kB
JavaScript
;
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 = __importDefault(require("../../helpers"));
const semantic_convention_1 = __importDefault(require("../../semantic-convention"));
const base_wrapper_1 = __importDefault(require("../base-wrapper"));
class OpenAIWrapper extends base_wrapper_1.default {
static _patchChatCompletionCreate(tracer) {
const genAIEndpoint = 'openai.resources.chat.completions';
return (originalMethod) => {
return async function (...args) {
const span = tracer.startSpan(genAIEndpoint, { kind: api_1.SpanKind.CLIENT });
return api_1.context
.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
return originalMethod.apply(this, args);
})
.then((response) => {
const { stream = false } = args[0];
if (!!stream) {
return helpers_1.default.createStreamProxy(response, OpenAIWrapper._chatCompletionGenerator({
args,
genAIEndpoint,
response,
span,
}));
}
return OpenAIWrapper._chatCompletion({ args, genAIEndpoint, response, span });
})
.catch((e) => {
helpers_1.default.handleException(span, e);
span.end();
});
};
};
}
static async _chatCompletion({ args, genAIEndpoint, response, span, }) {
try {
await OpenAIWrapper._chatCompletionCommonSetter({
args,
genAIEndpoint,
result: response,
span,
});
return response;
}
catch (e) {
helpers_1.default.handleException(span, e);
}
finally {
span.end();
}
}
static async *_chatCompletionGenerator({ args, genAIEndpoint, response, span, }) {
try {
const { messages } = args[0];
let { tools } = args[0];
const result = {
id: '0',
created: -1,
model: '',
choices: [
{
index: 0,
logprobs: null,
finish_reason: 'stop',
message: { role: 'assistant', content: '' },
},
],
usage: {
prompt_tokens: 0,
completion_tokens: 0,
total_tokens: 0,
},
};
for await (const chunk of response) {
result.id = chunk.id;
result.created = chunk.created;
result.model = chunk.model;
if (chunk.choices[0]?.finish_reason) {
result.choices[0].finish_reason = chunk.choices[0].finish_reason;
}
if (chunk.choices[0]?.logprobs) {
result.choices[0].logprobs = chunk.choices[0].logprobs;
}
if (chunk.choices[0]?.delta.content) {
result.choices[0].message.content += chunk.choices[0].delta.content;
}
if (chunk.choices[0]?.delta.tool_calls) {
tools = true;
}
yield chunk;
}
let promptTokens = 0;
for (const message of messages || []) {
promptTokens += helpers_1.default.openaiTokens(message.content, result.model) ?? 0;
}
const completionTokens = helpers_1.default.openaiTokens(result.choices[0].message.content ?? '', result.model);
if (completionTokens) {
result.usage = {
prompt_tokens: promptTokens,
completion_tokens: completionTokens,
total_tokens: promptTokens + completionTokens,
};
}
args[0].tools = tools;
await OpenAIWrapper._chatCompletionCommonSetter({
args,
genAIEndpoint,
result,
span,
});
return result;
}
catch (e) {
helpers_1.default.handleException(span, e);
}
finally {
span.end();
}
}
static async _chatCompletionCommonSetter({ args, genAIEndpoint, result, span, }) {
const traceContent = config_1.default.traceContent;
const { messages, frequency_penalty = 0, max_tokens = null, n = 1, presence_penalty = 0, seed = null, temperature = 1, top_p, user, stream = false, tools, } = args[0];
// Request Params attributes : Start
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_TOP_P, top_p || 1);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_MAX_TOKENS, max_tokens);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_TEMPERATURE, temperature);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_PRESENCE_PENALTY, presence_penalty);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_FREQUENCY_PENALTY, frequency_penalty);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_SEED, seed);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IS_STREAM, stream);
if (traceContent) {
// Format 'messages' into a single string
const messagePrompt = messages || [];
const formattedMessages = [];
for (const message of messagePrompt) {
const role = message.role;
const content = message.content;
if (Array.isArray(content)) {
const contentStr = content
.map((item) => {
if ('type' in item) {
return `${item.type}: ${item.text ? item.text : item.image_url}`;
}
else {
return `text: ${item.text}`;
}
})
.join(', ');
formattedMessages.push(`${role}: ${contentStr}`);
}
else {
formattedMessages.push(`${role}: ${content}`);
}
}
const prompt = formattedMessages.join('\n');
span.setAttribute(semantic_convention_1.default.GEN_AI_CONTENT_PROMPT, prompt);
}
// Request Params attributes : End
span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT);
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_ID, result.id);
const model = result.model || 'gpt-3.5-turbo';
const pricingInfo = await config_1.default.updatePricingJson(config_1.default.pricing_json);
// Calculate cost of the operation
const cost = helpers_1.default.getChatModelCost(model, pricingInfo, result.usage.prompt_tokens, result.usage.completion_tokens);
OpenAIWrapper.setBaseSpanAttributes(span, {
genAIEndpoint,
model,
user,
cost,
aiSystem: OpenAIWrapper.aiSystem,
});
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS, result.usage.prompt_tokens);
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS, result.usage.completion_tokens);
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_TOTAL_TOKENS, result.usage.total_tokens);
if (result.choices[0].finish_reason) {
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_FINISH_REASON, result.choices[0].finish_reason);
}
if (tools) {
span.setAttribute(semantic_convention_1.default.GEN_AI_CONTENT_COMPLETION, 'Function called with tools');
}
else {
if (traceContent) {
if (n === 1) {
span.setAttribute(semantic_convention_1.default.GEN_AI_CONTENT_COMPLETION, result.choices[0].message.content);
}
else {
let i = 0;
while (i < n) {
const attribute_name = `${semantic_convention_1.default.GEN_AI_CONTENT_COMPLETION}.[i]`;
span.setAttribute(attribute_name, result.choices[i].message.content);
i += 1;
}
}
}
}
}
static _patchEmbedding(tracer) {
const genAIEndpoint = 'openai.resources.embeddings';
const traceContent = config_1.default.traceContent;
return (originalMethod) => {
return async function (...args) {
const span = tracer.startSpan(genAIEndpoint, { kind: api_1.SpanKind.CLIENT });
return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
try {
const response = await originalMethod.apply(this, args);
const model = response.model || 'text-embedding-ada-002';
const pricingInfo = await config_1.default.updatePricingJson(config_1.default.pricing_json);
const cost = helpers_1.default.getEmbedModelCost(model, pricingInfo, response.usage.prompt_tokens);
span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_EMBEDDING);
const { dimensions, encoding_format = 'float', input, user } = args[0];
// Set base span attribues
OpenAIWrapper.setBaseSpanAttributes(span, {
genAIEndpoint,
model,
user,
cost,
aiSystem: OpenAIWrapper.aiSystem,
});
// Request Params attributes : Start
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_ENCODING_FORMATS, encoding_format);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_EMBEDDING_DIMENSION, dimensions);
if (traceContent) {
span.setAttribute(semantic_convention_1.default.GEN_AI_CONTENT_PROMPT, input);
}
// Request Params attributes : End
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS, response.usage.prompt_tokens);
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_TOTAL_TOKENS, response.usage.total_tokens);
return response;
}
catch (e) {
helpers_1.default.handleException(span, e);
}
finally {
span.end();
}
});
};
};
}
static _patchFineTune(tracer) {
const genAIEndpoint = 'openai.resources.fine_tuning.jobs';
return (originalMethod) => {
return async function (...args) {
const span = tracer.startSpan(genAIEndpoint, { kind: api_1.SpanKind.CLIENT });
return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
try {
const response = await originalMethod.apply(this, args);
const model = response.model || 'gpt-3.5-turbo';
const { hyperparameters = {}, suffix = '', training_file, user, validation_file, } = args[0];
// Set base span attribues
OpenAIWrapper.setBaseSpanAttributes(span, {
genAIEndpoint,
model,
user,
aiSystem: OpenAIWrapper.aiSystem,
});
span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FINETUNING);
// Request Params attributes : Start
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_TRAINING_FILE, training_file);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_VALIDATION_FILE, validation_file);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_FINETUNE_BATCH_SIZE, hyperparameters?.batch_size || 'auto');
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_FINETUNE_MODEL_LRM, hyperparameters?.learning_rate_multiplier || 'auto');
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_FINETUNE_MODEL_EPOCHS, hyperparameters?.n_epochs || 'auto');
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_FINETUNE_MODEL_SUFFIX, suffix);
// Request Params attributes : End
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_ID, response.id);
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS, response.usage.prompt_tokens);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_FINETUNE_STATUS, response.status);
return response;
}
catch (e) {
helpers_1.default.handleException(span, e);
}
finally {
span.end();
}
});
};
};
}
static _patchImageGenerate(tracer) {
const genAIEndpoint = 'openai.resources.images';
const traceContent = config_1.default.traceContent;
return (originalMethod) => {
return async function (...args) {
const span = tracer.startSpan(genAIEndpoint, { kind: api_1.SpanKind.CLIENT });
return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
try {
const response = await originalMethod.apply(this, args);
const { prompt, quality = 'standard', response_format = 'url', size = '1024x1024', style = 'vivid', user, } = args[0];
span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_IMAGE);
const model = response.model || 'dall-e-2';
const pricingInfo = await config_1.default.updatePricingJson(config_1.default.pricing_json);
// Calculate cost of the operation
const cost = (response.data?.length || 1) *
helpers_1.default.getImageModelCost(model, pricingInfo, size, quality);
OpenAIWrapper.setBaseSpanAttributes(span, {
genAIEndpoint,
model,
user,
cost,
aiSystem: OpenAIWrapper.aiSystem,
});
// Request Params attributes : Start
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IMAGE_SIZE, size);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IMAGE_QUALITY, quality);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IMAGE_STYLE, style);
if (traceContent) {
span.setAttribute(semantic_convention_1.default.GEN_AI_CONTENT_PROMPT, prompt);
}
// Request Params attributes : End
let imagesCount = 0;
if (response.data) {
for (const items of response.data) {
span.setAttribute(`${semantic_convention_1.default.GEN_AI_CONTENT_REVISED_PROMPT}.${imagesCount}`, items.revised_prompt || '');
const attributeName = `${semantic_convention_1.default.GEN_AI_RESPONSE_IMAGE}.${imagesCount}`;
span.setAttribute(attributeName, items[response_format]);
imagesCount++;
}
}
return response;
}
catch (e) {
helpers_1.default.handleException(span, e);
}
finally {
span.end();
}
});
};
};
}
static _patchImageVariation(tracer) {
const genAIEndpoint = 'openai.resources.images';
const traceContent = config_1.default.traceContent;
return (originalMethod) => {
return async function (...args) {
const span = tracer.startSpan(genAIEndpoint, { kind: api_1.SpanKind.CLIENT });
return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
try {
const response = await originalMethod.apply(this, args);
const { prompt, quality = 'standard', response_format = 'url', size = '1024x1024', style = 'vivid', user, } = args[0];
span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_IMAGE);
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_ID, response.created);
const model = response.model || 'dall-e-2';
const pricingInfo = await config_1.default.updatePricingJson(config_1.default.pricing_json);
// Calculate cost of the operation
const cost = (response.data?.length || 1) *
helpers_1.default.getImageModelCost(model, pricingInfo, size, quality);
OpenAIWrapper.setBaseSpanAttributes(span, {
genAIEndpoint,
model,
user,
cost,
aiSystem: OpenAIWrapper.aiSystem,
});
// Request Params attributes : Start
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IMAGE_SIZE, size);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IMAGE_QUALITY, quality);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IMAGE_STYLE, style);
if (traceContent) {
span.setAttribute(semantic_convention_1.default.GEN_AI_CONTENT_PROMPT, prompt);
}
// Request Params attributes : End
let imagesCount = 0;
if (response.data) {
for (const items of response.data) {
span.setAttribute(`${semantic_convention_1.default.GEN_AI_CONTENT_REVISED_PROMPT}.${imagesCount}`, items.revised_prompt || '');
const attributeName = `${semantic_convention_1.default.GEN_AI_RESPONSE_IMAGE}.${imagesCount}`;
span.setAttribute(attributeName, items[response_format]);
imagesCount++;
}
}
return response;
}
catch (e) {
helpers_1.default.handleException(span, e);
}
finally {
span.end();
}
});
};
};
}
static _patchAudioCreate(tracer) {
const genAIEndpoint = 'openai.resources.audio.speech';
const traceContent = config_1.default.traceContent;
return (originalMethod) => {
return async function (...args) {
const span = tracer.startSpan(genAIEndpoint, { kind: api_1.SpanKind.CLIENT });
return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
try {
const response = await originalMethod.apply(this, args);
const { input, user, voice, response_format = 'mp3', speed = 1 } = args[0];
span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AUDIO);
const model = response.model || 'tts-1';
const pricingInfo = await config_1.default.updatePricingJson(config_1.default.pricing_json);
// Calculate cost of the operation
const cost = helpers_1.default.getAudioModelCost(model, pricingInfo, input);
OpenAIWrapper.setBaseSpanAttributes(span, {
genAIEndpoint,
model,
user,
cost,
aiSystem: OpenAIWrapper.aiSystem,
});
// Request Params attributes : Start
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_AUDIO_VOICE, voice);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_AUDIO_RESPONSE_FORMAT, response_format);
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_AUDIO_SPEED, speed);
if (traceContent) {
span.setAttribute(semantic_convention_1.default.GEN_AI_CONTENT_PROMPT, input);
}
// Request Params attributes : End
return response;
}
catch (e) {
helpers_1.default.handleException(span, e);
}
finally {
span.end();
}
});
};
};
}
}
OpenAIWrapper.aiSystem = semantic_convention_1.default.GEN_AI_SYSTEM_OPENAI;
exports.default = OpenAIWrapper;
//# sourceMappingURL=wrapper.js.map