Views
No views yet
Qrzysztof/functiongemma-270m-it-prepaid-cards-v2
(a google/functiongemma-270m-it fine-tune for prepaid-card tool calling in
107 languages with noisy/multi-turn input).| File | Description |
|---|---|
model.safetensors (+ .index.json) | MLX weights, 8-bit quantized (≈8.5 bits/weight) |
config.json | MLX config incl. quantization info |
tokenizer.json, tokenizer_config.json | same tokenizer as the parent model |
chat_template.jinja | FunctionGemma chat template |
1pip install mlx-lm
2python3 -c "
3from mlx_lm import load, generate
4from mlx_lm.sample_utils import make_sampler
5model, tokenizer = load('Qrzysztof/functiongemma-270m-it-prepaid-cards-v2-mlx')
6prompt = '<bos><start_of_turn>developer...' # tokenizer.apply_chat_template(messages, tools=tools, add_generation_prompt=True)
7print(generate(model, tokenizer, prompt=prompt, max_tokens=96, sampler=make_sampler(temp=0.0)))
8"temperature= in favour of a sampler object
(make_sampler(temp=0.0)).1pip install mlx-lm
2python3 -m mlx_lm convert --hf-path <hf_model_dir> -q --q-bits 8
3# output lands in ./mlx_model/ (no --output-dir in mlx-lm 0.31)-q.| Format | Success rate |
|---|---|
| SafeTensors (reference) | 40/40 = 100% |
| MLX 8-bit | 40/40 = 100% |
google/functiongemma-270m-it directly).1pip install torch transformers trl datasets accelerate
2huggingface-cli login # accept the gemma license for google/functiongemma-270m-itmessages/tools as JSON strings (Arrow cannot infer
the nested schema), and TRL's SFTTrainer needs a uniform struct schema, so
normalize first:1import json
2from datasets import load_dataset
3from transformers import AutoModelForCausalLM, AutoTokenizer
4
5def normalize_messages(msgs):
6 out = []
7 for m in msgs:
8 n = {"role": m["role"], "content": m.get("content") or "", "name": None,
9 "tool_call_id": m.get("tool_call_id"), "tool_calls": None}
10 if m["role"] == "tool":
11 n["name"] = m["content"]["name"]
12 n["content"] = json.dumps(m["content"]["response"], ensure_ascii=False)
13 if m.get("tool_calls"):
14 n["tool_calls"] = [{"id": tc.get("id"), "type": tc.get("type", "function"),
15 "function": {"name": tc["function"]["name"],
16 "arguments": json.dumps(tc["function"]["arguments"], ensure_ascii=False)}}
17 for tc in m["tool_calls"]]
18 out.append(n)
19 return out
20
21def rows_to_dataset(rows):
22 from datasets import Dataset
23 return Dataset.from_list([{
24 "messages": normalize_messages(r["messages"]),
25 "tools": json.dumps(r["tools"], ensure_ascii=False),
26 } for r in rows])
27
28ds = load_dataset("Qrzysztof/ecommerce-chat-tool-calling", token=HF_TOKEN)["train"]
29train_rows = [{"messages": json.loads(r["messages_json"]), "tools": json.loads(r["tools_json"])}
30 for r in ds if r["split"] == "train"]
31train_ds = rows_to_dataset(train_rows)1import torch
2from transformers import AutoModelForCausalLM
3from trl import SFTConfig, SFTTrainer
4
5model = AutoModelForCausalLM.from_pretrained("google/functiongemma-270m-it",
6 dtype=torch.bfloat16, attn_implementation="eager")
7tokenizer = AutoTokenizer.from_pretrained("google/functiongemma-270m-it")
8
9trainer = SFTTrainer(
10 model=model,
11 args=SFTConfig(
12 output_dir="functiongemma-ecommerce",
13 max_length=1024, # covers the longest sample + margin
14 packing=False, # keep tool calls intact (no cross-sample packing)
15 num_train_epochs=3,
16 per_device_train_batch_size=8,
17 learning_rate=5e-5,
18 lr_scheduler_type="constant",
19 warmup_steps=50,
20 bf16=True, # or fp16 on non-Ampere GPUs
21 eval_strategy="epoch",
22 report_to="none",
23 ),
24 train_dataset=train_ds,
25 processing_class=tokenizer,
26)
27trainer.train()tools
column; assistant_only_loss=True (default) masks everything but the model's
own turns, so it learns to emit tool calls — not to copy the schema.1ok = 0
2for item in test_rows:
3 inputs = tokenizer.apply_chat_template(item["messages"][:-1], tools=item["tools"],
4 add_generation_prompt=True, return_tensors="pt")
5 out = model.generate(**inputs, max_new_tokens=256)
6 output = tokenizer.decode(out[0][len(inputs["input_ids"][0]):], skip_special_tokens=False)
7 expected = <expected tool name / args from expected_json>
8 ok += expected-tool-in-output and no-other-tool-in-outputtrainer.push_to_hub("YOUR_USER/functiongemma-ecommerce")noise.py engine skips any token containing digits.template_id) and hold out
whole languages + (for the e-commerce set) whole schemas — that is the
only honest way to measure generalization.packing=False for tool-calling data; packed sequences splice mid-call.max_length ≥ longest sample + a margin; ~1024 covers these datasets....-v2 (SafeTensors)...-tool-calling-v2