Image-Text-to-Text
Transformers
Safetensors
mage_vl
multimodal
vision-language-model
mage-vl
video-understanding
streaming
conversational
custom_code
Instructions to use Mage-Fans/Mage-VL with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Mage-Fans/Mage-VL with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="Mage-Fans/Mage-VL", trust_remote_code=True) 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 AutoModelForImageTextToText model = AutoModelForImageTextToText.from_pretrained("Mage-Fans/Mage-VL", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use Mage-Fans/Mage-VL with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Mage-Fans/Mage-VL" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Mage-Fans/Mage-VL", "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/Mage-Fans/Mage-VL
- SGLang
How to use Mage-Fans/Mage-VL 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 "Mage-Fans/Mage-VL" \ --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": "Mage-Fans/Mage-VL", "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 "Mage-Fans/Mage-VL" \ --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": "Mage-Fans/Mage-VL", "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 Mage-Fans/Mage-VL with Docker Model Runner:
docker model run hf.co/Mage-Fans/Mage-VL
| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| """Video probing utilities using ffprobe and OpenCV.""" | |
| import json | |
| import math | |
| import subprocess | |
| from typing import Tuple, List, Dict, Any | |
| import numpy as np | |
| import cv2 | |
| def get_total_frames_fps(video_path: str) -> Tuple[int, float, int, int]: | |
| """Get video metadata using OpenCV. | |
| Returns: (total_frames, fps, height, width) | |
| """ | |
| cap = cv2.VideoCapture(str(video_path)) | |
| if not cap.isOpened(): | |
| return 0, 0.0, 0, 0 | |
| total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) | |
| fps = float(cap.get(cv2.CAP_PROP_FPS)) | |
| w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) | |
| h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) | |
| cap.release() | |
| if not np.isfinite(fps): | |
| fps = 0.0 | |
| return max(0, total), max(0.0, fps), h, w | |
| def ffprobe_video_codec_name(video_path: str) -> str: | |
| """Best-effort fetch codec_name of the first video stream via ffprobe.""" | |
| try: | |
| cmd = [ | |
| "ffprobe", | |
| "-v", "error", | |
| "-select_streams", "v:0", | |
| "-show_entries", "stream=codec_name", | |
| "-of", "default=noprint_wrappers=1:nokey=1", | |
| str(video_path), | |
| ] | |
| p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) | |
| if p.returncode != 0: | |
| return "" | |
| return str((p.stdout or "").strip()).lower() | |
| except Exception: | |
| return "" | |
| def auto_max_total_patches( | |
| S_full: int, | |
| total_frames: int, | |
| fps: float, | |
| cap_total: int = 30000, | |
| ) -> Tuple[int, Dict[str, Any]]: | |
| """Choose max_total_patches (multiple of S_full) under cap_total. | |
| Heuristic: | |
| - duration <= 6s : short | |
| - duration > 6s : long | |
| Resolution tier via S_full (patches per full canvas): | |
| ~1080p: ~8160, ~720p: ~3680, <=480p: <=~1500 | |
| Returns: (max_total_patches, debug_dict) | |
| """ | |
| S_full = int(max(1, S_full)) | |
| cap_total = int(max(S_full, cap_total)) | |
| fps_use = float(fps) if (fps and fps > 0) else 30.0 | |
| duration_sec = float(total_frames) / float(fps_use) if total_frames > 0 else 0.0 | |
| tier_time = "short" if duration_sec <= 6.0 else "long" | |
| # Soft time cap (stricter, user-tuned) | |
| if duration_sec <= 7.0: | |
| max_images_by_time = 8 | |
| time_cap_mode = "<=7s->8" | |
| elif duration_sec <= 12.0: | |
| max_images_by_time = 12 | |
| time_cap_mode = "<=12s->12" | |
| else: | |
| max_images_by_time = max(1, int(math.ceil(duration_sec * 1.2))) | |
| time_cap_mode = ">12s->ceil(1.2x)" | |
| # Resolution tier via S_full | |
| if S_full >= 7000: # ~1080p | |
| target_num_images = 4 if tier_time == "short" else 8 | |
| tier_res = "1080p_like" | |
| elif S_full >= 2500: # ~720p | |
| target_num_images = 8 if tier_time == "short" else 12 | |
| tier_res = "720p_like" | |
| else: # small (320p/480p) | |
| target_num_images = 12 if tier_time == "short" else 28 | |
| tier_res = "small_like" | |
| num_images_cap = max(1, cap_total // S_full) | |
| num_images_final = int(min(target_num_images, num_images_cap, max_images_by_time)) | |
| # Ensure output image count is a multiple of 4 | |
| num_images_final = max(4, int(num_images_final // 4) * 4) | |
| max_total = int(num_images_final * S_full) | |
| dbg = { | |
| "cap_total": int(cap_total), | |
| "S_full": int(S_full), | |
| "fps_use": float(fps_use), | |
| "duration_sec": float(duration_sec), | |
| "time_cap_mode": str(time_cap_mode), | |
| "tier_time": tier_time, | |
| "tier_res": tier_res, | |
| "target_num_images": int(target_num_images), | |
| "num_images_cap": int(num_images_cap), | |
| "max_images_by_time": int(max_images_by_time), | |
| "num_images_final": int(num_images_final), | |
| "max_total_patches": int(max_total), | |
| } | |
| return max_total, dbg | |
| def ffprobe_keyframe_frame_ids(video_path: str, fps: float, total_frames: int) -> List[int]: | |
| """Return keyframe frame ids (best-effort) using ffprobe. | |
| Parse key_frame timestamps and convert to frame ids by round(ts * fps). | |
| """ | |
| try: | |
| fps_use = float(fps) if (fps and fps > 0) else 30.0 | |
| cmd = [ | |
| "ffprobe", | |
| "-v", "error", | |
| "-select_streams", "v:0", | |
| "-show_frames", | |
| "-show_entries", "frame=key_frame,best_effort_timestamp_time", | |
| "-of", "csv=p=0", | |
| str(video_path), | |
| ] | |
| p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) | |
| if p.returncode != 0: | |
| return [0] if total_frames > 0 else [] | |
| out: List[int] = [] | |
| for line in p.stdout.splitlines(): | |
| line = line.strip() | |
| if not line: | |
| continue | |
| parts = [x.strip() for x in line.split(",") if x.strip() != ""] | |
| if not parts: | |
| continue | |
| if parts[0].lower() == "frame": | |
| parts = parts[1:] | |
| if len(parts) < 2: | |
| continue | |
| try: | |
| kf = int(float(parts[0])) | |
| except Exception: | |
| continue | |
| if kf != 1: | |
| continue | |
| try: | |
| ts = float(parts[1]) | |
| except Exception: | |
| continue | |
| if not np.isfinite(ts): | |
| continue | |
| fid = int(round(ts * fps_use)) | |
| if total_frames > 0: | |
| fid = max(0, min(int(total_frames) - 1, fid)) | |
| out.append(int(fid)) | |
| out = sorted(set(out)) | |
| if not out: | |
| return [0] if total_frames > 0 else [] | |
| if out[0] != 0: | |
| out = [0] + out | |
| return out | |
| except Exception: | |
| return [0] if total_frames > 0 else [] | |
| def ffprobe_sum_pkt_size(video_path: str, start_sec: float, dur_sec: float) -> int: | |
| """Sum packet sizes in [start_sec, start_sec + dur_sec] using ffprobe read_intervals.""" | |
| try: | |
| start_sec = max(0.0, float(start_sec)) | |
| dur_sec = max(0.0, float(dur_sec)) | |
| interval = f"{start_sec:.6f}%+{dur_sec:.6f}" | |
| cmd = [ | |
| "ffprobe", | |
| "-v", "error", | |
| "-read_intervals", interval, | |
| "-select_streams", "v:0", | |
| "-show_packets", | |
| "-show_entries", "packet=size", | |
| "-of", "csv=p=0", | |
| str(video_path), | |
| ] | |
| p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) | |
| if p.returncode != 0: | |
| return 0 | |
| s = 0 | |
| for line in p.stdout.splitlines(): | |
| line = line.strip() | |
| if not line or line == "N/A": | |
| continue | |
| try: | |
| s += int(line) | |
| except Exception: | |
| continue | |
| return int(s) | |
| except Exception: | |
| return 0 | |
| def ffprobe_packets_pb_energy_bins( | |
| video_path: str, | |
| bin_sec: float = 0.5, | |
| smooth_bins: int = 1, | |
| ) -> Tuple[np.ndarray, np.ndarray, Dict[str, Any]]: | |
| """Return (bin_centers_sec, bin_energy, dbg) using packet sizes of NON-keyframes. | |
| Uses ffprobe -show_packets and reads packet pts_time/size/flags. | |
| - Exclude keyframes (flags contains 'K') so I-frames don't dominate. | |
| - Energy uses log1p(size) and is aggregated per time bin. | |
| - Optional smoothing over bins. | |
| """ | |
| dbg: Dict[str, Any] = { | |
| "bin_sec": float(bin_sec), | |
| "smooth_bins": int(smooth_bins), | |
| "packets_total": 0, | |
| "packets_pb": 0, | |
| "packets_key_skipped": 0, | |
| "packets_missing_pts": 0, | |
| "packets_missing_size": 0, | |
| "duration_est_sec": 0.0, | |
| } | |
| bin_sec = float(bin_sec) if (bin_sec and bin_sec > 1e-6) else 0.5 | |
| smooth_bins = int(max(0, int(smooth_bins))) | |
| cmd = [ | |
| "ffprobe", | |
| "-v", "error", | |
| "-select_streams", "v:0", | |
| "-show_packets", | |
| "-show_entries", "packet=pts_time,size,flags", | |
| "-of", "json", | |
| str(video_path), | |
| ] | |
| try: | |
| p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) | |
| if p.returncode != 0: | |
| dbg["error"] = "ffprobe_failed" | |
| return np.zeros((0,), dtype=np.float32), np.zeros((0,), dtype=np.float32), dbg | |
| j = json.loads(p.stdout or "{}") | |
| packets = j.get("packets", []) | |
| if not isinstance(packets, list) or not packets: | |
| dbg["error"] = "no_packets" | |
| return np.zeros((0,), dtype=np.float32), np.zeros((0,), dtype=np.float32), dbg | |
| except Exception: | |
| dbg["error"] = "ffprobe_exception" | |
| return np.zeros((0,), dtype=np.float32), np.zeros((0,), dtype=np.float32), dbg | |
| dbg["packets_total"] = int(len(packets)) | |
| ts: List[float] = [] | |
| es: List[float] = [] | |
| t_max = 0.0 | |
| for pkt in packets: | |
| if not isinstance(pkt, dict): | |
| continue | |
| flags = str(pkt.get("flags") or "") | |
| # Skip keyframes (I) | |
| if "K" in flags: | |
| dbg["packets_key_skipped"] += 1 | |
| continue | |
| pts_s = pkt.get("pts_time") | |
| size_s = pkt.get("size") | |
| if pts_s is None: | |
| dbg["packets_missing_pts"] += 1 | |
| continue | |
| if size_s is None: | |
| dbg["packets_missing_size"] += 1 | |
| continue | |
| try: | |
| t = float(pts_s) | |
| sz = float(size_s) | |
| except Exception: | |
| continue | |
| if (not np.isfinite(t)) or t < 0: | |
| continue | |
| if (not np.isfinite(sz)) or sz < 0: | |
| continue | |
| e = float(np.log1p(sz)) | |
| ts.append(t) | |
| es.append(e) | |
| if t > t_max: | |
| t_max = t | |
| dbg["packets_pb"] = int(len(ts)) | |
| dbg["duration_est_sec"] = float(t_max) | |
| if len(ts) == 0: | |
| dbg["error"] = "no_pb_packets" | |
| return np.zeros((0,), dtype=np.float32), np.zeros((0,), dtype=np.float32), dbg | |
| nb = int(max(1, math.ceil((t_max + 1e-9) / bin_sec))) | |
| energy = np.zeros((nb,), dtype=np.float32) | |
| counts = np.zeros((nb,), dtype=np.int32) | |
| for t, e in zip(ts, es): | |
| bi = int(min(nb - 1, max(0, int(t // bin_sec)))) | |
| energy[bi] += float(e) | |
| counts[bi] += 1 | |
| if smooth_bins > 0 and nb > 1: | |
| k = int(smooth_bins) | |
| sm = np.zeros_like(energy) | |
| for i in range(nb): | |
| a = max(0, i - k) | |
| b = min(nb, i + k + 1) | |
| sm[i] = float(energy[a:b].mean()) | |
| energy = sm | |
| centers = (np.arange(nb, dtype=np.float32) + 0.5) * float(bin_sec) | |
| dbg["bin_count"] = int(nb) | |
| dbg["bin_nonzero"] = int(np.sum(counts > 0)) | |
| dbg["counts_minmax"] = [int(counts.min()), int(counts.max())] if counts.size else [0, 0] | |
| return centers.astype(np.float32), energy.astype(np.float32), dbg | |