Metadata-Version: 2.4
Name: hermes-minimax-media
Version: 0.3.0
Summary: MiniMax (海螺 / minimax) image + video + music generation backends for Hermes Agent. Image: image-01 T2I+I2I. Video: Hailuo-2.3 / Hailuo-02 / S2V-01 T2V+I2V (async). Music: music-2.6 / music-cover / music-cover-free lyrics+prompt -> MP3 (sync). CN endpoint first, global fallback. Install: pip install hermes-minimax-media && hermes-minimax-setup.
Author: lora-sys
License: MIT
Project-URL: Homepage, https://github.com/lora-sys/hermes-minimax-media
Project-URL: Repository, https://github.com/lora-sys/hermes-minimax-media
Project-URL: Issues, https://github.com/lora-sys/hermes-minimax-media/issues
Project-URL: Changelog, https://github.com/lora-sys/hermes-minimax-media/blob/main/CHANGELOG.md
Keywords: hermes,hermes-agent,minimax,minimax,image-generation,video-generation,music-generation,ai,hailuo,t2i,t2v,i2v,t2m
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Multimedia :: Graphics
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: setup
Requires-Dist: pyyaml>=6.0; extra == "setup"
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Requires-Dist: pyyaml>=6.0; extra == "test"
Dynamic: license-file

# Hermes MiniMax Media

