Spaces:
Runtime error
Runtime error
File size: 3,411 Bytes
ad51766 | 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 | """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)
|