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)# 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=40) 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
| """ | |
| Generate the synthetic Arabic math-reasoning corpus with google/gemma-4-12B-it. | |
| Resumable by construction: | |
| * A "task" is one generation call that asks for ITEMS_PER_TASK problems. Task N's prompt is a | |
| pure function of N (synth_common.build_task), so nothing about the plan is stored — restart | |
| the script and it redraws the identical prompts. | |
| * Every completed task is appended to out_synth/generations.jsonl as one line and fsynced. | |
| On start the file is replayed, completed task_ids are skipped, and a torn final line (a | |
| machine that died mid-write) is dropped. Worst case a crash costs one in-flight batch. | |
| * The stop condition is *accepted rows*, not tasks: each cached generation is re-validated on | |
| load, so a resumed run knows how many good rows it already has and issues only what's missing. | |
| Backends: | |
| BACKEND=hf transformers batched generation (default, works here) | |
| BACKEND=vllm vLLM continuous batching (see the note below) | |
| vLLM cannot serve Gemma 4 on this box: every vLLM release that knows the gemma4 architecture | |
| (>= 0.20.2) pins torch 2.11, which is a CUDA 13 build, and this host's driver (550 / CUDA 12.4) | |
| caps at CUDA 12.x — vllm imports die on `libcudart.so.13`. The vLLM path is kept working for a | |
| box with driver >= 580, or for a gemma3/Qwen generator on the pinned .venv-vllm stack. | |
| Usage: | |
| P=/notebooks/50M/.venv-lfm2/bin/python | |
| TARGET=100000 $P -u synth_generate.py | |
| """ | |
| import json | |
| import os | |
| import sys | |
| import time | |
| from pathlib import Path | |
| import synth_common as sc | |
| MODEL_DIR = os.environ.get("GEN_MODEL", "./models/gemma-4-12B-it") | |
| OUT_DIR = Path(os.environ.get("OUT_DIR", "out_synth")) | |
| CACHE = OUT_DIR / "generations.jsonl" | |
| TARGET = int(os.environ.get("TARGET", 100_000)) | |
| BATCH = int(os.environ.get("BATCH", 32)) | |
| MAX_NEW = int(os.environ.get("MAX_NEW", 1400)) | |
| TEMPERATURE = float(os.environ.get("TEMPERATURE", 0.9)) | |
| TOP_P = float(os.environ.get("TOP_P", 0.95)) | |
| SEED = int(os.environ.get("SEED", 1234)) | |
| BACKEND = os.environ.get("BACKEND", "hf") | |
| # Recorded on every cached row so a merged multi-node corpus stays attributable. | |
| MODEL_NAME = os.environ.get("GEN_MODEL_NAME", os.path.basename(MODEL_DIR.rstrip("/"))) | |
| # Multimodal wrappers can default to eager attention, which is several times slower to decode. | |
| ATTN = os.environ.get("ATTN", "sdpa") | |
| MAX_TASKS = int(os.environ.get("MAX_TASKS", 400_000)) | |
| # Two machines generating at once must own disjoint task-id ranges: build_task() is a pure | |
| # function of the id, so overlapping ranges redraw byte-identical prompts and every row the | |
| # second machine produces dies as a duplicate template in build_synth_dataset.py. START_TASK | |
| # offsets this node's range; MAX_TASKS is counted from there, not from zero. | |
| START_TASK = int(os.environ.get("START_TASK", 0)) | |
| # Which slice of the variation grid to draw from. "default" is the original 15-operation grid; | |
| # "relational" is the comparison pool (x = 2y, x = y/2, x = y + n ...) that the default grid never | |
| # produced. Use a disjoint START_TASK for a relational run — same rule as two nodes not colliding. | |
| POOL = os.environ.get("POOL", "default") | |
| # Qwen3-style templates open a <think> block in the generation prompt unless this is passed, | |
| # and the model then spends the whole MAX_NEW budget reasoning before it ever emits a tag. | |
| CHAT_KWARGS = {"enable_thinking": False} if os.environ.get("NO_THINK", "0") == "1" else {} | |
| def load_cache(): | |
| """-> (set of finished task_ids, accepted row count). Tolerates a truncated final line.""" | |
| done, accepted = set(), 0 | |
| if not CACHE.exists(): | |
| return done, accepted | |
| with open(CACHE, encoding="utf-8") as fh: | |
| for line in fh: | |
| try: | |
| rec = json.loads(line) | |
| except json.JSONDecodeError: | |
| print("[!] dropping truncated final line of the cache") | |
| continue | |
| done.add(rec["task_id"]) | |
| for item in sc.parse_items(rec["raw"]): | |
| ok, _ = sc.validate(item) | |
| accepted += ok | |
| return done, accepted | |
| class HFBackend: | |
| """Batched transformers generation. No paged attention, but B sequences decode in parallel.""" | |
| def __init__(self): | |
| import torch | |
| import transformers | |
| from transformers import AutoProcessor, AutoTokenizer | |
| self.torch = torch | |
| try: | |
| self.tok = AutoProcessor.from_pretrained(MODEL_DIR).tokenizer | |
| except Exception: | |
| self.tok = AutoTokenizer.from_pretrained(MODEL_DIR) | |
| self.tok.padding_side = "left" | |
| if self.tok.pad_token_id is None: | |
| self.tok.pad_token = self.tok.eos_token | |
| print(f"[*] loading {MODEL_DIR} (bf16)", flush=True) | |
| # gemma-4-*-it is a unified multimodal checkpoint, so the plain causal-LM auto class does | |
| # not always claim it. Try the multimodal auto classes first and fall back. | |
| self.model, last = None, None | |
| for name in ("AutoModelForMultimodalLM", "AutoModelForImageTextToText", | |
| "AutoModelForCausalLM"): | |
| cls = getattr(transformers, name, None) | |
| if cls is None: | |
| continue | |
| try: | |
| self.model = cls.from_pretrained( | |
| MODEL_DIR, dtype=torch.bfloat16, device_map="cuda:0", | |
| attn_implementation=ATTN).eval() | |
| print(f" loaded via {name}", flush=True) | |
| break | |
| except Exception as exc: # noqa: BLE001 - report the last failure | |
| last = f"{name}: {exc}" | |
| if self.model is None: | |
| raise RuntimeError(f"could not load {MODEL_DIR}; last error -> {last}") | |
| self.model.config.use_cache = True | |
| def generate(self, prompts): | |
| texts = [self.tok.apply_chat_template([{"role": "user", "content": p}], | |
| tokenize=False, add_generation_prompt=True, | |
| **CHAT_KWARGS) | |
| for p in prompts] | |
| enc = self.tok(texts, return_tensors="pt", padding=True, add_special_tokens=False).to("cuda:0") | |
| with self.torch.no_grad(): | |
| out = self.model.generate(**enc, max_new_tokens=MAX_NEW, do_sample=True, | |
| temperature=TEMPERATURE, top_p=TOP_P, | |
| pad_token_id=self.tok.pad_token_id) | |
| width = enc["input_ids"].shape[1] | |
| return [self.tok.decode(seq[width:], skip_special_tokens=True) for seq in out] | |
| class VLLMBackend: | |
| def __init__(self): | |
| from vllm import LLM, SamplingParams | |
| kw = {} | |
| if os.environ.get("QUANT"): # e.g. QUANT=modelopt for NVFP4 checkpoints | |
| kw["quantization"] = os.environ["QUANT"] | |
| self.llm = LLM(model=MODEL_DIR, dtype=os.environ.get("DTYPE", "bfloat16"), | |
| max_num_seqs=BATCH, | |
| gpu_memory_utilization=float(os.environ.get("GPU_UTIL", 0.90)), | |
| max_model_len=int(os.environ.get("MAX_LEN", 4096)), **kw) | |
| self.params = SamplingParams(temperature=TEMPERATURE, top_p=TOP_P, max_tokens=MAX_NEW, | |
| seed=None) | |
| self.tok = self.llm.get_tokenizer() | |
| def generate(self, prompts): | |
| texts = [self.tok.apply_chat_template([{"role": "user", "content": p}], | |
| tokenize=False, add_generation_prompt=True, | |
| **CHAT_KWARGS) | |
| for p in prompts] | |
| outs = self.llm.generate(texts, self.params) | |
| return [o.outputs[0].text for o in outs] | |
| def main(): | |
| OUT_DIR.mkdir(exist_ok=True) | |
| done, accepted = load_cache() | |
| print(f"[*] cache: {len(done):,} tasks done, {accepted:,} rows accepted " | |
| f"(target {TARGET:,}) | model {MODEL_NAME} | tasks {START_TASK:,}.." | |
| f"{START_TASK + MAX_TASKS:,}", flush=True) | |
| if accepted >= TARGET: | |
| print("[+] target already met — nothing to do") | |
| return | |
| backend = (VLLMBackend if BACKEND == "vllm" else HFBackend)() | |
| next_id = START_TASK | |
| last_id = START_TASK + MAX_TASKS | |
| started, gen_rows, gen_tasks = time.time(), 0, 0 | |
| fh = open(CACHE, "a", encoding="utf-8") | |
| while accepted < TARGET and next_id < last_id: | |
| batch = [] | |
| while len(batch) < BATCH and next_id < last_id: | |
| if next_id not in done: | |
| batch.append((next_id, *sc.build_task(next_id, SEED, POOL))) | |
| next_id += 1 | |
| if not batch: | |
| break | |
| t0 = time.time() | |
| raws = backend.generate([p for _, _, p in batch]) | |
| batch_accept = 0 | |
| for (task_id, axes, _), raw in zip(batch, raws): | |
| fh.write(json.dumps({"task_id": task_id, "axes": axes, "raw": raw, | |
| "model": MODEL_NAME}, ensure_ascii=False) + "\n") | |
| for item in sc.parse_items(raw): | |
| ok, _ = sc.validate(item) | |
| batch_accept += ok | |
| fh.flush() | |
| os.fsync(fh.fileno()) # a crash costs the in-flight batch, never the cache | |
| accepted += batch_accept | |
| gen_rows += batch_accept | |
| gen_tasks += len(batch) | |
| dt = time.time() - t0 | |
| rate = gen_rows / max(time.time() - started, 1e-9) | |
| eta = (TARGET - accepted) / rate / 3600 if rate > 0 else float("inf") | |
| print(f"[{accepted:>7,}/{TARGET:,}] +{batch_accept:>3} rows " | |
| f"batch {len(batch)} in {dt:5.1f}s " | |
| f"accept {batch_accept / (len(batch) * sc.ITEMS_PER_TASK):5.1%} " | |
| f"{rate * 3600:,.0f} rows/h ETA {eta:4.1f}h", flush=True) | |
| fh.close() | |
| print(f"[+] {accepted:,} accepted rows in cache after {gen_tasks:,} new tasks") | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |