Metadata-Version: 2.1
Name: bithuman
Version: 2.11.20
Summary: Real-time talking-avatar SDK: audio in, lip-synced frames out, on your own Mac, Linux or Windows machine.
Keywords: bithuman,avatar,talking-avatar,lip-sync,digital-human,real-time,on-device,sdk,essence-2,expression-2
Author-Email: bitHuman <hello@bithuman.ai>
License: Commercial — see LICENSE file
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: MacOS
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: C++
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Multimedia
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Multimedia :: Video
Project-URL: Homepage, https://bithuman.ai
Project-URL: Documentation, https://docs.bithuman.ai
Requires-Python: <3.15,>=3.10
Requires-Dist: numpy>=1.26.0
Requires-Dist: loguru~=0.7
Requires-Dist: soundfile>=0.13
Requires-Dist: pydantic~=2.10
Requires-Dist: pydantic-settings~=2.8
Requires-Dist: av<19,>=12.0
Requires-Dist: opencv-python-headless>=4.8
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Requires-Dist: psutil>=5.9; extra == "test"
Provides-Extra: expression-2
Requires-Dist: ai-edge-litert>=2.1.5; extra == "expression-2"
Description-Content-Type: text/markdown

# bithuman

Run a bitHuman avatar on your own machine, in your own process.

```bash
pip install bithuman
DEMO=$(python -c 'import bithuman,os; print(os.path.join(os.path.dirname(bithuman.__file__), "assets", "demo_sample.wav"))')
python -m bithuman render sofia-ramirez "$DEMO"     # -> sofia-ramirez.mp4
```

