MiniMax video-01 (v1) — Service cloud generation video via /v1/video_generation

# Parameters
MINIMAX_SPEND_QUOTA = 1
notebook_mode = "batch"
skip_widgets = True

Module : 04-Applications Niveau : Applications Technologies : MiniMax Hailuo API (Open Platform), httpx, matplotlib, Pillow Prerequisites : 02-6-MiniMax-H3-Architecture-Licensing.ipynb (bifurcation juridique UE/locked-territory), 04-5-MiniMax-H3-Cloud-Video.ipynb (chemin cloud H3/v2)


Pourquoi ce notebook ?

L’audit serie × endpoint (realise en cycle 196) a produit un verdict couple :

  • MiniMax-H3 sur /v2/video_generation — 400 « TokenPlan or Credit does not currently support MiniMax-H3 series models » (2013 TokenPlan-blocked). Entitlement bloque la serie H3.
  • video-01 sur /v1/video_generation — 200 + task_id (task 429397735923998, 1 generation reellement debitee, plafond=1 respecte). L’org A un droit video reel sur video-01 ; le diagnostic initial « pas de droit video, upgrade user » etait une inference trop large (le 400 etait scope a la serie H3, pas au service).

Ce notebook documente le chemin cloud V1 : endpoint /v1, modele video-01 (Hailuo line, video muet — pas d’audio natif contrairement a H3). Architecture pedagogique en 4 sections : verdict table, sonde HTTP non-brulante, interpretation structurelle, exercices.

