mirror of
https://github.com/tcsenpai/agenticSeek.git
synced 2025-06-03 01:30:11 +00:00
Merge branch 'main' of https://github.com/Fosowl/agenticSeek
This commit is contained in:
commit
f79c3f10da
4
.github/FUNDING.yml
vendored
Normal file
4
.github/FUNDING.yml
vendored
Normal 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]
|
||||||
|
|
10
README.md
10
README.md
@ -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*
|
||||||
|
@ -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
|
||||||
|
@ -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
|
|
||||||
|
@ -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]
|
||||||
|
@ -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)
|
||||||
|
@ -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"):
|
||||||
|
@ -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",
|
||||||
|
@ -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 = """
|
||||||
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
@ -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),
|
||||||
|
@ -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):
|
||||||
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
@ -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):
|
||||||
|
@ -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():
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user