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-9B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use OraRL/Video-ORA-9B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="OraRL/Video-ORA-9B") 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-9B") model = AutoModelForMultimodalLM.from_pretrained("OraRL/Video-ORA-9B", 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-9B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "OraRL/Video-ORA-9B" # 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-9B", "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-9B
- SGLang
How to use OraRL/Video-ORA-9B 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-9B" \ --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-9B", "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-9B" \ --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-9B", "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-9B with Docker Model Runner:
docker model run hf.co/OraRL/Video-ORA-9B
| # ============================================================================= | |
| # TimeLens-Bench Evaluation — HuggingFace inference (multi-GPU data parallel) | |
| # | |
| # Usage: | |
| # bash eval/run_eval.sh [MODEL_PATH] [ENABLE_THINKING] [OUTPUT_DIR] | |
| # | |
| # Examples: | |
| # # Base model evaluation | |
| # bash eval/run_eval.sh /path/to/Qwen3.5-9B | |
| # | |
| # # Trained checkpoint evaluation | |
| # bash eval/run_eval.sh /path/to/checkpoint/huggingface | |
| # | |
| # # With thinking mode | |
| # bash eval/run_eval.sh /path/to/model true | |
| # ============================================================================= | |
| set -e | |
| SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" | |
| # eval/task/temporal_grounding/run_eval.sh → repository root (up 3 levels) | |
| PROJECT_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)" | |
| # ---------- paths ---------- | |
| MODEL_PATH="${1:-}" | |
| ENABLE_THINKING="${2:-false}" | |
| BENCH_DIR="${TIMELENS_BENCH_DIR:-${PROJECT_DIR}/data/eval/temporal_grounding}" | |
| if [[ -z "$MODEL_PATH" || ! -d "$MODEL_PATH" ]]; then | |
| echo "ERROR: MODEL_PATH must name an existing model directory." >&2 | |
| exit 2 | |
| fi | |
| if [[ ! -d "$BENCH_DIR" ]]; then | |
| echo "ERROR: TIMELENS_BENCH_DIR does not exist: $BENCH_DIR" >&2 | |
| exit 2 | |
| fi | |
| # ---------- eval settings ---------- | |
| DATASETS="${DATASETS:-charades-timelens}" | |
| SETTING="${SETTING:-timelens-fps${FPS:-4}-min${MIN_TOKENS:-1}-total${TOTAL_TOKENS:-128000}-new${MAX_NEW_TOKENS:-128}-${DATASETS//,/_}}" | |
| MIN_TOKENS="${MIN_TOKENS:-1}" | |
| TOTAL_TOKENS="${TOTAL_TOKENS:-128000}" | |
| MAX_PIXELS="${MAX_PIXELS:-409600}" | |
| MAX_FRAMES="${MAX_FRAMES:-2048}" | |
| FPS="${FPS:-4}" | |
| MAX_NEW_TOKENS="${MAX_NEW_TOKENS:-512}" | |
| REPETITION_PENALTY="${REPETITION_PENALTY:-1.0}" | |
| STOP_AFTER_ANSWER="${STOP_AFTER_ANSWER:-true}" | |
| PROMPT_MODE="${PROMPT_MODE:-same}" | |
| NUM_WORKERS="${NUM_WORKERS:-2}" | |
| MAX_SAMPLES="${TIMELENS_MAX_SAMPLES:-0}" | |
| RESUME="${TIMELENS_RESUME:-1}" | |
| MERGE_SHARDS="${TIMELENS_MERGE_SHARDS:-1}" | |
| if ! [[ "$MAX_SAMPLES" =~ ^[0-9]+$ ]]; then | |
| echo "ERROR: TIMELENS_MAX_SAMPLES must be a non-negative integer." >&2 | |
| exit 1 | |
| fi | |
| # ---------- output dir layout ---------- | |
| # Default layout: | |
| # <OUTPUT_ROOT>/<DATASET>/<RUN_TAG>/ | |
| # | |
| # If TIMELENS_USE_OUTPUT_ROOT_DIRECT=1, OUTPUT_ROOT is assumed to already be | |
| # the full run directory and each dataset is written under: | |
| # <OUTPUT_ROOT>/<DATASET>/ | |
| MODEL_TAG=$(basename "${MODEL_PATH%/}") | |
| TIMESTAMP=$(date +%Y%m%d_%H%M%S) | |
| OUTPUT_ROOT="${3:-${OUTPUT_ROOT:-${PROJECT_DIR}/outputs/temporal_grounding}}" | |
| RUN_TAG="eval_hf-${MODEL_TAG}-${TIMESTAMP}" | |
| if [ "$ENABLE_THINKING" = "true" ]; then | |
| RUN_TAG="${RUN_TAG}-think" | |
| fi | |
| if [ "${NO_ANSWER_WRAP:-0}" = "1" ]; then | |
| RUN_TAG="${RUN_TAG}-naw" | |
| fi | |
| # ---------- hardware ---------- | |
| # PyTorch wheels bundle CUDA user-space libraries. Prefer those libraries so a | |
| # cluster-wide CUDA toolkit cannot override them with an older nvJitLink ABI. | |
| if [ "${TIMELENS_USE_SYSTEM_CUDA:-0}" = "1" ]; then | |
| if [ -z "${CUDA_HOME:-}" ] || [ ! -d "$CUDA_HOME" ]; then | |
| echo "ERROR: TIMELENS_USE_SYSTEM_CUDA=1 requires a valid CUDA_HOME." >&2 | |
| exit 1 | |
| fi | |
| export CUDA_PATH="${CUDA_PATH:-$CUDA_HOME}" | |
| export PATH="$CUDA_HOME/bin:$PATH" | |
| export LD_LIBRARY_PATH="$CUDA_HOME/lib64:${LD_LIBRARY_PATH:-}" | |
| else | |
| PYTHON_NVIDIA_LIBS="$(python - <<'PY' | |
| from pathlib import Path | |
| import sysconfig | |
| root = Path(sysconfig.get_path("purelib")) / "nvidia" | |
| print(":".join(str(path) for path in sorted(root.glob("*/lib")) if path.is_dir())) | |
| PY | |
| )" | |
| PYTHON_RUNTIME_LIBS="$PYTHON_NVIDIA_LIBS" | |
| if [ -n "${CONDA_PREFIX:-}" ] && [ -d "$CONDA_PREFIX/lib" ]; then | |
| PYTHON_RUNTIME_LIBS="${PYTHON_RUNTIME_LIBS:+$PYTHON_RUNTIME_LIBS:}$CONDA_PREFIX/lib" | |
| fi | |
| if [ -n "$PYTHON_RUNTIME_LIBS" ]; then | |
| export LD_LIBRARY_PATH="$PYTHON_RUNTIME_LIBS${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" | |
| fi | |
| fi | |
| IFS="," read -ra GPULIST <<< "${CUDA_VISIBLE_DEVICES:-$(seq -s, 0 $(($(nvidia-smi -L | wc -l)-1)))}" | |
| NUM_GPUS=${#GPULIST[@]} | |
| NUM_SHARDS="${TIMELENS_GLOBAL_SHARD_COUNT:-${TIMELENS_NUM_SHARDS:-$NUM_GPUS}}" | |
| LOCAL_SHARDS="${TIMELENS_LOCAL_SHARD_COUNT:-$NUM_SHARDS}" | |
| SHARD_OFFSET="${TIMELENS_GLOBAL_SHARD_OFFSET:-0}" | |
| if ! [[ "$NUM_SHARDS" =~ ^[0-9]+$ ]] || [ "$NUM_SHARDS" -lt 1 ]; then | |
| echo "ERROR: TIMELENS_NUM_SHARDS must be a positive integer, got '$NUM_SHARDS'" >&2 | |
| exit 1 | |
| fi | |
| if ! [[ "$LOCAL_SHARDS" =~ ^[0-9]+$ ]] || [ "$LOCAL_SHARDS" -lt 1 ]; then | |
| echo "ERROR: TIMELENS_LOCAL_SHARD_COUNT must be positive, got '$LOCAL_SHARDS'" >&2 | |
| exit 1 | |
| fi | |
| if ! [[ "$SHARD_OFFSET" =~ ^[0-9]+$ ]] || \ | |
| [ $((SHARD_OFFSET + LOCAL_SHARDS)) -gt "$NUM_SHARDS" ]; then | |
| echo "ERROR: invalid shard range offset=$SHARD_OFFSET count=$LOCAL_SHARDS global=$NUM_SHARDS" >&2 | |
| exit 1 | |
| fi | |
| for GPU_ID in "${GPULIST[@]}"; do | |
| if [[ ! "$GPU_ID" =~ ^[0-9]+$ ]]; then | |
| echo "ERROR: CUDA_VISIBLE_DEVICES must contain comma-separated GPU ids, got '${CUDA_VISIBLE_DEVICES:-<unset>}'" >&2 | |
| exit 1 | |
| fi | |
| done | |
| echo "==============================================" | |
| echo "TimeLens-Bench Evaluation (HuggingFace)" | |
| echo "==============================================" | |
| echo "Model: $MODEL_PATH" | |
| echo "Thinking: $ENABLE_THINKING" | |
| echo "Prompt mode:$PROMPT_MODE" | |
| echo "NoAnswerWrap: ${NO_ANSWER_WRAP:-0}" | |
| echo "Datasets: $DATASETS" | |
| echo "GPUs: ${GPULIST[*]} (${NUM_GPUS} total)" | |
| echo "Shards: global=$NUM_SHARDS local=$LOCAL_SHARDS offset=$SHARD_OFFSET" | |
| echo "Tokens: min=$MIN_TOKENS, total=$TOTAL_TOKENS" | |
| echo "Video: max_pixels=$MAX_PIXELS, max_frames=$MAX_FRAMES" | |
| echo "FPS: $FPS" | |
| echo "Decode: max_new=$MAX_NEW_TOKENS, repetition_penalty=$REPETITION_PENALTY, stop_after_answer=$STOP_AFTER_ANSWER" | |
| echo "Max samples:$MAX_SAMPLES per shard (0 means all)" | |
| echo "Output root:$OUTPUT_ROOT" | |
| echo "Run tag: $RUN_TAG" | |
| echo "==============================================" | |
| mkdir -p "$OUTPUT_ROOT" | |
| # ---------- cleanup handler ---------- | |
| PIDS=() | |
| cleanup() { | |
| echo "" | |
| echo "Caught interrupt, killing all workers ..." | |
| for pid in "${PIDS[@]}"; do | |
| kill -TERM "$pid" 2>/dev/null || true | |
| done | |
| wait 2>/dev/null | |
| echo "All workers killed." | |
| exit 1 | |
| } | |
| trap cleanup INT TERM | |
| # ---------- run evaluation for each dataset ---------- | |
| IFS=',' read -ra DATASET_LIST <<< "$DATASETS" | |
| for DATASET in "${DATASET_LIST[@]}"; do | |
| if [ "${TIMELENS_USE_OUTPUT_ROOT_DIRECT:-0}" = "1" ]; then | |
| OUTPUT_DIR="${OUTPUT_ROOT}/${DATASET}" | |
| else | |
| OUTPUT_DIR="${OUTPUT_ROOT}/${DATASET}/${RUN_TAG}" | |
| fi | |
| mkdir -p "$OUTPUT_DIR" | |
| if [ "$SHARD_OFFSET" -eq 0 ]; then | |
| SETTING="$SETTING" DATASET="$DATASET" OUTPUT_DIR="$OUTPUT_DIR" MODEL_PATH="$MODEL_PATH" \ | |
| PROJECT_DIR="$PROJECT_DIR" \ | |
| ENABLE_THINKING="$ENABLE_THINKING" BENCH_DIR="$BENCH_DIR" GPUS="${GPULIST[*]}" \ | |
| NUM_GPUS="$NUM_GPUS" NUM_SHARDS="$NUM_SHARDS" LOCAL_SHARDS="$LOCAL_SHARDS" \ | |
| SHARD_OFFSET="$SHARD_OFFSET" MIN_TOKENS="$MIN_TOKENS" TOTAL_TOKENS="$TOTAL_TOKENS" \ | |
| MAX_PIXELS="$MAX_PIXELS" MAX_FRAMES="$MAX_FRAMES" FPS="$FPS" \ | |
| MAX_NEW_TOKENS="$MAX_NEW_TOKENS" REPETITION_PENALTY="$REPETITION_PENALTY" \ | |
| STOP_AFTER_ANSWER="$STOP_AFTER_ANSWER" PROMPT_MODE="$PROMPT_MODE" \ | |
| NUM_WORKERS="$NUM_WORKERS" MAX_SAMPLES="$MAX_SAMPLES" \ | |
| python - <<'PY' | |
| import json | |
| import os | |
| import sys | |
| project_dir = os.environ["PROJECT_DIR"] | |
| sys.path.insert(0, os.path.join(project_dir, "eval", "task")) | |
| try: | |
| from eval_prompt import PROMPT_WO_THINK, TIMELENS_OFFICIAL_PROMPT | |
| except Exception: | |
| PROMPT_WO_THINK = None | |
| TIMELENS_OFFICIAL_PROMPT = None | |
| prompt_mode = os.environ["PROMPT_MODE"] | |
| prompt_template = ( | |
| TIMELENS_OFFICIAL_PROMPT | |
| if prompt_mode == "timelens_official" | |
| else PROMPT_WO_THINK | |
| ) | |
| config = { | |
| "task": "temporal_grounding", | |
| "setting": os.environ["SETTING"], | |
| "model_path": os.environ["MODEL_PATH"], | |
| "dataset": os.environ["DATASET"], | |
| "bench_dir": os.environ["BENCH_DIR"], | |
| "output_dir": os.environ["OUTPUT_DIR"], | |
| "enable_thinking": os.environ["ENABLE_THINKING"], | |
| "gpus": os.environ["GPUS"].split(), | |
| "num_gpus": int(os.environ["NUM_GPUS"]), | |
| "video": { | |
| "min_tokens": int(os.environ["MIN_TOKENS"]), | |
| "total_tokens": int(os.environ["TOTAL_TOKENS"]), | |
| "max_pixels": int(os.environ["MAX_PIXELS"]), | |
| "max_frames": int(os.environ["MAX_FRAMES"]), | |
| "fps": float(os.environ["FPS"]), | |
| }, | |
| "decode": { | |
| "max_new_tokens": int(os.environ["MAX_NEW_TOKENS"]), | |
| "repetition_penalty": float(os.environ["REPETITION_PENALTY"]), | |
| "stop_after_answer": os.environ["STOP_AFTER_ANSWER"], | |
| }, | |
| "runtime": { | |
| "num_shards": int(os.environ["NUM_SHARDS"]), | |
| "local_shards": int(os.environ["LOCAL_SHARDS"]), | |
| "shard_offset": int(os.environ["SHARD_OFFSET"]), | |
| "num_workers": int(os.environ["NUM_WORKERS"]), | |
| "max_samples_per_shard": int(os.environ["MAX_SAMPLES"]), | |
| }, | |
| "prompt": { | |
| "mode": prompt_mode, | |
| "source": ( | |
| "eval/task/eval_prompt.py:TIMELENS_OFFICIAL_PROMPT" | |
| if prompt_mode == "timelens_official" | |
| else "eval/task/eval_prompt.py:PROMPT_WO_THINK" | |
| ), | |
| "template": prompt_template, | |
| }, | |
| } | |
| with open(os.path.join(os.environ["OUTPUT_DIR"], "run_config.json"), "w", encoding="utf-8") as f: | |
| json.dump(config, f, ensure_ascii=False, indent=2) | |
| PY | |
| fi | |
| echo "" | |
| echo ">>> Evaluating: $DATASET" | |
| echo " Output: $OUTPUT_DIR" | |
| PIDS=() | |
| SHARD_IDS=() | |
| for LOCAL_IDX in $(seq 0 $((LOCAL_SHARDS - 1))); do | |
| IDX=$((SHARD_OFFSET + LOCAL_IDX)) | |
| RESULT_PATH="$OUTPUT_DIR/results_${DATASET}_shard${IDX}.json" | |
| SUMMARY_PATH="$OUTPUT_DIR/summary_shard${IDX}.json" | |
| if [ "$RESUME" = "1" ] && [ -s "$RESULT_PATH" ] && [ -s "$SUMMARY_PATH" ]; then | |
| echo " [resume] Skipping completed shard $IDX/$NUM_SHARDS" | |
| continue | |
| fi | |
| GPU_ID="${GPULIST[$((LOCAL_IDX % NUM_GPUS))]}" | |
| ( | |
| export CUDA_VISIBLE_DEVICES=$GPU_ID | |
| export PYTHONUNBUFFERED=1 | |
| python - <<'PY' | |
| import os | |
| import torch | |
| print(f"Worker CUDA_VISIBLE_DEVICES: {os.environ.get('CUDA_VISIBLE_DEVICES')}", flush=True) | |
| print(f"Worker torch: {torch.__version__}", flush=True) | |
| print(f"Worker torch cuda: {torch.version.cuda}", flush=True) | |
| print(f"Worker cuda available: {torch.cuda.is_available()}", flush=True) | |
| print(f"Worker device count: {torch.cuda.device_count()}", flush=True) | |
| PY | |
| python "${SCRIPT_DIR}/eval_timelens_hf.py" \ | |
| --model_path "$MODEL_PATH" \ | |
| --bench_dir "$BENCH_DIR" \ | |
| --dataset "$DATASET" \ | |
| --output_dir "$OUTPUT_DIR" \ | |
| --enable_thinking "$ENABLE_THINKING" \ | |
| --prompt_mode "$PROMPT_MODE" \ | |
| --min_tokens $MIN_TOKENS \ | |
| --total_tokens $TOTAL_TOKENS \ | |
| --max_pixels $MAX_PIXELS \ | |
| --max_frames $MAX_FRAMES \ | |
| --fps $FPS \ | |
| --max_new_tokens $MAX_NEW_TOKENS \ | |
| --repetition_penalty $REPETITION_PENALTY \ | |
| --stop_after_answer "$STOP_AFTER_ANSWER" \ | |
| --num_workers $NUM_WORKERS \ | |
| --max_samples $MAX_SAMPLES \ | |
| --chunk $NUM_SHARDS \ | |
| --index $IDX | |
| ) > "$OUTPUT_DIR/worker_${DATASET}_${IDX}.log" 2>&1 & | |
| PIDS+=($!) | |
| SHARD_IDS+=($IDX) | |
| echo " Launched shard $IDX on GPU $GPU_ID (PID ${PIDS[-1]})" | |
| done | |
| echo " Waiting for ${#PIDS[@]} local workers ..." | |
| FAILED=0 | |
| for i in "${!PIDS[@]}"; do | |
| if wait "${PIDS[$i]}"; then | |
| echo " [DONE] shard ${SHARD_IDS[$i]} (PID ${PIDS[$i]})" | |
| else | |
| RC=$? | |
| echo " [FAIL] shard ${SHARD_IDS[$i]} (PID ${PIDS[$i]}) exited with code $RC" | |
| FAILED=1 | |
| fi | |
| done | |
| if [ $FAILED -ne 0 ]; then | |
| echo " Some workers failed. Check logs: $OUTPUT_DIR/worker_${DATASET}_*.log" | |
| exit 1 | |
| fi | |
| if [ "$MERGE_SHARDS" != "1" ]; then | |
| echo " Local shard range completed; centralized merge disabled." | |
| continue | |
| fi | |
| # ---------- merge shard results ---------- | |
| echo " Merging results ..." | |
| python -c " | |
| import json, os, sys | |
| output_dir = sys.argv[1] | |
| dataset = sys.argv[2] | |
| num_shards = int(sys.argv[3]) | |
| all_samples = [] | |
| for sid in range(num_shards): | |
| path = os.path.join(output_dir, f'results_{dataset}_shard{sid}.json') | |
| if os.path.isfile(path): | |
| with open(path) as f: | |
| all_samples.extend(json.load(f)) | |
| n = len(all_samples) | |
| if n == 0: | |
| print(f' No results for {dataset}') | |
| sys.exit(0) | |
| ious = [s['iou'] for s in all_samples] | |
| thresholds = [0.3, 0.5, 0.7] | |
| metrics = { | |
| 'num_samples': n, | |
| 'mIoU': round(sum(ious) / n * 100, 2), | |
| } | |
| for t in thresholds: | |
| metrics[f'R@{t}'] = round(sum(1 for x in ious if x >= t) / n * 100, 2) | |
| n_parsed = sum(1 for s in all_samples if s.get('pred_span') is not None) | |
| metrics['parse_rate'] = round(n_parsed / n * 100, 2) | |
| print(f' {dataset}: n={n} mIoU={metrics[\"mIoU\"]:.2f}% R@0.3={metrics[\"R@0.3\"]:.2f}% R@0.5={metrics[\"R@0.5\"]:.2f}% R@0.7={metrics[\"R@0.7\"]:.2f}% Parse={metrics[\"parse_rate\"]:.2f}%') | |
| # Save merged results | |
| merged_path = os.path.join(output_dir, f'results_{dataset}.json') | |
| with open(merged_path, 'w') as f: | |
| json.dump(all_samples, f, ensure_ascii=False, indent=2) | |
| # Save/update summary | |
| summary_path = os.path.join(output_dir, 'summary.json') | |
| summary = {} | |
| if os.path.isfile(summary_path): | |
| with open(summary_path) as f: | |
| summary = json.load(f) | |
| summary[dataset] = metrics | |
| with open(summary_path, 'w') as f: | |
| json.dump(summary, f, ensure_ascii=False, indent=2) | |
| # Cleanup shard files. Keep worker logs so per-sample generation traces | |
| # survive the merge; only the redundant shard JSONs (already folded into | |
| # results_{dataset}.json) are removed. Set TIMELENS_KEEP_SHARDS=1 to keep all. | |
| keep_shards = os.environ.get('TIMELENS_KEEP_SHARDS', '0') == '1' | |
| if not keep_shards: | |
| for sid in range(num_shards): | |
| for pattern in [f'results_{dataset}_shard{sid}.json', | |
| f'summary_shard{sid}.json']: | |
| p = os.path.join(output_dir, pattern) | |
| if os.path.isfile(p): | |
| os.remove(p) | |
| " "$OUTPUT_DIR" "$DATASET" "$NUM_SHARDS" | |
| done | |
| echo "" | |
| echo "==============================================" | |
| echo "All evaluations complete!" | |
| echo "Output root: $OUTPUT_ROOT" | |
| echo "Run tag: $RUN_TAG" | |
| echo "==============================================" | |
| # Print each dataset's summary, in order. | |
| for DATASET in "${DATASET_LIST[@]}"; do | |
| if [ "${TIMELENS_USE_OUTPUT_ROOT_DIRECT:-0}" = "1" ]; then | |
| SUMMARY="${OUTPUT_ROOT}/${DATASET}/summary.json" | |
| else | |
| SUMMARY="${OUTPUT_ROOT}/${DATASET}/${RUN_TAG}/summary.json" | |
| fi | |
| if [ -f "$SUMMARY" ]; then | |
| echo "" | |
| echo "[${DATASET}] -> $SUMMARY" | |
| cat "$SUMMARY" | |
| fi | |
| done | |
| echo "" | |