[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6G9H717VXRWRXRYWHB8MVHA":3,"skill-same:01M6G9H717VXRWRXRYWHB8MVHA":94},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":28,"publishedAt":49,"linkOnly":50,"markdownContent":51,"markdownBody":52,"frontMatter":53,"refName":54,"commitSha":55,"skillDirectory":56,"forksCount":13,"repositoryHtmlUrl":57,"repositoryHomepageUrl":6,"ownerHtmlUrl":58,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":59,"install":60,"resources":73},"01M6G9H717VXRWRXRYWHB8MVHA","podcast-generation",null,"Generate AI-powered podcast-style audio narratives using Azure OpenAI's GPT Realtime Mini model via WebSocket. Use when building text-to-speech features, audio narrative generation, podcast creation from content, or integrating with Azure OpenAI Realtime API for real audio output. Covers full-stack implementation from React frontend to Python FastAPI backend with WebSocket streaming.","ricardoaevt\u002Fnexus-roms","ricardoaevt","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F164531880?v=4","01M6DGFGSV6CF9AKPHABBREC79","01M6DGFGYNNPJVDFQA05TDPSBN",0,"AGPL-3.0","https:\u002F\u002Fgithub.com\u002Fricardoaevt\u002Fnexus-roms\u002Fblob\u002Fmain\u002F.agent\u002Fskills\u002Fmicrosoft_skills\u002F.github\u002Fskills\u002Fpodcast-generation\u002FSKILL.md",[17,22,25],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",{"categoryNo":23,"categoryName":24,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":26,"categoryName":27,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000016","图像与多媒体",[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"podcast generation","generation","podcast","text","openai realtime","narrative","python fastapi","creation","full","fastapi","model","frontend","stack","Audio","api","streaming","openai","generate","azure openai","react","2026-11-10 10:12:30",false,"---\nname: podcast-generation\ndescription: Generate AI-powered podcast-style audio narratives using Azure OpenAI's GPT Realtime Mini model via WebSocket. Use when building text-to-speech features, audio narrative generation, podcast creation from content, or integrating with Azure OpenAI Realtime API for real audio output. Covers full-stack implementation from React frontend to Python FastAPI backend with WebSocket streaming.\n---\n\n# Podcast Generation with GPT Realtime Mini\n\nGenerate real audio narratives from text content using Azure OpenAI's Realtime API.\n\n## Quick Start\n\n1. Configure environment variables for Realtime API\n2. Connect via WebSocket to Azure OpenAI Realtime endpoint\n3. Send text prompt, collect PCM audio chunks + transcript\n4. Convert PCM to WAV format\n5. Return base64-encoded audio to frontend for playback\n\n## Environment Configuration\n\n```env\nAZURE_OPENAI_AUDIO_API_KEY=your_realtime_api_key\nAZURE_OPENAI_AUDIO_ENDPOINT=https:\u002F\u002Fyour-resource.cognitiveservices.azure.com\nAZURE_OPENAI_AUDIO_DEPLOYMENT=gpt-realtime-mini\n```\n\n**Note**: Endpoint should NOT include `\u002Fopenai\u002Fv1\u002F` - just the base URL.\n\n## Core Workflow\n\n### Backend Audio Generation\n\n```python\nfrom openai import AsyncOpenAI\nimport base64\n\n# Convert HTTPS endpoint to WebSocket URL\nws_url = endpoint.replace(\"https:\u002F\u002F\", \"wss:\u002F\u002F\") + \"\u002Fopenai\u002Fv1\"\n\nclient = AsyncOpenAI(\n    websocket_base_url=ws_url,\n    api_key=api_key\n)\n\naudio_chunks = []\ntranscript_parts = []\n\nasync with client.realtime.connect(model=\"gpt-realtime-mini\") as conn:\n    # Configure for audio-only output\n    await conn.session.update(session={\n        \"output_modalities\": [\"audio\"],\n        \"instructions\": \"You are a narrator. Speak naturally.\"\n    })\n    \n    # Send text to narrate\n    await conn.conversation.item.create(item={\n        \"type\": \"message\",\n        \"role\": \"user\",\n        \"content\": [{\"type\": \"input_text\", \"text\": prompt}]\n    })\n    \n    await conn.response.create()\n    \n    # Collect streaming events\n    async for event in conn:\n        if event.type == \"response.output_audio.delta\":\n            audio_chunks.append(base64.b64decode(event.delta))\n        elif event.type == \"response.output_audio_transcript.delta\":\n            transcript_parts.append(event.delta)\n        elif event.type == \"response.done\":\n            break\n\n# Convert PCM to WAV (see scripts\u002Fpcm_to_wav.py)\npcm_audio = b''.join(audio_chunks)\nwav_audio = pcm_to_wav(pcm_audio, sample_rate=24000)\n```\n\n### Frontend Audio Playback\n\n```javascript\n\u002F\u002F Convert base64 WAV to playable blob\nconst base64ToBlob = (base64, mimeType) => {\n  const bytes = atob(base64);\n  const arr = new Uint8Array(bytes.length);\n  for (let i = 0; i \u003C bytes.length; i++) arr[i] = bytes.charCodeAt(i);\n  return new Blob([arr], { type: mimeType });\n};\n\nconst audioBlob = base64ToBlob(response.audio_data, 'audio\u002Fwav');\nconst audioUrl = URL.createObjectURL(audioBlob);\nnew Audio(audioUrl).play();\n```\n\n## Voice Options\n\n| Voice | Character |\n|-------|-----------|\n| alloy | Neutral |\n| echo | Warm |\n| fable | Expressive |\n| onyx | Deep |\n| nova | Friendly |\n| shimmer | Clear |\n\n## Realtime API Events\n\n- `response.output_audio.delta` - Base64 audio chunk\n- `response.output_audio_transcript.delta` - Transcript text\n- `response.done` - Generation complete\n- `error` - Handle with `event.error.message`\n\n## Audio Format\n\n- **Input**: Text prompt\n- **Output**: PCM audio (24kHz, 16-bit, mono)\n- **Storage**: Base64-encoded WAV\n\n## References\n\n- **Full architecture**: See [references\u002Farchitecture.md](references\u002Farchitecture.md) for complete stack design\n- **Code examples**: See [references\u002Fcode-examples.md](references\u002Fcode-examples.md) for production patterns\n- **PCM conversion**: Use [scripts\u002Fpcm_to_wav.py](scripts\u002Fpcm_to_wav.py) for audio format conversion\n","# Podcast Generation with GPT Realtime Mini\n\nGenerate real audio narratives from text content using Azure OpenAI's Realtime API.\n\n## Quick Start\n\n1. Configure environment variables for Realtime API\n2. Connect via WebSocket to Azure OpenAI Realtime endpoint\n3. Send text prompt, collect PCM audio chunks + transcript\n4. Convert PCM to WAV format\n5. Return base64-encoded audio to frontend for playback\n\n## Environment Configuration\n\n```env\nAZURE_OPENAI_AUDIO_API_KEY=your_realtime_api_key\nAZURE_OPENAI_AUDIO_ENDPOINT=https:\u002F\u002Fyour-resource.cognitiveservices.azure.com\nAZURE_OPENAI_AUDIO_DEPLOYMENT=gpt-realtime-mini\n```\n\n**Note**: Endpoint should NOT include `\u002Fopenai\u002Fv1\u002F` - just the base URL.\n\n## Core Workflow\n\n### Backend Audio Generation\n\n```python\nfrom openai import AsyncOpenAI\nimport base64\n\n# Convert HTTPS endpoint to WebSocket URL\nws_url = endpoint.replace(\"https:\u002F\u002F\", \"wss:\u002F\u002F\") + \"\u002Fopenai\u002Fv1\"\n\nclient = AsyncOpenAI(\n    websocket_base_url=ws_url,\n    api_key=api_key\n)\n\naudio_chunks = []\ntranscript_parts = []\n\nasync with client.realtime.connect(model=\"gpt-realtime-mini\") as conn:\n    # Configure for audio-only output\n    await conn.session.update(session={\n        \"output_modalities\": [\"audio\"],\n        \"instructions\": \"You are a narrator. Speak naturally.\"\n    })\n    \n    # Send text to narrate\n    await conn.conversation.item.create(item={\n        \"type\": \"message\",\n        \"role\": \"user\",\n        \"content\": [{\"type\": \"input_text\", \"text\": prompt}]\n    })\n    \n    await conn.response.create()\n    \n    # Collect streaming events\n    async for event in conn:\n        if event.type == \"response.output_audio.delta\":\n            audio_chunks.append(base64.b64decode(event.delta))\n        elif event.type == \"response.output_audio_transcript.delta\":\n            transcript_parts.append(event.delta)\n        elif event.type == \"response.done\":\n            break\n\n# Convert PCM to WAV (see scripts\u002Fpcm_to_wav.py)\npcm_audio = b''.join(audio_chunks)\nwav_audio = pcm_to_wav(pcm_audio, sample_rate=24000)\n```\n\n### Frontend Audio Playback\n\n```javascript\n\u002F\u002F Convert base64 WAV to playable blob\nconst base64ToBlob = (base64, mimeType) => {\n  const bytes = atob(base64);\n  const arr = new Uint8Array(bytes.length);\n  for (let i = 0; i \u003C bytes.length; i++) arr[i] = bytes.charCodeAt(i);\n  return new Blob([arr], { type: mimeType });\n};\n\nconst audioBlob = base64ToBlob(response.audio_data, 'audio\u002Fwav');\nconst audioUrl = URL.createObjectURL(audioBlob);\nnew Audio(audioUrl).play();\n```\n\n## Voice Options\n\n| Voice | Character |\n|-------|-----------|\n| alloy | Neutral |\n| echo | Warm |\n| fable | Expressive |\n| onyx | Deep |\n| nova | Friendly |\n| shimmer | Clear |\n\n## Realtime API Events\n\n- `response.output_audio.delta` - Base64 audio chunk\n- `response.output_audio_transcript.delta` - Transcript text\n- `response.done` - Generation complete\n- `error` - Handle with `event.error.message`\n\n## Audio Format\n\n- **Input**: Text prompt\n- **Output**: PCM audio (24kHz, 16-bit, mono)\n- **Storage**: Base64-encoded WAV\n\n## References\n\n- **Full architecture**: See [references\u002Farchitecture.md](references\u002Farchitecture.md) for complete stack design\n- **Code examples**: See [references\u002Fcode-examples.md](references\u002Fcode-examples.md) for production patterns\n- **PCM conversion**: Use [scripts\u002Fpcm_to_wav.py](scripts\u002Fpcm_to_wav.py) for audio format conversion\n",{"name":5,"description":7},"main","d41309588593cee5c2fb44062474efc3a99fbcb6",".agent\u002Fskills\u002Fmicrosoft_skills\u002F.github\u002Fskills\u002Fpodcast-generation","https:\u002F\u002Fgithub.com\u002Fricardoaevt\u002Fnexus-roms","https:\u002F\u002Fgithub.com\u002Fricardoaevt","2026-03-23 00:05:03",{"source":8,"skillName":5,"commands":61,"prompt":71,"zipAvailable":72},[62,65,68],{"tool":63,"command":64},"npx","npx skills add ricardoaevt\u002Fnexus-roms --skill podcast-generation",{"tool":66,"command":67},"bunx","bunx skills add ricardoaevt\u002Fnexus-roms --skill podcast-generation",{"tool":69,"command":70},"pnpm","pnpm dlx skills add ricardoaevt\u002Fnexus-roms --skill podcast-generation","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fricardoaevt\u002Fnexus-roms\u002Fblob\u002Fmain\u002F.agent\u002Fskills\u002Fmicrosoft_skills\u002F.github\u002Fskills\u002Fpodcast-generation\u002FSKILL.md",true,[74,80,84,88],{"path":75,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":78,"sourceHtmlUrl":79},"references\u002Facceptance-criteria.md","REFERENCE","参考文档",12277,"https:\u002F\u002Fgithub.com\u002Fricardoaevt\u002Fnexus-roms\u002Fblob\u002Fmain\u002F.agent\u002Fskills\u002Fmicrosoft_skills\u002F.github\u002Fskills\u002Fpodcast-generation\u002Freferences\u002Facceptance-criteria.md",{"path":81,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":82,"sourceHtmlUrl":83},"references\u002Farchitecture.md",4736,"https:\u002F\u002Fgithub.com\u002Fricardoaevt\u002Fnexus-roms\u002Fblob\u002Fmain\u002F.agent\u002Fskills\u002Fmicrosoft_skills\u002F.github\u002Fskills\u002Fpodcast-generation\u002Freferences\u002Farchitecture.md",{"path":85,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":86,"sourceHtmlUrl":87},"references\u002Fcode-examples.md",6116,"https:\u002F\u002Fgithub.com\u002Fricardoaevt\u002Fnexus-roms\u002Fblob\u002Fmain\u002F.agent\u002Fskills\u002Fmicrosoft_skills\u002F.github\u002Fskills\u002Fpodcast-generation\u002Freferences\u002Fcode-examples.md",{"path":89,"resourceType":90,"resourceTypeDesc":91,"sizeBytes":92,"sourceHtmlUrl":93},"scripts\u002Fpcm_to_wav.py","SCRIPT","脚本",2475,"https:\u002F\u002Fgithub.com\u002Fricardoaevt\u002Fnexus-roms\u002Fblob\u002Fmain\u002F.agent\u002Fskills\u002Fmicrosoft_skills\u002F.github\u002Fskills\u002Fpodcast-generation\u002Fscripts\u002Fpcm_to_wav.py",[95,120,136,151,162,171],{"skillNo":96,"name":97,"nameZh":6,"description":98,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":99,"licenseFileUrl":6,"sourceHtmlUrl":100,"categories":101,"keywords":102,"publishedAt":49},"01M6G9HYKQRK6HBKVTZM3258M3","openspec-propose","Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.","MIT","https:\u002F\u002Fgithub.com\u002Fricardoaevt\u002Fnexus-roms\u002Fblob\u002Fmain\u002F.agent\u002Fskills\u002Fopenspec-propose\u002FSKILL.md",[],[103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119],"openspec propose","openspec","cli","generated","with","proposal","complete","get","build","implementation","ready","use","user","artifacts","tasks","change","design",{"skillNo":121,"name":122,"nameZh":6,"description":123,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":99,"licenseFileUrl":6,"sourceHtmlUrl":124,"categories":125,"keywords":126,"publishedAt":49},"01M6G9HV9V20WFCHYVSDNQMJVK","openspec-new-change","Start a new OpenSpec change using the experimental artifact workflow. Use when the user wants to create a new feature, fix, or modification with a structured step-by-step approach.","https:\u002F\u002Fgithub.com\u002Fricardoaevt\u002Fnexus-roms\u002Fblob\u002Fmain\u002F.agent\u002Fskills\u002Fopenspec-new-change\u002FSKILL.md",[],[104,127,118,128,105,129,130,131,114,115,132,133,134,135,107],"openspec new change","new feature","structured","artifact","workflow","create","start","fix","feature",{"skillNo":137,"name":138,"nameZh":6,"description":139,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":99,"licenseFileUrl":6,"sourceHtmlUrl":140,"categories":141,"keywords":142,"publishedAt":49},"01M6G9HRF6R89QZXGSFC9DRQW3","openspec-explore","Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.","https:\u002F\u002Fgithub.com\u002Fricardoaevt\u002Fnexus-roms\u002Fblob\u002Fmain\u002F.agent\u002Fskills\u002Fopenspec-explore\u002FSKILL.md",[],[143,144,104,114,145,105,118,115,146,147,148,149,150],"openspec explore","explore","partner","think","thinking partner","thinking","ideas","requirements",{"skillNo":152,"name":153,"nameZh":6,"description":154,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":99,"licenseFileUrl":6,"sourceHtmlUrl":155,"categories":156,"keywords":157,"publishedAt":49},"01M6G9HP3APFCXZG7XRWBMM524","openspec-continue-change","Continue working on an OpenSpec change by creating the next artifact. Use when the user wants to progress their change, create the next artifact, or continue their workflow.","https:\u002F\u002Fgithub.com\u002Fricardoaevt\u002Fnexus-roms\u002Fblob\u002Fmain\u002F.agent\u002Fskills\u002Fopenspec-continue-change\u002FSKILL.md",[],[104,158,159,118,131,105,132,115,114,160,130,161],"continue","openspec continue change","next","progress",{"skillNo":163,"name":164,"nameZh":6,"description":165,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":99,"licenseFileUrl":6,"sourceHtmlUrl":166,"categories":167,"keywords":168,"publishedAt":49},"01M6G9HKT3WMSDBSKA7YM4MSSQ","openspec-archive-change","Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.","https:\u002F\u002Fgithub.com\u002Fricardoaevt\u002Fnexus-roms\u002Fblob\u002Fmain\u002F.agent\u002Fskills\u002Fopenspec-archive-change\u002FSKILL.md",[],[169,104,170,118,105,131,115,114,112,109],"openspec archive change","archive",{"skillNo":172,"name":173,"nameZh":6,"description":174,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":99,"licenseFileUrl":6,"sourceHtmlUrl":175,"categories":176,"keywords":177,"publishedAt":49},"01M6G9HHEG0RJK4E0YTVZZ6A8T","openspec-apply-change","Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.","https:\u002F\u002Fgithub.com\u002Fricardoaevt\u002Fnexus-roms\u002Fblob\u002Fmain\u002F.agent\u002Fskills\u002Fopenspec-apply-change\u002FSKILL.md",[],[104,118,178,179,117,133,158,180,115,114,112,105],"openspec apply change","implement","work"]