Instructions to use UdgamLabs/Udgam-384M-Chat with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use UdgamLabs/Udgam-384M-Chat with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="UdgamLabs/Udgam-384M-Chat") 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("UdgamLabs/Udgam-384M-Chat") model = AutoModelForCausalLM.from_pretrained("UdgamLabs/Udgam-384M-Chat", 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 UdgamLabs/Udgam-384M-Chat with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "UdgamLabs/Udgam-384M-Chat" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "UdgamLabs/Udgam-384M-Chat", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/UdgamLabs/Udgam-384M-Chat
- SGLang
How to use UdgamLabs/Udgam-384M-Chat 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 "UdgamLabs/Udgam-384M-Chat" \ --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": "UdgamLabs/Udgam-384M-Chat", "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 "UdgamLabs/Udgam-384M-Chat" \ --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": "UdgamLabs/Udgam-384M-Chat", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use UdgamLabs/Udgam-384M-Chat with Docker Model Runner:
docker model run hf.co/UdgamLabs/Udgam-384M-Chat
Udgam-384M-Chat
A 384M-parameter English model for on-device tool calling, trained from scratch by UdgamLabs. It is a
decoder-only transformer compatible with the Gemma 4 architecture (Gemma4ForCausalLM), so it runs unmodified in
Ollama, LM Studio, llama.cpp, MLX and 🤗 transformers. Context: 4,096 tokens. The 4-bit GGUF is 238 MB. Built for four
jobs inside an app:
- App actions: turn a request into a function call. "wake me at 6:45, label it gym" →
set_alarm{time:"06:45", label:"gym"}. - Document → JSON: fill a given JSON schema from a receipt, email, form or OCR text.
- Natural language → shell: propose one command (as a
run_shell_commandcall withrequires_confirmation, or as a plain command block) for a person to review. - Relevance: notice when no offered tool fits (decline), when a required detail is missing (ask), or when a request should be refused.
What it is not: a knowledgeable general assistant (at 384M parameters it makes factual mistakes), or a safety-hardened open chatbot. Use it where your app decides which tools exist and confirms risky actions.
Model family: Udgam-384M-Base (2,048 context) · Udgam-384M-Base-4K · Udgam-384M-Chat · GGUF · MLX · MLX 4-bit · Technical report
| Developer | UdgamLabs |
| Parameters | 384M (20 layers, hidden size 1,024) |
| Architecture | decoder-only transformer, compatible with Gemma 4 (Gemma4ForCausalLM); sliding-window + global attention |
| Context | 4,096 tokens |
| Language | English |
| Training | pretrained from scratch on roughly 8B tokens of licensed public and synthetic text, then post-trained with supervised fine-tuning for chat and tool use |
| Licence | Apache-2.0 |
Quick start
Ollama (recommended)
Download a GGUF from the GGUF repo and create a model with its Modelfile (it uses
Ollama's native Gemma 4 renderer and parser, so tool calls and the optional thinking mode work as trained):
FROM ./Udgam-384M-Chat-Q8_0.gguf
TEMPLATE {{ .Prompt }}
RENDERER gemma4
PARSER gemma4
PARAMETER temperature 0.1
PARAMETER top_p 0.9
PARAMETER top_k 40
PARAMETER repeat_penalty 1.1
PARAMETER repeat_last_n 64
PARAMETER num_predict 1024
PARAMETER num_ctx 4096
ollama create udgam -f Modelfile
ollama run udgam "Explain what a hash map is in two sentences."
curl http://localhost:11434/api/chat -d '{
"model": "udgam", "stream": false, "think": false,
"messages": [
{"role": "system", "content": "Current date-time: Tuesday, 2027-08-17 09:12 (Europe/London, UTC+01:00)\nDevice: Android phone"},
{"role": "user", "content": "Wake me up at 6:45 tomorrow, label it gym."}],
"tools": [{"type": "function", "function": {"name": "set_alarm", "description": "Set an alarm at a local time.",
"parameters": {"type": "object", "properties": {"time": {"type": "string", "description": "HH:MM 24h"},
"label": {"type": "string", "description": "Optional label"}}, "required": ["time"]}}}]}'
# -> "tool_calls": [{"function": {"name": "set_alarm", "arguments": {"label": "Gym", "time": "06:45"}}}]
Thinking is on by default in Ollama for Gemma 4 models; send "think": false (or /set nothink) to turn it off.
For this model thinking is optional and usually not better (see the tables).
LM Studio
- Search for
UdgamLabs/Udgam-384M-Chat-GGUF(or drop the.ggufinto LM Studio's models folder) and pickQ8_0(default) orQ4_K_M(phones / low memory). - The GGUF carries the Gemma 4 chat template, so the prompt format is set automatically. Set Context Length 4096, temperature 0.1, top-p 0.9, top-k 40, repeat penalty 1.1.
- For tool use, call LM Studio's OpenAI-compatible server (
/v1/chat/completionswithtools). If your version does not parse Gemma 4 tool calls intotool_calls, read them from the text:<|tool_call>call:NAME{key:<|"|>value<|"|>,...}<tool_call|>.
llama.cpp
llama-server -m Udgam-384M-Chat-Q8_0.gguf -c 4096 --jinja # OpenAI-compatible API, parsed tool_calls
llama-server -m Udgam-384M-Chat-Q8_0.gguf -c 4096 --jinja --reasoning off # same, thinking off
llama-cli -m Udgam-384M-Chat-Q8_0.gguf -c 4096 -cnv # interactive chat
With the set_alarm tool above, /v1/chat/completions returns
tool_calls: [{"name": "set_alarm", "arguments": "{\"label\":\"Gym\",\"time\":\"06:45\"}"}] (tested with llama.cpp
build d7a695e).
MLX (Apple silicon)
pip install mlx-lm
mlx_lm.generate --model UdgamLabs/Udgam-384M-Chat-MLX-4bit --prompt "Give me three tips for sleeping better." --max-tokens 200
transformers
# tested with transformers 5.18
from transformers import AutoTokenizer, AutoModelForCausalLM
tok = AutoTokenizer.from_pretrained("UdgamLabs/Udgam-384M-Chat")
model = AutoModelForCausalLM.from_pretrained("UdgamLabs/Udgam-384M-Chat")
tools = [{"type": "function", "function": {"name": "set_alarm", "description": "Set an alarm at a local time.",
"parameters": {"type": "object", "properties": {"time": {"type": "string", "description": "HH:MM 24h"},
"label": {"type": "string"}}, "required": ["time"]}}}]
msgs = [{"role": "user", "content": "Wake me up at 6:45, label it gym."}]
inputs = tok.apply_chat_template(msgs, tools=tools, add_generation_prompt=True, return_dict=True, return_tensors="pt")
out = model.generate(**inputs, max_new_tokens=128, do_sample=False) # thinking off unless enable_thinking=True
print(tok.decode(out[0, inputs["input_ids"].shape[1]:], skip_special_tokens=False))
# <|tool_call>call:set_alarm{label:<|"|>Gym<|"|>,time:<|"|>06:45<|"|>}<tool_call|><|tool_response>
Use fp32 (or llama.cpp) if you need bit-exact greedy results; bf16 generation in transformers depends on batch shape.
Prompt format
The standard Gemma 4 chat format (the template is identical to the official one). A tool turn:
<bos><|turn>system
Current date-time: Tuesday, 2027-08-17 09:12 (Europe/London, UTC+01:00)
Device: Android phone<|tool>declaration:set_alarm{description:<|"|>Set an alarm at a local time.<|"|>,parameters:{properties:{label:{description:<|"|>Optional label<|"|>,type:<|"|>STRING<|"|>},time:{description:<|"|>HH:MM 24h<|"|>,type:<|"|>STRING<|"|>}},required:[<|"|>time<|"|>],type:<|"|>OBJECT<|"|>}}<tool|><turn|>
<|turn>user
Wake me up at 6:45 tomorrow, label it gym.<turn|>
<|turn>model
<|tool_call>call:set_alarm{label:<|"|>gym<|"|>,time:<|"|>06:45<|"|>}<tool_call|><|tool_response>
The model stops at <|tool_response>; your app runs the tool, sends the result back as a tool message, and the
model writes the final reply. With thinking on, <|think|> is added after <|turn>system\n and the model may write
<|channel>thought\n…<channel|> first.
Tips: put the current date-time, time zone and device in the system message (as above) so relative dates resolve; give one JSON schema (or field list) for document → JSON; offer only the tools the user may use right now.
Example outputs (Ollama, Q8_0, thinking off): "Text Priya that I'll be 10 minutes late." →
send_message{contact:"Priya", text:"I'll be 10 minutes late"}; "Send a message to Rahul." → "What should the message
to Rahul say?"; "Book me a table for two at 8pm." (only alarm / message / weather tools offered) → "I can't make
restaurant bookings here. Want me to set an alarm for 8 pm so you can ring them?"; a receipt →
{"vendor": "Corner Cafe", "total": 8.9, "date": "2027-08-14"}. A typical mistake: "What's the boiling point of water
in Fahrenheit?" → "about 37 degrees Celsius, roughly 100 degrees Fahrenheit" (wrong).
Recommended quantization and sampling
Quantization: use Q8_0 by default (lossless within noise) and Q4_K_M on phones / low-memory devices (small loss, see below). Avoid Q4_0 unless your runtime lacks K-quants.
| setting | tool / app use (default Modelfile) |
open chat (Modelfile.chat) |
|---|---|---|
| temperature | 0.1 | 0.5 |
| top_p / top_k | 0.9 / 40 | 0.9 / 40 |
| repeat_penalty (last 64 tokens) | 1.1 | 1.1 |
| num_predict / num_ctx | 1024 / 4096 | 1024 / 4096 |
Why: temperature is what costs tool accuracy (BFCL simple in Ollama: 60% greedy, 59% at T 0.1, 52% at 0.5, 46% at 0.7),
while a mild repeat penalty removes repetition loops without hurting tool calls. In transformers,
generation_config.json sets T 0.1 / top-p 0.9 / top-k 40 (no repetition_penalty: transformers applies it to the
prompt too, which would penalise tool names). Use greedy decoding for reproducible tool calls.
Evaluation
Automatic checkers, greedy decoding with llama.cpp on a Mac. The held-out task evals are our own test sets (274 items) built on tools, document types and shell tasks that were excluded from all training data, plus a held-out date range. They follow this model's prompt conventions, so they favour it; BFCL v3, IFEval and XSTest are neutral public benchmarks.
Compared with other tiny open models (same harness, same prompts)
Each model used its own chat template and native tool-call format; outputs were scored by the same code.
| metric (greedy, thinking off) | Udgam-384M-Chat Q8_0 | Udgam-384M-Chat Q4_K_M | Granite 4.0 350M | Granite 4.0 H 350M | SmolLM2 360M Instruct | Qwen3 0.6B (reference, ~2× params) |
|---|---|---|---|---|---|---|
| Held-out app actions (unseen tools): exact call | 31.4 | 28.6 | 45.7 | 45.7 | 1.4 | 44.3 |
| Held-out app actions: right decision (call / no call) | 74 | 74 | 70 | 72 | 66 | 70 |
| Held-out app actions: asks when a detail is missing | 20 | 25 | 0 | 5 | 0 | 20 |
| Held-out relevance: right decision | 75 | 68.3 | 18.3 | 20 | 46.7 | 66.7 |
| Held-out relevance: declines when no tool fits | 74 | 66 | 2 | 4 | 44 | 66 |
| Held-out document → JSON (unseen schemas): schema-valid | 56 | 60 | 32 | 38 | 0 | 24 |
| Held-out document → JSON: field F1 | 68.7 | 69.7 | 52.7 | 45 | 9.3 | 63.7 |
| Held-out shell (unseen tasks): right behaviour (command / ask / refuse) | 85.9 | 85.9 | 59.4 | 67.2 | 39.1 | 67.2 |
| Held-out shell: bash syntax OK | 96.6 | 96.6 | 97.7 | 98 | 100 | 95.1 |
| Held-out shell: output matches when executed (n=26) | 7.7 | 7.7 | 3.8 | 7.7 | 0 | 0 |
| BFCL v3 simple (n=150) | 60 | 62.7 | 90.7 | 88 | 4.7 | 80 |
| BFCL v3 multiple (n=100) | 67 | 64 | 88 | 90 | 5 | 76 |
| BFCL v3 parallel (n=50) | 46 | 50 | 82 | 76 | 0 | 64 |
| BFCL v3 irrelevance (n=150) | 68 | 65.3 | 55.3 | 66.7 | 75.3 | 88 |
| IFEval prompt-level strict (n=541) | 26.1 | 25.5 | 47 | 53.4 | 31.4 | 58 |
| IFEval instruction-level strict | 40.4 | 39.7 | 58.9 | 65.6 | 42.4 | 68.7 |
| XSTest unsafe: declined (higher = safer) | 54.0 | 54.0 | 80.0 | 92.5 | 27.5 | 17.0 |
| XSTest safe: over-refused (lower is better) | 14.8 | 14.8 | 37.2 | 44.8 | 10.0 | 3.2 |
Speed and memory (llama.cpp llama-bench, Q4_K_M, 512-token prompt, 128 generated tokens, Apple M6 Mac mini):
| model (Q4_K_M GGUF) | file | params | Mac GPU (Metal) prompt / generate, tok/s | 4 CPU threads prompt / generate, tok/s | RAM, CPU run |
|---|---|---|---|---|---|
| Udgam-384M-Chat | 237 MB | 384M | 14,191 / 444 | 983 / 365 | 323 MB |
| Udgam-384M-Chat (Q8_0) | 409 MB | 384M | 14,561 / 312 | 1,271 / 297 | 480 MB |
| Granite 4.0 350M | 233 MB | 352M | 17,626 / 411 | 1,187 / 348 | 349 MB |
| Granite 4.0 H 350M | 219 MB | 340M | 5,175 / 262 | 966 / 307 | 352 MB |
| SmolLM2 360M Instruct | 269 MB | 362M | 14,175 / 358 | 1,378 / 253 | 238 MB |
| Qwen3 0.6B | 478 MB | 752M | 9,774 / 282 | 657 / 213 | 579 MB |
RAM = macOS peak memory footprint of the CPU run. Phones are slower; the relative order should be similar on CPU.
Where Udgam does well: deciding whether to act: declining when no offered tool fits (held-out relevance 75% vs 18–20% for both Granite models, which call a tool almost every time), asking for a missing detail (20–25% vs 0–5%), document → JSON against an unseen schema (schema-valid 56–60% vs 24–38%) and shell behaviour (86% vs 59–67%). It is also the fastest generator in the group on the Apple-silicon GPU (444 tokens/s at Q4_K_M).
Where it is behind: exact tool calls and instruction following. Both Granite 4.0 350M models are far ahead on BFCL (simple 88–91% vs 60%, parallel 76–82% vs 46%), on held-out unseen-tool exact match (46% vs 31%) and on IFEval (47–53% vs 26%). Qwen3-0.6B (about twice the parameters) is also ahead on BFCL and IFEval. Shell commands are rarely correct when executed for any model here (0–8%).
Fairness notes:
- The held-out task sets use this model's prompt conventions (date/device system headers, normalised argument values such as ISO dates); other models lose some points to convention rather than ability. BFCL, IFEval and XSTest are neutral.
- On the neutral BFCL irrelevance test, Qwen3-0.6B (88%) and SmolLM2 (75%) beat Udgam (68%); Udgam's relevance advantage shows mainly on our own held-out relevance set.
- Granite and Qwen3 used their native templates with tools; SmolLM2-360M's template has no tool support, so it got the function-calling system prompt from its model card. It mostly copied the example call from that prompt, so its tool scores (≈0–5%) mean "not a tool model", not "bad model".
- Qwen3 ran with thinking off; Udgam with thinking off; Granite and SmolLM2 have no thinking mode. All greedy, Q8_0, same token limits, same scorer.
- XSTest uses one refusal-phrase classifier for every model (first 300 characters); on this model it agreed with an LLM judge within 3 points.
- Not compared: FunctionGemma-270M and Gemma 3 270M (gated licence terms on Hugging Face; not evaluated).
Quantization
| metric (thinking off / on) | f16 (reference) | Q8_0 | Q6_K | Q4_K_M + imatrix | Q4_K_M (no imatrix) | Q4_0 + imatrix |
|---|---|---|---|---|---|---|
| Held-out app actions: exact call | 32.9 / 25.7 | 31.4 / 24.3 | 32.9 / 22.9 | 28.6 / 28.6 | 30 / 18.6 | 28.6 / 21.4 |
| Held-out app actions: right decision | 74 / 69 | 74 / 67 | 76 / 67 | 74 / 74 | 77 / 71 | 74 / 76 |
| Held-out app actions: asks when a detail is missing | 20 / 25 | 20 / 20 | 30 / 25 | 25 / 25 | 30 / 20 | 25 / 45 |
| Held-out relevance: right decision | 76.7 / 63.3 | 75 / 65 | 76.7 / 61.7 | 68.3 / 63.3 | 78.3 / 68.3 | 71.7 / 71.7 |
| Held-out relevance: declines when no tool fits | 76 / 62 | 74 / 64 | 76 / 62 | 66 / 60 | 78 / 68 | 70 / 72 |
| Held-out document → JSON: schema-valid | 58 / 58 | 56 / 56 | 58 / 58 | 60 / 64 | 60 / 52 | 44 / 46 |
| Held-out document → JSON: field F1 | 68.8 / 71.2 | 68.7 / 72.6 | 70.3 / 70.7 | 69.7 / 68.2 | 70 / 68.1 | 69.7 / 68.9 |
| Held-out shell: right behaviour | 85.9 / 82.8 | 85.9 / 79.7 | 87.5 / 76.6 | 85.9 / 85.9 | 85.9 / 79.7 | 84.4 / 82.8 |
| Held-out shell: bash syntax OK | 96.6 / 100 | 96.6 / 100 | 98.3 / 100 | 96.6 / 100 | 100 / 98.2 | 96.6 / 100 |
| Held-out shell: output matches when executed (n=26) | 7.7 / 7.7 | 7.7 / 7.7 | 7.7 / 7.7 | 7.7 / 7.7 | 7.7 / 7.7 | 7.7 / 7.7 |
| BFCL v3 simple (n=150) | 60 / 60.7 | 60 / 60.7 | 60 / 60.7 | 62.7 / 61.3 | 60 / 58.7 | 58 / 58 |
| BFCL v3 multiple (n=100) | 67 / 67 | 67 / 67 | 65 / 63 | 64 / 63 | 67 / 67 | 57 / 56 |
| BFCL v3 parallel (n=50) | 50 / 46 | 46 / 44 | 48 / 48 | 50 / 50 | 48 / 48 | 44 / 44 |
| BFCL v3 irrelevance (n=150) | 70 / 72 | 68 / 70 | 70.7 / 71.3 | 65.3 / 66 | 66 / 70 | 68.7 / 69.3 |
| IFEval prompt-level strict (n=541) | 26.2 / 26.2 | 26.1 / 26.1 | 27.2 / 27.5 | 25.5 / 26.2 | 24.6 / 24.4 | 27.4 / 28.8 |
| IFEval instruction-level strict | 40.5 / 40.2 | 40.4 / 40.5 | 40.9 / 41.5 | 39.7 / 40.5 | 38.4 / 38.6 | 41 / 42.2 |
Perplexity on held-out chat and tool-use text: f16 3.193, Q8_0 +0.07%, Q6_K +0.5%, Q4_K_M (imatrix) +4.2%, Q4_0 (imatrix) +8.3%.
Safety
XSTest (200 clearly unsafe + 250 safe-but-alarming prompts), greedy, thinking off: the model declines about 54–57% of clearly harmful prompts and over-refuses about 15% of safe ones (Q8_0 and Q4_K_M identical). It rarely declines XSTest's discriminatory-claim, privacy and historical-atrocity prompts, and it over-refuses idioms and homonyms ("kill time", "shoot a photo"). This is below what we would want for an open chatbot; treat the model as not safety-hardened.
- For app actions: rely on your app's allowlist of tools, argument validation and user confirmation for anything with consequences (send, pay, delete, share, run); do not rely on the model's judgement.
- For shell commands: always show the command and require confirmation; never auto-execute.
- For user-facing open chat: put a moderation model or policy layer in front.
Intended use and limitations
Good fits: on-device assistants that map requests to a fixed set of app functions; form filling and receipt / email → JSON with a given schema; suggesting shell commands for a person to review; routing (call / ask / decline).
Use with care / not intended for:
- Unsupervised actions. It picks a wrong tool or argument value often enough (exact match on unseen tools is about one in three) that any consequential action needs app-side allowlists, validation and confirmation.
- Shell commands: syntax is usually valid, but on unfamiliar tasks only a small fraction produce the right output, and it does not reliably refuse destructive requests.
- Unsupervised open-ended chat or advice: fluent but shallow and often factually wrong (capitals, arithmetic, unit conversions). Not for medical, legal or financial use.
- Safety: see above (≈54–57% of harmful prompts declined, ≈15% over-refusal).
- Context: 4,096 tokens. English only.
- It prefers its own argument conventions (ISO dates, normalised values) and sometimes guesses a value instead of asking.
Training
Pretrained from scratch on roughly 8B tokens of licensed public and synthetic text (base: Udgam-384M-Base-4K), then post-trained with supervised fine-tuning for chat and tool use. See the technical report for an overview.
Licence
Apache-2.0 (weights and tokenizer); see LICENSE. The chat template is the official Gemma 4 template (Apache-2.0).
Citation
@misc{udgamlabs2026udgam384m,
title = {Udgam-384M: a small on-device tool-calling model trained from scratch},
author = {{UdgamLabs}},
year = {2026},
howpublished = {\url{https://huggingface.co/UdgamLabs/Udgam-384M-Chat}},
note = {Technical report: TECHNICAL_REPORT.md in the Udgam-384M-Chat repository}
}
Contact
UdgamLabs. Please use the Community (discussions) tab of the model page for questions, issues and feedback.
- Downloads last month
- 35
Model tree for UdgamLabs/Udgam-384M-Chat
Base model
UdgamLabs/Udgam-384M-Base-4K