Text Generation
Transformers
Safetensors
English
function-calling
tool-use
mobile-automation
android
gemma
unsloth
agent
Instructions to use ahmadw/functiongemma-270m-mobile with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use ahmadw/functiongemma-270m-mobile with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="ahmadw/functiongemma-270m-mobile")# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("ahmadw/functiongemma-270m-mobile", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use ahmadw/functiongemma-270m-mobile with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "ahmadw/functiongemma-270m-mobile" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ahmadw/functiongemma-270m-mobile", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/ahmadw/functiongemma-270m-mobile
- SGLang
How to use ahmadw/functiongemma-270m-mobile 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 "ahmadw/functiongemma-270m-mobile" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ahmadw/functiongemma-270m-mobile", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'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 "ahmadw/functiongemma-270m-mobile" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ahmadw/functiongemma-270m-mobile", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Unsloth Studio
How to use ahmadw/functiongemma-270m-mobile with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for ahmadw/functiongemma-270m-mobile to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for ahmadw/functiongemma-270m-mobile to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for ahmadw/functiongemma-270m-mobile to start chatting
Load model with FastModel
pip install unsloth from unsloth import FastModel model, tokenizer = FastModel.from_pretrained( model_name="ahmadw/functiongemma-270m-mobile", max_seq_length=2048, ) - Docker Model Runner
How to use ahmadw/functiongemma-270m-mobile with Docker Model Runner:
docker model run hf.co/ahmadw/functiongemma-270m-mobile
| license: gemma | |
| base_model: unsloth/functiongemma-270m-it | |
| library_name: transformers | |
| pipeline_tag: text-generation | |
| language: | |
| - en | |
| tags: | |
| - function-calling | |
| - tool-use | |
| - mobile-automation | |
| - android | |
| - gemma | |
| - unsloth | |
| - agent | |
| # FunctionGemma-270M — Mobile Actions | |
| A fine-tune of [`unsloth/functiongemma-270m-it`](https://huggingface.co/unsloth/functiongemma-270m-it) | |
| that turns a natural-language instruction into a **single, validated mobile-action tool call**. | |
| It targets two uses: | |
| - **Mobile test automation** — drive an Appium / UiAutomator / Espresso / XCUITest layer from plain-English steps. | |
| - **Agentic workflows** — let an on-device agent pick the next action as a structured, executable call. | |
| At 270M parameters it runs on CPU, a modest GPU, or fully on-device (LiteRT-LM / Google AI Edge). | |
| ## Repository layout | |
| | Path | Contents | | |
| | --- | --- | | |
| | `merged_16bit/` | Standalone 16-bit model (load this for inference) | | |
| | `lora/` | LoRA adapter only (apply on top of the base model) | | |
| ## Output format | |
| The model emits FunctionGemma's call DSL, e.g.: | |
| ``` | |
| <start_function_call>call:tap{selector:<escape>login_button<escape>}<end_function_call> | |
| ``` | |
| Parse it with: | |
| ```python | |
| import re | |
| CALL = re.compile(r"<start_function_call>call:(\w+)\{(.*?)\}<end_function_call>", re.DOTALL) | |
| ARG = re.compile(r"(\w+):(?:<escape>(.*?)<escape>|([^,}]*))") | |
| def parse(text): | |
| out = [] | |
| for name, raw in CALL.findall(text): | |
| args = {k: (e or p).strip() for k, e, p in ARG.findall(raw)} | |
| out.append({"name": name, "arguments": args}) | |
| return out | |
| ``` | |
| ## Usage | |
| ```python | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| import torch | |
| REPO = "ahmadw/functiongemma-270m-mobile" | |
| tok = AutoTokenizer.from_pretrained(REPO, subfolder="merged_16bit") | |
| model = AutoModelForCausalLM.from_pretrained(REPO, subfolder="merged_16bit").eval() | |
| prompt = tok.apply_chat_template( | |
| [{"role": "user", "content": "Tap the login button."}], | |
| tools=TOOLS, # the schema below | |
| tokenize=False, add_generation_prompt=True, | |
| ).removeprefix("<bos>") | |
| inputs = tok(prompt, return_tensors="pt") | |
| with torch.no_grad(): | |
| out = model.generate(**inputs, max_new_tokens=128, do_sample=False) | |
| print(tok.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)) | |
| # -> <start_function_call>call:tap{selector:<escape>login_button<escape>}<end_function_call> | |
| ``` | |
| You **must** pass the `tools` schema (below) so the chat template declares the available actions. | |
| ### Reliable tool calls (recommended) | |
| For production, constrain decoding so the model can only emit a syntactically valid call naming one | |
| of your tools (prevents hallucinated names). Pair the model with | |
| [`xgrammar`](https://pypi.org/project/xgrammar/) — build an EBNF grammar whose function name is | |
| restricted to your tool list, pass it as a `LogitsProcessor` to `generate(...)`, and snap any | |
| out-of-schema name to the nearest valid tool as a dependency-free fallback. | |
| ### Wiring into an automation driver | |
| The model decides *what*; your driver decides *how*. Map each tool name to a concrete action: | |
| ```python | |
| HANDLERS = { | |
| "launch_app": lambda d, app_name: d.activate_app(app_name), | |
| "tap": lambda d, selector: d.find(selector).click(), | |
| "type_text": lambda d, selector, text: d.find(selector).send_keys(text), | |
| "swipe": lambda d, direction: d.swipe(direction), | |
| "wait": lambda d, duration_ms: d.sleep(duration_ms / 1000), | |
| # ... one entry per tool ... | |
| } | |
| ``` | |
| > Selectors come from the live UI. The model infers a *reasonable* id (e.g. `login_button`) from | |
| > the wording; resolve it against the real accessibility tree, or pass the available element ids in | |
| > the instruction so the model can pick an exact match. | |
| ## Supported actions (38 tools) | |
| | Category | Tools | | |
| | --- | --- | | |
| | App lifecycle | `launch_app`, `close_app` | | |
| | Touch | `tap`, `double_tap`, `long_press`, `tap_coordinates`, `drag`, `swipe`, `pinch`, `scroll_to`, `pull_to_refresh` | | |
| | Text input | `type_text`, `clear_text`, `paste_text`, `copy_text`, `dismiss_keyboard` | | |
| | Controls | `toggle_switch`, `set_checkbox`, `select_option`, `set_slider`, `set_date`, `set_time`, `set_volume` | | |
| | Navigation | `navigate_back`, `go_home`, `open_app_switcher`, `open_notifications`, `open_quick_settings`, `press_keyboard_key`, `rotate_screen` | | |
| | Synchronization | `wait`, `wait_for_element` | | |
| | Assertions | `assert_element_visible`, `assert_element_not_visible`, `assert_text_equals`, `assert_text_contains` | | |
| | Utility | `take_screenshot`, `open_url` | | |
| <details> | |
| <summary>Full tool schema (copy into <code>TOOLS</code>)</summary> | |
| ```json | |
| [ | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "launch_app", | |
| "description": "Launch a mobile application by its visible name.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "app_name": { | |
| "type": "string", | |
| "description": "Visible name of the app to open." | |
| } | |
| }, | |
| "required": [ | |
| "app_name" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "close_app", | |
| "description": "Close or force stop a running application.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "app_name": { | |
| "type": "string", | |
| "description": "Visible name of the app to close." | |
| } | |
| }, | |
| "required": [ | |
| "app_name" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "tap", | |
| "description": "Tap a single UI element once by its selector. Use tap_coordinates for raw screen pixels.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id or label of the element." | |
| } | |
| }, | |
| "required": [ | |
| "selector" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "double_tap", | |
| "description": "Double tap a UI element.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id or label of the element." | |
| } | |
| }, | |
| "required": [ | |
| "selector" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "long_press", | |
| "description": "Press and hold a UI element.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id or label of the element." | |
| } | |
| }, | |
| "required": [ | |
| "selector" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "tap_coordinates", | |
| "description": "Tap the screen at absolute pixel x/y coordinates, with no element selector.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "x": { | |
| "type": "integer", | |
| "description": "Horizontal pixel coordinate." | |
| }, | |
| "y": { | |
| "type": "integer", | |
| "description": "Vertical pixel coordinate." | |
| } | |
| }, | |
| "required": [ | |
| "x", | |
| "y" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "drag", | |
| "description": "Drag one element onto another.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "source": { | |
| "type": "string", | |
| "description": "Logical id of the element to drag." | |
| }, | |
| "target": { | |
| "type": "string", | |
| "description": "Logical id of the drop target." | |
| } | |
| }, | |
| "required": [ | |
| "source", | |
| "target" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "swipe", | |
| "description": "Swipe across the screen in a direction.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "direction": { | |
| "type": "string", | |
| "description": "One of: up, down, left, right." | |
| } | |
| }, | |
| "required": [ | |
| "direction" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "pinch", | |
| "description": "Pinch with two fingers to zoom in or out on an element. Does not rotate the screen.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the element to zoom." | |
| }, | |
| "direction": { | |
| "type": "string", | |
| "description": "Zoom direction: in or out." | |
| } | |
| }, | |
| "required": [ | |
| "selector", | |
| "direction" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "scroll_to", | |
| "description": "Scroll until an element becomes visible.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the target element." | |
| } | |
| }, | |
| "required": [ | |
| "selector" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "pull_to_refresh", | |
| "description": "Pull down from the top to refresh the screen.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": {}, | |
| "required": [] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "type_text", | |
| "description": "Type text into an input field.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the input field." | |
| }, | |
| "text": { | |
| "type": "string", | |
| "description": "Text to enter into the field." | |
| } | |
| }, | |
| "required": [ | |
| "selector", | |
| "text" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "clear_text", | |
| "description": "Clear the contents of an input field.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the input field." | |
| } | |
| }, | |
| "required": [ | |
| "selector" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "paste_text", | |
| "description": "Paste the clipboard contents into an input field.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the input field." | |
| } | |
| }, | |
| "required": [ | |
| "selector" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "copy_text", | |
| "description": "Copy the text of an element to the clipboard.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the element to copy from." | |
| } | |
| }, | |
| "required": [ | |
| "selector" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "dismiss_keyboard", | |
| "description": "Hide the on-screen keyboard.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": {}, | |
| "required": [] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "toggle_switch", | |
| "description": "Set a toggle or switch to a desired state.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the switch." | |
| }, | |
| "state": { | |
| "type": "string", | |
| "description": "Desired state: on or off." | |
| } | |
| }, | |
| "required": [ | |
| "selector", | |
| "state" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "set_checkbox", | |
| "description": "Check or uncheck a checkbox.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the checkbox." | |
| }, | |
| "state": { | |
| "type": "string", | |
| "description": "Desired state: checked or unchecked." | |
| } | |
| }, | |
| "required": [ | |
| "selector", | |
| "state" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "select_option", | |
| "description": "Select an option from a dropdown or picker.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the dropdown." | |
| }, | |
| "option": { | |
| "type": "string", | |
| "description": "Visible option to select." | |
| } | |
| }, | |
| "required": [ | |
| "selector", | |
| "option" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "set_slider", | |
| "description": "Set a slider to a numeric value.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the slider." | |
| }, | |
| "value": { | |
| "type": "integer", | |
| "description": "Target value for the slider." | |
| } | |
| }, | |
| "required": [ | |
| "selector", | |
| "value" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "set_date", | |
| "description": "Set a date picker to a specific date.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the date field." | |
| }, | |
| "date": { | |
| "type": "string", | |
| "description": "Date in YYYY-MM-DD format." | |
| } | |
| }, | |
| "required": [ | |
| "selector", | |
| "date" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "set_time", | |
| "description": "Set a time picker to a specific time.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the time field." | |
| }, | |
| "time": { | |
| "type": "string", | |
| "description": "Time in HH:MM 24-hour format." | |
| } | |
| }, | |
| "required": [ | |
| "selector", | |
| "time" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "navigate_back", | |
| "description": "Go back one step to the previous screen. Does not return to the home screen.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": {}, | |
| "required": [] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "go_home", | |
| "description": "Return to the device home screen (launcher). This is not a single back step.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": {}, | |
| "required": [] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "open_app_switcher", | |
| "description": "Open the recent apps or app switcher.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": {}, | |
| "required": [] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "press_keyboard_key", | |
| "description": "Press a soft keyboard action key.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "key": { | |
| "type": "string", | |
| "description": "One of: enter, search, done, next, go." | |
| } | |
| }, | |
| "required": [ | |
| "key" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "open_notifications", | |
| "description": "Open the notifications panel.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": {}, | |
| "required": [] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "open_quick_settings", | |
| "description": "Open the quick toggles or control panel.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": {}, | |
| "required": [] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "rotate_screen", | |
| "description": "Rotate the screen orientation between portrait and landscape. Does not zoom.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "orientation": { | |
| "type": "string", | |
| "description": "Orientation: portrait or landscape." | |
| } | |
| }, | |
| "required": [ | |
| "orientation" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "set_volume", | |
| "description": "Set the media volume to a level.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "level": { | |
| "type": "integer", | |
| "description": "Volume level from 0 to 100." | |
| } | |
| }, | |
| "required": [ | |
| "level" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "wait_for_element", | |
| "description": "Wait until a specific element appears, up to a timeout. Requires an element selector.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the element." | |
| }, | |
| "timeout_ms": { | |
| "type": "integer", | |
| "description": "Maximum wait time in milliseconds." | |
| } | |
| }, | |
| "required": [ | |
| "selector", | |
| "timeout_ms" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "wait", | |
| "description": "Pause execution for a fixed duration in milliseconds. Does not wait for any element.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "duration_ms": { | |
| "type": "integer", | |
| "description": "Duration to wait in milliseconds." | |
| } | |
| }, | |
| "required": [ | |
| "duration_ms" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "assert_element_visible", | |
| "description": "Assert that an element is visible on screen.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the element." | |
| } | |
| }, | |
| "required": [ | |
| "selector" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "assert_element_not_visible", | |
| "description": "Assert that an element is not visible on screen.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the element." | |
| } | |
| }, | |
| "required": [ | |
| "selector" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "assert_text_equals", | |
| "description": "Assert that an element's text exactly equals an expected value (full match).", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the element." | |
| }, | |
| "expected_text": { | |
| "type": "string", | |
| "description": "Expected text content." | |
| } | |
| }, | |
| "required": [ | |
| "selector", | |
| "expected_text" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "assert_text_contains", | |
| "description": "Assert that an element's text contains a substring (partial match).", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "selector": { | |
| "type": "string", | |
| "description": "Logical id of the element." | |
| }, | |
| "text": { | |
| "type": "string", | |
| "description": "Substring expected within the element text." | |
| } | |
| }, | |
| "required": [ | |
| "selector", | |
| "text" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "take_screenshot", | |
| "description": "Capture a screenshot of the current screen.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "name": { | |
| "type": "string", | |
| "description": "File name for the screenshot." | |
| } | |
| }, | |
| "required": [ | |
| "name" | |
| ] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "open_url", | |
| "description": "Open a URL in the default browser.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "url": { | |
| "type": "string", | |
| "description": "Fully qualified URL to open." | |
| } | |
| }, | |
| "required": [ | |
| "url" | |
| ] | |
| } | |
| } | |
| } | |
| ] | |
| ``` | |
| </details> | |
| ## Evaluation | |
| Greedy + grammar-constrained decoding, on held-out paraphrase sets the model never saw in training: | |
| | Set | Tool selection | Exact match (name + args) | | |
| | --- | --- | --- | | |
| | Wild (colloquial paraphrases) | 97.7% | 90.7% | | |
| | Stress (high-variance, 64 cases) | 93.8% | 92.2% | | |
| Hallucinated tool names: 0. Remaining misses cluster on indirect phrasings with no action keyword | |
| (e.g. "make the picture smaller" -> `pinch` out). Accuracy is highest when the instruction names the action. | |
| ## Training | |
| - Base: `unsloth/functiongemma-270m-it` | |
| - Method: 16-bit LoRA (r=16, alpha=16; q/k/v/o + gate/up/down) via Unsloth, on a free Tesla T4 | |
| - LR 2e-4, linear schedule, `adamw_8bit`, 3 epochs, response-only loss masking | |
| - Synthetic instruction -> tool-call data with contrastive confusable pairs and broad phrasing variety | |
| ## License | |
| Governed by the [Gemma Terms of Use](https://ai.google.dev/gemma/terms). Inherits the base model license. | |