Instructions to use Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0 # Run inference directly in the terminal: llama cli -hf Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0 # Run inference directly in the terminal: llama cli -hf Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0
Build from source code
git clone https://github.com/ggml-org/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-app # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama serve -hf Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0 # Run inference directly in the terminal: ./build/bin/llama cli -hf Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0
Use Docker
docker model run hf.co/Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0
- Llama new
- LM Studio
- Jan
- vLLM
How to use Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct", "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/Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0
- Ollama
How to use Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct with Ollama:
ollama run hf.co/Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0
- Unsloth Desktop
- Pi
How to use Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Docker Model Runner
How to use Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct with Docker Model Runner:
docker model run hf.co/Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0
- Lemonade
How to use Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0
Run and chat with the model
lemonade run user.Qwen3-VL-D2R-Item-Reader-4B-Instruct-Q8_0
List all available models
lemonade list
- Hermes Agent
How to use Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct:Q8_0" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
Qwen3-VL-D2R-Item-Reader-4B-Instruct
A LoRA fine-tune of Qwen3-VL-4B-Instruct that transcribes a single Diablo II: Resurrected (D2R) item tooltip from a cropped screenshot into one structured JSON object.
- Base model: Qwen/Qwen3-VL-4B-Instruct
- Method: LoRA (vision + language layers) via Unsloth + TRL
- Task: image-text-to-text โ strict JSON (constrained decoding)
- Languages: English tooltips
Intended use
Transcribing a single, cropped D2R item tooltip (English) into structured fields. It is a narrow, in-domain transcription model, not a general-purpose VLM and not a chat model.
Out of scope: general OCR, other games, non-tooltip images, open-ended Q&A.
Input format
The model expects one tightly cropped tooltip, preprocessed to a fixed
784ร504 canvas (aspect-preserving fit, black padding). This matches the
training distribution; other sizes/aspect ratios degrade accuracy. Decode greedily
(temperature = 0) and constrain the output to the JSON schema below.
User turn: Extract the item from this tooltip.
System prompt (verbatim):
Transcribe the single Diablo II: Resurrected item tooltip in the image into one JSON object. Read it line by line, top to bottom. Output only the JSON.
Field order matches the tooltip top-to-bottom. Shape: {"name": string, "baseType": string|null, "category": "weapon"|"armor"|"shield"|"offhand"|"jewel"|"ring"|"amulet"|"charm"|"gem"|"rune"|"material"|"potion"|"consumable", "itemType": string|null, "quality": "crude"|"normal"|"superior", "rarity": "low"|"normal"|"magic"|"rare"|"set"|"unique"|"crafted", "runes": string|null, "damage": {"oneHand": {"min": integer, "max": integer}|null, "twoHand": {...}|null, "throw": {...}|null, "smite": {...}|null}|null, "defense": integer|null, "chanceToBlock": integer|null, "durability": {"current": integer, "max": integer}|null, "classRestriction": string|null, "requiredDexterity": integer|null, "requiredStrength": integer|null, "requiredLevel": integer|null, "attackSpeed": string|null, "mods": string[], "ethereal": boolean, "sockets": integer}
Read EVERY digit โ `+202%` is not `+2%`, `550` is not `55`. Never guess, complete, round, reorder, or fix spelling.
Fields:
- `name`: the item title (line 1). For a runeword, line 1 only โ never append the base type.
- `baseType`: the base-type line shown under the name (e.g. Ring, Great Helm, Phase Blade, Monarch). If the type is only baked into the name (no separate line), `baseType` = null.
- `category`: the broad kind. weapon; shield (a blocking shield); offhand (a non-shield off-hand like a necromancer head / grimoire); armor (everything else worn); jewel/ring/amulet/charm; gem; rune (a loose rune); material (Essence/Shard/Statue/Token/Key/quest); potion; consumable (scroll/tome).
- `itemType`: the finer slot / weapon class (e.g. Sword, Axe, Helm, Boots, Shield, Javelin, Bow, Orb). null for non-equipment.
- `classRestriction`: the character class an item is limited to (Amazon/Sorceress/Necromancer/Paladin/Barbarian/Druid/Assassin/Warlock), shown as an "(X Only)" line โ RED if the viewing character is a different class, WHITE if it matches; either way set the class name; else null.
- `quality`: the base item tier, read from the NAME TEXT only โ crude (name marked Cracked/Damaged/Low Quality/Crude), superior (name starts with "Superior"), else normal. Ignore the name COLOR here; default "normal" when no crude/superior word is present.
- `rarity`: the NAME COLOR tier โ low (gray, also ethereal/socketed-normal), normal (white), magic (blue), rare (yellow), set (green), unique (gold/tan), crafted (orange). Runewords show gold โ unique. Materials/runes are usually orange โ crafted. Read the actual name color carefully (gold vs yellow vs orange differ); always set it.
- `ethereal`: true if the tooltip shows "Ethereal (Cannot Be Repaired)".
- `sockets`: the N in "Socketed (N)", else 0.
- `defense`: the number in "Defense: N", else null.
- `chanceToBlock`: the number in "Chance to Block: N%", else null.
- `durability`: from "Durability: X of Y" โ {"current": X, "max": Y}, else null.
- `damage`: from the white damage lines โ set the matching key, others null. "Throw Damage: A to B" โ throw; "One-Hand Damage" โ oneHand; "Two-Hand Damage" โ twoHand; "Smite Damage" โ smite (paladin shields). An item may have more than one (a javelin has throw AND oneHand). null if no damage line.
- `attackSpeed`: the verbatim "<Class> Class - <Speed> Attack Speed" line (e.g. "Javelin Class - Very Fast Attack Speed"), else null.
- `runes`: for a runeword (a line in apostrophes like `'TalThul'`), the rune string without apostrophes; baseType = the base item line. Otherwise null.
- `requiredLevel`/`requiredStrength`/`requiredDexterity`: from the "Required X: N" lines, else null.
- `mods`: every REMAINING stat line, in order, verbatim (keep `+`, `-`, `%`, ranges like `15-45`, parentheses). These are the magical properties only (procs, auras, `+X to Skills`, resistances, `Adds A-B Fire Damage`, โฆ). Do NOT put into mods anything already captured above (name, baseType, itemType, classRestriction line, quality, ethereal/socketed line, defense/chanceToBlock/durability/damage, attackSpeed line, the Required-* lines) or UI hints ("Keep in Inventory to Gain Bonus", "Ctrl + Left Click ...", "Right Click to Use").
Output schema
The model is trained for constrained / structured decoding against the
following JSON schema (response_format โ json_schema, strict: true). Field
order mirrors the in-game top-to-bottom reading order.
{
"type": "object",
"properties": {
"name": {"type": "string"},
"baseType": {"type": ["string", "null"]},
"category": {
"type": "string",
"enum": [
"weapon", "armor", "shield", "offhand", "jewel", "ring", "amulet",
"charm", "gem", "rune", "material", "potion", "consumable"
]
},
"itemType": {"type": ["string", "null"]},
"quality": {
"type": "string",
"enum": ["crude", "normal", "superior"]
},
"rarity": {
"type": "string",
"enum": ["low", "normal", "magic", "rare", "set", "unique", "crafted"]
},
"runes": {"type": ["string", "null"]},
"damage": {
"type": ["object", "null"],
"properties": {
"oneHand": {"$ref": "#/$defs/range"},
"twoHand": {"$ref": "#/$defs/range"},
"throw": {"$ref": "#/$defs/range"},
"smite": {"$ref": "#/$defs/range"}
},
"required": ["oneHand", "twoHand", "throw", "smite"],
"additionalProperties": false
},
"defense": {"type": ["integer", "null"]},
"chanceToBlock": {"type": ["integer", "null"]},
"durability": {
"type": ["object", "null"],
"properties": {
"current": {"type": "integer"},
"max": {"type": "integer"}
},
"required": ["current", "max"],
"additionalProperties": false
},
"classRestriction": {"type": ["string", "null"]},
"requiredDexterity": {"type": ["integer", "null"]},
"requiredStrength": {"type": ["integer", "null"]},
"requiredLevel": {"type": ["integer", "null"]},
"attackSpeed": {"type": ["string", "null"]},
"mods": {"type": "array", "items": {"type": "string"}},
"ethereal": {"type": "boolean"},
"sockets": {"type": "integer"}
},
"required": [
"name", "baseType", "category", "itemType", "quality", "rarity", "runes",
"damage", "defense", "chanceToBlock", "durability", "classRestriction",
"requiredDexterity", "requiredStrength", "requiredLevel", "attackSpeed",
"mods", "ethereal", "sockets"
],
"additionalProperties": false,
"$defs": {
"range": {
"type": ["object", "null"],
"properties": {
"min": {"type": "integer"},
"max": {"type": "integer"}
},
"required": ["min", "max"],
"additionalProperties": false
}
}
}
Note on
rarity: rarity is essentially a function of the item-name color, which VLMs read unreliably (gold vs. yellow vs. orange are very close in hue). The model predicts it best-effort; for reliable results, treat it as a hint and prefer a pixel-based (HSV) color read of the name line.
Example outputs
Unique ring
{
"name": "Raven Frost",
"baseType": "Ring",
"category": "ring",
"itemType": null,
"quality": "normal",
"rarity": "unique",
"runes": null,
"damage": null,
"defense": null,
"chanceToBlock": null,
"durability": null,
"classRestriction": null,
"requiredDexterity": null,
"requiredStrength": null,
"requiredLevel": 45,
"attackSpeed": null,
"mods": ["+221 to Attack Rating", "Adds 15-45 Cold Damage", "+18 to Dexterity", "+40 to Mana", "Cold Absorb 20%", "Cannot Be Frozen"],
"ethereal": false,
"sockets": 0
}
Runeword (base item + rune sequence)
{
"name": "Insight",
"baseType": "Scythe",
"category": "weapon",
"itemType": "Polearm",
"quality": "normal",
"rarity": "unique",
"runes": "RalTirTalSol",
"damage": {"oneHand": null, "twoHand": {"min": 18, "max": 40}, "throw": null, "smite": null},
"defense": null,
"chanceToBlock": null,
"durability": {"current": 65, "max": 65},
"classRestriction": null,
"requiredDexterity": null,
"requiredStrength": 87,
"requiredLevel": 27,
"attackSpeed": null,
"mods": ["Level 15 Meditation Aura When Equipped", "+35% Faster Cast Rate", "+247% Enhanced Damage", "+9 to Minimum Damage", "213% Bonus to Attack Rating", "Adds 5-30 Fire Damage", "+75 Poison Damage Over 5 Seconds", "+5 to Critical Strike", "+5 to All Attributes", "+2 to Mana After Each Kill", "23% Better Chance of Getting Magic Items"],
"ethereal": false,
"sockets": 4
}
Magic charm (procedural name)
{
"name": "Harpoonist's Grand Charm of Sustenance",
"baseType": "Grand Charm",
"category": "charm",
"itemType": null,
"quality": "normal",
"rarity": "magic",
"runes": null,
"damage": null,
"defense": null,
"chanceToBlock": null,
"durability": null,
"classRestriction": null,
"requiredDexterity": null,
"requiredStrength": null,
"requiredLevel": 42,
"attackSpeed": null,
"mods": ["+2 to Javelin and Spear Skills (Amazon Only)", "+38 to Life"],
"ethereal": false,
"sockets": 0
}
Training data
- Synthetic tooltips (primary): procedurally rendered from D2R game-data tables (item bases, affixes, uniques, sets, runewords) and composited over real stash/inventory backgrounds. Rendering is calibrated to the in-game tooltip (font, colors, line spacing, panel, border, separator) to minimize the syntheticโreal gap.
- Real captures (small, verified): a handful of hand-verified in-game tooltip crops mixed in (oversampled) as a domain anchor.
Each training example is: system prompt + user turn + the 784ร504 tooltip image, with the target JSON as the assistant turn.
Training procedure
- Framework: Unsloth + TRL
SFTTrainer, LoRA on vision and language layers. - Precision: bf16 with gradient checkpointing.
- Key hyperparameters: LoRA r=16 / alpha=16, learning rate 2e-4, linear schedule, effective batch via gradient accumulation, ~1 epoch over the synthetic set.
- Hardware: AMD Radeon RX 9070 (gfx1201) under WSL2 + ROCm.
How to run
Use any OpenAI-compatible local server (e.g. llama.cpp / LM Studio) with the GGUF build (model + vision mmproj):
- Crop the tooltip and fit it to a 784ร504 canvas (aspect-preserving, black pad).
- Send the image with the system prompt above and
temperature = 0, constraining the response to the JSON schema.
Limitations
- Domain-locked: only English D2R tooltips, cropped and canvas-fit as above.
- Color/rarity: see the rarity note โ prefer a pixel-based rarity source.
- Tiny digits / very long tooltips can still be misread; verify critical values.
- No formal benchmark; evaluated qualitatively on held-out real captures.
License
Derived/LoRA weights are released under Apache-2.0, inheriting the base
model's license (verify the exact terms of Qwen/Qwen3-VL-4B-Instruct before
redistribution). Diablo II: Resurrected and all related assets/trademarks belong
to Blizzard Entertainment; this is an unofficial, fan-made model with no
affiliation or endorsement.
Acknowledgements
- Qwen team for Qwen3-VL.
- Unsloth for the fine-tuning stack.
- The blizzhackers
d2dataproject for the game-data tables used to generate the synthetic training data.
- Downloads last month
- 26
8-bit
Model tree for Seismoforg/Qwen3-VL-D2R-Item-Reader-4B-Instruct
Base model
Qwen/Qwen3-VL-4B-Instruct