fix: complete MiniMax image provider contracts

This commit is contained in:
calesthio
2026-08-13 09:02:22 -07:00
parent 9c2850f02e
commit b1aefb364a
4 changed files with 120 additions and 1 deletions

View File

@@ -8,6 +8,13 @@ FAL_KEY=
# Alias for FAL_KEY (some SDKs/docs use this name); either one is read.
FAL_AI_API_KEY=
# --- MiniMax official direct API ---
# First-party image generation (image-01 / image-01-live).
# Get one at https://platform.minimax.io/user-center/basic-information/interface-key
MINIMAX_API_KEY=
# Optional: global (default) or cn.
MINIMAX_REGION=global
# --- Replicate ---
# Replicate-hosted video gen (seedance_replicate). Needed to make the
# Replicate-backed Seedance path selectable alongside the fal.ai one.

View File

@@ -50,6 +50,7 @@ AZURE_SPEECH_REGION= # Speech resource region, e.g. eastus
# MULTI-MODEL GATEWAY (one key, 6+ tools)
FAL_KEY= # FLUX, Recraft, Kling, Veo, MiniMax video
MINIMAX_API_KEY= # MiniMax first-party image generation
# KLING OFFICIAL DIRECT API
KLING_API_KEY= # Official Kling video, image, TTS, avatar, lip sync
@@ -227,6 +228,40 @@ No subscription — pure pay-as-you-go, no minimum spend.
---
### MiniMax — Official Direct Image API
> **Low-cost first-party image generation.** The direct MiniMax API supports
> seeded text-to-image, character subject references, custom dimensions, and
> global or mainland-China routing without a gateway.
**Tool unlocked:** `minimax_image`
**Env var:** `MINIMAX_API_KEY`
**Optional region:** `MINIMAX_REGION=global` (default) or `cn`
#### Setup
1. Create a MiniMax Open Platform account.
2. Generate an API key in the account's API-key page.
3. Add `MINIMAX_API_KEY=...` to `.env`.
4. For a mainland-China account, also set `MINIMAX_REGION=cn`.
#### Pricing
| Models | Global pay-as-you-go price |
|--------|----------------------------|
| `image-01`, `image-01-live` | $0.0035 per generated image |
MiniMax also offers subscription token plans with included daily image quota.
OpenMontage conservatively reports the standard pay-as-you-go amount in cost
estimates and generation results.
The tool is automatically discoverable through `image_selector`; choose it
with `preferred_provider: "minimax"`.
---
### Kling Official — Direct API
> **Official Kling path.** This is separate from `kling_video` via fal.ai: it uses Kling's official `Authorization: Bearer <KLING_API_KEY>` API, provider name `kling_official`, and direct Classic/Turbo/Omni task protocols.

View File

@@ -54,6 +54,75 @@ def test_status_requires_api_key(monkeypatch: pytest.MonkeyPatch) -> None:
assert tool.get_status() == ToolStatus.AVAILABLE
def test_cost_estimate_and_result_report_paid_images(
monkeypatch: pytest.MonkeyPatch, tmp_path
) -> None:
monkeypatch.setenv("MINIMAX_API_KEY", "test-key")
monkeypatch.setattr(
requests,
"post",
lambda *args, **kwargs: FakeResponse(
json_data={
"data": {
"image_base64": [
base64.b64encode(b"one").decode("ascii"),
base64.b64encode(b"two").decode("ascii"),
]
},
"base_resp": {"status_code": 0},
}
),
)
tool = MiniMaxImage()
inputs = {
"prompt": "A lighthouse at dusk",
"response_format": "base64",
"n": 2,
"output_path": str(tmp_path / "image.png"),
}
assert tool.estimate_cost(inputs) == pytest.approx(0.007)
result = tool.execute(inputs)
assert result.success, result.error
assert result.cost_usd == pytest.approx(0.007)
def test_image_selector_can_route_to_minimax(
monkeypatch: pytest.MonkeyPatch, tmp_path
) -> None:
from tools.graphics.image_selector import ImageSelector
monkeypatch.setenv("MINIMAX_API_KEY", "test-key")
monkeypatch.setattr(
requests,
"post",
lambda *args, **kwargs: FakeResponse(
json_data={
"data": {
"image_base64": [base64.b64encode(b"image").decode("ascii")]
},
"base_resp": {"status_code": 0},
}
),
)
tool = MiniMaxImage()
selector = ImageSelector()
monkeypatch.setattr(selector, "_providers", lambda: [tool])
result = selector.execute(
{
"prompt": "A lighthouse at dusk",
"preferred_provider": "minimax",
"response_format": "base64",
"output_path": str(tmp_path / "selected.png"),
}
)
assert result.success, result.error
assert result.data["selected_provider"] == "minimax"
assert result.data["selected_tool"] == "minimax_image"
@pytest.mark.parametrize(
("region", "expected_base_url"),
[

View File

@@ -24,6 +24,8 @@ from tools.base_tool import (
MODELS = ["image-01", "image-01-live"]
DEFAULT_MODEL = "image-01"
DEFAULT_REGION = "global"
# Official global pay-as-you-go rate for image-01/image-01-live.
PRICE_PER_IMAGE_USD = 0.0035
REGION_BASE_URLS = {
"global": "https://api.minimax.io",
"global_en": "https://api.minimax.io",
@@ -48,7 +50,9 @@ class MiniMaxImage(BaseTool):
"Set MINIMAX_API_KEY to your MiniMax API key. "
"Optionally set MINIMAX_REGION to global or cn."
)
agent_skills = ["flux-best-practices"]
# MiniMax is not a FLUX model. Use the provider-neutral visual direction
# skill until a dedicated MiniMax prompting skill is available.
agent_skills = ["visual-style"]
capabilities = ["generate_image", "text_to_image"]
supports = {
@@ -209,6 +213,9 @@ class MiniMaxImage(BaseTool):
def _safe_error(exc: Exception, api_key: str) -> str:
return str(exc).replace(api_key, "[redacted]") if api_key else str(exc)
def estimate_cost(self, inputs: dict[str, Any]) -> float:
return PRICE_PER_IMAGE_USD * int(inputs.get("n", 1))
def execute(self, inputs: dict[str, Any]) -> ToolResult:
api_key = os.environ.get("MINIMAX_API_KEY", "")
if not api_key:
@@ -285,6 +292,7 @@ class MiniMaxImage(BaseTool):
"request_id": data.get("id"),
},
artifacts=outputs,
cost_usd=self.estimate_cost(inputs),
duration_seconds=round(time.time() - start, 2),
model=payload["model"],
)