Files
wehub-resource-sync c889a57b6b
Test Suites / Build CI Environment (push) Has been cancelled
Test Suites / Basic Tests (push) Has been cancelled
Test Suites / End-to-End Tests (push) Has been cancelled
Test Suites / CLI Tests (push) Has been cancelled
Test Suites / Slow End-to-End Tests (push) Has been cancelled
Test Suites / Graph Database Tests (push) Has been cancelled
Test Suites / Vector DB Tests (push) Has been cancelled
Test Suites / Temporal Graph Test (push) Has been cancelled
Test Suites / Search Test on Different DBs (push) Has been cancelled
Test Suites / Example Tests (push) Has been cancelled
Test Suites / Notebook Tests (push) Has been cancelled
Test Suites / OS and Python Tests Ubuntu (push) Has been cancelled
Test Suites / OS and Python Tests Extended (push) Has been cancelled
Test Suites / LLM Test Suite (push) Has been cancelled
Test Suites / S3 File Storage Test (push) Has been cancelled
Test Suites / Run Integration Tests (push) Has been cancelled
Test Suites / MCP Tests (push) Has been cancelled
Test Suites / Docker Compose Test (push) Has been cancelled
Test Suites / Docker CI test (push) Has been cancelled
Test Suites / Relational DB Migration Tests (push) Has been cancelled
Test Suites / Distributed Cognee Test (push) Has been cancelled
Test Suites / DB Examples Tests (push) Has been cancelled
Test Suites / Test Completion Status (push) Has been cancelled
Test Suites / Claude Code Review (push) Has been cancelled
Test Suites / basic checks (push) Has been cancelled
build | Build and Push Cognee MCP Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
build | Build and Push Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.11) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.12) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (kuzu, kuzu) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (neo4j, neo4j) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Examples (push) Has been cancelled
Weighted Edges Tests / Code Quality for Weighted Edges (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:02:24 +08:00

158 lines
5.6 KiB
Python

"""Tool registry: resolves a tool name to a Tool DataPoint + handler callable.
Two storage tiers:
- Built-in tools are registered in-memory at import time. They exist for every
dataset and do not require cognify to have run.
- User-defined tools live in the graph as Tool DataPoints, written at ingest
time (e.g. when a Postgres SourceConnection is attached to a dataset) and
retrieved via the graph engine.
"""
import importlib
from typing import Any, Callable, Dict, List, Optional
from uuid import UUID
from cognee.modules.engine.models import Tool
from cognee.modules.tools.errors import ToolInvocationError, ToolNotFoundError
from cognee.shared.logging_utils import get_logger
ToolHandler = Callable[..., Any]
logger = get_logger("cognee.tools.registry")
_BUILTIN_TOOLS: Dict[str, Tool] = {}
def register_builtin_tool(tool: Tool) -> None:
"""Register a built-in Tool. Built-ins are global (dataset_id is None)."""
_BUILTIN_TOOLS[tool.name] = tool
def resolve_handler(handler_ref: str) -> ToolHandler:
"""Import and return the async handler referenced by a dotted path."""
if ":" in handler_ref:
module_path, attr = handler_ref.split(":", 1)
elif "." in handler_ref:
module_path, _, attr = handler_ref.rpartition(".")
else:
raise ToolInvocationError(
f"handler_ref must be a dotted path or module:attr form, got {handler_ref!r}"
)
try:
module = importlib.import_module(module_path)
except ImportError as exc:
raise ToolInvocationError(f"Could not import {module_path}: {exc}") from exc
if not hasattr(module, attr):
raise ToolInvocationError(f"{module_path} has no attribute {attr!r}")
handler = getattr(module, attr)
if not callable(handler):
raise ToolInvocationError(f"{handler_ref} is not callable")
return handler
async def get_tool(name: str, dataset_id: Optional[UUID] = None) -> Tool:
"""Look up a Tool by name. Checks built-ins first, then the graph."""
if name in _BUILTIN_TOOLS:
return _BUILTIN_TOOLS[name]
graph_tool = await _find_tool_in_graph(name=name, dataset_id=dataset_id)
if graph_tool is not None:
return graph_tool
raise ToolNotFoundError(
f"Tool {name!r} is not registered" + (f" for dataset {dataset_id}" if dataset_id else "")
)
async def list_tools_for_dataset(dataset_id: Optional[UUID] = None) -> List[Tool]:
"""Return every tool visible for a dataset: all built-ins plus graph-scoped tools."""
tools: List[Tool] = list(_BUILTIN_TOOLS.values())
tools.extend(await _list_tools_in_graph(dataset_id=dataset_id))
return tools
async def _find_tool_in_graph(name: str, dataset_id: Optional[UUID]) -> Optional[Tool]:
"""Query the graph for a Tool by name within an optional dataset scope."""
nodes = await _query_tool_nodes(dataset_id=dataset_id)
for node in nodes:
if getattr(node, "name", None) == name:
return node
return None
async def _list_tools_in_graph(dataset_id: Optional[UUID]) -> List[Tool]:
"""Return every Tool DataPoint scoped to a dataset (or globally scoped)."""
return await _query_tool_nodes(dataset_id=dataset_id)
async def _query_tool_nodes(dataset_id: Optional[UUID]) -> List[Tool]:
"""Fetch Tool DataPoints from the graph. Returns [] when the graph is empty
or no Tool nodes are persisted.
This path returns [] when graph-backed tools are unavailable, but logs
backend failures so permission and registry incidents are diagnosable.
"""
try:
from cognee.infrastructure.databases.graph import get_graph_engine
except Exception as exc:
logger.warning("Unable to import graph engine while resolving tools: %s", exc)
return []
try:
graph_engine = await get_graph_engine()
except Exception as exc:
logger.warning("Unable to initialize graph engine while resolving tools: %s", exc)
return []
get_by_type = getattr(graph_engine, "get_nodes_by_type", None)
get_graph_data = getattr(graph_engine, "get_graph_data", None)
if get_by_type is None and get_graph_data is None:
logger.warning("Graph engine %s cannot list Tool nodes", type(graph_engine).__name__)
return []
try:
if get_by_type is not None:
raw_nodes = await get_by_type(node_type=Tool)
else:
raw_nodes, _ = await get_graph_data()
except Exception as exc:
logger.warning("Graph-backed Tool lookup failed: %s", exc)
return []
if isinstance(raw_nodes, tuple) and len(raw_nodes) == 2:
raw_nodes = raw_nodes[0]
tools: List[Tool] = []
for raw in raw_nodes or []:
tool = _coerce_tool(raw)
if tool is None:
continue
if dataset_id is None:
if tool.dataset_id is not None:
continue
elif tool.dataset_id not in (None, dataset_id):
continue
tools.append(tool)
return tools
def _coerce_tool(raw) -> Optional[Tool]:
"""Best-effort conversion of a graph-node or vector-payload dict into Tool.
Graph-stored DataPoint metadata lacks the "type" key required by the MetaData
TypedDict; strip it before validation and let Pydantic re-derive defaults.
"""
if isinstance(raw, Tool):
return raw
if isinstance(raw, (list, tuple)) and len(raw) > 1:
raw = raw[1]
data = raw.model_dump() if hasattr(raw, "model_dump") else raw
if not isinstance(data, dict):
return None
data = {k: v for k, v in data.items() if k != "metadata"}
try:
return Tool.model_validate(data)
except Exception:
return None