Spaces:
Running
Running
| """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) | |