diff --git a/api.py b/api.py
index cb0e2fa..fa82896 100755
--- a/api.py
+++ b/api.py
@@ -128,6 +128,12 @@ async def is_active():
logger.info("Is active endpoint called")
return {"is_active": interaction.is_active}
+@api.get("/stop")
+async def stop():
+ logger.info("Stop endpoint called")
+ interaction.current_agent.request_stop()
+ return JSONResponse(status_code=200, content={"status": "stopped"})
+
@api.get("/latest_answer")
async def get_latest_answer():
global query_resp_history
diff --git a/frontend/agentic-seek-front/src/App.js b/frontend/agentic-seek-front/src/App.js
index 4293a6a..7f16b9d 100644
--- a/frontend/agentic-seek-front/src/App.js
+++ b/frontend/agentic-seek-front/src/App.js
@@ -122,6 +122,19 @@ function App() {
}));
};
+ const handleStop = async (e) => {
+ e.preventDefault();
+ checkHealth();
+ setIsLoading(false);
+ setError(null);
+ try {
+ const res = await axios.get('http://127.0.0.1:8000/stop');
+ setStatus("Requesting stop...");
+ } catch (err) {
+ console.error('Error stopping the agent:', err);
+ }
+ }
+
const handleSubmit = async (e) => {
e.preventDefault();
checkHealth();
@@ -214,6 +227,9 @@ function App() {
+
diff --git a/sources/.screenshots/updated_screen.png b/sources/.screenshots/updated_screen.png
new file mode 100644
index 0000000..fc6aa5d
Binary files /dev/null and b/sources/.screenshots/updated_screen.png differ
diff --git a/sources/agents/agent.py b/sources/agents/agent.py
index ca4c079..0e6bfaf 100644
--- a/sources/agents/agent.py
+++ b/sources/agents/agent.py
@@ -46,6 +46,7 @@ class Agent():
self.last_answer = ""
self.last_reasoning = ""
self.status_message = "Haven't started yet"
+ self.stop = False
self.verbose = verbose
self.executor = ThreadPoolExecutor(max_workers=1)
@@ -119,6 +120,13 @@ class Agent():
except Exception as e:
raise e
+ def request_stop(self) -> None:
+ """
+ Request the agent to stop.
+ """
+ self.stop = True
+ self.status_message = "Stopped"
+
@abstractmethod
def process(self, prompt, speech_module) -> str:
"""
diff --git a/sources/agents/browser_agent.py b/sources/agents/browser_agent.py
index 20dc7c9..3817fb3 100644
--- a/sources/agents/browser_agent.py
+++ b/sources/agents/browser_agent.py
@@ -181,6 +181,7 @@ class BrowserAgent(Agent):
animate_thinking("Thinking...", color="status")
self.memory.push('user', prompt)
answer, reasoning = await self.llm_request()
+ self.last_reasoning = reasoning
if show_reasoning:
pretty_print(reasoning, color="failure")
pretty_print(answer, color="output")
@@ -349,11 +350,13 @@ class BrowserAgent(Agent):
self.show_search_results(search_result)
prompt = self.make_newsearch_prompt(user_prompt, search_result)
unvisited = [None]
- while not complete and len(unvisited) > 0:
-
+ while not complete and len(unvisited) > 0 and not self.stop:
self.memory.clear()
unvisited = self.select_unvisited(search_result)
answer, reasoning = await self.llm_decide(prompt, show_reasoning = False)
+ if self.stop:
+ pretty_print(f"Requested stop.", color="failure")
+ break
if self.last_answer == answer:
prompt = self.stuck_prompt(user_prompt, unvisited)
continue
@@ -424,4 +427,4 @@ class BrowserAgent(Agent):
return answer, reasoning
if __name__ == "__main__":
- pass
\ No newline at end of file
+ pass
diff --git a/sources/agents/code_agent.py b/sources/agents/code_agent.py
index f7ba3b9..f75baec 100644
--- a/sources/agents/code_agent.py
+++ b/sources/agents/code_agent.py
@@ -51,10 +51,12 @@ class CoderAgent(Agent):
self.memory.push('user', prompt)
clarify_trigger = "REQUEST_CLARIFICATION"
- while attempt < max_attempts:
+ while attempt < max_attempts and not self.stop:
+ print("Stopped?", self.stop)
animate_thinking("Thinking...", color="status")
await self.wait_message(speech_module)
answer, reasoning = await self.llm_request()
+ self.last_reasoning = reasoning
if clarify_trigger in answer:
self.last_answer = answer
await asyncio.sleep(0)
diff --git a/sources/agents/file_agent.py b/sources/agents/file_agent.py
index 8dbd8e8..d88b898 100644
--- a/sources/agents/file_agent.py
+++ b/sources/agents/file_agent.py
@@ -28,10 +28,11 @@ class FileAgent(Agent):
exec_success = False
prompt += f"\nYou must work in directory: {self.work_dir}"
self.memory.push('user', prompt)
- while exec_success is False:
+ while exec_success is False and not self.stop:
await self.wait_message(speech_module)
animate_thinking("Thinking...", color="status")
answer, reasoning = await self.llm_request()
+ self.last_reasoning = reasoning
exec_success, _ = self.execute_modules(answer)
answer = self.remove_blocks(answer)
self.last_answer = answer
diff --git a/sources/agents/planner_agent.py b/sources/agents/planner_agent.py
index 1a6d089..a588604 100644
--- a/sources/agents/planner_agent.py
+++ b/sources/agents/planner_agent.py
@@ -83,11 +83,15 @@ class PlannerAgent(Agent):
self.logger.warning(f"Agent {task['agent']} does not exist.")
pretty_print(f"Agent {task['agent']} does not exist.", color="warning")
return []
- agent = {
- 'agent': task['agent'],
- 'id': task['id'],
- 'task': task['task']
- }
+ try:
+ agent = {
+ 'agent': task['agent'],
+ 'id': task['id'],
+ 'task': task['task']
+ }
+ except:
+ self.logger.warning("Missing field in json plan.")
+ return []
self.logger.info(f"Created agent {task['agent']} with task: {task['task']}")
if 'need' in task:
self.logger.info(f"Agent {task['agent']} was given info:\n {task['need']}")
@@ -156,6 +160,7 @@ class PlannerAgent(Agent):
return []
agents_tasks = self.parse_agent_tasks(answer)
if agents_tasks == []:
+ self.show_plan(agents_tasks, answer)
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")
continue
@@ -178,7 +183,11 @@ class PlannerAgent(Agent):
last_agent_work = agents_work_result[id]
tool_success_str = "success" if success else "failure"
pretty_print(f"Agent {id} work {tool_success_str}.", color="success" if success else "failure")
- if int(id) == len(agents_tasks):
+ try:
+ id_int = int(id)
+ except Exception as e:
+ return agents_tasks
+ if id_int == len(agents_tasks):
next_task = "No task follow, this was the last step. If it failed add a task to recover."
else:
next_task = f"Next task is: {agents_tasks[int(id)][0]}."
@@ -258,7 +267,7 @@ class PlannerAgent(Agent):
return "Failed to parse the tasks.", ""
i = 0
steps = len(agents_tasks)
- while i < steps:
+ while i < steps and not self.stop:
task_name, task = agents_tasks[i][0], agents_tasks[i][1]
self.status_message = "Starting agents..."
pretty_print(f"I will {task_name}.", color="info")
@@ -272,6 +281,8 @@ class PlannerAgent(Agent):
answer, success = await self.start_agent_process(task, required_infos)
except Exception as e:
raise e
+ if self.stop:
+ pretty_print(f"Requested stop.", color="failure")
agents_work_result[task['id']] = answer
agents_tasks = await self.update_plan(goal, agents_tasks, agents_work_result, task['id'], success)
steps = len(agents_tasks)
diff --git a/sources/browser.py b/sources/browser.py
index e33f1df..739097b 100644
--- a/sources/browser.py
+++ b/sources/browser.py
@@ -206,7 +206,11 @@ class Browser:
def setup_tabs(self):
self.tabs = self.driver.window_handles
- self.driver.get("https://www.google.com")
+ try:
+ self.driver.get("https://www.google.com")
+ except Exception as e:
+ self.logger.log(f"Failed to setup initial tab:" + str(e))
+ pass
self.screenshot()
def switch_control_tab(self):
@@ -215,7 +219,11 @@ class Browser:
def load_anticatpcha_manually(self):
pretty_print("You might want to install the AntiCaptcha extension for captchas.", color="warning")
- self.driver.get(self.anticaptcha)
+ try:
+ self.driver.get(self.anticaptcha)
+ except Exception as e:
+ self.logger.log(f"Failed to setup initial tab:" + str(e))
+ pass
def human_move(element):
actions = ActionChains(driver)
@@ -685,15 +693,17 @@ class Browser:
input_elements = self.driver.execute_script(script)
if __name__ == "__main__":
- driver = create_driver(headless=False, stealth_mode=True)
- browser = Browser(driver, anticaptcha_manual_install=False)
+ driver = create_driver(headless=False, stealth_mode=True, crx_path="../crx/nopecha.crx")
+ browser = Browser(driver, anticaptcha_manual_install=True)
input("press enter to continue")
print("AntiCaptcha / Form Test")
+ browser.go_to("https://www.biorxiv.org/content/10.1101/2025.05.19.654955v1")
+ time.sleep(55)
+ browser.go_to("https://www.google.com/recaptcha/api2/demo")
browser.go_to("https://bot.sannysoft.com")
time.sleep(5)
#txt = browser.get_text()
- #browser.go_to("https://www.google.com/recaptcha/api2/demo")
browser.go_to("https://home.openweathermap.org/users/sign_up")
inputs_visible = browser.get_form_inputs()
print("inputs:", inputs_visible)
diff --git a/sources/llm_provider.py b/sources/llm_provider.py
index 263ed07..d12cc72 100644
--- a/sources/llm_provider.py
+++ b/sources/llm_provider.py
@@ -32,11 +32,12 @@ class Provider:
"deepseek": self.deepseek_fn,
"together": self.together_fn,
"dsk_deepseek": self.dsk_deepseek,
- "test": self.test_fn
+ "test": self.test_fn,
+ "anthropic": self.anthropic_fn
}
self.logger = Logger("provider.log")
self.api_key = None
- self.unsafe_providers = ["openai", "deepseek", "dsk_deepseek", "together", "google"]
+ self.unsafe_providers = ["openai", "deepseek", "dsk_deepseek", "together", "google", "anthropic"]
if self.provider_name not in self.available_providers:
raise ValueError(f"Unknown provider: {provider_name}")
if self.provider_name in self.unsafe_providers and self.is_local == False:
@@ -57,6 +58,38 @@ class Provider:
exit(1)
return api_key
+ def anthropic_fn(self, history, verbose=False):
+ """
+ Use Anthropic to generate text.
+ """
+ from anthropic import Anthropic
+
+ client = Anthropic(api_key=self.api_key)
+ system_message = None
+ messages = []
+ for message in history:
+ clean_message = {'role': message['role'], 'content': message['content']}
+ if message['role'] == 'system':
+ system_message = message['content']
+ else:
+ messages.append(clean_message)
+
+ try:
+ response = client.messages.create(
+ model=self.model,
+ max_tokens=1024,
+ messages=messages,
+ system=system_message
+ )
+ if response is None:
+ raise Exception("Anthropic response is empty.")
+ thought = response.content[0].text
+ if verbose:
+ print(thought)
+ return thought
+ except Exception as e:
+ raise Exception(f"Anthropic API error: {str(e)}") from e
+
def respond(self, history, verbose=True):
"""
Use the choosen provider to generate text.
diff --git a/sources/tools/BashInterpreter.py b/sources/tools/BashInterpreter.py
index 9d56584..2793604 100644
--- a/sources/tools/BashInterpreter.py
+++ b/sources/tools/BashInterpreter.py
@@ -8,7 +8,7 @@ if __name__ == "__main__": # if running as a script for individual testing
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from sources.tools.tools import Tools
-from sources.tools.safety import is_unsafe
+from sources.tools.safety import is_any_unsafe
class BashInterpreter(Tools):
"""
@@ -43,9 +43,9 @@ class BashInterpreter(Tools):
for command in commands:
command = f"cd {self.work_dir} && {command}"
command = command.replace('\n', '')
- if self.safe_mode and is_unsafe(commands):
+ if self.safe_mode and is_any_unsafe(commands):
print(f"Unsafe command rejected: {command}")
- return "Unsafe command detected, execution aborted."
+ return "\nUnsafe command detected, execution aborted."
if self.language_bash_attempt(command) and self.allow_language_exec_bash == False:
continue
try:
@@ -100,6 +100,7 @@ class BashInterpreter(Tools):
r"not permitted",
r"not installed",
r"not found",
+ r"aborted",
r"no such",
r"too many",
r"too few",
diff --git a/sources/tools/safety.py b/sources/tools/safety.py
index 5009331..42485e7 100644
--- a/sources/tools/safety.py
+++ b/sources/tools/safety.py
@@ -66,6 +66,15 @@ unsafe_commands_windows = [
"bootcfg"
]
+def is_any_unsafe(cmds):
+ """
+ check if any bash command is unsafe.
+ """
+ for cmd in cmds:
+ if is_unsafe(cmd):
+ return True
+ return False
+
def is_unsafe(cmd):
"""
check if a bash command is unsafe.