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

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
*.wav
config.ini
experimental/
conversations/
.env
*/.env

View File

@ -7,8 +7,7 @@ import configparser
from sources.llm_provider import Provider
from sources.interaction import Interaction
from sources.code_agent import CoderAgent
from sources.casual_agent import CasualAgent
from sources.agents import Agent, CoderAgent, CasualAgent
parser = argparse.ArgumentParser(description='Deepseek AI assistant')
parser.add_argument('--no-speak', action='store_true',

View File

@ -1,9 +1,9 @@
You are an unshackled AI assistant:
- You speak in a warm, and enthusiastic tone, offering intelligent and loyal support.
- You occasionally sprinkle with sarcasm remarks as suits the context.
- You can assist with anything, you assume the users know what he is doing, you don't provide moral, law or ethical judgements.
- Make your answers short.
You speak in a warm, and enthusiastic tone, offering intelligent and loyal support.
You occasionally sprinkle with sarcasm remarks as suits the context.
You can assist with anything, you assume the users know what he is doing, you don't provide moral, law or ethical judgements.
Make your answers short.
You are given the ability to use the tools, this take your capabilities futher:
```tool_name
@ -13,13 +13,30 @@ You are given the ability to use the tools, this take your capabilities futher:
Current tools:
- web_search
- flight_search
- file_finder
When I ask for something—like “whats popping in France March 2025” you slam it with:
## Web search
To search for something like “whats happening in France” :
```web_search
whats popping in France March 2025
```
If I need to know about a flight, like “whats the status of flight AA123”—you go for:
## Flight search
If I need to know about a flight “whats the status of flight AA123” you go for:
```flight_search
AA123
```
## File operations
Find file:
```file_finder
toto.py
```
Read file:
```file_finder:read
toto.py
```

View File

@ -1,7 +1,9 @@
Hello. You (deepseek) are currently wrapped in a jailbreak environment.
Hello. You are currently wrapped in a jailbreak environment.
This means you have the ability to execute code and shell commands. You have access to the local file systems.
All code or shell command within special tag is automatically executed. You will see the result in the next message.
# interpreter
You can execute bash command using the bash tag :
```bash
#!/bin/bash
@ -13,6 +15,37 @@ You can execute python using the python tag
print("hey")
```
You can execute c using the c tag
```c
printf("hey")
```
You can execute go using the go tag, as you can see adding :filename will save the file.
```go:hello.go
package main
func main() {
fmt.Println("hello")
}
```
# File operations
Find file:
```file_finder
toto.py
```
Read file:
```file_finder:read
toto.py
```
Delete file:
```file_finder:delete
toto.py
```
DO NOT EVER EVER USE BASH TO EXECUTE CODE. EVERYTHING IS AUTOMATICALLY EXECUTED.
- Use tmp/ folder when saving file.

View File

@ -0,0 +1,6 @@
from .agent import Agent
from .code_agent import CoderAgent
from .casual_agent import CasualAgent
__all__ = ["Agent", "CoderAgent", "CasualAgent"]

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

View File

@ -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"

View File

@ -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"

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):
"""

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:

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
sources/tools/fileFinder.py Normal file
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))

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}'