| from __future__ import annotations |
|
|
| import ast |
| import operator |
| from collections.abc import Callable |
| from dataclasses import dataclass |
| from typing import Any |
|
|
| from datasets.loader import dataset_statistics, preview_huggingface_dataset |
| from mcp_tools.vindex_tool import vindex_dependency_report, vindex_verification_report |
| from models.model_catalog import load_model_catalog |
| from models.service_factory import create_text_service |
|
|
|
|
| @dataclass(frozen=True) |
| class ToolResult: |
| """Simple serializable result for local tool calls.""" |
|
|
| name: str |
| payload: dict[str, Any] |
|
|
|
|
| def dataset_stats_tool(path: str) -> ToolResult: |
| return ToolResult("dataset_stats", dataset_statistics(path).as_dict()) |
|
|
|
|
| def hf_dataset_preview_tool(dataset_id: str, split: str = "train") -> ToolResult: |
| preview = preview_huggingface_dataset(dataset_id, split) |
| return ToolResult( |
| "hf_dataset_preview", |
| { |
| "dataset_id": preview.dataset_id, |
| "split": preview.split, |
| "rows": preview.rows, |
| "columns": preview.columns, |
| "samples": preview.samples, |
| "status": preview.status, |
| }, |
| ) |
|
|
|
|
| def safe_calculator_tool(expression: str) -> ToolResult: |
| value = _safe_eval(expression) |
| return ToolResult("safe_calculator", {"expression": expression, "value": value}) |
|
|
|
|
| def model_inference_tool( |
| prompt: str, |
| model_id: str = "minicpm5_1b", |
| backend: str = "placeholder", |
| system_prompt: str = "", |
| ) -> ToolResult: |
| catalog = load_model_catalog("config/models.yaml") |
| service = create_text_service(catalog[model_id], backend) |
| response = service.chat(system_prompt, prompt) |
| return ToolResult( |
| "model_inference", |
| { |
| "model_id": model_id, |
| "backend": backend, |
| "response": response, |
| }, |
| ) |
|
|
|
|
| def vindex_status_tool() -> ToolResult: |
| return ToolResult("vindex_status", vindex_dependency_report()) |
|
|
|
|
| def vindex_plan_tool(method: str, payload: dict[str, Any] | None = None) -> ToolResult: |
| return ToolResult( |
| "vindex_plan", |
| vindex_verification_report(method=method, payload=payload or {}), |
| ) |
|
|
|
|
| def tool_registry() -> dict[str, Any]: |
| return { |
| "dataset_stats": dataset_stats_tool, |
| "hf_dataset_preview": hf_dataset_preview_tool, |
| "safe_calculator": safe_calculator_tool, |
| "model_inference": model_inference_tool, |
| "vindex_status": vindex_status_tool, |
| "vindex_plan": vindex_plan_tool, |
| } |
|
|
|
|
| def _safe_eval(expression: str) -> float: |
| operators: dict[type[ast.operator] | type[ast.unaryop], Callable[..., float]] = { |
| ast.Add: operator.add, |
| ast.Sub: operator.sub, |
| ast.Mult: operator.mul, |
| ast.Div: operator.truediv, |
| ast.Pow: operator.pow, |
| ast.USub: operator.neg, |
| } |
|
|
| def eval_node(node: ast.AST) -> float: |
| if isinstance(node, ast.Expression): |
| return eval_node(node.body) |
| if isinstance(node, ast.Constant) and isinstance(node.value, int | float): |
| return float(node.value) |
| if isinstance(node, ast.BinOp) and type(node.op) in operators: |
| return float(operators[type(node.op)](eval_node(node.left), eval_node(node.right))) |
| if isinstance(node, ast.UnaryOp) and type(node.op) in operators: |
| return float(operators[type(node.op)](eval_node(node.operand))) |
| raise ValueError("Only numeric arithmetic expressions are allowed.") |
|
|
| return eval_node(ast.parse(expression, mode="eval")) |
|
|