Merge pull request #18 from Fosowl/dev
Fix various issues, requirement not up to date, search not working, file search bug
@ -3,9 +3,9 @@ is_local = True
|
||||
provider_name = ollama
|
||||
provider_model = deepseek-r1:14b
|
||||
provider_server_address = 127.0.0.1:5000
|
||||
agent_name = jarvis
|
||||
agent_name = Friday
|
||||
recover_last_session = True
|
||||
save_session = False
|
||||
speak = True
|
||||
listen = False
|
||||
work_dir = None
|
||||
work_dir = none
|
Before Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 254 KiB |
BIN
media/exemples/basic_web_search.png
Normal file
After Width: | Height: | Size: 286 KiB |
BIN
media/exemples/files_interaction.png
Normal file
After Width: | Height: | Size: 178 KiB |
BIN
media/exemples/find_files.png
Normal file
After Width: | Height: | Size: 103 KiB |
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 107 KiB |
@ -13,7 +13,10 @@ flask==3.1.0
|
||||
soundfile==0.13.1
|
||||
protobuf==3.20.3
|
||||
termcolor==2.5.0
|
||||
ipython==9.0.2
|
||||
gliclass==0.1.8
|
||||
pyaudio==0.2.14
|
||||
librosa==0.10.2.post1
|
||||
# if use chinese
|
||||
ordered_set
|
||||
pypinyin
|
||||
|
3
setup.py
@ -30,6 +30,9 @@ setup(
|
||||
"protobuf==3.20.3",
|
||||
"termcolor==2.5.0",
|
||||
"gliclass==0.1.8",
|
||||
"ipython==7.16.1",
|
||||
"pyaudio-0.2.14",
|
||||
"librosa==0.10.2.post1"
|
||||
],
|
||||
extras_require={
|
||||
"chinese": [
|
||||
|
@ -3,10 +3,13 @@ from typing import Tuple, Callable
|
||||
from abc import abstractmethod
|
||||
import os
|
||||
import random
|
||||
import time
|
||||
|
||||
from sources.memory import Memory
|
||||
from sources.utility import pretty_print
|
||||
|
||||
random.seed(time.time())
|
||||
|
||||
class executorResult:
|
||||
"""
|
||||
A class to store the result of a tool execution.
|
||||
@ -106,7 +109,6 @@ class Agent():
|
||||
if speech_module is None:
|
||||
return
|
||||
messages = ["Please be patient sir, I am working on it.",
|
||||
"At it, sir. In the meantime, how about a joke?",
|
||||
"Computing... I recommand you have a coffee while I work.",
|
||||
"Hold on, I’m crunching numbers.",
|
||||
"Working on it sir, please let me think."]
|
||||
|
@ -18,22 +18,23 @@ class CasualAgent(Agent):
|
||||
"file_finder": FileFinder(),
|
||||
"bash": BashInterpreter()
|
||||
}
|
||||
self.role = "talking, advices and philosophical"
|
||||
self.role = "talking, advices, events and philosophical"
|
||||
|
||||
def process(self, prompt, speech_module) -> str:
|
||||
complete = False
|
||||
exec_success = False
|
||||
self.memory.push('user', prompt)
|
||||
|
||||
self.wait_message(speech_module)
|
||||
while not complete:
|
||||
if exec_success:
|
||||
complete = True
|
||||
animate_thinking("Thinking...", color="status")
|
||||
answer, reasoning = self.llm_request()
|
||||
exec_success, _ = self.execute_modules(answer)
|
||||
answer = self.remove_blocks(answer)
|
||||
self.last_answer = answer
|
||||
complete = True
|
||||
for tool in self.tools.values():
|
||||
if tool.found_executable_blocks():
|
||||
complete = False # AI read results and continue the conversation
|
||||
return answer, reasoning
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -32,6 +32,7 @@ class CoderAgent(Agent):
|
||||
animate_thinking("Thinking...", color="status")
|
||||
self.wait_message(speech_module)
|
||||
answer, reasoning = self.llm_request()
|
||||
animate_thinking("Executing code...", color="status")
|
||||
exec_success, _ = self.execute_modules(answer)
|
||||
answer = self.remove_blocks(answer)
|
||||
self.last_answer = answer
|
||||
|
@ -30,6 +30,10 @@ class FileAgent(Agent):
|
||||
exec_success, _ = self.execute_modules(answer)
|
||||
answer = self.remove_blocks(answer)
|
||||
self.last_answer = answer
|
||||
complete = True
|
||||
for name, tool in self.tools.items():
|
||||
if tool.found_executable_blocks():
|
||||
complete = False # AI read results and continue the conversation
|
||||
return answer, reasoning
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -81,6 +81,9 @@ class PlannerAgent(Agent):
|
||||
speech_module.speak(f"I will {task_name}. I assigned the {task['agent']} agent to the task.")
|
||||
try:
|
||||
self.agents[task['agent'].lower()].process(agent_prompt, None)
|
||||
pretty_print(f"-- Agent answer ---\n\n", color="output")
|
||||
self.agents[task['agent'].lower()].show_answer()
|
||||
pretty_print(f"\n\n", color="output")
|
||||
except Exception as e:
|
||||
pretty_print(f"Error: {e}", color="failure")
|
||||
speech_module.speak(f"I encountered an error: {e}")
|
||||
|
@ -80,7 +80,7 @@ if __name__ == "__main__":
|
||||
Hey could you search the web for the latest news on the stock market ?
|
||||
""",
|
||||
"""
|
||||
hey can you give dating advice ?
|
||||
hey can you give give a list of the files in the current directory ?
|
||||
""",
|
||||
"""
|
||||
Make a cool game to illustrate the current relation between USA and europe
|
||||
|
@ -40,8 +40,7 @@ class Speech():
|
||||
)
|
||||
for i, (gs, ps, audio) in enumerate(generator):
|
||||
audio_file = 'sample.wav'
|
||||
print(audio_file)
|
||||
display(Audio(data=audio, rate=24000, autoplay=i==0))
|
||||
display(Audio(data=audio, rate=24000, autoplay=i==0), display_id=False)
|
||||
sf.write(audio_file, audio, 24000) # save each audio file
|
||||
if platform.system().lower() != "windows":
|
||||
subprocess.call(["afplay", audio_file])
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
from io import StringIO
|
||||
|
||||
@ -25,10 +26,15 @@ class PyInterpreter(Tools):
|
||||
return "Code rejected by user."
|
||||
stdout_buffer = StringIO()
|
||||
sys.stdout = stdout_buffer
|
||||
global_vars = {
|
||||
'__builtins__': __builtins__,
|
||||
'os': os,
|
||||
'sys': sys,
|
||||
}
|
||||
code = '\n\n'.join(codes)
|
||||
try:
|
||||
try:
|
||||
buffer = exec(code)
|
||||
buffer = exec(code, global_vars)
|
||||
if buffer is not None:
|
||||
output = buffer + '\n'
|
||||
except Exception as e:
|
||||
|
@ -62,7 +62,6 @@ class FileFinder(Tools):
|
||||
file_path = None
|
||||
excluded_files = [".pyc", ".o", ".so", ".a", ".lib", ".dll", ".dylib", ".so", ".git"]
|
||||
for root, dirs, files in os.walk(directory_path):
|
||||
print(f"Root: {root}, Files: {files}")
|
||||
for file in files:
|
||||
if any(excluded_file in file for excluded_file in excluded_files):
|
||||
continue
|
||||
|
@ -39,6 +39,7 @@ class Tools():
|
||||
self.messages = []
|
||||
self.config = configparser.ConfigParser()
|
||||
self.current_dir = self.create_work_dir()
|
||||
self.excutable_blocks_found = False
|
||||
|
||||
def check_config_dir_validity(self):
|
||||
"""
|
||||
@ -129,6 +130,14 @@ class Tools():
|
||||
print(f"Saving code block to: {save_path}")
|
||||
with open(os.path.join(directory, save_path_file), 'w') as f:
|
||||
f.write(block)
|
||||
|
||||
def found_executable_blocks(self):
|
||||
"""
|
||||
Check if executable blocks were found.
|
||||
"""
|
||||
tmp = self.excutable_blocks_found
|
||||
self.excutable_blocks_found = False
|
||||
return tmp
|
||||
|
||||
def load_exec_block(self, llm_text: str) -> tuple[list[str], str | None]:
|
||||
"""
|
||||
@ -178,6 +187,7 @@ class Tools():
|
||||
if ':' in content.split('\n')[0]:
|
||||
save_path = content.split('\n')[0].split(':')[1]
|
||||
content = content[content.find('\n')+1:]
|
||||
self.excutable_blocks_found = True
|
||||
code_blocks.append(content)
|
||||
start_index = end_pos + len(end_tag)
|
||||
return code_blocks, save_path
|
||||
|
@ -6,9 +6,13 @@ import dotenv
|
||||
dotenv.load_dotenv()
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from utility import animate_thinking, pretty_print
|
||||
from tools import Tools
|
||||
else:
|
||||
from sources.tools.tools import Tools
|
||||
from sources.utility import animate_thinking, pretty_print
|
||||
|
||||
class webSearch(Tools):
|
||||
def __init__(self, api_key: str = None):
|
||||
@ -24,6 +28,7 @@ class webSearch(Tools):
|
||||
return "Error: No SerpApi key provided."
|
||||
for block in blocks:
|
||||
query = block.strip()
|
||||
pretty_print(f"Searching for: {query}", color="status")
|
||||
if not query:
|
||||
return "Error: No search query provided."
|
||||
|
||||
@ -32,19 +37,21 @@ class webSearch(Tools):
|
||||
params = {
|
||||
"q": query,
|
||||
"api_key": self.api_key,
|
||||
"num": 1,
|
||||
"num": 100,
|
||||
"output": "json"
|
||||
}
|
||||
response = requests.get(url, params=params)
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()
|
||||
results = []
|
||||
if "organic_results" in data and len(data["organic_results"]) > 0:
|
||||
first_result = data["organic_results"][0]
|
||||
title = first_result.get("title", "No title")
|
||||
snippet = first_result.get("snippet", "No snippet available")
|
||||
link = first_result.get("link", "No link available")
|
||||
return f"Title: {title}\nSnippet: {snippet}\nLink: {link}"
|
||||
for result in data["organic_results"][:50]:
|
||||
title = result.get("title", "No title")
|
||||
snippet = result.get("snippet", "No snippet available")
|
||||
link = result.get("link", "No link available")
|
||||
results.append(f"Title: {title}\nSnippet: {snippet}\nLink: {link}")
|
||||
return "\n\n".join(results)
|
||||
else:
|
||||
return "No results found for the query."
|
||||
except requests.RequestException as e:
|
||||
@ -65,6 +72,6 @@ class webSearch(Tools):
|
||||
if __name__ == "__main__":
|
||||
search_tool = webSearch(api_key=os.getenv("SERPAPI_KEY"))
|
||||
query = "when did covid start"
|
||||
result = search_tool.execute(query, safety=True)
|
||||
result = search_tool.execute([query], safety=True)
|
||||
feedback = search_tool.interpreter_feedback(result)
|
||||
print(feedback)
|
@ -2,6 +2,9 @@
|
||||
from colorama import Fore
|
||||
from termcolor import colored
|
||||
import platform
|
||||
import threading
|
||||
import itertools
|
||||
import time
|
||||
|
||||
|
||||
def pretty_print(text, color = "info"):
|
||||
@ -49,44 +52,41 @@ def pretty_print(text, color = "info"):
|
||||
color = "default"
|
||||
print(colored(text, color_map[color]))
|
||||
|
||||
def animate_thinking(text="thinking...", color="status", duration=2):
|
||||
def animate_thinking(text, color="status", duration=2):
|
||||
"""
|
||||
Display an animated "thinking..." indicator.
|
||||
|
||||
Display an animated "thinking..." indicator in a separate thread.
|
||||
Args:
|
||||
text (str): Text to display (default: "thinking...")
|
||||
color (str): Color for the text (matches pretty_print colors)
|
||||
text (str): Text to display
|
||||
color (str): Color for the text
|
||||
duration (float): How long to animate in seconds
|
||||
"""
|
||||
import time
|
||||
import itertools
|
||||
def _animate():
|
||||
color_map = {
|
||||
"success": (Fore.GREEN, "green"),
|
||||
"failure": (Fore.RED, "red"),
|
||||
"status": (Fore.LIGHTGREEN_EX, "light_green"),
|
||||
"code": (Fore.LIGHTBLUE_EX, "light_blue"),
|
||||
"warning": (Fore.YELLOW, "yellow"),
|
||||
"output": (Fore.LIGHTCYAN_EX, "cyan"),
|
||||
"default": (Fore.RESET, "black"),
|
||||
"info": (Fore.CYAN, "cyan")
|
||||
}
|
||||
|
||||
color_map = {
|
||||
"success": (Fore.GREEN, "green"),
|
||||
"failure": (Fore.RED, "red"),
|
||||
"status": (Fore.LIGHTGREEN_EX, "light_green"),
|
||||
"code": (Fore.LIGHTBLUE_EX, "light_blue"),
|
||||
"warning": (Fore.YELLOW, "yellow"),
|
||||
"output": (Fore.LIGHTCYAN_EX, "cyan"),
|
||||
"default": (Fore.RESET, "black"),
|
||||
"info": (Fore.CYAN, "cyan")
|
||||
}
|
||||
fore_color, term_color = color_map[color]
|
||||
spinner = itertools.cycle(['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'])
|
||||
end_time = time.time() + duration
|
||||
|
||||
if color not in color_map:
|
||||
color = "info"
|
||||
|
||||
fore_color, term_color = color_map[color]
|
||||
spinner = itertools.cycle(['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'])
|
||||
end_time = time.time() + duration
|
||||
|
||||
while time.time() < end_time:
|
||||
symbol = next(spinner)
|
||||
if platform.system().lower() != "windows":
|
||||
print(f"\r{fore_color}{symbol} {text}{Fore.RESET}", end="", flush=True)
|
||||
else:
|
||||
print(colored(f"\r{symbol} {text}", term_color), end="", flush=True)
|
||||
time.sleep(0.1)
|
||||
print()
|
||||
while time.time() < end_time:
|
||||
symbol = next(spinner)
|
||||
if platform.system().lower() != "windows":
|
||||
print(f"\r{fore_color}{symbol} {text}{Fore.RESET}", end="", flush=True)
|
||||
else:
|
||||
print(colored(f"\r{symbol} {text}", term_color), end="", flush=True)
|
||||
time.sleep(0.1)
|
||||
print()
|
||||
animation_thread = threading.Thread(target=_animate)
|
||||
animation_thread.daemon = True
|
||||
animation_thread.start()
|
||||
|
||||
def timer_decorator(func):
|
||||
"""
|
||||
|