Merge pull request #18 from Fosowl/dev

Fix various issues, requirement not up to date, search not working, file search bug
This commit is contained in:
Martin 2025-03-11 12:50:14 +01:00 committed by GitHub
commit 4ed24669d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 89 additions and 51 deletions

View File

@ -3,9 +3,9 @@ is_local = True
provider_name = ollama provider_name = ollama
provider_model = deepseek-r1:14b provider_model = deepseek-r1:14b
provider_server_address = 127.0.0.1:5000 provider_server_address = 127.0.0.1:5000
agent_name = jarvis agent_name = Friday
recover_last_session = True recover_last_session = True
save_session = False save_session = False
speak = True speak = True
listen = False listen = False
work_dir = None work_dir = none

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

View File

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

View File

@ -13,7 +13,10 @@ flask==3.1.0
soundfile==0.13.1 soundfile==0.13.1
protobuf==3.20.3 protobuf==3.20.3
termcolor==2.5.0 termcolor==2.5.0
ipython==9.0.2
gliclass==0.1.8 gliclass==0.1.8
pyaudio==0.2.14
librosa==0.10.2.post1
# if use chinese # if use chinese
ordered_set ordered_set
pypinyin pypinyin

View File

@ -30,6 +30,9 @@ setup(
"protobuf==3.20.3", "protobuf==3.20.3",
"termcolor==2.5.0", "termcolor==2.5.0",
"gliclass==0.1.8", "gliclass==0.1.8",
"ipython==7.16.1",
"pyaudio-0.2.14",
"librosa==0.10.2.post1"
], ],
extras_require={ extras_require={
"chinese": [ "chinese": [

View File

@ -3,10 +3,13 @@ from typing import Tuple, Callable
from abc import abstractmethod from abc import abstractmethod
import os import os
import random import random
import time
from sources.memory import Memory from sources.memory import Memory
from sources.utility import pretty_print from sources.utility import pretty_print
random.seed(time.time())
class executorResult: class executorResult:
""" """
A class to store the result of a tool execution. A class to store the result of a tool execution.
@ -106,7 +109,6 @@ class Agent():
if speech_module is None: if speech_module is None:
return return
messages = ["Please be patient sir, I am working on it.", 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.", "Computing... I recommand you have a coffee while I work.",
"Hold on, Im crunching numbers.", "Hold on, Im crunching numbers.",
"Working on it sir, please let me think."] "Working on it sir, please let me think."]

View File

@ -18,22 +18,23 @@ class CasualAgent(Agent):
"file_finder": FileFinder(), "file_finder": FileFinder(),
"bash": BashInterpreter() "bash": BashInterpreter()
} }
self.role = "talking, advices and philosophical" self.role = "talking, advices, events and philosophical"
def process(self, prompt, speech_module) -> str: def process(self, prompt, speech_module) -> str:
complete = False complete = False
exec_success = False
self.memory.push('user', prompt) self.memory.push('user', prompt)
self.wait_message(speech_module) self.wait_message(speech_module)
while not complete: while not complete:
if exec_success:
complete = True
animate_thinking("Thinking...", color="status") animate_thinking("Thinking...", color="status")
answer, reasoning = self.llm_request() answer, reasoning = self.llm_request()
exec_success, _ = self.execute_modules(answer) exec_success, _ = self.execute_modules(answer)
answer = self.remove_blocks(answer) answer = self.remove_blocks(answer)
self.last_answer = 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 return answer, reasoning
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -32,6 +32,7 @@ class CoderAgent(Agent):
animate_thinking("Thinking...", color="status") animate_thinking("Thinking...", color="status")
self.wait_message(speech_module) self.wait_message(speech_module)
answer, reasoning = self.llm_request() answer, reasoning = self.llm_request()
animate_thinking("Executing code...", color="status")
exec_success, _ = self.execute_modules(answer) exec_success, _ = self.execute_modules(answer)
answer = self.remove_blocks(answer) answer = self.remove_blocks(answer)
self.last_answer = answer self.last_answer = answer

View File

@ -30,6 +30,10 @@ class FileAgent(Agent):
exec_success, _ = self.execute_modules(answer) exec_success, _ = self.execute_modules(answer)
answer = self.remove_blocks(answer) answer = self.remove_blocks(answer)
self.last_answer = 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 return answer, reasoning
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -81,6 +81,9 @@ class PlannerAgent(Agent):
speech_module.speak(f"I will {task_name}. I assigned the {task['agent']} agent to the task.") speech_module.speak(f"I will {task_name}. I assigned the {task['agent']} agent to the task.")
try: try:
self.agents[task['agent'].lower()].process(agent_prompt, None) 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: except Exception as e:
pretty_print(f"Error: {e}", color="failure") pretty_print(f"Error: {e}", color="failure")
speech_module.speak(f"I encountered an error: {e}") speech_module.speak(f"I encountered an error: {e}")

View File

@ -80,7 +80,7 @@ if __name__ == "__main__":
Hey could you search the web for the latest news on the stock market ? 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 Make a cool game to illustrate the current relation between USA and europe

View File

@ -40,8 +40,7 @@ class Speech():
) )
for i, (gs, ps, audio) in enumerate(generator): for i, (gs, ps, audio) in enumerate(generator):
audio_file = 'sample.wav' audio_file = 'sample.wav'
print(audio_file) display(Audio(data=audio, rate=24000, autoplay=i==0), display_id=False)
display(Audio(data=audio, rate=24000, autoplay=i==0))
sf.write(audio_file, audio, 24000) # save each audio file sf.write(audio_file, audio, 24000) # save each audio file
if platform.system().lower() != "windows": if platform.system().lower() != "windows":
subprocess.call(["afplay", audio_file]) subprocess.call(["afplay", audio_file])

View File

@ -1,5 +1,6 @@
import sys import sys
import os
import re import re
from io import StringIO from io import StringIO
@ -25,10 +26,15 @@ class PyInterpreter(Tools):
return "Code rejected by user." return "Code rejected by user."
stdout_buffer = StringIO() stdout_buffer = StringIO()
sys.stdout = stdout_buffer sys.stdout = stdout_buffer
global_vars = {
'__builtins__': __builtins__,
'os': os,
'sys': sys,
}
code = '\n\n'.join(codes) code = '\n\n'.join(codes)
try: try:
try: try:
buffer = exec(code) buffer = exec(code, global_vars)
if buffer is not None: if buffer is not None:
output = buffer + '\n' output = buffer + '\n'
except Exception as e: except Exception as e:

View File

@ -62,7 +62,6 @@ class FileFinder(Tools):
file_path = None file_path = None
excluded_files = [".pyc", ".o", ".so", ".a", ".lib", ".dll", ".dylib", ".so", ".git"] excluded_files = [".pyc", ".o", ".so", ".a", ".lib", ".dll", ".dylib", ".so", ".git"]
for root, dirs, files in os.walk(directory_path): for root, dirs, files in os.walk(directory_path):
print(f"Root: {root}, Files: {files}")
for file in files: for file in files:
if any(excluded_file in file for excluded_file in excluded_files): if any(excluded_file in file for excluded_file in excluded_files):
continue continue

View File

@ -39,6 +39,7 @@ class Tools():
self.messages = [] self.messages = []
self.config = configparser.ConfigParser() self.config = configparser.ConfigParser()
self.current_dir = self.create_work_dir() self.current_dir = self.create_work_dir()
self.excutable_blocks_found = False
def check_config_dir_validity(self): def check_config_dir_validity(self):
""" """
@ -129,6 +130,14 @@ class Tools():
print(f"Saving code block to: {save_path}") print(f"Saving code block to: {save_path}")
with open(os.path.join(directory, save_path_file), 'w') as f: with open(os.path.join(directory, save_path_file), 'w') as f:
f.write(block) 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]: 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]: if ':' in content.split('\n')[0]:
save_path = content.split('\n')[0].split(':')[1] save_path = content.split('\n')[0].split(':')[1]
content = content[content.find('\n')+1:] content = content[content.find('\n')+1:]
self.excutable_blocks_found = True
code_blocks.append(content) code_blocks.append(content)
start_index = end_pos + len(end_tag) start_index = end_pos + len(end_tag)
return code_blocks, save_path return code_blocks, save_path

View File

