--- language: - tr - en license: apache-2.0 library_name: transformers base_model: AlicanKiraz0/Kara-Kumru-v1.0-2B pipeline_tag: text-generation tags: - turkish - tool-calling - function-calling - hermes - kara-kumru - mistral - gguf --- # Roka — Turkish Tool-Calling Fine-Tune of Kara-Kumru 2B Roka is a supervised fine-tune of `AlicanKiraz0/Kara-Kumru-v1.0-2B` that teaches a 2B-parameter Turkish language model to use five tools (web search, calculator, date/time, weather, URL reader) via a Hermes-style `` output format. This is a **v0.2 research preview**, released for reproducibility and community feedback. It is not a production-grade tool-calling agent and has known weaknesses (see *Limitations*). The v0.2 training set is fully decontaminated against the evaluation set: no test-set query appears verbatim in train or validation. ## Model at a glance | | | |---|---| | **Base model** | `AlicanKiraz0/Kara-Kumru-v1.0-2B` (Mistral architecture, Llama-3 chat template, Turkish-pretrained) | | **Upstream base** | `vngrs-ai/Kumru-2B` | | **Parameters** | ~2.15B | | **Fine-tuning** | Full fine-tuning, 3 epochs, LR 5e-5 linear, bf16, TRL SFTTrainer | | **Hardware** | Single NVIDIA A6000 (~65 min / epoch ~22 min) | | **Languages** | Primarily Turkish; ~13% of the training mix is English (Glaive-sourced synthetic tool-calling examples) | | **License** | Apache 2.0 (inherited from base chain) | ## Tool set | Tool | Description | |---|---| | `web_search` | Internet search (DuckDuckGo) | | `calculator` | Arithmetic expression evaluator | | `datetime` | Date/time and calendar arithmetic (9 actions: `today`, `now`, `day_of_week`, `add_days`, `date_diff`, `days_until`, `day_of_year`, `end_of_month`, `days_until_weekday`) | | `hava_durumu` | Weather query by city name | | `sayfa_oku` | URL content reader | The model is trained to emit tool calls as: ``` {"name": "datetime", "arguments": {"action": "today"}} ``` Tool results are fed back to the model wrapped in `` inside a user turn, and the model synthesizes a final Turkish answer. ## Evaluation The test set contains 260 Turkish prompts spread over six categories (simple tool calls, fullflow multi-step, parallel, multiple tools, irrelevance, adversarial). Scoring uses an alignment-aware harness (`scripts/rescore_aligned.py`) that normalizes equivalent datetime actions and accepts semantically equivalent arithmetic expressions. ### Overall results (Roka v0.2, April 2026) | View | n | Full-Match | Tool-Call Acc. | Name Acc. | Arg Acc. | |---|---|---|---|---|---| | **All test (held-out)** | 260 | **73.5%** | 93.1% | 71.9% | 60.6% | Every test query was verified to be absent from both `data/train.jsonl` and `data/val.jsonl`, so the 73.5% number above is a genuinely held-out measurement. See *Decontamination history* below for why this is lower than an earlier, un-decontaminated run. ### Per-subcategory results | Subcategory | n | Full-Match | |---|---|---| | simple/web_search | 30 | **93.3%** | | simple/weather | 20 | **100.0%** | | simple/url_reader | 15 | **100.0%** | | simple/calculator | 20 | 70.0% | | simple/datetime | 15 | 46.7% | | fullflow | 35 | **80.0%** | | multiple | 45 | 64.4% | | parallel | 15 | **0.0%** | | adversarial/turkish_special | 10 | 90.0% | | adversarial/edge_case | 5 | 40.0% | | adversarial/ambiguous | 15 | 26.7% | | irrelevance/greeting | 15 | **100.0%** | | irrelevance/identity | 10 | **100.0%** | | irrelevance/opinion | 10 | **100.0%** | **Parallel tool calls score 0% because the training mix does not contain parallel-call examples.** This is a known gap, not a reproducibility failure. ### Decontamination history During preparation for this release we audited the training set and found that **44 of the 260 test queries appeared verbatim in train/val** (8 in simple/datetime, 6 in simple/web_search, 7 in multiple, and the rest in irrelevance/identity and irrelevance/greeting). We removed all 76 matching train examples and 6 matching val examples, and retrained on the clean split. That retraining is the model reported above. For transparency we also report the before-and-after numbers on the 216 test queries that were **not** affected by the decontamination (i.e., the genuinely held-out subset from the *pre-cleanup* model's perspective): | Model | Training data | Clean-216 FM | |---|---|---| | v0.1 pre-clean | original (with 76 overlaps) | 78.2% | | **v0.2** (released) | decontaminated | **73.6%** | The ~4.6-point drop is informative: it is *not* contamination-inflation. The removed training examples were pattern-providing (datetime variants, fullflow web-search turns, distractor augmentations of the same base queries), and losing them cost about 4.6 points of generalization even on held-out queries. The cost of honest decontamination was larger than the narrow definition of "memorization gain" would predict. We report the post-decontamination number because it is the only one that is defensible as a held-out measurement. A future v0.3 will attempt to recover the gap by adding clean synthetic replacements for the removed examples. ## Development journey (brief) Arriving at the final model required an honest amount of dead-ends. 1. **Baseline (Run 10)** — 62.7% aligned FM with an earlier pipeline, before any of the spec-005 data work. 2. **Phase A v1–v4 collapse** — four consecutive training runs where loss converged to near-zero but test-set Full-Match stayed at 0/260. All of them passed `loss` sanity checks, so the failure was invisible from inside the run. 3. **Root cause** — TRL issue [#3910](https://github.com/huggingface/trl/issues/3910): the `max_seq_length` argument was silently renamed to `max_length` (default 1024) in TRL 0.20+. Every assistant turn longer than 1024 tokens (≈75% of our fullflow examples) was being truncated before it contributed to the loss. The model trained to completion on fragments, not on full tool-calling traces. Fix: pass `max_length=4096` explicitly. 4. **Data iterations** - Removed the `unit` argument from all `hava_durumu` training examples (the test set does not supply it). `simple/weather` Full-Match rose from 10% to 100%. - Added 45 supplementary `datetime` examples covering `day_of_year`, `end_of_month`, and `days_until_weekday` — test actions that were absent from the R10 training data. - Those supplementary examples caused a regression on `day_of_week` queries ("23 Nisan hangi güne denk geliyor?" was mis-routed to `day_of_year`). A targeted set of 30 `day_of_week` contrast examples fixed it. 5. **Final v0.1 model** — 4,778 training / 509 validation examples, 795 optimizer steps. Reported 76.9% all-test, 78.2% on the clean-216 subset. 6. **v0.2 — decontamination** — 76 train and 6 val examples whose first user turn matched a test query were removed, producing a 4,702 / 503 split. Retraining on this split gave the 73.5% number now reported above. The 4.6-point drop on the clean-216 subset between v0.1 and v0.2 is the cost of honest decontamination — see *Decontamination history*. Total compute used across Phase A and v0.2: ~5 A6000-hours. ## Limitations - **Multi-turn pattern lock-in.** The SFT mix contains very few multi-turn tool-calling sequences. If the user starts with a chit-chat turn ("selam"), the model tends to stay in plain-chat mode on subsequent turns and skip the tool call. The provided `scripts/serve_ui.py` works around this by feeding only the current user message (without prior turns) into the tool-decision loop. - **Parallel tool calls: 0%.** Not trained. - **`hava_durumu` has no temporal parameter.** Queries like "yarın İstanbul'da hava" still produce `{"city": "İstanbul"}` because that is what the schema allows. The fix is a schema change + data regeneration, not a prompt change. - **Adversarial/ambiguous: 40%.** The model is easily nudged off-task by ambiguous phrasing. - **Long-passage synthesis is brittle.** When `sayfa_oku` returns several paragraphs, the synthesized summary sometimes fragments quotes in an unnatural way. - **Hermes parser coupling.** Native OpenAI-style `tool_calls` parsing via `llama-server` requires the provided `training/roka_tool_template.jinja` chat template and requires the client to pass the full list of 5 tools. Passing a subset confuses llama.cpp's Hermes detector. - **Scoring discrepancy.** The in-training `training/eval.py` scorer disagrees slightly with the alignment-aware rescorer. Only the rescored numbers are reported above. Resolving the discrepancy is open work. ## Training data - **4,778 train / 509 validation** examples, Hermes-format chat turns. - **~72% Turkish, ~13% English, ~15% short/symbolic.** The English fraction is Glaive-sourced synthetic tool-calling data retained for multi-tool pattern coverage. - **Deterministic generators** for `calculator`, `datetime`, `hava_durumu` (in `training/generators/`). - **Real DuckDuckGo search results** cached in `data/ddg_cache.json` and used to construct `web_search` fullflow examples. - **PII scan**: only two flagged matches in user-facing content, both false positives (embedded WSJ article IDs). No email addresses, Turkish ID numbers, credit cards, or IP addresses found. ## Contamination verification The released v0.2 model is trained on a split where **no test query appears verbatim** in either train or validation. The decontamination script (`scripts/decontaminate.py`) normalizes whitespace and case before matching. The pre-decontamination overlap distribution (all removed in v0.2) was: | Subcategory | Overlap (removed) | |---|---| | irrelevance/identity | 8 / 10 | | irrelevance/greeting | 11 / 15 | | simple/datetime | 8 / 15 | | simple/web_search | 6 / 30 | | multiple | 7 / 45 | | adversarial/turkish_special | 1 / 10 | | adversarial/opinion | 1 / 10 | | simple/weather | 1 / 20 | | fullflow | 1 / 35 | Because augmentation variants of each base query (masked/distractor versions) shared the same user turn, removing 44 unique queries deleted 76 train examples and 6 val examples in total. The remaining 4,702 / 503 split is what v0.2 was trained on. This decontamination is **exact-string**, not fuzzy. Near-duplicates (paraphrases that return the same tool call) are still present. Closing the paraphrase loophole requires a more elaborate embedding-based deduplication pass, which is left for v0.3. ## Repository layout ``` src/ Inference clients (transformers & llama-server) training/ tools.py Tool schemas + training system prompt train.py TRL SFTTrainer entry point eval.py Test-set scorer (in-training) roka_tool_template.jinja llama-server chat template with Hermes detection hook generators/ Deterministic data generators per tool scripts/ work_pipeline.py End-to-end pod orchestration pod_run_and_dump.py On-pod training → prediction dump → HF upload rescore_aligned.py Alignment-aware rescorer (authoritative numbers) serve_ui.py FastAPI chat UI wrapping the agent data/ train.jsonl, val.jsonl, test_set.json specs/005-post-run10-75/ Spec, plan, and task list for this iteration ``` Github: (https://github.com/bilersan/roka) ## Reproducibility 1. Clone the repo and install requirements: ```bash pip install -r requirements.txt ``` 2. Regenerate the training set (deterministic): ```bash python -m training.build_dataset ``` 3. Train (RunPod-hosted, ~1 GPU-hour on an A6000): ```bash python -m scripts.work_pipeline ``` 4. Rescore predictions with the alignment-aware harness: ```bash python -m scripts.rescore_aligned --predictions .work/artifacts/predictions/.json ``` The training recipe is fully specified in `training/config.yaml`. The only hyperparameter that is unusually specific is `max_length: 4096` in `training/train.py` — removing it reproduces the Phase A v1–v4 collapse described above. ## Intended use and out-of-scope use **Intended**: Turkish-language tool-calling agents for well-defined tools, research on small-model function calling, educational demonstrations of the SFT pipeline. **Out of scope**: - Safety-critical applications. The model has not been evaluated for harmful-content refusal beyond what Kara-Kumru inherits from its base. - Parallel / agentic planning over large tool catalogs. - Multi-turn conversational agents that need to preserve long prior context. - Any application that requires the model to use tools not present in the training schema. ## License This repository and the released weights are distributed under the **Apache License 2.0**, inherited from both `AlicanKiraz0/Kara-Kumru-v1.0-2B` and its upstream base `vngrs-ai/Kumru-2B`. See `LICENSE`. ## Citation If you use Roka in research, please cite both the base model and this work: ```bibtex @misc{roka_2026, title = {Roka: Turkish Tool-Calling Fine-Tune of Kara-Kumru 2B}, author = {Bilik, Ersan}, year = {2026}, url = {https://huggingface.co/ersanbil/roka} } @misc{karakumru_2025, title = {Kara-Kumru-v1.0-2B}, author = {Kiraz, Alican}, year = {2025}, url = {https://huggingface.co/AlicanKiraz0/Kara-Kumru-v1.0-2B} } ``` ## Acknowledgements - **vngrs-ai** for the open Turkish base model `Kumru-2B`. - **Alican Kiraz** for the Turkish-conversational fine-tune `Kara-Kumru-v1.0-2B`. - **Hugging Face TRL / Unsloth** for the training stack. - **Glaive-AI function-calling dataset** for the English portion of the multi-tool synthetic mix. ## Contact and feedback Issues and pull requests are welcome on the GitHub mirror. This is a research preview — please file bugs for any behavior that contradicts the documented limitations above; those are the interesting cases.