{ "kind": "open-flow-workflow", "version": 1, "name": "Generate an OpenAI Speech MP3", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_3d9cf276dffc512a": { "name": "Speech text and voice", "kind": "start", "inputDefs": [ { "jsonSchema": { "type": "string", "minLength": 1, "maxLength": 4096 }, "nullable": false, "handle": "input_text" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "voice" } ] }, "n_ce95d9ae61b23489": { "inputs": { "input_text": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_3d9cf276dffc512a", "output": "input_text" } ] }, "voice": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_3d9cf276dffc512a", "output": "voice" } ] } }, "name": "Validate speech input", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "input_text" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "voice" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "input_text" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "voice" } ], "name": "Validate speech input", "moduleId": "n_4edfa86bac813768", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_d3e766d0f9988ff7": { "inputs": { "model": { "kind": "value", "value": "tts-1" }, "input": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_ce95d9ae61b23489", "output": "input_text" } ] }, "voice": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_ce95d9ae61b23489", "output": "voice" } ] }, "response_format": { "kind": "value", "value": "mp3" }, "stream_format": { "kind": "value", "value": "audio" } }, "name": "Generate MP3 speech", "kind": "task", "task": { "inputs": [ { "description": "The text-to-speech model to use.", "jsonSchema": { "type": "string", "description": "The text-to-speech model to use." }, "nullable": false, "handle": "model" }, { "description": "The text to synthesize into speech.", "jsonSchema": { "type": "string", "description": "The text to synthesize into speech." }, "nullable": false, "handle": "input" }, { "description": "The voice configuration used for synthesis.", "jsonSchema": { "anyOf": [ { "type": "string", "description": "A built-in voice name." }, { "type": "object", "additionalProperties": { "description": "Any JSON value accepted by the upstream API." }, "description": "A structured voice object accepted by the API." } ], "description": "The voice configuration used for synthesis." }, "nullable": false, "handle": "voice" }, { "description": "Optional voice instructions that guide the synthesis style.", "jsonSchema": { "type": "string", "description": "Optional voice instructions that guide the synthesis style." }, "nullable": true, "handle": "instructions", "value": null }, { "description": "The audio format to return.", "jsonSchema": { "type": "string", "enum": [ "mp3", "opus", "aac", "flac", "wav", "pcm" ], "description": "The audio format to return." }, "nullable": true, "handle": "response_format", "value": null }, { "description": "The playback speed multiplier.", "jsonSchema": { "type": "number", "minimum": 0.25, "maximum": 4, "description": "The playback speed multiplier." }, "nullable": true, "handle": "speed", "value": null }, { "description": "The speech response delivery format. This connector only accepts audio or an omitted value.", "jsonSchema": { "type": "string", "enum": [ "audio", "sse" ], "description": "The speech response delivery format. This connector only accepts audio or an omitted value." }, "nullable": true, "handle": "stream_format", "value": null } ], "outputs": [ { "description": "The synthesized audio encoded as base64.", "jsonSchema": { "type": "string", "description": "The synthesized audio encoded as base64." }, "nullable": false, "handle": "content_base64" }, { "description": "The MIME type of the synthesized audio.", "jsonSchema": { "type": "string", "description": "The MIME type of the synthesized audio." }, "nullable": false, "handle": "content_type" } ], "name": "Generate MP3 speech", "executor": { "kind": "connector", "action": "openai.create_speech" } } }, "n_9725d352e6e05b5a": { "inputs": { "content_base64": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d3e766d0f9988ff7", "output": "content_base64" } ] }, "content_type": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d3e766d0f9988ff7", "output": "content_type" } ] } }, "name": "Save audio artifact", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string", "description": "The synthesized audio encoded as base64." }, "nullable": false, "handle": "content_base64" }, { "jsonSchema": { "type": "string", "description": "The MIME type of the synthesized audio." }, "nullable": false, "handle": "content_type" } ], "outputs": [ { "jsonSchema": { "type": "object", "properties": { "kind": { "const": "artifact" }, "id": { "type": "string" }, "name": { "type": "string" }, "mediaType": { "type": "string" }, "size": { "type": "integer", "minimum": 1 }, "digest": { "type": "string" } }, "required": [ "kind", "id", "name", "size", "digest" ] }, "nullable": false, "handle": "audio" } ], "name": "Save audio artifact", "moduleId": "n_78e5b70d5e5d0ecd", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_4e489267785bfa9f": { "name": "Download speech audio", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "object", "properties": { "kind": { "const": "artifact" }, "id": { "type": "string" }, "name": { "type": "string" }, "mediaType": { "type": "string" }, "size": { "type": "integer", "minimum": 1 }, "digest": { "type": "string" } }, "required": [ "kind", "id", "name", "size", "digest" ] }, "nullable": false, "handle": "audio" } ], "outputMappings": { "audio": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_9725d352e6e05b5a", "output": "audio" } ] } } } }, "edges": [ { "source": "n_3d9cf276dffc512a", "target": "n_ce95d9ae61b23489" }, { "source": "n_ce95d9ae61b23489", "target": "n_d3e766d0f9988ff7" }, { "source": "n_d3e766d0f9988ff7", "target": "n_9725d352e6e05b5a" }, { "source": "n_9725d352e6e05b5a", "target": "n_4e489267785bfa9f" } ] } }, "modules": { "n_4edfa86bac813768": { "name": "Validate speech input", "imports": [], "source": "export default function prepare({input_text,voice}){if(typeof input_text!=='string'||!input_text.trim()||Array.from(input_text).length>4096)throw Error('Supply text with 1–4096 characters');const selected=voice||'alloy';if(!['alloy','ash','coral','echo','fable','onyx','nova','sage','shimmer'].includes(selected))throw Error('Unsupported voice');return{input_text,voice:selected}}\n" }, "n_78e5b70d5e5d0ecd": { "name": "Save audio artifact", "imports": [], "source": "import { Buffer } from 'node:buffer';\n\nexport default async function saveSpeech({ content_base64, content_type }, context) {\n if (typeof content_base64 !== 'string' || !content_base64 || content_base64.length % 4 !== 0 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(content_base64)) throw new Error('The speech response does not contain valid base64 audio.');\n const mediaType = typeof content_type === 'string' ? content_type.split(';')[0].trim().toLowerCase() : '';\n if (!['audio/mpeg', 'audio/mp3', 'application/octet-stream'].includes(mediaType)) throw new Error('The speech response is not MP3 audio.');\n const bytes = new Uint8Array(Buffer.from(content_base64, 'base64'));\n if (!bytes.length || Buffer.from(bytes).toString('base64') !== content_base64) throw new Error('The speech response is empty or has invalid base64 padding.');\n return { audio: await context.artifact.put(bytes, { name: 'speech.mp3', mediaType: 'audio/mpeg' }) };\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }