Skip to main content

Mirobody

The AI-native health data engine — Collect · Translate · Agent.

One lab prints A1c, the next HbA1c, a third hemoglobin A1c — one test, three spellings, and that is before the units disagree. Mirobody turns lab reports, wearables and genomics into one language AI can read: LOINC-coded, UCUM-normalized, FHIR-ready. The resolver runs offline, and the engine powers Theta Wellness, a live consumer health product with 5,000+ registered users and 500+ daily active.

English · 中文

License: Apache-2.0 Python 3.12+ PyPI Downloads Benchmarks arXiv Docs GitHub stars

📚 Documentation · ▶ Live demo — no sign-up · 🔌 API platform

⚡ Try it in 60 seconds

No key, no config, no network — and with uvx, no install either:

uvx mirobody resolve "LDL cholesterol" 血红蛋白 ヘモグロビン "空腹血糖(GLU)" 血脂

pip install mirobody instead if you want it on your PATH. Either way the resolver answers from a bundle that ships with the package.

mirobody resolve: four languages landing on one LOINC code, fully offline

血红蛋白 and ヘモグロビン land on the same code as hemoglobin, LOINC 718-7; 空腹血糖(GLU) on fasting glucose. 血脂 (lipids) is a category, not an observation, so the resolver returns nothing rather than a plausible wrong code.

from mirobody.engine import resolve, resolve_reading

resolve("血红蛋白").loinc                                # '718-7'   any language, one code
resolve("total cholesterol").loinc                     # '2093-3'  [Mass/volume]
resolve_reading("total cholesterol", "5.0", "mmol/L")   # '14647-2' [Moles/volume]
resolve_reading("total cholesterol", "193", "mg/dL")    # '2093-3'  the unit picks the code

resolve("中性粒细胞百分比").loinc                          # '26511-6' Neutrophils/Leukocytes
resolve_reading("中性粒细胞", "62 %", None).loinc          # '26511-6' a percentage...
resolve_reading("中性粒细胞", "4.2", "10*9/L").loinc       # '26499-4' ...and a count are two codes
resolve("血脂").resolved                                 # False    a category, not an observation

Pass the value and the unit when you have them. LOINC encodes the unit and the result type into the identity, so the same name resolves to different codes. resolve abstains rather than guessing: an empty code is a gap worth a second look; method="refused" is a decision. → Engine reference · Indicators

Why Mirobody

  • Two reports, same test, different spelling and units. That is the problem. Mirobody translates any reading, in any language, to LOINC + UCUM — and refuses to guess when it does not know the term.
  • AI can only reason over what it can read. The agent reads the original documents, charts lab draws against sensor-derived series, and cites the page it read from.
  • Self-hosted, standards-based, Apache-2.0. One ./deploy.sh runs the whole stack on your own machine; what comes out is LOINC-coded, FHIR-ready records you can take anywhere, and the same tools are served over MCP to Claude Desktop, Cursor or your own agent. No GPU: the app is self-hosted, the model is not. You bring one API key to a hosted provider; nothing runs inference on your machine. "Offline" here means the resolver — name to code, with no network and no key — not a local LLM.

Collect · Translate · Agent

From wearables to food photos — one standard format, ready for AI.

The engine does three things, and the codebase, the docs and Contributing are organized around exactly these three stages:

Stage What it means Where
① Collect Pull signals in: 3 device providers · 7 file formats · Apple Health (mirobody import apple export.zip, or a signed iOS client POSTs it in) collect/
② Translate (standardize) One standard: resolve any reading to canonical codes (LOINC · SNOMED CT · RxNorm), normalize units to UCUM, land on FHIR-recognized code systems indicator/
③ Agent Reason: an agent reads the original documents through a virtual filesystem and answers with charts and citations agent/

By the numbers

Concept graph 440,961 nodes · 22,044,110 cross-vocabulary edges · 595,746 source ids distilled into canonical concepts (LOINC · SNOMED CT · RxNorm bridges)
Aliases 49,253 multilingual (中文 22,578 · 日本語 16,809 · de·es·fr·ko·ru); hemoglobin, 血红蛋白, 血紅素 and ヘモグロビン all land on 718-7
Traditional Chinese a shipped 3,336-character zh-Hant → zh-Hans fold table, plus curated Traditional rows — a curated row always beats a fold
Units ~310 UCUM families with dimensional analysis and a molar-mass bridge keyed by LOINC code; 305 standard pulse indicators
Coverage 213/213 on the panels an ordinary checkup prints, in English, Chinese (Simplified and Traditional) and Japanese (test_engine_coverage.py)
Bundle LOINC 2.82: mirobody.BUNDLE_VERSION → loinc-2.82+2026.08.28-af2524b7a285 — release, cut date and a digest over the bundle's own members
Install pip install mirobody is 2 packages, on numpy only — ~52 MB on macOS, ~100 MB on Linux, where numpy bundles its own BLAS

Why 2.82 and not 2.83, what LOINC covers of the wearable world, and the opt-in semantic tier that cannot abstain: → Standardization in depth

📊 Benchmarks — open and independently reproducible

The most-downloaded health-AI benchmarks in their category on Hugging Face, 4,000+ downloads a month each: ESL-Bench (event-driven longitudinal health agents — 100 synthetic users, 10,000 queries, arXiv:2604.02834) · MedHall-Bench (medical hallucination) · MedHarm-Bench (harmful medical advice). Reproduce any of them with one command via mirobody-eval, which also seeds a deployment with synthetic, PHI-free trajectories.

🚀 Run the whole thing

git clone --depth 1 https://github.com/thetahealth/mirobody.git && cd mirobody
git lfs install && git lfs pull   # the resolver's LOINC bundle; a fresh clone holds a pointer stub until you do
./deploy.sh                       # Postgres + pgvector, Redis, server, worker → http://localhost:18060

--depth 1 because the history is mostly superseded frontend builds and you almost certainly do not want it: it takes the clone from ~125 MB to ~99 MB (measured 2026-09-14 — most of what is left is the LFS bundle, which both copies need). Drop the flag if you plan to send a pull request. ./deploy.sh also fetches the 22 MB concept graph behind semantic indicator search, which is a release asset rather than a tracked file — see mirobody/res/EXTERNAL.tsv.

Sign in as caregiver@mirobody.ai, code 111111. No mail provider needed: the sign-in page opens on password, and an account of your own is one request away:

curl -X POST localhost:18060/password/register -H 'Content-Type: application/json' \
     -d '{"email":"you@example.com","password":"at-least-8-chars"}'

Your care circle. The account is named for the role it plays: you sign in as the caregiver, and the record you read belongs to someone else. A circle is the unit of sharing — each member holds their own record and decides, on their own row, whether the others may view it.

Your own thin record next to hers — the thick one you can only view.

SEED_DEMO_DATA is on by default, so the circle is already populated: you own a thin record — a few weeks of self-tracked vitals and one unremarkable checkup — and one synthetic person shares a thick one with you, Demo (synthetic): 244 indicators, 14,273 readings across two years, five documents the agent can read. Same question, two records: your HbA1c answers with one boring-normal value from data you own; hers with a two-year story from data you can only view. The recording walks both sides — your own indicators and files, then the switch to her shared record and its two years of HbA1c:

Your own account's indicators and an uploaded report, then switching to Demo's shared record and opening two years of HbA1c

The switch in that diagram is a column, not a promise: care_circle_members.health_access, NOT NULL DEFAULT 0, on your own row. Being invited into a circle shares nothing — the member decides, and no other person's action can raise it. A route that forgets to check answers 403 instead of handing over a record. examples/06_care_circle_rules.py prints the whole decision table offline. Set SEED_DEMO_DATA=false for a deployment that will hold real data.

One key runs everything, and none of it is your GPU. The key points at a hosted model; your machine runs no inference. Browsing the seeded record needs no key; the upload and the questions below ride one. Put ONE key in the .env next to compose.yaml and docker compose restart — the app re-reads /app/.env; a shell export does not reach the containers. The .env is the only place for the key: config.llm.yaml names the variable (api_key: OPENROUTER_API_KEY), never the secret. An OpenRouter key as OPENROUTER_API_KEY is the recommended one; a DashScope key when openrouter.ai is unreachable from your network; a Google, OpenAI (OPENAI_API_KEY), Anthropic (ANTHROPIC_API_KEY) or DeepSeek key works alone as well. Every model decision — which model chats, which reads report photos, which extracts the indicators, which embeds — is a line in config.llm.yaml, where you can read and change it (a self-hosted gateway is one <PREFIX>_BASE_URL in .env). The boot log, and mirobody doctor, print what each surface selected and name the fix where one has nothing.

① Collect + ② Translate. Drop demo/lab_report_2025-10-15.pdf on the Data page and twelve analytes come out with values and units, each linking back to the page it was read from:

Dropping a lab-report PDF on the Data page; twelve analytes extracted, each linked to its source file

