Text Generation
Transformers
Safetensors
English
German
qwen3_5
image-text-to-text
title-generation
lora
qwen3.5
simplixity
conversational
Instructions to use stneric/simplixity-title-0.8b with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use stneric/simplixity-title-0.8b with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="stneric/simplixity-title-0.8b") 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 AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("stneric/simplixity-title-0.8b") model = AutoModelForMultimodalLM.from_pretrained("stneric/simplixity-title-0.8b", 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=256) print(processor.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use stneric/simplixity-title-0.8b with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "stneric/simplixity-title-0.8b" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "stneric/simplixity-title-0.8b", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/stneric/simplixity-title-0.8b
- SGLang
How to use stneric/simplixity-title-0.8b 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 "stneric/simplixity-title-0.8b" \ --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": "stneric/simplixity-title-0.8b", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'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 "stneric/simplixity-title-0.8b" \ --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": "stneric/simplixity-title-0.8b", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use stneric/simplixity-title-0.8b with Docker Model Runner:
docker model run hf.co/stneric/simplixity-title-0.8b
Download training/eval/eval.py from stneric/simplixity-title-0.8b: direct link, hf CLI and curl.
- Browser
- Download file 9.5 kB
-
https://huggingface.co/stneric/simplixity-title-0.8b/resolve/main/training/eval/eval.py
- Command line
-
hf download hf://stneric/simplixity-title-0.8b/training/eval/eval.py
-
curl -L -o eval.py https://huggingface.co/stneric/simplixity-title-0.8b/resolve/main/training/eval/eval.py
9.5 kB
| """Title models in Ollama on eval/messages.jsonl, sent the way Simplixity sends them. | |
| The request is the one app/llm/client.py builds for a title on Ollama: the | |
| OpenAI-compatible endpoint, temperature 0.2, max_tokens 24 and | |
| reasoning_effort "none" (reasoning_tokens=0 adds no headroom on top). The | |
| system prompt is read from the training data, which is the app's title prompt | |
| verbatim (TITLE and IN_ENGLISH from app/agent/prompts.py). | |
| python3 -I eval.py simplixity-title:0.8b-v1-q8_0 simplixity-title:0.8b-v2-q8_0 | |
| A model given as `label=hf:/path/to/checkpoint` runs in transformers on the GPU | |
| at bf16 instead, for checkpoints this Ollama cannot import: 0.40 on Linux takes | |
| safetensors only through its MLX runtime, which the Arch package leaves out. | |
| It renders the same chat template (the generation prompt ends in the closed | |
| empty thinking block, as with reasoning_effort "none") and samples like | |
| Ollama's defaults: temperature 0.2, top_k 40, top_p 0.9. | |
| uv run python -I eval/eval.py v2=hf:runs/v2/model simplixity-title:0.8b-v1-q8_0 | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import os | |
| import re | |
| import statistics | |
| import time | |
| import urllib.request | |
| HERE = os.path.dirname(os.path.abspath(__file__)) | |
| TRAIN = os.path.expanduser("~/models/simplixity-title-0.8b/training/train.jsonl") | |
| TOKENIZER = os.path.expanduser("~/models/simplixity-title-0.8b") | |
| QUOTES = set("\"“”„«»‚‘`") | |
| # A title is German if it has one of these, a word with an umlaut or ß, or a | |
| # word of five letters or more copied from a German message ("Kurzarbeit | |
| # Gehalt Bedeutung" has neither of the first two). Names the keys list | |
| # (Großglockner, BahnCard, Mietpreisbremse) and the words below, which are | |
| # English as well, do not count. | |
| SHARED_WORDS = {"restaurant", "docker", "compose", "python", "commit", "laptop", "blockchain", | |
| "filesharing", "gravelbike", "ibuprofen", "tirol", "pandas", "semikolon", "winter"} | |
| GERMAN_WORDS = { | |
| "der", "die", "das", "und", "mit", "für", "von", "im", "ist", "ein", "eine", | |
| "zu", "auf", "bei", "wie", "nicht", "oder", "den", "dem", "des", "zum", "zur", | |
| "über", "nach", "vom", "am", "beim", "unterschied", "frage", "tipps", | |
| } | |
| def system_prompt() -> str: | |
| with open(TRAIN) as f: | |
| return json.loads(f.readline())["messages"][0]["content"] | |
| def ask(base: str, model: str, system: str, text: str) -> tuple[str, float]: | |
| payload = { | |
| "model": model, | |
| "messages": [{"role": "system", "content": system}, {"role": "user", "content": text[:1000]}], | |
| "temperature": 0.2, | |
| "stream": False, | |
| "max_tokens": 24, | |
| "reasoning_effort": "none", | |
| } | |
| req = urllib.request.Request( | |
| f"{base}/v1/chat/completions", | |
| data=json.dumps(payload).encode(), | |
| headers={"Content-Type": "application/json"}, | |
| ) | |
| start = time.perf_counter() | |
| with urllib.request.urlopen(req, timeout=120) as resp: | |
| data = json.load(resp) | |
| elapsed = time.perf_counter() - start | |
| return (data["choices"][0]["message"].get("content") or "").strip(), elapsed | |
| class Local: | |
| def __init__(self, path: str) -> None: | |
| import torch | |
| from transformers import AutoModelForImageTextToText, AutoTokenizer | |
| self.torch = torch | |
| config = os.path.join(path, "adapter_config.json") | |
| if os.path.exists(config): | |
| # A training checkpoint: LoRA on top of the model it was trained from. | |
| from peft import PeftModel | |
| with open(config) as f: | |
| init = json.load(f)["base_model_name_or_path"] | |
| self.tok = AutoTokenizer.from_pretrained(TOKENIZER) | |
| base = AutoModelForImageTextToText.from_pretrained(init, dtype=torch.bfloat16) | |
| self.model = PeftModel.from_pretrained(base, path).merge_and_unload().cuda().eval() | |
| else: | |
| self.tok = AutoTokenizer.from_pretrained(path) | |
| self.model = AutoModelForImageTextToText.from_pretrained(path, dtype=torch.bfloat16).cuda().eval() | |
| # The checkpoints have no generation_config.json, so generate would stop | |
| # only at config.json's <|endoftext|>. Ollama stops at the end of the | |
| # turn, <|im_end|>; without it v1 keeps writing "\n!\n!..." to 24 tokens. | |
| self.stop = [self.tok.convert_tokens_to_ids("<|im_end|>"), self.tok.eos_token_id] | |
| def ask(self, system: str, text: str) -> tuple[str, float]: | |
| messages = [{"role": "system", "content": system}, {"role": "user", "content": text[:1000]}] | |
| prompt = self.tok.apply_chat_template(messages, add_generation_prompt=True, tokenize=False) | |
| ids = self.tok(prompt, add_special_tokens=False, return_tensors="pt").to("cuda") | |
| start = time.perf_counter() | |
| with self.torch.no_grad(): | |
| out = self.model.generate( | |
| **ids, max_new_tokens=24, do_sample=True, temperature=0.2, top_k=40, top_p=0.9, | |
| eos_token_id=self.stop, | |
| ) | |
| elapsed = time.perf_counter() - start | |
| title = self.tok.decode(out[0, ids["input_ids"].shape[1] :], skip_special_tokens=True) | |
| return title.strip(), elapsed | |
| def judge(title: str, msg: dict) -> dict: | |
| words = title.split() | |
| lower = title.lower() | |
| tokens = {w.strip(".,:;!?()'’").lower() for w in words} | |
| def name(w: str) -> bool: | |
| return w in SHARED_WORDS or any(k in w or w in k for k in msg["keys"]) | |
| source = set(re.findall(r"\w+", msg["text"].lower())) if msg["lang"] == "de" else set() | |
| umlaut = any(re.search(r"[äöüß]", w) and not name(w) for w in tokens) | |
| copied = any(len(w) >= 5 and w in source and not name(w) for w in tokens) | |
| checks = { | |
| "empty": not title, | |
| "too_long": len(words) > 6, | |
| "quotes": any(c in QUOTES for c in title) or title[:1] == "'" or title[-1:] == "'", | |
| "trailing_punct": title[-1:] in set(".!?:;,…"), | |
| "chat_about": "chat about" in lower, | |
| "german": bool(tokens & GERMAN_WORDS) or umlaut or copied, | |
| } | |
| return { | |
| "format_ok": not any(checks.values()), | |
| "fails": [k for k, v in checks.items() if v], | |
| "key_ok": any(k in lower for k in msg["keys"]) if msg["keys"] else None, | |
| } | |
| def pct(xs: list[bool]) -> str: | |
| return f"{100 * sum(xs) / len(xs):.0f}%" if xs else "–" | |
| def main() -> None: | |
| p = argparse.ArgumentParser() | |
| p.add_argument("models", nargs="*") | |
| p.add_argument("--runs", type=int, default=3) | |
| p.add_argument("--base", default="http://localhost:11434") | |
| p.add_argument("--out", default=os.path.join(HERE, "results.jsonl")) | |
| p.add_argument("--messages", default=os.path.join(HERE, "messages.jsonl")) | |
| p.add_argument("--rejudge", action="store_true", help="score the titles in --out again, generate nothing") | |
| args = p.parse_args() | |
| system = system_prompt() | |
| with open(args.messages) as f: | |
| messages = [json.loads(line) for line in f] | |
| rows = [] | |
| labels = [] | |
| if args.rejudge: | |
| by_id = {m["id"]: m for m in messages} | |
| with open(args.out) as f: | |
| rows = [json.loads(line) for line in f] | |
| rows = [{**r, **judge(r["title"], by_id[r["id"]])} for r in rows] | |
| labels = list(dict.fromkeys(r["model"] for r in rows)) | |
| args.runs = len({r["run"] for r in rows}) | |
| args.models = [] | |
| for spec in args.models: | |
| model, _, path = spec.partition("=hf:") | |
| labels.append(model) | |
| if path: | |
| local = Local(os.path.expanduser(path)) | |
| call = local.ask | |
| else: | |
| def call(system: str, text: str, model: str = model) -> tuple[str, float]: | |
| return ask(args.base, model, system, text) | |
| call(system, "hello") # load it before anything is timed | |
| for run in range(args.runs): | |
| for msg in messages: | |
| title, elapsed = call(system, msg["text"]) | |
| rows.append({"model": model, "run": run, "id": msg["id"], "lang": msg["lang"], | |
| "title": title, "seconds": elapsed, **judge(title, msg)}) | |
| print(f"{model} run {run + 1}/{args.runs} done", flush=True) | |
| if path: | |
| del local, call | |
| import torch | |
| torch.cuda.empty_cache() | |
| args.models = labels | |
| with open(args.out, "w") as f: | |
| for r in rows: | |
| f.write(json.dumps(r, ensure_ascii=False) + "\n") | |
| print(f"\n{len(messages)} messages × {args.runs} runs\n") | |
| head = ["", *args.models] | |
| lines = [head, ["---"] * len(head)] | |
| for label, sel in (("all", None), ("en", "en"), ("de", "de")): | |
| cell = [] | |
| for model in args.models: | |
| rs = [r for r in rows if r["model"] == model and sel in (None, r["lang"])] | |
| cell.append(pct([r["format_ok"] for r in rs])) | |
| lines.append([f"All format rules ({label})", *cell]) | |
| for fail in ("too_long", "german", "quotes", "trailing_punct", "empty", "chat_about"): | |
| lines.append([f" fails: {fail}", *[ | |
| pct([fail in r["fails"] for r in rows if r["model"] == m]) for m in args.models]]) | |
| lines.append(["Key term named", *[ | |
| pct([r["key_ok"] for r in rows if r["model"] == m and r["key_ok"] is not None]) for m in args.models]]) | |
| lines.append(["Median per title", *[ | |
| f"{statistics.median(r['seconds'] for r in rows if r['model'] == m):.2f} s" for m in args.models]]) | |
| for line in lines: | |
| print("| " + " | ".join(line) + " |") | |
| if __name__ == "__main__": | |
| main() | |