UNPKG

ai-functions

Version:

Core AI primitives for building intelligent applications

285 lines 9.67 kB
/** * OpenTelemetry Integration for ai-functions * * Provides instrumented wrappers and telemetry utilities for AI function calls. * * @example * ```ts * import { withTelemetry, instrumentGenerate } from 'ai-functions/telemetry' * * // Enable telemetry globally * withTelemetry({ provider: createConsoleTelemetryProvider() }, async () => { * const text = await generate('Explain quantum computing') * }) * * // Or instrument individual functions * const traced = instrumentGenerate(generate) * const text = await traced('Explain quantum computing') * ``` * * @packageDocumentation */ import { getTracer, getMeter, getLogger, setTelemetryProvider, getTelemetryProvider, createAIMetrics, SemanticAttributes, MetricNames, } from '@org.ai/types'; // Package info const PACKAGE_NAME = 'ai-functions'; const PACKAGE_VERSION = '2.1.4'; // ============================================================================ // Package-level Telemetry // ============================================================================ let packageTracer; let packageMeter; let packageLogger; let aiMetrics; /** * Get the tracer for ai-functions */ export function getFunctionsTracer() { if (!packageTracer) { packageTracer = getTracer(PACKAGE_NAME, PACKAGE_VERSION); } return packageTracer; } /** * Get the meter for ai-functions */ export function getFunctionsMeter() { if (!packageMeter) { packageMeter = getMeter(PACKAGE_NAME, PACKAGE_VERSION); } return packageMeter; } /** * Get the logger for ai-functions */ export function getFunctionsLogger() { if (!packageLogger) { packageLogger = getLogger(PACKAGE_NAME); } return packageLogger; } /** * Get AI metrics for the package */ export function getAIMetrics() { if (!aiMetrics) { aiMetrics = createAIMetrics(getFunctionsMeter()); } return aiMetrics; } /** * Reset cached telemetry instances (useful after changing provider) */ export function resetTelemetry() { packageTracer = undefined; packageMeter = undefined; packageLogger = undefined; aiMetrics = undefined; } /** * Execute a function with telemetry enabled * * @example * ```ts * import { createConsoleTelemetryProvider } from '@org.ai/types' * * await withTelemetry({ * provider: createConsoleTelemetryProvider() * }, async () => { * const text = await generate('Hello') * }) * ``` */ export async function withTelemetry(options, fn) { const previousProvider = getTelemetryProvider(); if (options.provider) { setTelemetryProvider(options.provider); resetTelemetry(); } try { return await fn(); } finally { if (options.provider) { setTelemetryProvider(previousProvider); resetTelemetry(); } } } // ============================================================================ // Instrumentation Wrappers // ============================================================================ /** * Record AI request metrics */ export function recordAIRequest(params) { const metrics = getAIMetrics(); const labels = { model: params.model, provider: params.provider, status: params.success ? 'success' : 'error', }; metrics.requestTotal.add(1, labels); metrics.requestDuration.record(params.durationMs, labels); metrics.tokensUsed.add(params.inputTokens + params.outputTokens, { ...labels, type: 'total', }); if (!params.success) { metrics.requestErrors.add(1, labels); } if (params.costUsd !== undefined) { metrics.costTotal.add(params.costUsd, labels); } } /** * Create a traced version of any async function */ export function traced(name, fn, options = {}) { return async (...args) => { const tracer = getFunctionsTracer(); const logger = getFunctionsLogger(); const attributes = { [SemanticAttributes.AI_FUNCTION_NAME]: name, ...(options.getAttributes ? options.getAttributes(...args) : {}), }; return tracer.withSpan(name, { kind: options.kind || 'internal', attributes }, async (span) => { logger.debug(`Executing ${name}`, { attributes }); try { const result = await fn(...args); span.setStatus('ok'); return result; } catch (error) { const message = error instanceof Error ? error.message : String(error); span.setStatus('error', message); span.setAttribute(SemanticAttributes.EXCEPTION_MESSAGE, message); if (error instanceof Error && error.stack) { span.setAttribute(SemanticAttributes.EXCEPTION_STACKTRACE, error.stack); } logger.error(`Error in ${name}`, error instanceof Error ? error : undefined, { attributes, }); throw error; } }); }; } /** * Create instrumented generate function * * @example * ```ts * import { generate } from 'ai-functions' * import { instrumentGenerate } from 'ai-functions/telemetry' * * const tracedGenerate = instrumentGenerate(generate) * const result = await tracedGenerate('text', 'Hello world') * ``` */ export function instrumentGenerate(generateFn) { return traced('ai.generate', generateFn, { kind: 'client', getAttributes: (type) => ({ [SemanticAttributes.AI_FUNCTION_TYPE]: type || 'text', }), }); } /** * Create instrumented AI function with full metrics */ export function instrumentAIFunction(name, fn, options = {}) { return async (input) => { const tracer = getFunctionsTracer(); const logger = getFunctionsLogger(); const startTime = Date.now(); const attributes = { [SemanticAttributes.AI_FUNCTION_NAME]: name, ...(options.model && { [SemanticAttributes.AI_MODEL]: options.model }), ...(options.provider && { [SemanticAttributes.AI_PROVIDER]: options.provider }), }; return tracer.withSpan(`ai.function.${name}`, { kind: 'client', attributes }, async (span) => { logger.info(`AI function ${name} started`, { model: options.model }); try { const output = await fn(input); const durationMs = Date.now() - startTime; // Record metrics const tokens = options.getTokens?.(input, output); const cost = options.getCost?.(input, output); if (tokens) { span.setAttribute(SemanticAttributes.AI_INPUT_TOKENS, tokens.input); span.setAttribute(SemanticAttributes.AI_OUTPUT_TOKENS, tokens.output); span.setAttribute(SemanticAttributes.AI_TOTAL_TOKENS, tokens.input + tokens.output); } if (cost !== undefined) { span.setAttribute(SemanticAttributes.AI_COST_USD, cost); } recordAIRequest({ model: options.model || 'unknown', provider: options.provider || 'unknown', inputTokens: tokens?.input || 0, outputTokens: tokens?.output || 0, durationMs, success: true, costUsd: cost, }); span.setStatus('ok'); logger.info(`AI function ${name} completed`, { durationMs, tokens: tokens?.input !== undefined ? tokens.input + (tokens.output || 0) : undefined, }); return output; } catch (error) { const durationMs = Date.now() - startTime; const message = error instanceof Error ? error.message : String(error); recordAIRequest({ model: options.model || 'unknown', provider: options.provider || 'unknown', inputTokens: 0, outputTokens: 0, durationMs, success: false, }); span.setStatus('error', message); logger.error(`AI function ${name} failed`, error instanceof Error ? error : undefined, { durationMs, }); throw error; } }); }; } // ============================================================================ // Span Helpers // ============================================================================ /** * Start a span for an AI operation */ export function startAISpan(name, attributes) { const tracer = getFunctionsTracer(); return tracer.startSpan(name, { kind: 'client', attributes: { [SemanticAttributes.SERVICE_NAME]: PACKAGE_NAME, ...attributes, }, }); } /** * Execute code within a span */ export async function withAISpan(name, fn, attributes) { const tracer = getFunctionsTracer(); return tracer.withSpan(name, { kind: 'client', attributes }, fn); } // ============================================================================ // Re-exports from @org.ai/types // ============================================================================ export { // Global functions getTracer, getMeter, getLogger, setTelemetryProvider, getTelemetryProvider, // Constants SemanticAttributes, MetricNames, // Utilities createAIMetrics, } from '@org.ai/types'; //# sourceMappingURL=telemetry.js.map