Text Generation
Transformers
Safetensors
Arabic
llama
arabic
reasoning
chain-of-thought
math
gsm8k
small-language-model
slm
sft
conversational
text-generation-inference
Instructions to use oddadmix/Nawah-Math-Reasoning with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use oddadmix/Nawah-Math-Reasoning with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="oddadmix/Nawah-Math-Reasoning") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("oddadmix/Nawah-Math-Reasoning") model = AutoModelForCausalLM.from_pretrained("oddadmix/Nawah-Math-Reasoning", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.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(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use oddadmix/Nawah-Math-Reasoning with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "oddadmix/Nawah-Math-Reasoning" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "oddadmix/Nawah-Math-Reasoning", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/oddadmix/Nawah-Math-Reasoning
- SGLang
How to use oddadmix/Nawah-Math-Reasoning 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 "oddadmix/Nawah-Math-Reasoning" \ --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": "oddadmix/Nawah-Math-Reasoning", "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 "oddadmix/Nawah-Math-Reasoning" \ --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": "oddadmix/Nawah-Math-Reasoning", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use oddadmix/Nawah-Math-Reasoning with Docker Model Runner:
docker model run hf.co/oddadmix/Nawah-Math-Reasoning
Download code/space/app.py from oddadmix/Nawah-Math-Reasoning: direct link, hf CLI and curl.
- Browser
- Download file 11.3 kB
-
https://huggingface.co/oddadmix/Nawah-Math-Reasoning/resolve/main/code/space/app.py
- Command line
-
hf download hf://oddadmix/Nawah-Math-Reasoning/code/space/app.py
-
curl -L -o app.py https://huggingface.co/oddadmix/Nawah-Math-Reasoning/resolve/main/code/space/app.py
11.3 kB
| """ | |
| Nawah-Math-Reasoning โ Gradio demo. | |
| The prompt rendering (ChatML + BOS prepend) is IDENTICAL to train_reasoning.py. A 51M model | |
| is very sensitive to format drift, so do not change render_prompt() without changing training. | |
| Runs on ZeroGPU. The model is only ~52M parameters and works on CPU too, but ZeroGPU keeps | |
| responses snappy. `import spaces` must come BEFORE torch so it can patch the CUDA calls. | |
| The model emits <think>โฆ</think> before its answer, so the stream is split live into two | |
| panels: the reasoning trace and the final answer. | |
| Deploy: push this + requirements.txt + README.md to a Gradio Space. The released model is | |
| public, so no token is needed; MODEL_HF_TOKEN is still read for pointing MODEL_ID at a private | |
| checkpoint. | |
| """ | |
| import os | |
| import re | |
| import threading | |
| import spaces # import BEFORE torch so it can patch CUDA calls | |
| import gradio as gr | |
| import torch | |
| from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer | |
| # โโ Config โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| MODEL_ID = os.environ.get("MODEL_ID", "oddadmix/Nawah-Math-Reasoning") | |
| # Unused for the public release; needed only if MODEL_ID is repointed at a private repo. | |
| # HF_TOKEN is reserved by Spaces (a secret set under that name does not reach the container), | |
| # so MODEL_HF_TOKEN is the one to set. | |
| HF_TOKEN = os.environ.get("MODEL_HF_TOKEN") or os.environ.get("HF_TOKEN") | |
| IM_START, IM_END = "<|im_start|>", "<|im_end|>" | |
| THINK_OPEN, THINK_CLOSE = "<think>", "</think>" | |
| MAX_NEW_TOKENS_CAP = 1500 | |
| # โโ Load (once, at startup) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| print("[*] token env vars present:", | |
| [k for k in ("MODEL_HF_TOKEN", "HF_TOKEN") if os.environ.get(k)] or "NONE") | |
| print(f"[*] Loading {MODEL_ID} ...") | |
| tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, token=HF_TOKEN) | |
| model = AutoModelForCausalLM.from_pretrained(MODEL_ID, dtype=torch.bfloat16, token=HF_TOKEN) | |
| model.to("cuda").eval() | |
| CTX = getattr(model.config, "max_position_embeddings", 2048) | |
| _eos = {tokenizer.eos_token_id} if tokenizer.eos_token_id is not None else set() | |
| _im_end = tokenizer.convert_tokens_to_ids(IM_END) | |
| if isinstance(_im_end, int) and _im_end >= 0: | |
| _eos.add(_im_end) | |
| EOS_IDS = list(_eos) or None | |
| PAD_ID = tokenizer.pad_token_id if tokenizer.pad_token_id is not None else tokenizer.eos_token_id | |
| print(f"[+] {model.num_parameters():,} params | eos_ids={EOS_IDS} | ctx={CTX}") | |
| # โโ Prompt rendering โ must match train_reasoning.py โโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def render_prompt(question: str) -> str: | |
| # Single-turn only: the model was trained on one user turn per sample, so a chat | |
| # history would be out of distribution. | |
| return f"{IM_START}user\n{question.strip()}{IM_END}\n{IM_START}assistant\n" | |
| STRIP_RE = re.compile(r"<\|im_end\|>|</s>|<pad>|<s>") | |
| def split_stream(text: str): | |
| """-> (reasoning_so_far, answer_so_far). Handles the partial state mid-stream.""" | |
| text = STRIP_RE.sub("", text) | |
| if THINK_CLOSE in text: | |
| reasoning, answer = text.split(THINK_CLOSE, 1) | |
| return reasoning.replace(THINK_OPEN, "").strip(), answer.strip() | |
| return text.replace(THINK_OPEN, "").strip(), "" | |
| # โโ Generate โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def solve(question, max_new_tokens, temperature, repetition_penalty): | |
| question = (question or "").strip() | |
| if not question: | |
| yield "", "", "" | |
| return | |
| ids = tokenizer(render_prompt(question), add_special_tokens=False)["input_ids"] | |
| if tokenizer.bos_token_id is not None: | |
| ids = [tokenizer.bos_token_id] + ids # match training's explicit BOS | |
| input_ids = torch.tensor([ids], device=model.device) | |
| # skip_special_tokens must stay False โ <think>/</think> are real special tokens | |
| # in this tokenizer, and stripping them would destroy the split. | |
| streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=False) | |
| kwargs = dict( | |
| input_ids=input_ids, | |
| attention_mask=torch.ones_like(input_ids), | |
| max_new_tokens=int(max_new_tokens), | |
| repetition_penalty=float(repetition_penalty), | |
| eos_token_id=EOS_IDS, | |
| pad_token_id=PAD_ID, | |
| streamer=streamer, | |
| ) | |
| if temperature and temperature > 0: | |
| kwargs.update(do_sample=True, temperature=float(temperature), top_p=0.95) | |
| else: | |
| kwargs.update(do_sample=False) # greedy โ how the model was evaluated | |
| threading.Thread(target=model.generate, kwargs=kwargs).start() | |
| out = "" | |
| for chunk in streamer: | |
| out += chunk | |
| reasoning, answer = split_stream(out) | |
| yield reasoning, (answer or "โฆ"), out | |
| reasoning, answer = split_stream(out) | |
| if not answer: | |
| answer = "โ ๏ธ ูู ููุบูู ุงููู ูุฐุฌ ูุณู ุงูุชูููุฑ โ ุฌุฑูุจ ุณุคุงููุง ุฃูุฑุจ ูุฃู ุซูุฉ ุงูุชุฏุฑูุจ.\n" \ | |
| "(The model never closed `</think>` โ try a question closer to its training distribution.)" | |
| yield reasoning, answer, out | |
| # โโ UI โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| DESCRIPTION = """ | |
| <div style="text-align:center"> | |
| <h1>๐ง Nawah-Math-Reasoning</h1> | |
| <p>ูู ูุฐุฌ ุงุณุชุฏูุงู ุนุฑุจู ุตุบูุฑ (~52M ุจุงุฑุงู ุชุฑ) ููููุฑ ุฎุทูุฉ ุจุฎุทูุฉ ุฏุงุฎู ูุณู <code><think></code> | |
| ุซู ูุนุทู ุงูุฅุฌุงุจุฉ ุงูููุงุฆูุฉ.<br> | |
| A ~52M-parameter Arabic reasoning model that thinks step by step inside | |
| <code><think></code> before answering.</p> | |
| <p><i>ูู ูุฐุฌ ุตุบูุฑ ุจู ุง ูููู ููุนู ู ุญุชู ุนูู ุงูู ุนุงูุฌ (CPU).<br> | |
| Small enough to run on a CPU โ this Space uses ZeroGPU for snappier responses.</i></p> | |
| <p> | |
| <a href="https://huggingface.co/oddadmix/Nawah-Math-Reasoning">Model</a> ยท | |
| <a href="https://huggingface.co/oddadmix/Nawah-Math-Reasoning/tree/main/code">Training code</a> ยท | |
| <a href="https://huggingface.co/datasets/oddadmix/arabic-math-reasoning-synth">Synthetic dataset</a> ยท | |
| <a href="https://huggingface.co/datasets/oddadmix/gsm8k-reasoning-ar">GSM8K-ar dataset</a> | |
| <br><i>Weights, both datasets and the full training code are open โ Apache 2.0.</i> | |
| </p> | |
| </div> | |
| """ | |
| NOTE = """ | |
| ### ๐ ุงููุชุงุฆุฌ / Results | |
| Number agreement, greedy decoding, on held-out splits โ the same rows for every version of the | |
| model, so the numbers are comparable. | |
| | eval set | n | score | | |
| |---|---:|---:| | |
| | GSM8K-ar | 600 | **79.0%** | | |
| | Arabic_Reasoning | 400 | **73.0%** | | |
| | synthetic math | 1000 | **40.4%** | | |
| | synthetic relational (`ุถุนู`, `ูุตู`, `ุฃูุซุฑ ุจูโฆ`) | 400 | **52.2%** | | |
| ### โ ๏ธ ุญุฏูุฏ ุงููู ูุฐุฌ / Limitations | |
| ูู ูุฐุฌ ุชุฌุฑูุจู ุจุญุฌู 52M: ูุฌูุฏ **ุดูู** ุงูุงุณุชุฏูุงู ุงูุนุฑุจู ููุญูู ู ุณุงุฆู ุงููููุณุจ ูุงูุญุณุงุจ ุงูุจุณูุทุฉุ | |
| ูููู **ูุฎุทุฆ ูู ุงูุญุณุงุจ ูุซูุฑูุง** โ ุบุงูุจูุง ุฎุทูุงุช ุงูุญู ุณููู ุฉ ุซู ุชูุน ุบูุทุฉ ูู ุนู ููุฉ ุญุณุงุจูุฉ ูุงุญุฏุฉ | |
| ูููู ู ุงููู ูุฐุฌ ุนูู ุฑูู ู ุงูุฎุงุทุฆ. ุงูุฃุณุฆูุฉ ุงูู ูุชูุญุฉ ูุบูุฑ ุงูุญุณุงุจูุฉ ุฎุงุฑุฌ ูุทุงููุ ูุงูุญูุงุฑ ู ุชุนุฏุฏ | |
| ุงูุฃุฏูุงุฑ ูุฐูู. | |
| A 52M proof of concept. It reliably produces the *shape* of Arabic step-by-step reasoning, but | |
| **arithmetic errors are the dominant failure mode**: the derivation is usually structurally | |
| right, one computation is wrong, and the model then stays faithful to its own bad number. The | |
| 40.4% and 52.2% above are the honest ceiling on multi-step problems. Single-turn only; | |
| open-ended and non-mathematical questions are out of distribution. | |
| """ | |
| EXAMPLES = [ | |
| "ุฅุฐุง ูุงู ูุฏูู 1500 ุฑูุงู ูุฃูููุช 20% ู ููุง ุนูู ุงููุชุจุ ููู ุชุจูู ู ุนูุ", | |
| "ูู ู ุตูุน ุชู ุฅูุชุงุฌ 5000 ูุญุฏุฉุ ููุงูุช ูุณุจุฉ ุงููุญุฏุงุช ุงูู ุนูุจุฉ 2%ุ ูู ุง ุนุฏุฏ ุงููุญุฏุงุช ุงูุณููู ุฉุ", | |
| "ูู ู ุฏุฑุณุฉ ุจูุง 500 ุทุงูุจุ ุฅุฐุง ูุงูุช ูุณุจุฉ ุงูุฐููุฑ 55%ุ ูู ุง ุนุฏุฏ ุงูุทุงูุจุงุชุ", | |
| "ูุฏู ุชุงุฌุฑ 240 ููููุบุฑุงู ูุง ู ู ุงูุฃุฑุฒุ ุจุงุน ู ููุง 35%ุ ููู ููููุบุฑุงู ูุง ุชุจูู ูุฏููุ", | |
| "ุฅุฐุง ูุงู ุนู ุฑ ุฃุญู ุฏ 12 ุณูุฉ ูุนู ุฑ ุฃุฎูู ุถุนู ุนู ุฑูุ ูู ุง ู ุฌู ูุน ุนู ุฑููู ุงุ", | |
| "ูู ุญุฏููุฉ 80 ุญููุงููุงุ 25% ู ููุง ุทููุฑุ ููุตู ุงูุทููุฑ ุจูุถุงุก. ูู ุนุฏุฏ ุงูุทููุฑ ุงูุจูุถุงุกุ", | |
| "ุฌู ุน ุณุงู ู 45 ุตุฏูุฉุ ูุฌู ุน ุฃุฎูู ุถุนู ูุฐุง ุงูุนุฏุฏ. ูู ุตุฏูุฉ ุฌู ุนุง ู ุนูุงุ", | |
| "ูุฏู ูููู 60 ุฌููููุงุ ููุฏู ูุฏู ุฃูู ู ููุง ุจู 18 ุฌููููุง. ูู ู ุนูู ุง ู ุนูุงุ", | |
| ] | |
| with gr.Blocks(title="Nawah-Math-Reasoning") as demo: | |
| gr.HTML(DESCRIPTION) | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| question = gr.Textbox( | |
| label="ุงูุณุคุงู / Question", rtl=True, lines=3, | |
| placeholder="ุงูุชุจ ู ุณุฃูุฉ ุญุณุงุจูุฉ ููุงโฆ", | |
| ) | |
| with gr.Row(): | |
| submit = gr.Button("๐งฎ ุญู / Solve", variant="primary") | |
| clear = gr.Button("ู ุณุญ / Clear") | |
| with gr.Accordion("โ๏ธ ุฅุนุฏุงุฏุงุช ุงูุชูููุฏ / Generation settings", open=False): | |
| max_new_tokens = gr.Slider(32, MAX_NEW_TOKENS_CAP, value=320, step=8, | |
| label="ุฃูุตู ุนุฏุฏ ุชูููุฒ / Max new tokens") | |
| temperature = gr.Slider(0.0, 1.5, value=0.0, step=0.05, | |
| label="ุฏุฑุฌุฉ ุงูุญุฑุงุฑุฉ / Temperature (0 = greedy, as evaluated)") | |
| repetition_penalty = gr.Slider(1.0, 1.5, value=1.0, step=0.01, | |
| label="ุนููุจุฉ ุงูุชูุฑุงุฑ / Repetition penalty") | |
| with gr.Column(scale=4): | |
| answer_box = gr.Textbox(label="โ ุงูุฅุฌุงุจุฉ / Answer", rtl=True, lines=3) | |
| with gr.Accordion("๐ง ุงูุชูููุฑ / Reasoning trace", open=True): | |
| reasoning_box = gr.Textbox(label="", rtl=True, lines=12) | |
| with gr.Accordion("๐ ุงูู ุฎุฑุฌุงุช ุงูุฎุงู / Raw output", open=False): | |
| raw_box = gr.Textbox(label="", lines=8) | |
| gr.Examples(examples=EXAMPLES, inputs=question, label="ุฃู ุซูุฉ / Examples") | |
| gr.Markdown(NOTE) | |
| inputs = [question, max_new_tokens, temperature, repetition_penalty] | |
| outputs = [reasoning_box, answer_box, raw_box] | |
| submit.click(solve, inputs=inputs, outputs=outputs) | |
| question.submit(solve, inputs=inputs, outputs=outputs) | |
| clear.click(lambda: ("", "", "", ""), outputs=[question] + outputs) | |
| if __name__ == "__main__": | |
| demo.queue().launch(theme=gr.themes.Soft()) | |