Spaces:
Runtime error
Runtime error
File size: 5,996 Bytes
264262c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | import gradio as gr
import json
import spaces
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
MODEL_ID = "dispatchAI/Llama-3.2-1B-FunctionCall-mobile"
tokenizer = None
model = None
def load_model():
global tokenizer, model
if tokenizer is None:
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
model = AutoModelForCausalLM.from_pretrained(MODEL_ID, torch_dtype=torch.float16, device_map="auto")
return tokenizer, model
# Available functions the agent can call
AVAILABLE_FUNCTIONS = {
"set_alarm": {"description": "Set an alarm", "params": {"time": "string", "label": "string"}},
"send_message": {"description": "Send a message to a contact", "params": {"to": "string", "message": "string"}},
"call_contact": {"description": "Call a contact", "params": {"contact": "string"}},
"search_web": {"description": "Search the web", "params": {"query": "string"}},
"open_app": {"description": "Open an application", "params": {"app_name": "string"}},
"set_timer": {"description": "Set a timer", "params": {"duration_minutes": "integer"}},
"get_weather": {"description": "Get weather for a location", "params": {"location": "string"}},
}
@spaces.GPU
def parse_function_call(user_input: str) -> str:
"""Parse user input into a function call using the mobile function-calling model.
Args:
user_input: Natural language user input (e.g., "Set an alarm for 7am")
Returns:
JSON with the parsed function call
"""
tokenizer, model = load_model()
functions_text = json.dumps(AVAILABLE_FUNCTIONS, indent=2)
prompt = f"""You are a function-calling assistant. Parse the user's request into a function call.
Available functions:
{functions_text}
User request: "{user_input}"
Respond with ONLY a JSON object, no other text:
{{"function": "function_name", "parameters": {{...}}}}"""
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
with torch.no_grad():
outputs = model.generate(**inputs, max_new_tokens=100, temperature=0.1, do_sample=True, pad_token_id=tokenizer.eos_token_id)
response = tokenizer.decode(outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True).strip()
# Try to parse as JSON
try:
# Find JSON in response
start = response.find("{")
end = response.rfind("}") + 1
if start >= 0 and end > start:
parsed = json.loads(response[start:end])
func_name = parsed.get("function", "unknown")
params = parsed.get("parameters", {})
# Check if function exists
if func_name in AVAILABLE_FUNCTIONS:
return json.dumps({
"status": "success",
"function": func_name,
"parameters": params,
"description": AVAILABLE_FUNCTIONS[func_name]["description"],
"user_input": user_input,
}, indent=2)
else:
return json.dumps({
"status": "unknown_function",
"function": func_name,
"parameters": params,
"available_functions": list(AVAILABLE_FUNCTIONS.keys()),
}, indent=2)
except json.JSONDecodeError:
pass
# Fallback: keyword-based parsing
lower = user_input.lower()
if "alarm" in lower:
return json.dumps({"status": "fallback", "function": "set_alarm", "parameters": {"time": "07:00", "label": "alarm"}, "source": "keyword_fallback"}, indent=2)
elif "call" in lower:
contact = user_input.replace("call", "").strip()
return json.dumps({"status": "fallback", "function": "call_contact", "parameters": {"contact": contact}, "source": "keyword_fallback"}, indent=2)
elif "message" in lower or "text" in lower or "send" in lower:
return json.dumps({"status": "fallback", "function": "send_message", "parameters": {"to": "unknown", "message": ""}, "source": "keyword_fallback"}, indent=2)
elif "timer" in lower:
return json.dumps({"status": "fallback", "function": "set_timer", "parameters": {"duration_minutes": 5}, "source": "keyword_fallback"}, indent=2)
elif "weather" in lower:
return json.dumps({"status": "fallback", "function": "get_weather", "parameters": {"location": "current"}, "source": "keyword_fallback"}, indent=2)
return json.dumps({"status": "no_match", "message": "Could not parse function call", "available_functions": list(AVAILABLE_FUNCTIONS.keys())}, indent=2)
def list_functions() -> str:
"""List all available functions the agent can call."""
return json.dumps(AVAILABLE_FUNCTIONS, indent=2)
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue"), title="dispatchAI Function Calling Agent") as demo:
gr.Markdown("""
# 🔧 dispatchAI Function Calling Agent
An on-device agent that parses natural language into structured function calls.
**Model:** [Llama-3.2-1B-FunctionCall-mobile](https://huggingface.co/dispatchAI/Llama-3.2-1B-FunctionCall-mobile) (1B params, Q4 quantized)
Try: "Set an alarm for 7am", "Call mom", "Send a message to John saying I'll be late"
""")
with gr.Row():
inp = gr.Textbox(label="User Input", placeholder="Set an alarm for 7am tomorrow", scale=3)
btn = gr.Button("Parse Function Call", variant="primary", scale=1)
out = gr.Textbox(label="Parsed Function Call (JSON)", lines=12)
btn.click(fn=parse_function_call, inputs=inp, outputs=out)
with gr.Accordion("Available Functions", open=False):
fn_btn = gr.Button("List Functions")
fn_out = gr.Textbox(label="Functions (JSON)", lines=15)
fn_btn.click(fn=list_functions, outputs=fn_out)
gr.Markdown("---\n🚀 [dispatchAI](https://huggingface.co/dispatchAI) — Small. Mobile. Free. UAE-built.")
demo.launch(mcp_server=True)
|