FunctionGemma-ndp

A 270M FunctionGemma fine-tune for tool-calling against the National Data Platform (NDP) MCP server.

Supports three tools: list_organizations, search_datasets, get_dataset_details.

Usage

from transformers import AutoModelForCausalLM, AutoTokenizer

mid = "grc-iit/FunctionGemma-ndp"
tok = AutoTokenizer.from_pretrained(mid, subfolder="merged_16bit")
mdl = AutoModelForCausalLM.from_pretrained(
    mid, subfolder="merged_16bit", device_map="auto",
)

messages = [
    {"role": "developer", "content":
     "You are a model that can do function calling with the following functions"},
    {"role": "user", "content": "List all organizations on the NDP global server"},
]
prompt = tok.apply_chat_template(
    messages, tools=[...], add_generation_prompt=True, tokenize=False,
)

Output format is FunctionGemma native:

<start_function_call>call:list_organizations{server:<escape>global<escape>}<end_function_call>

Live test against the upstream NDP MCP

End-to-end: model โ†’ tool call โ†’ upstream clio-kit NDP MCP โ†’ real NDP response.

# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "transformers>=4.45", "torch>=2.4", "accelerate>=0.34",
#   "sentencepiece>=0.2", "protobuf>=4", "mcp>=1.0",
# ]
# ///
import asyncio, json, re
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

MID = "grc-iit/FunctionGemma-ndp"
PROMPT = "List all organizations on the NDP global server"

# 14-tool NDP catalog reshaped as OpenAI function specs (truncated here).
tools = [{"type": "function", "function": {
    "name": "list_organizations",
    "description": "List organizations available in the National Data Platform.",
    "parameters": {"type": "object", "properties": {
        "name_filter": {"type": "string"}, "server": {"type": "string"},
    }, "required": []},
}}]

tok = AutoTokenizer.from_pretrained(MID, subfolder="merged_16bit")
mdl = AutoModelForCausalLM.from_pretrained(
    MID, subfolder="merged_16bit", dtype=torch.bfloat16, device_map="auto",
)
text = tok.apply_chat_template(
    [{"role": "user", "content": PROMPT}],
    tools=tools, add_generation_prompt=True, tokenize=False,
)
inp = tok(text, return_tensors="pt").to(mdl.device)
out = mdl.generate(**inp, max_new_tokens=300)
raw = tok.decode(out[0][inp.input_ids.shape[-1]:], skip_special_tokens=False)

# Parse FunctionGemma format: <start_function_call>call:NAME{k:v,...}<end_function_call>
m = re.search(r"<start_function_call>\s*call:(\w+)\s*\{(.*?)\}\s*<end_function_call>",
              raw, re.DOTALL)
name = m.group(1)
args = {}
for k, v in re.findall(r"(\w+)\s*:\s*(<escape>.*?<escape>|None|\w+)", m.group(2)):
    if v == "None":
        continue                                      # strip phantom nulls
    args[k] = re.sub(r"<escape>|<escape>", "", v) if "<escape>" in v else v

# Spawn the upstream clio-kit NDP MCP and call the parsed tool against it.
async def call():
    params = StdioServerParameters(command="uvx", args=[
        "--from",
        "git+https://github.com/iowarp/clio-kit.git#subdirectory=clio-kit-mcp-servers/ndp",
        "ndp-mcp",
    ])
    async with stdio_client(params) as (r, w):
        async with ClientSession(r, w) as s:
            await s.initialize()
            out = await s.call_tool(name, args)
            print("".join(c.text for c in out.content if hasattr(c, "text")))

asyncio.run(call())

Save as test.py and run:

uv run --isolated test.py

Examples

Tested end-to-end against the upstream NDP MCP server.

Prompt: List all organizations on the NDP global server

<start_function_call>call:list_organizations{name_filter:None,server:<escape>global<escape>}<end_function_call>

MCP response (truncated):

{"organizations": ["ai-genomics-at-scale", "aquasteady", "burnpro3d",
                   "cal-oes", "california-forest-observatory", ...],
 "count": 84, "server": "global"}

Prompt: Get details for dataset clm-full-climate-connectivity-network

<start_function_call>call:get_dataset_details{
  dataset_identifier:<escape>clm-full-climate-connectivity-network<escape>,
  identifier_type:<escape>name<escape>,
  server:<escape>global<escape>
}<end_function_call>

MCP response (truncated):

{"dataset": {
   "id": "3264e7ee-ef6d-42d5-b722-8ad39670cf3d",
   "name": "clm-full-climate-connectivity-network",
   "title": "Full Climate Connectivity Network",
   "owner_org": "california-landscape-metrics",
   "resources": [{...4 resources: WMS, WFS, SHP, HTML...}]
 }}

Prompt: Find datasets about climate

<start_function_call>call:search_datasets{
  limit:20, search_terms:[<escape>climate<escape>], server:<escape>global<escape>
}<end_function_call>

MCP response: {"datasets": [...], "count": 20, "total_found": "20 of 137"}

Files

  • merged_16bit/ โ€” full safetensors checkpoint
  • lora/ โ€” LoRA adapter only

Training

  • Base model: unsloth/functiongemma-270m-it
  • Method: LoRA (r=64, alpha=128)
  • Hyperparameters: 3 epochs, batch size 16, learning rate 2e-4
  • Train loss: 0.25
  • Hardware: 1ร— H200

Citation

Built with Phagocyte.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for shazzadulimun/FunctionGemma-ndp

Adapter
(9)
this model