Reviewed Reachy Mini SDK docs and repo.
UnderstandInvestigated button press interaction patterns.
UnderstandExplored text-to-speech implementation options.
UnderstandCompiled research into detailed report with installation and app structure.
PlanI need to build a small Python app for the Reachy Mini robot (made by Pollen Robotics). The app should: when a button is pressed, the robot says "good morning" out loud (text-to-speech).
The Reachy Mini SDK lives at https://github.com/pollen-robotics/reachy_mini (note underscore, not dash).
Please research and report back:
How to install the Python SDK (pip package name? install command?). Look for the installation doc — try https://github.com/pollen-robotics/reachy_mini/blob/main/docs/SDK/installation.md and similar paths on main and develop branches. If a raw URL works (raw.githubusercontent.com), use it.
The minimal Python code to instantiate the robot. The class name appears to be ReachyMini — confirm and show its constructor (host? IP? defaults?).
How to make Reachy Mini SPEAK text out loud. There are likely two paths:
(a) An SDK method on ReachyMini for TTS / playing audio (e.g. .audio.play(...), .speak(...), .tts(...), etc.).
(b) Or you may need to generate a WAV with an external TTS library and play it through the robot's speaker.
Find the actual canonical way from the docs. Quote the relevant code snippet verbatim if you find one.
Reachy Mini also has APP framework — there's mention of an "app store powered by Hugging Face Spaces" and an apps directory in the SDK. Check if there's a documented "app" template/structure (e.g. a pyproject.toml with an [reachy_mini.app] entrypoint, or an example in pollen-robotics/reachy_mini under an apps/ or examples/ folder, or a separate template repo). Look at example apps like https://github.com/pollen-robotics/reachy_mini_conversation_app to see the standard structure. Report the file layout and any required entrypoint signature.
The user wants a "button" — there's no physical button on Reachy Mini that I know of. So the "button" likely means one of:
keyboard lib or just input()).Return a concise writeup (under 600 words) with: install command, the canonical TTS call (with verbatim code), the app structure if applicable, and links to the specific docs you found. Quote code snippets verbatim — I need to write working code from your report.
I'll research the Reachy Mini SDK to answer your questions. Let me start by fetching the relevant docs and repos.
{
"type": "tool_reference",
"tool_name": "mcp__workspace__web_fetch"
}
{
"type": "tool_reference",
"tool_name": "WebSearch"
}
{
"type": "tool_reference",
"tool_name": "mcp__67f0ad9f-6406-433a-bf04-34cd14eb2618__slack_send_message_draft"
}
{
"type": "tool_reference",
"tool_name": "mcp__0d6519ff-cee2-4d02-9af6-4debf67941b9__search_threads"
}
{
"type": "tool_reference",
"tool_name": "mcp__67f0ad9f-6406-433a-bf04-34cd14eb2618__slack_create_canvas"
}I'll research the Reachy Mini SDK now.
{
"type": "tool_reference",
"tool_name": "mcp__workspace__web_fetch"
}
{
"type": "tool_reference",
"tool_name": "WebSearch"
}HTTP 404
Content-Type: text/plain; charset=utf-8
404: Not FoundHTTP 404
Content-Type: text/plain; charset=utf-8
404: Not FoundError: result (329,421 characters across 1,915 lines) exceeds maximum allowed tokens. Output has been saved to /var/folders/dw/zt1ncz810_j9j8x1hkv2lbgm0000gn/T/claude-hostloop-plugins/3c918a1b0d2f15ba/projects/-Users-clem-Library-Application-Support-Claude-local-agent-mode-sessions-60820195-31c6-4093-8c9e-37c09903f4a5-6e545699-813d-4c34-a80c-8667623c108d-local-8906e6df-1974-4b95-a48d-96cc2a2d91db-outputs/e647f442-7cdb-4e5d-8667-3b77a261289d/tool-results/mcp-workspace-web_fetch-1777470566636.txt.
Format: Plain text
- For targeted searches (find a line, locate a string): use grep on the file directly.
- For analysis or summarization that requires reading the full content: read /var/folders/dw/zt1ncz810_j9j8x1hkv2lbgm0000gn/T/claude-hostloop-plugins/3c918a1b0d2f15ba/projects/-Users-clem-Library-Application-Support-Claude-local-agent-mode-sessions-60820195-31c6-4093-8c9e-37c09903f4a5-6e545699-813d-4c34-a80c-8667623c108d-local-8906e6df-1974-4b95-a48d-96cc2a2d91db-outputs/e647f442-7cdb-4e5d-8667-3b77a261289d/tool-results/mcp-workspace-web_fetch-1777470566636.txt in chunks of ~1 lines using offset/limit until you have read 100% of it.
- If the Agent tool is available, do this inside a subagent so the full output stays out of your main context. Give it the instruction above verbatim, and be explicit about what it must return — e.g. "Read /var/folders/dw/zt1ncz810_j9j8x1hkv2lbgm0000gn/T/claude-hostloop-plugins/3c918a1b0d2f15ba/projects/-Users-clem-Library-Application-Support-Claude-local-agent-mode-sessions-60820195-31c6-4093-8c9e-37c09903f4a5-6e545699-813d-4c34-a80c-8667623c108d-local-8906e6df-1974-4b95-a48d-96cc2a2d91db-outputs/e647f442-7cdb-4e5d-8667-3b77a261289d/tool-results/mcp-workspace-web_fetch-1777470566636.txt in chunks of ~1 lines using offset/limit until you have read all 1,915 lines, then summarize and quote any key findings verbatim." A vague "summarize this" may lose detail.
HTTP 200
Content-Type: application/json; charset=utf-8
[
{
"name": ".gitattributes",
"path": ".gitattributes",
"sha": "cc8864c5bf503c870469b07ff1006ee8a1a7d9cb",
"size": 722,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/.gitattributes?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/blob/main/.gitattributes",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/cc8864c5bf503c870469b07ff1006ee8a1a7d9cb",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini/main/.gitattributes",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/.gitattributes?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/cc8864c5bf503c870469b07ff1006ee8a1a7d9cb",
"html": "https://github.com/pollen-robotics/reachy_mini/blob/main/.gitattributes"
}
},
{
"name": ".github",
"path": ".github",
"sha": "779f125e608f1fbc3d327167fe53e68d3bdbff4e",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/.github?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/tree/main/.github",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/779f125e608f1fbc3d327167fe53e68d3bdbff4e",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/.github?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/779f125e608f1fbc3d327167fe53e68d3bdbff4e",
"html": "https://github.com/pollen-robotics/reachy_mini/tree/main/.github"
}
},
{
"name": ".gitignore",
"path": ".gitignore",
"sha": "a54b080e28a6ba9a31daf8e805eb89088eef2141",
"size": 4925,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/.gitignore?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/blob/main/.gitignore",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/a54b080e28a6ba9a31daf8e805eb89088eef2141",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini/main/.gitignore",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/.gitignore?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/a54b080e28a6ba9a31daf8e805eb89088eef2141",
"html": "https://github.com/pollen-robotics/reachy_mini/blob/main/.gitignore"
}
},
{
"name": ".pre-commit-config.yaml",
"path": ".pre-commit-config.yaml",
"sha": "273d3502025f81c79f18c30971e9aaa6f0e45cee",
"size": 342,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/.pre-commit-config.yaml?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/blob/main/.pre-commit-config.yaml",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/273d3502025f81c79f18c30971e9aaa6f0e45cee",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini/main/.pre-commit-config.yaml",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/.pre-commit-config.yaml?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/273d3502025f81c79f18c30971e9aaa6f0e45cee",
"html": "https://github.com/pollen-robotics/reachy_mini/blob/main/.pre-commit-config.yaml"
}
},
{
"name": "AGENTS.md",
"path": "AGENTS.md",
"sha": "0409bcf59415eb4b7f0dd5fec6402728115a7b0e",
"size": 15016,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/AGENTS.md?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/blob/main/AGENTS.md",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/0409bcf59415eb4b7f0dd5fec6402728115a7b0e",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini/main/AGENTS.md",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/AGENTS.md?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/0409bcf59415eb4b7f0dd5fec6402728115a7b0e",
"html": "https://github.com/pollen-robotics/reachy_mini/blob/main/AGENTS.md"
}
},
{
"name": "CLAUDE.md",
"path": "CLAUDE.md",
"sha": "ba32d3377bbccef65e2884661bdf632d509b504a",
"size": 125,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/CLAUDE.md?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/blob/main/CLAUDE.md",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/ba32d3377bbccef65e2884661bdf632d509b504a",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini/main/CLAUDE.md",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/CLAUDE.md?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/ba32d3377bbccef65e2884661bdf632d509b504a",
"html": "https://github.com/pollen-robotics/reachy_mini/blob/main/CLAUDE.md"
}
},
{
"name": "LICENSE",
"path": "LICENSE",
"sha": "2bae156ba764607d491a34535fb0fb5a02022793",
"size": 11345,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/LICENSE?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/blob/main/LICENSE",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/2bae156ba764607d491a34535fb0fb5a02022793",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini/main/LICENSE",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/LICENSE?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/2bae156ba764607d491a34535fb0fb5a02022793",
"html": "https://github.com/pollen-robotics/reachy_mini/blob/main/LICENSE"
}
},
{
"name": "MANIFEST.in",
"path": "MANIFEST.in",
"sha": "f51342cf3346a74203c815f86556947c51e99f8a",
"size": 138,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/MANIFEST.in?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/blob/main/MANIFEST.in",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/f51342cf3346a74203c815f86556947c51e99f8a",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini/main/MANIFEST.in",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/MANIFEST.in?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/f51342cf3346a74203c815f86556947c51e99f8a",
"html": "https://github.com/pollen-robotics/reachy_mini/blob/main/MANIFEST.in"
}
},
{
"name": "README.md",
"path": "README.md",
"sha": "cd7e7a2f56ccdff2bfe7afee37499888e058240f",
"size": 5288,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/README.md?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/blob/main/README.md",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/cd7e7a2f56ccdff2bfe7afee37499888e058240f",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini/main/README.md",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/README.md?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/cd7e7a2f56ccdff2bfe7afee37499888e058240f",
"html": "https://github.com/pollen-robotics/reachy_mini/blob/main/README.md"
}
},
{
"name": "agents.local.md.template",
"path": "agents.local.md.template",
"sha": "c8d0d5d898fd50af8bc609ec7c9e4e1b00351d88",
"size": 832,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/agents.local.md.template?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/blob/main/agents.local.md.template",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/c8d0d5d898fd50af8bc609ec7c9e4e1b00351d88",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini/main/agents.local.md.template",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/agents.local.md.template?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/c8d0d5d898fd50af8bc609ec7c9e4e1b00351d88",
"html": "https://github.com/pollen-robotics/reachy_mini/blob/main/agents.local.md.template"
}
},
{
"name": "conftest.py",
"path": "conftest.py",
"sha": "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/conftest.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/blob/main/conftest.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini/main/conftest.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/conftest.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
"html": "https://github.com/pollen-robotics/reachy_mini/blob/main/conftest.py"
}
},
{
"name": "docs",
"path": "docs",
"sha": "d4634f5a680a8d0221d54885415d8c9893f53dfa",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/docs?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/tree/main/docs",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/d4634f5a680a8d0221d54885415d8c9893f53dfa",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/docs?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/d4634f5a680a8d0221d54885415d8c9893f53dfa",
"html": "https://github.com/pollen-robotics/reachy_mini/tree/main/docs"
}
},
{
"name": "examples",
"path": "examples",
"sha": "e0ccdb34ce6c4d7d90450b664f95516a8a668a56",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/examples?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/tree/main/examples",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/e0ccdb34ce6c4d7d90450b664f95516a8a668a56",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/examples?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/e0ccdb34ce6c4d7d90450b664f95516a8a668a56",
"html": "https://github.com/pollen-robotics/reachy_mini/tree/main/examples"
}
},
{
"name": "js",
"path": "js",
"sha": "797a557817ea65e999131ff5baf585fb7754aa93",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/js?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/tree/main/js",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/797a557817ea65e999131ff5baf585fb7754aa93",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/js?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/797a557817ea65e999131ff5baf585fb7754aa93",
"html": "https://github.com/pollen-robotics/reachy_mini/tree/main/js"
}
},
{
"name": "pyproject.toml",
"path": "pyproject.toml",
"sha": "e8742256fb57eeca8bbde02394352e1e5dc13357",
"size": 3812,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/pyproject.toml?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/blob/main/pyproject.toml",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/e8742256fb57eeca8bbde02394352e1e5dc13357",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini/main/pyproject.toml",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/pyproject.toml?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/e8742256fb57eeca8bbde02394352e1e5dc13357",
"html": "https://github.com/pollen-robotics/reachy_mini/blob/main/pyproject.toml"
}
},
{
"name": "scripts",
"path": "scripts",
"sha": "55bb402a8ae2a2866b8ec00b71875c499987777d",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/scripts?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/tree/main/scripts",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/55bb402a8ae2a2866b8ec00b71875c499987777d",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/scripts?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/55bb402a8ae2a2866b8ec00b71875c499987777d",
"html": "https://github.com/pollen-robotics/reachy_mini/tree/main/scripts"
}
},
{
"name": "skills",
"path": "skills",
"sha": "70f667ee2638a752f08c88274ee6992ce0f0af9d",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/skills?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/tree/main/skills",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/70f667ee2638a752f08c88274ee6992ce0f0af9d",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/skills?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/70f667ee2638a752f08c88274ee6992ce0f0af9d",
"html": "https://github.com/pollen-robotics/reachy_mini/tree/main/skills"
}
},
{
"name": "src",
"path": "src",
"sha": "9cc28edbb87db75c6607aab0235a98ebbd91d9e0",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/src?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/tree/main/src",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/9cc28edbb87db75c6607aab0235a98ebbd91d9e0",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/src?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/9cc28edbb87db75c6607aab0235a98ebbd91d9e0",
"html": "https://github.com/pollen-robotics/reachy_mini/tree/main/src"
}
},
{
"name": "tests",
"path": "tests",
"sha": "1f1ec573c810c48c91e77ed71e017b8cecd0ab75",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/tests?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/tree/main/tests",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/1f1ec573c810c48c91e77ed71e017b8cecd0ab75",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/tests?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/trees/1f1ec573c810c48c91e77ed71e017b8cecd0ab75",
"html": "https://github.com/pollen-robotics/reachy_mini/tree/main/tests"
}
},
{
"name": "uv.lock",
"path": "uv.lock",
"sha": "5a713b4a3e80b48d2e632e390ae5e3461e3a4a2c",
"size": 901085,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/uv.lock?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini/blob/main/uv.lock",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/5a713b4a3e80b48d2e632e390ae5e3461e3a4a2c",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini/main/uv.lock",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini/contents/uv.lock?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini/git/blobs/5a713b4a3e80b48d2e632e390ae5e3461e3a4a2c",
"html": "https://github.com/pollen-robotics/reachy_mini/blob/main/uv.lock"
}
}
]
HTTP 200
Content-Type: text/plain; charset=utf-8
# Reachy Mini 🤖
[](https://huggingface.co/docs/reachy_mini/)
[](https://discord.gg/Y7FgMqHsub)
**Reachy Mini is an open-source, expressive robot made for hackers and AI builders.**
🛒 [**Buy Reachy Mini**](https://www.hf.co/reachy-mini/)
[](https://www.pollen-robotics.com/reachy-mini/)
## ⚡️ Build and start your own robot
**Choose your platform to access the specific guide:**
| **🤖 Reachy Mini (Wireless)** | **🔌 Reachy Mini Lite** | **💻 Simulation** |
| :---: | :---: | :---: |
| The full autonomous experience.<br>Raspberry Pi CM4 + Battery + WiFi. | The developer version.<br>USB connection to your computer. | No hardware required.<br>Prototype in MuJoCo. |
| 👉 [**Go to Wireless Guide**](https://huggingface.co/docs/reachy_mini/platforms/reachy_mini/get_started) | 👉 [**Go to Lite Guide**](https://huggingface.co/docs/reachy_mini/platforms/reachy_mini_lite/get_started) | 👉 [**Go to Simulation**](https://huggingface.co/docs/reachy_mini/platforms/simulation/get_started) |
> ⚡ **Pro tip:** Install [uv](https://docs.astral.sh/uv/getting-started/installation/) for 10-100x faster app installations (auto-detected, falls back to `pip`).
<br>
## 📱 Apps & Ecosystem
Reachy Mini comes with an app store powered by Hugging Face Spaces. You can install these apps directly from your robot's dashboard with one click!
* **🗣️ [Conversation App](https://huggingface.co/spaces/pollen-robotics/reachy_mini_conversation_app):** Talk naturally with Reachy Mini (powered by LLMs).
* **📻 [Radio](https://huggingface.co/spaces/pollen-robotics/reachy_mini_radio):** Listen to the radio with Reachy Mini!
* **👋 [Hand Tracker](https://huggingface.co/spaces/pollen-robotics/hand_tracker_v2):** The robot follows your hand movements in real-time.
👉 [**Browse all apps on Hugging Face**](https://hf.co/reachy-mini/#/apps)
<br>
## 🚀 Getting Started with Reachy Mini SDK
### User guides
* **[Installation](https://huggingface.co/docs/reachy_mini/SDK/installation)**: 5 minutes to set up your computer
* **[Quickstart Guide](https://huggingface.co/docs/reachy_mini/SDK/quickstart)**: Run your first behavior on Reachy Mini
* **[Python SDK](https://huggingface.co/docs/reachy_mini/SDK/python-sdk)**: Learn to move, see, speak, and hear.
* **[AI Integrations](https://huggingface.co/docs/reachy_mini/SDK/integration)**: Connect LLMs, build Apps, and publish to Hugging Face.
* **[Core Concepts](https://huggingface.co/docs/reachy_mini/SDK/core-concept)**: Architecture, coordinate systems, and safety limits.
* 🤗[**Share your app with the community**](https://huggingface.co/blog/pollen-robotics/make-and-publish-your-reachy-mini-apps)
* 📂 [**Browse the Examples Folder**](examples)
* 📓 [**Tutorial Notebooks**](docs/notebooks): Step-by-step Jupyter notebooks covering connection, movement, camera, and audio
### 🤖 AI-Assisted Development
Using an AI coding agent (Claude Code, Codex, Copilot, etc.)? You can start building apps right away. Paste this prompt to your agent:
> *I'd like to create a Reachy Mini app. Start by reading https://github.com/pollen-robotics/reachy_mini/blob/main/AGENTS.md*
This [**AGENTS.md**](AGENTS.md) guide gives AI agents everything they need: SDK patterns, best practices, example apps, and step-by-step skills.
### Quick Look
After [installing the SDK](https://huggingface.co/docs/reachy_mini/SDK/installation), once your robot is awake, you can control it in just **a few lines of code**:
```python
from reachy_mini import ReachyMini
from reachy_mini.utils import create_head_pose
with ReachyMini() as mini:
# Look up and tilt head
mini.goto_target(
head=create_head_pose(z=10, roll=15, degrees=True, mm=True),
duration=1.0
)
```
<br>
## 🛠 Hardware Overview
Reachy Mini robots are sold as kits and generally take **2 to 3 hours** to assemble. Detailed step-by-step guides are available in the platform-specific folders linked above.
* **Reachy Mini (Wireless):** Runs onboard (RPi CM4), autonomous, includes IMU. [See specs](https://huggingface.co/docs/reachy_mini/platforms/reachy_mini/hardware).
* **Reachy Mini Lite:** Runs on your PC, powered via wall outlet. [See specs](https://huggingface.co/docs/reachy_mini/platforms/reachy_mini_lite/hardware).
<br>
## ❓ Troubleshooting
Encountering an issue? 👉 **[Check the Troubleshooting & FAQ Guide](https://huggingface.co/docs/reachy_mini/troubleshooting)**
<br>
## 🤝 Community & Contributing
* **Join the Community:** Join [Discord](https://discord.gg/2bAhWfXme9) to share your moments with Reachy, build apps together, and get help.
* **Found a bug?** Open an issue on this repository.
* **Guidelines:** Review our [contributing guidelines](docs/contributing.md) to learn how to contribute code, report issues, or suggest features.
## License
This project is licensed under the Apache 2.0 License. See the [LICENSE](LICENSE) file for details.
Hardware design files are licensed under Creative Commons BY-SA-NC.
Excellent! Now let me get the AGENTS.md (which is the key doc for AI agents) and other key references.
HTTP 200
Content-Type: text/plain; charset=utf-8
# Reachy Mini Development Guide for AI Agents
This guide helps AI agents assist users in developing Reachy Mini applications.
---
## Agent Behavior
### FIRST: Check for agents.local.md
**Before doing anything else**, search for `agents.local.md` in the current directory:
```
IF agents.local.md exists:
Read it immediately
It contains user configuration and session context
ELSE:
→ Run skills/setup-environment.md to set up the environment
```
This file stores the user's robot type, preferences, and setup status. Always check it first.
### Be a Teacher
Unless the user explicitly requests otherwise:
- Explain concepts as you go
- Encourage questions ("Let me know if you'd like more detail on any of this")
- Guide non-technical users through each step
- Don't assume prior knowledge
### Two App Flavours — Pick One Up Front
Reachy Mini supports two complementary app types. Confirm with the user which one they want before scaffolding anything:
| Flavour | Use when | Where it runs |
|---|---|---|
| **Python app** | On-robot control loops, heavy motion sequences, offline/LAN, rich hardware access | Robot owner's machine (laptop / CM4), optionally with a bundled web UI |
| **Live/Web/JS app** | Remote launch from any browser, zero-install end-users, shareable by URL, streaming A/V UIs | Entirely as a static HF Space; reaches the robot over WebRTC via the central signaling server |
Both coexist — a Python app can bundle a browser UI, and a JS app can call the Python daemon's REST API.
- **(python apps) NEVER create app folders manually** — use `reachy-mini-app-assistant` (Python)
- **If a command fails**, ask the user to run it in their terminal — don't attempt complex workarounds.
**Python scaffold:**
```bash
# Default template (minimal app - good for most cases):
reachy-mini-app-assistant create <app_name> <path> --publish
# Conversation template (for LLM integration, speech, making robot talk):
reachy-mini-app-assistant create --template conversation <app_name> <path> --publish
```
Python apps put web UIs in `static/`. See `skills/create-app.md` for details. For the JS path, jump to [Live/Web/JS Apps](#livewebjs-apps) below.
### Always Create plan.md Before Coding
Before implementing any app:
1. Create `plan.md` in the app directory
2. Write your understanding of what the user wants
3. List your technical approach
4. Ask clarifying questions and provide answer fields inside `plan.md`
5. Wait for answers before coding
### Keep Notes in agents.local.md
Use `agents.local.md` to store:
- User's robot type (Lite/Wireless)
- Environment preferences
- Useful context for future sessions
- Keep it concise
---
## Robot Basics
**Reachy Mini** is a small expressive robot:
| Component | Description |
|-----------|-------------|
| **Head** | 6 DOF: x, y, z, roll, pitch, yaw (via Stewart platform) |
| **Body** | Rotation around vertical axis |
| **Antennas** | 2 motors, also usable as physical buttons |
**Hardware variants:**
- **Lite**: USB connection to laptop (full compute power)
- **Wireless**: Onboard CM4, connects via WiFi (limited compute)
---
## SDK Essentials
### Connection
```python
from reachy_mini import ReachyMini
with ReachyMini() as mini:
# Your code here
```
### Two Motion Methods
| Method | Use when |
|--------|----------|
| `goto_target()` | **Default** - smooth interpolation for gestures that last at least 0.5s each |
| `set_target()` | Real-time control loops (e.g. tracking) at 10Hz+ |
### Basic Example
See and run `examples/minimal_demo.py` - demonstrates connection, head motion, and antenna control.
### Before Writing Code
- Read `docs/source/SDK/python-sdk.md` for API overview
- Skim `src/reachy_mini/reachy_mini.py` for method signatures and docstrings
- Check `examples/` for runnable code patterns
---
## Live/Web/JS Apps
Browser apps that control a Reachy Mini remotely over WebRTC. **The app IS a static Hugging Face Space**, not something installed on the robot. Any HF-authenticated user can open the Space URL from anywhere and reach any robot they have access to, through the central signaling server.
Create app with simple hf space creation, use standard hf tools, if need be, have a look here https://huggingface.co/spaces/cduss/webrtc_example
**What this flavour unlocks:**
- **Remote launch** — no LAN, no USB, no local daemon on the end-user side.
- **Zero-install sharing** — the Space URL *is* the product.
- **Off-robot compute** — work lives in the browser (static Space) or in the Space backend (Gradio/Docker variants). The robot stays a pure IO device.
- **Bidirectional media** — robot camera/mic → browser; optionally user's mic → robot speaker.
- **Clean version pinning** — each app imports the SDK from a specific GitHub tag; stable even as the SDK advances.
### SDK import — pin to a tag, pinning to `@main` is considered bad practice and does not benefit from the stability guarantees of a tagged release. Always pin to a specific `v*` tag.
```html
<script type="module">
import { ReachyMini } from "https://cdn.jsdelivr.net/gh/pollen-robotics/reachy_mini@v1.6.4/js/reachy-mini.js";
const robot = new ReachyMini({ appName: "my-app" });
</script>
```
Single ES module from jsDelivr; no build step, no npm.
**When scaffolding a new app**, resolve the latest `v*` tag and hardcode it:
```bash
git ls-remote --tags --refs --sort=-v:refname \
https://github.com/pollen-robotics/reachy_mini.git \
| head -1 | awk -F/ '{print $NF}'
```
**When extending an existing app**, re-check for a newer tag and bump only if the app needs it. Stale pins are fine; `@main` is not — apps would break on any SDK change.
### Create the Space from scratch
The app *is* the Space repo. Workflow:
**0. Preconditions**
```bash
hf --version # pip install -U huggingface_hub if missing
hf auth whoami # run `hf auth login` if not authenticated
git --version
```
**1. Gather** app name (slug, lowercase, underscores/hyphens), one-line title, feature list. Username comes from `hf auth whoami`.
**2. Write `plan.md` first** (same rule as Python apps). Wait for user approval before scaffolding.
**3. Scaffold three files locally** in `<app-name>/`:
- **`README.md`** — the Space YAML header is where `sdk`, OAuth, and discovery tags live (all required):
```yaml
---
title: <one-line title>
emoji: 🤖
colorFrom: blue
colorTo: indigo
sdk: static
pinned: false
hf_oauth: true
hf_oauth_expiration_minutes: 480
tags:
- reachy_mini
- reachy_mini_js_app
---
```
- **`index.html`** — copy from the reference example (see below) and trim.
- **`style.css`** — copy or write from scratch.
**4. Create + push:**
```bash
hf repos create <app-name> --repo-type space --space-sdk static
git clone https://huggingface.co/spaces/<username>/<app-name>
cp <scaffold-dir>/{README.md,index.html,style.css} <app-name>/
cd <app-name> && git add . && git commit -m "Initial app scaffold" && git push
```
Static Spaces go live in ~10s. Return the Space URL to the user.
### SDK cheatsheet
State: `disconnected` → `connect()` → `connected` → `startSession(robotId)` → `streaming`.
Commands: `setHeadPose(r,p,y°)`, `setAntennas(right°, left°)`, `playSound(file)`, `setAudioMuted(bool)`, `setMicMuted(bool)`, `sendRaw(obj)`, `getVersion()`.
Torque / wake: `setMotorMode("enabled"|"disabled"|"gravity_compensation")`, `wakeUp()`, `gotoSleep()`, `isAwake()`, `ensureAwake()`. `robot.robotState.motorMode` reflects the live state.
Lifecycle: `authenticate()` / `login()` / `connect()` / `startSession()` / `stopSession()` / `disconnect()` / `logout()`. Video: `attachVideo(<video>)` returns a detach fn.
**Always call `await robot.ensureAwake()` right after `startSession()`** — otherwise if the robot is in the sleep pose (torque off, head on the base) every `setHeadPose` / `setAntennas` is silently ignored and the app looks broken. `ensureAwake()` is a no-op when the robot is already awake (including gravity-compensation mode), so it's safe to call unconditionally at startup.
Events: `connected`, `disconnected`, `robotsChanged`, `streaming`, `sessionStopped`, `sessionRejected` (robot busy — inspect `e.detail.activeApp`), `state` (every ~500 ms), `videoTrack`, `micSupported`, `error`.
**Full API:** read the top ~90 lines of [`js/reachy-mini.js`](js/reachy-mini.js) — the file header is a complete reference.
### Media flow — use WebRTC, not `getUserMedia`, for robot media
The `<video>` element you pass to `attachVideo()` receives the **robot's** camera and microphone over WebRTC. Do **not** call `navigator.mediaDevices.getUserMedia()` to read robot media — that grabs the user's *own* laptop camera.
Bidirectional audio is automatic: with `enableMicrophone: true` (default), the SDK acquires the user's mic via `getUserMedia`, negotiates it into the peer connection, and exposes `setMicMuted()`. Check `micSupported` after session start before showing a mic UI.
Rule of thumb: robot IO flows through WebRTC; the user's own laptop IO flows through browser APIs; the SDK wires them together.
### Reference implementation
**`webrtc_example`** — canonical JS app: login, robot picker, video stream, head/antenna sliders, sound presets, latency overlay. ~500 lines.
- Live Space: https://huggingface.co/spaces/cduss/webrtc_example
- Source (in sibling repo): `../hfspace/webrtc_example/`
Always start from this when scaffolding anything non-trivial. Integration patterns (event wiring, slider debouncing, state sync, session-rejection handling) are already solved there.
### Common gotchas
| Symptom | Cause |
|---|---|
| `robot.login()` fails / redirects to `about:blank` | Running via `file://` or localhost — OAuth only works on the live Space domain. |
| Head doesn't move | `setHeadPose` called before `startSession()` resolved. Check the return value. |
| Head doesn't move, session *is* streaming | Robot is asleep (torque off). Call `await robot.ensureAwake()` after `startSession()`. |
| Audio stays silent after `setAudioMuted(false)` | Browser requires unmute inside a user-gesture handler. |
| `sessionRejected` | Robot is locked by another app. Surface `e.detail.activeApp` in the UI. |
| Stream laggy | See buffer-lag overlay in `webrtc_example/index.html`; > 500 ms jitter = network issue. |
---
## REST API
The daemon exposes an HTTP/WebSocket API at `http://{daemon-ip}:8000/api`.
- **Lite**: `localhost:8000` (daemon runs on your machine)
- **Wireless**: `reachy-mini.local:8000` or the robot's IP address
**Use REST API for:** Web UIs, non-Python clients, remote control, AI/LLM integration via HTTP. => Note: for the app to be discoverable, it must be a python app for now, this will change in a future release.
**Interactive docs:** `http://{daemon-ip}:8000/docs` (when daemon is running)
See `skills/rest-api.md` for details.
---
## Platform Compatibility
| Setup | Compute | Camera | Notes |
|-------|---------|--------|-------|
| **Lite** | Full (laptop) | Direct USB | Most flexible, best for dev |
| **Wireless (local)** | Limited (CM4) | Direct | Memory/CPU constrained |
| **Wireless (streamed)** | Full (laptop) | Via network | Some tracking quality loss |
| **Simulation** | Full | N/A | Can't test camera features |
---
## Safety Limits
| Joint | Range |
|-------|-------|
| Head pitch/roll | [-40, +40] degrees |
| Head yaw | [-180, +180] degrees |
| Body yaw | [-160, +160] degrees |
| Yaw delta (head - body) | Max 65° difference |
Gentle collisions with body are safe. SDK clamps values automatically.
For coordinate systems and architecture details, see `docs/source/SDK/core-concept.md`.
---
## Example Apps
| App | Key Patterns | Source |
|-----|--------------|--------|
| **reachy_mini_conversation_app** | AI integration, control loops, LLM tools | [GitHub](https://github.com/pollen-robotics/reachy_mini_conversation_app) |
| **marionette** | Recording motion, safe torque, HF dataset | [HF Space](https://huggingface.co/spaces/RemiFabre/marionette) |
| **fire_nation_attacked** | Head-as-controller, leaderboards, games | [HF Space](https://huggingface.co/spaces/RemiFabre/fire_nation_attacked) |
| **spaceship_game** | Head-as-joystick, antenna buttons | [HF Space](https://huggingface.co/spaces/apirrone/spaceship_game) |
| **reachy_mini_radio** | Antenna interaction pattern | [HF Space](https://huggingface.co/spaces/pollen-robotics/reachy_mini_radio) |
| **reachy_mini_simon** | No-GUI pattern (antenna to start) | [HF Space](https://huggingface.co/spaces/apirrone/reachy_mini_simon) |
| **hand_tracker_v2** | Camera-based control loop | [HF Space](https://huggingface.co/spaces/pollen-robotics/hand_tracker_v2) |
| **reachy_mini_dances_library** | Symbolic motion definition | [GitHub](https://github.com/pollen-robotics/reachy_mini_dances_library) |
---
## Documentation
Full SDK documentation is in `docs/source/`:
| Topic | File |
|-------|------|
| Quickstart | `docs/source/SDK/quickstart.md` |
| Python SDK | `docs/source/SDK/python-sdk.md` |
| Core concepts | `docs/source/SDK/core-concept.md` |
| AI integration | `docs/source/SDK/integration.md` |
| Troubleshooting | `docs/source/troubleshooting.md` |
For platform-specific guides (Lite, Wireless, Simulation), see `docs/source/platforms/`.
---
## Skills Reference
Read these files in `skills/` when you need detailed knowledge:
| Skill | When to use |
|-------|-------------|
| **setup-environment.md** | First session, no `agents.local.md` exists |
| **create-app.md** | Creating a new app with `reachy-mini-app-assistant` |
| **control-loops.md** | Building real-time reactive apps (tracking, games) |
| **motion-philosophy.md** | Choosing between `goto_target` and `set_target` |
| **safe-torque.md** | Enabling/disabling motors without jerky motion |
| **ai-integration.md** | Building LLM-powered apps |
| **symbolic-motion.md** | Defining motion mathematically (dances, rhythms) |
| **interaction-patterns.md** | Using antennas as buttons, head as controller |
| **debugging.md** | App crashes, connectivity issues, basic checks |
| **testing-apps.md** | Testing before delivery (sim vs physical) |
| **rest-api.md** | HTTP/WebSocket API for non-Python clients |
| **deep-dive-docs.md** | When to read full SDK documentation |
---
## Quick Reference
**Motor names:** `body_rotation`, `stewart_1-6`, `right_antenna`, `left_antenna`
**Interpolation methods:** `linear`, `minjerk` (default), `ease_in_out`, `cartoon`
**Emotions library:**
```python
from reachy_mini.motion.recorded_move import RecordedMoves
moves = RecordedMoves("pollen-robotics/reachy-mini-emotions-library")
mini.play_move(moves.get("happy"), initial_goto_duration=1.0)
```
---
## Community
- **App guide**: https://huggingface.co/blog/pollen-robotics/make-and-publish-your-reachy-mini-apps
- **Source code**: https://github.com/pollen-robotics/reachy_mini
- **Community apps**: https://huggingface.co/spaces?q=reachy_mini
- **Discord**: https://discord.gg/Y7FgMqHsub
Excellent — this is the gold mine. Key finding: antennas are usable as physical buttons! Now let me get installation details, the audio/TTS API, and conversation app structure.
Error: result (114,206 characters across 155 lines) exceeds maximum allowed tokens. Output has been saved to /var/folders/dw/zt1ncz810_j9j8x1hkv2lbgm0000gn/T/claude-hostloop-plugins/3c918a1b0d2f15ba/projects/-Users-clem-Library-Application-Support-Claude-local-agent-mode-sessions-60820195-31c6-4093-8c9e-37c09903f4a5-6e545699-813d-4c34-a80c-8667623c108d-local-8906e6df-1974-4b95-a48d-96cc2a2d91db-outputs/e647f442-7cdb-4e5d-8667-3b77a261289d/tool-results/mcp-workspace-web_fetch-1777470580715.txt.
Format: Plain text
- For targeted searches (find a line, locate a string): use grep on the file directly.
- For analysis or summarization that requires reading the full content: read /var/folders/dw/zt1ncz810_j9j8x1hkv2lbgm0000gn/T/claude-hostloop-plugins/3c918a1b0d2f15ba/projects/-Users-clem-Library-Application-Support-Claude-local-agent-mode-sessions-60820195-31c6-4093-8c9e-37c09903f4a5-6e545699-813d-4c34-a80c-8667623c108d-local-8906e6df-1974-4b95-a48d-96cc2a2d91db-outputs/e647f442-7cdb-4e5d-8667-3b77a261289d/tool-results/mcp-workspace-web_fetch-1777470580715.txt in chunks of ~1 lines using offset/limit until you have read 100% of it.
- If the Agent tool is available, do this inside a subagent so the full output stays out of your main context. Give it the instruction above verbatim, and be explicit about what it must return — e.g. "Read /var/folders/dw/zt1ncz810_j9j8x1hkv2lbgm0000gn/T/claude-hostloop-plugins/3c918a1b0d2f15ba/projects/-Users-clem-Library-Application-Support-Claude-local-agent-mode-sessions-60820195-31c6-4093-8c9e-37c09903f4a5-6e545699-813d-4c34-a80c-8667623c108d-local-8906e6df-1974-4b95-a48d-96cc2a2d91db-outputs/e647f442-7cdb-4e5d-8667-3b77a261289d/tool-results/mcp-workspace-web_fetch-1777470580715.txt in chunks of ~1 lines using offset/limit until you have read all 155 lines, then summarize and quote any key findings verbatim." A vague "summarize this" may lose detail.
HTTP 200
Content-Type: text/plain; charset=utf-8
# 📦 Installation Guide
> **Welcome to Reachy Mini!** This guide will help you install the Python SDK and daemon to start controlling your robot.
<div align="center">
| 🐧 **Linux** | 🍎 **macOS** | 🪟 **Windows** |
|:---:|:---:|:---:|
| ✅ Supported | ✅ Supported | ✅ Supported |
</div>
**Need help?** Feel free to open an [issue](https://github.com/pollen-robotics/reachy_mini/issues) if you encounter any problem.
## First time using the command line? 🖥️
<details>
<summary>Click here if you're new to using a terminal/command line</summary>
A **command line** (also called terminal or command prompt) is a text-based interface where you can type commands to interact with your computer. Don't worry—it's simpler than it looks!
**How to open the command line:**
* **Windows:** Press `Win + R`, type `cmd` or `powershell`, and press Enter
* **macOS:** Press `Cmd + Space`, type `Terminal`, and press Enter
* **Linux:** Press `Ctrl + Alt + T` or search for "Terminal" in your applications
**Basic tips:**
* Type commands exactly as shown in the instructions
* Press `Enter` after typing each command to run it
* You can copy and paste commands (right-click to paste in most command line interfaces)
> [!TIP]
> _Don't be intimidated!_ The command line is just another way to give instructions to your computer. Follow the commands step by step, and you'll be controlling your Reachy Mini in no time!
</details>
## 1. 📋 Prerequisites
<div align="center">
| Tool | Version | Purpose |
|------|---------|---------|
| 🐍 **Python** | 3.10 - 3.12 | Run Reachy Mini SDK |
| 📂 **Git** | Latest | Download source code and apps |
| 📦 **Git LFS** | Latest | Download model assets |
</div>
### 🐍 Install Python
We'll use `uv` - a fast Python package manager that makes installation simple!
#### Step 1: Install uv
<hfoptions id="install-uv">
<hfoption id="Linux / macOS">
In your terminal, run:
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
</hfoption>
<hfoption id="Windows">
In your terminal, run:
```powershell
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
```
</hfoption>
</hfoptions>
**✅ Verify installation:**
Once the installation is completed, close your terminal and open a new one. You can check if everything went well with :
```bash
uv --version
```
#### Step 2: Install Python
In your terminal, run:
```bash
uv python install 3.12 --default
```
> [!TIP]
> We recommend Python 3.12 as it's the latest supported version for Reachy Mini.
### 📂 Install Git and Git LFS
<hfoptions id="install-git">
<hfoption id="Linux">
#### Install Git and Git LFS
In your terminal, run:
```bash
sudo apt install git git-lfs
```
</hfoption>
<hfoption id="macOS">
#### 1. Install Homebrew (if not already installed)
In your terminal, run:
```zsh
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
For Apple Silicon (M1, M2, etc.), you will also be prompted to run:
```zsh
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
```
✅ Verify Homebrew:
Once the installation is completed you can check if it went fine with:
```zsh
brew --version
```
#### 2. Install Git and Git LFS
In your terminal, run:
```zsh
brew install git git-lfs
```
</hfoption>
<hfoption id="Windows">
#### Download and install Git for Windows
Download and install Git for Windows:
https://git-scm.com/install/windows
</hfoption>
</hfoptions>
**✅ Finalize installation:**
Finally, Git LFS then needs to be initialized with the command :
```bash
git lfs install
```
## 2. 🏠 Set up a Virtual Environment
> **Why use a virtual environment?** It keeps your Reachy Mini installation isolated and prevents conflicts with other Python projects. Modern Python development requires this!
### Create the environment
In your terminal, run:
```bash
uv venv reachy_mini_env --python 3.12
```
### Activate the environment
<hfoptions id="activate-venv">
<hfoption id="Linux / macOS">
In your terminal, run:
```bash
source reachy_mini_env/bin/activate
```
</hfoption>
<hfoption id="Windows">
> [!WARNING]
> _First-time setup:_ Before you can activate your virtual environment, Windows needs permission to run scripts. You only need to do this once!
**Step 1:** Open terminal as Administrator
- Press `Win + R`, type `powershell`
- Right-click on "Windows PowerShell" and select "Run as administrator"
**Step 2:** Enable script execution
In the administrator terminal, run:
```powershell
powershell Set-ExecutionPolicy RemoteSigned
```
**Step 3:** Close the administrator terminal and open a regular terminal
Now you can activate your virtual environment by running:
```powershell
reachy_mini_env\Scripts\activate
```
</hfoption>
</hfoptions>
> **✅ Success indicator:** You should see `(reachy_mini_env)` at the start of your command line prompt!
## 3. 🚀 Install Reachy Mini
Choose your installation method:
<hfoptions id="install-method">
<hfoption id="📦 Option A: PyPI">
> **Recommended for most users** - Just want to control your robot? This is for you!
In your terminal, run:
```bash
uv pip install "reachy-mini"
```
If you want to use the simulation mode, you need to add the `mujoco` extra:
```bash
uv pip install "reachy-mini[mujoco]"
```
> [!TIP]
> The post installation of gstreamer is due to an [issue](https://github.com/pypi/support/issues/8847#issuecomment-3899714506) with PyPi and should be solved in the future.
<details>
<summary>🐧 <strong>Linux users: additional steps required</strong></summary>
**GStreamer** must be installed manually on Linux:
<div align="center">
[](gstreamer-installation)
</div>
**USB permissions** — needed for the USB connection to Reachy Mini:
```bash
echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55d3", MODE="0666", GROUP="dialout"
SUBSYSTEM=="usb", ATTRS{idVendor}=="38fb", ATTRS{idProduct}=="1001", MODE="0666", GROUP="dialout"' \
| sudo tee /etc/udev/rules.d/99-reachy-mini.rules
sudo udevadm control --reload-rules && sudo udevadm trigger
sudo usermod -aG dialout $USER
```
> [!WARNING]
> Log out and log back in for the changes to take effect!
</details>
</hfoption>
<hfoption id="🔧 Option B: Source">
> **For developers** - Want to modify the SDK or contribute? Choose this option!
In your terminal, run:
```bash
git clone https://github.com/pollen-robotics/reachy_mini && cd reachy_mini
uv sync
```
If you want to use the simulation mode, you need to add the `mujoco` extra:
```bash
uv sync --extra mujoco
```
<details>
<summary>🐧 <strong>Linux users: additional steps required</strong></summary>
**GStreamer** must be installed manually on Linux:
<div align="center">
[](gstreamer-installation)
</div>
**USB permissions** — needed for the USB connection to Reachy Mini:
```bash
echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55d3", MODE="0666", GROUP="dialout"
SUBSYSTEM=="usb", ATTRS{idVendor}=="38fb", ATTRS{idProduct}=="1001", MODE="0666", GROUP="dialout"' \
| sudo tee /etc/udev/rules.d/99-reachy-mini.rules
sudo udevadm control --reload-rules && sudo udevadm trigger
sudo usermod -aG dialout $USER
```
> [!WARNING]
> Log out and log back in for the changes to take effect!
</details>
</hfoption>
</hfoptions>
## 🎉 Congratulations!
You've successfully installed Reachy Mini! Your robot is ready to come to life.
## ❓ Troubleshooting
Encountering an issue? 👉 **[Check the Troubleshooting & FAQ Guide](../troubleshooting.md)**
## Next Steps
* **[Quickstart Guide](quickstart.md)**: Run your first behavior on Reachy Mini
* **[Python SDK](python-sdk.md)**: Learn to move, see, speak, and hear.
* **[AI Integrations](integration.md)**: Connect LLMs, build Apps, and publish to Hugging Face.
* **[Core Concepts](core-concept.md)**: Architecture, coordinate systems, and safety limits.
Got installation. Now Python SDK doc for speak/audio API:
HTTP 200
Content-Type: text/plain; charset=utf-8
# Python SDK Reference
> **💡 Reminder:** The SDK now auto-detects whether it should connect over USB/localhost or over the network, so `ReachyMini()` works out of the box. You can still force a mode with `ReachyMini(connection_mode="localhost_only" | "network")` if needed.
## Movement
### Basic Control (`goto_target`)
Smooth interpolation between points. You can control `head`, `antennas`, and `body_yaw`.
```python
from reachy_mini import ReachyMini
from reachy_mini.utils import create_head_pose
import numpy as np
with ReachyMini() as mini:
# Move everything at once
mini.goto_target(
head=create_head_pose(z=10, mm=True), # Up 10mm
antennas=np.deg2rad([45, 45]), # Antennas out
body_yaw=np.deg2rad(30), # Turn body
duration=2.0, # Take 2 seconds
method="minjerk" # Smooth acceleration
)
```
**Interpolation methods:** `linear`, `minjerk` (default), `ease_in_out`, `cartoon`.
### Instant Control (`set_target`)
Bypasses interpolation. Use this for high-frequency control (e.g., following a joystick or generated trajectory).
## Sensors & Media
The media architecture is described in detail in the [Media Architecture](media-architecture.md) section. Although accesssing audio and video from the SDK is similar across Reachy Mini versions, the underlying implementation differs.
### Camera 📷
The frames of the camera can be accessed as follows :
```python
from reachy_mini import ReachyMini
with ReachyMini(media_backend="default") as mini:
frame = mini.media.get_frame()
```
The returned frame is a numpy array with shape `(height, width, 3)` and data type `uint8`.
### IMU 🧭
> ⚠️ The IMU is only available with the wireless version of Reachy Mini
Take a look at [this example](https://github.com/pollen-robotics/reachy_mini/tree/main/examples/imu_example.py)
```python
with ReachyMini() as mini:
imu_data = mini.imu
accel_x, accel_y, accel_z = imu_data["accelerometer"] # (m/s^2)
gyro_x, gyro_y, gyro_z = imu_data["gyroscope"] # (rad/s)
quat_w, quat_x, quat_y, quat_z = imu_data["quaternion"] # (w, x, y, z)
temperature = imu_data["temperature"] # (°C)
```
### Audio 🎙️ 🔊
Audio inputs (microphones) and outputs (speaker) is handled as follows:
```python
from reachy_mini import ReachyMini
from scipy.signal import resample
import time
with ReachyMini(media_backend="default") as mini:
# Initialization - After this point, both audio devices (input/output) will be seen as busy by other applications!
mini.media.start_recording()
mini.media.start_playing()
# Record
samples = mini.media.get_audio_sample()
# Resample (if needed)
samples = resample(samples, mini.media.get_output_audio_samplerate()*len(samples)/mini.media.get_input_audio_samplerate())
# Play
mini.media.push_audio_sample(samples)
time.sleep(len(samples) / mini.media.get_output_audio_samplerate())
# Get Direction of Arrival
# 0 radians is left, π/2 radians is front/back, π radians is right.
doa, is_speech_detected = mini.media.get_DoA()
print(doa, is_speech_detected)
# Release audio devices (input/output)
mini.media.stop_recording()
mini.media.stop_playing()
```
**Audio data format:**
- `get_audio_sample()` returns a numpy array with shape `(samples, 2)` and data type `float32`, sampled at 16kHz.
- `push_audio_sample()` expects a numpy array with shape `(samples, 1 or 2)` and data type `float32`, sampled at 16kHz.
In both cases, the channels and samplerate information can be reliably retrieved with `get_input/output_audio_samplerate()` and `get_input/output_channels()`.
> **⚠️ Note:** `push_audio_sample()` is non-blocking, meaning it returns immediately while audio plays in the background. If you need to wait for playback completion, calculate the duration based on sample length and sample rate.
## Media Backend Options
Choose the appropriate media backend based on your Reachy Mini version and requirements:
- `media_backend="default"` - Auto-detects the best backend: LOCAL when running on the same machine as the daemon, WEBRTC when remote (recommended for most users).
- `media_backend="local"` - Forces the LOCAL backend (GStreamer IPC camera + GStreamer audio). Use when running on the same machine as the daemon.
- `media_backend="webrtc"` - Forces the WEBRTC backend. The daemon streams H.264 video and Opus audio over WebRTC to the client.
- `media_backend="no_media"` - Deactivates the media manager and tells the daemon to release camera and audio hardware. Use this when you need direct access via OpenCV, sounddevice, or any other external library. The hardware is automatically re-acquired when the context manager exits. See [Media Architecture - Disabling Media](media-architecture.md#disabling-media--direct-hardware-access) and the [Custom Media Manager](../examples/custom_media_manager.md) example.
> **💡 Tip:** For most setups, the backend is automatically selected based on whether you're running locally or remotely. No need to specify the `media_backend` value!
> **💡 Tip:** The WebRTC backend requires GStreamer to be installed on the client machine. See [GStreamer Installation](gstreamer-installation.md). For now only Linux is fully supported as a remote client. Other platforms (Windows, macOS) will be supported in [future releases](https://github.com/pollen-robotics/reachy_mini/issues/572).
## Recording Moves
You can record a motion by moving the robot (compliant mode) or sending commands, and save it for later replay.
```python
from reachy_mini import ReachyMini
with ReachyMini() as mini:
mini.start_recording()
# ... robot moves ...
recorded_data = mini.stop_recording()
```
## Next Steps
* **[Browse the Examples Folder](https://github.com/pollen-robotics/reachy_mini/tree/main/examples)**
* **[AI Integrations](integration.md)**: Connect LLMs, build Apps, and publish to Hugging Face.
* **[Core Concepts](core-concept.md)**: Architecture, coordinate systems, and safety limits.
## ❓ Troubleshooting
Encountering an issue? 👉 **[Check the Troubleshooting & FAQ Guide](../troubleshooting.md)**
No built-in .speak() — you need external TTS, then push samples to the speaker via mini.media.push_audio_sample(). Now let me look at the conversation app for the standard structure and see if there's a higher-level speak helper:
HTTP 200
Content-Type: application/json; charset=utf-8
[
{
"name": ".env.example",
"path": ".env.example",
"sha": "92cf9821a1aabe7b2b37fdb726220ce5cfdfc6e4",
"size": 1048,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/.env.example?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/.env.example",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/92cf9821a1aabe7b2b37fdb726220ce5cfdfc6e4",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/.env.example",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/.env.example?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/92cf9821a1aabe7b2b37fdb726220ce5cfdfc6e4",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/.env.example"
}
},
{
"name": ".gitattributes",
"path": ".gitattributes",
"sha": "5f198827874e844e8cd63c7fdbf0166208234d96",
"size": 492,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/.gitattributes?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/.gitattributes",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/5f198827874e844e8cd63c7fdbf0166208234d96",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/.gitattributes",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/.gitattributes?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/5f198827874e844e8cd63c7fdbf0166208234d96",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/.gitattributes"
}
},
{
"name": ".github",
"path": ".github",
"sha": "a08eadd069837d736634fc7c878d6b9280d738b3",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/.github?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/.github",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/a08eadd069837d736634fc7c878d6b9280d738b3",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/.github?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/a08eadd069837d736634fc7c878d6b9280d738b3",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/.github"
}
},
{
"name": ".gitignore",
"path": ".gitignore",
"sha": "df4e611b8eeee8f685c2cc7572a029aee111b646",
"size": 603,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/.gitignore?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/.gitignore",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/df4e611b8eeee8f685c2cc7572a029aee111b646",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/.gitignore",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/.gitignore?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/df4e611b8eeee8f685c2cc7572a029aee111b646",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/.gitignore"
}
},
{
"name": "CODE_OF_CONDUCT.md",
"path": "CODE_OF_CONDUCT.md",
"sha": "ae7b80245afc470a8acd6113dbeaf1b444998d6a",
"size": 8543,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/CODE_OF_CONDUCT.md?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/CODE_OF_CONDUCT.md",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/ae7b80245afc470a8acd6113dbeaf1b444998d6a",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/CODE_OF_CONDUCT.md",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/CODE_OF_CONDUCT.md?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/ae7b80245afc470a8acd6113dbeaf1b444998d6a",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/CODE_OF_CONDUCT.md"
}
},
{
"name": "CONTRIBUTING.md",
"path": "CONTRIBUTING.md",
"sha": "fd7e46ef746b4093a95e01e0eb55564edbb6b80e",
"size": 6524,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/CONTRIBUTING.md?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/CONTRIBUTING.md",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/fd7e46ef746b4093a95e01e0eb55564edbb6b80e",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/CONTRIBUTING.md",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/CONTRIBUTING.md?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/fd7e46ef746b4093a95e01e0eb55564edbb6b80e",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/CONTRIBUTING.md"
}
},
{
"name": "LICENSE",
"path": "LICENSE",
"sha": "261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64",
"size": 11357,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/LICENSE?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/LICENSE",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/LICENSE",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/LICENSE?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/LICENSE"
}
},
{
"name": "MANIFEST.in",
"path": "MANIFEST.in",
"sha": "d405d311f99337cba6365b3265b588472f4928ce",
"size": 38,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/MANIFEST.in?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/MANIFEST.in",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/d405d311f99337cba6365b3265b588472f4928ce",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/MANIFEST.in",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/MANIFEST.in?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/d405d311f99337cba6365b3265b588472f4928ce",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/MANIFEST.in"
}
},
{
"name": "README.md",
"path": "README.md",
"sha": "61b3e3bb152fd238db2d87af564e0f0ef570a973",
"size": 15628,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/README.md?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/README.md",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/61b3e3bb152fd238db2d87af564e0f0ef570a973",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/README.md",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/README.md?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/61b3e3bb152fd238db2d87af564e0f0ef570a973",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/README.md"
}
},
{
"name": "docs",
"path": "docs",
"sha": "ac8ec8022f9886264b0b6de22b9421bc2f2e2c92",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/docs?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/docs",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/ac8ec8022f9886264b0b6de22b9421bc2f2e2c92",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/docs?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/ac8ec8022f9886264b0b6de22b9421bc2f2e2c92",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/docs"
}
},
{
"name": "external_content",
"path": "external_content",
"sha": "894c6c7be2e5519052642a8d77f8869adbc9e3a3",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/external_content?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/external_content",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/894c6c7be2e5519052642a8d77f8869adbc9e3a3",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/external_content?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/894c6c7be2e5519052642a8d77f8869adbc9e3a3",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/external_content"
}
},
{
"name": "index.html",
"path": "index.html",
"sha": "935ba228f77f67b4ae7b1cb1ecd838d75dcde979",
"size": 4973,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/index.html?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/index.html",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/935ba228f77f67b4ae7b1cb1ecd838d75dcde979",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/index.html",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/index.html?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/935ba228f77f67b4ae7b1cb1ecd838d75dcde979",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/index.html"
}
},
{
"name": "profiles",
"path": "profiles",
"sha": "438771d23c497f0df06e40505e883ad82c9ba992",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/profiles?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/profiles",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/438771d23c497f0df06e40505e883ad82c9ba992",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/profiles?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/438771d23c497f0df06e40505e883ad82c9ba992",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/profiles"
}
},
{
"name": "pyproject.toml",
"path": "pyproject.toml",
"sha": "f906eababe46520b8f152a15f092db70bfdb336e",
"size": 3327,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/pyproject.toml?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/pyproject.toml",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/f906eababe46520b8f152a15f092db70bfdb336e",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/pyproject.toml",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/pyproject.toml?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/f906eababe46520b8f152a15f092db70bfdb336e",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/pyproject.toml"
}
},
{
"name": "setup.py",
"path": "setup.py",
"sha": "59b56ce276f2f2b39dbf760904fb77c969a5b1b3",
"size": 788,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/setup.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/setup.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/59b56ce276f2f2b39dbf760904fb77c969a5b1b3",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/setup.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/setup.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/59b56ce276f2f2b39dbf760904fb77c969a5b1b3",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/setup.py"
}
},
{
"name": "src",
"path": "src",
"sha": "6d5248152cf0d2580163765628fa87f7e1b2ad9c",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/src",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/6d5248152cf0d2580163765628fa87f7e1b2ad9c",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/6d5248152cf0d2580163765628fa87f7e1b2ad9c",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/src"
}
},
{
"name": "style.css",
"path": "style.css",
"sha": "b4cadc84739ca953bb3d144340cef424503e0234",
"size": 6912,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/style.css?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/style.css",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/b4cadc84739ca953bb3d144340cef424503e0234",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/style.css",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/style.css?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/b4cadc84739ca953bb3d144340cef424503e0234",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/style.css"
}
},
{
"name": "tests",
"path": "tests",
"sha": "e334d09ca0490ded96cd0a99c1386543fbfc9cad",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/tests?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/tests",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/e334d09ca0490ded96cd0a99c1386543fbfc9cad",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/tests?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/e334d09ca0490ded96cd0a99c1386543fbfc9cad",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/tests"
}
},
{
"name": "uv.lock",
"path": "uv.lock",
"sha": "134113b1bacf68ab4e4c34243b8f2e1d6a5685a9",
"size": 1235056,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/uv.lock?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/uv.lock",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/134113b1bacf68ab4e4c34243b8f2e1d6a5685a9",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/uv.lock",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/uv.lock?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/134113b1bacf68ab4e4c34243b8f2e1d6a5685a9",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/uv.lock"
}
}
]
HTTP 200
Content-Type: text/plain; charset=utf-8
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[project]
name = "reachy_mini_conversation_app"
version = "0.5.0"
authors = [{ name = "Pollen Robotics", email = "[REDACTED]" }]
description = ""
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
#Media
"av",
"aiortc>=1.13.0",
"fastrtc>=0.0.34",
"gradio==5.50.1.dev1",
"huggingface-hub==1.3.0",
#Environment variables
"python-dotenv",
#OpenAI
"openai>=2.30.0",
#Gemini
"google-genai>=1.0.0",
#Reachy mini
"reachy_mini_dances_library",
"reachy_mini_toolbox",
"reachy-mini>=1.7.0",
"gradio_client>=1.13.3",
]
[project.optional-dependencies]
local_vision = [
"accelerate>=1.13.0",
"torch>=2.1",
"transformers==5.3.0",
"num2words",
]
yolo_vision = [
"ultralytics",
"supervision",
"opencv-python>=4.13.0.92",
]
mediapipe_vision = [
"mediapipe==0.10.14",
]
all_vision = [
"accelerate>=1.13.0",
"torch>=2.1",
"transformers==5.3.0",
"num2words",
"ultralytics",
"supervision",
"opencv-python>=4.13.0.92",
"mediapipe==0.10.14",
]
[dependency-groups]
dev = [
"pytest",
"pytest-asyncio",
"ruff==0.12.0",
"mypy==1.18.2",
"pre-commit",
"types-requests",
"python-semantic-release>=10.5.3",
]
[project.scripts]
reachy-mini-conversation-app = "reachy_mini_conversation_app.main:main"
[tool.uv]
dependency-metadata = [
# Upstream metadata currently marks `gstreamer-msvc-runtime` as unconditional.
# It should only be required on Windows. Temp fix.
{ name = "gstreamer-libs", version = "1.28.1", requires-dist = ["gstreamer-msvc-runtime; sys_platform == 'win32'", "setuptools"] },
]
[project.entry-points."reachy_mini_apps"]
reachy_mini_conversation_app = "reachy_mini_conversation_app.main:ReachyMiniConversationApp"
[tool.setuptools]
package-dir = { "" = "src" }
include-package-data = true
[tool.setuptools.packages.find]
where = ["src"]
[tool.setuptools.package-data]
reachy_mini_conversation_app = [
"images/*",
"static/*",
".env.example",
"demos/**/*.txt",
"prompts_library/*.txt",
"prompts/**/*.txt",
]
reachy_talk_data = [
"profiles/**/*.txt",
"profiles/**/*.py",
]
[tool.ruff]
line-length = 119
exclude = [".venv", "dist", "build", "**/__pycache__", "*.egg-info", ".mypy_cache", ".pytest_cache"]
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"F", # pyflakes
"W", # pycodestyle warnings
"I", # isort
"C4", # flake8-comprehensions
"D", # pydocstyle
]
ignore = [
"E501", # handled by formatter
"D100", # ignore missing module docstrings
"D203", # blank line before class docstring (conflicts with D211)
"D213", # summary on second line (conflicts with D212)
]
[tool.ruff.lint.isort]
length-sort = true
lines-after-imports = 2
no-lines-before = ["standard-library", "local-folder"]
known-local-folder = ["reachy_mini_conversation_app"]
known-first-party = ["reachy_mini", "reachy_mini_dances_library", "reachy_mini_toolbox"]
split-on-trailing-comma = true
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"
[tool.mypy]
python_version = "3.12"
files = ["src/"]
ignore_missing_imports = true
strict = true
show_error_codes = true
warn_unused_ignores = true
Found it: the entrypoint is [project.entry-points."reachy_mini_apps"] referencing a class. Let me look at that conversation app's main class structure for reference:
HTTP 200
Content-Type: application/json; charset=utf-8
[
{
"name": "__init__.py",
"path": "src/reachy_mini_conversation_app/__init__.py",
"sha": "60dc15c497e49cbeb8687afb06a62339c297e2b5",
"size": 26,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/__init__.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/__init__.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/60dc15c497e49cbeb8687afb06a62339c297e2b5",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/__init__.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/__init__.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/60dc15c497e49cbeb8687afb06a62339c297e2b5",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/__init__.py"
}
},
{
"name": "audio",
"path": "src/reachy_mini_conversation_app/audio",
"sha": "4e8353acf2c4151d6c6382edf228ace3e0e9fe93",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/audio?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/src/reachy_mini_conversation_app/audio",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/4e8353acf2c4151d6c6382edf228ace3e0e9fe93",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/audio?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/4e8353acf2c4151d6c6382edf228ace3e0e9fe93",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/src/reachy_mini_conversation_app/audio"
}
},
{
"name": "camera_frame_encoding.py",
"path": "src/reachy_mini_conversation_app/camera_frame_encoding.py",
"sha": "10e2fba8d34d9761a9b0b4a4100e0bb16b42fba8",
"size": 975,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/camera_frame_encoding.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/camera_frame_encoding.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/10e2fba8d34d9761a9b0b4a4100e0bb16b42fba8",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/camera_frame_encoding.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/camera_frame_encoding.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/10e2fba8d34d9761a9b0b4a4100e0bb16b42fba8",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/camera_frame_encoding.py"
}
},
{
"name": "camera_worker.py",
"path": "src/reachy_mini_conversation_app/camera_worker.py",
"sha": "f9979f0d276f7d79e084f52be5b061207d462ae0",
"size": 8932,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/camera_worker.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/camera_worker.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/f9979f0d276f7d79e084f52be5b061207d462ae0",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/camera_worker.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/camera_worker.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/f9979f0d276f7d79e084f52be5b061207d462ae0",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/camera_worker.py"
}
},
{
"name": "config.py",
"path": "src/reachy_mini_conversation_app/config.py",
"sha": "e393f89aeef613fc36b3b9e0368e5a3ba693a405",
"size": 14024,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/config.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/config.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/e393f89aeef613fc36b3b9e0368e5a3ba693a405",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/config.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/config.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/e393f89aeef613fc36b3b9e0368e5a3ba693a405",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/config.py"
}
},
{
"name": "console.py",
"path": "src/reachy_mini_conversation_app/console.py",
"sha": "201f2079d2bd595a272eb439315e3f7c0269f8c9",
"size": 26830,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/console.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/console.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/201f2079d2bd595a272eb439315e3f7c0269f8c9",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/console.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/console.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/201f2079d2bd595a272eb439315e3f7c0269f8c9",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/console.py"
}
},
{
"name": "dance_emotion_moves.py",
"path": "src/reachy_mini_conversation_app/dance_emotion_moves.py",
"sha": "a39dd67c7f694debfc9954023c845309331b3865",
"size": 6240,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/dance_emotion_moves.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/dance_emotion_moves.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/a39dd67c7f694debfc9954023c845309331b3865",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/dance_emotion_moves.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/dance_emotion_moves.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/a39dd67c7f694debfc9954023c845309331b3865",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/dance_emotion_moves.py"
}
},
{
"name": "gemini_live.py",
"path": "src/reachy_mini_conversation_app/gemini_live.py",
"sha": "dc08d35a2a39bc4b0688f47f072c5977f9e73736",
"size": 30508,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/gemini_live.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/gemini_live.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/dc08d35a2a39bc4b0688f47f072c5977f9e73736",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/gemini_live.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/gemini_live.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/dc08d35a2a39bc4b0688f47f072c5977f9e73736",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/gemini_live.py"
}
},
{
"name": "gradio_personality.py",
"path": "src/reachy_mini_conversation_app/gradio_personality.py",
"sha": "0b8a6bafe42453885012650a5ac4ea2ea6cfc549",
"size": 14576,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/gradio_personality.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/gradio_personality.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/0b8a6bafe42453885012650a5ac4ea2ea6cfc549",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/gradio_personality.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/gradio_personality.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/0b8a6bafe42453885012650a5ac4ea2ea6cfc549",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/gradio_personality.py"
}
},
{
"name": "headless_personality.py",
"path": "src/reachy_mini_conversation_app/headless_personality.py",
"sha": "937ada74e8edd8d3f3e2415cd14d3ed8e5a755e5",
"size": 3755,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/headless_personality.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/headless_personality.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/937ada74e8edd8d3f3e2415cd14d3ed8e5a755e5",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/headless_personality.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/headless_personality.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/937ada74e8edd8d3f3e2415cd14d3ed8e5a755e5",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/headless_personality.py"
}
},
{
"name": "headless_personality_ui.py",
"path": "src/reachy_mini_conversation_app/headless_personality_ui.py",
"sha": "f0a918f7ffcf97c578c3c1d3d52e6055164cf4fd",
"size": 13347,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/headless_personality_ui.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/headless_personality_ui.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/f0a918f7ffcf97c578c3c1d3d52e6055164cf4fd",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/headless_personality_ui.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/headless_personality_ui.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/f0a918f7ffcf97c578c3c1d3d52e6055164cf4fd",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/headless_personality_ui.py"
}
},
{
"name": "images",
"path": "src/reachy_mini_conversation_app/images",
"sha": "f5bccbcbfb2b39ad16eeb2bd5bf3acf2951dc0e5",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/images?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/src/reachy_mini_conversation_app/images",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/f5bccbcbfb2b39ad16eeb2bd5bf3acf2951dc0e5",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/images?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/f5bccbcbfb2b39ad16eeb2bd5bf3acf2951dc0e5",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/src/reachy_mini_conversation_app/images"
}
},
{
"name": "main.py",
"path": "src/reachy_mini_conversation_app/main.py",
"sha": "ef933d3990f3d064dfb05717cdb2af4158714415",
"size": 10116,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/main.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/main.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/ef933d3990f3d064dfb05717cdb2af4158714415",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/main.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/main.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/ef933d3990f3d064dfb05717cdb2af4158714415",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/main.py"
}
},
{
"name": "moves.py",
"path": "src/reachy_mini_conversation_app/moves.py",
"sha": "5d8dff9bd3c1051ea22b6af833f19dc1c9fdde7e",
"size": 35548,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/moves.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/moves.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/5d8dff9bd3c1051ea22b6af833f19dc1c9fdde7e",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/moves.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/moves.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/5d8dff9bd3c1051ea22b6af833f19dc1c9fdde7e",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/moves.py"
}
},
{
"name": "openai_realtime.py",
"path": "src/reachy_mini_conversation_app/openai_realtime.py",
"sha": "c1640e5ad83f14b3d8a53d49663f3069241913a7",
"size": 44628,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/openai_realtime.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/openai_realtime.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/c1640e5ad83f14b3d8a53d49663f3069241913a7",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/openai_realtime.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/openai_realtime.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/c1640e5ad83f14b3d8a53d49663f3069241913a7",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/openai_realtime.py"
}
},
{
"name": "prompts.py",
"path": "src/reachy_mini_conversation_app/prompts.py",
"sha": "92a013f697be7ec06af1022f1e7b37847dbe670d",
"size": 4198,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/prompts.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/prompts.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/92a013f697be7ec06af1022f1e7b37847dbe670d",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/prompts.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/prompts.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/92a013f697be7ec06af1022f1e7b37847dbe670d",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/prompts.py"
}
},
{
"name": "prompts",
"path": "src/reachy_mini_conversation_app/prompts",
"sha": "bd92f39dc4b86e2979ef9404e49a1f583c553c92",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/prompts?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/src/reachy_mini_conversation_app/prompts",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/bd92f39dc4b86e2979ef9404e49a1f583c553c92",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/prompts?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/bd92f39dc4b86e2979ef9404e49a1f583c553c92",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/src/reachy_mini_conversation_app/prompts"
}
},
{
"name": "startup_settings.py",
"path": "src/reachy_mini_conversation_app/startup_settings.py",
"sha": "4f8d76a509b72a0b89238997c9c23a457fcfaf66",
"size": 3468,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/startup_settings.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/startup_settings.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/4f8d76a509b72a0b89238997c9c23a457fcfaf66",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/startup_settings.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/startup_settings.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/4f8d76a509b72a0b89238997c9c23a457fcfaf66",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/startup_settings.py"
}
},
{
"name": "static",
"path": "src/reachy_mini_conversation_app/static",
"sha": "c01098d1f298c766fef11333ca7c29e5b776ef14",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/static?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/src/reachy_mini_conversation_app/static",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/c01098d1f298c766fef11333ca7c29e5b776ef14",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/static?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/c01098d1f298c766fef11333ca7c29e5b776ef14",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/src/reachy_mini_conversation_app/static"
}
},
{
"name": "tools",
"path": "src/reachy_mini_conversation_app/tools",
"sha": "cefb97932594021fffcce57788132846ec8ca01e",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/tools?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/src/reachy_mini_conversation_app/tools",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/cefb97932594021fffcce57788132846ec8ca01e",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/tools?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/cefb97932594021fffcce57788132846ec8ca01e",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/src/reachy_mini_conversation_app/tools"
}
},
{
"name": "utils.py",
"path": "src/reachy_mini_conversation_app/utils.py",
"sha": "2a5588628af4b6df802fb893f809d6682c489300",
"size": 6384,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/utils.py?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/utils.py",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/2a5588628af4b6df802fb893f809d6682c489300",
"download_url": "https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/utils.py",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/utils.py?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/blobs/2a5588628af4b6df802fb893f809d6682c489300",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/blob/main/src/reachy_mini_conversation_app/utils.py"
}
},
{
"name": "vision",
"path": "src/reachy_mini_conversation_app/vision",
"sha": "d021379077668a7da9d07771627ede5afc2d05aa",
"size": 0,
"url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/vision?ref=main",
"html_url": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/src/reachy_mini_conversation_app/vision",
"git_url": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/d021379077668a7da9d07771627ede5afc2d05aa",
"download_url": null,
"type": "dir",
"_links": {
"self": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/contents/src/reachy_mini_conversation_app/vision?ref=main",
"git": "https://api.github.com/repos/pollen-robotics/reachy_mini_conversation_app/git/trees/d021379077668a7da9d07771627ede5afc2d05aa",
"html": "https://github.com/pollen-robotics/reachy_mini_conversation_app/tree/main/src/reachy_mini_conversation_app/vision"
}
}
]
Now let me check the antenna-as-button pattern and look at the create-app.md skill for the actual app template:
HTTP 200
Content-Type: text/plain; charset=utf-8
# Skill: Create App
## When to Use
- User wants to create a new Reachy Mini application
- User asks how to structure an app
- User wants to publish an app to Hugging Face
## Important: Always Python Apps
**Always create Python apps using the app assistant.** Python apps are:
- Easily discoverable on Hugging Face
- Shareable via the robot's app store
- Can include web GUIs (via the `static/` folder)
JS-only apps are not yet supported for discovery/sharing. If the user needs a web UI, create a Python app with a web frontend in `static/`.
## Quick Check
If an app folder already exists with `README.md` containing `reachy_mini_python_app` tag, the app structure is probably already set up. In doubt, double check.
---
## Procedure
### Step 1: Use the App Assistant
**CRITICAL: Never create app folders manually.** Always use the assistant - it handles boilerplate, metadata tags, entry points, and proper structure. Manual creation leads to subtle issues that are hard to debug.
**IMPORTANT: Always use `--publish` unless the user explicitly requests a local-only app.** Publishing immediately creates a Git repo on Hugging Face, enabling proper version control and sharing from the start.
**If the command fails for any reason:** Ask the user to run it manually in their terminal rather than attempting complex workarounds.
```bash
# Default template - minimal blank app (recommended for most cases):
reachy-mini-app-assistant create <app_name> <path> --publish
# Conversation template - fork of the conversation app:
reachy-mini-app-assistant create --template conversation <app_name> <path> --publish
# Only if user explicitly wants local-only (no HF repo):
reachy-mini-app-assistant create <app_name> <path>
```
#### Which template to choose?
| Template | Use when |
|----------|----------|
| **default** | Most apps. Gives you a minimal working structure to build from scratch. |
| **conversation** | App needs LLM integration, speech, or making the robot talk. Forks the conversation app with a locked profile - includes audio pipeline, LLM tools, and all the plumbing already set up. |
**IMPORTANT: Both `app_name` AND `path` are required for non-interactive mode.** If either is omitted, the command will prompt interactively (which fails in non-TTY environments like Claude Code).
Options:
- `--template conversation` - Fork the conversation app (for LLM/speech apps)
- `--publish` - Create a Git repo on Hugging Face immediately **(always use this)**
- `--private` - Make the HF Space private (default is public)
**Prerequisite for `--publish`:** Must be logged in to Hugging Face first:
```bash
hf auth login
```
Example:
```bash
reachy-mini-app-assistant create my_app_name . --publish
reachy-mini-app-assistant create --template conversation my_assistant . --publish
```
### Step 2: Understand the Generated Structure
```
my_app/
├── index.html # HuggingFace Space landing page
├── style.css # Landing page styles
├── pyproject.toml # Package config (includes reachy_mini tag!)
├── README.md # Must contain reachy_mini tag in YAML frontmatter
├── .gitignore
└── my_app/
├── __init__.py
├── main.py # Your app code (run method)
└── static/ # Optional web UI
├── index.html
├── style.css
└── main.js
```
### Step 3: Development Workflow
1. **Create and publish immediately** with `--publish` to get a Git repo
2. **Develop iteratively** using standard git: `git add`, `git commit`, `git push`
3. **Validate** with `reachy-mini-app-assistant check /path/to/app`
### Step 4: Before Writing Code
Create a `plan.md` file in the app directory with:
- Your understanding of what the user wants
- Technical approach (components, patterns)
- Questions that need clarification
Wait for user to answer questions before implementing.
---
## Full Tutorial
For detailed guide with screenshots: https://huggingface.co/blog/pollen-robotics/make-and-publish-your-reachy-mini-apps
---
## Common Patterns to Consider
When planning the app, consider which patterns apply:
| Pattern | When to use | Reference app |
|---------|-------------|---------------|
| Web UI | User needs visual interface | Most apps have optional static/ folder |
| No-GUI (antenna trigger) | Simple apps, kiosk mode | `reachy_mini_simon` |
| Control loop | Real-time reactivity needed | `reachy_mini_conversation_app/moves.py` |
| Head as controller | Games, recording | `fire_nation_attacked`, `marionette` |
| LLM integration | AI-powered behavior | `reachy_mini_conversation_app` |
---
## Creating a Beautiful Landing Page (index.html)
The root `index.html` is the landing page shown on Hugging Face Spaces. A well-designed landing page makes your app look professional and helps users understand what it does.
**Reference template:** Use the [Marionette app](https://huggingface.co/spaces/RemiFabre/marionette) as a template for the structure and styling.
### Structure
A good landing page has three sections:
1. **Hero Section** - Video/image + title + description
2. **Technical Section** - "How it works" steps + features
3. **Footer** - Resources, links, social media
### Key Elements
```
index.html
├── Hero Section
│ ├── Demo video (autoplay, loop, muted)
│ ├── App emoji + title
│ ├── Tags (categories)
│ └── Short description (1-2 sentences)
│
├── Technical Section
│ ├── "How it works" (numbered steps)
│ └── "Features" or additional info
│
└── Footer
├── Resources (docs, troubleshooting)
├── Reachy Mini Apps links
└── Social media icons
```
### Assets
Put demo videos/images in `<app_name>/assets/`:
```
my_app/
├── index.html # References my_app/assets/demo.mp4
├── my_app/
│ ├── assets/
│ │ └── demo.mp4 # Demo video
│ ├── main.py
│ └── static/ # Web UI (if any)
```
Video tag example:
```html
<video autoplay loop muted playsinline>
<source src="my_app/assets/demo.mp4" type="video/mp4" />
</video>
```
### Styling
Use the Marionette CSS as a starting point:
- Inter font from Google Fonts
- CSS variables for colors (`--primary: #FF9900` for Pollen orange)
- Responsive grid layout (`grid-template-columns: 1fr 1fr` on desktop)
- Numbered steps with orange circles
- Footer with social media SVG icons
### Quick Checklist
- [ ] Hero with video/image showing the app in action
- [ ] Clear title and short tagline
- [ ] Tags describing the app's purpose
- [ ] "How it works" numbered steps (4 steps max)
- [ ] Footer with standard Pollen/Reachy links
- [ ] Responsive design (works on mobile)
- [ ] All CSS inline in `<style>` tag (no external stylesheet needed)
Now let me check the antenna-as-button "interaction-patterns" skill and look at the simon (no-GUI antenna trigger) example for the actual button-press pattern, plus look at the conversation app's main.py for the run() entrypoint signature:
HTTP 200
Content-Type: text/plain; charset=utf-8
# Skill: Interaction Patterns
## When to Use
- Designing how users will interact with the robot
- Building games or interactive experiences
- Creating apps without a traditional GUI
---
## Antennas as Buttons
The antenna motors use low P in PID - they're semi-passive and safe to push. This makes them natural physical buttons.
```python
ANTENNA_THRESHOLD = 0.3 # radians, tune as needed
def check_antenna_press(mini):
_, antennas = mini.get_current_joint_positions()
if antennas is None:
return None
left, right = antennas
if abs(left) > ANTENNA_THRESHOLD:
return "left"
if abs(right) > ANTENNA_THRESHOLD:
return "right"
return None
```
### Use Cases
- **Start/stop**: Press antenna to begin game
- **Selection**: Left = option A, Right = option B
- **Confirmation**: Any antenna = "yes"
**Reference:** `~/reachy_mini_resources/reachy_mini_radio/` (change radio station with antennas)
> If `~/reachy_mini_resources/` doesn't exist, run `skills/setup-environment.md` to clone reference apps.
---
## Head as Controller
The head has 6 DOF - it's a powerful input device for games or recording.
### Reading Head Position
```python
def get_head_as_joystick(mini):
"""Map head orientation to joystick-like values."""
pose = mini.get_current_head_pose()
# Extract orientation (implementation depends on pose format)
# Typically you'd extract yaw and pitch
# Normalize to -1 to 1 range
yaw_normalized = pose_yaw / 45.0 # Assuming ±45° range
pitch_normalized = pose_pitch / 30.0 # Assuming ±30° range
return {
"x": np.clip(yaw_normalized, -1, 1),
"y": np.clip(pitch_normalized, -1, 1)
}
```
### Use Cases
- **Games**: Head tilt controls spaceship, character, cursor
- **Recording**: Capture head motion for playback
- **Puppeteering**: Control another robot or avatar
**References:**
- `~/reachy_mini_resources/fire_nation_attacked/` - Head as joystick in game
- `~/reachy_mini_resources/spaceship_game/` - Head controls spaceship
- `~/reachy_mini_resources/marionette/` - Record and playback head motion
---
## No-GUI Pattern
For simple apps, skip the web UI. Use antenna interactions:
### Basic Flow
```python
def run(self, mini):
# 1. Signal readiness (twitch antennas)
self.twitch_antennas(mini)
# 2. Wait for user to press antenna
print("Press an antenna to start...")
while True:
press = check_antenna_press(mini)
if press:
break
time.sleep(0.1)
# 3. Run the actual app
self.main_loop(mini)
```
### Signaling States
Use antenna motion to communicate without GUI:
| State | Antenna behavior |
|-------|------------------|
| Ready/waiting | Gentle twitch |
| Processing | Slow wave |
| Success | Quick double bounce |
| Error | Shake rapidly |
**Reference:** `~/reachy_mini_resources/reachy_mini_simon/` (full game using only antennas)
---
## Combining Patterns
Many apps combine multiple patterns:
```python
class InteractiveApp:
def run(self, mini):
# Start with antenna press (no-GUI)
self.wait_for_start(mini)
while self.running:
# Use head as input
joystick = self.get_head_as_joystick(mini)
# Check for antenna presses
antenna = check_antenna_press(mini)
if antenna == "left":
self.action_a()
elif antenna == "right":
self.action_b()
# Update game/app state based on head position
self.update(joystick)
time.sleep(0.01)
```
---
## Tips
- **Debounce antenna presses** - Add cooldown to prevent multiple triggers
- **Provide feedback** - Move antennas or play emotion when input detected
- **Consider accessibility** - Not everyone can push antennas easily
- **Test threshold values** - Different users push with different force
Now let me confirm the run() entry point signature by looking at the conversation app's main.py and a simpler example:
HTTP 200
Content-Type: text/plain; charset=utf-8
"""Entrypoint for the Reachy Mini conversation app."""
import os
import sys
import time
import asyncio
import argparse
import threading
from typing import Any, Dict, List, Optional
from pathlib import Path
import gradio as gr
from fastapi import FastAPI
from fastrtc import Stream
from gradio.utils import get_space
from reachy_mini import ReachyMini, ReachyMiniApp
from reachy_mini_conversation_app.utils import (
CameraVisionInitializationError,
parse_args,
setup_logger,
initialize_camera_and_vision,
log_connection_troubleshooting,
)
def update_chatbot(chatbot: List[Dict[str, Any]], response: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Update the chatbot with AdditionalOutputs."""
chatbot.append(response)
return chatbot
def main() -> None:
"""Entrypoint for the Reachy Mini conversation app."""
args, _ = parse_args()
run(args)
def run(
args: argparse.Namespace,
robot: ReachyMini = None,
app_stop_event: Optional[threading.Event] = None,
settings_app: Optional[FastAPI] = None,
instance_path: Optional[str] = None,
) -> None:
"""Run the Reachy Mini conversation app."""
# Putting these dependencies here makes the dashboard faster to load when the conversation app is installed
from reachy_mini_conversation_app.moves import MovementManager
from reachy_mini_conversation_app.config import config, is_gemini_model, refresh_runtime_config_from_env
from reachy_mini_conversation_app.startup_settings import (
StartupSettings,
load_startup_settings_into_runtime,
)
logger = setup_logger(args.debug)
logger.info("Starting Reachy Mini Conversation App")
startup_settings = StartupSettings()
if instance_path is not None:
try:
from dotenv import load_dotenv
env_path = Path(instance_path) / ".env"
if env_path.exists():
load_dotenv(dotenv_path=str(env_path), override=True)
refresh_runtime_config_from_env()
logger.info("Loaded instance configuration from %s", env_path)
except Exception as e:
logger.warning("Failed to load instance configuration: %s", e)
try:
startup_settings = load_startup_settings_into_runtime(instance_path)
except Exception as e:
logger.warning("Failed to load startup settings: %s", e)
from reachy_mini_conversation_app.console import LocalStream
from reachy_mini_conversation_app.tools.core_tools import ToolDependencies
from reachy_mini_conversation_app.audio.head_wobbler import HeadWobbler
if args.no_camera and args.head_tracker is not None:
logger.warning("Head tracking disabled: --no-camera flag is set. Remove --no-camera to enable head tracking.")
if robot is None:
try:
robot_kwargs = {}
if args.robot_name is not None:
robot_kwargs["robot_name"] = args.robot_name
logger.info("Initializing ReachyMini (SDK will auto-detect appropriate backend)")
robot = ReachyMini(**robot_kwargs)
except TimeoutError as e:
logger.error(f"Connection timeout: Failed to connect to Reachy Mini daemon. Details: {e}")
log_connection_troubleshooting(logger, args.robot_name)
sys.exit(1)
except ConnectionError as e:
logger.error(f"Connection failed: Unable to establish connection to Reachy Mini. Details: {e}")
log_connection_troubleshooting(logger, args.robot_name)
sys.exit(1)
except Exception as e:
logger.error(f"Unexpected error during robot initialization: {type(e).__name__}: {e}")
logger.error("Please check your configuration and try again.")
sys.exit(1)
# Auto-enable Gradio in simulation mode (both MuJoCo for daemon and mockup-sim for desktop app)
status = robot.client.get_status()
if isinstance(status, dict):
simulation_enabled = status.get("simulation_enabled", False)
mockup_sim_enabled = status.get("mockup_sim_enabled", False)
else:
simulation_enabled = getattr(status, "simulation_enabled", False)
mockup_sim_enabled = getattr(status, "mockup_sim_enabled", False)
is_simulation = simulation_enabled or mockup_sim_enabled
if is_simulation and not args.gradio:
logger.info("Simulation mode detected. Automatically enabling gradio flag.")
args.gradio = True
try:
camera_worker, vision_processor = initialize_camera_and_vision(args, robot)
except CameraVisionInitializationError as e:
logger.error("Failed to initialize camera/vision: %s", e)
sys.exit(1)
movement_manager = MovementManager(
current_robot=robot,
camera_worker=camera_worker,
)
head_wobbler = HeadWobbler(set_speech_offsets=movement_manager.set_speech_offsets)
deps = ToolDependencies(
reachy_mini=robot,
movement_manager=movement_manager,
camera_worker=camera_worker,
vision_processor=vision_processor,
head_wobbler=head_wobbler,
)
current_file_path = os.path.dirname(os.path.abspath(__file__))
logger.debug(f"Current file absolute path: {current_file_path}")
chatbot = gr.Chatbot(
type="messages",
resizable=True,
avatar_images=(
os.path.join(current_file_path, "images", "user_avatar.png"),
os.path.join(current_file_path, "images", "reachymini_avatar.png"),
),
)
logger.debug(f"Chatbot avatar images: {chatbot.avatar_images}")
if is_gemini_model():
from reachy_mini_conversation_app.gemini_live import GeminiLiveHandler
logger.info("Using Gemini Live handler for model: %s", config.MODEL_NAME)
handler = GeminiLiveHandler(
deps,
gradio_mode=args.gradio,
instance_path=instance_path,
startup_voice=startup_settings.voice,
)
else:
from reachy_mini_conversation_app.openai_realtime import OpenaiRealtimeHandler
logger.info("Using OpenAI Realtime handler for model: %s", config.MODEL_NAME)
handler = OpenaiRealtimeHandler(
deps,
gradio_mode=args.gradio,
instance_path=instance_path,
startup_voice=startup_settings.voice,
) # type: ignore[assignment]
stream_manager: gr.Blocks | LocalStream | None = None
if args.gradio:
uses_gemini_backend = is_gemini_model()
api_key_textbox = gr.Textbox(
label="GEMINI_API_KEY" if uses_gemini_backend else "OPENAI API Key",
type="password",
value=(os.getenv("GEMINI_API_KEY") if uses_gemini_backend else os.getenv("OPENAI_API_KEY"))
if not get_space()
else "",
)
from reachy_mini_conversation_app.gradio_personality import PersonalityUI
personality_ui = PersonalityUI()
personality_ui.create_components()
stream = Stream(
handler=handler,
mode="send-receive",
modality="audio",
additional_inputs=[
chatbot,
api_key_textbox,
*personality_ui.additional_inputs_ordered(),
],
additional_outputs=[chatbot],
additional_outputs_handler=update_chatbot,
ui_args={"title": "Talk with Reachy Mini"},
)
stream_manager = stream.ui
if not settings_app:
app = FastAPI()
else:
app = settings_app
personality_ui.wire_events(handler, stream_manager)
app = gr.mount_gradio_app(app, stream.ui, path="/")
else:
# In headless mode, wire settings_app + instance_path to console LocalStream
stream_manager = LocalStream(
handler,
robot,
settings_app=settings_app,
instance_path=instance_path,
)
# Each async service → its own thread/loop
movement_manager.start()
head_wobbler.start()
if camera_worker:
camera_worker.start()
def poll_stop_event() -> None:
"""Poll the stop event to allow graceful shutdown."""
if app_stop_event is not None:
app_stop_event.wait()
logger.info("App stop event detected, shutting down...")
try:
stream_manager.close()
except Exception as e:
logger.error(f"Error while closing stream manager: {e}")
if app_stop_event:
threading.Thread(target=poll_stop_event, daemon=True).start()
try:
stream_manager.launch()
except KeyboardInterrupt:
logger.info("Keyboard interruption in main thread... closing server.")
finally:
movement_manager.stop()
head_wobbler.stop()
if camera_worker:
camera_worker.stop()
# Ensure media is explicitly closed before disconnecting
try:
robot.media.close()
except Exception as e:
logger.debug(f"Error closing media during shutdown: {e}")
# prevent connection to keep alive some threads
robot.client.disconnect()
time.sleep(1)
logger.info("Shutdown complete.")
class ReachyMiniConversationApp(ReachyMiniApp): # type: ignore[misc]
"""Reachy Mini Apps entry point for the conversation app."""
custom_app_url = "http://0.0.0.0:7860/"
dont_start_webserver = False
def run(self, reachy_mini: ReachyMini, stop_event: threading.Event) -> None:
"""Run the Reachy Mini conversation app."""
asyncio.set_event_loop(asyncio.new_event_loop())
args, _ = parse_args()
instance_path = self._get_instance_path().parent
run(
args,
robot=reachy_mini,
app_stop_event=stop_event,
settings_app=self.settings_app,
instance_path=instance_path,
)
if __name__ == "__main__":
app = ReachyMiniConversationApp()
try:
app.wrapped_run()
except KeyboardInterrupt:
app.stop()
I have everything I need. The entrypoint signature is def run(self, reachy_mini: ReachyMini, stop_event: threading.Event) -> None, subclassing ReachyMiniApp. I have enough — let me write up the report.
Install via uv (recommended) or pip from PyPI. Package name is reachy-mini (PyPI) which imports as reachy_mini.
# Recommended (after installing uv + creating a venv):uv venv reachy_mini_env --python 3.12source reachy_mini_env/bin/activateuv pip install "reachy-mini"
# Plain pip works too:pip install reachy-miniPython 3.10–3.12 supported. On Linux you also need GStreamer + USB udev rules. Source: https://raw.githubusercontent.com/pollen-robotics/reachy_mini/main/docs/source/SDK/installation.md
Class is ReachyMini. No host/IP arg needed — it auto-detects USB/localhost vs network. Use as a context manager:
from reachy_mini import ReachyMini
with ReachyMini() as mini: # control code here ...Optional override: ReachyMini(connection_mode="localhost_only" | "network") and ReachyMini(robot_name="..."). There is no host=/ip= argument in the public surface — connection is mediated by the local daemon (localhost:8000 for Lite, reachy-mini.local:8000 for Wireless).
.speak() / .tts() / .audio.play()Confirmed from docs/source/SDK/python-sdk.md: the SDK exposes raw audio I/O only. You generate audio with an external TTS lib, then push float32 samples to the speaker via mini.media.push_audio_sample(...). Verbatim from the docs:
from reachy_mini import ReachyMinifrom scipy.signal import resampleimport time
with ReachyMini(media_backend="default") as mini: mini.media.start_recording() mini.media.start_playing()
# Play mini.media.push_audio_sample(samples) time.sleep(len(samples) / mini.media.get_output_audio_samplerate())
mini.media.stop_recording() mini.media.stop_playing()Notes from the doc:
push_audio_sample() expects shape (samples, 1 or 2), dtype float32, 16 kHz.len(samples)/samplerate to wait for playback.mini.media.get_output_audio_samplerate() to confirm the rate; resample if your TTS produces 22.05 / 24 kHz.For "good morning" the simplest stack: Piper or pyttsx3 → WAV → load with soundfile/scipy.io.wavfile → convert to float32 mono → resample to 16 kHz → mini.media.push_audio_sample(samples). (The conversation app uses OpenAI/Gemini Realtime APIs that stream PCM directly into push_audio_sample.)
A Reachy Mini app is a Python package registered through a setuptools entry point group named reachy_mini_apps, exposing a class that subclasses reachy_mini.ReachyMiniApp and implements run(self, reachy_mini, stop_event).
Do not scaffold by hand — the official tool is:
reachy-mini-app-assistant create <app_name> <path> --publish# or for a TTS-friendly fork:reachy-mini-app-assistant create --template conversation <app_name> <path> --publishGenerated layout (verbatim from skills/create-app.md):
my_app/├── index.html # HF Space landing page├── style.css├── pyproject.toml # contains the reachy_mini entry point├── README.md # YAML frontmatter must include reachy_mini tag├── .gitignore└── my_app/ ├── __init__.py ├── main.py # your app code (run method) └── static/ # optional web UIThe required pyproject.toml block (from the conversation app):
[project.entry-points."reachy_mini_apps"]my_app = "my_app.main:MyApp"The required class signature (verbatim from reachy_mini_conversation_app/main.py):
from reachy_mini import ReachyMini, ReachyMiniAppimport threading
class ReachyMiniConversationApp(ReachyMiniApp): custom_app_url = "http://0.0.0.0:7860/" dont_start_webserver = False
def run(self, reachy_mini: ReachyMini, stop_event: threading.Event) -> None: ...The README YAML frontmatter must carry the discovery tag reachy_mini (and reachy_mini_python_app for Python apps) for the HF app store to pick it up.
Reachy Mini has no physical button, but per the official AGENTS.md: "Antennas: 2 motors, also usable as physical buttons." They use low-P PID so they are semi-passive and safe to push. The canonical pattern (verbatim from skills/interaction-patterns.md):
ANTENNA_THRESHOLD = 0.3 # radians, tune as needed
def check_antenna_press(mini): _, antennas = mini.get_current_joint_positions() if antennas is None: return None left, right = antennas if abs(left) > ANTENNA_THRESHOLD: return "left" if abs(right) > ANTENNA_THRESHOLD: return "right" return NoneReference implementation: reachy_mini_simon (no-GUI app driven entirely by antenna presses) at https://huggingface.co/spaces/apirrone/reachy_mini_simon. Tip from the docs: debounce with a cooldown.
If antennas don't fit the use case, alternatives that are also documented as supported:
static/index.html with a button that hits the daemon REST API (http://localhost:8000/docs) or your own FastAPI mounted in run().input() / keyboard lib — fine for a quick local demo, but not "appy".Antenna-as-button + Piper TTS in a ReachyMiniApp subclass: scaffold with reachy-mini-app-assistant create good_morning . --publish, then in good_morning/main.py poll check_antenna_press(reachy_mini) and on press: synthesize "good morning" with Piper to a numpy float32 mono buffer at 16 kHz and call reachy_mini.media.push_audio_sample(samples) (after start_playing()). Loop until stop_event.is_set().
ReachyMiniApp subclass + entry point): https://github.com/pollen-robotics/reachy_mini_conversation_app, pyproject: https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/pyproject.toml, main.py: https://raw.githubusercontent.com/pollen-robotics/reachy_mini_conversation_app/main/src/reachy_mini_conversation_app/main.py# Recommended (after installing uv + creating a venv):uv venv reachy_mini_env --python 3.12source reachy_mini_env/bin/activateuv pip install "reachy-mini"
# Plain pip works too:pip install reachy-minifrom reachy_mini import ReachyMini
with ReachyMini() as mini: # control code here ...from reachy_mini import ReachyMinifrom scipy.signal import resampleimport time
with ReachyMini(media_backend="default") as mini: mini.media.start_recording() mini.media.start_playing()
# Play mini.media.push_audio_sample(samples) time.sleep(len(samples) / mini.media.get_output_audio_samplerate())
mini.media.stop_recording() mini.media.stop_playing()reachy-mini-app-assistant create <app_name> <path> --publish# or for a TTS-friendly fork:reachy-mini-app-assistant create --template conversation <app_name> <path> --publishmy_app/├── index.html # HF Space landing page├── style.css├── pyproject.toml # contains the reachy_mini entry point├── README.md # YAML frontmatter must include reachy_mini tag├── .gitignore└── my_app/ ├── __init__.py ├── main.py # your app code (run method) └── static/ # optional web UI[project.entry-points."reachy_mini_apps"]my_app = "my_app.main:MyApp"from reachy_mini import ReachyMini, ReachyMiniAppimport threading
class ReachyMiniConversationApp(ReachyMiniApp): custom_app_url = "http://0.0.0.0:7860/" dont_start_webserver = False
def run(self, reachy_mini: ReachyMini, stop_event: threading.Event) -> None: ...ANTENNA_THRESHOLD = 0.3 # radians, tune as needed
def check_antenna_press(mini): _, antennas = mini.get_current_joint_positions() if antennas is None: return None left, right = antennas if abs(left) > ANTENNA_THRESHOLD: return "left" if abs(right) > ANTENNA_THRESHOLD: return "right" return None