/** * Prepis hovoru: dva enginy naraz a sloucení jejich vysledku. * * Sluzba: https://services.csbot.cz/apps/audio-transcription * Endpoint: POST /dual-with-merge (multipart/form-data) * * Vzor **volani, ktere vraci tri verze teze veci**. Sluzba prepise nahravku * Deepgramem i Whisperem a treti odpoved slozi z obou. Vraci se vsechny tri, * protoze kdyz jeden engine vypadne, druhy porad neco rekne - a strom se pak * ma podle ceho rozhodnout, misto aby cely krok spadl. */ export const manifest = { id: 'transcription.transcribe', name: 'Přepsat nahrávku', description: 'Přepíše nahrávku Deepgramem i Whisperem a oba přepisy sloučí do jednoho. ' + 'Sloučený výsledek rozlišuje mluvčí a je přesnější než každý engine zvlášť.', inputs: [ { id: 'fileName', label: 'Název nahrávky', type: 'string', required: true, hint: 'Včetně přípony, například hovor.mp3.', }, { id: 'contentBase64', label: 'Nahrávka (Base64)', type: 'string', required: true, multiline: true, hint: 'Obsah zakódovaný v Base64.', }, { id: 'language', label: 'Jazyk nahrávky', type: 'string', required: false, default: 'cs', hint: 'ISO kód jazyka, například cs.', }, { id: 'diarize', label: 'Rozlišovat mluvčí', type: 'boolean', required: false, default: true, hint: 'Zapnuté přidá do přepisu štítky Mluvčí 0, Mluvčí 1.', }, { id: 'combinePrompt', label: 'Instrukce pro sloučení', type: 'string', required: false, multiline: true, hint: 'Bez vyplnění se použije výchozí instrukce služby, která je psaná pro české hovory.', }, { id: 'chatModel', label: 'Model pro sloučení', type: 'string', required: false, hint: 'Bez vyplnění rozhoduje služba. Slučování dělá OpenAI chat model.', }, ], outputs: [ { id: 'text', label: 'Sloučený přepis', type: 'string', required: true }, { id: 'deepgram', label: 'Přepis z Deepgramu', type: 'string', required: false }, { id: 'whisper', label: 'Přepis z Whisperu', type: 'string', required: false }, { id: 'partial', label: 'Část přepisu selhala', type: 'boolean', required: true }, { id: 'problems', label: 'Co selhalo', type: 'string', required: false }, ], // Dva prepisy a sloucení za sebou. Kratsi limit by utnul i kratkou nahravku. timeoutMs: 120000, }; const byExtension = { mp3: 'audio/mpeg', mpga: 'audio/mpeg', mp4: 'audio/mp4', m4a: 'audio/mp4', wav: 'audio/wav', webm: 'audio/webm', ogg: 'audio/ogg', flac: 'audio/flac', }; export async function run(inputs, ctx) { const { pick, text } = ctx.util; const parts = inputs.fileName.split('.'); const extension = parts.length > 1 ? parts[parts.length - 1].toLowerCase() : ''; const { body } = await ctx.http.postForm('/dual-with-merge', { language: inputs.language, diarize: inputs.diarize, combine_prompt: inputs.combinePrompt, chat_model: inputs.chatModel, file: { filename: inputs.fileName, base64: inputs.contentBase64.trim(), contentType: byExtension[extension] ?? 'application/octet-stream', }, }); const merged = text(pick(body, 'merge'), ''); const deepgram = text(pick(body, 'deepgram'), ''); const whisper = text(pick(body, 'gpt'), ''); const errors = pick(body, 'errors'); const problems = Array.isArray(errors) && errors.length > 0 ? errors.join(' | ') : null; if (problems) ctx.log(`Část přepisu selhala: ${problems}`); // Kdyz sloucení selhalo, prazdny vystup by strom poslal dal jako platny text. // Radsi se sahne po tom, co prislo - a nahlas se rekne, ze je to nahradnik. const fallback = deepgram !== '' ? deepgram : whisper; if (merged === '' && fallback === '') { ctx.fail('Služba nevrátila žádný přepis.', problems ?? body); } if (merged === '') ctx.log('Sloučení nevyšlo, vrací se přepis z jednoho enginu.'); return { text: merged !== '' ? merged : fallback, deepgram: deepgram !== '' ? deepgram : null, whisper: whisper !== '' ? whisper : null, partial: problems !== null || merged === '', problems, }; }