{"id":53614,"date":"2026-10-12T14:29:04","date_gmt":"2026-10-12T04:29:04","guid":{"rendered":"https:\/\/www.cloudproinc.com.au\/?p=53614"},"modified":"2026-10-12T14:29:04","modified_gmt":"2026-10-12T04:29:04","slug":"turn-wordpress-posts-into-voice-blogs-with-python-openai-tts","status":"publish","type":"post","link":"https:\/\/www.cloudproinc.com.au\/index.php\/2026\/10\/12\/turn-wordpress-posts-into-voice-blogs-with-python-openai-tts\/","title":{"rendered":"Turn WordPress Posts into \u201cVoice Blogs\u201d with Python + OpenAI TTS"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">This blog post, &#8220;Turn WordPress Posts into \u201cVoice Blogs\u201d with Python + OpenAI TTS&#8221; will show you how to pull posts from a WordPress site via the REST API, converts the article content to speech using OpenAI\u2019s Text-to-Speech (TTS), saves an MP3, and (optionally) uploads the file back to your WordPress Media Library.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p class=\"wp-block-paragraph\">Below is a guided tour of how it works, with key snippets and tips to adapt it for your site.<\/p>\n\n\n\n<figure class=\"wp-block-audio\"><audio controls src=\"https:\/\/cloudproin-e5ddd09d0f1b51fcfd2f-endpoint.azureedge.net\/blobcloudproinf8788b00c9\/wp-content\/uploads\/2025\/08\/turn-wordpress-posts-into-voice-blogs-with-python-openai-tts.mp3\"><\/audio><\/figure>\n\n\n\n<div class=\"wp-block-yoast-seo-table-of-contents yoast-table-of-contents\"><h2>Table of contents<\/h2><ul><li><a href=\"#h-what-the-script-does-end-to-end\" data-level=\"2\">What the script does (end-to-end)<\/a><\/li><li><a href=\"#h-prerequisites\" data-level=\"2\">Prerequisites<\/a><\/li><li><a href=\"#h-fetch-posts-via-the-wordpress-rest-api\" data-level=\"2\">Fetch posts via the WordPress REST API<\/a><\/li><li><a href=\"#h-strip-html-into-clean-narration-text\" data-level=\"2\">Strip HTML into clean narration text<\/a><\/li><li><a href=\"#h-stream-speech-to-mp3-with-openai\" data-level=\"2\">Stream speech to MP3 with OpenAI<\/a><\/li><li><a href=\"#h-optional-upload-mp3-back-to-wordpress\" data-level=\"2\">Optional: Upload MP3 back to WordPress<\/a><\/li><li><a href=\"#h-command-line-usage\" data-level=\"2\">Command-line usage<\/a><\/li><li><a href=\"#h-security-amp-reliability-notes\" data-level=\"2\">Security &amp; reliability notes<\/a><\/li><\/ul><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-what-the-script-does-end-to-end\">What the script does (end-to-end)<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Loads config from environment (<code>.env<\/code>) and command-line flags.<\/li>\n\n\n\n<li>Lists recent WordPress posts (title, date, link).<\/li>\n\n\n\n<li>Lets you choose a post interactively.<\/li>\n\n\n\n<li>Fetches and sanitizes the post\u2019s HTML into readable text.<\/li>\n\n\n\n<li>Streams TTS audio with OpenAI and writes an MP3.<\/li>\n\n\n\n<li>Optionally uploads the MP3 to WordPress using Application Passwords.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-prerequisites\">Prerequisites<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Python 3.10+<\/li>\n\n\n\n<li>Packages: <code>requests<\/code>, <code>python-dotenv<\/code>, <code>openai<\/code><\/li>\n\n\n\n<li>OpenAI API key in your environment (<code>OPENAI_API_KEY<\/code>)<\/li>\n\n\n\n<li>WordPress site with REST API enabled (default for modern WP)<\/li>\n\n\n\n<li>(Optional) WordPress Application Password for media upload<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><code>.env<\/code> example:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-1\"><code>OPENAI_API_KEY=sk-...\nWP_URL=https:\/\/your-site.com\nWP_USER=your-wp-username\nWP_APP_PASSWORD=abcd efgh ijkl mnop\nTTS_VOICE=alloy\nTTS_MODEL=gpt-4o-mini-tts\nTTS_MAX_CHARS=16000\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-fetch-posts-via-the-wordpress-rest-api\">Fetch posts via the WordPress REST API<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The script relies on WordPress\u2019s built-in REST endpoints:<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-2\"><code>def fetch_posts(base_url: str, per_page: int = 20) -&gt; List&#91;Dict]:\n    url = base_url.rstrip(\"\/\") + \"\/wp-json\/wp\/v2\/posts\"\n    params = {\"per_page\": per_page, \"_fields\": \"id,slug,title,link,date\", \"orderby\": \"date\", \"order\": \"desc\"}\n    resp = requests.get(url, params=params, timeout=20)\n    resp.raise_for_status()\n    return resp.json()\n<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"545\" height=\"369\" data-src=\"\/wp-content\/uploads\/2025\/08\/image-7.png\" alt=\"List blog posts\" class=\"wp-image-53615 lazyload\" data-srcset=\"\/wp-content\/uploads\/2025\/08\/image-7.png 545w, \/wp-content\/uploads\/2025\/08\/image-7-300x203.png 300w, \/wp-content\/uploads\/2025\/08\/image-7-480x325.png 480w\" data-sizes=\"(max-width: 545px) 100vw, 545px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 545px; --smush-placeholder-aspect-ratio: 545\/369;\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">It then prints a numbered list so you can pick the post:<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-3\"><code>def choose_post(posts: List&#91;Dict]) -&gt; Optional&#91;Dict]:\n    for idx, p in enumerate(posts, 1):\n        title = strip_html(str(p.get(\"title\", {}).get(\"rendered\") or \"(untitled)\"))\n        print(f\"{idx}. {title}\")\n    # prompt for a number...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Once selected, it fetches the full content:<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-4\"><code>def fetch_post_content(base_url: str, post_id: int) -&gt; Dict:\n    url = base_url.rstrip(\"\/\") + f\"\/wp-json\/wp\/v2\/posts\/{post_id}\"\n    params = {\"_fields\": \"id,slug,title,content\"}\n    resp = requests.get(url, params=params, timeout=20)\n    resp.raise_for_status()\n    return resp.json()\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-strip-html-into-clean-narration-text\">Strip HTML into clean narration text<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Blog HTML can be messy for TTS. The <code>strip_html<\/code> helper removes scripts\/styles and, by default, <strong>removes code blocks<\/strong> (they\u2019re often awkward to read aloud). You can include code with <code>--include-code<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-5\"><code>def strip_html(html: str, include_code: bool = False) -&gt; str:\n    html = re.sub(r\"&lt;(script|style)&#91;^&gt;]*&gt;.*?&lt;\/\\1&gt;\", \" \", html, flags=re.DOTALL|re.IGNORECASE)\n    if not include_code:\n        html = re.sub(r\"&lt;(pre|code)&#91;^&gt;]*&gt;.*?&lt;\/\\1&gt;\", \" \", html, flags=re.DOTALL|re.IGNORECASE)\n        html = re.sub(r\"```.*?```\", \" \", html, flags=re.DOTALL)\n    html = re.sub(r\"&lt;(br|\/p|\/div|\/li|\/h&#91;1-6])&#91;^&gt;]*&gt;\", \"\\n\", html, flags=re.IGNORECASE)\n    text = re.sub(r\"&lt;&#91;^&gt;]+&gt;\", \" \", html)\n    # unescape &amp; normalize\n    return re.sub(r\"\\s+\", \" \", text.replace(\"&amp;nbsp;\",\" \").replace(\"&amp;amp;\",\"&amp;\")).strip()\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">There\u2019s also a <strong>length guard<\/strong> (<code>TTS_MAX_CHARS<\/code>) to avoid sending overly long inputs to TTS.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-stream-speech-to-mp3-with-openai\">Stream speech to MP3 with OpenAI<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The TTS step uses the OpenAI Python SDK\u2019s streaming interface, which writes audio directly to disk (efficient and robust):<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-6\"><code>def synthesize_to_mp3(text: str, out_path: Path, voice: str = \"alloy\", model: str = \"gpt-4o-mini-tts\") -&gt; Path:\n    client = OpenAI(api_key=os.getenv(\"OPENAI_API_KEY\"))\n    out_path.parent.mkdir(parents=True, exist_ok=True)\n    with client.audio.speech.with_streaming_response.create(\n        model=model,\n        voice=voice,\n        input=text,\n        response_format=\"mp3\",\n    ) as response:\n        response.stream_to_file(out_path)\n    return out_path\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Why streaming?<\/strong> It avoids buffering the entire audio in memory and handles longer posts gracefully.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"545\" height=\"105\" data-src=\"\/wp-content\/uploads\/2025\/08\/image-8.png\" alt=\"\" class=\"wp-image-53616 lazyload\" data-srcset=\"\/wp-content\/uploads\/2025\/08\/image-8.png 545w, \/wp-content\/uploads\/2025\/08\/image-8-300x58.png 300w, \/wp-content\/uploads\/2025\/08\/image-8-480x92.png 480w\" data-sizes=\"(max-width: 545px) 100vw, 545px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 545px; --smush-placeholder-aspect-ratio: 545\/105;\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-optional-upload-mp3-back-to-wordpress\">Optional: Upload MP3 back to WordPress<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you pass <code>--upload<\/code> (or answer \u201cy\u201d at the prompt), the script posts the MP3 to <code>\/wp-json\/wp\/v2\/media<\/code> using Basic Auth with a <strong><a href=\"https:\/\/jetpack.com\/resources\/wordpress-rest-api\/\">WordPress Application Password<\/a><\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-7\"><code>def upload_media(base_url: str, file_path: Path, username: str, app_password: str, *, title=None, description=None) -&gt; Dict:\n    endpoint = base_url.rstrip(\"\/\") + \"\/wp-json\/wp\/v2\/media\"\n    token = base64.b64encode(f\"{username}:{app_password}\".encode(\"utf-8\")).decode(\"ascii\")\n    headers = {\"Authorization\": f\"Basic {token}\"}\n    with open(file_path, \"rb\") as f:\n        files = {\"file\": (file_path.name, f, \"audio\/mpeg\")}\n        resp = requests.post(endpoint, headers=headers, files=files, data={\"title\": title or \"\"}, timeout=120)\n    resp.raise_for_status()\n    return resp.json()\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">On success, WordPress returns the media <code>id<\/code> and <code>source_url<\/code> so you can embed or share the audio easily.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"630\" height=\"74\" data-src=\"\/wp-content\/uploads\/2025\/08\/image-9.png\" alt=\"\" class=\"wp-image-53618 lazyload\" data-srcset=\"\/wp-content\/uploads\/2025\/08\/image-9.png 630w, \/wp-content\/uploads\/2025\/08\/image-9-300x35.png 300w, \/wp-content\/uploads\/2025\/08\/image-9-480x56.png 480w\" data-sizes=\"(max-width: 630px) 100vw, 630px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 630px; --smush-placeholder-aspect-ratio: 630\/74;\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-command-line-usage\">Command-line usage<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The script is fully configurable via flags or environment variables:<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-8\"><code>python wp_voice_blog.py \\\n  --url https:\/\/your-site.com \\\n  --limit 10 \\\n  --voice alloy \\\n  --model gpt-4o-mini-tts \\\n  --outdir WordPress\/tts_out \\\n  --upload\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Other useful flags<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>--include-code<\/code> \u2014 read code blocks aloud.<\/li>\n\n\n\n<li><code>--wp-user<\/code> \/ <code>--wp-app-pass<\/code> \u2014 override <code>.env<\/code> values for uploads.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-security-amp-reliability-notes\">Security &amp; reliability notes<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Secrets<\/strong>: Keep your <code>OPENAI_API_KEY<\/code> and WP Application Password out of source control. Use <code>.env<\/code> locally and secure secrets in CI\/CD.<\/li>\n\n\n\n<li><strong>Timeouts<\/strong>: Requests use sensible timeouts; you can tune them if your site is slow.<\/li>\n\n\n\n<li><strong>Rate limits<\/strong>: If you plan to batch through many posts, add brief sleeps and handle HTTP 429 gracefully.<\/li>\n\n\n\n<li><strong>Attribution<\/strong>: Consider adding an audio disclaimer (\u201cThis post was auto-narrated\u201d) for transparency.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-yoast-seo-related-links yoast-seo-related-links\">\n<li><a href=\"https:\/\/www.cloudproinc.com.au\/index.php\/2024\/10\/07\/deploy-azure-openai-resource-using-bicep\/\">Deploy Azure OpenAI Resource Using Bicep<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.cloudproinc.com.au\/index.php\/2025\/08\/04\/how-to-fix-the-critical-error-on-azure-wordpress-web-app\/\">How to Fix the &#8216;Critical Error&#8217; on Azure WordPress Web App<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.cloudproinc.com.au\/index.php\/2024\/07\/08\/creating-custom-error-pages-in-azure-web-app\/\">Creating Custom Error Pages in Azure Web App<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.cloudproinc.com.au\/index.php\/2025\/05\/01\/building-a-blazor-net-app-that-recognizes-images-with-openai\/\">Building a Blazor .NET App that Recognizes Images with OpenAI<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.cloudproinc.com.au\/index.php\/2024\/10\/07\/creating-a-text-to-speech-power-app-using-openai-whisper\/\">Creating a Text-to-Speech Power App Using OpenAI Whisper<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>This blog post, &#8220;Turn WordPress Posts into \u201cVoice Blogs\u201d with Python + OpenAI TTS&#8221; will show you how to pull posts from a WordPress site via the REST API, converts the article content to speech using OpenAI\u2019s Text-to-Speech (TTS), saves an MP3, and (optionally) uploads the file back to your WordPress Media Library.<\/p>\n","protected":false},"author":1,"featured_media":53666,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"off","_et_pb_old_content":"","_et_gb_content_width":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[24,13,53,79,78],"tags":[],"class_list":["post-53614","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai","category-blog","category-openai","category-python","category-wordpress"],"jetpack-related-posts":[{"id":56828,"url":"https:\/\/www.cloudproinc.com.au\/index.php\/2026\/10\/12\/customise-voice-synthesis-with-azure-speech-and-ssml\/","url_meta":{"origin":53614,"position":0},"title":"Customise Voice Synthesis With Azure Speech And SSML","author":"CPI Staff","date":"October 12, 2026","format":false,"excerpt":"Learn how to customise synthetic voices using Azure Cognitive Services and SSML to deliver clearer, more natural text-to-speech experiences in your applications.","rel":"","context":"In &quot;Azure AI Services&quot;","block_context":{"text":"Azure AI Services","link":"https:\/\/www.cloudproinc.com.au\/index.php\/category\/azure-ai-services\/"},"img":{"alt_text":"","src":"\/wp-content\/uploads\/2025\/11\/customise-voice-synthesis-with-azure-speech-and-ssml.png","width":350,"height":200,"srcset":"\/wp-content\/uploads\/2025\/11\/customise-voice-synthesis-with-azure-speech-and-ssml.png 1x, \/wp-content\/uploads\/2025\/11\/customise-voice-synthesis-with-azure-speech-and-ssml.png 1.5x, \/wp-content\/uploads\/2025\/11\/customise-voice-synthesis-with-azure-speech-and-ssml.png 2x, \/wp-content\/uploads\/2025\/11\/customise-voice-synthesis-with-azure-speech-and-ssml.png 3x, \/wp-content\/uploads\/2025\/11\/customise-voice-synthesis-with-azure-speech-and-ssml.png 4x"},"classes":[]},{"id":53774,"url":"https:\/\/www.cloudproinc.com.au\/index.php\/2026\/10\/12\/integrate-tiktoken-in-python-applications\/","url_meta":{"origin":53614,"position":1},"title":"Integrate Tiktoken in Python Applications","author":"CPI Staff","date":"October 12, 2026","format":false,"excerpt":"Learn what Tiktoken is and how to use it in Python to count tokens, budget prompts, and chunk text with a practical, step-by-step example.","rel":"","context":"In &quot;AI&quot;","block_context":{"text":"AI","link":"https:\/\/www.cloudproinc.com.au\/index.php\/category\/ai\/"},"img":{"alt_text":"","src":"\/wp-content\/uploads\/2025\/09\/integrate-tiktoken-in-python-applications.png","width":350,"height":200,"srcset":"\/wp-content\/uploads\/2025\/09\/integrate-tiktoken-in-python-applications.png 1x, \/wp-content\/uploads\/2025\/09\/integrate-tiktoken-in-python-applications.png 1.5x, \/wp-content\/uploads\/2025\/09\/integrate-tiktoken-in-python-applications.png 2x, \/wp-content\/uploads\/2025\/09\/integrate-tiktoken-in-python-applications.png 3x, \/wp-content\/uploads\/2025\/09\/integrate-tiktoken-in-python-applications.png 4x"},"classes":[]},{"id":53555,"url":"https:\/\/www.cloudproinc.com.au\/index.php\/2026\/10\/12\/counting-tokens-using-the-openai-python-sdk\/","url_meta":{"origin":53614,"position":2},"title":"Counting Tokens Using the OpenAI Python SDK","author":"CPI Staff","date":"October 12, 2026","format":false,"excerpt":"This post provides a comprehensive guide on counting tokens using the OpenAI Python SDK, covering Python virtual environments, managing your OpenAI API key securely, and the role of the requirements.txt file. In the world of Large Language Models (LLMs) and Artificial Intelligence (AI), the term \"token\" frequently arises. Tokens are\u2026","rel":"","context":"In &quot;AI&quot;","block_context":{"text":"AI","link":"https:\/\/www.cloudproinc.com.au\/index.php\/category\/ai\/"},"img":{"alt_text":"","src":"\/wp-content\/uploads\/2025\/07\/image-23.png","width":350,"height":200,"srcset":"\/wp-content\/uploads\/2025\/07\/image-23.png 1x, \/wp-content\/uploads\/2025\/07\/image-23.png 1.5x, \/wp-content\/uploads\/2025\/07\/image-23.png 2x"},"classes":[]},{"id":53960,"url":"https:\/\/www.cloudproinc.com.au\/index.php\/2026\/10\/12\/langchain-architecture-explained\/","url_meta":{"origin":53614,"position":3},"title":"LangChain Architecture Explained","author":"CPI Staff","date":"October 12, 2026","format":false,"excerpt":"A practical tour of LangChain\u2019s building blocks\u2014models, prompts, chains, memory, tools, and RAG\u2014plus LCEL, tracing, and deployment tips for production AI apps.","rel":"","context":"In &quot;AI&quot;","block_context":{"text":"AI","link":"https:\/\/www.cloudproinc.com.au\/index.php\/category\/ai\/"},"img":{"alt_text":"","src":"\/wp-content\/uploads\/2025\/09\/langchain-architecture-explained-for-agents-rag-and-production-apps.png","width":350,"height":200,"srcset":"\/wp-content\/uploads\/2025\/09\/langchain-architecture-explained-for-agents-rag-and-production-apps.png 1x, \/wp-content\/uploads\/2025\/09\/langchain-architecture-explained-for-agents-rag-and-production-apps.png 1.5x, \/wp-content\/uploads\/2025\/09\/langchain-architecture-explained-for-agents-rag-and-production-apps.png 2x, \/wp-content\/uploads\/2025\/09\/langchain-architecture-explained-for-agents-rag-and-production-apps.png 3x, \/wp-content\/uploads\/2025\/09\/langchain-architecture-explained-for-agents-rag-and-production-apps.png 4x"},"classes":[]},{"id":53640,"url":"https:\/\/www.cloudproinc.com.au\/index.php\/2026\/10\/12\/build-a-multi-agent-assistant-in-python-with-the-openai-agents-sdk\/","url_meta":{"origin":53614,"position":4},"title":"Build a Multi-Agent Assistant in Python with the OpenAI Agents SDK","author":"CPI Staff","date":"October 12, 2026","format":false,"excerpt":"This post \"Build a Multi-Agent Assistant in Python with the OpenAI Agents SDK\" shows how to build an AI agent that can (a) generate secure passwords, (b) tell the current time, and (c) hand off coding questions to a Python-tutor sub-agent. Along the way, we\u2019ll cover what the Agents SDK\u2026","rel":"","context":"In &quot;AI Agents&quot;","block_context":{"text":"AI Agents","link":"https:\/\/www.cloudproinc.com.au\/index.php\/category\/ai-agents\/"},"img":{"alt_text":"","src":"\/wp-content\/uploads\/2025\/08\/build-a-multi-agent-assistant-in-python-with-the-openai-agen-1.png","width":350,"height":200,"srcset":"\/wp-content\/uploads\/2025\/08\/build-a-multi-agent-assistant-in-python-with-the-openai-agen-1.png 1x, \/wp-content\/uploads\/2025\/08\/build-a-multi-agent-assistant-in-python-with-the-openai-agen-1.png 1.5x, \/wp-content\/uploads\/2025\/08\/build-a-multi-agent-assistant-in-python-with-the-openai-agen-1.png 2x, \/wp-content\/uploads\/2025\/08\/build-a-multi-agent-assistant-in-python-with-the-openai-agen-1.png 3x, \/wp-content\/uploads\/2025\/08\/build-a-multi-agent-assistant-in-python-with-the-openai-agen-1.png 4x"},"classes":[]},{"id":739,"url":"https:\/\/www.cloudproinc.com.au\/index.php\/2026\/10\/12\/creating-a-text-to-speech-power-app-using-openai-whisper\/","url_meta":{"origin":53614,"position":5},"title":"Creating a Text-to-Speech Power App Using OpenAI Whisper","author":"CPI Staff","date":"October 12, 2026","format":false,"excerpt":"In this post on OpenAI and Microsoft Power Apps, we will create a Text-to-Speech Power App using OpenAI Whisper. Table of contentsCreating a Text-to-Speech Power App Using OpenAI WhisperGet an API Key from OpenAICreating a Text-to-Speech Power AppCreate Power Automate FlowRelated Articles OpenAI whisper is OpenAI's Text-to-Speech service, offering human-like\u2026","rel":"","context":"In &quot;AI&quot;","block_context":{"text":"AI","link":"https:\/\/www.cloudproinc.com.au\/index.php\/category\/ai\/"},"img":{"alt_text":"","src":"\/wp-content\/uploads\/2024\/10\/Creating-a-Text-to-Speech-Power-App-Using-OpenAI-Whisper.webp","width":350,"height":200,"srcset":"\/wp-content\/uploads\/2024\/10\/Creating-a-Text-to-Speech-Power-App-Using-OpenAI-Whisper.webp 1x, \/wp-content\/uploads\/2024\/10\/Creating-a-Text-to-Speech-Power-App-Using-OpenAI-Whisper.webp 1.5x, \/wp-content\/uploads\/2024\/10\/Creating-a-Text-to-Speech-Power-App-Using-OpenAI-Whisper.webp 2x"},"classes":[]}],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"\/wp-content\/uploads\/2025\/08\/turn-wordpress-posts-into-voice-blogs-with-python-openai-tts-1.png","_links":{"self":[{"href":"https:\/\/www.cloudproinc.com.au\/index.php\/wp-json\/wp\/v2\/posts\/53614","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cloudproinc.com.au\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cloudproinc.com.au\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cloudproinc.com.au\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cloudproinc.com.au\/index.php\/wp-json\/wp\/v2\/comments?post=53614"}],"version-history":[{"count":3,"href":"https:\/\/www.cloudproinc.com.au\/index.php\/wp-json\/wp\/v2\/posts\/53614\/revisions"}],"predecessor-version":[{"id":53624,"href":"https:\/\/www.cloudproinc.com.au\/index.php\/wp-json\/wp\/v2\/posts\/53614\/revisions\/53624"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cloudproinc.com.au\/index.php\/wp-json\/wp\/v2\/media\/53666"}],"wp:attachment":[{"href":"https:\/\/www.cloudproinc.com.au\/index.php\/wp-json\/wp\/v2\/media?parent=53614"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cloudproinc.com.au\/index.php\/wp-json\/wp\/v2\/categories?post=53614"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cloudproinc.com.au\/index.php\/wp-json\/wp\/v2\/tags?post=53614"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}