Two arguments — **an avatar and some audio** — and a video you can play (with
your API secret in `BITHUMAN_API_SECRET`; see [the key](#the-key)). The
avatar is a showcase name (`python -m bithuman list`), your agent's ten-character
code (`python -m bithuman list --mine`; fetched once, which is free), or a file
you already have. The audio above is 15 s of speech that ships with this package,
so the first run needs nothing you do not already have. The commands are
spelled as in the separate `bithuman` CLI: `render <avatar> <audio> [-o out.mp4]
[--limit N] [--json]` and `list [--mine]`. If you signed in with that CLI
(`bithuman login`, which this package does not install), the secret it saved is
read here too.

In your own program it is the same two things. The command above fetched the
avatar to `~/.cache/bithuman/showcase/`:

```python
import bithuman, os

speech = os.path.join(os.path.dirname(bithuman.__file__),
                      "assets", "demo_sample.wav")   # 15 s, ships in the wheel
avatar_file = os.path.expanduser("~/.cache/bithuman/showcase/sofia-ramirez.imx")

avatar = bithuman.open(avatar_file)
frames = 0
for image in avatar.render(speech):   # each image: (height, width, 3) uint8, RGB
    frames += 1
print(frames, "frames")
```

That is the whole thing: **open an avatar, then render audio through it.**

### both families, the same two lines

An **essence-2** avatar and an **expression-2** avatar are opened and rendered
by the code above, unchanged. Nothing you write says which one you have, and
you do not have to know.

expression-2 needs one extra package on the machine:

```bash
pip install "bithuman[expression-2]"
```

Open an expression-2 avatar without it and the refusal says so, and says that
line. Nothing else differs.

### offline rendering, without 3 GB of CUDA you will never run

`bithuman[offline]` adds torch and onnxruntime. From PyPI's default index that
resolves to the **CUDA** build of torch, and the extra costs **3.18 GB of
wheels — 2.45 GB of it `nvidia-*`, `cuda-*` and `triton`** that the offline
route never executes. Install torch first from
[PyTorch's own selector](https://pytorch.org/get-started/locally/) — choose the
*Compute Platform* **without** CUDA and run the one line it prints — and the
same extra then resolves to **0.18 GB, with no CUDA wheel in the set at all**
(the extra asks for `torch>=2.1`, and any build satisfies it):

```bash
pip install "bithuman[offline]"      # after torch, from the line the selector printed
```

(Measured 2026-09-19 on Linux x86_64 with `pip install --dry-run --report`:
49 wheels / 3.18 GB from the default index alone, 30 wheels / 0.18 GB with
PyTorch's CUDA-free index beside it. Use the default index only if you
actually want CUDA.)

---

## The surface

| you write | it means |
|---|---|
| `bithuman.open(source)` | open the avatar file on this machine; returns an `Avatar` |
| `avatar.render(audio)` | yield the frames for that audio |
| `Avatar` | what `open` gives you |
| `AvatarError` | catch this for any refusal |
| `InvalidAvatar` | we cannot find it, or it is not a usable avatar |
| `NotSupported` | this avatar cannot run here |
| `NotAuthorised` | the key is missing, invalid, or out of credit |
| `Failed` | we could not do it — the message says which |

There is nothing to configure: this package runs the avatar on this machine.
For a live conversation, where audio arrives as it is spoken, use
`AsyncBithuman` ([streaming](#streaming-a-live-conversation) below).

### audio in

`audio` is 16 kHz mono, and it is either a buffer or a stream — the same call:

```python
avatar.render(speech)                      # an audio file path
avatar.render(samples)                     # int16 or float32 in [-1, 1]
avatar.render(raw_bytes)                   # 16 kHz mono, signed 16-bit
avatar.render(microphone())                # any iterable of the above
```

### frames out

Each frame is a `(height, width, 3)` uint8 array in **RGB** order, in order, at
the avatar's own frame rate, which is a property of the avatar, not something
to choose: **25 per second for Essence 2, 20 per second for Expression 2**.

```python
import cv2
for image in avatar.render(speech):
    cv2.imshow("avatar", image[:, :, ::-1])   # OpenCV wants BGR
    cv2.waitKey(1)
```

### stopping early

Someone interrupting the avatar is "stop consuming and close the iterator":

```python
images = avatar.render(speech)
for n, image in enumerate(images):
    if n == 50:                       # e.g. the user started talking
        images.close()
        break
```

### releasing it

`with` frees everything at the end of the block; without it, the avatar is
freed when it is garbage collected.

```python
with bithuman.open(avatar_file) as avatar:
    for image in avatar.render(speech):
        frames += 1
```

### streaming a live conversation

`AsyncBithuman` takes audio as it arrives and yields frames with their audio,
paced at the avatar's play rate:

```python
from bithuman import AsyncBithuman          # inside an async function:

avatar = await AsyncBithuman.create(model_path=avatar_file)   # reads BITHUMAN_API_SECRET
await avatar.push_audio(pcm16_bytes, 16000, last_chunk=False)  # as the audio arrives
await avatar.flush()                                            # end of the reply
async for frame in avatar.run():
    if frame.has_image:
        show(frame.bgr_image)            # BGR numpy array
    if frame.audio_chunk:
        play(frame.audio_chunk.array)    # the audio that goes with this frame
await avatar.shutdown()
```

The full example is at <https://docs.bithuman.ai/platforms/python/app>.

---

## The four refusals

Each one leads to a different fix, and none of them asks you to know anything
about how we are built.

```python
try:
    avatar = bithuman.open(avatar_file)
    for image in avatar.render(speech):
        pass
except bithuman.InvalidAvatar:
    ...   # fix the path or the code, or fetch the avatar again
except bithuman.NotSupported:
    ...   # use the cloud package, or another device
except bithuman.NotAuthorised:
    ...   # fix the credential
except bithuman.Failed:
    ...   # retry, then report it
```

Every one of them is an `AvatarError`, so `except bithuman.AvatarError` catches
all four.

---

## The key

Rendering is metered, and the key belongs in the environment rather than in
your code:

```bash
export BITHUMAN_API_SECRET=...
```

Without one, `render` refuses with `NotAuthorised` before it hands you a
frame. Get a key at <https://www.bithuman.ai/developer/api-keys>.

`python -m bithuman` also reads a `.env` file in the current directory; that is
deprecated and will stop, so export the variable instead. What is already in
the environment always wins.

---

## Where it runs

| | |
|---|---|
| Python | 3.10 – 3.14 |
| macOS | Apple silicon |
| Linux | x86-64 and arm64 |
| Windows | Windows 11, x86-64 (64-bit Python) |
| Intel Macs, Windows on Arm | no wheel — `pip install` refuses rather than quietly giving you an old release |

`ffmpeg` is used to read an audio file when it is on your PATH; when it is
not, the decoder this package already installs reads the same file in this
process, so it is not something to install first.

Two environment variables:

| | |
|---|---|
| `BITHUMAN_API_SECRET` | your API secret, from https://www.bithuman.ai/developer/api-keys — rendering is metered, so it is required unless you pass `api_secret=`. `BITHUMAN_API_KEY` is read as a deprecated alias |
| `BITHUMAN_CACHE_DIR` | where a prepared avatar is kept (default `~/.cache/bithuman`) |

---

## This package never puts a command on your PATH

`pip install bithuman` installs a library and nothing else — and
`python -m bithuman` is why that costs you nothing: a module needs no script,
cannot collide with one, and is there the moment pip finishes. The full
`bithuman` command-line tool (a live avatar, a conversation) is a different
artifact and is **not** installed with pip:

```bash
curl -fsSL https://install.bithuman.ai | sh
brew install bithuman-product/bithuman/bithuman-cli      # macOS, equivalently
```

That is on purpose: a pip-installed command named `bithuman` would overwrite
the one the CLI installer or Homebrew put at the same path.

---

## Using it from a LiveKit agent

The LiveKit integration is a separate package, `livekit-plugins-bithuman`,
published by LiveKit out of `github.com/livekit/agents`:

```bash
pip install livekit-plugins-bithuman "bithuman[expression-2]"
```

**Do not give that plugin your account API secret.** Up to and including
1.8.4, the plugin copies the secret it is given into the avatar's participant
attributes, which every participant in the LiveKit room can read. Keep the
secret on your server under another name (`BITHUMAN_MASTER_SECRET`), mint a
one-hour runtime token per session (`POST /v1/runtime-tokens/mint`) and pass
that token to the plugin instead. The worker is at
<https://docs.bithuman.ai/platforms/livekit>.

---

## Licence

Proprietary — this package carries the runtime. See `LICENSE`.
