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_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
|
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
|
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
|
||||||
|
3
setup.py
@ -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": [
|
||||||
|
@ -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, I’m crunching numbers.",
|
"Hold on, I’m crunching numbers.",
|
||||||
"Working on it sir, please let me think."]
|
"Working on it sir, please let me think."]
|
||||||
|
@ -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__":
|
||||||
|
@ -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
|
||||||
|
@ -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__":
|
||||||
|
@ -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}")
|
||||||
|
@ -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
|
||||||
|
@ -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])
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|