Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Failing after 34s
Build & Deploy / 🏗️ Build (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
157 lines
5.0 KiB
Plaintext
157 lines
5.0 KiB
Plaintext
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
|
const genAiAttributes = require('./gen-ai-attributes.js');
|
|
const messageTruncation = require('./messageTruncation.js');
|
|
|
|
/**
|
|
* Maps AI method paths to OpenTelemetry semantic convention operation names
|
|
* @see https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/#llm-request-spans
|
|
*/
|
|
function getFinalOperationName(methodPath) {
|
|
if (methodPath.includes('messages')) {
|
|
return 'chat';
|
|
}
|
|
if (methodPath.includes('completions')) {
|
|
return 'text_completion';
|
|
}
|
|
// Google GenAI: models.generateContent* -> generate_content (actually generates AI responses)
|
|
if (methodPath.includes('generateContent')) {
|
|
return 'generate_content';
|
|
}
|
|
// Anthropic: models.get/retrieve -> models (metadata retrieval only)
|
|
if (methodPath.includes('models')) {
|
|
return 'models';
|
|
}
|
|
if (methodPath.includes('chat')) {
|
|
return 'chat';
|
|
}
|
|
return methodPath.split('.').pop() || 'unknown';
|
|
}
|
|
|
|
/**
|
|
* Get the span operation for AI methods
|
|
* Following Sentry's convention: "gen_ai.{operation_name}"
|
|
*/
|
|
function getSpanOperation(methodPath) {
|
|
return `gen_ai.${getFinalOperationName(methodPath)}`;
|
|
}
|
|
|
|
/**
|
|
* Build method path from current traversal
|
|
*/
|
|
function buildMethodPath(currentPath, prop) {
|
|
return currentPath ? `${currentPath}.${prop}` : prop;
|
|
}
|
|
|
|
/**
|
|
* Set token usage attributes
|
|
* @param span - The span to add attributes to
|
|
* @param promptTokens - The number of prompt tokens
|
|
* @param completionTokens - The number of completion tokens
|
|
* @param cachedInputTokens - The number of cached input tokens
|
|
* @param cachedOutputTokens - The number of cached output tokens
|
|
*/
|
|
function setTokenUsageAttributes(
|
|
span,
|
|
promptTokens,
|
|
completionTokens,
|
|
cachedInputTokens,
|
|
cachedOutputTokens,
|
|
) {
|
|
if (promptTokens !== undefined) {
|
|
span.setAttributes({
|
|
[genAiAttributes.GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: promptTokens,
|
|
});
|
|
}
|
|
if (completionTokens !== undefined) {
|
|
span.setAttributes({
|
|
[genAiAttributes.GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: completionTokens,
|
|
});
|
|
}
|
|
if (
|
|
promptTokens !== undefined ||
|
|
completionTokens !== undefined ||
|
|
cachedInputTokens !== undefined ||
|
|
cachedOutputTokens !== undefined
|
|
) {
|
|
/**
|
|
* Total input tokens in a request is the summation of `input_tokens`,
|
|
* `cache_creation_input_tokens`, and `cache_read_input_tokens`.
|
|
*/
|
|
const totalTokens =
|
|
(promptTokens ?? 0) + (completionTokens ?? 0) + (cachedInputTokens ?? 0) + (cachedOutputTokens ?? 0);
|
|
|
|
span.setAttributes({
|
|
[genAiAttributes.GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: totalTokens,
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the truncated JSON string for a string or array of strings.
|
|
*
|
|
* @param value - The string or array of strings to truncate
|
|
* @returns The truncated JSON string
|
|
*/
|
|
function getTruncatedJsonString(value) {
|
|
if (typeof value === 'string') {
|
|
// Some values are already JSON strings, so we don't need to duplicate the JSON parsing
|
|
return messageTruncation.truncateGenAiStringInput(value);
|
|
}
|
|
if (Array.isArray(value)) {
|
|
// truncateGenAiMessages returns an array of strings, so we need to stringify it
|
|
const truncatedMessages = messageTruncation.truncateGenAiMessages(value);
|
|
return JSON.stringify(truncatedMessages);
|
|
}
|
|
// value is an object, so we need to stringify it
|
|
return JSON.stringify(value);
|
|
}
|
|
|
|
/**
|
|
* Extract system instructions from messages array.
|
|
* Finds the first system message and formats it according to OpenTelemetry semantic conventions.
|
|
*
|
|
* @param messages - Array of messages to extract system instructions from
|
|
* @returns systemInstructions (JSON string) and filteredMessages (without system message)
|
|
*/
|
|
function extractSystemInstructions(messages)
|
|
|
|
{
|
|
if (!Array.isArray(messages)) {
|
|
return { systemInstructions: undefined, filteredMessages: messages };
|
|
}
|
|
|
|
const systemMessageIndex = messages.findIndex(
|
|
msg => msg && typeof msg === 'object' && 'role' in msg && (msg ).role === 'system',
|
|
);
|
|
|
|
if (systemMessageIndex === -1) {
|
|
return { systemInstructions: undefined, filteredMessages: messages };
|
|
}
|
|
|
|
const systemMessage = messages[systemMessageIndex] ;
|
|
const systemContent =
|
|
typeof systemMessage.content === 'string'
|
|
? systemMessage.content
|
|
: systemMessage.content !== undefined
|
|
? JSON.stringify(systemMessage.content)
|
|
: undefined;
|
|
|
|
if (!systemContent) {
|
|
return { systemInstructions: undefined, filteredMessages: messages };
|
|
}
|
|
|
|
const systemInstructions = JSON.stringify([{ type: 'text', content: systemContent }]);
|
|
const filteredMessages = [...messages.slice(0, systemMessageIndex), ...messages.slice(systemMessageIndex + 1)];
|
|
|
|
return { systemInstructions, filteredMessages };
|
|
}
|
|
|
|
exports.buildMethodPath = buildMethodPath;
|
|
exports.extractSystemInstructions = extractSystemInstructions;
|
|
exports.getFinalOperationName = getFinalOperationName;
|
|
exports.getSpanOperation = getSpanOperation;
|
|
exports.getTruncatedJsonString = getTruncatedJsonString;
|
|
exports.setTokenUsageAttributes = setTokenUsageAttributes;
|
|
//# sourceMappingURL=utils.js.map
|