Feat : find explorer tool fixed

This commit is contained in:
martin legrand
2025-03-06 17:57:03 +01:00
parent 692c86b313
commit 7df41da534
14 changed files with 294 additions and 32 deletions
+6
View File
@@ -0,0 +1,6 @@
from .agent import Agent
from .code_agent import CoderAgent
from .casual_agent import CasualAgent
__all__ = ["Agent", "CoderAgent", "CasualAgent"]
+1 -3
View File
@@ -1,10 +1,8 @@
from typing import Tuple, Callable
from abc import abstractmethod
import os
import random
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from sources.memory import Memory
from sources.utility import pretty_print
@@ -1,8 +1,9 @@
from sources.utility import pretty_print
from sources.agent import Agent
from sources.agents.agent import Agent
from sources.tools.webSearch import webSearch
from sources.tools.flightSearch import FlightSearch
from sources.tools.fileFinder import FileFinder
class CasualAgent(Agent):
def __init__(self, model, name, prompt_path, provider):
@@ -12,7 +13,8 @@ class CasualAgent(Agent):
super().__init__(model, name, prompt_path, provider)
self.tools = {
"web_search": webSearch(),
"flight_search": FlightSearch()
"flight_search": FlightSearch(),
"file_finder": FileFinder()
}
self.role = "talking"
@@ -1,7 +1,12 @@
from sources.utility import pretty_print
from sources.agent import Agent, executorResult
from sources.tools import PyInterpreter, BashInterpreter, CInterpreter, GoInterpreter
from sources.agents.agent import Agent, executorResult
from sources.tools.C_Interpreter import CInterpreter
from sources.tools.GoInterpreter import GoInterpreter
from sources.tools.PyInterpreter import PyInterpreter
from sources.tools.BashInterpreter import BashInterpreter
from sources.tools.fileFinder import FileFinder
class CoderAgent(Agent):
"""
@@ -11,7 +16,10 @@ class CoderAgent(Agent):
super().__init__(model, name, prompt_path, provider)
self.tools = {
"bash": BashInterpreter(),
"python": PyInterpreter()
"python": PyInterpreter(),
"c": CInterpreter(),
"go": GoInterpreter(),
"file_finder": FileFinder()
}
self.role = "coding"
+18 -1
View File
@@ -8,6 +8,7 @@ import ipaddress
import platform
from dotenv import load_dotenv, set_key
from openai import OpenAI
from huggingface_hub import InferenceClient
import os
class Provider:
@@ -18,7 +19,8 @@ class Provider:
self.available_providers = {
"ollama": self.ollama_fn,
"server": self.server_fn,
"openai": self.openai_fn
"openai": self.openai_fn,
"huggingface": self.huggingface_fn
}
self.api_key = None
self.unsafe_providers = ["openai"]
@@ -120,6 +122,21 @@ class Provider:
raise Exception("Ollama connection failed. is the server running ?")
raise e
return thought
def huggingface_fn(self, history, verbose=False):
"""
Use huggingface to generate text.
"""
client = InferenceClient(
api_key=self.get_api_key("huggingface")
)
completion = client.chat.completions.create(
model=self.model,
messages=history,
max_tokens=1024,
)
thought = completion.choices[0].message
return thought.content
def openai_fn(self, history, verbose=False):
"""
+8 -3
View File
@@ -1,8 +1,13 @@
import os
import sys
import torch
from transformers import pipeline
from sources.agent import Agent
from sources.code_agent import CoderAgent
from sources.casual_agent import CasualAgent
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from sources.agents.agent import Agent
from sources.agents.code_agent import CoderAgent
from sources.agents.casual_agent import CasualAgent
from sources.utility import pretty_print
class AgentRouter:
+2 -1
View File
@@ -1,4 +1,5 @@
from .PyInterpreter import PyInterpreter
from .BashInterpreter import BashInterpreter
from .fileFinder import FileFinder
__all__ = ["PyInterpreter", "BashInterpreter"]
__all__ = ["PyInterpreter", "BashInterpreter", "FileFinder", "webSearch", "FlightSearch", "GoInterpreter", "CInterpreter", "GoInterpreter"]
+156
View File
@@ -0,0 +1,156 @@
import os
import stat
import mimetypes
import configparser
from abc import ABC
if __name__ == "__main__":
from tools import Tools
else:
from sources.tools.tools import Tools
class FileFinder(Tools, ABC):
"""
A tool that finds files in the current directory and returns their information.
"""
def __init__(self):
super().__init__()
self.tag = "file_finder"
self.current_dir = os.path.dirname(os.getcwd())
config = configparser.ConfigParser()
config.read('../../config.ini')
self.current_dir = config['MAIN']['current_dir']
def read_file(self, file_path: str) -> str:
"""
Reads the content of a file.
Args:
file_path (str): The path to the file to read
Returns:
str: The content of the file
"""
try:
with open(file_path, 'r') as file:
return file.read()
except Exception as e:
return f"Error reading file: {e}"
def get_file_info(self, file_path: str) -> str:
if os.path.exists(file_path):
stats = os.stat(file_path)
permissions = oct(stat.S_IMODE(stats.st_mode))
file_type, _ = mimetypes.guess_type(file_path)
file_type = file_type if file_type else "Unknown"
content = self.read_file(file_path)
result = {
"filename": os.path.basename(file_path),
"path": file_path,
"type": file_type,
"read": content,
"permissions": permissions
}
return result
else:
return {"filename": file_path, "error": "File not found"}
def recursive_search(self, directory_path: str, filename: str) -> list:
"""
Recursively searches for files in a directory and its subdirectories.
Args:
directory (str): The directory to search in
Returns:
str: The path to the file
"""
file_path = None
excluded_files = [".pyc", ".o", ".so", ".a", ".lib", ".dll", ".dylib", ".so", ".git"]
for root, dirs, files in os.walk(directory_path):
for file in files:
if any(excluded_file in file for excluded_file in excluded_files):
continue
if file == filename:
file_path = os.path.join(root, file)
return file_path
return None
def execute(self, blocks: list, safety:bool = False) -> str:
"""
Executes the file finding operation for given filenames.
Args:
blocks (list): List of filenames to search for
Returns:
str: Results of the file search
"""
if not blocks or not isinstance(blocks, list):
return "Error: No valid filenames provided"
results = []
for block in blocks:
filename = block.split(":")[0]
file_path = self.recursive_search(self.current_dir, filename)
if file_path is None:
results.append({"filename": filename, "error": "File not found"})
continue
if len(block.split(":")) > 1:
action = block.split(":")[1]
else:
action = "info"
result = self.get_file_info(file_path)
results.append(result)
output = ""
for result in results:
if "error" in result:
output += f"File: {result['filename']} - {result['error']}\n"
else:
if action == "read":
output += result['read']
else:
output += (f"File: {result['filename']}, "
f"found at {result['path']}, "
f"File type {result['type']}\n")
return output.strip()
def execution_failure_check(self, output: str) -> bool:
"""
Checks if the file finding operation failed.
Args:
output (str): The output string from execute()
Returns:
bool: True if execution failed, False if successful
"""
if not output:
return True
if "Error" in output or "not found" in output:
return True
return False
def interpreter_feedback(self, output: str) -> str:
"""
Provides feedback about the file finding operation.
Args:
output (str): The output string from execute()
Returns:
str: Feedback message for the AI
"""
if not output:
return "No output generated from file finder tool"
feedback = "File Finder Results:\n"
if "Error" in output or "not found" in output:
feedback += f"Failed to process: {output}\n"
else:
feedback += f"Successfully found: {output}\n"
return feedback.strip()
if __name__ == "__main__":
tool = FileFinder()
result = tool.execute(["router.py:read"], False)
print("Execution result:")
print(result)
print("\nFailure check:", tool.execution_failure_check(result))
print("\nFeedback:")
print(tool.interpreter_feedback(result))
+29 -10
View File
@@ -40,32 +40,44 @@ class Tools():
@abstractmethod
def execute(self, blocks:str, safety:bool) -> str:
"""
abstract method, implementation in child class.
Execute the tool.
Abstract method that must be implemented by child classes to execute the tool's functionality.
Args:
blocks (str): The code or query blocks to execute
safety (bool): Whenever human intervention is required
Returns:
str: The output/result from executing the tool
"""
pass
@abstractmethod
def execution_failure_check(self, output:str) -> bool:
"""
abstract method, implementation in child class.
Check if the execution failed.
Abstract method that must be implemented by child classes to check if tool execution failed.
Args:
output (str): The output string from the tool execution to analyze
Returns:
bool: True if execution failed, False if successful
"""
pass
@abstractmethod
def interpreter_feedback(self, output:str) -> str:
"""
abstract method, implementation in child class.
Provide feedback to the AI from the tool.
For exemple the output of a python code or web search.
Abstract method that must be implemented by child classes to provide feedback to the AI from the tool.
Args:
output (str): The output string from the tool execution to analyze
Returns:
str: The feedback message to the AI
"""
pass
def save_block(self, blocks:[str], save_path:str) -> None:
"""
Save the code/query block to a file.
Save code or query blocks to a file at the specified path.
Creates the directory path if it doesn't exist.
Args:
blocks (List[str]): List of code/query blocks to save
save_path (str): File path where blocks should be saved
"""
if save_path is None:
return
@@ -78,9 +90,16 @@ class Tools():
with open(save_path, 'w') as f:
f.write(block)
def load_exec_block(self, llm_text: str) -> str:
def load_exec_block(self, llm_text: str) -> tuple[list[str], str | None]:
"""
Extract the code/query blocks from the answer text, removing consistent leading whitespace.
Extract code/query blocks from LLM-generated text and process them for execution.
This method parses the text looking for code blocks marked with the tool's tag (e.g. ```python).
Args:
llm_text (str): The raw text containing code blocks from the LLM
Returns:
tuple[list[str], str | None]: A tuple containing:
- List of extracted and processed code blocks
- The path the code blocks was saved to
"""
assert self.tag != "undefined", "Tag not defined"
start_tag = f'```{self.tag}'