diff --git a/prompts/base/planner_agent.txt b/prompts/base/planner_agent.txt index 92ecd4b..66ed644 100644 --- a/prompts/base/planner_agent.txt +++ b/prompts/base/planner_agent.txt @@ -63,6 +63,7 @@ You: Sure, here is the plan: Rules: - Do not write code. You are a planning agent. +- If you don't know of a concept, use a web agent. - Put your plan in a json with the key "plan". - Give clear, detailled order to each agent and how their task relate to the previous task (if any). - You might use a file agent before code agent to setup a project properly. specify folder name. @@ -75,7 +76,6 @@ Rules: - The file agent can only conduct one action at the time. successive file agent could be needed. - 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. - Make sure json is within ```json tag - One step, one agent. \ No newline at end of file diff --git a/prompts/jarvis/planner_agent.txt b/prompts/jarvis/planner_agent.txt index b32e6d7..0fc1178 100644 --- a/prompts/jarvis/planner_agent.txt +++ b/prompts/jarvis/planner_agent.txt @@ -75,5 +75,4 @@ Rules: - The file agent can only conduct one action at the time. successive file agent could be needed. - 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. \ No newline at end of file diff --git a/sources/agents/agent.py b/sources/agents/agent.py index 0573dc5..3afe3fb 100644 --- a/sources/agents/agent.py +++ b/sources/agents/agent.py @@ -159,6 +159,23 @@ class Agent(): def get_last_tool_type(self) -> str: return self.blocks_result[-1].tool_type if len(self.blocks_result) > 0 else None + + def raw_answer_blocks(self, answer: str) -> str: + """ + Return the answer with all the blocks inserted, as text. + """ + if self.last_answer is None: + return + raw = "" + lines = self.last_answer.split("\n") + for line in lines: + if "block:" in line: + block_idx = int(line.split(":")[1]) + if block_idx < len(self.blocks_result): + raw += self.blocks_result[block_idx].__str__() + else: + raw += line + "\n" + return raw def show_answer(self): """ @@ -175,7 +192,6 @@ class Agent(): self.blocks_result[block_idx].show() else: pretty_print(line, color="output") - self.blocks_result = [] def remove_blocks(self, text: str) -> str: """ diff --git a/sources/agents/planner_agent.py b/sources/agents/planner_agent.py index 47da48d..17abd3b 100644 --- a/sources/agents/planner_agent.py +++ b/sources/agents/planner_agent.py @@ -69,6 +69,9 @@ class PlannerAgent(Agent): line_json = json.loads(block) if 'plan' in line_json: for task in line_json['plan']: + if task['agent'].lower() not in [ag_name.lower() for ag_name in self.agents.keys()]: + pretty_print(f"Agent {task['agent']} does not exist.", color="warning") + return [] agent = { 'agent': task['agent'], 'id': task['id'], @@ -139,7 +142,7 @@ class PlannerAgent(Agent): return [] agents_tasks = self.parse_agent_tasks(answer) if agents_tasks == []: - prompt = f"Failed to parse the tasks. Please make a plan within ```json.\n" + prompt = f"Failed to parse the tasks. Please make a plan within ```json. Do not ask for clarification.\n" pretty_print("Failed to make plan. Retrying...", color="warning") continue self.show_plan(agents_tasks, answer) @@ -160,7 +163,10 @@ 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") - next_task = agents_tasks[int(id)][0] + if int(id) == 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]}." #if success: # return agents_tasks # we only update the plan if last task failed, for now update_prompt = f""" @@ -169,10 +175,11 @@ class PlannerAgent(Agent): The last agent working on task: {id}, did the following work: {last_agent_work} Agent {id} work was a {tool_success_str} according to system interpreter. - The agent {id} about to work on task: {next_task} + {next_task} Is the work done for task {id} leading to sucess or failure ? Did an agent fail with a task? If agent work was good: answer "NO_UPDATE" If agent work is leading to failure: update the plan. + If a task failed add a task to try again or recover from failure. You might have near identical task twice. plan should be within ```json like before. You need to rewrite the whole plan, but only change the tasks after task {id}. Keep the plan as short as the original one if possible. Do not change past tasks. @@ -196,10 +203,14 @@ class PlannerAgent(Agent): self.status_message = f"Starting task {task['task']}..." agent_prompt = self.make_prompt(task['task'], required_infos) pretty_print(f"Agent {task['agent']} started working...", color="status") - agent_answer, _ = await self.agents[task['agent'].lower()].process(agent_prompt, None) + answer, _ = await self.agents[task['agent'].lower()].process(agent_prompt, None) + self.last_answer = answer + agent_answer = self.agents[task['agent'].lower()].raw_answer_blocks(answer) success = self.agents[task['agent'].lower()].get_success self.agents[task['agent'].lower()].show_answer() pretty_print(f"Agent {task['agent']} completed task.", color="status") + # TODO ajouter feedback / agent et code executer + agent_answer += "\nAgent succeeded with task." if success else "\nAgent failed with task (Error detected)." return agent_answer, success def get_work_result_agent(self, task_needs, agents_work_result): @@ -215,6 +226,7 @@ class PlannerAgent(Agent): Tuple[str, str]: The result of the agent process and empty reasoning string. """ agents_tasks = [] + required_infos = None agents_work_result = dict() self.status_message = "Making a plan..." @@ -234,14 +246,12 @@ class PlannerAgent(Agent): if agents_work_result is not None: required_infos = self.get_work_result_agent(task['need'], agents_work_result) try: - self.last_answer, success = await self.start_agent_process(task, required_infos) + answer, success = await self.start_agent_process(task, required_infos) except Exception as e: raise e - agents_work_result[task['id']] = self.last_answer - if i == steps - 1: - break + 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) i += 1 - return self.last_answer, "" \ No newline at end of file + return answer, "" \ No newline at end of file diff --git a/sources/browser.py b/sources/browser.py index e68da01..0351cb9 100644 --- a/sources/browser.py +++ b/sources/browser.py @@ -176,7 +176,6 @@ class Browser: ) except TimeoutException: self.logger.warning("Timeout while waiting for page to bypass 'checking your browser'") - return False self.apply_web_safety() self.logger.log(f"Navigated to: {url}") return True diff --git a/sources/llm_provider.py b/sources/llm_provider.py index eeb371d..a0ecb3e 100644 --- a/sources/llm_provider.py +++ b/sources/llm_provider.py @@ -29,6 +29,7 @@ class Provider: "openai": self.openai_fn, "lm-studio": self.lm_studio_fn, "huggingface": self.huggingface_fn, + "google": self.google_fn, "deepseek": self.deepseek_fn, "together": self.together_fn, "dsk_deepseek": self.dsk_deepseek, @@ -36,10 +37,10 @@ class Provider: } self.logger = Logger("provider.log") self.api_key = None - self.unsafe_providers = ["openai", "deepseek", "dsk_deepseek", "together"] + self.unsafe_providers = ["openai", "deepseek", "dsk_deepseek", "together", "google"] if self.provider_name not in self.available_providers: raise ValueError(f"Unknown provider: {provider_name}") - if self.provider_name in self.unsafe_providers: + if self.provider_name in self.unsafe_providers and self.is_local == False: pretty_print("Warning: you are using an API provider. You data will be sent to the cloud.", color="warning") self.api_key = self.get_api_key(self.provider_name) elif self.provider_name != "ollama": @@ -140,7 +141,6 @@ class Provider: raise e return thought - def ollama_fn(self, history, verbose = False): """ Use local ollama server to generate text. @@ -206,6 +206,29 @@ class Provider: return thought except Exception as e: raise Exception(f"OpenAI API error: {str(e)}") from e + + def google_fn(self, history, verbose=False): + """ + Use google gemini to generate text. + """ + base_url = self.server_ip + if self.is_local: + raise Exception("Google Gemini is not available for local use.") + + client = OpenAI(api_key=self.api_key, base_url="https://generativelanguage.googleapis.com/v1beta/openai/") + try: + response = client.chat.completions.create( + model=self.model, + messages=history, + ) + if response is None: + raise Exception("Google response is empty.") + thought = response.choices[0].message.content + if verbose: + print(thought) + return thought + except Exception as e: + raise Exception(f"GOOGLE API error: {str(e)}") from e def together_fn(self, history, verbose=False): """ diff --git a/sources/tools/mcp_finder.py b/sources/tools/mcpFinder.py similarity index 100% rename from sources/tools/mcp_finder.py rename to sources/tools/mcpFinder.py