|
|
""" |
|
|
File Tools for SPARKNET |
|
|
Tools for file system operations |
|
|
""" |
|
|
|
|
|
from pathlib import Path |
|
|
from typing import Optional |
|
|
from loguru import logger |
|
|
from .base_tool import BaseTool, ToolResult |
|
|
import json |
|
|
|
|
|
|
|
|
class FileReaderTool(BaseTool): |
|
|
"""Tool for reading files.""" |
|
|
|
|
|
def __init__(self): |
|
|
super().__init__( |
|
|
name="file_reader", |
|
|
description="Read contents of a file from the file system", |
|
|
) |
|
|
self.add_parameter("file_path", "str", "Path to the file to read", required=True) |
|
|
self.add_parameter("encoding", "str", "File encoding", required=False, default="utf-8") |
|
|
|
|
|
async def execute(self, file_path: str, encoding: str = "utf-8", **kwargs) -> ToolResult: |
|
|
""" |
|
|
Read file contents. |
|
|
|
|
|
Args: |
|
|
file_path: Path to file |
|
|
encoding: File encoding |
|
|
|
|
|
Returns: |
|
|
ToolResult with file contents |
|
|
""" |
|
|
try: |
|
|
path = Path(file_path) |
|
|
|
|
|
if not path.exists(): |
|
|
return ToolResult( |
|
|
success=False, |
|
|
output=None, |
|
|
error=f"File not found: {file_path}", |
|
|
) |
|
|
|
|
|
if not path.is_file(): |
|
|
return ToolResult( |
|
|
success=False, |
|
|
output=None, |
|
|
error=f"Path is not a file: {file_path}", |
|
|
) |
|
|
|
|
|
with open(path, "r", encoding=encoding) as f: |
|
|
contents = f.read() |
|
|
|
|
|
return ToolResult( |
|
|
success=True, |
|
|
output=contents, |
|
|
metadata={ |
|
|
"file_path": str(path.absolute()), |
|
|
"size_bytes": len(contents), |
|
|
"encoding": encoding, |
|
|
}, |
|
|
) |
|
|
|
|
|
except Exception as e: |
|
|
return ToolResult( |
|
|
success=False, |
|
|
output=None, |
|
|
error=f"Error reading file: {str(e)}", |
|
|
) |
|
|
|
|
|
|
|
|
class FileWriterTool(BaseTool): |
|
|
"""Tool for writing files.""" |
|
|
|
|
|
def __init__(self): |
|
|
super().__init__( |
|
|
name="file_writer", |
|
|
description="Write contents to a file", |
|
|
) |
|
|
self.add_parameter("file_path", "str", "Path to the file to write", required=True) |
|
|
self.add_parameter("content", "str", "Content to write to file", required=True) |
|
|
self.add_parameter("encoding", "str", "File encoding", required=False, default="utf-8") |
|
|
self.add_parameter("append", "bool", "Append to file instead of overwriting", required=False, default=False) |
|
|
|
|
|
async def execute( |
|
|
self, |
|
|
file_path: str, |
|
|
content: str, |
|
|
encoding: str = "utf-8", |
|
|
append: bool = False, |
|
|
**kwargs, |
|
|
) -> ToolResult: |
|
|
""" |
|
|
Write content to file. |
|
|
|
|
|
Args: |
|
|
file_path: Path to file |
|
|
content: Content to write |
|
|
encoding: File encoding |
|
|
append: Whether to append |
|
|
|
|
|
Returns: |
|
|
ToolResult |
|
|
""" |
|
|
try: |
|
|
path = Path(file_path) |
|
|
|
|
|
|
|
|
path.parent.mkdir(parents=True, exist_ok=True) |
|
|
|
|
|
mode = "a" if append else "w" |
|
|
with open(path, mode, encoding=encoding) as f: |
|
|
f.write(content) |
|
|
|
|
|
return ToolResult( |
|
|
success=True, |
|
|
output=f"Successfully wrote to {file_path}", |
|
|
metadata={ |
|
|
"file_path": str(path.absolute()), |
|
|
"bytes_written": len(content.encode(encoding)), |
|
|
"mode": "append" if append else "write", |
|
|
}, |
|
|
) |
|
|
|
|
|
except Exception as e: |
|
|
return ToolResult( |
|
|
success=False, |
|
|
output=None, |
|
|
error=f"Error writing file: {str(e)}", |
|
|
) |
|
|
|
|
|
|
|
|
class FileSearchTool(BaseTool): |
|
|
"""Tool for searching files.""" |
|
|
|
|
|
def __init__(self): |
|
|
super().__init__( |
|
|
name="file_search", |
|
|
description="Search for files matching a pattern", |
|
|
) |
|
|
self.add_parameter("directory", "str", "Directory to search in", required=True) |
|
|
self.add_parameter("pattern", "str", "File pattern to match (e.g., '*.txt')", required=True) |
|
|
self.add_parameter("recursive", "bool", "Search recursively", required=False, default=True) |
|
|
|
|
|
async def execute( |
|
|
self, |
|
|
directory: str, |
|
|
pattern: str, |
|
|
recursive: bool = True, |
|
|
**kwargs, |
|
|
) -> ToolResult: |
|
|
""" |
|
|
Search for files. |
|
|
|
|
|
Args: |
|
|
directory: Directory to search |
|
|
pattern: File pattern |
|
|
recursive: Search recursively |
|
|
|
|
|
Returns: |
|
|
ToolResult with list of matching files |
|
|
""" |
|
|
try: |
|
|
path = Path(directory) |
|
|
|
|
|
if not path.exists(): |
|
|
return ToolResult( |
|
|
success=False, |
|
|
output=None, |
|
|
error=f"Directory not found: {directory}", |
|
|
) |
|
|
|
|
|
if recursive: |
|
|
files = list(path.rglob(pattern)) |
|
|
else: |
|
|
files = list(path.glob(pattern)) |
|
|
|
|
|
file_paths = [str(f.absolute()) for f in files if f.is_file()] |
|
|
|
|
|
return ToolResult( |
|
|
success=True, |
|
|
output=file_paths, |
|
|
metadata={ |
|
|
"directory": str(path.absolute()), |
|
|
"pattern": pattern, |
|
|
"count": len(file_paths), |
|
|
"recursive": recursive, |
|
|
}, |
|
|
) |
|
|
|
|
|
except Exception as e: |
|
|
return ToolResult( |
|
|
success=False, |
|
|
output=None, |
|
|
error=f"Error searching files: {str(e)}", |
|
|
) |
|
|
|
|
|
|
|
|
class DirectoryListTool(BaseTool): |
|
|
"""Tool for listing directory contents.""" |
|
|
|
|
|
def __init__(self): |
|
|
super().__init__( |
|
|
name="directory_list", |
|
|
description="List contents of a directory", |
|
|
) |
|
|
self.add_parameter("directory", "str", "Directory to list", required=True) |
|
|
self.add_parameter("include_hidden", "bool", "Include hidden files", required=False, default=False) |
|
|
|
|
|
async def execute( |
|
|
self, |
|
|
directory: str, |
|
|
include_hidden: bool = False, |
|
|
**kwargs, |
|
|
) -> ToolResult: |
|
|
""" |
|
|
List directory contents. |
|
|
|
|
|
Args: |
|
|
directory: Directory to list |
|
|
include_hidden: Include hidden files |
|
|
|
|
|
Returns: |
|
|
ToolResult with directory contents |
|
|
""" |
|
|
try: |
|
|
path = Path(directory) |
|
|
|
|
|
if not path.exists(): |
|
|
return ToolResult( |
|
|
success=False, |
|
|
output=None, |
|
|
error=f"Directory not found: {directory}", |
|
|
) |
|
|
|
|
|
if not path.is_dir(): |
|
|
return ToolResult( |
|
|
success=False, |
|
|
output=None, |
|
|
error=f"Path is not a directory: {directory}", |
|
|
) |
|
|
|
|
|
items = [] |
|
|
for item in path.iterdir(): |
|
|
if not include_hidden and item.name.startswith("."): |
|
|
continue |
|
|
|
|
|
items.append({ |
|
|
"name": item.name, |
|
|
"path": str(item.absolute()), |
|
|
"type": "directory" if item.is_dir() else "file", |
|
|
"size": item.stat().st_size if item.is_file() else None, |
|
|
}) |
|
|
|
|
|
return ToolResult( |
|
|
success=True, |
|
|
output=items, |
|
|
metadata={ |
|
|
"directory": str(path.absolute()), |
|
|
"count": len(items), |
|
|
}, |
|
|
) |
|
|
|
|
|
except Exception as e: |
|
|
return ToolResult( |
|
|
success=False, |
|
|
output=None, |
|
|
error=f"Error listing directory: {str(e)}", |
|
|
) |
|
|
|