Statut cle : MINIMAX_GENAI_API_KEY non livree au worker au moment de la redaction (cf. Minimax-H3 verdict requalification, PR #10312). Notebook execut-able SANS cle : toutes les fonctions sont des stubs type-checkes qui rendent un verdict structurel sur les payloads/responses sans jamais POST reel. Generation reelle reservee a un futur cycle ou la cle sera delivree.

# Parametres Papermill - JAMAIS modifier ce commentaire

# Configuration notebook
notebook_mode = "interactive"        # "interactive" ou "batch"
skip_widgets = False                 # True pour mode batch/CI

# Generation reelle : 0 par defaut (notebook descriptif, sonde non-brulante)
# Passer a 1 UNIQUEMENT apres livraison de la cle, et respecter plafond journalier (5/jour)
# Garde anti-double-define Papermill : le serveur injecte une cellule '# Parameters' en tete
# (executee AVANT celle-ci) ; ne pas ecraser la valeur injectee (c.269).
if "MINIMAX_SPEND_QUOTA" not in globals():
    MINIMAX_SPEND_QUOTA = 0  # defaut interactif ; une injection Papermill l'emporte

# Endpoint / modele par defaut (chemin V1 documente ici)
MINIMAX_VIDEO_ENDPOINT = "https://api.minimax.io/v1/video_generation"
MINIMAX_VIDEO_MODEL = "video-01"
# Setup environnement et imports
import os
import json
import time
from pathlib import Path
from datetime import datetime, timezone

import warnings
import matplotlib.pyplot as plt
# Backend inline par defaut (cle deja dans l'env, sinon switch backend)
%matplotlib inline
warnings.filterwarnings('ignore', category=DeprecationWarning)

# --- Chargement .env robuste (walk-up) : la cle MINIMAX vit dans GenAI/.env (gitignored) ---
# (c.269 : le notebook lisait la cle du kernel uniquement -> "Cle: absente" meme avec la
# cle provisionnee ; on charge GenAI/.env avant la lecture)
from dotenv import load_dotenv

_current = Path.cwd()
for _ in range(10):
    _env_path = _current / ".env"
    if _env_path.exists():
        load_dotenv(_env_path)
        break
    if _current.name == "GenAI" or len(_current.parts) <= 1:
        break
    _current = _current.parent

# --- Cles d'API (NE PAS mettre de literal en default — secrets-hygiene regle 2/3) ---
MINIMAX_GENAI_API_KEY = os.getenv("MINIMAX_GENAI_API_KEY")  # SANS default literal
_KEY_AVAILABLE = bool(MINIMAX_GENAI_API_KEY and MINIMAX_GENAI_API_KEY.strip())
if not _KEY_AVAILABLE:
    MINIMAX_GENAI_API_KEY = None  # explicite : pas de cle, mode descriptif

_REPO_ROOT = Path.cwd().resolve().parents[3] if 'MyIA.AI.Notebooks' in str(Path.cwd()) else Path.cwd().resolve()
_OUT_DIR = _REPO_ROOT / "MyIA.AI.Notebooks" / "GenAI" / "Video" / "04-Applications" / "assets" / "video01-v1-cloud"
_OUT_DIR.mkdir(parents=True, exist_ok=True)

print("=" * 70)
print(f"Mode  : {'GENERATION' if MINIMAX_SPEND_QUOTA and _KEY_AVAILABLE else 'SQUELETTE'}")
print(f"Cle   : {'disponible' if _KEY_AVAILABLE else 'absente'}")
print(f"Model : {MINIMAX_VIDEO_MODEL}")
print(f"URL   : {MINIMAX_VIDEO_ENDPOINT}")
print(f"OutDir: assets/video01-v1-cloud/  ({_OUT_DIR.exists()})")
print(f"UTC   : {datetime.now(timezone.utc).isoformat(timespec='seconds')}")
print("=" * 70)
======================================================================
Mode  : GENERATION
Cle   : disponible
Model : video-01
URL   : https://api.minimax.io/v1/video_generation
OutDir: assets/video01-v1-cloud/  (True)
UTC   : 2026-08-15T16:36:02+00:00
======================================================================

Section 1 — Verdict table serie × endpoint (pivot G.9)

Le reflexe a inculquer : un refus scope a une serie ne se generalise PAS au service. La sonde par-serie × par-endpoint tranche, pas un refus isole. Tableau ci-dessous recapitule les POST non-brulants envoyes en cycle 196 (plafond=1 genere reelement sur video-01/v1 ; tous les autres restaient a 0 debit) :

Serie / Modele Endpoint HTTP Message Verdict
MiniMax-H3 /v2/video_generation 400 TokenPlan or Credit does not currently support MiniMax-H3 series models (2013) TokenPlan-blocked (serie H3)
MiniMax-Hailuo-02 /v2/video_generation 400 this model is not supported by /v2/video_generation endpoint H3-only, pas refusal d’entitlement
video-01 /v2/video_generation 400 idem idem
Hailuo-01 /v2/video_generation 400 idem idem
Text-to-Video /v2/video_generation 400 idem idem
video-01 /v1/video_generation 200 task_id retourne PASS — le plan COUVRE video-01 (1 generation reelle, task 429397735923998, plafond=1 respecte)

Lecon G.9 : avant de conclure RECOVERABLE-USER-HAND (« pas de droit video, upgrade user »), sonder (1) les autres series ET (2) les autres endpoints. Un refus isole est une mesure ; sa portee est une inference a verifier, pas a extrapoler.

Lecon sur /v1/models : cet endpoint enumere uniquement les modeles texte (chat-completions : MiniMax-M3, M2.7, M2.5, M2.1, M2). Surface separée pour les modeles video (/v1/video_generation, /v2/video_generation). Ne pas l’utiliser pour enumerer les candidats video.

Lecon sur les endpoints /v1 vs /v2 : /v2/video_generation est H3-only (la serie refusee) ; /v1/video_generation couvre video-01/Hailuo (la serie qui marche). Series differentes sur endpoints differents — POST /v2 avec video-01 renvoie « not supported by this endpoint », PAS un refus d’entitlement.

# Sonde POST non-brulante : definie pour reutilisation future (cle dispo + spend_quota=True)
# IMPORTANT : par defaut NO_POST=True. La sonde ne brulera jamais de generation sans :
#   (a) cle reellement disponible
#   (b) MINIMAX_SPEND_QUOTA=1 (consentement explicite)
#   (c) plafond journalier verifie (5/jour sinon)

def probe_minimax_video(
    *,
    payload: dict,
    model: str = MINIMAX_VIDEO_MODEL,
    endpoint: str = MINIMAX_VIDEO_ENDPOINT,
    no_post: bool = True,
    timeout_s: float = 30.0,
) -> dict:
    """Sonde HTTP non-brulante vers /v1/video_generation (ou autre).

    Args:
        payload : dict de la requete (prompt, model_id, etc.)
        model   : id du modele (default video-01)
        endpoint: URL de l'endpoint (default /v1/video_generation)
        no_post : si True (defaut), N'ENVOIE PAS la requete. Retourne la forme structurelle pre-voyage + verdict attendu.
        timeout_s: timeout HTTP si no_post=False.

    Returns:
        dict avec cles : {"sent": bool, "endpoint": str, "model": str,
                          "predicted_status": int, "expected_body": dict,
                          "actual_status": int|None, "actual_body": dict|None,
                          "verdict": str}
    """
    if no_post:
        # Mode descriptif : predire le verdict sans POST
        return {
            "sent": False,
            "endpoint": endpoint,
            "model": model,
            "predicted_status": None,
            "expected_body": {"note": "no_post=True, cle non requise, generation non-debitee"},
            "actual_status": None,
            "actual_body": None,
            "verdict": "DESCRIPTIF_STUB",
        }

    # Mode generation reelle — gate strict
    if not (_KEY_AVAILABLE and MINIMAX_SPEND_QUOTA):
        return {
            "sent": False,
            "endpoint": endpoint,
            "model": model,
            "verdict": "BLOCKED_CLE_OU_QUOTA",
        }

    import httpx  # import paresseux pour eviter surcharge en mode descriptif
    headers = {
        "Authorization": f"Bearer {MINIMAX_GENAI_API_KEY}",
        "Content-Type": "application/json",
    }
    body = {**payload, "model": model}
    try:
        r = httpx.post(endpoint, headers=headers, json=body, timeout=timeout_s)
        actual_body = r.json() if r.headers.get("content-type", "").startswith("application/json") else {"raw": r.text[:200]}
        return {
            "sent": True,
            "endpoint": endpoint,
            "model": model,
            "actual_status": r.status_code,
            "actual_body": actual_body,
            "verdict": "OK" if r.status_code == 200 else f"HTTP_{r.status_code}",
        }
    except Exception as e:
        return {
            "sent": True,
            "endpoint": endpoint,
            "model": model,
            "actual_status": None,
            "actual_body": {"error": repr(e)},
            "verdict": f"EXC_{type(e).__name__}",
        }

Section 2 — Methodologie de probe (le « comment »)

Cinq regles enseignees par ce notebook :

  1. POST-diagnostic par (serie, endpoint) en tableau — pas un refus isole. Premier HTTP 200 = 1 generation debittee, plafond atteint. Tous les autres endpoints restes a 0 debit.
  2. /v1/models n’enumere QUE le texte — surface produit separee pour la video. Ne pas l’utiliser pour enumerer les candidats video.
  3. Secrets-hygiene regle 2/3 : os.getenv("MINIMAX_GENAI_API_KEY") SANS default literal. Si manquant : mode descriptif explicite (pas de « absent » maquille en cle partielle).
  4. Plafond=1 generation par session de probe — pas une regle provider, mais un garde-fou user (evite le burn de quota en cycle d’audit). Verdict YES/NO sur la premiere 200, pas apres.
  5. Refus scope serie × endpoint ≠ refus du service — extrapoler un seul 400 a l’ensemble du service est une erreur G.9 classique. Sonder les autres series + endpoints AVANT de remonter un verdict d’entitlement.
# Validation : mode descriptif par defaut ; generation reelle si cle + MINIMAX_SPEND_QUOTA=1
_spend_enabled = bool(MINIMAX_SPEND_QUOTA and _KEY_AVAILABLE)
result = probe_minimax_video(
    payload={"prompt": "a cat walking in a garden", "duration": 6, "resolution": "720p"},
    no_post=not _spend_enabled,  # generation reelle quand cle dispo + quota explicite (1 max)
)

import json as _json
print(_json.dumps(result, indent=2, ensure_ascii=False))

print()
print("=" * 70)
print(f"Verdict  : {result['verdict']}")
print(f"Sent     : {result['sent']}")
print(f"Endpoint : {result['endpoint']}")
print(f"Model    : {result['model']}")
print("=" * 70)
{
  "sent": true,
  "endpoint": "https://api.minimax.io/v1/video_generation",
  "model": "video-01",
  "actual_status": 200,
  "actual_body": {
    "task_id": "431137218855159",
    "base_resp": {
      "status_code": 0,
      "status_msg": "success"
    }
  },
  "verdict": "OK"
}

======================================================================
Verdict  : OK
Sent     : True
Endpoint : https://api.minimax.io/v1/video_generation
Model    : video-01
======================================================================

Lecture chiffree — le verdict OK atteste une generation reelle, pas une extrapolation. Le JSON commite rend "actual_status": 200, un task_id "431137218855159", "status_code": 0 et "verdict": "OK" : la sonde a debite exactement une generation sur le quota journalier consenti — la cle etait disponible au setup et le parametre de depense leve la garde no_post. La section precedente decrit le regime sans cle (verdict structurel, rien de debite) ; cette sortie-ci est l’autre regime, execute : le verdict est firsthand, c’est la reponse du service, pas une lecture de sa documentation.

Interpretation — le verdict DESCRIPTIF_STUB est une feature, pas un echec

Tant que MINIMAX_GENAI_API_KEY n’est pas livree, le notebook reste execut-able SANS cle et rend un verdict structurel (DESCRIPTIF_STUB) sur la forme des requetes/responses. C’est la meme posture cle que 04-5 : key-gated skeleton.

Trois garanties portees par ce mode descriptif :

  • 0 generation reelle debit — la sonde ne POST jamais sans no_post=False ET cle ET quota=1. Plafond=1 du cycle 196 preserve.
  • Verdict structurel reproductible — la sortie de la sonde est deterministe (meme payload, meme verdict). Permet de tester les exercices sans dependre d’un service externe.
  • Migration vers mode generation triviale : passer MINIMAX_SPEND_QUOTA=1, livrer MINIMAX_GENAI_API_KEY, appeler probe_minimax_video(payload=..., no_post=False). La sonde bascule en mode reel sans autre changement de code.

Section 3 — Exercices

Exercice 1 — Predire le verdict d’une sonde sur une serie/endpoint inconnus

Objectif : ecrire une fonction predict_verdict(series, endpoint) qui prend une serie + endpoint et renvoie le verdict attendu d’apres la table Section 1. La fonction doit gerer les 6 combinaisons de la table et rejeter toute combinaison hors-table par un verdict INCONNU.

Signature :

def predict_verdict(series: str, endpoint: str) -> str:
    # votre implementation ici
    pass

Hint : utiliser un dict littera { (serie, endpoint): verdict } ou un set de combinaisons valides.

def predict_verdict(series: str, endpoint: str) -> str:
    """A partir de la table Section 1, renvoie le verdict d'une combinaison serie/endpoint.

    Cas a supporter :
      - ("MiniMax-H3", "/v2/video_generation")             -> "TokenPlan-blocked (2013)"
      - ("MiniMax-Hailuo-02", "/v2/video_generation")     -> "endpoint H3-only"
      - ("video-01", "/v2/video_generation")               -> "endpoint H3-only"
      - ("Hailuo-01", "/v2/video_generation")              -> "endpoint H3-only"
      - ("Text-to-Video", "/v2/video_generation")         -> "endpoint H3-only"
      - ("video-01", "/v1/video_generation")               -> "PASS (plan couvre)"
      - autre                                              -> "INCONNU"

    Args:
        series  : nom de la serie (ex. "video-01")
        endpoint: chemin de l'endpoint (ex. "/v1/video_generation")

    Returns:
        str : verdict textuel court.
    """
    # TODO etudiant : implementer la table de verdit et la logique de lookup.
    return None  # placeholder

Exercice 2 — Compteur de debit journalier (anti-burn-quota)

Objectif : ecrire une fonction daily_debit_count(log_path) qui parse un fichier de log ligne-par-ligne (format timestamp|status|model|task_id) et renvoie le nombre de generations reussies (status=200) sur la journee courante (UTC).

Application : c’est exactement le garde-fou qui empeche de bruler le quota 5/jour du provider en cycle d’audit — verifier AVANT chaque POST reel.

Hint : utiliser datetime.now(timezone.utc).date() pour comparer au timestamp de chaque ligne.

def daily_debit_count(log_path: Path) -> int:
    """Compte les generations reussies (status=200) sur la journee courante (UTC).

    Format attendu par ligne : "timestamp|status|model|task_id"
    Exemple : '2026-08-10T17:46:05Z|200|video-01|429397735923998'

    Args:
        log_path : chemin du fichier de log (une ligne par requete).

    Returns:
        int : nombre de generations reussies aujourd'hui (UTC).
    """
    # TODO etudiant : lire le fichier, parser chaque ligne, filter status=200
    # ET meme journee UTC, compter.
    return None  # placeholder

Exercice 3 — Tableau comparatif video-01/v1 vs H3/v2 (verdict SOTA consolide)

Objectif : construire un tableau comparatif synthetique des deux chemins cloud documentes dans le depot (ce notebook + 04-5) avec les colonnes : (endpoint, modele, audio_natif, resolution_max, licence_ue, depense_par_5gen, verdict_entitlement). Renvoyer une list[dict].

Application : c’est le recapitulatif qu’attend un lecteur choisit sa voie : si l’audio natif est requis → H3/v2 avec licence UE bloquee dans certains territoires ; sinon → video-01/v1 plan-couvert, video muet suffit. Le choix depend du cas d’usage.

def build_cloud_video_comparison_table() -> list:
    """Construit le tableau comparatif video-01/v1 vs H3/v2 (chemins cloud MiniMax documentes).

    Returns:
        list[dict] avec cles : endpoint, modele, audio_natif, resolution_max,
                               licence_ue, depense_par_5gen, verdict_entitlement.
    """
    # TODO etudiant : remplir les 2 lignes (video-01/v1 + H3/v2) avec les valeurs
    # connues de la probe cycle 196.
    return None  # placeholder

Section 4 — Verdict SOTA consolide (cross-series)

Notebook Endpoint Modele Verdict Audio UE-licence Cout indicatif
CogVideoX-2b local local (RTX 3090) THUDM/CogVideoX-2b SOTA-OK muet Apache-2.0 0 (cout GPU local)
04-5 MiniMax-H3/v2 cloud /v2/video_generation MiniMax-H3 RECOVERABLE-USER-HAND (serie H3 bloquee 2013) oui (HD/2K + audio natif synchronise) UE-locked (Community License cession) ~5 generations / 5j
04-5b MiniMax-video-01/v1 (ce notebook) /v1/video_generation video-01 SOTA-OK (plan-couvert) muet UE (Pas d’exclusion territoriale trouvee dans les 3 instruments lus) identique 04-5
04-4 Production Video Pipeline orchestrateur local multiplexe chaisage selon briques selon briques selon briques

Verdict concentre :

  • Pour la video sans audio en UE : video-01/v1 est la voie cloud immediatement disponible avec le plan actuel (plafond 5 generations / jour, plafond respecte a 1 dans cette serie).
  • Pour la video + audio natif : seul H3/v2 le permet (HD/2K + audio stereo). H3 est bloque par 2013 TokenPlan dans cette org — escalade RECOVERABLE-USER-HAND necessaire si le use-case exige l’audio.
  • Pour la video offline / pas de cle API / GPU disponible : 02-7 CogVideoX-2b local (Apache-2.0).

Lecon G.9 retenons : extrapoler un refus isole a l’ensemble d’un service est une erreur frequente. Sonder serie × endpoint en tableau, pas par un seul POST.


Navigation : << 04-5 H3/v2 cloud | ↑ Video Applications | ↑ Video Series

MiniMax video-01 (v1) — Generation video par service cloud /v1/video_generation, modele video-01 (Hailuo line, muet), plan-couvert UE.

Retour au sommet