""" Tools adapter — bridges the existing skill/tool system with LangGraph's ToolNode. LangGraph's ToolNode expects callable tools (typically @tool-decorated functions). This module wraps our skill-based tool definitions and async executors so ToolNode can invoke them without any changes to the skills/ layer. """ from __future__ import annotations import json from typing import Any from langchain_core.tools import tool from skills import get_all_tools, execute_tool def build_langgraph_tools(skill_names: list[str]) -> list: """ Convert the registered skill tool definitions into LangChain-compatible @tool-decorated functions that ToolNode can call. Each tool wraps the existing `execute_tool()` pipeline, so the skill system's ToolResult + httpx session handling is fully preserved. """ tool_defs = get_all_tools(skill_names) wrapped: list = [] for td in tool_defs: fn_def = td.get("function", {}) fn_name = fn_def.get("name", "") fn_desc = fn_def.get("description", "") # Create a unique factory so each closure captures the right fn_name def _make_tool(name: str, desc: str, skills: list[str]): @tool(name, description=desc) async def _wrapped(**kwargs: Any) -> str: """Execute the tool via the skill system and return its content.""" result = await execute_tool(skills, name, kwargs) if result is None: return f"Tool '{name}' is not available." return result.content # Stash the original OpenAI schema so LangGraph can use it _wrapped.metadata = fn_def return _wrapped wrapped.append(_make_tool(fn_name, fn_desc, skill_names)) return wrapped