(
params: GenerateAudioArgs,
context?: ServerToolContext
)
| 58 | name: GenerateAudio.id, |
| 59 | |
| 60 | async execute( |
| 61 | params: GenerateAudioArgs, |
| 62 | context?: ServerToolContext |
| 63 | ): Promise<GenerateAudioResult> { |
| 64 | if (!context?.userId) { |
| 65 | throw new Error('Authentication required') |
| 66 | } |
| 67 | const workspaceId = context.workspaceId |
| 68 | if (!workspaceId) { |
| 69 | return { success: false, message: 'Workspace ID is required' } |
| 70 | } |
| 71 | if (!params.prompt) { |
| 72 | return { success: false, message: 'prompt is required' } |
| 73 | } |
| 74 | |
| 75 | const type = (params.type || 'speech') as AudioType |
| 76 | if (!VALID_TYPES.includes(type)) { |
| 77 | return { |
| 78 | success: false, |
| 79 | message: `Invalid type "${params.type}". Must be one of: ${VALID_TYPES.join(', ')}`, |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | // Voice cloning: a reference sample clones that voice into the generated speech. |
| 84 | let voiceSampleDataUri: string | undefined |
| 85 | const samplePath = params.inputs?.files?.[0]?.path |
| 86 | if (samplePath) { |
| 87 | const sample = await resolveWorkspaceFileReference(workspaceId, samplePath) |
| 88 | if (!sample) { |
| 89 | return { success: false, message: `Voice sample not found: ${samplePath}` } |
| 90 | } |
| 91 | const sampleBuffer = await fetchWorkspaceFileBuffer(sample) |
| 92 | const sampleMime = sample.type || 'audio/mpeg' |
| 93 | voiceSampleDataUri = `data:${sampleMime};base64,${sampleBuffer.toString('base64')}` |
| 94 | } |
| 95 | |
| 96 | try { |
| 97 | logger.info('Generating audio', { |
| 98 | type, |
| 99 | model: params.model, |
| 100 | promptLength: params.prompt.length, |
| 101 | voiceClone: Boolean(voiceSampleDataUri), |
| 102 | }) |
| 103 | |
| 104 | const result = await generateFalAudio({ |
| 105 | prompt: params.prompt, |
| 106 | type, |
| 107 | model: params.model, |
| 108 | voice: params.voice, |
| 109 | duration: params.duration, |
| 110 | lyrics: params.lyrics, |
| 111 | instrumental: params.instrumental, |
| 112 | voiceSampleDataUri, |
| 113 | }) |
| 114 | |
| 115 | const outputFile = params.outputs?.files?.[0] |
| 116 | const ext = audioExtFromContentType(result.contentType) |
| 117 | const outputPath = outputFile?.path || `files/generated-audio.${ext}` |
nothing calls this directly
no test coverage detected