llm1b β€” a 1B-equivalent tool-using assistant trained in ~1 hour

Train a coherent, tool-calling LLM (warm-started from Qwen2.5-1.5B-Instruct, 1.54B params) from scratch on public SFT data in 61 minutes on a single Modal B300 β€” well under the 3-hour budget.

Final model: https://huggingface.co/KookiesXy/llm1b-1.5b-toolchat (public)

Metric Value
Training wall time 61.2 min (5,478 steps)
Tokens trained 179.5M (43,820 packed blocks Γ— 4,096)
Throughput ~48.9k train tok/s (bf16, SDPA, grad checkpointing)
Final holdout loss 0.855
Chat coherence (7 tasks, avg) 1.0
Tool tasks passed 12/12 in 3 consecutive eval runs; correct tool + args in every run (12/12)

What the model can do

  • Chat: fluent multi-sentence answers, stories, haiku, explanations, plans.
  • Tools: given 1–2 relevant tool schemas in the prompt, emits perfect <tool_call>{"name": ..., "arguments": {...}}</tool_call> JSON, executes the tool in an agent loop, and answers from the result.

Example (from the eval transcript, weather_tokyo):

user:   What's the weather in Tokyo?
model:  <tool_call>{"name": "get_weather", "arguments": {"city": "Tokyo"}}</tool_call>
tool:   {"city": "Tokyo", "temperature": 27, "condition": "rainy"}
model:  The current temperature in Tokyo is 27 degrees Celsius and it's currently raining.
        Please bring an umbrella if you go outside.

Why it's fast

The whole training run is a single tightly-packed SFT pass over a small, high-quality dataset. No RL, no alignment loop, no data scaling.

  1. Data: 201,754 unique examples
    • Chat: mlabonne/orpo-dpo-mix-40k (40k), databricks/databricks-dolly-15k (15k), HuggingFaceH4/ultrachat_200k (60k subsample)
    • Tools: glaiveai/glaive-function-calling-v2 (~11k), converted from the raw USER/ASSISTANT/FUNCTION RESPONSE format into native Qwen messages + tools (OpenAI-style schemas) via glaive_to_messages; plus handcrafted "golden" multi-round tool examples. Tool examples are repeated Γ—2 so roughly 55% of training tokens are tool-use.
  2. Packing: examples are tokenized and packed into 4,096-token blocks with an assistant-only label mask. label[i] = ids[i+1] if assistant_mask[i+1] else -100 β€” predicting the next token (not the current one) is what prevents the copy-collapse bug (loss β†’ 0.001, newline-only generations).
  3. Training: 1.5B params, bf16, SDPA attention, gradient checkpointing, fused AdamW, LR 1.5e-5 β†’ 2e-6 cosine over 5,478 steps, batch 8 Γ— 4096 = 32k tok/step, grad clip 1.0. On the B300 this sustains ~49k tok/s β€” the whole 179.5M-token pass finishes in ~61 min.
  4. Eval during training: holdout loss + chat samples + tool samples every 300 steps; best checkpoint saved; resume from ckpt/last.pt.

Tool use: what worked and what didn't

  • βœ… Native <tools> format: training on Qwen's native tool-call rendering (schemas in the system message, <tool_call> XML output) makes the model emit clean JSON tool calls.
  • βœ… Small candidate toolset at inference: giving the model the relevant 1–2 schemas per request works reliably. The full 6-schema preamble degrades into a {"\n{"\n{... loop (the 1.5B model can't route among 6 similar schemas yet). In production this is standard "tool routing": pick the 1–2 likely tools first.
  • βœ… One tool call per agent round: the eval loop executes the first <tool_call> and returns its result, like a real function-calling agent. The model sometimes emits extra speculative calls; executing only the first keeps the loop grounded.
  • βœ… Final eval (fixed harness): 12/12 tool tasks passed in three consecutive runs, chat coherence 1.0. Earlier 10-11/12 runs were harness artifacts: the loose-JSON fallback parsed example JSON printed in prose as a tool call, and the mock web_search returned NYC-pizza results for any query. The final harness parses only <tool_call> blocks and the mock echoes the query.
  • ⚠️ Residual weaknesses: occasionally emits several calls in one turn, and on rare long multi-round sessions can fall into the {"\n{ loop. Both are typical for a 1.5B model trained on only 180M tokens.

Reproduce

Prereqs: Modal account, hf-token secret, HF token in C:\Users\User\.cache\huggingface\token (or HF_TOKEN).

$env:PYTHONUTF8 = "1"   # Windows only

# 1. build the packed dataset (~9 min, CPU container)
modal run .\modal_train.py --step data --tag v2

# 2. train on a B300 (~61 min)
modal run .\modal_train.py --step train --run-id run-3

# 3. final eval on the exported model (~3 min, B300)
modal run .\modal_train.py --step eval --run-id run-3

The train step auto-exports hf_export/ (HF format) and pushes to KookiesXy/llm1b-1.5b-toolchat. If the push ever fails (the pinned image's push_to_hub rejects safe_serialization=), run:

modal run .\push_hf.py --run-id run-3

Inspect live checkpoints:

modal run .\diag.py --run-id run-3 --temperature 0.6 --one-tool

Files

  • data_build.py β€” dataset download, glaiveβ†’Qwen conversion, packing, holdout.
  • train_core.py β€” packed SFT loop, eval, checkpointing, export, HF push.
  • eval_core.py β€” functional eval: chat coherence + full tool loop with execution.
  • modal_train.py β€” Modal app (data/train/eval steps).
  • diag.py β€” raw generation diagnostics against a live checkpoint.
  • push_hf.py β€” manual HF push of an exported run.
  • smoke_b300.py β€” B300 throughput benchmark.
  • iso.py / iso2.py β€” isolated syntax checks.
Downloads last month
-
Safetensors
Model size
2B params
Tensor type
BF16
Β·
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Model tree for KookiesXy/llm1b-1.5b-toolchat

Finetuned
(1762)
this model