Image-Text-to-Text
Transformers
Safetensors
English
Chinese
sensenova_loop
text-generation
nvfp4
modelopt
quantized
multimodal
looped-transformer
mhc
mtp
custom_code
conversational
8-bit precision
Instructions to use gitcommit90/SenseNova-Loop-9B-Preview-NVFP4 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use gitcommit90/SenseNova-Loop-9B-Preview-NVFP4 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="gitcommit90/SenseNova-Loop-9B-Preview-NVFP4", 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)# pip install -U transformers accelerate # Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("gitcommit90/SenseNova-Loop-9B-Preview-NVFP4", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use gitcommit90/SenseNova-Loop-9B-Preview-NVFP4 with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "gitcommit90/SenseNova-Loop-9B-Preview-NVFP4" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "gitcommit90/SenseNova-Loop-9B-Preview-NVFP4", "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/gitcommit90/SenseNova-Loop-9B-Preview-NVFP4
- SGLang
How to use gitcommit90/SenseNova-Loop-9B-Preview-NVFP4 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 "gitcommit90/SenseNova-Loop-9B-Preview-NVFP4" \ --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": "gitcommit90/SenseNova-Loop-9B-Preview-NVFP4", "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 "gitcommit90/SenseNova-Loop-9B-Preview-NVFP4" \ --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": "gitcommit90/SenseNova-Loop-9B-Preview-NVFP4", "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 gitcommit90/SenseNova-Loop-9B-Preview-NVFP4 with Docker Model Runner:
docker model run hf.co/gitcommit90/SenseNova-Loop-9B-Preview-NVFP4
NVFP4 (ModelOpt) quantization of sensenova/SenseNova-Loop-9B-Preview @27abea77 with BF16 MTP head
a57aa9f verified Download quantization/build_calib.py from gitcommit90/SenseNova-Loop-9B-Preview-NVFP4: direct link, hf CLI and curl.
- Browser
- Download file 14.6 kB
-
https://huggingface.co/gitcommit90/SenseNova-Loop-9B-Preview-NVFP4/resolve/main/quantization/build_calib.py
- Command line
-
hf download hf://gitcommit90/SenseNova-Loop-9B-Preview-NVFP4/quantization/build_calib.py
-
curl -L -o build_calib.py https://huggingface.co/gitcommit90/SenseNova-Loop-9B-Preview-NVFP4/resolve/main/quantization/build_calib.py
14.6 kB
| #!/usr/bin/env python | |
| """Build calib/calib_v1.jsonl: 256 chat-format calibration examples for NVFP4 (ModelOpt) PTQ. | |
| .venv-eval/bin/python calib/build_calib.py # raw files are expected under calib/raw/ | |
| (downloads: see SOURCES; fetched with huggingface_hub.hf_hub_download at the pinned revisions) | |
| Each line: {"id", "category", "source", "messages": [...OpenAI-style...], "tools"?: [...], | |
| "enable_thinking": bool, "approx_text_tokens": int, "n_images": int} | |
| Images: {"type": "image", "image": "calib/images/<file>.jpg"} (path relative to project root), | |
| which HF processors' apply_chat_template understand; the chat template also accepts image_url. | |
| Thinking examples carry the trace in the assistant's `reasoning_content` (the chat template renders | |
| it as <think>\n...\n</think>\n\n before the answer); all other final assistant turns render with | |
| the empty <think>\n\n</think>\n\n block, matching enable_thinking=False inference. | |
| Disjointness from eval/data is enforced by construction (different datasets/splits) AND checked | |
| by 13-gram overlap against every eval prompt (summary in calib/calib_v1.meta.json). | |
| """ | |
| from __future__ import annotations | |
| import hashlib | |
| import json | |
| import random | |
| import re | |
| import sys | |
| from collections import Counter | |
| from pathlib import Path | |
| import pyarrow.parquet as pq | |
| ROOT = Path(__file__).resolve().parent.parent | |
| CAL = ROOT / "calib" | |
| RAW = CAL / "raw" | |
| SEED = 20261030 | |
| MAX_TEXT_TOKENS = 1800 | |
| TARGET = {"chat": 77, "code": 51, "math": 51, "tools": 38, "image": 39} # = 256 (30/20/20/15/15 %) | |
| N_THINK_MATH = 26 | |
| SOURCES = { | |
| "chat": dict(repo="OpenAssistant/oasst2", rev="179dd21fc55192153d94adb0e0ce8f69e222bf75", | |
| file="data/train-00000-of-00001-88ba0162028a73fc.parquet", split="train", license="Apache-2.0"), | |
| "code": dict(repo="google-research-datasets/mbpp", rev="4bb6404fdc6cacfda99d4ac4205087b89d32030c", | |
| file="full/train-00000-of-00001.parquet", split="full/train", license="CC-BY-4.0"), | |
| "math": dict(repo="openai/gsm8k", rev="740312add88f781978c0658806c59bc2815b9866", | |
| file="main/train-00000-of-00001.parquet", split="main/train", license="MIT"), | |
| "tools": dict(repo="glaiveai/glaive-function-calling-v2", rev="e7f4b6456019f5d8bcb991ef0dd67d8ff23221ac", | |
| file="glaive-function-calling-v2.json", split="train", license="Apache-2.0"), | |
| "image": dict(repo="lmms-lab/textvqa", rev="9c0699cd19768ac5ab97568f6b3cbac4c0062884", | |
| file="data/train-00000-of-00020.parquet", split="train (shard 0 of 20)", | |
| license="TextVQA annotations CC-BY-4.0; images OpenImages CC-BY-2.0 (Flickr attribution)"), | |
| } | |
| def raw(cat): | |
| s = SOURCES[cat] | |
| return RAW / s["repo"].replace("/", "__") / s["file"] | |
| _tok = None | |
| def ntok(text): | |
| global _tok | |
| if _tok is None: | |
| from tokenizers import Tokenizer | |
| _tok = Tokenizer.from_file(str(ROOT / "upstream/SenseNova-Loop-9B-Preview/tokenizer.json")) | |
| return len(_tok.encode(text, add_special_tokens=False).ids) | |
| def ex_text(ex): | |
| parts = [json.dumps(ex.get("tools") or "")] | |
| for m in ex["messages"]: | |
| c = m.get("content") | |
| if isinstance(c, list): | |
| c = " ".join(p.get("text", "") for p in c) | |
| parts.append(c or "") | |
| parts.append(m.get("reasoning_content") or "") | |
| parts.append(json.dumps(m.get("tool_calls") or "")) | |
| return "\n".join(parts) | |
| # ----------------------------------------------------------------------------- chat (oasst2) | |
| def build_chat(rng): | |
| t = pq.read_table(raw("chat"), columns=["message_id", "parent_id", "text", "role", "lang", "deleted", | |
| "review_result", "rank", "tree_state", "synthetic"]).to_pylist() | |
| kids = {} | |
| roots = [] | |
| for m in t: | |
| if m["deleted"] or m["review_result"] is False or m["synthetic"]: | |
| continue | |
| if m["parent_id"] is None: | |
| if m["role"] == "prompter" and m["tree_state"] == "ready_for_export": | |
| roots.append(m) | |
| else: | |
| kids.setdefault(m["parent_id"], []).append(m) | |
| def best(mid, role): | |
| cs = [c for c in kids.get(mid, []) if c["role"] == role] | |
| cs.sort(key=lambda c: (c["rank"] if c["rank"] is not None else 99, c["message_id"])) | |
| return cs[0] if cs else None | |
| roots.sort(key=lambda m: m["message_id"]) | |
| rng.shuffle(roots) | |
| want = {"en": 58, "zh": 10, "other": 9} | |
| got = {"en": [], "zh": [], "other": []} | |
| for r in roots: | |
| bucket = r["lang"] if r["lang"] in ("en", "zh") else "other" | |
| if len(got[bucket]) >= want[bucket]: | |
| continue | |
| msgs, cur = [{"role": "user", "content": r["text"]}], r | |
| turns = rng.choice([1, 1, 2]) | |
| for k in range(turns): | |
| a = best(cur["message_id"], "assistant") | |
| if a is None: | |
| break | |
| msgs.append({"role": "assistant", "content": a["text"]}) | |
| if k + 1 < turns: | |
| u = best(a["message_id"], "prompter") | |
| a2 = best(u["message_id"], "assistant") if u else None | |
| if not (u and a2): | |
| break | |
| msgs.append({"role": "user", "content": u["text"]}) | |
| cur = u | |
| if msgs[-1]["role"] != "assistant": | |
| continue | |
| ex = {"category": "chat", "source": f"oasst2:{r['message_id']}", "lang": r["lang"], "messages": msgs} | |
| if ntok(ex_text(ex)) > MAX_TEXT_TOKENS: | |
| continue | |
| got[bucket].append(ex) | |
| if all(len(got[b]) >= want[b] for b in want): | |
| break | |
| return got["en"] + got["zh"] + got["other"] | |
| # ----------------------------------------------------------------------------- code (MBPP train) | |
| def build_code(rng): | |
| rows = pq.read_table(raw("code")).to_pylist() | |
| rows.sort(key=lambda r: r["task_id"]) | |
| idx = sorted(rng.sample(range(len(rows)), TARGET["code"])) | |
| out = [] | |
| for i in idx: | |
| r = rows[i] | |
| tests = "\n".join(r["test_list"]) | |
| user = f"{r['text']} Your code should pass these tests:\n\n```python\n{tests}\n```" | |
| asst = f"```python\n{r['code'].strip()}\n```" | |
| out.append({"category": "code", "source": f"mbpp:task_id={r['task_id']}", | |
| "messages": [{"role": "user", "content": user}, {"role": "assistant", "content": asst}]}) | |
| return out | |
| # ----------------------------------------------------------------------------- math (GSM8K train) | |
| def build_math(rng): | |
| rows = pq.read_table(raw("math")).to_pylist() | |
| idx = sorted(rng.sample(range(len(rows)), TARGET["math"])) | |
| out = [] | |
| for j, i in enumerate(idx): | |
| q, sol = rows[i]["question"], rows[i]["answer"] | |
| steps, final = sol.split("####") | |
| steps = re.sub(r"<<[^>]*>>", "", steps).strip() | |
| final = final.strip() | |
| think = j < N_THINK_MATH | |
| if think: | |
| asst = {"role": "assistant", "reasoning_content": steps, | |
| "content": f"The final answer is {final}."} | |
| else: | |
| asst = {"role": "assistant", "content": f"{steps}\nThe final answer is {final}."} | |
| out.append({"category": "math", "source": f"gsm8k:train:{i}", "enable_thinking": think, | |
| "messages": [{"role": "user", "content": q}, asst]}) | |
| return out | |
| # ----------------------------------------------------------------------------- tools (glaive v2) | |
| def parse_glaive(rec): | |
| sysm = rec["system"] | |
| if "following functions" not in sysm: | |
| return None | |
| body = sysm.split("Use them if required -", 1)[-1].strip() | |
| dec, pos, fns = json.JSONDecoder(), 0, [] | |
| while pos < len(body): | |
| while pos < len(body) and body[pos] in " \n\t": | |
| pos += 1 | |
| if pos >= len(body): | |
| break | |
| try: | |
| obj, pos = dec.raw_decode(body, pos) | |
| except json.JSONDecodeError: | |
| return None | |
| fns.append({"type": "function", "function": obj}) | |
| if not fns: | |
| return None | |
| turns = re.split(r"\n*(USER|ASSISTANT|FUNCTION RESPONSE): ", "\n" + rec["chat"]) | |
| msgs, n_calls = [], 0 | |
| it = iter(turns[1:]) | |
| for role, text in zip(it, it): | |
| text = text.replace("<|endoftext|>", "").strip() | |
| if role == "USER": | |
| msgs.append({"role": "user", "content": text}) | |
| elif role == "FUNCTION RESPONSE": | |
| msgs.append({"role": "tool", "content": text}) | |
| else: | |
| if "<functioncall>" in text: | |
| pre, call = text.split("<functioncall>", 1) | |
| m = re.match(r'\s*\{\s*"name"\s*:\s*"([^"]+)"\s*,\s*"arguments"\s*:\s*\'(.*)\'\s*\}\s*$', call, re.S) | |
| if not m: | |
| return None | |
| try: | |
| args = json.loads(m.group(2)) | |
| except json.JSONDecodeError: | |
| return None | |
| if not isinstance(args, dict): | |
| return None | |
| msgs.append({"role": "assistant", "content": pre.strip(), | |
| "tool_calls": [{"id": f"call_{n_calls}", "type": "function", | |
| "function": {"name": m.group(1), "arguments": args}}]}) | |
| n_calls += 1 | |
| else: | |
| msgs.append({"role": "assistant", "content": text}) | |
| if n_calls == 0 or not msgs or msgs[0]["role"] != "user" or msgs[-1]["role"] != "assistant": | |
| return None | |
| if not any(m["role"] == "tool" for m in msgs): | |
| return None | |
| return fns, msgs | |
| def build_tools(rng): | |
| data = json.load(open(raw("tools"))) | |
| order = list(range(len(data))) | |
| rng.shuffle(order) | |
| out = [] | |
| for i in order: | |
| p = parse_glaive(data[i]) | |
| if not p: | |
| continue | |
| ex = {"category": "tools", "source": f"glaive-fc-v2:{i}", "tools": p[0], "messages": p[1]} | |
| if ntok(ex_text(ex)) > MAX_TEXT_TOKENS: | |
| continue | |
| out.append(ex) | |
| if len(out) >= TARGET["tools"]: | |
| break | |
| return out | |
| # ----------------------------------------------------------------------------- image (TextVQA train) | |
| def build_image(rng, eval_image_ids): | |
| from tasks import vqa_norm # eval/ is on sys.path | |
| t = pq.read_table(raw("image"), columns=["question_id", "question", "answers", "image_id"]).to_pylist() | |
| cand = [i for i, r in enumerate(t) if r["image_id"] not in eval_image_ids | |
| and Counter(vqa_norm(a) for a in r["answers"]).most_common(1)[0][1] >= 3] | |
| rng.shuffle(cand) | |
| seen_img, pickd = set(), [] | |
| for i in cand: | |
| if t[i]["image_id"] in seen_img: | |
| continue | |
| seen_img.add(t[i]["image_id"]) | |
| pickd.append(i) | |
| if len(pickd) >= TARGET["image"]: | |
| break | |
| pickd.sort() | |
| imgs = pq.read_table(raw("image"), columns=["image"]).take(pickd).column("image").to_pylist() | |
| (CAL / "images").mkdir(exist_ok=True) | |
| out = [] | |
| for i, im in zip(pickd, imgs): | |
| r = t[i] | |
| b = im["bytes"] | |
| ext = "png" if b[:4] == b"\x89PNG" else "jpg" | |
| rel = f"calib/images/textvqa_train_{r['question_id']}.{ext}" | |
| (ROOT / rel).write_bytes(b) | |
| ans = Counter(a.strip() for a in r["answers"]).most_common(1)[0][0] | |
| out.append({"category": "image", "source": f"textvqa:train:qid={r['question_id']}", "image_id": r["image_id"], | |
| "image_sha256": hashlib.sha256(b).hexdigest(), "messages": [ | |
| {"role": "user", "content": [{"type": "image", "image": rel}, | |
| {"type": "text", "text": r["question"].strip().capitalize() | |
| + "\nAnswer the question using a single word or phrase."}]}, | |
| {"role": "assistant", "content": ans}]}) | |
| return out | |
| # ----------------------------------------------------------------------------- overlap check | |
| def shingles(text, n=13): | |
| w = re.findall(r"\w+", text.lower()) | |
| return {" ".join(w[i:i + n]) for i in range(max(0, len(w) - n + 1))} | |
| def main(): | |
| sys.path.insert(0, str(ROOT / "eval")) | |
| eval_dir = ROOT / "eval/data" | |
| eval_sh, eval_imgs = set(), set() | |
| for p in eval_dir.glob("*.jsonl"): | |
| for l in open(p): | |
| r = json.loads(l) | |
| eval_sh |= shingles(json.dumps(r, ensure_ascii=False)) | |
| if "image_id" in r: | |
| eval_imgs.add(r["image_id"]) | |
| exs = [] | |
| exs += build_chat(random.Random(f"{SEED}-chat")) | |
| exs += build_code(random.Random(f"{SEED}-code")) | |
| exs += build_math(random.Random(f"{SEED}-math")) | |
| exs += build_tools(random.Random(f"{SEED}-tools")) | |
| exs += build_image(random.Random(f"{SEED}-image"), eval_imgs) | |
| cnt = Counter(e["category"] for e in exs) | |
| assert dict(cnt) == TARGET, cnt | |
| overlaps = [] | |
| for k, e in enumerate(exs): | |
| e["id"] = f"calib_v1_{k:03d}" | |
| e.setdefault("enable_thinking", False) | |
| e["approx_text_tokens"] = ntok(ex_text(e)) | |
| e["n_images"] = sum(1 for m in e["messages"] if isinstance(m["content"], list) | |
| for p in m["content"] if p.get("type") == "image") | |
| ov = shingles(ex_text(e)) & eval_sh | |
| if ov: | |
| overlaps.append({"id": e["id"], "source": e["source"], "n_shared_13grams": len(ov), | |
| "example": sorted(ov)[0]}) | |
| order = ["id", "category", "source", "enable_thinking", "approx_text_tokens", "n_images"] | |
| with open(CAL / "calib_v1.jsonl", "w") as f: | |
| for e in exs: | |
| f.write(json.dumps({**{k: e[k] for k in order}, **{k: v for k, v in e.items() if k not in order}}, | |
| ensure_ascii=False) + "\n") | |
| toks = [e["approx_text_tokens"] for e in exs] | |
| meta = {"version": "calib_v1", "seed": SEED, "n": len(exs), "counts": dict(cnt), | |
| "n_thinking_examples": sum(e["enable_thinking"] for e in exs), | |
| "langs_chat": dict(Counter(e.get("lang") for e in exs if e["category"] == "chat")), | |
| "text_tokens": {"min": min(toks), "max": max(toks), "mean": sum(toks) / len(toks), "sum": sum(toks)}, | |
| "max_text_tokens_filter": MAX_TEXT_TOKENS, "sources": SOURCES, | |
| "eval_overlap_13gram": overlaps, | |
| "sha256": hashlib.sha256((CAL / "calib_v1.jsonl").read_bytes()).hexdigest()} | |
| (CAL / "calib_v1.meta.json").write_text(json.dumps(meta, indent=2, ensure_ascii=False)) | |
| print(json.dumps({k: meta[k] for k in ("n", "counts", "n_thinking_examples", "langs_chat", "text_tokens")})) | |
| print("13-gram overlaps with eval:", len(overlaps), overlaps[:3]) | |
| if __name__ == "__main__": | |
| main() | |