[MiniMax](https://www.minimax.io) (海螺 / minimax) **image + video + music** generation backends for [Hermes Agent](https://github.com/NousResearch/hermes-agent).

|| Surface | Model | Modes |
||---|---|---|
|| `image_gen/minimax` | `image-01` | Text-to-Image, Image-to-Image (subject reference) |
|| `video_gen/minimax` | `MiniMax-Hailuo-2.3` | T2V, I2V, 6/10s @ 768P/1080P |
|| `video_gen/minimax` | `MiniMax-Hailuo-02` | T2V, I2V with start-end frames |
|| `video_gen/minimax` | `S2V-01` | Subject-reference T2V (face consistency) |
|| `music_gen/minimax` | `music-2.6` | Lyrics + prompt -> MP3 (sync); instrumental & cover modes |

CN endpoint (`api.minimaxi.com`) is used by default when `MINIMAX_CN_API_KEY` is set;
global (`api.minimax.io`) is the automatic fallback when only `MINIMAX_API_KEY` is set.

## Demo

### Image (image-01)

| Apple on plate | Cat astronaut | Cyberpunk city |
|---|---|---|
| ![Apple](docs/assets/screenshot-apple.jpg) | ![Cat astronaut](docs/assets/screenshot-cat-astronaut.png) | ![Cyberpunk city](docs/assets/screenshot-city.png) |
| `a tiny red apple on a white plate, studio lighting, photorealistic` | `a cute cat astronaut floating in a colorful nebula, digital art, vibrant` | `futuristic tokyo cityscape at night, neon lights, rain reflections, cyberpunk` |

### Video (Hailuo-2.3, 6s @ 768P)

`a calico cat napping in a sunbeam, soft cinematic lighting, gentle breathing motion`

![Demo GIF](docs/assets/demo.gif)

[MP4 source (608KB)](docs/assets/screenshot-cat-napping.mp4)

### Music (music-2.6)

Both demos are real MiniMax Music API outputs (music-2.6, 256 kbps MP3 @ 44.1 kHz
stereo), generated by the plugin's live smoke tests:

| Instrumental (2:05) | Vocals (1:41) |
|---|---|
| `Cheerful acoustic guitar, warm strumming, sunny afternoon vibe` | `Soulful Blues, Slow Tempo, Electric Guitar, Male Vocal, Rainy Night, Melancholy` + custom lyrics |
| [▶ Play MP3 (3.8 MB)](docs/assets/demo-music-instrumental.mp3) | [▶ Play MP3 (3.9 MB)](docs/assets/demo-music-vocals.mp3) |

Lyrics used for the vocal demo:

```text
[Verse]
Rain on the windowpane, soft and slow
Coffee steam rising, the room aglow
...

[Chorus]
Midnight rain, fallin down on me
Like tears I cannot cry for all to see
Just the rhythm of the rain
And the blues deep in my soul

... (full lyrics in tests/test_music_gen_minimax.py)
```

## Installation

### From PyPI

```bash
pip install hermes-minimax-media[setup]   # [setup] pulls PyYAML for the setup helper
hermes-minimax-setup                       # writes plugins.enabled + provider blocks
hermes gateway restart                     # pick up the new config
```

The package registers three entry points with the `hermes_agent.plugins` group
(`minimax-imggen`, `minimax-vidgen`, `minimax-musicgen`). Hermes auto-discovers
them on next gateway start; **but the loader only enables plugins that are
listed in `plugins.enabled` in `~/.hermes/config.yaml`**, so the
`hermes-minimax-setup` one-shot is required to wire the names up. It's
idempotent — re-running on an already-configured file is a no-op.

Why a CLI helper instead of pip doing it directly? `setup.py` runs in an
isolated build environment and can't reach `~/.hermes/`. We also didn't want
`pip install` to silently mutate user config.

If you'd rather do it manually:

```bash
hermes plugins enable minimax-imggen minimax-vidgen minimax-musicgen
```

### From source

```bash
git clone https://github.com/lora-sys/hermes-minimax-media.git
cd hermes-minimax-media
pip install -e ".[setup]"
hermes-minimax-setup
```

### Manual install (no pip)

```bash
mkdir -p ~/.hermes/plugins/{image_gen,video_gen,music_gen}
cp -r src/hermes_minimax_media/plugins/image_gen/minimax \
      ~/.hermes/plugins/image_gen/minimax
cp -r src/hermes_minimax_media/plugins/video_gen/minimax \
      ~/.hermes/plugins/video_gen/minimax
cp -r src/hermes_minimax_media/plugins/music_gen/minimax \
      ~/.hermes/plugins/music_gen/minimax

hermes plugins enable image_gen/minimax video_gen/minimax music_gen/minimax
```

Note the **path-derived keys** (`image_gen/minimax`, etc.) for the manual
install — those match the bundled layout that the directory scanner walks.
The pip-installed entry-point plugins use the bare entry-point name
(`minimax-imggen`, etc.). They're not interchangeable; pick the row that
matches your install method. `hermes plugins enable` will tell you which
keys are valid for your environment.

## Configuration

### 1. Set your MiniMax API key

Add to `~/.hermes/.env`:

```bash
# CN endpoint (api.minimaxi.com) — used by default
MINIMAX_CN_API_KEY=eyJ...

# Optional: global endpoint (api.minimax.io) — fallback
MINIMAX_API_KEY=eyJ...
```

Get a key at https://api.minimaxi.com/user-center/basic-information/interface-key
(or https://www.minimax.io for the global endpoint).

### 2. Enable the plugins in `~/.hermes/config.yaml`

The `hermes-minimax-setup` one-shot (from the install step above) writes this
block for you. If you'd rather hand-edit, the equivalent is:

```yaml
plugins:
  enabled:
    - minimax-imggen       # pip entry-point name (NOT image_gen/minimax — that's the manual-install key)
    - minimax-vidgen
    - minimax-musicgen
    # ... other plugins

image_gen:
  provider: minimax
  minimax:
    model: image-01

video_gen:
  provider: minimax
  minimax:
    model: MiniMax-Hailuo-2.3   # or Hailuo-02, S2V-01

music_gen:
  # NOTE: music_gen currently lacks a top-level provider surface in upstream
  # Hermes — the music_generate tool is registered directly by the plugin's
  # register() callback. We still set the per-provider model here so when the
  # surface lands it's pre-configured.
  minimax:
    model: music-2.6   # or music-cover, music-cover-free
```

**Why `minimax-imggen` instead of `image_gen/minimax`?** The pip-installed
entry point declares its key as `minimax-imggen` (the entry-point name). The
manual `~/.hermes/plugins/image_gen/minimax/` layout declares its key as
`image_gen/minimax` (path-derived). The two layouts are not interchangeable
— pick whichever matches your install method. `hermes plugins list` shows
the right key for whatever is installed.

### 3. Restart Hermes

```bash
hermes gateway restart
```

### Verify the setup

```bash
hermes plugins list --json | python3 -c "import sys, json; print([p['name'] for p in json.load(sys.stdin) if 'minimax' in p['name']])"
# Expect: ['minimax-imggen', 'minimax-musicgen', 'minimax-vidgen']
hermes-minimax-setup --check
# Expect: OK  /home/lora/.hermes/config.yaml
```

## Usage

Once configured, ask Hermes naturally — the model picks the right tool:

**Text-to-image:**
> 画一只戴墨镜的猫

**Image-to-image** (subject reference):
> 把这张自拍转成吉卜力风格 (pass an `image_url`)

**Text-to-video:**
> 做一个 6 秒的延时摄影：城市黄昏的车流

**Image-to-video** (animate a still):
> 让这张海浪照片动起来 (Hailuo-2.3, pass `image_url`)

**Subject-reference video** (face-consistent character):
> 让视频里这个角色挥手 (S2V-01, pass `image_url` of a face)

**Text-to-music** (lyrics + style):
> 写一首蓝调，主题是雨夜 (the agent will fill in lyrics with lyrics_optimizer=true, then call music_generate)

**Instrumental music** (no vocals):
> 做一个 60 秒的欢快尤克里里纯音乐 (is_instrumental=true)

**Custom lyrics + style:**
> 用我写的歌词 [Verse]\n... 做一首电子流行歌

## Models

### Image

|| Model | Modes | Aspect ratios | Notes |
||---|---|---|---|
|| `image-01` | T2I, I2I | 16:9, 9:16, 1:1 | Built-in prompt optimizer on; `seed` supported |

### Video

|| Model | Duration | Resolution | T2V | I2V | Subject ref | Start-end frames |
||---|---|---|---|---|---|---|
|| `MiniMax-Hailuo-2.3` | 6 / 10 s | 768P, 1080P | ✅ | ✅ | ❌ | ❌ |
|| `MiniMax-Hailuo-02` | 6 / 10 s | 768P, 1080P | ✅ | ✅ | ❌ | ✅ |
|| `S2V-01` | 6 s | 1080P | ❌ | ❌ | ✅ (face) | ❌ |

> Note: `720P` is **not** a valid MiniMax resolution. The plugin auto-coerces
> `720P` -> `768P` to avoid API errors. Don't rely on `720P` working.

### Music

|| Model | Modes | Output | Notes |
||---|---|---|---|
|| `music-2.6` | T2M (lyrics + prompt), instrumental | MP3 / WAV / PCM | Default. Supports `lyrics_optimizer=true` to auto-write lyrics from prompt. |
|| `music-cover` | Cover song (needs `cover_feature_id`) | MP3 / WAV / PCM | Two-step workflow via the Music Cover Preprocess API. |
|| `music-cover-free` | Cover song (free tier) | MP3 / WAV / PCM | Same as `music-cover`, free quota. |

> The `music_generate` tool runs **synchronously** (unlike video): one POST
> to `/v1/music_generation`, no task polling. Typical latency is 30-90s.
> Output audio is cached at `~/.hermes/audio_cache/minimax_music-2.6_<ts>.mp3`.

## Endpoint resolution

| Env state | Endpoint used |
|---|---|
| `MINIMAX_CN_API_KEY` set | `https://api.minimaxi.com` (CN, default) |
| Only `MINIMAX_API_KEY` set | `https://api.minimax.io` (global) |
| `MINIMAX_CN_BASE_URL` / `MINIMAX_BASE_URL` set | Override host only; paths are always `/v1/...` |

## Troubleshooting

### `MiniMax image API error 401`

Wrong key or the wrong region. Verify the key is valid at the relevant user
center (CN: https://api.minimaxi.com ; global: https://www.minimax.io). The
plugin auto-picks the right endpoint based on which env var is set, but if
both are set, CN wins.

### `MiniMax submit returned no task_id: ... does not support resolution 720P`

`720P` was a wrong assumption from older docs. The plugin normalizes it to
`768P`. If you see this error from a hand-rolled request, switch to `768P`.

### Video generation times out (>5 min)

The plugin polls every 10s for up to 5 minutes. Hailuo-2.3 typically returns
in 30-90s for a 6s/768P clip; 10s/1080P can stretch to 3-4 minutes. If you
hit the 5-min cap repeatedly, switch to a shorter duration or lower
resolution.

### `MiniMax submit error 429: ... balance not enough`

Out of credits. Top up at https://api.minimaxi.com/user-center/payment
(or the global user center).

### Plugin not loading

Run `hermes-minimax-setup --check` — it'll tell you exactly what's missing
from `plugins.enabled` (the loader silently leaves a not-enabled plugin out).

Verify the path matches what `config.yaml` says:

```bash
ls ~/.hermes/plugins/image_gen/minimax/    # manual install
ls ~/.hermes/plugins/video_gen/minimax/    # manual install
```

Each directory should contain both `__init__.py` and `plugin.yaml`.

For pip installs, the entry-point discovery path is what counts:

```bash
python3 -c "import importlib.metadata; eps = importlib.metadata.entry_points(); \
            names = eps.select(group='hermes_agent.plugins') if hasattr(eps,'select') else eps.get('hermes_agent.plugins',[]); \
            print([e.name for e in names if 'minimax' in e.name])"
# Expect: ['minimax-imggen', 'minimax-musicgen', 'minimax-vidgen']
```

If those names show up but the plugin still doesn't load, run
`hermes-minimax-setup` to write them into `plugins.enabled`.

If you installed via `pip install hermes-minimax-media`, the entry points
`minimax-imggen` / `minimax-vidgen` / `minimax-musicgen` are auto-registered
on install; you only need the `hermes-minimax-setup` step (or its manual
equivalent `hermes plugins enable <name>`) to make Hermes' opt-in loader pick
them up.

> Historical note: the loader's entry-point branch sets `key=ep.name` and
> `kind="standalone"`, so writing `plugins.enabled: [image_gen/minimax]`
> (the path-derived key from the manual-install layout) does NOT match an
> entry-point-installed plugin. Use the entry-point name (`minimax-imggen`)
> instead. `hermes plugins list` will print the exact key to use.

## Development

```bash
# Clone
git clone https://github.com/lora-sys/hermes-minimax-media
cd hermes-minimax-media

# Editable install
pip install -e .

# Run tests (no API key needed)
pytest tests/ -v

# Run live smoke (requires MINIMAX_CN_API_KEY)
MINIMAX_CN_API_KEY=... pytest tests/ -v -m requires_live

# Lint
ruff check src/ tests/

# Build
python -m build
```

## License

MIT
