Instructions to use lukesalamone/chess-reasoner-0.8b with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use lukesalamone/chess-reasoner-0.8b with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="lukesalamone/chess-reasoner-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("lukesalamone/chess-reasoner-0.8b") model = AutoModelForMultimodalLM.from_pretrained("lukesalamone/chess-reasoner-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 lukesalamone/chess-reasoner-0.8b with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "lukesalamone/chess-reasoner-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": "lukesalamone/chess-reasoner-0.8b", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/lukesalamone/chess-reasoner-0.8b
- SGLang
How to use lukesalamone/chess-reasoner-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 "lukesalamone/chess-reasoner-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": "lukesalamone/chess-reasoner-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 "lukesalamone/chess-reasoner-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": "lukesalamone/chess-reasoner-0.8b", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use lukesalamone/chess-reasoner-0.8b with Docker Model Runner:
docker model run hf.co/lukesalamone/chess-reasoner-0.8b
Chess Reasoner 0.8B
A small 0.8B parameter language model that finds chess moves by using tools.
The model calls tools to gather facts about the board: what pieces attack a square, which pieces are hanging, what happens after Nxe5. Each tool call requires a sentence justifying its purpose. After the model has checked candidate moves, it answers with its chosen move.
The base model is Qwen3.5-0.8B, fine-tuned with supervised learning followed by reinforcement learning (GRPO).
Installation
Please see the Github repo for installation instructions: https://github.com/lukesalamone/chess_reasoner_demo
The agent loop
The model makes one tool call at a time. Example:
<tool>attackers(e5) | why: Is the pawn on e5 defended? If not, Nxe5 wins it.</tool>
The harness then runs the call on a scratch board and sends back the result:
<result>e5 (black pawn): attacked by Nf3; defended by Nc6.</result>
The model then either makes another tool call or gives a final answer. The harness limits the model to 24 tool calls, and requires a justification for making the tool call itself to prevent the reasoning trace from devolving into a list of bare tool calls. There is some basic validation on the justifications to prevent malformed, short, long, or repeated justifications.
| Tool Call | Description |
|---|---|
piece_on(square) |
What piece is on a square |
pieces() |
Every piece on the board and where it is (both sides) |
material() |
Material count for both sides in pawn units |
attackers(square) |
Which pieces (any side) attack a square. If occupied, distinguishes attackers from defenders |
targets(square) |
What enemy pieces the piece on square attacks, and what friendly pieces it defends |
hanging(color) |
What pieces of this color are hanging |
pins(color) |
What pins against the king for color |
forcing_moves() |
Legal checks, captures, and promotions are available in this position |
legal_moves() |
Every legal move in this position, plus whether the side to move is in check |
try(move) |
Push a move onto the stack to evaluate one ply deeper (up to 4 deep) |
undo() |
Pop off the stack to back out of a search |
quiet_moves() |
Legal moves that are not in forcing_moves() |
root() |
Pop the whole stack to return to the starting position |
threats() |
What the opponent could do if it were their move: mates, checks, captures that win material |
exchange(square) |
Result of the capture sequence on a square, starting with the best first capture |
Prompts and sampling
The system prompt and user prompt are in the demo repo (system_prompt.txt, prompt.py). The model was trained with them and expects them unchanged. The chat template is plain ChatML (chat_template.jinja). Each assistant turn is one tool call with its reason. After its calls, the model ends with <answer>MOVE</answer>, where MOVE is in SAN.
Evaluation
The test set is 775 held-out positions that are still contested (Stockfish evaluation within 1000 centipawns, no forced mate). None of these positions was used in training.
A move counts as a "hit" when it is within 50 centipawns of Stockfish's best move at depth 20. Four answers were sampled per position.
| hit rate | |
|---|---|
| a random legal move | 25.2% |
| chess-reasoner-0.8b | 57.1% |
It answers with a legal move 99.9% of the time, after 22 tool calls on average.
Training details
- Supervised fine-tuning on 34k reasoning transcripts from a teacher model. Transcripts were kept only if their final move was within 50 centipawns of the best move.
- LoRA rank 64, 1 epoch, LR 1e-4, BS 16, max sequence length 6,144 tokens
- GRPO (group relative policy optimization) for 150 steps:
- 16 positions per step, 16 samples per position, temperature 1.0, LR 1e-5
- The reward was exp(โcpl / 100) for a legal answer and 0 otherwise, where cpl is the answer's centipawn loss against Stockfish at depth 20.
The LoRA weights are merged into the bf16 model. The training positions and test positions come from the ChessBench dataset.
Limitations
- It needs the tool loop. Without tools it has no reliable view of the board.
- It only picks moves. It was not trained to discuss chess in general.
- On test positions, hit rate is only about 57%.
More information
You can find more information in my blog post: https://blog.lukesalamone.com/posts/chess-reasoning-llm/
License
Apache 2.0
- Downloads last month
- 372