Video-Text-to-Text
Transformers
Safetensors
qwen3_5
image-text-to-text
robotics
reward-model
vlm
gr00t
system-2
Instructions to use nvidia/Flora-Rewarder with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use nvidia/Flora-Rewarder with Transformers:
# pip install -U transformers accelerate # Load model directly from transformers import AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("nvidia/Flora-Rewarder") model = AutoModelForMultimodalLM.from_pretrained("nvidia/Flora-Rewarder", device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 6,459 Bytes
79dd27b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | """Shared command-line plumbing and the offline vLLM engine."""
from __future__ import annotations
import argparse
import json
import os
from pathlib import Path
import re
import sys
from typing import Any, Iterable
# Without --model: $FLORA_REWARDER_REPO, else the downloaded copy of the repository that holds
# batch_utils/ (absolute, not resolved: Hub cache files are symlinks).
MODEL_ENV = "FLORA_REWARDER_REPO"
REPOSITORY_COPY = Path(__file__).absolute().parent.parent
# Pixel budget for collage clips (terminal and dense): at least ``shortest_edge`` and at most
# ``longest_edge`` pixels over all frames of one clip. Collages are planned inside this budget,
# so frames reach the model at their planned size.
# Configuration and tokenizer files: enough to render prompts for --dry-run.
PROCESSOR_FILES = ["*.json", "*.jinja", "*.txt"]
COLLAGE_VIDEO_SIZE = {"shortest_edge": 4096, "longest_edge": 29_360_128}
def default_model() -> str | None:
if os.environ.get(MODEL_ENV):
return os.environ[MODEL_ENV]
if (REPOSITORY_COPY / "model.safetensors.index.json").is_file():
return str(REPOSITORY_COPY)
return None
def base_parser(description: str, *, tensor_parallel_size: int = 1) -> argparse.ArgumentParser:
p = argparse.ArgumentParser(description=description)
source = p.add_mutually_exclusive_group(required=True)
source.add_argument("--jobs", type=Path, help="JSONL file, one episode per line (paths relative to the file).")
source.add_argument("--video", type=Path, nargs="+", help="Camera video(s) of one episode, in order.")
p.add_argument("--task", help="Task instruction for --video.")
p.add_argument("--output", type=Path, required=True, help="Output JSONL file.")
p.add_argument("--overwrite", action="store_true", help="Replace an existing output file.")
p.add_argument("--model", default=default_model(),
help=f"Hub repository id or local directory (default: ${MODEL_ENV}, else this repository's copy).")
p.add_argument("--revision", default=None, help="Hub revision (branch, tag or commit).")
p.add_argument("--tensor-parallel-size", type=int, default=tensor_parallel_size)
p.add_argument("--gpu-memory-utilization", type=float, default=0.88)
p.add_argument("--max-num-seqs", type=int, default=8, help="Concurrent requests in the engine.")
p.add_argument("--seed", type=int, default=0)
p.add_argument("--dry-run", action="store_true", help="Read the videos and print the first prompt; no model.")
return p
def read_jobs(args: argparse.Namespace, extra_fields: Iterable[str] = ()) -> list[dict[str, Any]]:
"""Jobs from ``--jobs`` or from ``--video``/``--task``; video paths become absolute."""
if args.video:
if not args.task:
raise SystemExit("--task is required with --video")
return [{"id": "episode", "task": args.task, "videos": [str(p.expanduser().resolve()) for p in args.video]}]
root = args.jobs.expanduser().resolve().parent
allowed = {"id", "task", "videos", *extra_fields}
jobs, ids = [], set()
for number, line in enumerate(args.jobs.read_text().splitlines(), 1):
if not line.strip():
continue
job = json.loads(line)
if not isinstance(job, dict) or set(job) - allowed or not {"task", "videos"} <= set(job):
raise SystemExit(f"{args.jobs}:{number}: expected keys {sorted(allowed)}")
job.setdefault("id", f"episode_{number:06d}")
if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.-]{0,127}", str(job["id"])) or job["id"] in ids:
raise SystemExit(f"{args.jobs}:{number}: invalid or duplicate id {job['id']!r}")
ids.add(job["id"])
if not isinstance(job["videos"], list) or not job["videos"]:
raise SystemExit(f"{args.jobs}:{number}: 'videos' must be a non-empty list")
job["videos"] = [str((root / Path(v).expanduser()).resolve()) for v in job["videos"]]
jobs.append(job)
if not jobs:
raise SystemExit(f"{args.jobs} has no jobs")
return jobs
def open_output(args: argparse.Namespace):
if args.output.exists() and not args.overwrite:
raise SystemExit(f"{args.output} exists; pass --overwrite to replace it")
args.output.parent.mkdir(parents=True, exist_ok=True)
return args.output.open("w")
def write_record(stream, record: dict[str, Any]) -> None:
stream.write(json.dumps(record, ensure_ascii=False) + "\n")
stream.flush()
def resolve_model(model: str | None, revision: str | None = None, *, weights: bool = True) -> str:
"""A local directory with the model; ``weights=False`` fetches only the processor files."""
if not model:
raise SystemExit(f"Pass --model (a Hub repository id or a local directory) or set {MODEL_ENV}.")
path = Path(model).expanduser()
if path.is_dir():
return str(path.resolve())
from huggingface_hub import snapshot_download
return snapshot_download(model, revision=revision, allow_patterns=None if weights else PROCESSOR_FILES)
def load_processor(path: str):
from transformers import AutoProcessor
return AutoProcessor.from_pretrained(path)
def load_llm(
path: str,
args: argparse.Namespace,
*,
max_model_len: int,
videos_per_prompt: int = 1,
mm_processor_kwargs: dict[str, Any] | None = None,
**engine_kwargs: Any,
):
os.environ.setdefault("TOKENIZERS_PARALLELISM", "false")
from vllm import LLM
return LLM(
model=path,
tensor_parallel_size=args.tensor_parallel_size,
dtype="bfloat16",
gpu_memory_utilization=args.gpu_memory_utilization,
max_model_len=max_model_len,
max_num_seqs=args.max_num_seqs,
limit_mm_per_prompt={"image": 0, "video": videos_per_prompt},
mm_processor_kwargs=mm_processor_kwargs,
# Triton prefill for the linear-attention layers: the configuration the reported
# results were measured with.
gdn_prefill_backend="triton",
enable_prefix_caching=True,
seed=args.seed,
**engine_kwargs,
)
def render(processor, messages, *, thinking: bool, vision_ids: bool = False) -> str:
return processor.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
enable_thinking=thinking,
add_vision_id=vision_ids,
)
def log(message: str) -> None:
print(message, file=sys.stderr, flush=True)
|