--- license: mit language: - en library_name: transformers pipeline_tag: text-generation base_model: Qwen/Qwen2.5-Coder-0.5B tags: - flutter - dart - code-generation - qwen2 datasets: - bbidpa/flutter-full-examples-v1 --- # Qwen2.5-Coder-0.5B-Flutter-direct [Qwen2.5-Coder-0.5B](https://huggingface.co/Qwen/Qwen2.5-Coder-0.5B), fine-tuned to generate a **complete Flutter/Dart file in one shot** from a natural-language goal. Fine-tuned on 5M tokens of flat goal → complete-file examples ([bbidpa/flutter-full-examples-v1](https://huggingface.co/datasets/bbidpa/flutter-full-examples-v1)). This model is part of a paired comparison studying whether small models benefit more from learning to emit a whole file at once, or from learning to build code up iteratively via small diffs. Its companion on the same base model is [Qwen2.5-Coder-0.5B-Flutter-steps](https://huggingface.co/bbidpa/Qwen2.5-Coder-0.5B-Flutter-steps). The same comparison is also run on a 100M-parameter model trained fully from scratch: [Rainbow-Pony-100M-Flutter-direct](https://huggingface.co/bbidpa/Rainbow-Pony-100m-Flutter-direct) / [-steps](https://huggingface.co/bbidpa/Rainbow-Pony-100m-Flutter-steps). ## Load it Since Qwen2.5-Coder is a standard, already-registered `transformers` architecture, plain `AutoModel` loading works with no custom code: ```python from transformers import AutoModelForCausalLM, AutoTokenizer REPO = "bbidpa/Qwen2.5-Coder-0.5B-Flutter-direct" tokenizer = AutoTokenizer.from_pretrained(REPO) model = AutoModelForCausalLM.from_pretrained(REPO) ``` For the exact calling convention used in the examples below (and shared with the from-scratch TinyGPT models in this collection), wrap it with these two small adapters instead: ```python import torch import torch.nn as nn import torch.nn.functional as F from transformers import AutoTokenizer, AutoModelForCausalLM DEVICE = "cuda" if torch.cuda.is_available() else "cpu" class QwenTokenizerAdapter: def __init__(self, model_id): self.hf = AutoTokenizer.from_pretrained(model_id) self.eos_id = self.hf.eos_token_id self.bos_id = self.hf.bos_token_id if self.hf.bos_token_id is not None else self.eos_id self.pad_id = self.hf.pad_token_id if self.hf.pad_token_id is not None else self.eos_id self.vocab_size = len(self.hf) self.tokenizer = self def encode(self, text, add_special_tokens=False): return self.hf.encode(text, add_special_tokens=add_special_tokens) def decode(self, ids, skip_special_tokens=False): return self.hf.decode(ids, skip_special_tokens=skip_special_tokens) def id_to_token(self, idx): return self.hf.convert_ids_to_tokens([int(idx)])[0] def tokens(self, text): return self.hf.tokenize(text) class HFModelWrapper(nn.Module): """Matches TinyGPT's call convention -- model(xb, yb) -> (logits, loss), model.generate(idx, max_new_tokens=, eos_id=, top_k=) -> full sequence.""" def __init__(self, hf_model): super().__init__() self.hf_model = hf_model def forward(self, xb, yb=None): logits = self.hf_model(input_ids=xb).logits loss = None if yb is not None: loss = F.cross_entropy( logits.view(-1, logits.size(-1)), yb.view(-1), ignore_index=-100, ) return logits, loss def generate(self, idx, max_new_tokens, eos_id=None, top_k=None, temperature=None, do_sample=None): return self.hf_model.generate( input_ids=idx, max_new_tokens=max_new_tokens, eos_token_id=eos_id, pad_token_id=eos_id, top_k=top_k, do_sample=do_sample if do_sample is not None else (top_k is not None or temperature is not None), temperature=temperature if temperature is not None else 1.0, ) @property def vocab_size(self): return self.hf_model.config.vocab_size def load_hf_checkpoint(path, device): hf_model = AutoModelForCausalLM.from_pretrained( path, torch_dtype=torch.bfloat16 if torch.cuda.is_available() else torch.float32, attn_implementation="sdpa", ).to(device) return HFModelWrapper(hf_model).to(device) tokenizer = QwenTokenizerAdapter(REPO) model = load_hf_checkpoint(REPO, DEVICE).eval() ``` ## Prompt format Same tag vocabulary as the rest of this collection. `` holds the current file contents (empty for a from-scratch generation), `` holds prior steps (unused for this model's typical single-step usage), and the model completes everything after ``: ``` Write a widget that displays a select button with months in it ``` ## Generate ```python def render_step_prompt_from_data( goal: str, code: str = "", history: list[dict] | None = None, action_type: str = "", action_desc: str = "", changes: list[dict] | None = None, is_last_step: bool = False, include_output: bool = False, ) -> str: history = history or [] changes = changes or [] history_text = "\n".join( f"{h['type']}{h['desc']}" for h in history ) text = f""" {goal} {code} {history_text} """ if include_output: hunks = "\n".join( f"\n\n{h['search']}\n\n\n{h['replace']}\n\n" for h in changes ) output = f"{action_type}{action_desc}\n\n{hunks}\n" if is_last_step: output += "\n" text += output + "\n" return text def generate_output(model, tokenizer, device, prompt, max_new_tokens=300, **generate_kwargs): prompt_ids = tokenizer.encode(prompt, add_special_tokens=False) idx = torch.tensor([[tokenizer.bos_id] + prompt_ids], dtype=torch.long).to(device) generated = model.generate(idx, max_new_tokens=max_new_tokens, eos_id=tokenizer.eos_id, **generate_kwargs) text = tokenizer.decode(generated[0].tolist(), skip_special_tokens=False) output = text.split("")[-1].split("")[0].strip() return output prompt = render_step_prompt_from_data( goal="Write a widget that displays a select button with months in it", ) text = generate_output(model, tokenizer, DEVICE, prompt, max_new_tokens=1024) print(text) ``` ### Example **Input goal:** `Write a widget that displays a select button with months in it` ```python test_row = { "goal": "Write a widget that displays a select button with months in it", "code": """""", "history": [], "action_type": "", "action_desc": "", "is_last_step": False, "changes": [], } prompt = render_step_prompt_from_data( goal=test_row['goal'], code=test_row['code'], history=test_row['history'], action_type=test_row['action_type'], action_desc=test_row['action_desc'], changes=test_row['changes'], is_last_step=test_row['is_last_step'], include_output=False ) text = generate_output(model, tokenizer, DEVICE, prompt, max_new_tokens=300) print(text) ``` **Output:** ``` import 'package:flutter/material.dart'; class SelectMonth extends StatefulWidget { @override _SelectMonthState createState() => _SelectMonthState(); } class _SelectMonthState extends State { String? selectedMonth; final List monthNames = ['January', 'February', 'March', 'April', 'May', 'June']; @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ DropdownButton( value: selectedMonth, items: monthNames .map((month) => DropdownMenuItem(value: month, child: Text(month))) .toList(), onChanged: (val) => setState(() => selectedMonth = val), ), Text('Selected month: ${selectedMonth ?? 'None'}'), ], ); } } ``` ## Training details | | | |---|---| | Base model | [Qwen/Qwen2.5-Coder-0.5B](https://huggingface.co/Qwen/Qwen2.5-Coder-0.5B) (~0.5B params, pretrained by Alibaba/Qwen) | | Fine-tuning | 5M tokens, flat goal → complete-file examples | | Tokenizer | Qwen's native BPE tokenizer, extended with structural special tokens | ## Related - Companion model (iterative diff-based training, same base): [bbidpa/Qwen2.5-Coder-0.5B-Flutter-steps](https://huggingface.co/bbidpa/Qwen2.5-Coder-0.5B-Flutter-steps) - Same comparison, 100M model trained from scratch: [bbidpa/Rainbow-Pony-100m-Flutter-direct](https://huggingface.co/bbidpa/Rainbow-Pony-100m-Flutter-direct) - Training dataset: [bbidpa/flutter-full-examples-v1](https://huggingface.co/datasets/bbidpa/flutter-full-examples-v1) - Training/eval code: [Upcoming]