{
    "componentChunkName": "component---src-pages-blog-mdx-slug-js",
    "path": "/blog/2026-06-19-building-a-voice-assistant/",
    "result": {"data":{"mdx":{"frontmatter":{"title":"Teaching a Voice Assistant to Speak Spanish Like Me","tags":["AI","Mistral","voice","machine learning","Voxtral"],"categories":[],"date":"June 19, 2026","image":null,"imageAlt":null},"id":"182b1abf-7702-5cb7-8de9-ba26e74b811f","body":"var _excluded = [\"components\"];\nfunction _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }\nfunction _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }\nfunction _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Teaching a Voice Assistant to Speak Spanish Like Me\",\n  \"date\": \"2026-06-19T00:00:00.000Z\",\n  \"tags\": [\"AI\", \"Mistral\", \"voice\", \"machine learning\", \"Voxtral\"],\n  \"categories\": [],\n  \"draft\": false\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"Project one in the Mistral course turned out to be the subtitle tool I wrote about \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"/posts/2026-06-12-building-the-subtitle-tool\"\n  }, \"last time\"), \". Project two showed up right after, and its spec pointed straight at a question I'd been sitting on since \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"/posts/2026-06-06-first-steps-with-mistral\"\n  }, \"my first post about Mistral\"), \": Could I build something where you ask a question in one language and get the answer back in another, in a voice that still sounds like a real person?\"), mdx(\"p\", null, \"The answer turned out to be yes, but not in the way I expected, and getting there meant running straight into one of those facts about a model that you only discover by trying the thing it's supposedly capable of and watching it not quite work.\"), mdx(\"p\", null, \"The app itself is a turn-based voice assistant: hold a button, ask a question out loud, watch the recognized transcript appear, hear a spoken answer back.\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"text\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-text\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-text\"\n  }, \"mic audio -> Voxtral Realtime STT -> editable transcript\\n           -> mistral-small-latest -> Voxtral TTS -> playback\"))), mdx(\"p\", null, \"\\\"Turn-based\\\" is a deliberate constraint from the spec, not a limitation I ran into. A full duplex agent, one that listens while it's also talking and can be interrupted mid-sentence, is a much harder system to build and a much easier one to get wrong. The course spec called for something that \\\"should feel like a practical voice FAQ or helper assistant rather than an experimental demo,\\\" and turn-based is what makes that achievable: you speak, it processes, it answers, and the state is always one of a small fixed set. The code's on \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/gjack/voice-assistant\"\n  }, \"GitHub\"), \" if you want to dig through it yourself.\"), mdx(\"hr\", null), mdx(\"h2\", null, \"A State Machine You Can Actually Point To\"), mdx(\"p\", null, \"The whole app is built around one idea: at any moment, the session is in exactly one of \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"idle\"), \", \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"listening\"), \", \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"transcribing\"), \", \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"thinking\"), \", \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"speaking\"), \", or \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"error\"), \". Not \\\"roughly listening,\\\" not two things at once. One state, always known, always sent to the frontend the moment it changes:\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"python\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-python\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"async\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"def\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"send_state\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"ws\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" WebSocket\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" SessionState\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" new_status\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token builtin\"\n  }, \"str\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \"\\n    state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"status \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" new_status\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"await\"), \" ws\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"send_json\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"type\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"state\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"state\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" new_status\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\")))), mdx(\"p\", null, \"The frontend doesn't infer state from a pile of booleans; it just renders whatever the server tells it. A badge updates, a hint sentence below it changes, and buttons enable or disable themselves:\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"javascript\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-javascript\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"export\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"function\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"setState\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token parameter\"\n  }, \"state\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \"\\n    currentState \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"renderBadge\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"const\"), \" isIdle \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" state \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"===\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"idle\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n    talkBtn\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"disabled \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"!\"), \"isIdle\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n    sendBtn\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"disabled \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"!\"), \"isIdle\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n    transcriptInput\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"disabled \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"!\"), \"isIdle\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n    stopBtn\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"hidden \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" state \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"!==\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"speaking\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\")))), mdx(\"p\", null, \"This sounds almost too simple to mention, but it's the thing that made the rest of the app easy to reason about. Every bug I ran into while building this had an obvious shape once I knew which of six states it happened in. Compare that to the subtitle tool's realtime side, where partial text could be flickering in at any moment regardless of what else was happening. There, the looseness was the point. Here, the rigidity is.\"), mdx(\"hr\", null), mdx(\"h2\", null, \"Push, Talk, Then Stop and Wait\"), mdx(\"p\", null, \"Holding the talk button (or holding Space, the keyboard shortcut) starts capture exactly the way the subtitle tool's microphone input did: 32-bit float samples from \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"getUserMedia\"), \", rescaled into 16-bit PCM, shipped over a WebSocket. I won't repeat that conversion here since I walked through it last time, but it's worth noting the capture only runs while a flag is true:\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"javascript\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-javascript\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"processorNode\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function-variable function\"\n  }, \"onaudioprocess\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token parameter\"\n  }, \"e\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=>\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"if\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"!\"), \"streamingAudio \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"||\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"!\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"isOpen\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"return\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"const\"), \" float32 \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" e\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"inputBuffer\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"getChannelData\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"0\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"const\"), \" int16 \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"new\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token class-name\"\n  }, \"Int16Array\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"float32\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"length\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"for\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"let\"), \" i \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"0\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \" i \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"<\"), \" float32\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"length\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \" i\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"++\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \"\\n        \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"const\"), \" s \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" Math\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"max\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"-\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"1\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" Math\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"min\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"1\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" float32\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), \"i\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n        int16\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), \"i\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" s \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"<\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"0\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"?\"), \" s \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"*\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"0x8000\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \":\"), \" s \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"*\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"0x7fff\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"sendBinary\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"int16\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"buffer\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\")))), mdx(\"p\", null, \"What's different from the subtitle tool is what happens on the backend once the button comes back up. In the realtime subtitling app, two async tasks raced each other and whichever finished first triggered cleanup for both, because either side (user clicking Stop, or a connection error) could legitimately end things first. Here there's a clear order, because there's a clear trigger: the button being released.\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"python\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-python\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"receive_task \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" asyncio\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"create_task\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"receive_audio\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\nevents_task \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" asyncio\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"create_task\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"process_events\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\n\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token comment\"\n  }, \"# Wait for the client to release the talk button (or disconnect).\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"await\"), \" receive_task\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"await\"), \" send_state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"ws\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"transcribing\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\n\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"try\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"await\"), \" asyncio\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"wait_for\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"events_task\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" timeout\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \"ASR_TRANSCRIPT_TIMEOUT_S\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"except\"), \" asyncio\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"TimeoutError\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \"\\n    events_task\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"cancel\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\")))), mdx(\"p\", null, mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"receive_audio\"), \" forwards mic bytes to Voxtral until it sees a \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"stop_listening\"), \" control message, then flushes and ends the audio stream. Only once that's done does the state flip to \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"transcribing\"), \" and the app waits (with a timeout, in case Voxtral never sends a final event) for \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"process_events\"), \" to produce the finished transcript. Sequential, not racing, because in a push-to-talk flow there's only one thing that can end the turn.\"), mdx(\"hr\", null), mdx(\"h2\", null, \"The Transcript You're Allowed to Argue With\"), mdx(\"p\", null, \"Here's the part of the spec I didn't expect to matter as much as it did: the two-phase flow, where you review and edit the recognized transcript before it's sent to the LLM. Not a single pipeline from speech to answer, but a deliberate pause in the middle where the recognized text sits in an editable box and waits for you to either fix it or send it as-is.\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"python\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-python\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"pending_user_text \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" normalized\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"await\"), \" ws\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"send_json\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"type\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"transcript\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"text\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" normalized\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"await\"), \" send_state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"ws\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"idle\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\")))), mdx(\"p\", null, \"Notice that last line: after transcription finishes, the state goes back to \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"idle\"), \", not straight to \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"thinking\"), \". Nothing happens automatically. The transcript lands in the input box, and the app just waits for you to either edit it or hit Send (or Enter). It's a small interaction choice, but it changes the feel of the whole thing from \\\"an agent that's listening to everything I say\\\" to \\\"a tool that heard something and is checking with me before it acts on it.\\\" Given that Voxtral can occasionally mis-hear a word, especially with an accent (something I ran into directly with the subtitle tool's \\\"voice\\\" becoming \\\"both\\\"), having a checkpoint before that mistake gets sent to an LLM and read back to me out loud felt like the right default, not an extra feature.\"), mdx(\"hr\", null), mdx(\"h2\", null, \"One Endpoint, Four Personalities\"), mdx(\"p\", null, \"Once a transcript is sent, \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"handle_response\"), \" in \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"conversation.py\"), \" takes over: build a prompt from the current persona, call \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"mistral-small-latest\"), \", then hand the answer to TTS.\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"python\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-python\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"preset \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" PRESETS\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"get\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"preset\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" PRESETS\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), \"DEFAULT_PRESET\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\nsystem_prompt \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" preset\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"system_prompt\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), \"\\n\\nmessages \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"role\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"system\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"content\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" system_prompt\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), \"\\nmessages\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"extend\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"history\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"-\"), \"RESPONSE_CONFIG\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"history_limit\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\n\\nresponse \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"await\"), \" asyncio\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"to_thread\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"\\n    client\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"chat\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"complete\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n    model\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \"LLM_MODEL\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n    messages\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \"messages\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n    max_tokens\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \"RESPONSE_CONFIG\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"max_tokens\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n    reasoning_effort\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \"preset\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"reasoning_effort\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\")))), mdx(\"p\", null, \"The four starter personas (\", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"helper\"), \", \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"tutor\"), \", \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"course\"), \", \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"sarcastic\"), \") are just different system prompts and a different \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"reasoning_effort\"), \" per preset, the same parameter from the first Mistral exercise I wrote about: a quick factual answer doesn't need much thought, but \\\"Technical Tutor\\\" is set to \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"\\\"high\\\"\"), \" because explaining something step by step benefits from the model actually working through it first. Having that as a structured field on the preset, rather than a setting buried somewhere else, made it trivial to give each persona a different cost/depth tradeoff without touching any of the orchestration code.\"), mdx(\"p\", null, mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"asyncio.to_thread\"), \" is doing a quiet but necessary job in both this call and the TTS call a few lines later: the Mistral SDK calls here are synchronous, and the rest of the server is one async event loop. Without offloading them to a thread, a single slow chat completion would block every other WebSocket connection on the server, not just the one waiting on it.\"), mdx(\"hr\", null), mdx(\"h2\", null, \"Telling It What Language to Answer In, Without a Language Parameter\"), mdx(\"p\", null, \"This is where the part I actually cared about lives. I added a language dropdown: English, French, Spanish, German, Italian, Portuguese, independent of whatever language you spoke or typed in. Ask in English, get the answer in Spanish. The twist is that Voxtral's TTS API has no language parameter at all; it just speaks whatever text it's given, in whatever language that text happens to be. So \\\"respond in a different language\\\" can't be a TTS setting. It has to happen one step earlier, in the system prompt the LLM sees:\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"python\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-python\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"if\"), \" state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"language \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"!=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"auto\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \"\\n    language_label \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" LANGUAGES\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"get\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"language\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"get\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"label\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"language\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\n    system_prompt \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"+=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"\\n        \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string-interpolation\"\n  }, mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token string\"\n  }, \"f\\\"\\\\n\\\\nAlways respond in \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token interpolation\"\n  }, mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \"language_label\", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token punctuation\"\n  }, \"}\")), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token string\"\n  }, \", regardless of what \\\"\")), \"\\n        \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"language the user writes or speaks in.\\\"\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\")))), mdx(\"p\", null, \"The LLM does the translation as a side effect of generating its answer, and the TTS model never knows or cares that a translation happened; it just gets handed Spanish text and reads it. That part worked immediately and felt like a clean trick. What I didn't expect was what happened to the \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"voice\"), \" once it started speaking a language it wasn't built for.\"), mdx(\"p\", null, \"I picked a default English voice, asked a question, and set the language dropdown to Spanish. The transcript came back correctly translated. The audio played. And the voice reading it back to me sounded unmistakably like a British person reading Spanish off a page, technically correct words, completely wrong cadence and vowel sounds. It wasn't broken, it just didn't sound native, and once I noticed it I couldn't stop noticing it.\"), mdx(\"p\", null, \"Digging into why led to a field I'd glossed over while building the voice list: \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"client.audio.voices.list()\"), \" returns a \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"languages\"), \" tag per voice, things like \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"en_us\"), \", \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"en_gb\"), \", \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"fr_fr\"), \". Only English and French have built-in voices with a tag that matches them natively. Spanish, German, Italian, and Portuguese don't have a single built-in voice tagged for them at all, which means whatever voice you've selected gets used anyway, accent and all:\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"python\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-python\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"LANGUAGES \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"auto\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"label\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"Auto (match user)\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"voice_tags\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"en\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"label\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"English\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"voice_tags\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"en_us\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"en_gb\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"fr\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"label\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"French\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"voice_tags\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"fr_fr\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"es\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"label\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"Spanish\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"voice_tags\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"de\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"label\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"German\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"voice_tags\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"it\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"label\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"Italian\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"voice_tags\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"pt\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"label\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"Portuguese\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"voice_tags\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\")))), mdx(\"p\", null, \"The frontend uses those tags to filter the voice dropdown down to accent-matching options whenever they exist, and to show a hint when they don't:\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"javascript\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-javascript\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"const\"), \" noNativeVoice \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" lang \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"!==\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"auto\\\"\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"&&\"), \" tags\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"length \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"===\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"0\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\nvoiceHint\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"hidden \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"!\"), \"noNativeVoice\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"if\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"noNativeVoice\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \"\\n    voiceHint\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"textContent \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \"\\n        \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token template-string\"\n  }, mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token template-punctuation string\"\n  }, \"`\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token string\"\n  }, \"No built-in voice is native to \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token interpolation\"\n  }, mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token interpolation-punctuation punctuation\"\n  }, \"${\"), \"label\", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token interpolation-punctuation punctuation\"\n  }, \"}\")), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token string\"\n  }, \". For a more natural accent, \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token template-punctuation string\"\n  }, \"`\")), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"+\"), \"\\n        \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token template-string\"\n  }, mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token template-punctuation string\"\n  }, \"`\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token string\"\n  }, \"try \\\"Clone voice\\u2026\\\" with a \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token interpolation\"\n  }, mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token interpolation-punctuation punctuation\"\n  }, \"${\"), \"label\", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token interpolation-punctuation punctuation\"\n  }, \"}\")), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token string\"\n  }, \" speaker clip.\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token template-punctuation string\"\n  }, \"`\")), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\")))), mdx(\"p\", null, \"That hint is the whole answer: for a language with no native built-in voice, the only way to get a native-sounding accent is to clone one. So I did. I recorded ten seconds of myself speaking Spanish, my actual native language, and uploaded it through the clone button. That request doesn't go through the paid \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"voices.create()\"), \" endpoint; it uses \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"ref_audio\"), \", a base64-encoded clip that works on the free plan and lasts only for the lifetime of the running server process:\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"python\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-python\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token decorator annotation punctuation\"\n  }, \"@router\", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"post\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"/api/clone-voice\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"async\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"def\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"clone_voice\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token builtin\"\n  }, \"file\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" UploadFile \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" File\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \"\\n    audio_bytes \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"await\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token builtin\"\n  }, \"file\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"read\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\n    ref_audio_b64 \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" base64\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"b64encode\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"audio_bytes\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"decode\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"ascii\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\n    clone_id \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string-interpolation\"\n  }, mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token string\"\n  }, \"f\\\"clone:\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token interpolation\"\n  }, mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \"uuid\", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"uuid4\", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token builtin\"\n  }, \"hex\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token punctuation\"\n  }, \":\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token format-spec\"\n  }, \"8]\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token punctuation\"\n  }, \"}\")), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token string\"\n  }, \"\\\"\")), \"\\n    CLONED_VOICES\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), \"clone_id\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" ref_audio_b64\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"return\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"id\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" clone_id\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"name\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string-interpolation\"\n  }, mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token string\"\n  }, \"f\\\"Cloned (\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token interpolation\"\n  }, mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token builtin\"\n  }, \"file\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"filename\", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token punctuation\"\n  }, \"}\")), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"token string\"\n  }, \")\\\"\")), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\")))), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"python\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-python\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"tts_kwargs \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"model\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" TTS_MODEL\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"input\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" answer\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"response_format\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"mp3\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"if\"), \" state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"voice_id \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"and\"), \" state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"voice_id\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"startswith\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"clone:\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \"\\n    tts_kwargs\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"ref_audio\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" CLONED_VOICES\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"get\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"voice_id\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"elif\"), \" state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"voice_id\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \"\\n    tts_kwargs\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"voice_id\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" state\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"voice_id\"))), mdx(\"p\", null, \"I selected my own cloned voice, asked a question in English, set the response language to Spanish, and the answer came back in my own voice, speaking fluent, natively-accented Spanish, words I never actually said. Ten seconds of audio was enough. It's the same trick from the very first Mistral exercise that stopped me in my tracks, except this time it wasn't a scripted demo I was watching happen. It was an app I'd built, doing it on a question I made up on the spot, in a voice that was unmistakably mine.\"), mdx(\"hr\", null), mdx(\"h2\", null, \"What It's Like to Use\"), mdx(\"p\", null, mdx(\"span\", {\n    parentName: \"p\",\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"600px\"\n    }\n  }, \"\\n      \", mdx(\"a\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-link\",\n    \"href\": \"/static/cec5ebd28b47e3f6597853e6ab58f6d0/5f652/app-screenshot.png\",\n    \"style\": {\n      \"display\": \"block\"\n    },\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }, \"\\n    \", mdx(\"span\", {\n    parentName: \"a\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"121.33333333333334%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAYCAYAAAD6S912AAAACXBIWXMAABYlAAAWJQFJUiTwAAAC2ElEQVQ4y+2VXW7TQBDH84SgTeLYa3u9/rZjx3bifDVJW1UtFQKKSqqIUkBcAHoEzsANOALnQeI8f7TjJLilIKHyyMNPMztjz87OjmYb7Y6OrD/CdHaI4XiOcjTDZHZAaz9K0dFMNNsa5HeSlsK2UlENtDsMzLBhe120VQMN6WSmC8MK4EU5knwMy0vAuE8fhckAWTlDkAxg+ym8MCNbUkwg/BROmMMUIZjh0gYNuZMb9rB3cIrx/BhZOcd0/zHy4YICuEGGoNuH4/cg3IQCSpsf5TDtAKZwoVsOdO5AUc0qQ1W3IJwIXPhQdQHLDiqdWURH45Wui5rNhO2WCOIjhPERHG9CpWjINGWWj3YV7Owq2G2pJHealX4XzTbDg4cPcfrkMz5ef8eH629YvfoKRbWqI9t+gnJ6iP5ogXw4J/rjfRSjBfrjBUlZR+kvhnNiuCe/PwEXJQyeQTeTqobNlopef4rl5Xucr97ibHmF5y9f43z1DmcXlf7i4g3pJJdXeHZ+iZOnSxSTfXSYjmZbQUtRqyNXt+zAckJCFprblc7Xa1MEsNzohl/WUzMcdJhFmckLoQyplxSGVlujfmspGulb5Fph5Kv7N30pqfqxko2Nch/qMRr3DXab/wH/QcDbt/w3t37Xv3e2Tb3H/sSvm6yHw2ZgbhpYZRym5cLgDnSzGk1yLdFNmyQzBDW5HLD1TShDnbuwvYQGqsQNUvhRBj/O4UkZZWuZk+4GPQivC9MOCc0Q0HTxs4bS2S2mSNZ080lFMUGcjRH2hgiSEmE6JLuflPC6A5J22MNy9QXHp5+w22pWAQ3LB7djmHZ0ExHBi/soxgcYTA6R9vfIzp0YlhOTlIMiTk/gR3M6/o03RQaW0qwhvARpMUFezhCl5dZvrJE6MwWxfQK4CBCnJeH4CXS5Afehc4/QdAeqboMZztZWRyYi5baG8n3YvhWM0xtSR97673x1ZMAfZeBwAY3FMUoAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"a\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"The app's dark-themed interface: a Persona, Voice, and Language dropdown row, a chat bubble showing \\\"What is a planet?\\\" answered in French, the Hold to Talk button, and a collapsed Technical details drawer\",\n    \"title\": \"The app's dark-themed interface: a Persona, Voice, and Language dropdown row, a chat bubble showing \\\"What is a planet?\\\" answered in French, the Hold to Talk button, and a collapsed Technical details drawer\",\n    \"src\": \"/static/cec5ebd28b47e3f6597853e6ab58f6d0/0a47e/app-screenshot.png\",\n    \"srcSet\": [\"/static/cec5ebd28b47e3f6597853e6ab58f6d0/8a4e8/app-screenshot.png 150w\", \"/static/cec5ebd28b47e3f6597853e6ab58f6d0/5a46d/app-screenshot.png 300w\", \"/static/cec5ebd28b47e3f6597853e6ab58f6d0/0a47e/app-screenshot.png 600w\", \"/static/cec5ebd28b47e3f6597853e6ab58f6d0/1cfc2/app-screenshot.png 900w\", \"/static/cec5ebd28b47e3f6597853e6ab58f6d0/c1b63/app-screenshot.png 1200w\", \"/static/cec5ebd28b47e3f6597853e6ab58f6d0/5f652/app-screenshot.png 1302w\"],\n    \"sizes\": \"(max-width: 600px) 100vw, 600px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\",\n    \"decoding\": \"async\"\n  }), \"\\n  \"), \"\\n    \")), mdx(\"p\", null, \"Open the app, hold the talk button, ask something. The badge moves through Listening, Transcribing, Thinking, Speaking, in that order, every time, no ambiguity about what's currently happening. The recognized transcript lands in an editable box; you can fix a mis-heard word before it goes anywhere. Pick a persona and the same question gets a noticeably different answer: dry and short from Sarcastic Dev, patient and stepwise from Technical Tutor. Pick a response language other than your own and the assistant translates on the way out, voice and all.\"), mdx(\"p\", null, \"None of the individual pieces are new technology. Speech recognition, chat completion, and text-to-speech all exist on their own. What stood out to me here was how much of building this was about composing three separate models into one coherent turn, and how a tiny detail buried in a voice's metadata (a \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"languages\"), \" tag most people would never look at) ends up being the difference between an app that translates and an app that actually sounds like it belongs in the language it's speaking.\"), mdx(\"hr\", null), mdx(\"h2\", null, \"What I'm Curious About Next\"), mdx(\"p\", null, \"The non-goals list for this version was explicit: no barge-in, no interrupting the assistant mid-answer, no full duplex conversation. That was the right call for getting a stable v1 working, but now that it exists, the constraint is the thing nagging at me.\"), mdx(\"p\", null, \"Right now, if the assistant gives a long answer in Technical Tutor mode and I already understood the first sentence, my only option is to let it finish or hit Stop and lose the rest. A real conversation doesn't work that way; people interrupt, redirect, and talk over each other constantly, and a voice assistant that can't tolerate any of that still feels like it's reading at you rather than talking with you.\"), mdx(\"p\", null, \"I don't know yet whether that's a small change (listen for audio input while \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"speaking\"), \", treat it as an immediate cancel-and-restart) or a much bigger one (actual overlapping audio, deciding what to do with a half-finished answer the user talked over). That's the next thing I want to find out.\"));\n}\n;\nMDXContent.isMDXComponent = true;","timeToRead":7,"slug":"2026-06-19-building-a-voice-assistant/"},"allMdx":{"edges":[{"next":{"slug":"2013-03-17-silicon-valley-devfest/","frontmatter":{"title":"Silicon Valley DevFest"},"id":"edcdad98-0f1c-5f08-b9bf-ef2584a6784d"},"previous":null,"node":{"id":"3378703f-653c-58fd-a11e-7adc675fb79e","frontmatter":{"title":"Makings of a Pythonista"},"slug":"2012-11-21-makings-of-a-pythonista"}},{"next":{"slug":"2015-06-14-in-search-of-a-faster-query","frontmatter":{"title":"In search of a faster query"},"id":"c6497654-5ee0-5b14-b735-e08bbbc6a4bf"},"previous":{"frontmatter":{"title":"Makings of a Pythonista"},"slug":"2012-11-21-makings-of-a-pythonista"},"node":{"id":"edcdad98-0f1c-5f08-b9bf-ef2584a6784d","frontmatter":{"title":"Silicon Valley DevFest"},"slug":"2013-03-17-silicon-valley-devfest/"}},{"next":{"slug":"2015-11-01-diary-of-a-dev-joys-of-building","frontmatter":{"title":"Diary of a Junior Dev: The joys of building"},"id":"ca6c84a6-3aea-5278-bf26-267d43acb227"},"previous":{"frontmatter":{"title":"Silicon Valley DevFest"},"slug":"2013-03-17-silicon-valley-devfest/"},"node":{"id":"c6497654-5ee0-5b14-b735-e08bbbc6a4bf","frontmatter":{"title":"In search of a faster query"},"slug":"2015-06-14-in-search-of-a-faster-query"}},{"next":{"slug":"2016-12-24-working-with-d3-based-chart-library/","frontmatter":{"title":"TIL: Working with a D3 based chart library"},"id":"776dcc9f-2ac4-511a-a9c6-982c226e0478"},"previous":{"frontmatter":{"title":"In search of a faster query"},"slug":"2015-06-14-in-search-of-a-faster-query"},"node":{"id":"ca6c84a6-3aea-5278-bf26-267d43acb227","frontmatter":{"title":"Diary of a Junior Dev: The joys of building"},"slug":"2015-11-01-diary-of-a-dev-joys-of-building"}},{"next":{"slug":"2017-03-15-react-conf-2017/","frontmatter":{"title":"React Conf 2017"},"id":"97c0196c-f0cd-5d6a-b2b9-fd3c6f1f1baa"},"previous":{"frontmatter":{"title":"Diary of a Junior Dev: The joys of building"},"slug":"2015-11-01-diary-of-a-dev-joys-of-building"},"node":{"id":"776dcc9f-2ac4-511a-a9c6-982c226e0478","frontmatter":{"title":"TIL: Working with a D3 based chart library"},"slug":"2016-12-24-working-with-d3-based-chart-library/"}},{"next":{"slug":"2017-04-12-the-initial-state-gotcha","frontmatter":{"title":"The Initial State Gotcha"},"id":"82041731-cb81-571d-958c-00f57d99b927"},"previous":{"frontmatter":{"title":"TIL: Working with a D3 based chart library"},"slug":"2016-12-24-working-with-d3-based-chart-library/"},"node":{"id":"97c0196c-f0cd-5d6a-b2b9-fd3c6f1f1baa","frontmatter":{"title":"React Conf 2017"},"slug":"2017-03-15-react-conf-2017/"}},{"next":{"slug":"2017-05-13-blue-socks-at-orange/","frontmatter":{"title":"Blue Socks Spotted at Orange"},"id":"893cf729-88c0-5706-bcb8-d14f75a30f2b"},"previous":{"frontmatter":{"title":"React Conf 2017"},"slug":"2017-03-15-react-conf-2017/"},"node":{"id":"82041731-cb81-571d-958c-00f57d99b927","frontmatter":{"title":"The Initial State Gotcha"},"slug":"2017-04-12-the-initial-state-gotcha"}},{"next":{"slug":"once-upon-a-blog/","frontmatter":{"title":"Once upon a Blog"},"id":"db5caf55-934b-5de1-aa6b-5a2078e4f869"},"previous":{"frontmatter":{"title":"The Initial State Gotcha"},"slug":"2017-04-12-the-initial-state-gotcha"},"node":{"id":"893cf729-88c0-5706-bcb8-d14f75a30f2b","frontmatter":{"title":"Blue Socks Spotted at Orange"},"slug":"2017-05-13-blue-socks-at-orange/"}},{"next":{"slug":"story-of-a-laptop-upgrade","frontmatter":{"title":"Story of a laptop upgrade"},"id":"91dabe95-0227-5d01-b1d1-f10959c5d43a"},"previous":{"frontmatter":{"title":"Blue Socks Spotted at Orange"},"slug":"2017-05-13-blue-socks-at-orange/"},"node":{"id":"db5caf55-934b-5de1-aa6b-5a2078e4f869","frontmatter":{"title":"Once upon a Blog"},"slug":"once-upon-a-blog/"}},{"next":{"slug":"the-code-we-write-today/","frontmatter":{"title":"The code we write today"},"id":"08584458-be98-53c2-8cf3-9fa51060e04b"},"previous":{"frontmatter":{"title":"Once upon a Blog"},"slug":"once-upon-a-blog/"},"node":{"id":"91dabe95-0227-5d01-b1d1-f10959c5d43a","frontmatter":{"title":"Story of a laptop upgrade"},"slug":"story-of-a-laptop-upgrade"}},{"next":{"slug":"rails-on-docker/","frontmatter":{"title":"Rails on Docker"},"id":"5e92911a-24f7-562d-8ecd-9a7391960489"},"previous":{"frontmatter":{"title":"Story of a laptop upgrade"},"slug":"story-of-a-laptop-upgrade"},"node":{"id":"08584458-be98-53c2-8cf3-9fa51060e04b","frontmatter":{"title":"The code we write today"},"slug":"the-code-we-write-today/"}},{"next":{"slug":"rails-on-docker-part-two/","frontmatter":{"title":"Rails on Docker Part Two"},"id":"534e725d-71be-5e6f-ace7-75db51185701"},"previous":{"frontmatter":{"title":"The code we write today"},"slug":"the-code-we-write-today/"},"node":{"id":"5e92911a-24f7-562d-8ecd-9a7391960489","frontmatter":{"title":"Rails on Docker"},"slug":"rails-on-docker/"}},{"next":{"slug":"super-powers-for-active-record-relation","frontmatter":{"title":"Super Powers for ActiveRecord::Relation"},"id":"3d5d8b08-cf43-57be-8c43-aad95e2dfd06"},"previous":{"frontmatter":{"title":"Rails on Docker"},"slug":"rails-on-docker/"},"node":{"id":"534e725d-71be-5e6f-ace7-75db51185701","frontmatter":{"title":"Rails on Docker Part Two"},"slug":"rails-on-docker-part-two/"}},{"next":{"slug":"2026-05-30-outsmarting-github-copilot","frontmatter":{"title":"I Tried to Outsmart Copilot. It Made Me a Better Developer."},"id":"8bc48193-2ba7-5463-9edb-0f5e3a910bb1"},"previous":{"frontmatter":{"title":"Rails on Docker Part Two"},"slug":"rails-on-docker-part-two/"},"node":{"id":"3d5d8b08-cf43-57be-8c43-aad95e2dfd06","frontmatter":{"title":"Super Powers for ActiveRecord::Relation"},"slug":"super-powers-for-active-record-relation"}},{"next":{"slug":"2026-06-06-first-steps-with-mistral","frontmatter":{"title":"I Started a Mistral Tutorial. It Ended with My Voice Speaking French."},"id":"cb7d26e3-c28a-5c0a-a659-83f90d04bd15"},"previous":{"frontmatter":{"title":"Super Powers for ActiveRecord::Relation"},"slug":"super-powers-for-active-record-relation"},"node":{"id":"8bc48193-2ba7-5463-9edb-0f5e3a910bb1","frontmatter":{"title":"I Tried to Outsmart Copilot. It Made Me a Better Developer."},"slug":"2026-05-30-outsmarting-github-copilot"}},{"next":{"slug":"2026-06-12-building-the-subtitle-tool/","frontmatter":{"title":"Building a Live Subtitle Tool With Mistral"},"id":"fb5e6f8e-7da3-506b-b50c-869e8bdde883"},"previous":{"frontmatter":{"title":"I Tried to Outsmart Copilot. It Made Me a Better Developer."},"slug":"2026-05-30-outsmarting-github-copilot"},"node":{"id":"cb7d26e3-c28a-5c0a-a659-83f90d04bd15","frontmatter":{"title":"I Started a Mistral Tutorial. It Ended with My Voice Speaking French."},"slug":"2026-06-06-first-steps-with-mistral"}},{"next":{"slug":"2026-06-19-building-a-voice-assistant/","frontmatter":{"title":"Teaching a Voice Assistant to Speak Spanish Like Me"},"id":"182b1abf-7702-5cb7-8de9-ba26e74b811f"},"previous":{"frontmatter":{"title":"I Started a Mistral Tutorial. It Ended with My Voice Speaking French."},"slug":"2026-06-06-first-steps-with-mistral"},"node":{"id":"fb5e6f8e-7da3-506b-b50c-869e8bdde883","frontmatter":{"title":"Building a Live Subtitle Tool With Mistral"},"slug":"2026-06-12-building-the-subtitle-tool/"}},{"next":null,"previous":{"frontmatter":{"title":"Building a Live Subtitle Tool With Mistral"},"slug":"2026-06-12-building-the-subtitle-tool/"},"node":{"id":"182b1abf-7702-5cb7-8de9-ba26e74b811f","frontmatter":{"title":"Teaching a Voice Assistant to Speak Spanish Like Me"},"slug":"2026-06-19-building-a-voice-assistant/"}}]}},"pageContext":{"id":"182b1abf-7702-5cb7-8de9-ba26e74b811f","slug":"2026-06-19-building-a-voice-assistant/","__params":{"slug":"2026-06-19-building-a-voice-assistant"}}},
    "staticQueryHashes": ["2201243204"]}