From 99ebbdfea2d02ebdc655dcc1927c77aac73487bc Mon Sep 17 00:00:00 2001 From: LHMQ878 <72402929@cityu-dg.edu.cn> Date: Wed, 12 Aug 2026 19:30:37 +0800 Subject: [PATCH] fix: create default agent tools on first use (#1175) --- docs/agent/configuration.md | 1 + setup.py | 2 +- src/python/txtai/agent/tool/factory.py | 62 +++++++++++++++++++------- test/python/testagent.py | 17 +++++++ 4 files changed, 64 insertions(+), 18 deletions(-) diff --git a/docs/agent/configuration.md b/docs/agent/configuration.md index d53f5118..0b2f5b92 100644 --- a/docs/agent/configuration.md +++ b/docs/agent/configuration.md @@ -99,6 +99,7 @@ The following shortcut strings load tools directly. Passing a Tool instance is a | write | Writes content to file | | *.md | Loads a [`skill.md`](https://agentskills.io/specification) file | + ## instructions ```yaml diff --git a/setup.py b/setup.py index 4a897192..9172dea0 100644 --- a/setup.py +++ b/setup.py @@ -53,7 +53,7 @@ extras["dev"] = [ "pylint", ] -extras["agent"] = ["jinja2>=3.1.6", "mcp<2.0", "mcpadapt>=0.1.0", "smolagents>=1.23"] +extras["agent"] = ["beautifulsoup4>=4.9.3", "jinja2>=3.1.6", "mcp<2.0", "mcpadapt>=0.1.0", "smolagents>=1.23"] extras["ann"] = [ "annoy>=1.16.3", diff --git a/src/python/txtai/agent/tool/factory.py b/src/python/txtai/agent/tool/factory.py index 06aa81ea..9474b99d 100644 --- a/src/python/txtai/agent/tool/factory.py +++ b/src/python/txtai/agent/tool/factory.py @@ -30,22 +30,50 @@ class ToolFactory: Methods to create tools. """ - # Default toolkit - DEFAULTS = { - "bash": BashTool(), - "edit": EditTool(), - "glob": GlobTool(), - "grep": GrepTool(), - "python": PythonInterpreterTool(), - "question": UserInputTool(), - "read": ReadTool(), - "todowrite": TodoWriteTool(), - "websearch": WebSearchTool(), - "write": WriteTool(), - } + # Names in the default toolkit. + DEFAULTS = ( + "bash", + "edit", + "glob", + "grep", + "python", + "question", + "read", + "todowrite", + "websearch", + "webview", + "write", + ) - # Backwards compatible mappings - DEFAULTS["webview"] = DEFAULTS["read"] + @staticmethod + def default(name): + """ + Creates a default tool by alias name. + + Args: + name: default tool alias name + + Returns: + Tool + """ + + tools = { + "bash": BashTool, + "edit": EditTool, + "glob": GlobTool, + "grep": GrepTool, + "python": PythonInterpreterTool, + "question": UserInputTool, + "read": ReadTool, + "webview": ReadTool, + "todowrite": TodoWriteTool, + "websearch": WebSearchTool, + "write": WriteTool, + } + if name not in tools: + raise KeyError(name) + + return tools[name]() @staticmethod def create(config): @@ -81,11 +109,11 @@ class ToolFactory: # Get default tool, if applicable elif isinstance(tool, str) and tool in ToolFactory.DEFAULTS: - tool = ToolFactory.DEFAULTS[tool] + tool = ToolFactory.default(tool) # Get ALL default tools, if applicable elif isinstance(tool, str) and tool == "defaults": - tools.extend(set(ToolFactory.DEFAULTS.values())) + tools.extend(ToolFactory.default(name) for name in ToolFactory.DEFAULTS if name != "webview") tool = None # Support importing MCP tool collections diff --git a/test/python/testagent.py b/test/python/testagent.py index 1b6bdcf2..bf7d9542 100644 --- a/test/python/testagent.py +++ b/test/python/testagent.py @@ -3,6 +3,7 @@ Agent module tests """ import os +import subprocess import tempfile import unittest @@ -13,6 +14,7 @@ from datetime import datetime from smolagents import CodeAgent, PythonInterpreterTool from txtai.agent import Agent +from txtai.agent.tool import ToolFactory from txtai.embeddings import Embeddings # agents.md content @@ -227,3 +229,18 @@ class TestAgent(unittest.TestCase): agent = Agent(tools=["http://localhost:8000/mcp"], llm="hf-internal-testing/tiny-random-LlamaForCausalLM", max_steps=1) self.assertEqual(len(agent.tools), 2) + + +class TestToolFactory(unittest.TestCase): + """ + ToolFactory tests. + """ + + def testDefaultToolCreation(self): + """ + Test default tools are created on demand. + """ + + tool = ToolFactory.default("bash") + self.assertEqual(tool.name, "bash") + self.assertIsNot(tool, ToolFactory.default("bash"))