Image-Text-to-Text
Transformers
Safetensors
English
qwen3_5
piko
piko-9b
multimodal
vision-language
hybrid-attention
linear-attention
ocr
document-understanding
conversational
Instructions to use Dexy2/Piko-9b with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Dexy2/Piko-9b with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="Dexy2/Piko-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("Dexy2/Piko-9b") model = AutoModelForMultimodalLM.from_pretrained("Dexy2/Piko-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 Dexy2/Piko-9b with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Dexy2/Piko-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": "Dexy2/Piko-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/Dexy2/Piko-9b
- SGLang
How to use Dexy2/Piko-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 "Dexy2/Piko-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": "Dexy2/Piko-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 "Dexy2/Piko-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": "Dexy2/Piko-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 Dexy2/Piko-9b with Docker Model Runner:
docker model run hf.co/Dexy2/Piko-9b
| #!/usr/bin/env python3 | |
| """Load Piko-9b once and exercise every capability the model card claims. | |
| Each check is recorded independently, so a failure in one modality does not | |
| hide the results of the others. Output is a JSON record suitable for pasting | |
| into the audit report; nothing here is scored by hand. | |
| Usage | |
| ----- | |
| python scripts/validate_inference.py --model <path-or-repo-id> \ | |
| --output reports/inference_validation.json | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import platform | |
| import sys | |
| import time | |
| import traceback | |
| from collections.abc import Callable | |
| from pathlib import Path | |
| from typing import Any | |
| def build_ocr_image(path: Path) -> None: | |
| """Render a deterministic synthetic receipt. No network, no licensing risk.""" | |
| from PIL import Image, ImageDraw | |
| image = Image.new("RGB", (520, 300), "white") | |
| draw = ImageDraw.Draw(image) | |
| lines = [ | |
| "NORTHGATE HARDWARE", | |
| "144 Mill Road", | |
| "", | |
| "Date: 2026-03-14", | |
| "Invoice: 40817", | |
| "", | |
| "Hex bolts M6 12.40", | |
| "Wood glue 6.25", | |
| "Sandpaper pack 4.10", | |
| "", | |
| "TOTAL 22.75", | |
| ] | |
| y = 18 | |
| for line in lines: | |
| draw.text((24, y), line, fill="black") | |
| y += 24 | |
| image.save(path) | |
| def build_chart_image(path: Path) -> None: | |
| """Render a deterministic bar chart with labelled values.""" | |
| from PIL import Image, ImageDraw | |
| image = Image.new("RGB", (460, 300), "white") | |
| draw = ImageDraw.Draw(image) | |
| bars = [("Q1", 40), ("Q2", 95), ("Q3", 60), ("Q4", 130)] | |
| base_y = 250 | |
| for index, (label, value) in enumerate(bars): | |
| x = 60 + index * 90 | |
| draw.rectangle([x, base_y - value, x + 50, base_y], fill="black") | |
| draw.text((x + 12, base_y + 8), label, fill="black") | |
| draw.text((x + 6, base_y - value - 16), str(value), fill="black") | |
| draw.text((40, 12), "Units sold by quarter", fill="black") | |
| image.save(path) | |
| class Validator: | |
| def __init__( | |
| self, model_path: str, dtype: str, device_map: Any, quantization: str = "none" | |
| ) -> None: | |
| self.model_path = model_path | |
| self.dtype = dtype | |
| self.device_map = device_map | |
| self.quantization = quantization | |
| self.results: list[dict[str, Any]] = [] | |
| self.model = None | |
| self.processor = None | |
| self.tokenizer = None | |
| # -- harness ---------------------------------------------------------- # | |
| def check(self, name: str, fn: Callable[[], Any]) -> Any: | |
| started = time.perf_counter() | |
| try: | |
| detail = fn() | |
| record = { | |
| "check": name, | |
| "status": "pass", | |
| "seconds": round(time.perf_counter() - started, 2), | |
| "detail": detail, | |
| } | |
| except Exception as exc: # noqa: BLE001 - every failure must be recorded | |
| record = { | |
| "check": name, | |
| "status": "fail", | |
| "seconds": round(time.perf_counter() - started, 2), | |
| "error": f"{type(exc).__name__}: {exc}", | |
| "traceback": traceback.format_exc(limit=4), | |
| } | |
| self.results.append(record) | |
| marker = "PASS" if record["status"] == "pass" else "FAIL" | |
| print(f"[{marker}] {name} ({record['seconds']}s)", flush=True) | |
| if record["status"] == "fail": | |
| print(f" {record['error']}", flush=True) | |
| return record | |
| # -- loading ---------------------------------------------------------- # | |
| def load(self) -> dict[str, Any]: | |
| import torch | |
| from transformers import AutoConfig, AutoProcessor, AutoTokenizer | |
| torch_dtype = {"bfloat16": torch.bfloat16, "float16": torch.float16}[self.dtype] | |
| config = AutoConfig.from_pretrained(self.model_path) | |
| extra: dict[str, Any] = {} | |
| if self.quantization == "4bit": | |
| from transformers import BitsAndBytesConfig | |
| extra["quantization_config"] = BitsAndBytesConfig( | |
| load_in_4bit=True, | |
| bnb_4bit_quant_type="nf4", | |
| bnb_4bit_compute_dtype=torch_dtype, | |
| bnb_4bit_use_double_quant=True, | |
| ) | |
| elif self.quantization == "8bit": | |
| from transformers import BitsAndBytesConfig | |
| extra["quantization_config"] = BitsAndBytesConfig(load_in_8bit=True) | |
| loaded_with = None | |
| model = None | |
| errors: dict[str, str] = {} | |
| for class_name in ("AutoModelForMultimodalLM", "AutoModelForImageTextToText"): | |
| try: | |
| import transformers | |
| cls = getattr(transformers, class_name) | |
| except AttributeError: | |
| errors[class_name] = "class not available in this transformers version" | |
| continue | |
| try: | |
| model = cls.from_pretrained( | |
| self.model_path, | |
| dtype=torch_dtype, | |
| device_map=self.device_map, | |
| **extra, | |
| ) | |
| loaded_with = class_name | |
| break | |
| except Exception as exc: # noqa: BLE001 | |
| errors[class_name] = f"{type(exc).__name__}: {exc}" | |
| if model is None: | |
| raise RuntimeError(f"No auto class could load the model: {errors}") | |
| model.eval() | |
| self.model = model | |
| self.processor = AutoProcessor.from_pretrained(self.model_path) | |
| self.tokenizer = AutoTokenizer.from_pretrained(self.model_path) | |
| parameters = sum(p.numel() for p in model.parameters()) | |
| vision_parameters = 0 | |
| if hasattr(model, "model") and hasattr(model.model, "visual"): | |
| vision_parameters = sum(p.numel() for p in model.model.visual.parameters()) | |
| return { | |
| "loaded_with": loaded_with, | |
| "auto_class_errors": errors, | |
| "trust_remote_code_required": False, | |
| "architectures": config.architectures, | |
| "model_type": config.model_type, | |
| "total_parameters": parameters, | |
| "vision_parameters": vision_parameters, | |
| "language_parameters": parameters - vision_parameters, | |
| "device_map": str(getattr(model, "hf_device_map", self.device_map)), | |
| "processor_class": type(self.processor).__name__, | |
| "tokenizer_class": type(self.tokenizer).__name__, | |
| } | |
| # -- generation helpers ----------------------------------------------- # | |
| def _generate(self, messages: list[dict[str, Any]], max_new_tokens: int) -> str: | |
| import torch | |
| inputs = self.processor.apply_chat_template( | |
| messages, | |
| add_generation_prompt=True, | |
| tokenize=True, | |
| return_dict=True, | |
| return_tensors="pt", | |
| ).to(self.model.device) | |
| with torch.inference_mode(): | |
| output = self.model.generate(**inputs, max_new_tokens=max_new_tokens, do_sample=False) | |
| prompt_length = inputs["input_ids"].shape[1] | |
| return self.processor.decode(output[0][prompt_length:], skip_special_tokens=True).strip() | |
| def text_only(self, prompt: str, max_new_tokens: int = 96) -> dict[str, Any]: | |
| messages = [{"role": "user", "content": [{"type": "text", "text": prompt}]}] | |
| text = self._generate(messages, max_new_tokens) | |
| return {"prompt": prompt, "response": text} | |
| def with_image( | |
| self, image_path: Path, prompt: str, max_new_tokens: int = 128 | |
| ) -> dict[str, Any]: | |
| messages = [ | |
| { | |
| "role": "user", | |
| "content": [ | |
| {"type": "image", "url": str(image_path)}, | |
| {"type": "text", "text": prompt}, | |
| ], | |
| } | |
| ] | |
| text = self._generate(messages, max_new_tokens) | |
| return {"image": image_path.name, "prompt": prompt, "response": text} | |
| def main() -> None: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--model", required=True) | |
| parser.add_argument("--dtype", default="bfloat16", choices=["bfloat16", "float16"]) | |
| parser.add_argument("--device-map", default="auto") | |
| parser.add_argument( | |
| "--quantization", | |
| default="none", | |
| choices=["none", "4bit", "8bit"], | |
| help="CPU offload corrupts this architecture; use 4bit to stay resident on one GPU.", | |
| ) | |
| parser.add_argument("--assets", type=Path, default=Path("evaluation/prompts/assets")) | |
| parser.add_argument("--output", type=Path, default=Path("reports/inference_validation.json")) | |
| args = parser.parse_args() | |
| import torch | |
| import transformers | |
| args.assets.mkdir(parents=True, exist_ok=True) | |
| ocr_image = args.assets / "synthetic_receipt.png" | |
| chart_image = args.assets / "synthetic_chart.png" | |
| build_ocr_image(ocr_image) | |
| build_chart_image(chart_image) | |
| device_map: Any = args.device_map | |
| if args.quantization != "none" and device_map == "auto": | |
| device_map = {"": 0} # keep every module on one device | |
| validator = Validator(args.model, args.dtype, device_map, args.quantization) | |
| environment = { | |
| "python": platform.python_version(), | |
| "platform": platform.platform(), | |
| "torch": torch.__version__, | |
| "transformers": transformers.__version__, | |
| "cuda_available": torch.cuda.is_available(), | |
| "gpu": torch.cuda.get_device_name(0) if torch.cuda.is_available() else None, | |
| "vram_bytes": torch.cuda.get_device_properties(0).total_memory | |
| if torch.cuda.is_available() | |
| else None, | |
| "dtype": args.dtype, | |
| "device_map": str(device_map), | |
| "quantization": args.quantization, | |
| "model": args.model, | |
| "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S%z"), | |
| } | |
| load_record = validator.check("load_model", validator.load) | |
| if load_record["status"] == "fail": | |
| _write(args.output, environment, validator.results) | |
| sys.exit("Model failed to load; remaining checks skipped.") | |
| validator.check( | |
| "text_only_generation", | |
| lambda: validator.text_only("Write a Python function that reverses a string."), | |
| ) | |
| validator.check( | |
| "text_only_identity", | |
| lambda: validator.text_only("What model are you? Answer in one short sentence.", 48), | |
| ) | |
| validator.check( | |
| "text_only_reasoning", | |
| lambda: validator.text_only( | |
| "A shop sells pens at 3 for $2. How much do 12 pens cost? Answer with the number only.", | |
| 48, | |
| ), | |
| ) | |
| validator.check( | |
| "image_ocr", | |
| lambda: validator.with_image( | |
| ocr_image, "Read this receipt. Give the merchant name and the total." | |
| ), | |
| ) | |
| validator.check( | |
| "image_document_json", | |
| lambda: validator.with_image( | |
| ocr_image, | |
| 'Return only JSON: {"merchant": str, "date": "YYYY-MM-DD", "total": float}', | |
| ), | |
| ) | |
| validator.check( | |
| "image_chart", | |
| lambda: validator.with_image(chart_image, "Which quarter is highest, and what value?"), | |
| ) | |
| validator.check( | |
| "image_caption", | |
| lambda: validator.with_image(chart_image, "Describe this image in one sentence."), | |
| ) | |
| def multi_turn() -> dict[str, Any]: | |
| messages = [ | |
| {"role": "user", "content": [{"type": "text", "text": "My favourite number is 47."}]}, | |
| {"role": "assistant", "content": [{"type": "text", "text": "Noted."}]}, | |
| { | |
| "role": "user", | |
| "content": [{"type": "text", "text": "Double my favourite number. Number only."}], | |
| }, | |
| ] | |
| return {"response": validator._generate(messages, 32)} | |
| validator.check("multi_turn_conversation", multi_turn) | |
| def determinism() -> dict[str, Any]: | |
| first = validator.text_only("Name three primary colours.", 32)["response"] | |
| second = validator.text_only("Name three primary colours.", 32)["response"] | |
| return {"identical": first == second, "first": first, "second": second} | |
| validator.check("greedy_determinism", determinism) | |
| def batch() -> dict[str, Any]: | |
| import torch | |
| prompts = ["Capital of Japan?", "2 + 2 = ?"] | |
| texts = [ | |
| validator.processor.apply_chat_template( | |
| [{"role": "user", "content": [{"type": "text", "text": p}]}], | |
| add_generation_prompt=True, | |
| tokenize=False, | |
| ) | |
| for p in prompts | |
| ] | |
| inputs = validator.processor(text=texts, return_tensors="pt", padding=True).to( | |
| validator.model.device | |
| ) | |
| with torch.inference_mode(): | |
| output = validator.model.generate(**inputs, max_new_tokens=24, do_sample=False) | |
| decoded = [ | |
| validator.processor.decode( | |
| output[i][inputs["input_ids"].shape[1] :], skip_special_tokens=True | |
| ).strip() | |
| for i in range(len(prompts)) | |
| ] | |
| return {"prompts": prompts, "responses": decoded} | |
| validator.check("batch_inference", batch) | |
| def long_context() -> dict[str, Any]: | |
| needle = "The maintenance code for the north pump is QF-8812." | |
| filler = "Routine log entry: all systems nominal. " * 900 | |
| prompt = f"{filler}\n{needle}\n{filler}\n\nWhat is the maintenance code for the north pump?" | |
| tokens = len(validator.tokenizer(prompt)["input_ids"]) | |
| response = validator.text_only(prompt, 32)["response"] | |
| return { | |
| "prompt_tokens": tokens, | |
| "response": response, | |
| "contains_needle": "QF-8812" in response, | |
| } | |
| validator.check("long_context_retrieval", long_context) | |
| _write(args.output, environment, validator.results) | |
| passed = sum(1 for r in validator.results if r["status"] == "pass") | |
| print(f"\n{passed}/{len(validator.results)} checks passed -> {args.output}") | |
| def _write(output: Path, environment: dict[str, Any], results: list[dict[str, Any]]) -> None: | |
| output.parent.mkdir(parents=True, exist_ok=True) | |
| payload = { | |
| "environment": environment, | |
| "summary": { | |
| "total": len(results), | |
| "passed": sum(1 for r in results if r["status"] == "pass"), | |
| "failed": sum(1 for r in results if r["status"] == "fail"), | |
| }, | |
| "results": results, | |
| } | |
| output.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8") | |
| if __name__ == "__main__": | |
| main() | |