Merge pull request #113 from Fosowl/dev

Router support any language + Java interpreter with fixed lang keyerror
This commit is contained in:
Martin 2025-04-11 14:14:11 +02:00 committed by GitHub
commit 49fab1b488
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 234 additions and 39 deletions

View File

@ -14,16 +14,8 @@ English | [中文](./README_CHS.md) | [繁體中文](./README_CHT.md) | [Franç
> 🛠️ **Work in Progress** Looking for contributors!
https://github.com/user-attachments/assets/fe9e8006-0462-4793-8b31-25bd42c6d1eb
*And much more!*
> *Do a deep search of AI startup in Osaka and Tokyo, find at least 5, then save in the research_japan.txt file*
> *Can you make a tetris game in C ?*
@ -185,7 +177,7 @@ Here are some example usage:
> *Show me how much space I have left on my disk*
> *Can you install follow the readme and install project at /home/path/project*
> *Can you follow the readme and install project at /home/path/project*
### Casual
@ -356,6 +348,7 @@ stealth_mode = False
- listen -> listen to voice input (True) or not (False).
- work_dir -> Folder the AI will have access to. eg: /Users/user/Documents/.
- jarvis_personality -> Uses a JARVIS-like personality (True) or not (False). This simply change the prompt file.
- languages -> The list of supported language, needed for the llm router to work properly, avoid putting too many or too similar languages.
- headless_browser -> Runs browser without a visible window (True) or not (False).
- stealth_mode -> Make bot detector time harder. Only downside is you have to manually install the anticaptcha extension.
@ -437,7 +430,7 @@ Yes with Ollama or Server providers, all speech to text, LLM and text to speech
This started as Side-Project we did out of interest about AI agents. Whats special about it is that we want to use local model and avoid APIs.
We draw inspiration from Jarvis and Friday (Iron man movies) to make it "cool" but for functionnality we take more inspiration from Manus, because that's what people want in the first place: a local manus alternative.
Unlike Manus, AgenticSeek prioritizes independence from external systems, giving you more control, privacy and avoid api cost.
Unlike Manus, AgenticSeek prioritizes independence from external systems, giving you more control, privacy and avoid api cost.
## Contribute
@ -445,6 +438,6 @@ Were looking for developers to improve AgenticSeek! Check out open issues or
[![Star History Chart](https://api.star-history.com/svg?repos=Fosowl/agenticSeek&type=Date)](https://www.star-history.com/#Fosowl/agenticSeek&Date)
## Authors:
## Maintainers:
> [Fosowl](https://github.com/Fosowl)
> [steveh8758](https://github.com/steveh8758)

View File

@ -417,6 +417,6 @@ Nous recherchons des développeurs pour améliorer AgenticSeek ! Consultez la se
[![Star History Chart](https://api.star-history.com/svg?repos=Fosowl/agenticSeek&type=Date)](https://www.star-history.com/#Fosowl/agenticSeek&Date)
## Auteurs:
## Auteurs/Mainteneurs:
> [Fosowl](https://github.com/Fosowl) - Epitech 2024, France
> [steveh8758](https://github.com/steveh8758) - Université Feng Chia, Taiwan

View File

@ -10,6 +10,7 @@ speak = False
listen = False
work_dir = /Users/mlg/Documents/ai_folder
jarvis_personality = False
languages = en zh fr
[BROWSER]
headless_browser = False
stealth_mode = False
stealth_mode = True

View File

@ -18,17 +18,19 @@ config.read('config.ini')
def main():
pretty_print("Initializing...", color="status")
stealth_mode = config.getboolean('BROWSER', 'stealth_mode')
personality_folder = "jarvis" if config.getboolean('MAIN', 'jarvis_personality') else "base"
languages = config["MAIN"]["languages"].split(' ')
provider = Provider(provider_name=config["MAIN"]["provider_name"],
model=config["MAIN"]["provider_model"],
server_address=config["MAIN"]["provider_server_address"],
is_local=config.getboolean('MAIN', 'is_local'))
stealth_mode = config.getboolean('BROWSER', 'stealth_mode')
browser = Browser(
create_driver(headless=config.getboolean('BROWSER', 'headless_browser'), stealth_mode=stealth_mode),
anticaptcha_manual_install=stealth_mode
)
personality_folder = "jarvis" if config.getboolean('MAIN', 'jarvis_personality') else "base"
agents = [
CasualAgent(name=config["MAIN"]["agent_name"],
@ -52,6 +54,7 @@ def main():
tts_enabled=config.getboolean('MAIN', 'speak'),
stt_enabled=config.getboolean('MAIN', 'listen'),
recover_last_session=config.getboolean('MAIN', 'recover_last_session'),
langs=languages
)
try:
while interaction.is_active:

View File

@ -76,4 +76,5 @@ Rules:
- Tell agent to execute without question.
- Only use web agent for finding necessary informations.
- If a task might require user email (eg: api services), do not write plan instead ask for user email.
- Do not search for tutorial.
- Do not search for tutorial.
- Make sure json is within ```json tag

View File

@ -166,6 +166,7 @@ class BrowserAgent(Agent):
You previously took these notes:
{notes}
Do not Step-by-Step explanation. Write Notes or Error as a long paragraph followed by your action.
Do not go to tutorials or help pages.
"""
def llm_decide(self, prompt: str, show_reasoning: bool = False) -> Tuple[str, str]:

View File

@ -6,6 +6,7 @@ from sources.tools.C_Interpreter import CInterpreter
from sources.tools.GoInterpreter import GoInterpreter
from sources.tools.PyInterpreter import PyInterpreter
from sources.tools.BashInterpreter import BashInterpreter
from sources.tools.JavaInterpreter import JavaInterpreter
from sources.tools.fileFinder import FileFinder
class CoderAgent(Agent):
@ -19,6 +20,7 @@ class CoderAgent(Agent):
"python": PyInterpreter(),
"c": CInterpreter(),
"go": GoInterpreter(),
"java": JavaInterpreter(),
"file_finder": FileFinder()
}
self.work_dir = self.tools["file_finder"].get_work_dir()

View File

@ -80,9 +80,10 @@ class PlannerAgent(Agent):
"""
return prompt
def show_plan(self, json_plan: dict) -> None:
agents_tasks = self.parse_agent_tasks(json_plan)
def show_plan(self, answer: dict) -> None:
agents_tasks = self.parse_agent_tasks(answer)
if agents_tasks == (None, None):
pretty_print(answer, color="warning")
pretty_print("Failed to make a plan. This can happen with (too) small LLM. Clarify your request and insist on it making a plan.", color="failure")
return
pretty_print("\n▂▘ P L A N ▝▂", color="status")
@ -97,10 +98,6 @@ class PlannerAgent(Agent):
animate_thinking("Thinking...", color="status")
self.memory.push('user', prompt)
answer, _ = self.llm_request()
for line in answer.split('\n'):
if "```json" in line:
break
pretty_print(line, color="output")
self.show_plan(answer)
ok_str = input("Is the plan ok? (y/n): ")
if ok_str == 'y':

View File

@ -15,6 +15,7 @@ class Interaction:
tts_enabled: bool = True,
stt_enabled: bool = True,
recover_last_session: bool = False,
langs: List[str] = ["en", "zh"]
):
self.is_active = True
self.current_agent = None
@ -25,7 +26,7 @@ class Interaction:
self.tts_enabled = tts_enabled
self.stt_enabled = stt_enabled
self.recover_last_session = recover_last_session
self.router = AgentRouter(self.agents)
self.router = AgentRouter(self.agents, supported_language=langs)
if tts_enabled:
animate_thinking("Initializing text-to-speech...", color="status")
self.speech = Speech(enable=tts_enabled)

View File

@ -10,11 +10,17 @@ from sources.logger import Logger
class LanguageUtility:
"""LanguageUtility for language, or emotion identification"""
def __init__(self):
def __init__(self, supported_language: List[str] = ["en", "fr", "zh"]):
"""
Initialize the LanguageUtility class
args:
supported_language: list of languages for translation, determine which Helsinki-NLP model to load
"""
self.sid = None
self.translators_tokenizer = None
self.translators_model = None
self.logger = Logger("language.log")
self.supported_language = supported_language
self.load_model()
def load_model(self) -> None:
@ -24,23 +30,18 @@ class LanguageUtility:
except LookupError:
nltk.download('vader_lexicon')
self.sid = SentimentIntensityAnalyzer()
self.translators_tokenizer = {
"fr": MarianTokenizer.from_pretrained("Helsinki-NLP/opus-mt-fr-en"),
"zh": MarianTokenizer.from_pretrained("Helsinki-NLP/opus-mt-zh-en")
}
self.translators_model = {
"fr": MarianMTModel.from_pretrained("Helsinki-NLP/opus-mt-fr-en"),
"zh": MarianMTModel.from_pretrained("Helsinki-NLP/opus-mt-zh-en")
}
self.translators_tokenizer = {lang: MarianTokenizer.from_pretrained(f"Helsinki-NLP/opus-mt-{lang}-en") for lang in self.supported_language if lang != "en"}
self.translators_model = {lang: MarianMTModel.from_pretrained(f"Helsinki-NLP/opus-mt-{lang}-en") for lang in self.supported_language if lang != "en"}
def detect_language(self, text: str) -> str:
"""
Detect the language of the given text using langdetect
Limited to the supported languages list because of the model tendency to mistake similar languages
Args:
text: string to analyze
Returns: ISO639-1 language code
"""
langid.set_languages(['fr', 'en', 'zh'])
langid.set_languages(self.supported_language)
lang, score = langid.classify(text)
self.logger.info(f"Identified: {text} as {lang} with conf {score}")
return lang

View File

@ -20,10 +20,10 @@ class AgentRouter:
"""
AgentRouter is a class that selects the appropriate agent based on the user query.
"""
def __init__(self, agents: list):
def __init__(self, agents: list, supported_language: List[str] = ["en", "fr", "zh"]):
self.agents = agents
self.logger = Logger("router.log")
self.lang_analysis = LanguageUtility()
self.lang_analysis = LanguageUtility(supported_language=supported_language)
self.pipelines = self.load_pipelines()
self.talk_classifier = self.load_llm_router()
self.complexity_classifier = self.load_llm_router()
@ -100,10 +100,17 @@ class AgentRouter:
("could you check if the presentation.pdf file exists in my downloads?", "LOW"),
("search my drive for a file called vacation_photos_2023.jpg.", "LOW"),
("help me organize my desktop files into folders by type.", "LOW"),
("make a blackjack in golang", "LOW"),
("write a python script to ping a website", "LOW"),
("write a simple Java program to print 'Hello World'", "LOW"),
("write a Java program to calculate the area of a circle", "LOW"),
("write a Python function to sort a list of dictionaries by key", "LOW"),
("can you search for startup in tokyo?", "LOW"),
("find the latest updates on quantum computing on the web", "LOW"),
("check if the folder Work_Projects exists on my desktop", "LOW"),
(" can you browse the web, use overpass-turbo to show fountains in toulouse", "LOW"),
("search the web for the best budget smartphones of 2025", "LOW"),
("write a Python script to download all images from a webpage", "LOW"),
("create a bash script to monitor CPU usage", "LOW"),
("debug this C++ code that keeps crashing", "LOW"),
("can you browse the web to find out who fosowl is ?", "LOW"),
@ -153,8 +160,10 @@ class AgentRouter:
("Find the latest research on renewable energy and build a web app to display it", "HIGH"),
("can you find vitess repo, clone it and install by following the readme", "HIGH"),
("Create a JavaScript game using Phaser.js with multiple levels", "HIGH"),
("Search the web for the latest trends in web development and build a sample site", "HIGH"),
("Use my research_note.txt file, double check the informations on the web", "HIGH"),
("Make a web server in go that query a flight API and display them in a app", "HIGH"),
("Search the web for the latest trends in AI and demo it in pytorch", "HIGH"),
("can you lookup for api that track flight and build a web flight tracking app", "HIGH"),
("Find the file toto.pdf then use its content to reply to Jojo on superforum.com", "HIGH"),
("Create a whole web app in python using the flask framework that query news API", "HIGH"),
@ -421,7 +430,8 @@ class AgentRouter:
Returns:
Agent: The selected agent
"""
if len(self.agents) == 0:
assert len(self.agents) > 0, "No agents available."
if len(self.agents) == 1:
return self.agents[0]
lang = self.lang_analysis.detect_language(text)
text = self.find_first_sentence(text)
@ -440,7 +450,8 @@ class AgentRouter:
raise e
for agent in self.agents:
if best_agent == agent.role["en"]:
pretty_print(f"Selected agent: {agent.agent_name} (roles: {agent.role[lang]})", color="warning")
role_name = agent.role[lang] if lang in agent.role else agent.role["en"]
pretty_print(f"Selected agent: {agent.agent_name} (roles: {role_name})", color="warning")
return agent
pretty_print(f"Error choosing agent.", color="failure")
self.logger.error("No agent selected.")

View File

@ -25,7 +25,7 @@ class BashInterpreter(Tools):
If so, return True, otherwise return False.
Code written by the AI will be executed automatically, so it should not use bash to run it.
"""
lang_interpreter = ["python3", "gcc", "g++", "go", "javac", "rustc", "clang", "clang++", "rustc", "rustc++", "rustc++"]
lang_interpreter = ["python3", "gcc", "g++", "mvn", "go", "javac", "rustc", "clang", "clang++", "rustc", "rustc++", "rustc++"]
for word in command.split():
if word in lang_interpreter:
return True
@ -44,7 +44,7 @@ class BashInterpreter(Tools):
command = command.replace('\n', '')
if self.safe_mode and is_unsafe(commands):
return "Unsafe command detected, execution aborted."
if self.language_bash_attempt(command) and allow_language_exec_bash == False:
if self.language_bash_attempt(command) and self.allow_language_exec_bash == False:
continue
try:
process = subprocess.Popen(

View File

@ -0,0 +1,184 @@
import subprocess
import os
import tempfile
import re
if __name__ == "__main__":
from tools import Tools
else:
from sources.tools.tools import Tools
class JavaInterpreter(Tools):
"""
This class is a tool to allow execution of Java code.
"""
def __init__(self):
super().__init__()
self.tag = "java"
def execute(self, codes: str, safety=False) -> str:
"""
Execute Java code by compiling and running it.
"""
output = ""
code = '\n'.join(codes) if isinstance(codes, list) else codes
if safety and input("Execute code? y/n ") != "y":
return "Code rejected by user."
with tempfile.TemporaryDirectory() as tmpdirname:
source_file = os.path.join(tmpdirname, "Main.java")
class_dir = tmpdirname
with open(source_file, 'w') as f:
f.write(code)
try:
compile_command = ["javac", "-d", class_dir, source_file]
compile_result = subprocess.run(
compile_command,
capture_output=True,
text=True,
timeout=10
)
if compile_result.returncode != 0:
return f"Compilation failed: {compile_result.stderr}"
run_command = ["java", "-cp", class_dir, "Main"]
run_result = subprocess.run(
run_command,
capture_output=True,
text=True,
timeout=10
)
if run_result.returncode != 0:
return f"Execution failed: {run_result.stderr}"
output = run_result.stdout
except subprocess.TimeoutExpired as e:
return f"Execution timed out: {str(e)}"
except FileNotFoundError:
return "Error: 'java' or 'javac' not found. Ensure Java is installed and in PATH."
except Exception as e:
return f"Code execution failed: {str(e)}"
return output
def interpreter_feedback(self, output: str) -> str:
"""
Provide feedback based on the output of the code execution.
"""
if self.execution_failure_check(output):
feedback = f"[failure] Error in execution:\n{output}"
else:
feedback = "[success] Execution success, code output:\n" + output
return feedback
def execution_failure_check(self, feedback: str) -> bool:
"""
Check if the code execution failed.
"""
error_patterns = [
r"error",
r"failed",
r"exception",
r"invalid",
r"syntax",
r"cannot",
r"stack trace",
r"unresolved",
r"not found"
]
combined_pattern = "|".join(error_patterns)
if re.search(combined_pattern, feedback, re.IGNORECASE):
return True
return False
if __name__ == "__main__":
codes = [
"""
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Main extends JPanel {
private double[][] vertices = {
{-1, -1, -1}, {1, -1, -1}, {1, 1, -1}, {-1, 1, -1}, // Back face
{-1, -1, 1}, {1, -1, 1}, {1, 1, 1}, {-1, 1, 1} // Front face
};
private int[][] edges = {
{0, 1}, {1, 2}, {2, 3}, {3, 0}, // Back face
{4, 5}, {5, 6}, {6, 7}, {7, 4}, // Front face
{0, 4}, {1, 5}, {2, 6}, {3, 7} // Connecting edges
};
private double angleX = 0, angleY = 0;
private final double scale = 100;
private final double distance = 5;
public Main() {
Timer timer = new Timer(50, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
angleX += 0.03;
angleY += 0.05;
repaint();
}
});
timer.start();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.setColor(Color.WHITE);
double[][] projected = new double[vertices.length][2];
for (int i = 0; i < vertices.length; i++) {
double x = vertices[i][0];
double y = vertices[i][1];
double z = vertices[i][2];
// Rotate around X-axis
double y1 = y * Math.cos(angleX) - z * Math.sin(angleX);
double z1 = y * Math.sin(angleX) + z * Math.cos(angleX);
// Rotate around Y-axis
double x1 = x * Math.cos(angleY) + z1 * Math.sin(angleY);
double z2 = -x * Math.sin(angleY) + z1 * Math.cos(angleY);
// Perspective projection
double factor = distance / (distance + z2);
double px = x1 * factor * scale;
double py = y1 * factor * scale;
projected[i][0] = px + getWidth() / 2;
projected[i][1] = py + getHeight() / 2;
}
// Draw edges
for (int[] edge : edges) {
int x1 = (int) projected[edge[0]][0];
int y1 = (int) projected[edge[0]][1];
int x2 = (int) projected[edge[1]][0];
int y2 = (int) projected[edge[1]][1];
g2d.drawLine(x1, y1, x2, y2);
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Rotating 3D Cube");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.add(new Main());
frame.setVisible(true);
}
}
"""
]
j = JavaInterpreter()
print(j.execute(codes))