This commit is contained in:
tcsenpai 2025-05-02 23:41:55 +02:00
commit f79c3f10da
21 changed files with 123 additions and 70 deletions

4
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,4 @@
# These are supported funding model platforms
github: [Fosowl ]# Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]

View File

@ -159,9 +159,9 @@ Set the desired provider in the `config.ini`. See below for a list of API provid
```sh ```sh
[MAIN] [MAIN]
is_local = False is_local = False
provider_name = openai provider_name = google
provider_model = gpt-4o provider_model = gemini-2.0-flash
provider_server_address = 127.0.0.1:5000 provider_server_address = 127.0.0.1:5000 # doesn't matter
``` ```
Warning: Make sure there is not trailing space in the config. Warning: Make sure there is not trailing space in the config.
@ -179,6 +179,10 @@ Example: export `TOGETHER_API_KEY="xxxxx"`
| togetherAI | No | Use together AI API (non-private) | | togetherAI | No | Use together AI API (non-private) |
| google | No | Use google gemini API (non-private) | | google | No | Use google gemini API (non-private) |
*We advice against using gpt-4o or other closedAI models*, performance are poor for web browsing and task planning.
Please also note that coding/bash might fail with gemini, it seem to ignore our prompt for format to respect, which are optimized for deepseek r1.
Next step: [Start services and run AgenticSeek](#Start-services-and-Run) Next step: [Start services and run AgenticSeek](#Start-services-and-Run)
*See the **Known issues** section if you are having issues* *See the **Known issues** section if you are having issues*

View File

@ -1,9 +1,9 @@
[MAIN] [MAIN]
is_local = True is_local = True
provider_name = openai provider_name = ollama
provider_model = gpt-4o provider_model = deepseek-r1:14b
provider_server_address = 192.168.1.6:11434/v1 provider_server_address = 127.0.0.1:11434
agent_name = Joey agent_name = Friday
recover_last_session = False recover_last_session = False
save_session = False save_session = False
speak = False speak = False

View File

@ -1,3 +1,5 @@
kokoro==0.9.4
certifi==2025.4.26
fastapi>=0.115.12 fastapi>=0.115.12
flask>=3.1.0 flask>=3.1.0
celery>=5.5.1 celery>=5.5.1
@ -18,10 +20,10 @@ torch>=2.4.1
python-dotenv>=1.0.0 python-dotenv>=1.0.0
ollama>=0.4.7 ollama>=0.4.7
scipy>=1.9.3 scipy>=1.9.3
kokoro>=0.7.12
soundfile>=0.13.1 soundfile>=0.13.1
protobuf>=3.20.3 protobuf>=3.20.3
termcolor>=2.4.0 termcolor>=2.4.0
pypdf>=5.4.0
ipython>=8.13.0 ipython>=8.13.0
pyaudio>=0.2.14 pyaudio>=0.2.14
librosa>=0.10.2.post1 librosa>=0.10.2.post1
@ -39,6 +41,7 @@ fake_useragent>=2.1.0
selenium_stealth>=1.0.6 selenium_stealth>=1.0.6
undetected-chromedriver>=3.5.5 undetected-chromedriver>=3.5.5
sentencepiece>=0.2.0 sentencepiece>=0.2.0
tqdm>4
openai openai
sniffio sniffio
tqdm>4 tqdm>4
@ -46,5 +49,3 @@ python-dotenv>=1.0.0
# if use chinese # if use chinese
ordered_set ordered_set
pypinyin pypinyin
cn2an
jieba

View File

@ -123,6 +123,8 @@ class Agent():
""" """
start_tag = "<think>" start_tag = "<think>"
end_tag = "</think>" end_tag = "</think>"
if text is None:
return None
start_idx = text.find(start_tag) start_idx = text.find(start_tag)
end_idx = text.rfind(end_tag)+8 end_idx = text.rfind(end_tag)+8
return text[start_idx:end_idx] return text[start_idx:end_idx]

View File

@ -151,7 +151,7 @@ class PlannerAgent(Agent):
return [] return []
agents_tasks = self.parse_agent_tasks(answer) agents_tasks = self.parse_agent_tasks(answer)
if agents_tasks == []: if agents_tasks == []:
prompt = f"Failed to parse the tasks. Please make a plan within ```json. Do not ask for clarification.\n" prompt = f"Failed to parse the tasks. Please write down your task followed by a json plan within ```json. Do not ask for clarification.\n"
pretty_print("Failed to make plan. Retrying...", color="warning") pretty_print("Failed to make plan. Retrying...", color="warning")
continue continue
self.show_plan(agents_tasks, answer) self.show_plan(agents_tasks, answer)

View File

@ -13,6 +13,8 @@ from fake_useragent import UserAgent
from selenium_stealth import stealth from selenium_stealth import stealth
import undetected_chromedriver as uc import undetected_chromedriver as uc
import chromedriver_autoinstaller import chromedriver_autoinstaller
import certifi
import ssl
import time import time
import random import random
import os import os
@ -28,6 +30,7 @@ from sources.utility import pretty_print, animate_thinking
from sources.logger import Logger from sources.logger import Logger
def get_chrome_path() -> str: def get_chrome_path() -> str:
"""Get the path to the Chrome executable.""" """Get the path to the Chrome executable."""
if sys.platform.startswith("win"): if sys.platform.startswith("win"):

View File

@ -72,6 +72,8 @@ class Provider:
except ModuleNotFoundError as e: except ModuleNotFoundError as e:
raise ModuleNotFoundError(f"{str(e)}\nA import related to provider {self.provider_name} was not found. Is it installed ?") raise ModuleNotFoundError(f"{str(e)}\nA import related to provider {self.provider_name} was not found. Is it installed ?")
except Exception as e: except Exception as e:
if "try again later" in str(e).lower():
return f"{self.provider_name} server is overloaded. Please try again later."
if "refused" in str(e): if "refused" in str(e):
return f"Server {self.server_ip} seem offline. Unable to answer." return f"Server {self.server_ip} seem offline. Unable to answer."
raise Exception(f"Provider {self.provider_name} failed: {str(e)}") from e raise Exception(f"Provider {self.provider_name} failed: {str(e)}") from e
@ -214,7 +216,7 @@ class Provider:
""" """
base_url = self.server_ip base_url = self.server_ip
if self.is_local: if self.is_local:
raise Exception("Google Gemini is not available for local use.") raise Exception("Google Gemini is not available for local use. Change config.ini")
client = OpenAI(api_key=self.api_key, base_url="https://generativelanguage.googleapis.com/v1beta/openai/") client = OpenAI(api_key=self.api_key, base_url="https://generativelanguage.googleapis.com/v1beta/openai/")
try: try:
@ -237,6 +239,8 @@ class Provider:
""" """
from together import Together from together import Together
client = Together(api_key=self.api_key) client = Together(api_key=self.api_key)
if self.is_local:
raise Exception("Together AI is not available for local use. Change config.ini")
try: try:
response = client.chat.completions.create( response = client.chat.completions.create(
@ -257,6 +261,8 @@ class Provider:
Use deepseek api to generate text. Use deepseek api to generate text.
""" """
client = OpenAI(api_key=self.api_key, base_url="https://api.deepseek.com") client = OpenAI(api_key=self.api_key, base_url="https://api.deepseek.com")
if self.is_local:
raise Exception("Deepseek (API) is not available for local use. Change config.ini")
try: try:
response = client.chat.completions.create( response = client.chat.completions.create(
model="deepseek-chat", model="deepseek-chat",

View File

@ -9,7 +9,10 @@ from kokoro import KPipeline
from IPython.display import display, Audio from IPython.display import display, Audio
import soundfile as sf import soundfile as sf
from sources.utility import pretty_print, animate_thinking if __name__ == "__main__":
from utility import pretty_print, animate_thinking
else:
from sources.utility import pretty_print, animate_thinking
class Speech(): class Speech():
""" """
@ -140,6 +143,7 @@ class Speech():
return sentence return sentence
if __name__ == "__main__": if __name__ == "__main__":
# TODO add info message for cn2an, jieba chinese related import
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
speech = Speech() speech = Speech()
tosay_en = """ tosay_en = """

View File

@ -1,15 +1,14 @@
import sys import os, sys
import re import re
from io import StringIO from io import StringIO
import subprocess import subprocess
if __name__ == "__main__": if __name__ == "__main__": # if running as a script for individual testing
from tools import Tools sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from safety import is_unsafe
else: from sources.tools.tools import Tools
from sources.tools.tools import Tools from sources.tools.safety import is_unsafe
from sources.tools.safety import is_unsafe
class BashInterpreter(Tools): class BashInterpreter(Tools):
""" """

View File

@ -1,12 +1,12 @@
import subprocess import subprocess
import os import os, sys
import tempfile import tempfile
import re import re
if __name__ == "__main__": if __name__ == "__main__": # if running as a script for individual testing
from tools import Tools sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
else:
from sources.tools.tools import Tools from sources.tools.tools import Tools
class CInterpreter(Tools): class CInterpreter(Tools):
""" """

View File

@ -1,12 +1,12 @@
import subprocess import subprocess
import os import os, sys
import tempfile import tempfile
import re import re
if __name__ == "__main__": if __name__ == "__main__": # if running as a script for individual testing
from tools import Tools sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
else:
from sources.tools.tools import Tools from sources.tools.tools import Tools
class GoInterpreter(Tools): class GoInterpreter(Tools):
""" """

View File

@ -1,12 +1,12 @@
import subprocess import subprocess
import os import os, sys
import tempfile import tempfile
import re import re
if __name__ == "__main__": if __name__ == "__main__": # if running as a script for individual testing
from tools import Tools sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
else:
from sources.tools.tools import Tools from sources.tools.tools import Tools
class JavaInterpreter(Tools): class JavaInterpreter(Tools):
""" """

View File

@ -4,10 +4,10 @@ import os
import re import re
from io import StringIO from io import StringIO
if __name__ == "__main__": if __name__ == "__main__": # if running as a script for individual testing
from tools import Tools sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
else:
from sources.tools.tools import Tools from sources.tools.tools import Tools
class PyInterpreter(Tools): class PyInterpreter(Tools):
""" """

View File

@ -1,13 +1,12 @@
import os import os, sys
import stat import stat
import mimetypes import mimetypes
import configparser import configparser
if __name__ == "__main__": if __name__ == "__main__": # if running as a script for individual testing
from tools import Tools sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
else:
from sources.tools.tools import Tools
from sources.tools.tools import Tools
class FileFinder(Tools): class FileFinder(Tools):
""" """
@ -30,14 +29,46 @@ class FileFinder(Tools):
return file.read() return file.read()
except Exception as e: except Exception as e:
return f"Error reading file: {e}" return f"Error reading file: {e}"
def read_arbitrary_file(self, file_path: str, file_type: str) -> str:
"""
Reads the content of a file with arbitrary encoding.
Args:
file_path (str): The path to the file to read
Returns:
str: The content of the file in markdown format
"""
mime_type, _ = mimetypes.guess_type(file_path)
if mime_type:
if mime_type.startswith(('image/', 'video/', 'audio/')):
return "can't read file type: image, video, or audio files are not supported."
content_raw = self.read_file(file_path)
if "text" in file_type:
content = content_raw
elif "pdf" in file_type:
from pypdf import PdfReader
reader = PdfReader(file_path)
content = '\n'.join([pt.extract_text() for pt in reader.pages])
elif "binary" in file_type:
content = content_raw.decode('utf-8', errors='replace')
else:
content = content_raw
return content
def get_file_info(self, file_path: str) -> str: def get_file_info(self, file_path: str) -> str:
"""
Gets information about a file, including its name, path, type, content, and permissions.
Args:
file_path (str): The path to the file
Returns:
str: A dictionary containing the file information
"""
if os.path.exists(file_path): if os.path.exists(file_path):
stats = os.stat(file_path) stats = os.stat(file_path)
permissions = oct(stat.S_IMODE(stats.st_mode)) permissions = oct(stat.S_IMODE(stats.st_mode))
file_type, _ = mimetypes.guess_type(file_path) file_type, _ = mimetypes.guess_type(file_path)
file_type = file_type if file_type else "Unknown" file_type = file_type if file_type else "Unknown"
content = self.read_file(file_path) content = self.read_arbitrary_file(file_path, file_type)
result = { result = {
"filename": os.path.basename(file_path), "filename": os.path.basename(file_path),

View File

@ -1,13 +1,13 @@
import os import os, sys
import requests import requests
import dotenv import dotenv
dotenv.load_dotenv() dotenv.load_dotenv()
if __name__ == "__main__": if __name__ == "__main__": # if running as a script for individual testing
from tools import Tools sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
else:
from sources.tools.tools import Tools from sources.tools.tools import Tools
class FlightSearch(Tools): class FlightSearch(Tools):
def __init__(self, api_key: str = None): def __init__(self, api_key: str = None):

View File

@ -1,12 +1,13 @@
import os import os, sys
import requests import requests
from urllib.parse import urljoin from urllib.parse import urljoin
from typing import Dict, Any, Optional from typing import Dict, Any, Optional
if __name__ == "__main__": from sources.tools.tools import Tools
from tools import Tools
else: if __name__ == "__main__": # if running as a script for individual testing
from sources.tools.tools import Tools sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
class MCP_finder(Tools): class MCP_finder(Tools):
""" """

View File

@ -2,10 +2,10 @@ import requests
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import os import os
if __name__ == "__main__": if __name__ == "__main__": # if running as a script for individual testing
from tools import Tools sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
else:
from sources.tools.tools import Tools from sources.tools.tools import Tools
class searxSearch(Tools): class searxSearch(Tools):
def __init__(self, base_url: str = None): def __init__(self, base_url: str = None):

View File

@ -14,13 +14,17 @@ For example:
print("Hello world") print("Hello world")
``` ```
This is then executed by the tool with its own class implementation of execute(). This is then executed by the tool with its own class implementation of execute().
A tool is not just for code tool but also API, internet, etc.. A tool is not just for code tool but also API, internet search, MCP, etc..
""" """
import sys import sys
import os import os
import configparser import configparser
from abc import abstractmethod from abc import abstractmethod
if __name__ == "__main__": # if running as a script for individual testing
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from sources.logger import Logger from sources.logger import Logger
class Tools(): class Tools():

View File

@ -5,14 +5,8 @@ import dotenv
dotenv.load_dotenv() dotenv.load_dotenv()
if __name__ == "__main__": from sources.tools.tools import Tools
import sys from sources.utility import animate_thinking, pretty_print
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
""" """
WARNING WARNING

View File

@ -17,13 +17,13 @@ class TestBrowserAgentParsing(unittest.TestCase):
# Test various link formats # Test various link formats
test_text = """ test_text = """
Check this out: https://thriveonai.com/15-ai-startups-in-japan-to-take-note-of, and www.google.com! Check this out: https://thriveonai.com/15-ai-startups-in-japan-to-take-note-of, and www.google.com!
Also try https://test.org/about?page=1, hey this one as well bro https://weatherstack.com/documentation. Also try https://test.org/about?page=1, hey this one as well bro https://weatherstack.com/documentation/.
""" """
expected = [ expected = [
"https://thriveonai.com/15-ai-startups-in-japan-to-take-note-of", "https://thriveonai.com/15-ai-startups-in-japan-to-take-note-of",
"www.google.com", "www.google.com",
"https://test.org/about?page=1", "https://test.org/about?page=1",
"https://weatherstack.com/documentation" "https://weatherstack.com/documentation",
] ]
result = self.agent.extract_links(test_text) result = self.agent.extract_links(test_text)
self.assertEqual(result, expected) self.assertEqual(result, expected)