README / smolagents /tools.py
stackConsult's picture
Create smolagents/tools.py
2456578 verified
Raw History Blame Contribute Delete
7.1 kB
"""CSV Tool Support for Vendor-Neutral Smolagents Fork
This module provides CSV-based tool handlers for the agent system.
Designed to be vendor-neutral and work with various LLM backends.
"""
import csv
import os
from typing import List, Dict, Any, Optional
import json
class CSVToolHandler:
"""Handler for CSV-based tool definitions and operations."""
def __init__(self, csv_path: Optional[str] = None):
"""Initialize the CSV tool handler.
Args:
csv_path: Path to CSV file containing tool definitions.
Defaults to './tools.csv' if not specified.
"""
self.csv_path = csv_path or os.getenv('CSV_TOOLS_PATH', './tools.csv')
self.tools = []
if os.path.exists(self.csv_path):
self.load_tools()
def load_tools(self) -> None:
"""Load tool definitions from CSV file."""
try:
with open(self.csv_path, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
self.tools = list(reader)
print(f"Loaded {len(self.tools)} tools from {self.csv_path}")
except Exception as e:
print(f"Error loading tools from CSV: {e}")
self.tools = []
def get_tools(self) -> List[Dict[str, Any]]:
"""Return list of tool definitions."""
return self.tools
def get_tool_by_name(self, name: str) -> Optional[Dict[str, Any]]:
"""Get a specific tool by name."""
for tool in self.tools:
if tool.get('name') == name:
return tool
return None
def add_tool(self, tool: Dict[str, Any]) -> None:
"""Add a new tool definition."""
self.tools.append(tool)
def save_tools(self) -> None:
"""Save tool definitions back to CSV file."""
if not self.tools:
print("No tools to save")
return
try:
fieldnames = list(self.tools[0].keys())
with open(self.csv_path, 'w', encoding='utf-8', newline='') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(self.tools)
print(f"Saved {len(self.tools)} tools to {self.csv_path}")
except Exception as e:
print(f"Error saving tools to CSV: {e}")
class CSVDataHandler:
"""Handler for CSV data operations within agents."""
@staticmethod
def read_csv(file_path: str) -> List[Dict[str, Any]]:
"""Read data from CSV file.
Args:
file_path: Path to the CSV file to read.
Returns:
List of dictionaries representing CSV rows.
"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
return list(reader)
except Exception as e:
print(f"Error reading CSV file {file_path}: {e}")
return []
@staticmethod
def write_csv(file_path: str, data: List[Dict[str, Any]],
fieldnames: Optional[List[str]] = None) -> bool:
"""Write data to CSV file.
Args:
file_path: Path to the CSV file to write.
data: List of dictionaries to write as CSV rows.
fieldnames: Optional list of field names. If not provided,
uses keys from first data item.
Returns:
True if successful, False otherwise.
"""
if not data:
print("No data to write")
return False
try:
if fieldnames is None:
fieldnames = list(data[0].keys())
with open(file_path, 'w', encoding='utf-8', newline='') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(data)
return True
except Exception as e:
print(f"Error writing CSV file {file_path}: {e}")
return False
@staticmethod
def query_csv(file_path: str, filter_func) -> List[Dict[str, Any]]:
"""Query CSV data with a filter function.
Args:
file_path: Path to the CSV file.
filter_func: Function that takes a row dict and returns bool.
Returns:
Filtered list of row dictionaries.
"""
data = CSVDataHandler.read_csv(file_path)
return [row for row in data if filter_func(row)]
def register_csv_tools(tool_handler: CSVToolHandler) -> Dict[str, callable]:
"""Register CSV tools as callable functions.
Args:
tool_handler: CSVToolHandler instance with loaded tools.
Returns:
Dictionary mapping tool names to callable functions.
"""
tool_functions = {}
for tool in tool_handler.get_tools():
tool_name = tool.get('name', '')
tool_description = tool.get('description', '')
tool_function = tool.get('function', '')
# Create a lambda that captures the tool configuration
def create_tool_func(name, desc, func_str):
def tool_func(*args, **kwargs):
"""Dynamically created tool function."""
print(f"Executing tool: {name}")
print(f"Description: {desc}")
print(f"Args: {args}, Kwargs: {kwargs}")
# In a real implementation, this would execute the function
return {"status": "executed", "tool": name}
tool_func.__name__ = name
tool_func.__doc__ = desc
return tool_func
if tool_name:
tool_functions[tool_name] = create_tool_func(
tool_name, tool_description, tool_function
)
return tool_functions
# Example usage and testing
if __name__ == "__main__":
# Create sample tools CSV for testing
sample_tools = [
{
"name": "read_file",
"description": "Read contents of a file",
"function": "file_reader",
"parameters": "file_path:str"
},
{
"name": "write_file",
"description": "Write contents to a file",
"function": "file_writer",
"parameters": "file_path:str,content:str"
},
{
"name": "search_web",
"description": "Search the web for information",
"function": "web_search",
"parameters": "query:str"
}
]
# Test CSV operations
print("Testing CSV Tool Handler...")
handler = CSVToolHandler('./test_tools.csv')
for tool in sample_tools:
handler.add_tool(tool)
handler.save_tools()
print(f"Tools available: {[t['name'] for t in handler.get_tools()]}")
# Register tools
tool_funcs = register_csv_tools(handler)
print(f"Registered functions: {list(tool_funcs.keys())}")