Image-Text-to-Text
Transformers
Safetensors
qwen3_5
vllm
video
multimodal
reinforcement-learning
temporal-grounding
object-tracking
video-segmentation
visual-question-answering
spatial-reasoning
qwen3.5
conversational
Instructions to use OraRL/Video-ORA-4B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use OraRL/Video-ORA-4B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="OraRL/Video-ORA-4B") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# Load model directly from transformers import AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("OraRL/Video-ORA-4B") model = AutoModelForMultimodalLM.from_pretrained("OraRL/Video-ORA-4B", device_map="auto") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] inputs = processor.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=40) print(processor.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use OraRL/Video-ORA-4B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "OraRL/Video-ORA-4B" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "OraRL/Video-ORA-4B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/OraRL/Video-ORA-4B
- SGLang
How to use OraRL/Video-ORA-4B with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "OraRL/Video-ORA-4B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "OraRL/Video-ORA-4B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "OraRL/Video-ORA-4B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "OraRL/Video-ORA-4B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use OraRL/Video-ORA-4B with Docker Model Runner:
docker model run hf.co/OraRL/Video-ORA-4B
| # Copyright 2024 Bytedance Ltd. and/or its affiliates | |
| # Copyright 2026 The OraRL Authors | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| """Strict sign-balanced rollout selection. | |
| The physical batch-slicing pattern is derived from the Apache-2.0 EasyR1/verl | |
| trainer. The selector here is limited to the OraRL paper recipe. | |
| """ | |
| from __future__ import annotations | |
| import math | |
| from typing import Any | |
| import torch | |
| from ._utils import ( | |
| boolean_mask, | |
| copy_and_refresh_meta, | |
| group_rows, | |
| sequence_advantages, | |
| ) | |
| from .config import SelectionConfig | |
| def _resolved_config( | |
| config: SelectionConfig | None, | |
| keep_per_group: int | None, | |
| positive_quota: int | None, | |
| negative_quota: int | None, | |
| world_size: int | None, | |
| n_rollouts: int | None, | |
| prune_ratio: float | None, | |
| ) -> SelectionConfig: | |
| base = SelectionConfig() if config is None else config | |
| if (n_rollouts is None) != (prune_ratio is None): | |
| raise ValueError("n_rollouts and prune_ratio must be provided together.") | |
| derived_keep: int | None = None | |
| if n_rollouts is not None and prune_ratio is not None: | |
| if n_rollouts <= 1: | |
| raise ValueError("n_rollouts must be greater than one.") | |
| if not math.isfinite(prune_ratio) or not 0.0 <= prune_ratio < 1.0: | |
| raise ValueError("prune_ratio must be finite and in [0, 1).") | |
| derived_keep = int(n_rollouts * (1.0 - prune_ratio)) | |
| if keep_per_group is not None and keep_per_group != derived_keep: | |
| raise ValueError("keep_per_group disagrees with the n_rollouts/prune_ratio budget.") | |
| resolved_keep = ( | |
| derived_keep | |
| if derived_keep is not None | |
| else base.keep_per_group | |
| if keep_per_group is None | |
| else keep_per_group | |
| ) | |
| return SelectionConfig( | |
| keep_per_group=resolved_keep, | |
| positive_quota=(base.positive_quota if positive_quota is None else positive_quota), | |
| negative_quota=(base.negative_quota if negative_quota is None else negative_quota), | |
| world_size=base.world_size if world_size is None else world_size, | |
| ) | |
| def _rank_by_magnitude(rows: list[int], scores: torch.Tensor) -> list[int]: | |
| return sorted( | |
| rows, | |
| key=lambda row: (-abs(float(scores[row].item())), row), | |
| ) | |
| def select_sign_balanced_rollouts( | |
| data: Any, | |
| keep_per_group: int | None = None, | |
| positive_quota: int | None = None, | |
| negative_quota: int | None = None, | |
| world_size: int | None = None, | |
| *, | |
| config: SelectionConfig | None = None, | |
| n_rollouts: int | None = None, | |
| prune_ratio: float | None = None, | |
| group_key: str = "uid", | |
| oracle_key: str = "is_oracle_row", | |
| ) -> tuple[Any, dict[str, float]]: | |
| """Select exactly one oracle plus strict positive/negative policy quotas. | |
| A short sign bucket is filled from the opposite sign by descending | |
| magnitude, followed by zero-advantage rows. The selected indices physically | |
| slice the DataProto-like object before the actor update. | |
| """ | |
| cfg = _resolved_config( | |
| config, | |
| keep_per_group, | |
| positive_quota, | |
| negative_quota, | |
| world_size, | |
| n_rollouts, | |
| prune_ratio, | |
| ) | |
| if "advantages" not in data.batch or "response_mask" not in data.batch: | |
| raise ValueError("selection requires advantages and response_mask.") | |
| if group_key not in data.non_tensor_batch: | |
| raise ValueError(f"selection requires non_tensor_batch[{group_key!r}].") | |
| if oracle_key not in data.non_tensor_batch: | |
| raise ValueError(f"selection requires non_tensor_batch[{oracle_key!r}].") | |
| advantages = data.batch["advantages"] | |
| response_mask = data.batch["response_mask"] | |
| signed_scores = sequence_advantages(advantages, response_mask) | |
| total_rows = signed_scores.numel() | |
| grouped = group_rows(data.non_tensor_batch[group_key], total_rows) | |
| oracle = boolean_mask( | |
| data.non_tensor_batch[oracle_key], | |
| total_rows, | |
| device=signed_scores.device, | |
| name=oracle_key, | |
| ) | |
| selected_rows: list[int] = [] | |
| positive_kept = 0 | |
| negative_kept = 0 | |
| zero_kept = 0 | |
| cross_sign_fallback = 0 | |
| zero_fallback = 0 | |
| for group_id, rows in grouped.items(): | |
| if len(rows) < cfg.keep_per_group: | |
| raise ValueError( | |
| f"group {group_id!r} has {len(rows)} rows, fewer than the " | |
| f"keep budget {cfg.keep_per_group}." | |
| ) | |
| oracle_rows = [row for row in rows if bool(oracle[row])] | |
| if len(oracle_rows) != 1: | |
| raise ValueError( | |
| f"group {group_id!r} requires exactly one oracle row, got {len(oracle_rows)}." | |
| ) | |
| oracle_row = oracle_rows[0] | |
| candidates = [row for row in rows if row != oracle_row] | |
| positive = _rank_by_magnitude( | |
| [row for row in candidates if float(signed_scores[row].item()) > 0.0], | |
| signed_scores, | |
| ) | |
| negative = _rank_by_magnitude( | |
| [row for row in candidates if float(signed_scores[row].item()) < 0.0], | |
| signed_scores, | |
| ) | |
| zeros = [row for row in candidates if float(signed_scores[row].item()) == 0.0] | |
| chosen = positive[: cfg.positive_quota] + negative[: cfg.negative_quota] | |
| chosen_set = set(chosen) | |
| policy_budget = cfg.keep_per_group - 1 | |
| remaining = policy_budget - len(chosen) | |
| if remaining > 0: | |
| opposite_sign_surplus = _rank_by_magnitude( | |
| [row for row in positive + negative if row not in chosen_set], | |
| signed_scores, | |
| ) | |
| cross_fill = opposite_sign_surplus[:remaining] | |
| chosen.extend(cross_fill) | |
| chosen_set.update(cross_fill) | |
| cross_sign_fallback += len(cross_fill) | |
| remaining -= len(cross_fill) | |
| if remaining > 0: | |
| zero_fill = [row for row in zeros if row not in chosen_set][:remaining] | |
| chosen.extend(zero_fill) | |
| chosen_set.update(zero_fill) | |
| zero_fallback += len(zero_fill) | |
| remaining -= len(zero_fill) | |
| if remaining > 0: | |
| final_fill = [row for row in candidates if row not in chosen_set][:remaining] | |
| chosen.extend(final_fill) | |
| remaining -= len(final_fill) | |
| if remaining != 0 or len(chosen) != policy_budget: | |
| raise RuntimeError(f"group {group_id!r} could not satisfy its keep budget.") | |
| for row in chosen: | |
| score = float(signed_scores[row].item()) | |
| positive_kept += int(score > 0.0) | |
| negative_kept += int(score < 0.0) | |
| zero_kept += int(score == 0.0) | |
| selected_rows.extend([oracle_row, *chosen]) | |
| selected_rows.sort() | |
| if len(selected_rows) % cfg.world_size != 0: | |
| raise RuntimeError( | |
| f"selected batch size {len(selected_rows)} is not divisible by " | |
| f"world_size {cfg.world_size}." | |
| ) | |
| selected = data[selected_rows] | |
| copy_and_refresh_meta(selected) | |
| selected_index = torch.tensor( | |
| selected_rows, | |
| dtype=torch.long, | |
| device=signed_scores.device, | |
| ) | |
| kept_abs = signed_scores.index_select(0, selected_index).abs() | |
| selected_set = set(selected_rows) | |
| dropped_rows = [row for row in range(total_rows) if row not in selected_set] | |
| metrics: dict[str, float] = { | |
| "orarl/selection_groups": float(len(grouped)), | |
| "orarl/keep_per_group": float(cfg.keep_per_group), | |
| "orarl/kept_rows": float(len(selected_rows)), | |
| "orarl/dropped_rows": float(total_rows - len(selected_rows)), | |
| "orarl/effective_keep_ratio": len(selected_rows) / float(total_rows), | |
| "orarl/oracle_rows_forced": float(len(grouped)), | |
| "orarl/positive_policy_rows_kept": float(positive_kept), | |
| "orarl/negative_policy_rows_kept": float(negative_kept), | |
| "orarl/zero_policy_rows_kept": float(zero_kept), | |
| "orarl/cross_sign_fallback_rows": float(cross_sign_fallback), | |
| "orarl/zero_fallback_rows": float(zero_fallback), | |
| "orarl/abs_advantage_kept_mean": float(kept_abs.mean().item()), | |
| } | |
| if prune_ratio is not None: | |
| metrics["orarl/requested_prune_ratio"] = prune_ratio | |
| if dropped_rows: | |
| dropped_index = torch.tensor( | |
| dropped_rows, | |
| dtype=torch.long, | |
| device=signed_scores.device, | |
| ) | |
| metrics["orarl/abs_advantage_dropped_mean"] = float( | |
| signed_scores.index_select(0, dropped_index).abs().mean().item() | |
| ) | |
| return selected, metrics | |