@ -6,9 +6,13 @@ import dotenv
dotenv.load_dotenv() dotenv.load_dotenv()
if __name__ == "__main__": 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 from tools import Tools
else: else:
from sources.tools.tools import Tools from sources.tools.tools import Tools
from sources.utility import animate_thinking, pretty_print
class webSearch(Tools): class webSearch(Tools):
def __init__(self, api_key: str = None): def __init__(self, api_key: str = None):
@ -24,6 +28,7 @@ class webSearch(Tools):
return "Error: No SerpApi key provided." return "Error: No SerpApi key provided."
for block in blocks: for block in blocks:
query = block.strip() query = block.strip()
pretty_print(f"Searching for: {query}", color="status")
if not query: if not query:
return "Error: No search query provided." return "Error: No search query provided."
@ -32,19 +37,21 @@ class webSearch(Tools):
params = { params = {
"q": query, "q": query,
"api_key": self.api_key, "api_key": self.api_key,
"num": 1, "num": 100,
"output": "json" "output": "json"
} }
response = requests.get(url, params=params) response = requests.get(url, params=params)
response.raise_for_status() response.raise_for_status()
data = response.json() data = response.json()
results = []
if "organic_results" in data and len(data["organic_results"]) > 0: if "organic_results" in data and len(data["organic_results"]) > 0:
first_result = data["organic_results"][0] for result in data["organic_results"][:50]:
title = first_result.get("title", "No title") title = result.get("title", "No title")
snippet = first_result.get("snippet", "No snippet available") snippet = result.get("snippet", "No snippet available")
link = first_result.get("link", "No link available") link = result.get("link", "No link available")
return f"Title: {title}\nSnippet: {snippet}\nLink: {link}" results.append(f"Title: {title}\nSnippet: {snippet}\nLink: {link}")
return "\n\n".join(results)
else: else:
return "No results found for the query." return "No results found for the query."
except requests.RequestException as e: except requests.RequestException as e:
@ -65,6 +72,6 @@ class webSearch(Tools):
if __name__ == "__main__": if __name__ == "__main__":
search_tool = webSearch(api_key=os.getenv("SERPAPI_KEY")) search_tool = webSearch(api_key=os.getenv("SERPAPI_KEY"))
query = "when did covid start" 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) feedback = search_tool.interpreter_feedback(result)
print(feedback) print(feedback)

View File

@ -2,6 +2,9 @@
from colorama import Fore from colorama import Fore
from termcolor import colored from termcolor import colored
import platform import platform
import threading
import itertools
import time
def pretty_print(text, color = "info"): def pretty_print(text, color = "info"):
@ -49,44 +52,41 @@ def pretty_print(text, color = "info"):
color = "default" color = "default"
print(colored(text, color_map[color])) 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: Args:
text (str): Text to display (default: "thinking...") text (str): Text to display
color (str): Color for the text (matches pretty_print colors) color (str): Color for the text
duration (float): How long to animate in seconds duration (float): How long to animate in seconds
""" """
import time def _animate():
import itertools 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 = { fore_color, term_color = color_map[color]
"success": (Fore.GREEN, "green"), spinner = itertools.cycle(['', '', '', '', '', '', '', '', '', ''])
"failure": (Fore.RED, "red"), end_time = time.time() + duration
"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")
}
if color not in color_map: while time.time() < end_time:
color = "info" symbol = next(spinner)
if platform.system().lower() != "windows":
fore_color, term_color = color_map[color] print(f"\r{fore_color}{symbol} {text}{Fore.RESET}", end="", flush=True)
spinner = itertools.cycle(['', '', '', '', '', '', '', '', '', '']) else:
end_time = time.time() + duration print(colored(f"\r{symbol} {text}", term_color), end="", flush=True)
time.sleep(0.1)
while time.time() < end_time: print()
symbol = next(spinner) animation_thread = threading.Thread(target=_animate)
if platform.system().lower() != "windows": animation_thread.daemon = True
print(f"\r{fore_color}{symbol} {text}{Fore.RESET}", end="", flush=True) animation_thread.start()
else:
print(colored(f"\r{symbol} {text}", term_color), end="", flush=True)
time.sleep(0.1)
print()
def timer_decorator(func): def timer_decorator(func):
""" """