#!/usr/bin/env node const baseURL = (process.env.GATEWAY_BASE_URL || 'http://localhost:8080').replace(/\/+$/, ''); const apiKey = process.env.GATEWAY_API_KEY || process.env.EASYAI_GATEWAY_API_KEY; const cloneModel = process.env.GATEWAY_VOICE_CLONE_MODEL || 'MiniMax-Voice-Clone'; const speechModel = process.env.GATEWAY_TTS_MODEL || 'speech-2.6-turbo'; const voiceId = process.env.VOICE_CLONE_ID || `voice_clone_${Date.now().toString(36)}`; const audioURL = process.env.VOICE_CLONE_AUDIO_URL || `${baseURL}/static/simulation/audio.wav`; const marker = `voice-clone-e2e-${Date.now().toString(36)}`; if (!apiKey) { throw new Error('Set GATEWAY_API_KEY or EASYAI_GATEWAY_API_KEY'); } function assert(condition, message) { if (!condition) throw new Error(message); } async function request(path, init = {}) { const res = await fetch(`${baseURL}${path}`, { ...init, headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', ...(init.headers || {}), }, }); const text = await res.text(); const body = text ? JSON.parse(text) : {}; if (!res.ok) { throw new Error(`${init.method || 'GET'} ${path} failed ${res.status}: ${text}`); } return body; } async function postAsyncTask(path, body) { const accepted = await request(path, { method: 'POST', headers: { 'X-Async': 'true' }, body: JSON.stringify(body), }); const taskId = accepted.taskId || accepted.task?.id; assert(taskId, `Expected async task id from ${path}`); return pollTask(taskId); } async function pollTask(taskId, timeoutMs = 120000) { const started = Date.now(); while (Date.now() - started < timeoutMs) { const task = await request(`/api/v1/tasks/${taskId}`); if (task.status === 'succeeded') return task; if (task.status === 'failed') { throw new Error(`Task ${taskId} failed: ${task.errorMessage || task.error || JSON.stringify(task)}`); } await new Promise((resolve) => setTimeout(resolve, 1000)); } throw new Error(`Timed out waiting for task ${taskId}`); } const cloneTask = await postAsyncTask('/v1/voice_clone', { model: cloneModel, voice_id: voiceId, audio_url: audioURL, text: 'hello voice clone preview', preview_model: process.env.VOICE_CLONE_PREVIEW_MODEL || 'speech-2.8-hd', runMode: 'simulation', simulation: true, integrationTestMarker: `${marker}-clone`, }); const cloneResult = cloneTask.result || {}; const clonedVoice = cloneResult.cloned_voice || cloneResult.clonedVoice; assert(cloneResult.status === 'success', `Unexpected clone result: ${JSON.stringify(cloneResult)}`); assert((cloneResult.voice_id || clonedVoice?.voiceId || clonedVoice?.voice_id) === voiceId, 'Clone voice_id mismatch'); assert(clonedVoice?.platformId || clonedVoice?.platform_id, 'Clone result missing platform binding'); const listResult = await request('/v1/voice_clone/voices'); const voices = listResult.items || listResult.data || []; const listedVoice = voices.find((item) => item.voiceId === voiceId || item.voice_id === voiceId); assert(listedVoice, 'Cloned voice is missing from voice list'); assert( (listedVoice.platformId || listedVoice.platform_id) === (clonedVoice.platformId || clonedVoice.platform_id), 'Listed voice platform binding mismatch', ); const speechTask = await postAsyncTask('/v1/speech/generations', { model: speechModel, text: 'hello from cloned voice', cloned_voice_id: clonedVoice.id, runMode: 'simulation', simulation: true, integrationTestMarker: `${marker}-speech`, }); const speechResult = speechTask.result || {}; assert(speechResult.status === 'success', `Unexpected speech result: ${JSON.stringify(speechResult)}`); const speechAttemptPlatformId = speechTask.attempts?.[0]?.platformId; assert(speechAttemptPlatformId, 'Speech task is missing attempt platformId'); assert( speechAttemptPlatformId === (clonedVoice.platformId || clonedVoice.platform_id), `Speech used ${speechAttemptPlatformId}, expected cloned voice platform ${clonedVoice.platformId || clonedVoice.platform_id}`, ); if (process.env.GATEWAY_CROSS_PLATFORM_TTS_MODEL) { try { await postAsyncTask('/v1/speech/generations', { model: process.env.GATEWAY_CROSS_PLATFORM_TTS_MODEL, text: 'this should not cross platform', cloned_voice_id: clonedVoice.id, runMode: 'simulation', simulation: true, }); throw new Error('Cross-platform TTS request unexpectedly succeeded'); } catch (error) { if (String(error?.message || '').includes('unexpectedly succeeded')) throw error; } } console.log( JSON.stringify( { ok: true, voiceId, clonedVoiceId: clonedVoice.id, platformId: clonedVoice.platformId || clonedVoice.platform_id, cloneTaskId: cloneTask.id, speechTaskId: speechTask.id, }, null, 2, ), );