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

189 lines 7.59 kB
/** * Music Generation Processing Utility * * Central registry + dispatch for music-generation handlers across * providers (Beatoven, ElevenLabs Music, Lyria, Replicate-hosted models). * * Mirrors the static-handler-registry pattern established by * `TTSProcessor` / `STTProcessor` / `VideoProcessor`. * * @module utils/musicProcessor */ import { ErrorCategory, ErrorSeverity } from "../constants/enums.js"; import { SpanSerializer, SpanStatus, SpanType, getMetricsAggregator, } from "../observability/index.js"; import { NeuroLinkError } from "./errorHandling.js"; import { logger } from "./logger.js"; /** * Music-specific error codes. */ export const MUSIC_ERROR_CODES = { PROVIDER_NOT_SUPPORTED: "MUSIC_PROVIDER_NOT_SUPPORTED", PROVIDER_NOT_CONFIGURED: "MUSIC_PROVIDER_NOT_CONFIGURED", GENERATION_FAILED: "MUSIC_GENERATION_FAILED", POLL_TIMEOUT: "MUSIC_POLL_TIMEOUT", PROMPT_REQUIRED: "MUSIC_PROMPT_REQUIRED", DURATION_INVALID: "MUSIC_DURATION_INVALID", INVALID_INPUT: "MUSIC_INVALID_INPUT", }; /** * Typed error class for music-generation failures. */ export class MusicError extends NeuroLinkError { constructor(options) { super({ code: options.code, message: options.message, category: options.category ?? ErrorCategory.EXECUTION, severity: options.severity ?? ErrorSeverity.HIGH, retriable: options.retriable ?? false, context: options.context, originalError: options.originalError, }); this.name = "MusicError"; } } /** * Static processor managing the music handler registry. */ export class MusicProcessor { static handlers = new Map(); /** * Register a music handler for a specific provider. */ static registerHandler(providerName, handler) { if (!providerName) { throw new Error("Provider name is required"); } if (!handler) { throw new Error("Handler is required"); } const key = providerName.toLowerCase(); if (this.handlers.has(key)) { logger.warn(`[MusicProcessor] Overwriting existing handler for provider: ${key}`); } this.handlers.set(key, handler); logger.debug(`[MusicProcessor] Registered music handler: ${key}`); } /** * Check if a provider has a registered music handler. */ static supports(providerName) { if (!providerName) { return false; } return this.handlers.has(providerName.toLowerCase()); } /** * List the names of all registered providers. */ static listProviders() { return Array.from(this.handlers.keys()); } static getHandler(providerName) { return this.handlers.get(providerName.toLowerCase()); } static buildSpanAttributes(provider, options) { return { "music.operation": "generate", "music.provider": provider, "music.duration": options.duration, "music.format": options.format, "music.genre": options.genre, "music.mood": options.mood, }; } /** * Generate a music track via the registered handler. * * @throws MusicError on registry miss, handler-not-configured, or * generation failure. */ static async generate(provider, options) { const span = SpanSerializer.createSpan(SpanType.MEDIA_GENERATION, "music.generate", this.buildSpanAttributes(provider, options)); try { const trimmedPrompt = options.prompt?.trim(); if (!trimmedPrompt) { throw new MusicError({ code: MUSIC_ERROR_CODES.PROMPT_REQUIRED, message: "Music generation requires a non-empty prompt", category: ErrorCategory.VALIDATION, severity: ErrorSeverity.LOW, retriable: false, context: { provider }, }); } const handler = this.getHandler(provider); if (!handler) { throw new MusicError({ code: MUSIC_ERROR_CODES.PROVIDER_NOT_SUPPORTED, message: `Music provider "${provider}" is not registered. Available: ${this.listProviders().join(", ")}`, category: ErrorCategory.CONFIGURATION, severity: ErrorSeverity.HIGH, retriable: false, context: { provider, available: this.listProviders() }, }); } if (!handler.isConfigured()) { throw new MusicError({ code: MUSIC_ERROR_CODES.PROVIDER_NOT_CONFIGURED, message: `Music provider "${provider}" is not configured. Set the required credentials.`, category: ErrorCategory.CONFIGURATION, severity: ErrorSeverity.HIGH, retriable: false, context: { provider }, }); } // Optional duration validation against per-provider max. if (options.duration !== undefined && handler.maxDurationSeconds !== undefined && options.duration > handler.maxDurationSeconds) { throw new MusicError({ code: MUSIC_ERROR_CODES.DURATION_INVALID, message: `Requested duration (${options.duration}s) exceeds provider maximum (${handler.maxDurationSeconds}s) for "${provider}"`, category: ErrorCategory.VALIDATION, severity: ErrorSeverity.MEDIUM, retriable: false, context: { provider, requested: options.duration, maximum: handler.maxDurationSeconds, }, }); } logger.debug(`[MusicProcessor] Starting music generation with provider: ${provider}`); const result = await handler.generate({ ...options, prompt: trimmedPrompt, }); const ended = SpanSerializer.endSpan(span, SpanStatus.OK); getMetricsAggregator().recordSpan(ended); logger.info(`[MusicProcessor] Generated ${result.size} bytes (${provider})`); return result; } catch (err) { const ended = SpanSerializer.endSpan(span, SpanStatus.ERROR, err instanceof Error ? err.message : String(err)); getMetricsAggregator().recordSpan(ended); if (err instanceof MusicError) { throw err; } const message = err instanceof Error ? err.message : String(err); throw new MusicError({ code: MUSIC_ERROR_CODES.GENERATION_FAILED, message: `Music generation failed for provider "${provider}": ${message}`, category: ErrorCategory.EXECUTION, severity: ErrorSeverity.HIGH, retriable: true, context: { provider, duration: options.duration, format: options.format, genre: options.genre, mood: options.mood, tempo: options.tempo, }, originalError: err instanceof Error ? err : undefined, }); } } } //# sourceMappingURL=musicProcessor.js.map