Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | 16x 16x 16x 15x 1x 16x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 15x | import type { UsageStats } from '../types/index.js';
interface SpanLike {
setAttribute(
key: string,
value: string | number | boolean | string[] | undefined,
): unknown;
}
interface GenAiMessage {
role: string;
content: unknown;
}
function providerFromModel(model: string | undefined): string | undefined {
Iif (!model) {
return undefined;
}
const slashIndex = model.indexOf('/');
if (slashIndex > 0) {
return model.slice(0, slashIndex);
}
return undefined;
}
/** Resolve the OpenTelemetry GenAI provider name from runtime and model selectors. */
export function genAiProviderName(runtime: string | undefined, model: string | undefined): string {
return providerFromModel(model) ?? (runtime === 'pi' ? 'pi' : 'anthropic');
}
/** Set GenAI token usage attributes expected by Sentry AI monitoring. */
export function setGenAiUsageAttrs(span: SpanLike, usage: UsageStats): void {
span.setAttribute('gen_ai.usage.input_tokens', usage.inputTokens);
span.setAttribute('gen_ai.usage.output_tokens', usage.outputTokens);
span.setAttribute('gen_ai.usage.input_tokens.cached', usage.cacheReadInputTokens ?? 0);
span.setAttribute('gen_ai.usage.input_tokens.cache_write', usage.cacheCreationInputTokens ?? 0);
span.setAttribute('gen_ai.usage.total_tokens', usage.inputTokens + usage.outputTokens);
}
/** Set OpenTelemetry GenAI system-instruction attributes for prompt spans. */
export function setGenAiSystemInstructionsAttr(span: SpanLike, systemPrompt: string): void {
span.setAttribute('gen_ai.system_instructions', JSON.stringify([
{ type: 'text', content: systemPrompt },
]));
}
function normalizeContentPart(part: unknown): Record<string, unknown> {
if (!part || typeof part !== 'object') {
return { type: 'text', content: String(part ?? '') };
}
const block = part as Record<string, unknown>;
if (block['type'] === 'text' && typeof block['text'] === 'string') {
return { type: 'text', content: block['text'] };
}
if (block['type'] === 'tool_use') {
return {
type: 'tool_call',
id: block['id'],
name: block['name'],
arguments: block['input'],
};
}
if (block['type'] === 'tool_result') {
return {
type: 'tool_call_response',
id: block['tool_use_id'],
result: block['content'],
};
}
return { ...block };
}
function normalizeMessage(message: GenAiMessage): Record<string, unknown> {
const { role, content } = message;
Eif (typeof content === 'string') {
return {
role,
parts: [{ type: 'text', content }],
};
}
if (Array.isArray(content)) {
return {
role,
parts: content.map(normalizeContentPart),
};
}
return {
role,
parts: [normalizeContentPart(content)],
};
}
/** Set OpenTelemetry GenAI input message attributes using the current schema. */
export function setGenAiInputMessagesAttr(span: SpanLike, messages: GenAiMessage[]): void {
span.setAttribute('gen_ai.input.messages', JSON.stringify(messages.map(normalizeMessage)));
}
/** Set OpenTelemetry GenAI output message attributes for text responses. */
export function setGenAiOutputMessagesAttr(
span: SpanLike,
responseText: string,
finishReason?: string | null,
): void {
span.setAttribute('gen_ai.output.messages', JSON.stringify([
{
role: 'assistant',
parts: [{ type: 'text', content: responseText }],
...(finishReason ? { finish_reason: finishReason } : {}),
},
]));
}
|