Hy3 / tools.py
ericsqin's picture
add hy3
ad51766
Raw
History Blame Contribute Delete
3.41 kB
"""Function-calling tool helpers (parsing / validating user-supplied JSON)."""
from __future__ import annotations
import json
import logging
from typing import Any, Optional
import gradio as gr
from i18n import t
logger = logging.getLogger(__name__)
def _normalize_tool_item(item: Any) -> Optional[dict]:
"""Accept either {type, function} or a bare {name, parameters} entry."""
if not isinstance(item, dict):
return None
if item.get("type") == "function" and "function" in item:
fn = item["function"]
if isinstance(fn, dict) and fn.get("name"):
return fn
return None
if item.get("name"):
return item
return None
def parse_functions_json(functions_json_str: Any) -> list[dict]:
if functions_json_str is None:
return []
if not isinstance(functions_json_str, str):
functions_json_str = str(functions_json_str)
if not functions_json_str.strip():
return []
try:
data = json.loads(functions_json_str)
except json.JSONDecodeError:
return []
if isinstance(data, dict):
data = [data]
if not isinstance(data, list):
return []
result: list[dict] = []
for item in data:
fn = _normalize_tool_item(item)
if fn:
result.append(fn)
return result
def build_tools_list(functions_json_str: Any) -> Optional[list[dict]]:
functions = parse_functions_json(functions_json_str)
logger.debug("parsed functions count=%d", len(functions))
if not functions:
return None
tools: list[dict] = []
for fn in functions:
params = fn.get("parameters", {})
if isinstance(params, str):
try:
params = json.loads(params)
except (json.JSONDecodeError, TypeError):
continue
tools.append({
"type": "function",
"function": {
"name": fn["name"],
"description": fn.get("description", ""),
"parameters": params,
},
})
return tools or None
def validate_functions_json(functions_json_str: str):
"""Gradio handler: validate + pretty-print the function definitions textbox."""
if not functions_json_str or not functions_json_str.strip():
gr.Warning(t("warn.fn.enter_json"))
return gr.update()
try:
data = json.loads(functions_json_str)
except json.JSONDecodeError as e:
gr.Warning(t("warn.fn.invalid_format", err=str(e)))
return gr.update()
if isinstance(data, dict):
data = [data]
if not isinstance(data, list):
gr.Warning(t("warn.fn.must_be_array"))
return gr.update()
names: list[str] = []
for i, item in enumerate(data):
if not isinstance(item, dict):
gr.Warning(t("warn.fn.item_not_object", i=i + 1))
return gr.update()
fn = _normalize_tool_item(item)
if not fn:
gr.Warning(t("warn.fn.item_invalid", i=i + 1))
return gr.update()
if fn["name"] in names:
gr.Warning(t("warn.fn.duplicate_name", name=fn["name"]))
return gr.update()
names.append(fn["name"])
formatted = json.dumps(data, indent=2, ensure_ascii=False)
gr.Info(t("info.fn.validation_passed", n=len(names), names=", ".join(names)))
return gr.update(value=formatted)