UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

132 lines 5.1 kB
import { createAnalytics } from "./analytics.js"; import { logger } from "../utils/logger.js"; import { extractTokenUsage, createEmptyTokenUsage, } from "../utils/tokenUtils.js"; import { NoOutputGeneratedError } from "ai"; /** * Base implementation for collecting analytics from Vercel AI SDK stream results */ export class BaseStreamAnalyticsCollector { /** * Collect token usage from stream result * Uses centralized tokenUtils for consistent extraction across providers */ async collectUsage(result) { try { const usage = await result.usage; if (!usage) { logger.debug("No usage data available from stream result"); return createEmptyTokenUsage(); } // Use centralized token extraction utility // Handles multiple provider formats, cache tokens, reasoning tokens, // and cache savings calculation return extractTokenUsage(usage); } catch (error) { if (NoOutputGeneratedError.isInstance(error)) { logger.debug("No output generated from stream — returning empty usage"); } else { logger.warn("Failed to collect usage from stream result", { error }); } return createEmptyTokenUsage(); } } /** * Collect response metadata from stream result */ async collectMetadata(result) { try { const [response, finishReason] = await Promise.all([ result.response, result.finishReason, ]); return { id: response?.id, model: response?.model, timestamp: response?.timestamp instanceof Date ? response.timestamp.getTime() : response?.timestamp || Date.now(), finishReason: finishReason, }; } catch (error) { if (NoOutputGeneratedError.isInstance(error)) { logger.debug("No output generated from stream — returning default metadata"); } else { logger.warn("Failed to collect metadata from stream result", { error, }); } return { timestamp: Date.now(), finishReason: "error", }; } } /** * Create comprehensive analytics from stream result */ async createAnalytics(provider, model, result, responseTime, metadata) { try { // Collect analytics data in parallel const [usage, responseMetadata] = await Promise.all([ this.collectUsage(result), this.collectMetadata(result), ]); // Get final text content and finish reason. // Guard each promise individually: AI SDK v6 rejects all of these with // NoOutputGeneratedError when the stream produced no output. const [content, finishReason, toolResults, toolCalls] = await Promise.all([ Promise.resolve(result.text).catch(() => ""), Promise.resolve(result.finishReason).catch(() => "error"), Promise.resolve(result.toolResults || []).catch(() => []), Promise.resolve(result.toolCalls || []).catch(() => []), ]); // Create comprehensive analytics return createAnalytics(provider, model, { usage, content, response: responseMetadata, finishReason: finishReason, toolResults: toolResults, toolCalls: toolCalls, }, responseTime, { ...metadata, streamingMode: true, responseId: responseMetadata.id, finishReason: finishReason, }); } catch (error) { logger.error("Failed to create analytics from stream result", { provider, model, error: error instanceof Error ? error.message : String(error), }); // Return minimal analytics on error return createAnalytics(provider, model, { usage: { input: 0, output: 0, total: 0 } }, responseTime, { ...metadata, streamingMode: true, analyticsError: true, }); } } /** * Clean up resources and force garbage collection if needed */ cleanup() { // Only force garbage collection if memory usage exceeds 500 MB const heapUsed = process.memoryUsage().heapUsed; const GC_THRESHOLD = 500 * 1024 * 1024; // 500 MB if (typeof global !== "undefined" && global.gc && heapUsed > GC_THRESHOLD) { global.gc(); } } } /** * Global instance of stream analytics collector */ export const streamAnalyticsCollector = new BaseStreamAnalyticsCollector(); //# sourceMappingURL=streamAnalytics.js.map