③ Agent. Ask about her HbA1c and the agent finds the data, charts the three lab draws against 104 sensor-derived estimates, and says plainly that the improvement did not hold. Ask again about the report you just uploaded and it reads that instead — the fourth scene of the four-minute walkthrough.

Asking about the shared record's HbA1c; the agent queries, charts lab and sensor series together, and reads the trend

→ Docker deployment · Configuration · Local Python setup

🔌 Use it, extend it

You want Do this Docs
Offline resolution and units in your code pip install mirobody — 2 packages, no key, no network Engine
A document turned into readings pip install 'mirobody[parse]' — PDF, image, Excel, Word, PowerPoint, text; only a scanned page reaches a vision model Engine
The agent harness as a library pip install 'mirobody[agent]' — middleware, virtual-filesystem backends, checkpointer Bringing your own agent
These tools in Claude Desktop, Cursor or your own loop Settings → MCP: every agent tool is also served at /mcp, gated per user MCP servers · examples/07_claude_agent_sdk.py
Your app talking to a deployment HTTP API, or backbone mode: your agent, our data layer API overview · Backbone
A new tool, skill or device provider Drop a file into mirobody/agent/tools/, mirobody/agent/skills/ or mirobody/collect/providers/ and restart — or pip install a package declaring a mirobody.providers / mirobody.tools / mirobody.agents entry point Adding tools · Skills · Providers
Your own agent harness AGENT_DIRS → your directory replaces the shipped agent mirobody/agent/README.md
The LOINC axis table and alias sources at build time mirobody.bundle — for generating a seed or a corpus mirobody/bundle.py

🤝 Contributing

The highest-leverage contribution is a term the resolver gets wrong. Run mirobody resolve "<term>"; if the answer is wrong or empty, report it or add a row to resolver_overrides.tsv plus a case to test_engine_coverage.py — the coverage score is the review.

pip install -e '.[test]' && pytest -q && lint-imports

→ CONTRIBUTING.md · Contributing guide · Repository layout · Roadmap · CHANGELOG · SECURITY

📚 Documentation

docs.mirobody.ai, in English and Chinese — start at the Quickstart, or go straight to the API reference. Long-form guides for contributors live in docs/.

🙏 Acknowledgements

Projects that shaped the kernel's rules — none of their code is included here: Open Wearables (the data-standardization failure modes kernel/series and kernel/quality close), Home Assistant (state_class), Open mHealth / IEEE 1752 (field names), wearipedia (synthetic payloads), dlt / Airbyte / Singer (connector shape), deepagents, LangChain and langchain-quickjs (the harness, the file-system projection, the eval REPL), Regenstrief Institute (LOINC), UCUM, HL7 FHIR, OHDSI OMOP — see LICENSE-3RD-PARTY.

⭐ Star History

Star History Chart

If it read a report for you, a star helps the next person find it. Releases land most weeks — Watch for them.

📚 Docs · ▶ Demo · 🔌 Platform · 🧪 Eval

Apache 2.0 · © 2026 Theta Health

Release files for mirobody 1.4.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mirobody 1.4.3
File Size Uploaded
mirobody-1.4.3.tar.gz 26.6 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for mirobody 1.4.3
File Interpreter ABI Platform
mirobody-1.4.3-py3-none-any.whl Python 3 none any Details

Total release size: 53.3 MB

Release files / mirobody-1.4.3.tar.gz

Download URL mirobody-1.4.3.tar.gz
Size 26.6 MB
Tags Source
SHA-256 checksum
How to use checksums
d2a0f851809bcb1de396b4dc574e5d4d79821d75bd933236905f6299fd03d64e
BLAKE2b-256 checksum
How to use checksums
ae1a188753cf10da488f2dec1aec8e923400a0fd55a5252e533b61df7e74ac6e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / mirobody-1.4.3-py3-none-any.whl

Download URL mirobody-1.4.3-py3-none-any.whl
Size 26.7 MB
Tags Python 3
SHA-256 checksum
How to use checksums
b46b4f4f2f0268e670aec3572aa6d2b0a44e03b392eabe42bf7817da3ecb6b7c
BLAKE2b-256 checksum
How to use checksums
0ede35e8274973d407a71e933b333eec9ce69a221bd55d9a15c90c773e67cfff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release history Release notifications | RSS feed

1.5.1

2 release files

1.5.0

2 release files

1.4.4

2 release files

This release

1.4.3 This release

2 release files

1.4.2

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.0.62

2 release files

1.0.51

2 release files

1.0.50

2 release files

1.0.4

2 release files

1.0.3

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page