From 2087dde4c4a3526cac3baf088ca334793db36a53 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 20 May 2026 11:18:02 +0530 Subject: [PATCH] added tool support for the agent --- src/fenn/agents/agent.py | 2 ++ src/fenn/agents/node.py | 17 +++++++++++----- src/fenn/agents/tools.py | 42 ++++++++++++++++++++++++++++++---------- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/fenn/agents/agent.py b/src/fenn/agents/agent.py index 128d558..f63227b 100644 --- a/src/fenn/agents/agent.py +++ b/src/fenn/agents/agent.py @@ -1,5 +1,6 @@ from fenn.agents import Flow from fenn.agents.node import ThinkNode, ActNode, ObserveNode +from fenn.agents.tools import get_tool_schema import yaml class Agent: @@ -27,6 +28,7 @@ def run(self, user_input): {"role": "system", "content": self.config["agent"]["system_prompt"]}, {"role": "user", "content": user_input} ], + "tools": get_tool_schema(), "iterations": 0, "max_iterations": self.config["agent"]["max_iterations"], "last_thought": None, diff --git a/src/fenn/agents/node.py b/src/fenn/agents/node.py index 89ce6b5..b53c0ba 100644 --- a/src/fenn/agents/node.py +++ b/src/fenn/agents/node.py @@ -1,5 +1,5 @@ from fenn.agents import Node -from fenn.agents.tools import TOOLS +from fenn.agents.tools import execute_tool class ThinkNode(Node): def prep(self, shared): @@ -22,13 +22,20 @@ def prep(self, shared): return shared["last_thought"] def exec(self, thought): - line = [l for l in thought.split("\n") if l.startswith("Action:")][0] - tool_call = line.replace("Action:", "").strip() + if "Action:" in thought: + line = [l for l in thought.split("\n") if l.startswith("Action:")][0] + tool_call = line.replace("Action:", "").strip() + else: + tool_call = thought.strip() tool_name = tool_call.split("(")[0] - tool_arg = tool_call.split("(")[1].rstrip(")") + args_str = tool_call.split("(")[1].rstrip(")") + tool_args = [arg.strip() for arg in args_str.split(",")] - result = TOOLS[tool_name](tool_arg) + try: + result = execute_tool(tool_name, *tool_args) + except Exception as e: + result = f"Error: {e}" return result def post(self, shared, prep_res, exec_res): diff --git a/src/fenn/agents/tools.py b/src/fenn/agents/tools.py index 50edbc0..9baf6e8 100644 --- a/src/fenn/agents/tools.py +++ b/src/fenn/agents/tools.py @@ -1,10 +1,32 @@ -TOOLS = { - "search": lambda query: f"Search result for: {query}", - "calculator": lambda expr: str(eval(expr)) -} - -# This is what you put in the LLM prompt so it knows what tools exist -TOOL_DESCRIPTIONS = """ -- search(query): Search the web -- calculator(expr): Evaluate a math expression -""" \ No newline at end of file +import inspect +from functools import wraps +from typing import Dict, Any + +TOOLS_REGISTRY: Dict[str, Dict[str, Any]] = {} + +def tool(func): + """ + A decorator that registers an executable function as a tool + """ + @wraps(func) + def decorator(*args, **kwargs): + return func(*args, **kwargs) + tool_name = func.__name__ + tools_description = inspect.getdoc(func) or "No description provided" + + TOOLS_REGISTRY[tool_name] = { + "schema": { + "name": tool_name, + "description": tools_description + }, + "execute": func + } + return decorator + +def get_tool_schema(): + return [info["schema"] for info in TOOLS_REGISTRY.values()] + +def execute_tool(name: str, *args, **kwargs): + if name not in TOOLS_REGISTRY: + raise ValueError(f"Tool '{name}' is not registered.") + return TOOLS_REGISTRY[name]["execute"](*args, **kwargs) \ No newline at end of file