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

173 lines 6.46 kB
/** * Google Lyria 3 Pro Music Handler * * Synchronous generation against the Generative Language API. Returns * audio inline as base64 in the response. * * @module music/providers/LyriaMusic * @see https://ai.google.dev/gemini-api/docs/music-generation */ import { ErrorCategory, ErrorSeverity } from "../../constants/enums.js"; import { MUSIC_ERROR_CODES, MusicError } from "../../utils/musicProcessor.js"; import { logger } from "../../utils/logger.js"; import { sanitizeForLog } from "../../utils/logSanitize.js"; const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta"; const DEFAULT_MODEL = "lyria-3-pro-preview"; const REQUEST_TIMEOUT_MS = 120_000; /** * Google Lyria 3 Pro Music Handler. * * Auth: `Authorization: Bearer ${GOOGLE_API_KEY}` or query-string * `?key=${GOOGLE_API_KEY}` (the latter is more compatible with the * Generative Language endpoints today). */ export class LyriaMusic { maxDurationSeconds = 30; supportedFormats = ["wav"]; supportedGenres = [ "ambient", "classical", "electronic", "jazz", "rock", "pop", "lo-fi", "cinematic", "orchestral", "world", ]; apiKey; baseUrl; model; constructor(apiKey) { const resolved = (apiKey ?? process.env.GOOGLE_AI_LYRIA_API_KEY ?? process.env.GOOGLE_API_KEY ?? process.env.GOOGLE_AI_API_KEY ?? process.env.GEMINI_API_KEY ?? "").trim(); this.apiKey = resolved.length > 0 ? resolved : null; this.baseUrl = (process.env.LYRIA_BASE_URL ?? DEFAULT_BASE_URL).replace(/\/$/, ""); this.model = process.env.LYRIA_MODEL ?? DEFAULT_MODEL; } isConfigured() { return this.apiKey !== null; } async generate(options) { if (!this.apiKey) { throw new MusicError({ code: MUSIC_ERROR_CODES.PROVIDER_NOT_CONFIGURED, message: "Lyria requires one of: GOOGLE_API_KEY, GOOGLE_AI_LYRIA_API_KEY, GOOGLE_AI_API_KEY, or GEMINI_API_KEY", category: ErrorCategory.CONFIGURATION, severity: ErrorSeverity.HIGH, retriable: false, }); } const startTime = Date.now(); const duration = Math.min(options.duration ?? 16, this.maxDurationSeconds); // Lyria API no longer accepts `audioGenerationOptions` — duration is // controlled implicitly by the prompt/model. Only `responseModalities` // is allowed under `generationConfig`. Embed duration in the prompt // so the model still gets the hint. const promptWithDuration = `${this.buildPrompt(options)}. Duration: ${duration} seconds`; const body = { contents: [ { parts: [ { text: promptWithDuration, }, ], }, ], generationConfig: { responseModalities: ["AUDIO"], }, }; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS); let response; try { response = await fetch(`${this.baseUrl}/models/${this.model}:generateContent?key=${this.apiKey}`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(body), signal: controller.signal, }); } catch (err) { if (err instanceof Error && err.name === "AbortError") { throw new MusicError({ code: MUSIC_ERROR_CODES.GENERATION_FAILED, message: `Lyria request timed out after ${REQUEST_TIMEOUT_MS / 1000}s`, category: ErrorCategory.NETWORK, severity: ErrorSeverity.HIGH, retriable: true, originalError: err, }); } throw err; } finally { clearTimeout(timeoutId); } if (!response.ok) { const rawText = await response.text(); const retriable = response.status === 408 || response.status === 429 || response.status >= 500; throw new MusicError({ code: MUSIC_ERROR_CODES.GENERATION_FAILED, message: `Lyria generation failed: ${response.status}${sanitizeForLog(rawText)}`, category: retriable ? ErrorCategory.NETWORK : ErrorCategory.EXECUTION, severity: ErrorSeverity.HIGH, retriable, context: { status: response.status }, }); } const data = (await response.json()); const audioPart = data.candidates?.[0]?.content?.parts?.find((p) => p.inlineData?.mimeType?.startsWith("audio/")); if (!audioPart?.inlineData?.data) { throw new MusicError({ code: MUSIC_ERROR_CODES.GENERATION_FAILED, message: "Lyria response missing audio data", category: ErrorCategory.EXECUTION, severity: ErrorSeverity.HIGH, retriable: false, context: { response: data }, }); } const buffer = Buffer.from(audioPart.inlineData.data, "base64"); const latency = Date.now() - startTime; logger.info(`[LyriaMusic] Generated ${buffer.length} bytes in ${latency}ms — model ${this.model}`); return { buffer, format: "wav", size: buffer.length, duration, provider: "lyria", metadata: { latency, provider: "lyria", model: this.model, sampleRate: 48_000, }, }; } buildPrompt(options) { const parts = [options.prompt]; if (options.genre) { parts.push(`Genre: ${options.genre}`); } if (options.mood) { parts.push(`Mood: ${options.mood}`); } if (options.tempo !== undefined) { parts.push(`${options.tempo} BPM`); } return parts.join(". "); } } //# sourceMappingURL=LyriaMusic.js.map