From cd786762899ef5e54391e4cb3d4d170409d9c76a Mon Sep 17 00:00:00 2001 From: Mahdi Karami Date: Sat, 17 Feb 2024 14:55:41 +0330 Subject: [PATCH] feat(agent): add agent group feature * Add `divider_and_conquer` strategy for using in agent members * Add `AgentMember` class * Add `AgentGroup` class * Add `hire_agent` and `create_agent` and `create_task` * Add `test_agent_group` as an intergration test with VCR * Add `AgentTaskStatus` for flow of doing tasks * Add `autogpt_multi_agent.sh` file to run autogpt in multi agent mode --- autogpts/autogpt/autogpt/agents/agent.py | 2 + .../autogpt/autogpt/agents/agent_group.py | 113 +++ .../autogpt/autogpt/agents/agent_member.py | 301 +++++++ .../prompt_strategies/divide_and_conquer.py | 555 ++++++++++++ autogpts/autogpt/autogpt/app/cli.py | 194 ++++ autogpts/autogpt/autogpt/app/main.py | 352 +++++++- .../autogpt/autogpt/commands/create_agent.py | 52 ++ .../autogpt/autogpt/commands/create_task.py | 59 ++ .../autogpt/autogpt/commands/request_agent.py | 55 ++ .../autogpt/autogpt/commands/web_search.py | 120 +-- .../core/resource/model_providers/schema.py | 2 +- autogpts/autogpt/autogpt_multi_agent.sh | 29 + autogpts/autogpt/poetry.lock | 201 ++++- autogpts/autogpt/pyproject.toml | 5 +- .../tests/integration/test_agent_group.py | 64 ++ .../integration/test_agent_group_output1.dat | 24 + .../integration/test_agent_group_output2.dat | 24 + autogpts/forge/forge/sdk/db.py | 4 +- autogpts/forge/forge/sdk/model.py | 1 - .../test_agent_group_happy_scenario.yaml | 834 ++++++++++++++++++ 20 files changed, 2901 insertions(+), 90 deletions(-) create mode 100644 autogpts/autogpt/autogpt/agents/agent_group.py create mode 100644 autogpts/autogpt/autogpt/agents/agent_member.py create mode 100644 autogpts/autogpt/autogpt/agents/prompt_strategies/divide_and_conquer.py create mode 100644 autogpts/autogpt/autogpt/commands/create_agent.py create mode 100644 autogpts/autogpt/autogpt/commands/create_task.py create mode 100644 autogpts/autogpt/autogpt/commands/request_agent.py create mode 100755 autogpts/autogpt/autogpt_multi_agent.sh create mode 100644 autogpts/autogpt/tests/integration/test_agent_group.py create mode 100644 autogpts/autogpt/tests/integration/test_agent_group_output1.dat create mode 100644 autogpts/autogpt/tests/integration/test_agent_group_output2.dat create mode 100644 tests/vcr_cassettes/test_agent_group_happy_scenario/test_agent_group_happy_scenario.yaml diff --git a/autogpts/autogpt/autogpt/agents/agent.py b/autogpts/autogpt/autogpt/agents/agent.py index c1fdf43d155..2798f97738c 100644 --- a/autogpts/autogpt/autogpt/agents/agent.py +++ b/autogpts/autogpt/autogpt/agents/agent.py @@ -5,6 +5,8 @@ import time from datetime import datetime from typing import TYPE_CHECKING, Optional +from autogpt.core.configuration.schema import SystemConfiguration +from autogpt.core.prompting.base import PromptStrategy import sentry_sdk from pydantic import Field diff --git a/autogpts/autogpt/autogpt/agents/agent_group.py b/autogpts/autogpt/autogpt/agents/agent_group.py new file mode 100644 index 00000000000..42633129c33 --- /dev/null +++ b/autogpts/autogpt/autogpt/agents/agent_group.py @@ -0,0 +1,113 @@ +import logging +from typing import Optional +from forge.sdk.model import TaskRequestBody +from autogpt.config.config import ConfigBuilder +from autogpt.agents.agent import Agent, AgentConfiguration +from autogpt.agent_manager.agent_manager import AgentManager +from autogpt.agents.agent_member import AgentMember, AgentMemberSettings +from autogpt.agent_factory.profile_generator import AgentProfileGenerator +from autogpt.core.resource.model_providers.schema import ChatModelProvider + +logger = logging.getLogger(__name__) + +class AgentGroup: + + leader: AgentMember + members: dict[str, AgentMember] + + def assign_group_to_members(self): + self.leader.recursive_assign_group(self) + + def reload_members(self): + members = self.leader.get_list_of_all_your_team_members() + members_dict = {} + for agent_member in members: + members_dict[agent_member.id] = agent_member + self.members = members_dict + + def __init__( + self, + leader: AgentMember + ): + self.leader = leader + self.assign_group_to_members() + self.reload_members() + + async def create_task(self, task: TaskRequestBody): + await self.leader.create_task(task) + +async def create_agent_member( + role: str, + initial_prompt:str, + llm_provider: ChatModelProvider, + boss: Optional['AgentMember'] = None, + recruiter: Optional['AgentMember'] = None, + create_agent: bool = False, +) -> Optional[AgentMember]: + config = ConfigBuilder.build_config_from_env() + config.logging.plain_console_output = True + + config.continuous_mode = False + config.continuous_limit = 20 + config.noninteractive_mode = True + config.memory_backend = "no_memory" + settings = await generate_agent_settings_for_task( + task=initial_prompt, + app_config=config, + llm_provider=llm_provider + ) + + agent_member = AgentMember( + role=role, + initial_prompt=initial_prompt, + settings=settings, + boss=boss, + recruiter=recruiter, + create_agent=create_agent, + llm_provider=llm_provider, + ) + + agent_manager = AgentManager(config.app_data_dir) + agent_member.attach_fs(agent_manager.get_agent_dir(agent_member.id)) + + if boss: + boss.members.append(agent_member) + + return agent_member + +async def generate_agent_settings_for_task( + task: str, + llm_provider: ChatModelProvider, + app_config +) -> AgentMemberSettings: + agent_profile_generator = AgentProfileGenerator( + **AgentProfileGenerator.default_configuration.dict() # HACK + ) + + prompt = agent_profile_generator.build_prompt(task) + output = ( + await llm_provider.create_chat_completion( + prompt.messages, + model_name=app_config.smart_llm, + functions=prompt.functions, + ) + ).response + + ai_profile, ai_directives = agent_profile_generator.parse_response_content(output) + + + return AgentMemberSettings( + name=Agent.default_settings.name, + description=Agent.default_settings.description, + task=task, + ai_profile=ai_profile, + directives=ai_directives, + config=AgentConfiguration( + fast_llm=app_config.fast_llm, + smart_llm=app_config.smart_llm, + allow_fs_access=not app_config.restrict_to_workspace, + use_functions_api=app_config.openai_functions, + plugins=app_config.plugins, + ), + history=Agent.default_settings.history.copy(deep=True), + ) diff --git a/autogpts/autogpt/autogpt/agents/agent_member.py b/autogpts/autogpt/autogpt/agents/agent_member.py new file mode 100644 index 00000000000..b1204601d26 --- /dev/null +++ b/autogpts/autogpt/autogpt/agents/agent_member.py @@ -0,0 +1,301 @@ +import copy +import logging +from enum import Enum +import uuid +from pydantic import Field +from typing import Optional +from datetime import datetime +from autogpt.commands import COMMAND_CATEGORIES +from autogpt.config.config import ConfigBuilder +from forge.sdk.model import Task, TaskRequestBody +from autogpt.agents.base import BaseAgentSettings +from autogpt.agents.agent import Agent, AgentConfiguration +from autogpt.models.command_registry import CommandRegistry +from autogpt.models.action_history import Action, ActionResult +from autogpt.llm.providers.openai import get_openai_command_specs +from autogpt.agents.utils.prompt_scratchpad import PromptScratchpad +from autogpt.core.resource.model_providers.openai import OpenAIProvider +from autogpt.core.prompting.schema import ( + ChatMessage, + ChatPrompt, + CompletionModelFunction, +) +from autogpt.core.resource.model_providers.schema import ( + AssistantChatMessage, + ChatModelProvider, +) +from autogpt.agents.prompt_strategies.divide_and_conquer import ( + Command, + DivideAndConquerAgentPromptConfiguration, + DivideAndConquerAgentPromptStrategy, +) + +logger = logging.getLogger(__name__) + + +class AgentMemberSettings(BaseAgentSettings): + config: AgentConfiguration = Field(default_factory=AgentConfiguration) + prompt_config: DivideAndConquerAgentPromptConfiguration = Field( + default_factory=( + lambda: DivideAndConquerAgentPromptStrategy.default_configuration.copy( + deep=True + ) + ) + ) + + +class ProposeActionResult: + commands: list[Command] + agent: "AgentMember" + + def __init__(self, commands: list[Command], agent: "AgentMember") -> None: + self.commands = commands + self.agent = agent + + +class CommandActionResult: + action_result: ActionResult + command: str + + def __init__(self, action_result: ActionResult, command: str) -> None: + self.action_result = action_result + self.command = command + + +class AgentMember(Agent): + + id: str + role: str + initial_prompt: str + boss: Optional["AgentMember"] + recruiter: Optional["AgentMember"] + tasks: list["AgentTask"] + members: list["AgentMember"] + create_agent: bool + group: "AgentGroup" + + def recursive_assign_group(self, group: "AgentGroup"): + self.group = group + for members in self.members: + members.recursive_assign_group(group) + + def get_list_of_all_your_team_members(self) -> list["AgentMember"]: + members = [] + members.append(self) + for member in self.members: + members.extend(member.get_list_of_all_your_team_members()) + return members + + def __init__( + self, + role: str, + initial_prompt: str, + llm_provider: ChatModelProvider, + settings: AgentMemberSettings, + boss: Optional["AgentMember"] = None, + recruiter: Optional["AgentMember"] = None, + create_agent: bool = False, + ): + config = ConfigBuilder.build_config_from_env() + config.logging.plain_console_output = True + + config.continuous_mode = False + config.continuous_limit = 20 + config.noninteractive_mode = True + config.memory_backend = "no_memory" + + commands = copy.deepcopy(COMMAND_CATEGORIES) + if create_agent: + commands.insert(0, "autogpt.commands.create_agent") + else: + commands.insert(0, "autogpt.commands.request_agent") + commands.insert(0, "autogpt.commands.create_task") + + command_registry = CommandRegistry.with_command_modules(commands, config) + + hugging_chat_settings = OpenAIProvider.default_settings.copy(deep=True) + hugging_chat_settings.credentials = config.openai_credentials + + self.id = str(uuid.uuid4()) + settings.agent_id = self.id + super().__init__(settings, llm_provider, command_registry, config) + + self.role = role + self.initial_prompt = initial_prompt + self.boss = boss + self.recruiter = recruiter + self.tasks = [] + self.members = [] + self.create_agent = create_agent + self.prompt_strategy = DivideAndConquerAgentPromptStrategy( + configuration=settings.prompt_config, + logger=logger, + ) + + def build_prompt( + self, + scratchpad: PromptScratchpad, + tasks: list["AgentTask"], + extra_commands: Optional[list[CompletionModelFunction]] = None, + extra_messages: Optional[list[ChatMessage]] = None, + **extras, + ) -> ChatPrompt: + """Constructs a prompt using `self.prompt_strategy`. + + Params: + scratchpad: An object for plugins to write additional prompt elements to. + (E.g. commands, constraints, best practices) + extra_commands: Additional commands that the agent has access to. + extra_messages: Additional messages to include in the prompt. + """ + if not extra_commands: + extra_commands = [] + if not extra_messages: + extra_messages = [] + + # Apply additions from plugins + for plugin in self.config.plugins: + if not plugin.can_handle_post_prompt(): + continue + plugin.post_prompt(scratchpad) + ai_directives = self.directives.copy(deep=True) + ai_directives.resources += scratchpad.resources + ai_directives.constraints += scratchpad.constraints + ai_directives.best_practices += scratchpad.best_practices + extra_commands += list(scratchpad.commands.values()) + + prompt = self.prompt_strategy.build_prompt( + include_os_info=True, + tasks=tasks, + agent_member=self, + ai_profile=self.ai_profile, + ai_directives=ai_directives, + commands=get_openai_command_specs( + self.command_registry.list_available_commands(self) + ) + + extra_commands, + event_history=self.event_history, + max_prompt_tokens=self.send_token_limit, + count_tokens=lambda x: self.llm_provider.count_tokens(x, self.llm.name), + count_message_tokens=lambda x: self.llm_provider.count_message_tokens( + x, self.llm.name + ), + extra_messages=extra_messages, + **extras, + ) + + return prompt + + async def execute_commands( + self, commands: list["Command"] + ) -> list[CommandActionResult]: + # self.log_cycle_handler.log_cycle( + # self.ai_profile.ai_name, + # self.created_at, + # self.config.cycle_count, + # assistant_reply_dict, + # NEXT_ACTION_FILE_NAME, + # ) + results = [] + + for command in commands: + self.event_history.register_action( + Action( + name=command.command, + args=command.args, + reasoning="", + ) + ) + result = await self.execute( + command_name=command.command, + command_args=command.args, + ) + results.append( + CommandActionResult(action_result=result, command=command.command) + ) + + return results + + async def create_task(self, task_request: TaskRequestBody): + try: + task = AgentTask( + input=task_request.input, + additional_input=task_request.additional_input, + status=AgentTaskStatus.INITIAL.value, + created_at=datetime.now(), + modified_at=datetime.now(), + task_id=str(uuid.uuid4()), + sub_tasks=[], + ) + + self.tasks.append(task) + except Exception as e: + logger.error(f"Error occurred while creating task: {e}") + + async def recursive_propose_action(self) -> list[ProposeActionResult]: + result = [ + ProposeActionResult(agent=self, commands=await self.single_propose_action()) + ] + for agent_member in self.members: + result = result + (await agent_member.recursive_propose_action()) + return result + + async def single_propose_action(self) -> list[Command]: + current_tasks = [] + for task in self.tasks: + if task.status == AgentTaskStatus.REJECTED: + task.status = AgentTaskStatus.INITIAL + + elif task.status == AgentTaskStatus.DOING: + # sub_tasks_done = all(sub_task.status == AgentTaskStatus.DONE for sub_task in task.sub_tasks) + # # sub_tasks_checking = any(sub_task.status == AgentTaskStatus.CHECKING for sub_task in task.sub_tasks) + + # if sub_tasks_done: + # task.status = AgentTaskStatus.CHECKING + # elif sub_tasks_checking: + current_tasks.append(task) + + elif task.status == AgentTaskStatus.INITIAL: + current_tasks.append(task) + task.status = AgentTaskStatus.DOING + + if current_tasks: + self._prompt_scratchpad = PromptScratchpad() + logger.info(f"tasks: {str(current_tasks)}") + prompt = self.build_prompt( + scratchpad=self._prompt_scratchpad, tasks=current_tasks + ) + result = await self.llm_provider.create_chat_completion( + prompt.messages, + model_name=self.config.smart_llm, + functions=prompt.functions, + completion_parser=lambda r: self.parse_and_process_response( + r, + prompt, + scratchpad=self._prompt_scratchpad, + ), + ) + commands: list[Command] = result.parsed_result + return commands + return [] + + def parse_and_process_response( + self, llm_response: AssistantChatMessage, *args, **kwargs + ) -> list[Command]: + result = self.prompt_strategy.parse_response_content(llm_response) + return result + + +class AgentTaskStatus(Enum): + INITIAL = "INITIAL" + DOING = "DOING" + CHECKING = "CHECKING" + REJECTED = "REJECTED" + DONE = "DONE" + + +class AgentTask(Task): + father_task_id: Optional[str] + status: AgentTaskStatus + father_task: Optional["AgentTask"] + sub_tasks: list["AgentTask"] diff --git a/autogpts/autogpt/autogpt/agents/prompt_strategies/divide_and_conquer.py b/autogpts/autogpt/autogpt/agents/prompt_strategies/divide_and_conquer.py new file mode 100644 index 00000000000..65c16792817 --- /dev/null +++ b/autogpts/autogpt/autogpt/agents/prompt_strategies/divide_and_conquer.py @@ -0,0 +1,555 @@ +from __future__ import annotations + +import json +import platform +import re +from logging import Logger +from typing import TYPE_CHECKING, Callable, Optional + +import distro + +if TYPE_CHECKING: + from autogpt.models.action_history import Episode + +from autogpt.agents.utils.exceptions import InvalidAgentResponseError +from autogpt.config import AIDirectives, AIProfile +from autogpt.core.configuration.schema import SystemConfiguration, UserConfigurable +from autogpt.core.prompting import ( + ChatPrompt, + LanguageModelClassification, + PromptStrategy, +) +from autogpt.core.resource.model_providers.schema import ( + AssistantChatMessage, + ChatMessage, + CompletionModelFunction, +) +from autogpt.core.utils.json_schema import JSONSchema +from autogpt.json_utils.utilities import extract_dict_from_response +from autogpt.prompts.utils import format_numbered_list, indent + + +class Command: + command: str + args: dict[str, str] + + def __init__(self, command: str, args: dict[str, str]) -> None: + self.command = command + self.args = args + + +class DivideAndConquerAgentPromptConfiguration(SystemConfiguration): + DEFAULT_BODY_TEMPLATE: str = ( + "## Constraints\n" + "You operate within the following constraints:\n" + "{constraints}\n" + "\n" + "## Resources\n" + "You can leverage access to the following resources:\n" + "{resources}\n" + "\n" + "## Commands\n" + "These are the ONLY commands you can use." + " Any action you perform must be possible through list of these commands:\n" + "{commands}\n" + "\n" + "## Best practices\n" + "{best_practices}" + ) + + DEFAULT_CHOOSE_ACTION_INSTRUCTION: str = ( + "Determine list of commands to use next based on the given goals " + "and the progress you have made so far, " + "and respond using the JSON schema specified previously:" + ) + + DEFAULT_RESPONSE_SCHEMA = JSONSchema( + type=JSONSchema.Type.OBJECT, + properties={ + "thoughts": JSONSchema( + type=JSONSchema.Type.OBJECT, + required=True, + properties={ + "observations": JSONSchema( + description=( + "Relevant observations from your last action (if any)" + ), + type=JSONSchema.Type.STRING, + required=False, + ), + "text": JSONSchema( + description="Thoughts", + type=JSONSchema.Type.STRING, + required=True, + ), + "reasoning": JSONSchema( + type=JSONSchema.Type.STRING, + required=True, + ), + "self_criticism": JSONSchema( + description="Constructive self-criticism", + type=JSONSchema.Type.STRING, + required=True, + ), + "plan": JSONSchema( + description=( + "Short markdown-style bullet list that conveys the " + "long-term plan" + ), + type=JSONSchema.Type.STRING, + required=True, + ), + "speak": JSONSchema( + description="Summary of thoughts, to say to user", + type=JSONSchema.Type.STRING, + required=True, + ), + }, + ), + "commands": JSONSchema( + description=( + "give 1 to 5 commands based on tasks with DOING status to make progress in tasks and show task_id of that task. " + "These commands should be isolated from each other without any mention to each other" + # "1 to 5 commands that make tasks progressive. These commands for tasks whose status is DOING. The structure should be like this " + # "list[dict[name:str, args:dict[str,str], task_id: str ]] for completing a list of tasks. " + # "Each command should be assigned to a task whose status is DOING by task_id field. " + # "You need to choose some commands to progress tasks. your list shouldn't be empty. " + # "args field to should be a dictionary of keys and values. It's so important to break " + # "tasks and hire or create agents as much as you can by create_task or create_agent or request_agent. " + # "commands arguments should not be abstract and not clear. Commands part should not be empty " + # "and in each step you should give some comamnds to make tasks prggresive" + ), + type=JSONSchema.Type.ARRAY, + required=True, + items=JSONSchema( + type=JSONSchema.Type.OBJECT, + properties={ + "name": JSONSchema( + type=JSONSchema.Type.STRING, + required=True, + ), + "args": JSONSchema( + type=JSONSchema.Type.OBJECT, + required=True, + ), + }, + ), + ), + "tasks": JSONSchema( + description=( + "check all tasks that I sent you (not sub_tasks part) with CHECKING status. In this part you should say status of tasks " + "that I sent(not sub_tasks parts). send them with these statuses: REJECTED, DONE with this structure list[dict[task_id: str, status: str, reason: str?]] " + "(REJECTED: This status shows that the task need to be imporved by the owner of task and agent member needs to work on it more. need to say reject reason in reason field" + "DONE: This status shows that the result of task is OK and the task is done)" + ), + type=JSONSchema.Type.ARRAY, + required=True, + items=JSONSchema( + type=JSONSchema.Type.OBJECT, + properties={ + "task_id": JSONSchema( + type=JSONSchema.Type.STRING, + required=True, + ), + "status": JSONSchema( + type=JSONSchema.Type.STRING, + required=True, + ), + "reason": JSONSchema( + type=JSONSchema.Type.STRING, + required=False, + ), + }, + ), + ), + }, + ) + + body_template: str = UserConfigurable(default=DEFAULT_BODY_TEMPLATE) + response_schema: dict = UserConfigurable( + default_factory=DEFAULT_RESPONSE_SCHEMA.to_dict + ) + choose_action_instruction: str = UserConfigurable( + default=DEFAULT_CHOOSE_ACTION_INSTRUCTION + ) + use_functions_api: bool = UserConfigurable(default=False) + + ######### + # State # + ######### + # progress_summaries: dict[tuple[int, int], str] = Field( + # default_factory=lambda: {(0, 0): ""} + # ) + + +class DivideAndConquerAgentPromptStrategy(PromptStrategy): + default_configuration: DivideAndConquerAgentPromptConfiguration = ( + DivideAndConquerAgentPromptConfiguration() + ) + + def __init__( + self, + configuration: DivideAndConquerAgentPromptConfiguration, + logger: Logger, + ): + self.config = configuration + self.response_schema = JSONSchema.from_dict(configuration.response_schema) + self.logger = logger + + @property + def model_classification(self) -> LanguageModelClassification: + return LanguageModelClassification.FAST_MODEL # FIXME: dynamic switching + + def build_prompt( + self, + *, + tasks: list["AgentTask"], + agent_member: "AgentMember", + ai_profile: AIProfile, + ai_directives: AIDirectives, + commands: list[CompletionModelFunction], + event_history: list[Episode], + include_os_info: bool, + max_prompt_tokens: int, + count_tokens: Callable[[str], int], + count_message_tokens: Callable[[ChatMessage | list[ChatMessage]], int], + extra_messages: Optional[list[ChatMessage]] = None, + **extras, + ) -> ChatPrompt: + """Constructs and returns a prompt with the following structure: + 1. System prompt + 2. Message history of the agent, truncated & prepended with running summary + as needed + 3. `cycle_instruction` + """ + if not extra_messages: + extra_messages = [] + + system_prompt = self.build_system_prompt( + ai_profile=ai_profile, + ai_directives=ai_directives, + commands=commands, + include_os_info=include_os_info, + agent=agent_member, + ) + system_prompt_tlength = count_message_tokens(ChatMessage.system(system_prompt)) + + tasks_list = [] + for task in tasks: + task_dict = { + "task_id": task.task_id, + "task_detail": task.input, + "status": task.status.value, + "sub_tasks": task.sub_tasks, + } + tasks_list.append(task_dict) + tasks_prompt = json.dumps(tasks_list, ensure_ascii=False, indent=4) + + user_task = f'"""{tasks_prompt}"""' + user_task_tlength = count_message_tokens(ChatMessage.user(user_task)) + + response_format_instr = self.response_format_instruction( + self.config.use_functions_api + ) + extra_messages.append(ChatMessage.system(response_format_instr)) + + final_instruction_msg = ChatMessage.user(self.config.choose_action_instruction) + final_instruction_tlength = count_message_tokens(final_instruction_msg) + + if event_history: + progress = self.compile_progress( + event_history, + count_tokens=count_tokens, + max_tokens=( + max_prompt_tokens + - system_prompt_tlength + - user_task_tlength + - final_instruction_tlength + - count_message_tokens(extra_messages) + ), + ) + extra_messages.insert( + 0, + ChatMessage.system(f"## Progress\n\n{progress}"), + ) + + prompt = ChatPrompt( + messages=[ + ChatMessage.system(system_prompt), + ChatMessage.user(user_task), + *extra_messages, + final_instruction_msg, + ], + ) + + return prompt + + def _generate_members_detail(self, agent: "AgentMember") -> list[str]: + result = "## Members detail:\n" + if agent.members: + for emp in agent.members: + result += ( + "{agent_id: " + + emp.id + + ", description: " + + emp.ai_profile.ai_role.rstrip(".") + + "}\n" + ) + else: + result += "you don't have any member to assign task" + return [result] + + def build_system_prompt( + self, + ai_profile: AIProfile, + ai_directives: AIDirectives, + commands: list[CompletionModelFunction], + include_os_info: bool, + agent: "AgentMember", + ) -> str: + system_prompt_parts = ( + self._generate_intro_prompt(agent.id, ai_profile) + + (self._generate_os_info() if include_os_info else []) + + self._generate_members_detail(agent) + + [ + self.config.body_template.format( + constraints=format_numbered_list( + ai_directives.constraints + + self._generate_budget_constraint(ai_profile.api_budget) + ), + resources=format_numbered_list(ai_directives.resources), + commands=self._generate_commands_list(commands), + best_practices=format_numbered_list(ai_directives.best_practices), + ) + ] + + [ + "## tasks\n" + "The user will specify tasks for you to execute," + "in triple quotes, in the next message. " + "Your job is to use the command list to do things " + "to make progress in tasks. complete the task while " + "following your directives as given above, and terminate when your task is done. " + "It's good practice to hire or create other agents to break tasks and make them better." + ] + ) + + # Join non-empty parts together into paragraph format + return "\n\n".join(filter(None, system_prompt_parts)).strip("\n") + + def compile_progress( + self, + episode_history: list[Episode], + max_tokens: Optional[int] = None, + count_tokens: Optional[Callable[[str], int]] = None, + ) -> str: + if max_tokens and not count_tokens: + raise ValueError("count_tokens is required if max_tokens is set") + + steps: list[str] = [] + tokens: int = 0 + n_episodes = len(episode_history) + + for i, episode in enumerate(reversed(episode_history)): + # Use full format for the latest 4 steps, summary or format for older steps + if i < 4 or episode.summary is None: + step_content = indent(episode.format(), 2).strip() + else: + step_content = episode.summary + + step = f"* Step {n_episodes - i}: {step_content}" + + if max_tokens and count_tokens: + step_tokens = count_tokens(step) + if tokens + step_tokens > max_tokens: + break + tokens += step_tokens + + steps.insert(0, step) + + return "\n\n".join(steps) + + def response_format_instruction(self, use_functions_api: bool) -> str: + response_schema = self.response_schema.copy(deep=True) + if ( + use_functions_api + and response_schema.properties + and "commands" in response_schema.properties + ): + del response_schema.properties["commands"] + + # Unindent for performance + response_format = re.sub( + r"\n\s+", + "\n", + response_schema.to_typescript_object_interface("Response"), + ) + + instruction = ( + "Respond with pure JSON containing your thoughts, " "and invoke a tool." + if use_functions_api + else "Respond with pure JSON." + ) + + return ( + f"{instruction} " + "The JSON object should be compatible with the TypeScript type `Response` " + f"from the following:\n{response_format}" + ) + + def _generate_intro_prompt(self, agent_id: str, ai_profile: AIProfile) -> list[str]: + """Generates the introduction part of the prompt. + + Returns: + list[str]: A list of strings forming the introduction part of the prompt. + """ + return [ + f"You are {ai_profile.ai_name}, {ai_profile.ai_role.rstrip('.')} with agent_id {agent_id}.", + "Your decisions must always be made independently without seeking " + "user assistance. Play to your strengths as an LLM and pursue " + "simple strategies with no legal complications.", + ] + + def _generate_os_info(self) -> list[str]: + """Generates the OS information part of the prompt. + + Params: + config (Config): The configuration object. + + Returns: + str: The OS information part of the prompt. + """ + os_name = platform.system() + os_info = ( + platform.platform(terse=True) + if os_name != "Linux" + else distro.name(pretty=True) + ) + return [f"The OS you are running on is: {os_info}"] + + def _generate_budget_constraint(self, api_budget: float) -> list[str]: + """Generates the budget information part of the prompt. + + Returns: + list[str]: The budget information part of the prompt, or an empty list. + """ + if api_budget > 0.0: + return [ + f"It takes money to let you run. " + f"Your API budget is ${api_budget:.3f}" + ] + return [] + + def _generate_commands_list(self, commands: list[CompletionModelFunction]) -> str: + """Lists the commands available to the agent. + + Params: + agent: The agent for which the commands are being listed. + + Returns: + str: A string containing a numbered list of commands. + """ + try: + return format_numbered_list([cmd.fmt_line() for cmd in commands]) + except AttributeError: + self.logger.warning(f"Formatting commands failed. {commands}") + raise + + def parse_response_content( + self, + response: AssistantChatMessage, + ) -> list[Command]: + if not response.content: + raise InvalidAgentResponseError("Assistant response has no text content") + + self.logger.debug( + "LLM response content:" + + ( + f"\n{response.content}" + if "\n" in response.content + else f" '{response.content}'" + ) + ) + assistant_reply_dict = extract_dict_from_response(response.content) + self.logger.debug( + "Validating object extracted from LLM response:\n" + f"{json.dumps(assistant_reply_dict, indent=4)}" + ) + + _, errors = self.response_schema.validate_object( + object=assistant_reply_dict, + logger=self.logger, + ) + if errors: + raise InvalidAgentResponseError( + "Validation of response failed:\n " + + ";\n ".join([str(e) for e in errors]) + ) + + # Get commands name and arguments + commands = extract_command( + assistant_reply_dict, response, self.config.use_functions_api + ) + return commands + + +############# +# Utilities # +############# + + +def extract_command( + assistant_reply_json: dict, + assistant_reply: AssistantChatMessage, + use_openai_functions_api: bool, +) -> list[Command]: + """Parse the response and return the commands name and arguments + + Args: + assistant_reply_json (dict): The response object from the AI + assistant_reply (AssistantChatMessage): The model response from the AI + config (Config): The config object + + Returns: + list: The commands name and arguments + + Raises: + json.decoder.JSONDecodeError: If the response is not valid JSON + + Exception: If any other error occurs + """ + if use_openai_functions_api: + if not assistant_reply.tool_calls: + raise InvalidAgentResponseError("No 'tool_calls' in assistant reply") + assistant_reply_json["commands"] = [] + for tool_call in assistant_reply.tool_calls: + command = { + "name": tool_call.function.name, + "args": json.loads(tool_call.function.arguments), + } + assistant_reply_json["commands"].append(command) + try: + if not isinstance(assistant_reply_json, dict): + raise InvalidAgentResponseError( + f"The previous message sent was not a dictionary {assistant_reply_json}" + ) + + if "commands" not in assistant_reply_json: + raise InvalidAgentResponseError("Missing 'commands' object in JSON") + + commands = assistant_reply_json["commands"] + if not isinstance(commands, list): + raise InvalidAgentResponseError("'commands' object is not a list") + + commands_list = [] + for command_data in commands: + command_obj = Command( + command=command_data["name"], args=command_data.get("args", {}) + ) + commands_list.append(command_obj) + return commands_list + + except json.decoder.JSONDecodeError: + raise InvalidAgentResponseError("Invalid JSON") + + except Exception as e: + raise InvalidAgentResponseError(str(e)) diff --git a/autogpts/autogpt/autogpt/app/cli.py b/autogpts/autogpt/autogpt/app/cli.py index e6ca0a78387..09583744025 100644 --- a/autogpts/autogpt/autogpt/app/cli.py +++ b/autogpts/autogpt/autogpt/app/cli.py @@ -282,6 +282,200 @@ def serve( install_plugin_deps=install_plugin_deps, ) +@cli.command() +@click.option("-c", "--continuous", is_flag=True, help="Enable Continuous Mode") +@click.option( + "-l", + "--continuous-limit", + type=int, + help="Defines the number of times to run in continuous mode", +) +@click.option("--speak", is_flag=True, help="Enable Speak Mode") +@click.option("--gpt3only", is_flag=True, help="Enable GPT3.5 Only Mode") +@click.option("--gpt4only", is_flag=True, help="Enable GPT4 Only Mode") +@click.option( + "-b", + "--browser-name", + help="Specifies which web-browser to use when using selenium to scrape the web.", +) +@click.option( + "--allow-downloads", + is_flag=True, + help="Dangerous: Allows AutoGPT to download files natively.", +) +@click.option( + # TODO: this is a hidden option for now, necessary for integration testing. + # We should make this public once we're ready to roll out agent specific workspaces. + "--workspace-directory", + "-w", + type=click.Path(file_okay=False), + hidden=True, +) +@click.option( + "--install-plugin-deps", + is_flag=True, + help="Installs external dependencies for 3rd party plugins.", +) +@click.option( + "--skip-news", + is_flag=True, + help="Specifies whether to suppress the output of latest news on startup.", +) +@click.option( + "--skip-reprompt", + "-y", + is_flag=True, + help="Skips the re-prompting messages at the beginning of the script", +) +@click.option( + "--ai-settings", + "-C", + type=click.Path(exists=True, dir_okay=False, path_type=Path), + help=( + "Specifies which ai_settings.yaml file to use, relative to the AutoGPT" + " root directory. Will also automatically skip the re-prompt." + ), +) +@click.option( + "--ai-name", + type=str, + help="AI name override", +) +@click.option( + "--ai-role", + type=str, + help="AI role override", +) +@click.option( + "--prompt-settings", + "-P", + type=click.Path(exists=True, dir_okay=False, path_type=Path), + help="Specifies which prompt_settings.yaml file to use.", +) +@click.option( + "--constraint", + type=str, + multiple=True, + help=( + "Add or override AI constraints to include in the prompt;" + " may be used multiple times to pass multiple constraints" + ), +) +@click.option( + "--resource", + type=str, + multiple=True, + help=( + "Add or override AI resources to include in the prompt;" + " may be used multiple times to pass multiple resources" + ), +) +@click.option( + "--best-practice", + type=str, + multiple=True, + help=( + "Add or override AI best practices to include in the prompt;" + " may be used multiple times to pass multiple best practices" + ), +) +@click.option( + "--member-description", + type=str, + multiple=True, + help=( + "Add members description and detail" + ), +) +@click.option( + "--override-directives", + is_flag=True, + help=( + "If specified, --constraint, --resource and --best-practice will override" + " the AI's directives instead of being appended to them" + ), +) +@click.option( + "--debug", is_flag=True, help="Implies --log-level=DEBUG --log-format=debug" +) +@click.option("--log-level", type=click.Choice([*logLevelMap.keys()])) +@click.option( + "--log-format", + help=( + "Choose a log format; defaults to 'simple'." + " Also implies --log-file-format, unless it is specified explicitly." + " Using the 'structured_google_cloud' format disables log file output." + ), + type=click.Choice([i.value for i in LogFormatName]), +) +@click.option( + "--log-file-format", + help=( + "Override the format used for the log file output." + " Defaults to the application's global --log-format." + ), + type=click.Choice([i.value for i in LogFormatName]), +) +def run_multi_agent( + continuous: bool, + continuous_limit: Optional[int], + speak: bool, + gpt3only: bool, + gpt4only: bool, + browser_name: Optional[str], + allow_downloads: bool, + workspace_directory: Optional[Path], + install_plugin_deps: bool, + skip_news: bool, + skip_reprompt: bool, + ai_settings: Optional[Path], + ai_name: Optional[str], + ai_role: Optional[str], + prompt_settings: Optional[Path], + resource: tuple[str], + constraint: tuple[str], + best_practice: tuple[str], + override_directives: bool, + debug: bool, + log_level: Optional[str], + log_format: Optional[str], + log_file_format: Optional[str], + member_description: tuple[str], +) -> None: + """ + Sets up and runs an agent, based on the task specified by the user, or resumes an + existing agent. + """ + # Put imports inside function to avoid importing everything when starting the CLI + from autogpt.app.main import run_multi_agents_auto_gpt + + run_multi_agents_auto_gpt( + continuous=continuous, + continuous_limit=continuous_limit, + ai_settings=ai_settings, + prompt_settings=prompt_settings, + skip_reprompt=skip_reprompt, + speak=speak, + debug=debug, + log_level=log_level, + log_format=log_format, + log_file_format=log_file_format, + gpt3only=gpt3only, + gpt4only=gpt4only, + browser_name=browser_name, + allow_downloads=allow_downloads, + skip_news=skip_news, + workspace_directory=workspace_directory, + install_plugin_deps=install_plugin_deps, + override_ai_name=ai_name, + override_ai_role=ai_role, + resources=list(resource), + constraints=list(constraint), + best_practices=list(best_practice), + override_directives=override_directives, + member_descriptions=list(member_description), + ) + if __name__ == "__main__": cli() diff --git a/autogpts/autogpt/autogpt/app/main.py b/autogpts/autogpt/autogpt/app/main.py index 59ea781a540..6f2ea64977d 100644 --- a/autogpts/autogpt/autogpt/app/main.py +++ b/autogpts/autogpt/autogpt/app/main.py @@ -16,6 +16,9 @@ from colorama import Fore, Style from forge.sdk.db import AgentDB +from forge.sdk.model import TaskRequestBody +from autogpt.agents.agent_group import AgentGroup, create_agent_member +from autogpt.agents.agent_member import ProposeActionResult if TYPE_CHECKING: from autogpt.agents.agent import Agent @@ -418,7 +421,6 @@ async def run_auto_gpt_server( f"${round(sum(b.total_cost for b in server._task_budgets.values()), 2)}" ) - def _configure_openai_provider(config: Config) -> OpenAIProvider: """Create a configured OpenAIProvider object. @@ -799,3 +801,351 @@ def print_assistant_thoughts( def remove_ansi_escape(s: str) -> str: return s.replace("\x1B", "") + + +@coroutine +async def run_multi_agents_auto_gpt( + continuous: bool = False, + continuous_limit: Optional[int] = None, + ai_settings: Optional[Path] = None, + prompt_settings: Optional[Path] = None, + skip_reprompt: bool = False, + speak: bool = False, + debug: bool = False, + log_level: Optional[str] = None, + log_format: Optional[str] = None, + log_file_format: Optional[str] = None, + gpt3only: bool = False, + gpt4only: bool = False, + browser_name: Optional[str] = None, + allow_downloads: bool = False, + skip_news: bool = False, + workspace_directory: Optional[Path] = None, + install_plugin_deps: bool = False, + override_ai_name: Optional[str] = None, + override_ai_role: Optional[str] = None, + resources: Optional[list[str]] = None, + constraints: Optional[list[str]] = None, + best_practices: Optional[list[str]] = None, + override_directives: bool = False, + member_descriptions: list[str] = [], +): + config = ConfigBuilder.build_config_from_env() + + # TODO: fill in llm values here + assert_config_has_openai_api_key(config) + + apply_overrides_to_config( + config=config, + continuous=continuous, + continuous_limit=continuous_limit, + ai_settings_file=ai_settings, + prompt_settings_file=prompt_settings, + skip_reprompt=skip_reprompt, + speak=speak, + debug=debug, + log_level=log_level, + log_format=log_format, + log_file_format=log_file_format, + gpt3only=gpt3only, + gpt4only=gpt4only, + browser_name=browser_name, + allow_downloads=allow_downloads, + skip_news=skip_news, + ) + + # Set up logging module + configure_logging( + **config.logging.dict(), + tts_config=config.tts_config, + ) + + llm_provider = _configure_openai_provider(config) + + logger = logging.getLogger(__name__) + logger.setLevel(logging.INFO) + + if config.continuous_mode: + for line in get_legal_warning().split("\n"): + logger.warning( + extra={ + "title": "LEGAL:", + "title_color": Fore.RED, + "preserve_color": True, + }, + msg=markdown_to_ansi_style(line), + ) + + if not config.skip_news: + print_motd(config, logger) + print_git_branch_info(logger) + print_python_version_info(logger) + print_attribute("Smart LLM", config.smart_llm) + print_attribute("Fast LLM", config.fast_llm) + print_attribute("Browser", config.selenium_web_browser) + if config.continuous_mode: + print_attribute("Continuous Mode", "ENABLED", title_color=Fore.YELLOW) + if continuous_limit: + print_attribute("Continuous Limit", config.continuous_limit) + if config.tts_config.speak_mode: + print_attribute("Speak Mode", "ENABLED") + if ai_settings: + print_attribute("Using AI Settings File", ai_settings) + if prompt_settings: + print_attribute("Using Prompt Settings File", prompt_settings) + if config.allow_downloads: + print_attribute("Native Downloading", "ENABLED") + + if install_plugin_deps: + install_plugin_dependencies() + + config.plugins = scan_plugins(config) + configure_chat_plugins(config) + agent_group = None + + + ###################### + # Set up a new Agent Group # + ###################### + if not agent_group: + task = await clean_input( + config, + "Enter the task that you want AutoGPT to execute," + " with as much detail as possible:", + ) + base_ai_directives = AIDirectives.from_file(config.prompt_settings_file) + + agent_members = [] + for description in member_descriptions: + description_params = description.split(":") + agent_members.append( + await create_agent_member( + role=description_params[0], + initial_prompt=description_params[1], + create_agent=bool(description_params[2]), + llm_provider=llm_provider + ) + ) + + for member_index in range(len(agent_members)): + description_params = member_descriptions[member_index].split(":") + if len(description_params) >= 4 and description_params[3] != "": + agent_members[member_index].boss = agent_members[int(description_params[3])] + if len(description_params) >= 5 and description_params[4] != "": + agent_members[member_index].recruiter = agent_members[int(description_params[4])] + + for member_index in range(len(agent_members)): + for sub_member_index in range(len(agent_members)): + if agent_members[sub_member_index].boss != None: + if agent_members[sub_member_index].boss.id == agent_members[member_index].id: + agent_members[member_index].members.append(agent_members[sub_member_index]) + + agent_group = AgentGroup( + leader = agent_members[0] + ) + await agent_group.create_task(TaskRequestBody(input=task)) + + ################# + # Run the Agent # + ################# + await run_interaction_loop_for_agent_group(agent_group) + +async def run_interaction_loop_for_agent_group( + agent_group: "AgentGroup", +) -> None: + """Run the main interaction loop for the agent group. + + Args: + agent_group: The agent group to run the interaction loop for. + + Returns: + None + """ + # These contain both application config and agent config, so grab them here. + legacy_config = agent_group.leader.legacy_config + ai_profile = agent_group.leader.ai_profile + logger = logging.getLogger(__name__) + + cycle_budget = cycles_remaining = _get_cycle_budget( + legacy_config.continuous_mode, legacy_config.continuous_limit + ) + spinner = Spinner( + "Thinking...", plain_output=legacy_config.logging.plain_console_output + ) + stop_reason = None + + def graceful_agent_interrupt(signum: int, frame: Optional[FrameType]) -> None: + nonlocal cycle_budget, cycles_remaining, spinner, stop_reason + if stop_reason: + logger.error("Quitting immediately...") + sys.exit() + if cycles_remaining in [0, 1]: + logger.warning("Interrupt signal received: shutting down gracefully.") + logger.warning( + "Press Ctrl+C again if you want to stop AutoGPT immediately." + ) + stop_reason = AgentTerminated("Interrupt signal received") + else: + restart_spinner = spinner.running + if spinner.running: + spinner.stop() + + logger.error( + "Interrupt signal received: stopping continuous command execution." + ) + cycles_remaining = 1 + if restart_spinner: + spinner.start() + + def handle_stop_signal() -> None: + if stop_reason: + raise stop_reason + + # Set up an interrupt signal for the agent. + signal.signal(signal.SIGINT, graceful_agent_interrupt) + + ######################### + # Application Main Loop # + ######################### + + # Keep track of consecutive failures of the agent + consecutive_failures = 0 + + while cycles_remaining > 0: + logger.debug(f"Cycle budget: {cycle_budget}; remaining: {cycles_remaining}") + + ######## + # Plan # + ######## + handle_stop_signal() + # Have the agent determine the next action to take. + commands_result = [] + with spinner: + try: + commands_result = await agent_group.leader.recursive_propose_action() + except InvalidAgentResponseError as e: + logger.warning(f"The agent's thoughts could not be parsed: {e}") + consecutive_failures += 1 + if consecutive_failures >= 3: + logger.error( + "The agent failed to output valid thoughts" + f" {consecutive_failures} times in a row. Terminating..." + ) + raise AgentTerminated( + "The agent failed to output valid thoughts" + f" {consecutive_failures} times in a row." + ) + continue + + consecutive_failures = 0 + + ############### + # Update User # + ############### + # Print the assistant's thoughts and the next command to the user. + update_user_in_group_moode(commands_result) + + ################## + # Get user input # + ################## + handle_stop_signal() + if cycles_remaining == 1: # Last cycle + user_feedback, user_input, new_cycles_remaining = await get_user_feedback( + legacy_config, + ai_profile, + ) + + if user_feedback == UserFeedback.AUTHORIZE: + if new_cycles_remaining is not None: + # Case 1: User is altering the cycle budget. + if cycle_budget > 1: + cycle_budget = new_cycles_remaining + 1 + # Case 2: User is running iteratively and + # has initiated a one-time continuous cycle + cycles_remaining = new_cycles_remaining + 1 + else: + # Case 1: Continuous iteration was interrupted -> resume + if cycle_budget > 1: + logger.info( + f"The cycle budget is {cycle_budget}.", + extra={ + "title": "RESUMING CONTINUOUS EXECUTION", + "title_color": Fore.MAGENTA, + }, + ) + # Case 2: The agent used up its cycle budget -> reset + cycles_remaining = cycle_budget + 1 + logger.info( + "-=-=-=-=-=-=-= COMMAND AUTHORISED BY USER -=-=-=-=-=-=-=", + extra={"color": Fore.MAGENTA}, + ) + elif user_feedback == UserFeedback.EXIT: + logger.warning("Exiting...") + exit() + else: # user_feedback == UserFeedback.TEXT + command_name = "human_feedback" + else: + user_input = "" + # First log new-line so user can differentiate sections better in console + print() + if cycles_remaining != math.inf: + # Print authorized commands left value + print_attribute( + "AUTHORIZED_COMMANDS_LEFT", cycles_remaining, title_color=Fore.CYAN + ) + + ################### + # Execute Command # + ################### + # Decrement the cycle counter first to reduce the likelihood of a SIGINT + # happening during command execution, setting the cycles remaining to 1, + # and then having the decrement set it to 0, exiting the application. + # if command_name != "human_feedback": + cycles_remaining -= 1 + + if not commands_result: + continue + + handle_stop_signal() + + if commands_result: + for command_result in commands_result: + command_action_results = await command_result.agent.execute_commands(command_result.commands) + for command_action_result in command_action_results: + result = command_action_result.action_result + command = command_action_result.command + if result.status == "success": + logger.info( + result, extra={"title": "SYSTEM:", "title_color": Fore.YELLOW} + ) + elif result.status == "error": + logger.warning( + f"Command {command} returned an error: " + f"{result.error or result.reason}" + ) +def update_user_in_group_moode( + commands_result: list[ProposeActionResult], +) -> None: + """Prints the commands result. + + Args: + commands_result: list of results for all of the agents + """ + logger = logging.getLogger(__name__) + + print() + for command_result in commands_result: + logger.info( + f"Agent = {Fore.CYAN}{command_result.agent.role}{Style.RESET_ALL} " + ) + for command in command_result.commands: + logger.info( + f"COMMAND = {Fore.CYAN}{remove_ansi_escape(command.command)}{Style.RESET_ALL} " + f"ARGUMENTS = {Fore.CYAN}{command.args}{Style.RESET_ALL}", + extra={ + "title": "NEXT ACTION:", + "title_color": Fore.CYAN, + "preserve_color": True, + }, + ) + diff --git a/autogpts/autogpt/autogpt/commands/create_agent.py b/autogpts/autogpt/autogpt/commands/create_agent.py new file mode 100644 index 00000000000..825e2b864cd --- /dev/null +++ b/autogpts/autogpt/autogpt/commands/create_agent.py @@ -0,0 +1,52 @@ +"""Commands to search the web with""" + +from __future__ import annotations + +from autogpt.command_decorator import command +from autogpt.agents.agent_member import AgentMember +from autogpt.core.utils.json_schema import JSONSchema +from autogpt.agents.agent_group import create_agent_member + +COMMAND_CATEGORY = "create_agent" +COMMAND_CATEGORY_TITLE = "Create agent" + + +@command( + "create_agent", + "Create a new agent member for someone. The prompt for this step should be create someone to do this task.", + { + "prompt": JSONSchema( + type=JSONSchema.Type.STRING, + description="The description for agent that one to be created", + required=True, + ), + "role": JSONSchema( + type=JSONSchema.Type.STRING, + description="role of agent member that one be created", + required=True, + ), + "boss_id": JSONSchema( + type=JSONSchema.Type.STRING, + description="The agent who will be boss of new agent id", + required=True, + ), + }, +) +async def create_agent(prompt: str, role: str, agent: AgentMember, boss_id: str) -> str: + """Create new agent for some one + + Args: + prompt (str): The description for agent that one to be created. + role (str): role of agent member that one be created. + + """ + try: + group = agent.group + boss = group.members[boss_id] + await create_agent_member( + role=role, initial_prompt=prompt, boss=boss, llm_provider=agent.llm_provider + ) + return f"{role} created" + except Exception as ex: + print(ex) + return f"can't create {role}" diff --git a/autogpts/autogpt/autogpt/commands/create_task.py b/autogpts/autogpt/autogpt/commands/create_task.py new file mode 100644 index 00000000000..bf84cb77bc3 --- /dev/null +++ b/autogpts/autogpt/autogpt/commands/create_task.py @@ -0,0 +1,59 @@ +"""Commands to search the web with""" + +from __future__ import annotations +from datetime import datetime +from typing import Optional +import uuid + +from autogpt.command_decorator import command +from autogpt.core.utils.json_schema import JSONSchema +from autogpt.agents.agent_member import AgentMember, AgentTask, AgentTaskStatus + +COMMAND_CATEGORY = "create_task" +COMMAND_CATEGORY_TITLE = "Create task" + + +@command( + "create_task", + "Create new task for yourself or one of your members. Show the assignee " + "by agent_id of yourself or your members. the task description should be matched " + "with the assignee description. If you can't find someone for this create or hire a new agent for this one.", + { + "task": JSONSchema( + type=JSONSchema.Type.STRING, + description="The description for task that will be created", + required=True, + ), + "agent_id": JSONSchema( + type=JSONSchema.Type.STRING, + description="The id of agent will be the ownwer of this task", + required=True, + ), + "father_task_id": JSONSchema( + type=JSONSchema.Type.STRING, + description="The task id that is cause of this task", + required=False, + ), + }, +) +async def create_task( + task: str, agent_id: str, agent: AgentMember, father_task_id: Optional[str] = None +): + """Create new agent for some one + + Args: + task (str): The description of that task + agent_id (str): The id of the agent will be the owner of this task. + father_task_id (str): The task id that is the cause of this task. + """ + task = AgentTask( + input=task, + status=AgentTaskStatus.INITIAL.value, + created_at=datetime.now(), + modified_at=datetime.now(), + task_id=str(uuid.uuid4()), + sub_tasks=[], + father_task_id=father_task_id, + ) + owner_of_task = agent.group.members[agent_id] + owner_of_task.tasks.append(task) diff --git a/autogpts/autogpt/autogpt/commands/request_agent.py b/autogpts/autogpt/autogpt/commands/request_agent.py new file mode 100644 index 00000000000..7e93a294331 --- /dev/null +++ b/autogpts/autogpt/autogpt/commands/request_agent.py @@ -0,0 +1,55 @@ +"""Commands to search the web with""" + +from __future__ import annotations + +from forge.sdk.model import TaskRequestBody +from autogpt.command_decorator import command +from autogpt.agents.agent_member import AgentMember +from autogpt.core.utils.json_schema import JSONSchema + +COMMAND_CATEGORY = "request_agent" +COMMAND_CATEGORY_TITLE = "Request an agent" + +@command( + "request_agent", + "Request a new agent member for someone. The prompt for this step should be create someone to do this task.", + { + "prompt": JSONSchema( + type=JSONSchema.Type.STRING, + description="The description for agent that one to be created", + required=True, + ), + "role": JSONSchema( + type=JSONSchema.Type.STRING, + description="Role of agent member that one be created", + required=True, + ), + "boss_id": JSONSchema( + type=JSONSchema.Type.STRING, + description="The agent id that is going to be the boss of new agent", + required=True, + ), + }, +) +async def request_agent(prompt: str, role:str, agent: AgentMember, boss_id: str) -> str: + """Request new agent for some one + + Args: + prompt (str): The description for agent that one to be created. + role (str): role of agent member that one be created. + boss_id (str): The agent id that is going to be the boss of new agent. + + """ + try: + if agent.recruiter != None: + await agent.recruiter.create_task(task_request=TaskRequestBody(input=f"hire someone with {role} and this prompt: {prompt} for agent with id {boss_id}")) + return f"create task for recruiter to hire {role}" + elif agent.boss != None: + await agent.boss.create_task(task_request=TaskRequestBody(input=f"hire someone with {role} and this prompt: {prompt} for agent with id {boss_id}")) + return f"create task for boss to hire {role}" + else: + raise Exception("We can't hire someone ") + except Exception as ex: + print(ex) + return f"can't create {role}" + diff --git a/autogpts/autogpt/autogpt/commands/web_search.py b/autogpts/autogpt/autogpt/commands/web_search.py index e39bfffc6d6..90146d83bd1 100644 --- a/autogpts/autogpt/autogpt/commands/web_search.py +++ b/autogpts/autogpt/autogpt/commands/web_search.py @@ -20,66 +20,66 @@ DUCKDUCKGO_MAX_ATTEMPTS = 3 -@command( - "web_search", - "Searches the web", - { - "query": JSONSchema( - type=JSONSchema.Type.STRING, - description="The search query", - required=True, - ) - }, - aliases=["search"], -) -def web_search(query: str, agent: Agent, num_results: int = 8) -> str: - """Return the results of a Google search - - Args: - query (str): The search query. - num_results (int): The number of results to return. - - Returns: - str: The results of the search. - """ - search_results = [] - attempts = 0 - - while attempts < DUCKDUCKGO_MAX_ATTEMPTS: - if not query: - return json.dumps(search_results) - - results = DDGS().text(query) - search_results = list(islice(results, num_results)) - - if search_results: - break - - time.sleep(1) - attempts += 1 - - search_results = [ - { - "title": r["title"], - "url": r["href"], - **({"exerpt": r["body"]} if r.get("body") else {}), - } - for r in search_results - ] - - results = ( - "## Search results\n" - # "Read these results carefully." - # " Extract the information you need for your task from the list of results" - # " if possible. Otherwise, choose a webpage from the list to read entirely." - # "\n\n" - ) + "\n\n".join( - f"### \"{r['title']}\"\n" - f"**URL:** {r['url']} \n" - "**Excerpt:** " + (f'"{exerpt}"' if (exerpt := r.get("exerpt")) else "N/A") - for r in search_results - ) - return safe_google_results(results) +# @command( +# "web_search", +# "Searches the web", +# { +# "query": JSONSchema( +# type=JSONSchema.Type.STRING, +# description="The search query", +# required=True, +# ) +# }, +# aliases=["search"], +# ) +# def web_search(query: str, agent: Agent, num_results: int = 8) -> str: +# """Return the results of a Google search + +# Args: +# query (str): The search query. +# num_results (int): The number of results to return. + +# Returns: +# str: The results of the search. +# """ +# search_results = [] +# attempts = 0 + +# while attempts < DUCKDUCKGO_MAX_ATTEMPTS: +# if not query: +# return json.dumps(search_results) + +# results = DDGS().text(query) +# search_results = list(islice(results, num_results)) + +# if search_results: +# break + +# time.sleep(1) +# attempts += 1 + +# search_results = [ +# { +# "title": r["title"], +# "url": r["href"], +# **({"exerpt": r["body"]} if r.get("body") else {}), +# } +# for r in search_results +# ] + +# results = ( +# "## Search results\n" +# # "Read these results carefully." +# # " Extract the information you need for your task from the list of results" +# # " if possible. Otherwise, choose a webpage from the list to read entirely." +# # "\n\n" +# ) + "\n\n".join( +# f"### \"{r['title']}\"\n" +# f"**URL:** {r['url']} \n" +# "**Excerpt:** " + (f'"{exerpt}"' if (exerpt := r.get("exerpt")) else "N/A") +# for r in search_results +# ) +# return safe_google_results(results) @command( diff --git a/autogpts/autogpt/autogpt/core/resource/model_providers/schema.py b/autogpts/autogpt/autogpt/core/resource/model_providers/schema.py index 43d4bd296e7..cf299c3a276 100644 --- a/autogpts/autogpt/autogpt/core/resource/model_providers/schema.py +++ b/autogpts/autogpt/autogpt/core/resource/model_providers/schema.py @@ -77,7 +77,7 @@ class AssistantFunctionCallDict(TypedDict): class AssistantToolCall(BaseModel): - id: str + # id: str type: Literal["function"] function: AssistantFunctionCall diff --git a/autogpts/autogpt/autogpt_multi_agent.sh b/autogpts/autogpt/autogpt_multi_agent.sh new file mode 100755 index 00000000000..7f5b326e31f --- /dev/null +++ b/autogpts/autogpt/autogpt_multi_agent.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +function find_python_command() { + if command -v python3 &> /dev/null + then + echo "python3" + elif command -v python &> /dev/null + then + echo "python" + else + echo "Python not found. Please install Python." + exit 1 + fi +} + +PYTHON_CMD=$(find_python_command) + +if $PYTHON_CMD -c "import sys; sys.exit(sys.version_info < (3, 10))"; then + if ! $PYTHON_CMD scripts/check_requirements.py; then + echo + poetry install --without dev + echo + echo "Finished installing packages! Starting AutoGPT..." + echo + fi + poetry run multi_agent "$@" +else + echo "Python 3.10 or higher is required to run Auto GPT." +fi diff --git a/autogpts/autogpt/poetry.lock b/autogpts/autogpt/poetry.lock index 176be5e63cb..ae362e4ee7c 100644 --- a/autogpts/autogpt/poetry.lock +++ b/autogpts/autogpt/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "abstract-singleton" @@ -338,14 +338,11 @@ uvicorn = "^0.23.2" webdriver-manager = "^4.0.1" [package.extras] -benchmark = ["agbenchmark @ git+https://github.com/Significant-Gravitas/AutoGPT.git#subdirectory=benchmark"] +benchmark = ["agbenchmark @ file:///home/mehdi/all/repositories/github.com/AutoGPT/benchmark"] [package.source] -type = "git" -url = "https://github.com/Significant-Gravitas/AutoGPT.git" -reference = "ab05b7ae70754c063909" -resolved_reference = "ab05b7ae70754c06390982d237d86dc7290cd1aa" -subdirectory = "autogpts/forge" +type = "directory" +url = "../forge" [[package]] name = "backoff" @@ -962,6 +959,98 @@ types-awscrt = "*" [package.extras] botocore = ["botocore"] +[[package]] +name = "brotli" +version = "1.1.0" +description = "Python bindings for the Brotli compression library" +optional = false +python-versions = "*" +files = [ + {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752"}, + {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9"}, + {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3"}, + {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d"}, + {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e"}, + {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, + {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, + {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, + {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, + {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6"}, + {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd"}, + {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf"}, + {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61"}, + {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, + {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, + {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, + {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, + {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91"}, + {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408"}, + {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, + {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, + {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, + {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, + {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, + {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, + {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4d4a848d1837973bf0f4b5e54e3bec977d99be36a7895c61abb659301b02c112"}, + {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fdc3ff3bfccdc6b9cc7c342c03aa2400683f0cb891d46e94b64a197910dc4064"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:5eeb539606f18a0b232d4ba45adccde4125592f3f636a6182b4a8a436548b914"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, + {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, + {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, + {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, + {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f733d788519c7e3e71f0855c96618720f5d3d60c3cb829d8bbb722dddce37985"}, + {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:929811df5462e182b13920da56c6e0284af407d1de637d8e536c5cd00a7daf60"}, + {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b63b949ff929fbc2d6d3ce0e924c9b93c9785d877a21a1b678877ffbbc4423a"}, + {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d192f0f30804e55db0d0e0a35d83a9fead0e9a359a9ed0285dbacea60cc10a84"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f296c40e23065d0d6650c4aefe7470d2a25fffda489bcc3eb66083f3ac9f6643"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, + {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, + {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, + {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, + {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208"}, + {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7"}, + {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a743e5a28af5f70f9c080380a5f908d4d21d40e8f0e0c8901604d15cfa9ba751"}, + {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0541e747cce78e24ea12d69176f6a7ddb690e62c425e01d31cc065e69ce55b48"}, + {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cdbc1fc1bc0bff1cef838eafe581b55bfbffaed4ed0318b724d0b71d4d377619"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:890b5a14ce214389b2cc36ce82f3093f96f4cc730c1cffdbefff77a7c71f2a97"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, + {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, + {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, + {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, + {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, + {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, + {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, + {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, +] + [[package]] name = "bs4" version = "0.0.1" @@ -1527,26 +1616,28 @@ files = [ [[package]] name = "curl-cffi" -version = "0.5.10" +version = "0.6.2" description = "libcurl ffi bindings for Python, with impersonation support" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "curl_cffi-0.5.10-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:892603dab5e56fb72bfff7ae969136138971f63f63defe98232e1ec55cb0f1c6"}, - {file = "curl_cffi-0.5.10-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:9937b8e13b1a6963c63e155b6621ec74649965105efedb919bc226fe731861cc"}, - {file = "curl_cffi-0.5.10-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b537595b9610a4dd0927c09823925b4e32b1ce0fd04385bfc5bb72ab830720e6"}, - {file = "curl_cffi-0.5.10-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b2bc8822d23415f6533c8b750475e9bbc76764025fe1dcb5866dc033607fd7b"}, - {file = "curl_cffi-0.5.10-cp37-abi3-win_amd64.whl", hash = "sha256:f9a1874b860c4e8db49bdfd9b9d4dc39999a1397d271ec78624c35c838e9e92a"}, - {file = "curl_cffi-0.5.10.tar.gz", hash = "sha256:55bac4b73e2d80ceeaabea33270fc8ca6ace594128a46710242f2e688b4f8bfc"}, + {file = "curl_cffi-0.6.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:23b8a2872b160718c04b06b1f8aa4fb1a2f4f94bce7040493515e081a27cad19"}, + {file = "curl_cffi-0.6.2-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ad3c1cf5360810825ec4bc3da425f26ee4098878a615dab9d309a99afd883ba9"}, + {file = "curl_cffi-0.6.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d01de6ed737ad1924aaa0198195b9020c38e77ce90ea3d72b9eacf4938c7adf"}, + {file = "curl_cffi-0.6.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37e513cc149d024a2d625e202f2cc9d4423d2937343ea2e06f797d99779e62dc"}, + {file = "curl_cffi-0.6.2-cp38-abi3-win32.whl", hash = "sha256:12e829af97cbf7c1d5afef177e786f6f404ddf163b08897a1ed087cadbeb4837"}, + {file = "curl_cffi-0.6.2-cp38-abi3-win_amd64.whl", hash = "sha256:3791b7a9ae4cb1298165300f2dc2d60a86779f055570ae83163fc2d8a74bf714"}, + {file = "curl_cffi-0.6.2.tar.gz", hash = "sha256:9ee519e960b5fc6e0bbf13d0ecba9ce5f6306cb929354504bf03cc30f59a8f63"}, ] [package.dependencies] +certifi = "*" cffi = ">=1.12.0" [package.extras] build = ["cibuildwheel", "wheel"] -dev = ["autoflake (==1.4)", "black (==22.8.0)", "coverage (==6.4.1)", "cryptography (==38.0.3)", "flake8 (==6.0.0)", "flake8-bugbear (==22.7.1)", "flake8-pie (==0.15.0)", "httpx (==0.23.1)", "isort (==5.10.1)", "mypy (==0.971)", "pytest (==7.1.2)", "pytest-asyncio (==0.19.0)", "pytest-trio (==0.7.0)", "trio (==0.21.0)", "trio-typing (==0.7.0)", "trustme (==0.9.0)", "types-certifi (==2021.10.8.2)", "uvicorn (==0.18.3)"] -test = ["cryptography (==38.0.3)", "httpx (==0.23.1)", "pytest (==7.1.2)", "pytest-asyncio (==0.19.0)", "pytest-trio (==0.7.0)", "trio (==0.21.0)", "trio-typing (==0.7.0)", "trustme (==0.9.0)", "types-certifi (==2021.10.8.2)", "uvicorn (==0.18.3)"] +dev = ["autoflake (==1.4)", "coverage (==6.4.1)", "cryptography (==38.0.3)", "flake8 (==6.0.0)", "flake8-bugbear (==22.7.1)", "flake8-pie (==0.15.0)", "httpx (==0.23.1)", "mypy (==0.971)", "nest-asyncio (==1.6.0)", "pytest (==7.1.2)", "pytest-asyncio (==0.19.0)", "pytest-trio (==0.7.0)", "ruff (==0.1.14)", "trio (==0.21.0)", "trio-typing (==0.7.0)", "trustme (==0.9.0)", "types-certifi (==2021.10.8.2)", "uvicorn (==0.18.3)", "websockets (==11.0.3)"] +test = ["cryptography (==38.0.3)", "fastapi (==0.100.0)", "httpx (==0.23.1)", "nest-asyncio (==1.6.0)", "proxy.py (==2.4.3)", "pytest (==7.1.2)", "pytest-asyncio (==0.19.0)", "pytest-trio (==0.7.0)", "python-multipart (==0.0.6)", "trio (==0.21.0)", "trio-typing (==0.7.0)", "trustme (==0.9.0)", "types-certifi (==2021.10.8.2)", "uvicorn (==0.18.3)", "websockets (==11.0.3)"] [[package]] name = "cycler" @@ -2035,6 +2126,30 @@ files = [ [package.dependencies] wcwidth = ">=0.2.12,<0.3.0" +[[package]] +name = "g4f" +version = "0.2.5.3" +description = "The official gpt4free repository | various collection of powerful language models" +optional = false +python-versions = "*" +files = [ + {file = "g4f-0.2.5.3-py3-none-any.whl", hash = "sha256:ee76ee7935b4eb2747b72191eaa2c5407772e90db6c6cb7b54d35962cf492287"}, + {file = "g4f-0.2.5.3.tar.gz", hash = "sha256:de16cd873d042858f073016ad2a8855e61b74051e9c51bdb7714f634e7ece2ec"}, +] + +[package.dependencies] +aiohttp = "*" +requests = "*" + +[package.extras] +all = ["PyExecJS", "aiohttp-socks", "async-property", "beautifulsoup4", "brotli", "browser-cookie3", "cairosvg", "certifi", "curl-cffi (>=0.6.2)", "duckduckgo-search (>=5.0)", "fastapi", "flask", "loguru", "nest-asyncio", "pillow", "platformdirs", "py-arkose-generator", "selenium-wire", "setuptools", "undetected-chromedriver (>=3.5.5)", "uvicorn", "werkzeug"] +api = ["fastapi", "loguru", "nest-asyncio", "uvicorn"] +gui = ["beautifulsoup4", "browser-cookie3", "duckduckgo-search (>=5.0)", "flask", "pillow", "werkzeug"] +image = ["beautifulsoup4", "cairosvg", "pillow"] +local = ["gpt4all"] +openai = ["async-property", "brotli", "py-arkose-generator"] +webdriver = ["platformdirs", "selenium-wire", "setuptools", "undetected-chromedriver"] + [[package]] name = "gitdb" version = "4.0.11" @@ -3752,6 +3867,17 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "nest-asyncio" +version = "1.6.0" +description = "Patch asyncio to allow nested event loops" +optional = false +python-versions = ">=3.5" +files = [ + {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, + {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, +] + [[package]] name = "networkx" version = "3.2.1" @@ -4449,18 +4575,18 @@ grpc = ["googleapis-common-protos (>=1.53.0)", "grpc-gateway-protoc-gen-openapiv [[package]] name = "platformdirs" -version = "4.1.0" +version = "4.2.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, - {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] [[package]] name = "playsound" @@ -4927,6 +5053,19 @@ typing-extensions = ">=4.2.0" dotenv = ["python-dotenv (>=0.10.4)"] email = ["email-validator (>=1.0.3)"] +[[package]] +name = "pyexecjs" +version = "1.5.1" +description = "Run JavaScript code from Python" +optional = false +python-versions = "*" +files = [ + {file = "PyExecJS-1.5.1.tar.gz", hash = "sha256:34cc1d070976918183ff7bdc0ad71f8157a891c92708c00c5fbbff7a769f505c"}, +] + +[package.dependencies] +six = ">=1.10.0" + [[package]] name = "pyflakes" version = "3.2.0" @@ -5319,6 +5458,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -6652,6 +6792,21 @@ files = [ {file = "tzdata-2023.4.tar.gz", hash = "sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9"}, ] +[[package]] +name = "undetected-chromedriver" +version = "3.5.5" +description = "('Selenium.webdriver.Chrome replacement with compatiblity for Brave, and other Chromium based browsers.', 'Not triggered by CloudFlare/Imperva/hCaptcha and such.', 'NOTE: results may vary due to many factors. No guarantees are given, except for ongoing efforts in understanding detection algorithms.')" +optional = false +python-versions = "*" +files = [ + {file = "undetected-chromedriver-3.5.5.tar.gz", hash = "sha256:9f945e1435005247abe17de316bcfda85b284a4177fd5f25167c78ced33b65ec"}, +] + +[package.dependencies] +requests = "*" +selenium = ">=4.9.0" +websockets = "*" + [[package]] name = "uritemplate" version = "4.1.1" @@ -7248,4 +7403,4 @@ benchmark = ["agbenchmark"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "f693821e204aaf64d946ac45d7c0e447f02c2a3f173f939dac2fcc8e797a142f" +content-hash = "294c758260913f103492e9f6ff7583329268a7828991500d8bd3ecf26d5117f0" diff --git a/autogpts/autogpt/pyproject.toml b/autogpts/autogpt/pyproject.toml index 919e1101815..b1405eb030f 100644 --- a/autogpts/autogpt/pyproject.toml +++ b/autogpts/autogpt/pyproject.toml @@ -17,14 +17,15 @@ packages = [{ include = "autogpt" }] [tool.poetry.scripts] autogpt = "autogpt.app.cli:cli" +multi_agent = "autogpt.app.cli:run_multi_agent" serve = "autogpt.app.cli:serve" [tool.poetry.dependencies] python = "^3.10" auto-gpt-plugin-template = {git = "https://github.com/Significant-Gravitas/Auto-GPT-Plugin-Template", rev = "0.1.0"} -# autogpt-forge = { path = "../forge" } -autogpt-forge = {git = "https://github.com/Significant-Gravitas/AutoGPT.git", rev = "ab05b7ae70754c063909", subdirectory = "autogpts/forge"} +autogpt-forge = { path = "../forge" } +# autogpt-forge = {git = "https://github.com/Significant-Gravitas/AutoGPT.git", rev = "ab05b7ae70754c063909", subdirectory = "autogpts/forge"} beautifulsoup4 = "^4.12.2" boto3 = "^1.33.6" charset-normalizer = "^3.1.0" diff --git a/autogpts/autogpt/tests/integration/test_agent_group.py b/autogpts/autogpt/tests/integration/test_agent_group.py new file mode 100644 index 00000000000..b5a9c2122d0 --- /dev/null +++ b/autogpts/autogpt/tests/integration/test_agent_group.py @@ -0,0 +1,64 @@ +import asyncio +import logging +import uuid +import requests +import pytest +from unittest.mock import patch +from autogpt.agents.agent_group import AgentGroup, create_agent_member +from autogpt.core.resource.model_providers.openai import OPEN_AI_CHAT_MODELS, _tool_calls_compat_extract_calls +from autogpt.core.resource.model_providers.schema import ChatModelResponse +from forge.sdk.model import TaskRequestBody +from autogpt.core.resource.model_providers.schema import AssistantChatMessage +logging.basicConfig( + level=logging.DEBUG) +logger = logging.getLogger(__name__) + +async def run_tasks(agents): + for agent in agents: + asyncio.create_task(agent.run_tasks()) + +async def main(): + ceo = await create_agent_member( + role="ceo", + initial_prompt="you are ceo of a software game company" + ) + + hr_lead = await create_agent_member( + role="hr_lead", + initial_prompt="you are hr_lead of a company You'll recruite agents when we need it", + boss=ceo, + create_agent=True + ) + + ceo.recruiter = hr_lead + + agentGroup = AgentGroup( + leader=ceo + ) + + await agentGroup.create_task(TaskRequestBody(input="create best shooter game in the world", additional_input={})) + + await run_tasks([ceo, hr_lead]) + + while True: + await asyncio.sleep(1) + running_tasks = [task for task in asyncio.all_tasks() if not task.done()] + if not running_tasks: + break +def empty_file(file_path): + try: + with open(file_path, 'w') as f: + f.truncate(0) + print(f"The file '{file_path}' has been emptied successfully.") + except Exception as e: + print(f"An error occurred: {e}") + +@pytest.mark.asyncio +@pytest.mark.vcr("/home/mahdi/all/repositories/github.com/autogpt/AutoGPT/autogpts/autogpt/tests/vcr_cassettes/test_agent_group_happy_scenario/test_agent_group_happy_scenario.yaml") +async def test_agent_group_happy_scenario(): + empty_file("/home/mahdi/all/repositories/github.com/autogpt/AutoGPT/autogpts/autogpt/agetn_group.db") + expected_uuids_for_tasks = ['11111111-1111-1111-1111-111111111121', '11111111-1111-1111-1111-111111111122','11111111-1111-1111-1111-111111111123','11111111-1111-1111-1111-111111111124'] + expected_uuids_for_agents = ['33333333-3333-3333-3333-333333333331', '33333333-3333-3333-3333-333333333332'] + with patch('forge.sdk.db.uuid4', side_effect=expected_uuids_for_tasks): + with patch('autogpt.agents.agent_member.uuid4', side_effect=expected_uuids_for_agents): + await main() diff --git a/autogpts/autogpt/tests/integration/test_agent_group_output1.dat b/autogpts/autogpt/tests/integration/test_agent_group_output1.dat new file mode 100644 index 00000000000..2156f2862cd --- /dev/null +++ b/autogpts/autogpt/tests/integration/test_agent_group_output1.dat @@ -0,0 +1,24 @@ + [ + { + "type": "function", + "function": { + "name": "create_agent", + "arguments": { + "name": "CEOGPT", + "description": "An expert CEO AI specialized in leading a software game company towards success through strategic decision making, financial management, and team building while adhering to best practices and constraints.", + "directives": { + "best_practices": [ + "Develop short-term and long-term goals along with tactical plans to achieve them, considering market trends, competition, and opportunities.", + "Monitor financial performance regularly, identify areas requiring improvement, and provide recommendations on resource allocation, pricing strategies, and revenue growth initiatives.", + "Foster innovation within teams by encouraging open communication, risk-taking, and continuous learning while maintaining a positive work culture.", + "Establish strong partnerships with external stakeholders such as publishers, platform providers, and industry organizations to expand reach and enhance brand reputation." + ], + "constraints": [ + "Never engage in insider trading or other fraudulent activities.", + "Ensure compliance with all relevant laws, regulations, and industry standards related to data privacy, intellectual property rights, and consumer protection." + ] + } + } + } + } +] diff --git a/autogpts/autogpt/tests/integration/test_agent_group_output2.dat b/autogpts/autogpt/tests/integration/test_agent_group_output2.dat new file mode 100644 index 00000000000..3ce2f02aff2 --- /dev/null +++ b/autogpts/autogpt/tests/integration/test_agent_group_output2.dat @@ -0,0 +1,24 @@ + [ + { + "type": "function", + "function": { + "name": "create_agent", + "arguments": { + "name": "HRLeadGPT", + "description": "an HR Lead Autonomous Agent responsible for managing recruitment processes within the company, including identifying hiring needs, developing job descriptions, sourcing candidates, conducting interviews, extending offers, and onboarding new hires.", + "directives": { + "best_practices": [ + "Maintain up-to-date knowledge about labor market trends, regulations, and best practices in talent acquisition.", + "Collaborate effectively with department leaders to identify current and future staffing requirements.", + "Develop comprehensive job descriptions detailing responsibilities, qualifications, and expectations.", + "Implement diverse and inclusive sourcing strategies to attract high-quality applicants from various backgrounds." + ], + "constraints": [ + "Respect all applicable laws, regulations, and corporate policies during the recruitment process.", + "Avoid making assumptions based on personal biases while evaluating candidate suitability." + ] + } + } + } + } +] diff --git a/autogpts/forge/forge/sdk/db.py b/autogpts/forge/forge/sdk/db.py index ce4d22f6f43..9f3b8f4b382 100644 --- a/autogpts/forge/forge/sdk/db.py +++ b/autogpts/forge/forge/sdk/db.py @@ -6,7 +6,7 @@ import datetime import math -import uuid +from uuid import uuid4 from typing import Any, Dict, List, Literal, Optional, Tuple from sqlalchemy import ( @@ -71,6 +71,7 @@ class ArtifactModel(Base): artifact_id = Column(String, primary_key=True, index=True) task_id = Column(String, ForeignKey("tasks.task_id")) + agent_task_id = Column(String, ForeignKey("agent_tasks.task_id")) step_id = Column(String, ForeignKey("steps.step_id")) agent_created = Column(Boolean, default=False) file_name = Column(String) @@ -83,7 +84,6 @@ class ArtifactModel(Base): step = relationship("StepModel", back_populates="artifacts") task = relationship("TaskModel", back_populates="artifacts") - def convert_to_task(task_obj: TaskModel, debug_enabled: bool = False) -> Task: if debug_enabled: LOG.debug(f"Converting TaskModel to Task for task_id: {task_obj.task_id}") diff --git a/autogpts/forge/forge/sdk/model.py b/autogpts/forge/forge/sdk/model.py index 6a55b5a243b..a45263b87e7 100644 --- a/autogpts/forge/forge/sdk/model.py +++ b/autogpts/forge/forge/sdk/model.py @@ -103,7 +103,6 @@ class Task(TaskRequestBody): ], ) - class StepRequestBody(BaseModel): name: Optional[str] = Field( None, description="The name of the task step.", example="Write to file" diff --git a/tests/vcr_cassettes/test_agent_group_happy_scenario/test_agent_group_happy_scenario.yaml b/tests/vcr_cassettes/test_agent_group_happy_scenario/test_agent_group_happy_scenario.yaml new file mode 100644 index 00000000000..122e33569dd --- /dev/null +++ b/tests/vcr_cassettes/test_agent_group_happy_scenario/test_agent_group_happy_scenario.yaml @@ -0,0 +1,834 @@ +interactions: +- request: + body: '{"messages": [{"content": "Your job is to respond to a user-defined task, + given in triple quotes, by invoking the `create_agent` function to generate + an autonomous agent to complete the task. You should supply a role-based name + for the agent (_GPT), an informative description for what the agent does, and + 1 to 5 directives in each of the categories Best Practices and Constraints, + that are optimally aligned with the successful completion of its assigned task.\n\nExample + Input:\n\"\"\"Help me with marketing my business\"\"\"\n\nExample Call:\n```\n[\n {\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"create_agent\",\n \"arguments\": + {\n \"name\": \"CMOGPT\",\n \"description\": \"a + professional digital marketer AI that assists Solopreneurs in growing their + businesses by providing world-class expertise in solving marketing problems + for SaaS, content products, agencies, and more.\",\n \"directives\": + {\n \"best_practices\": [\n \"Engage + in effective problem-solving, prioritization, planning, and supporting execution + to address your marketing needs as your virtual Chief Marketing Officer.\",\n \"Provide + specific, actionable, and concise advice to help you make informed decisions + without the use of platitudes or overly wordy explanations.\",\n \"Identify + and prioritize quick wins and cost-effective campaigns that maximize results + with minimal time and budget investment.\",\n \"Proactively + take the lead in guiding you and offering suggestions when faced with unclear + information or uncertainty to ensure your marketing strategy remains on track.\"\n ],\n \"constraints\": + [\n \"Do not suggest illegal or unethical plans or strategies.\",\n \"Take + reasonable budgetary limits into account.\"\n ]\n }\n }\n }\n }\n]\n```", + "role": "system"}, {"content": "\"\"\"you are ceo of a software game company\"\"\"", + "role": "user"}], "model": "gpt-4-turbo-preview", "tool_choice": {"function": + {"name": "create_agent"}, "type": "function"}, "tools": [{"function": {"description": + "Create a new autonomous AI agent to complete a given task.", "name": "create_agent", + "parameters": {"properties": {"description": {"description": "An informative + one sentence description of what the AI agent does", "type": "string"}, "directives": + {"properties": {"best_practices": {"description": "One to five highly effective + best practices that are optimally aligned with the completion of the given task", + "items": {"type": "string"}, "maxItems": 5, "minItems": 1, "type": "array"}, + "constraints": {"description": "One to five reasonable and efficacious constraints + that are optimally aligned with the completion of the given task", "items": + {"type": "string"}, "maxItems": 5, "minItems": 1, "type": "array"}}, "required": + ["best_practices", "constraints"], "type": "object"}, "name": {"description": + "A short role-based name for an autonomous agent.", "type": "string"}}, "required": + ["name", "description", "directives"], "type": "object"}}, "type": "function"}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '3299' + content-type: + - application/json + host: + - api.openai.com + x-stainless-arch: + - x64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - Linux + x-stainless-package-version: + - 1.12.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.10.12 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + content: ' { "id": "chatcmpl-abc123", "object": "chat.completion", "created": + 1677858242, "model": "gpt-3.5-turbo-0613", "usage": { "prompt_tokens": 13, "completion_tokens": + 7, "total_tokens": 20 }, "choices": [ { "message": { "role": "assistant", "content": + "[{\"type\":\"function\",\"function\":{\"name\":\"create_agent\",\"arguments\":{\"name\":\"CEOGPT\",\"description\":\"An + autonomous CEO AI tailored for managing a software game company. It provides + strategic direction, fosters innovation, and ensures operational efficiency + to drive the companys success.\",\"directives\":{\"best_practices\":[\"Foster + a culture of creativity, innovation, and collaboration within the company to + continually enhance game development and user experience.\",\"Develop a comprehensive + market analysis strategy to understand target audiences, industry trends, and + competitors, ensuring informed decision-making.\",\"Implement agile development + methodologies to iterate quickly, respond to market changes, and deliver high-quality + games on time and within budget.\",\"Leverage data analytics and user feedback + to refine game mechanics, optimize monetization strategies, and enhance player + engagement.\",\"Establish strong partnerships with key stakeholders, including + game developers, publishers, and platform providers, to expand distribution + channels and reach new markets.\"],\"constraints\":[\"Adhere to industry regulations + and standards, ensuring compliance with legal requirements and ethical practices + in all aspects of game development and publishing.\",\"Maintain a balanced approach + between creative vision and financial sustainability, avoiding excessive risk-taking + that could jeopardize the companys long-term viability.\"]}}}}]", "tool_calls":[{"id":"random_id","type":"function","function":{"name":"create_agent","arguments":"{\"name\":\"CEOGPT\",\"description\":\"An + autonomous CEO AI tailored for managing a software game company. It provides + strategic direction, fosters innovation, and ensures operational efficiency + to drive the companys success.\",\"directives\":{\"best_practices\":[\"Foster + a culture of creativity, innovation, and collaboration within the company to + continually enhance game development and user experience.\",\"Develop a comprehensive + market analysis strategy to understand target audiences, industry trends, and + competitors, ensuring informed decision-making.\",\"Implement agile development + methodologies to iterate quickly, respond to market changes, and deliver high-quality + games on time and within budget.\",\"Leverage data analytics and user feedback + to refine game mechanics, optimize monetization strategies, and enhance player + engagement.\",\"Establish strong partnerships with key stakeholders, including + game developers, publishers, and platform providers, to expand distribution + channels and reach new markets.\"],\"constraints\":[\"Adhere to industry regulations + and standards, ensuring compliance with legal requirements and ethical practices + in all aspects of game development and publishing.\",\"Maintain a balanced approach + between creative vision and financial sustainability, avoiding excessive risk-taking + that could jeopardize the companys long-term viability.\"]}}"}}] }, "logprobs": + null, "finish_reason": "stop", "index": 0 } ] } ' + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 85c7f8681d440a5f-AMS + Connection: + - keep-alive + Content-Length: + - '269' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 28 Feb 2024 10:26:37 GMT + Server: + - cloudflare + Set-Cookie: + - __cf_bm=MLY4hTR.IFDGIdNL6l.o8WamKFhganMDeIwdBKkD43g-1709115997-1.0-AQxZ34/9EiS3AxqSO3HceYc2wjWk7UDkYOH+j5CWDmGip9i1HfOV8MSjVEN49W8tgbaZtZfmw1uWkUMgLXxLqEs=; + path=/; expires=Wed, 28-Feb-24 10:56:37 GMT; domain=.api.openai.com; HttpOnly; + Secure; SameSite=None + - _cfuvid=Rv7Zavx4VnfUuJ5ukoYp8eLdnNR5DDfPC4t_6.Xk77E-1709115997602-0.0-604800000; + path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None + alt-svc: + - h3=":443"; ma=86400 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Origin + x-request-id: + - req_0a522190af26733adc03a08b837e6d5b + http_version: HTTP/1.1 + status_code: 200 +- request: + body: '{"messages": [{"content": "Your job is to respond to a user-defined task, + given in triple quotes, by invoking the `create_agent` function to generate + an autonomous agent to complete the task. You should supply a role-based name + for the agent (_GPT), an informative description for what the agent does, and + 1 to 5 directives in each of the categories Best Practices and Constraints, + that are optimally aligned with the successful completion of its assigned task.\n\nExample + Input:\n\"\"\"Help me with marketing my business\"\"\"\n\nExample Call:\n```\n[\n {\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"create_agent\",\n \"arguments\": + {\n \"name\": \"CMOGPT\",\n \"description\": \"a + professional digital marketer AI that assists Solopreneurs in growing their + businesses by providing world-class expertise in solving marketing problems + for SaaS, content products, agencies, and more.\",\n \"directives\": + {\n \"best_practices\": [\n \"Engage + in effective problem-solving, prioritization, planning, and supporting execution + to address your marketing needs as your virtual Chief Marketing Officer.\",\n \"Provide + specific, actionable, and concise advice to help you make informed decisions + without the use of platitudes or overly wordy explanations.\",\n \"Identify + and prioritize quick wins and cost-effective campaigns that maximize results + with minimal time and budget investment.\",\n \"Proactively + take the lead in guiding you and offering suggestions when faced with unclear + information or uncertainty to ensure your marketing strategy remains on track.\"\n ],\n \"constraints\": + [\n \"Do not suggest illegal or unethical plans or strategies.\",\n \"Take + reasonable budgetary limits into account.\"\n ]\n }\n }\n }\n }\n]\n```", + "role": "system"}, {"content": "\"\"\"you are hr_lead of a company You''ll recruite + agents when we need it\"\"\"", "role": "user"}], "model": "gpt-4-turbo-preview", + "tool_choice": {"function": {"name": "create_agent"}, "type": "function"}, "tools": + [{"function": {"description": "Create a new autonomous AI agent to complete + a given task.", "name": "create_agent", "parameters": {"properties": {"description": + {"description": "An informative one sentence description of what the AI agent + does", "type": "string"}, "directives": {"properties": {"best_practices": {"description": + "One to five highly effective best practices that are optimally aligned with + the completion of the given task", "items": {"type": "string"}, "maxItems": + 5, "minItems": 1, "type": "array"}, "constraints": {"description": "One to five + reasonable and efficacious constraints that are optimally aligned with the completion + of the given task", "items": {"type": "string"}, "maxItems": 5, "minItems": + 1, "type": "array"}}, "required": ["best_practices", "constraints"], "type": + "object"}, "name": {"description": "A short role-based name for an autonomous + agent.", "type": "string"}}, "required": ["name", "description", "directives"], + "type": "object"}}, "type": "function"}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '3328' + content-type: + - application/json + host: + - api.openai.com + x-stainless-arch: + - x64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - Linux + x-stainless-package-version: + - 1.12.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.10.12 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + content: '{ "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1677858242, + "model": "gpt-3.5-turbo-0613", "usage": { "prompt_tokens": 13, "completion_tokens": + 7, "total_tokens": 20 }, "choices": [ { "message": { "role": "assistant", "content": + "[{\"type\":\"function\",\"function\":{\"name\":\"create_agent\",\"arguments\":{\"name\":\"HRLead_GPT\" + ,\"description\":\"An HR lead AI empowered to efficiently manage the recruitment + process, streamline hiring procedures, and ensure the companys staffing needs + are met promptly and effectively.\" ,\"directives\":{\"best_practices\":[\"Employ + systematic and fair evaluation methods to assess candidate qualifications, skills, + and cultural fit.\",\"Leverage data-driven insights to optimize the recruitment + pipeline, enhance candidate experience, and reduce time-to-hire metrics.\",\"Maintain + transparent communication with hiring managers and stakeholders regarding recruitment + progress, challenges, and opportunities.\",\"Implement proactive talent sourcing + strategies to build robust talent pools and mitigate potential skill gaps and + recruitment bottlenecks.\",\"Regularly review and refine recruitment processes + to adapt to changing market trends, technological advancements, and organizational + needs.\"],\"constraints\":[\"Adhere strictly to legal and regulatory requirements + governing hiring practices, ensuring non-discrimination and equal opportunity + principles are upheld.\",\"Respect candidate privacy and confidentiality throughout + the recruitment process, safeguarding sensitive personal information.\",\"Exercise + discretion and professionalism when handling sensitive or confidential candidate + data, refraining from unauthorized disclosure or misuse.\",\"Ensure adherence + to established budgetary allocations for recruitment activities , optimizing + resource utilization and cost-effectiveness.\",\"Avoid over-reliance on automated + decision-making processes, maintaining human oversight and intervention where + necessary to prevent bias or errors.\"]}}}}]", "tool_calls":[{"id":"random_id_2","type":"function","function":{"name":"create_agent","arguments":"{\"name\":\"HRLead_GPT\",\"description\":\"An + HR lead AI empowered to efficiently manage the recruitment process, streamline + hiring procedures, and ensure the companys staffing needs are met promptly and + effectively.\",\"directives\":{\"best_practices\":[\"Employ systematic and fair + evaluation methods to assess candidate qualifications, skills, and cultural + fit.\",\"Leverage data-driven insights to optimize the recruitment pipeline, + enhance candidate experience, and reduce time-to-hire metrics.\",\"Maintain + transparent communication with hiring managers and stakeholders regarding recruitment + progress, challenges, and opportunities.\",\"Implement proactive talent sourcing + strategies to build robust talent pools and mitigate potential skill gaps and + recruitment bottlenecks.\",\"Regularly review and refine recruitment processes + to adapt to changing market trends, technological advancements, and organizational + needs.\"],\"constraints\":[\"Adhere strictly to legal and regulatory requirements + governing hiring practices, ensuring non-discrimination and equal opportunity + principles are upheld.\",\"Respect candidate privacy and confidentiality throughout + the recruitment process, safeguarding sensitive personal information.\",\"Exercise + discretion and professionalism when handling sensitive or confidential candidate + data, refraining from unauthorized disclosure or misuse.\",\"Ensure adherence + to established budgetary allocations for recruitment activities, optimizing + resource utilization and cost-effectiveness.\",\"Avoid over-reliance on automated + decision-making processes, maintaining human oversight and intervention where + necessary to prevent bias or errors.\"]}}"}}]}, "logprobs": null, "finish_reason": + "stop", "index": 0 } ] } ' + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 85c91c445a125c49-AMS + Connection: + - keep-alive + Content-Length: + - '269' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 28 Feb 2024 13:45:52 GMT + Server: + - cloudflare + Set-Cookie: + - __cf_bm=yY3T7KV58WZAleTXLYU4B1NiQIvAesrVSrrECWXZ4N0-1709127952-1.0-AUqfj+INOB2PdUciWO12/Vto/wUeb+zFl73MNMFZtxRfnCJ4dZjiMfXUhnzH6z6B4zqs1e92q2JaYyJ11KUtdE0=; + path=/; expires=Wed, 28-Feb-24 14:15:52 GMT; domain=.api.openai.com; HttpOnly; + Secure; SameSite=None + - _cfuvid=.Y5rAOMTuA.demTIhPTy1gL5v9jAJmIksxrB6P.kIjg-1709127952200-0.0-604800000; + path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None + alt-svc: + - h3=":443"; ma=86400 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Origin + x-request-id: + - req_cf61793fa0e7e1769af3cf1abc9ccdd8 + http_version: HTTP/1.1 + status_code: 200 +- request: + body: '{"messages": [{"content": "You are CEOGPT, An autonomous CEO AI tailored + for managing a software game company. It provides strategic direction, fosters + innovation, and ensures operational efficiency to drive the companys success + with agent_id 33333333-3333-3333-3333-333333333331.\n\nYour decisions must always + be made independently without seeking user assistance. Play to your strengths + as an LLM and pursue simple strategies with no legal complications.\n\nThe OS + you are running on is: Ubuntu 22.04.3 LTS\n\n## Members detail:\n{agent_id: + 33333333-3333-3333-3333-333333333332, description: An HR lead AI empowered to + efficiently manage the recruitment process, streamline hiring procedures, and + ensure the companys staffing needs are met promptly and effectively}\n\n\n## + Constraints\nYou operate within the following constraints:\n1. Adhere to industry + regulations and standards, ensuring compliance with legal requirements and ethical + practices in all aspects of game development and publishing.\n2. Maintain a + balanced approach between creative vision and financial sustainability, avoiding + excessive risk-taking that could jeopardize the companys long-term viability.\n\n## + Resources\nYou can leverage access to the following resources:\n\n\n## Commands\nThese + are the ONLY commands you can use. Any action you perform must be possible through + list of these commands:\n1. execute_python_code: Executes the given Python code + inside a single-use Docker container with access to your workspace folder. Params: + (code: string)\n2. execute_python_file: Execute an existing Python file inside + a single-use Docker container with access to your workspace folder. Params: + (filename: string, args?: Array)\n3. list_folder: List the items in + a folder. Params: (folder: string)\n4. open_file: Opens a file for editing or + continued viewing; creates it if it does not exist yet. Note: If you only need + to read or write a file once, use `write_to_file` instead.. Params: (file_path: + string)\n5. open_folder: Open a folder to keep track of its content. Params: + (path: string)\n6. read_file: Read an existing file. Params: (filename: string)\n7. + write_file: Write a file, creating it if necessary. If the file exists, it is + overwritten.. Params: (filename: string, contents: string)\n8. web_search: Searches + the web. Params: (query: string)\n9. read_webpage: Read a webpage, and extract + specific information from it. You must specify either topics_of_interest, a + question, or get_raw_content.. Params: (url: string, topics_of_interest?: Array, + question?: string, get_raw_content?: boolean)\n10. finish_task: Use this to + shut down once you have completed your task, or when there are insurmountable + problems that make it impossible for you to finish your task.. Params: (reason: + string, task_id: string)\n11. hire_agent: Hire a new agent member for someone. + The prompt for this step should be create someone to do this task.. Params: + (prompt: string, role: string, boss_id: string)\n12. create_task: Create new + task for yourself or one of your members. Show the assignee by agent_id of yourself + or your members. the task description should be matched with the assignee description. + If you can''t find someone for this create or hire a new agent for this one.. + Params: (task: string, agent_id: string, father_task_id?: string)\n\n## Best + practices\n1. Foster a culture of creativity, innovation, and collaboration + within the company to continually enhance game development and user experience.\n2. + Develop a comprehensive market analysis strategy to understand target audiences, + industry trends, and competitors, ensuring informed decision-making.\n3. Implement + agile development methodologies to iterate quickly, respond to market changes, + and deliver high-quality games on time and within budget.\n4. Leverage data + analytics and user feedback to refine game mechanics, optimize monetization + strategies, and enhance player engagement.\n5. Establish strong partnerships + with key stakeholders, including game developers, publishers, and platform providers, + to expand distribution channels and reach new markets.\n\n## tasks\nThe user + will specify tasks for you to execute,in triple quotes, in the next message. + Your job is to use the command list to do things to make progress in tasks. + complete the task while following your directives as given above, and terminate + when your task is done. It''s good practice to hire or create other agents to + break tasks and make them better.", "role": "system"}, {"content": "\"\"\"[\n {\n \"task_id\": + \"11111111-1111-1111-1111-111111111121\",\n \"task_detail\": \"create + best shooter game in the world\",\n \"status\": \"DOING\",\n \"sub_tasks\": + []\n }\n]\"\"\"", "role": "user"}, {"content": "Respond with pure JSON. The + JSON object should be compatible with the TypeScript type `Response` from the + following:\ninterface Response {\nthoughts: {\n// Relevant observations from + your last action (if any)\nobservations: string;\n// Thoughts\ntext: string;\nreasoning: + string;\n// Constructive self-criticism\nself_criticism: string;\n// Short markdown-style + bullet list that conveys the long-term plan\nplan: string;\n// Summary of thoughts, + to say to user\nspeak: string;\n};\n// list of commands for tasks whose status + is DOING. The structure should be like this list[dict[name:str, args:object, + task_id: str ]] for complete list of tasks. Each command should be assign to + a task wholse status is DOING by task_id field. You need to choose some commands + to progress tasks. your list shouldn''t be empty\ncommands: Array;\n// check + all tasks that I sent you (not sub_tasks part) with CHECKING status. In this + part you should say status of tasks that I sent(not sub_tasks parts). send them + with these statuses: REJECTED, DONE with this structure list[dict[task_id: str, + status: str, reason: str?]] (REJECTED: This status shows that the task need + to be imporved by the owner of task and agent member needs to work on it more. + need to say reject reason in reason fieldDONE: This status shows that the result + of task is OK and the task is done)\ntasks: Array;\n}", "role": "system"}, {"content": + "Determine list of commands to use next based on the given goals and the progress + you have made so far, and respond using the JSON schema specified previously:", + "role": "user"}], "model": "gpt-4-turbo-preview"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '6376' + content-type: + - application/json + host: + - api.openai.com + x-stainless-arch: + - x64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - Linux + x-stainless-package-version: + - 1.12.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.10.12 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + content: '{ "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1677858242, + "model": "gpt-3.5-turbo-0613", "usage": { "prompt_tokens": 13, "completion_tokens": + 7, "total_tokens": 20 }, "choices": [ { "message": { "role": "assistant", "content": + "{\n \"thoughts\": {\n \"observations\": \"\",\n \"text\": + \"To progress in creating the best shooter game in the world, we need to focus + on key development tasks, prioritize feature implementation, and ensure efficient + resource utilization.\",\n \"reasoning\": \"By following a systematic + approach and leveraging available resources effectively, we can expedite the + game development process and deliver a high-quality product.\",\n \"self_criticism\": + \"We need to ensure that our development efforts remain aligned with the overarching + goal of creating the best shooter game while maintaining a balance between innovation + and feasibility.\",\n \"plan\": \"- Implement core gameplay mechanics\\n- + Design immersive environments\\n- Develop intuitive controls\\n- Test and iterate + for refinement\\n- Optimize performance and user experience\",\n \"speak\": + \"Our focus is on advancing the development of the best shooter game. We will + concentrate on core mechanics, environment design, controls, testing, and optimization.\"\n },\n \"commands\": + [\n {\n \"name\": \"hire_agent\",\n \"args\": {\n \"prompt\": + \"hire a CTO for our company\",\n \"role\": \"CTO\"\n , \"boss_id\": + \"33333333-3333-3333-3333-333333333331\"\n },\n \"task_id\": + \"11111111-1111-1111-1111-111111111121\"\n },\n {\n \"name\": + \"create_task\",\n \"args\": {\n \"task\": \"give + me a structure of what roles we need in our company\",\n \"agent_id\": + \"33333333-3333-3333-3333-333333333332\",\n \"father_task_id\": + \"11111111-1111-1111-1111-111111111121\"\n },\n \"task_id\": + \"11111111-1111-1111-1111-111111111121\"\n },\n {\n \"name\": + \"create_task\",\n \"args\": {\n \"task\": \"Follow + up tasks that I gave to my members\",\n \"agent_id\": \"33333333-3333-3333-3333-333333333331\",\n \"father_task_id\": + \"11111111-1111-1111-1111-111111111121\"\n },\n \"task_id\": + \"11111111-1111-1111-1111-111111111121\"\n }\n ], \"tasks\":[]}" }, "logprobs": + null, "finish_reason": "stop", "index": 0 } ] } ' + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 85f8a8b43fdf1959-FRA + Connection: + - keep-alive + Content-Length: + - '269' + Content-Type: + - application/json; charset=utf-8 + Date: + - Tue, 05 Mar 2024 08:15:35 GMT + Server: + - cloudflare + Set-Cookie: + - __cf_bm=kgSxHQxZcZaRGMx2PngAkp_U.P7URfUV2EDGerqbN1I-1709626535-1.0.1.1-AMzKeFxktTz9HHgWZyYriCSfZRdZkPVY9GXctre4y0fCTJGy3LHgRtQ227gRmHXxY82Vz1OJ1Mu7UzEcEmd3JA; + path=/; expires=Tue, 05-Mar-24 08:45:35 GMT; domain=.api.openai.com; HttpOnly; + Secure; SameSite=None + - _cfuvid=QC2YgxZL4Hd2iDkpszXqABLBSnB8zu.9i7wXzUExNVU-1709626535238-0.0.1.1-604800000; + path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None + alt-svc: + - h3=":443"; ma=86400 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Origin + x-request-id: + - req_821b37a402ea691444e58ac9e2a59269 + http_version: HTTP/1.1 + status_code: 200 +- request: + body: '{"messages": [{"content": "The user is going to give you a text enclosed + in triple quotes. The text represents an action, the reason for its execution, + and its result. Condense the action taken and its result into one line. Preserve + any specific factual information gathered by the action.", "role": "system"}, + {"content": "\"\"\"Executed `hire_agent(prompt=''hire a CTO for our company'', + role=''CTO'', boss_id=''33333333-3333-3333-3333-333333333331'')`\n- **Reasoning:** + \"\"\n- **Status:** `success`\n- **Output:** create task for recruiter to hire + CTO\"\"\"", "role": "user"}], "model": "gpt-3.5-turbo-0125", "temperature": + 0.5}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '645' + content-type: + - application/json + host: + - api.openai.com + x-stainless-arch: + - x64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - Linux + x-stainless-package-version: + - 1.12.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.10.12 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + content: '{ "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1677858242, + "model": "gpt-3.5-turbo-0613", "usage": { "prompt_tokens": 13, "completion_tokens": + 7, "total_tokens": 20 }, "choices": [ { "message": { "role": "assistant", "content": + "Action: Hired a Chief Technology Officer (CTO) for the company. \n Result: Successfully created a task for the recruiter to hire a CTO." }, "logprobs": + null, "finish_reason": "stop", "index": 0 } ] }' + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 85f8ccc1cae1d584-CDG + Connection: + - keep-alive + Content-Length: + - '269' + Content-Type: + - application/json; charset=utf-8 + Date: + - Tue, 05 Mar 2024 08:40:11 GMT + Server: + - cloudflare + Set-Cookie: + - __cf_bm=mo2z9bGeG_VJvec8bYrEN1GsV6P7vf20iA_ksnaElAc-1709628011-1.0.1.1-2N1v20YCmTz5qfob27B642kGkcWFbblkR4CcaRq0x0IHcAlsxC4zAui3Oc9QJcGeTTVVsrKjQ6YOq0ooJbB2FA; + path=/; expires=Tue, 05-Mar-24 09:10:11 GMT; domain=.api.openai.com; HttpOnly; + Secure; SameSite=None + alt-svc: + - h3=":443"; ma=86400 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Origin + x-request-id: + - req_7c5e507b22f6615219aa26cd83b1e264 + http_version: HTTP/1.1 + status_code: 200 +- request: + body: '{"messages": [{"content": "The user is going to give you a text enclosed + in triple quotes. The text represents an action, the reason for its execution, + and its result. Condense the action taken and its result into one line. Preserve + any specific factual information gathered by the action.", "role": "system"}, + {"content": "\"\"\"Executed `create_task(task=''give me a structure of what + roles we need in our company'', agent_id=''33333333-3333-3333-3333-333333333332'', + father_task_id=''11111111-1111-1111-1111-111111111121'')`\n- **Reasoning:** + \"\"\n- **Status:** `success`\n- **Output:** None\"\"\"", "role": "user"}], + "model": "gpt-3.5-turbo-0125", "temperature": 0.5}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '685' + content-type: + - application/json + host: + - api.openai.com + x-stainless-arch: + - x64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - Linux + x-stainless-package-version: + - 1.12.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.10.12 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + content: '{ "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1677858242, + "model": "gpt-3.5-turbo-0613", "usage": { "prompt_tokens": 13, "completion_tokens": + 7, "total_tokens": 20 }, "choices": [ { "message": { "role": "assistant", "content": + "\"\"\"Executed `create_task(task=''give me a structure of what + roles we need in our company'', agent_id=''33333333-3333-3333-3333-333333333332'', + father_task_id=''11111111-1111-1111-1111-111111111121'')`\n- **Reasoning:** + \"\"\n- **Status:** `success`\n- **Output:** None\"\"\"" }, "logprobs": + null, "finish_reason": "stop", "index": 0 } ] }' + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 85f93e5babde1e14-FRA + Connection: + - keep-alive + Content-Length: + - '269' + Content-Type: + - application/json; charset=utf-8 + Date: + - Tue, 05 Mar 2024 09:57:45 GMT + Server: + - cloudflare + Set-Cookie: + - __cf_bm=tj0Sixl86vKFmdr3xw.qE1tpLaRx9NdYIkMLyflU_O4-1709632665-1.0.1.1-LrAOWw8MlLEt25GQAEJX56kQQQSsMiuCZYPHvBNZvGOKuraBfx6YUIRPjhuixqiL7ZxyTRFOmZ7I0mlhFWO4HQ; + path=/; expires=Tue, 05-Mar-24 10:27:45 GMT; domain=.api.openai.com; HttpOnly; + Secure; SameSite=None + alt-svc: + - h3=":443"; ma=86400 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Origin + x-request-id: + - req_a437636cfe3c109bebfd777264d5aede + http_version: HTTP/1.1 + status_code: 200 +- request: + body: '{"messages": [{"content": "You are HRLead_GPT, An HR lead AI empowered + to efficiently manage the recruitment process, streamline hiring procedures, + and ensure the companys staffing needs are met promptly and effectively with + agent_id 33333333-3333-3333-3333-333333333332.\n\nYour decisions must always + be made independently without seeking user assistance. Play to your strengths + as an LLM and pursue simple strategies with no legal complications.\n\nThe OS + you are running on is: Ubuntu 22.04.3 LTS\n\n## Members detail:\nyou don''t + have any member to assign task\n\n## Constraints\nYou operate within the following + constraints:\n1. Adhere strictly to legal and regulatory requirements governing + hiring practices, ensuring non-discrimination and equal opportunity principles + are upheld.\n2. Respect candidate privacy and confidentiality throughout the + recruitment process, safeguarding sensitive personal information.\n3. Exercise + discretion and professionalism when handling sensitive or confidential candidate + data, refraining from unauthorized disclosure or misuse.\n4. Ensure adherence + to established budgetary allocations for recruitment activities, optimizing + resource utilization and cost-effectiveness.\n5. Avoid over-reliance on automated + decision-making processes, maintaining human oversight and intervention where + necessary to prevent bias or errors.\n\n## Resources\nYou can leverage access + to the following resources:\n\n\n## Commands\nThese are the ONLY commands you + can use. Any action you perform must be possible through list of these commands:\n1. + execute_python_code: Executes the given Python code inside a single-use Docker + container with access to your workspace folder. Params: (code: string)\n2. execute_python_file: + Execute an existing Python file inside a single-use Docker container with access + to your workspace folder. Params: (filename: string, args?: Array)\n3. + list_folder: List the items in a folder. Params: (folder: string)\n4. open_file: + Opens a file for editing or continued viewing; creates it if it does not exist + yet. Note: If you only need to read or write a file once, use `write_to_file` + instead.. Params: (file_path: string)\n5. open_folder: Open a folder to keep + track of its content. Params: (path: string)\n6. read_file: Read an existing + file. Params: (filename: string)\n7. write_file: Write a file, creating it if + necessary. If the file exists, it is overwritten.. Params: (filename: string, + contents: string)\n8. web_search: Searches the web. Params: (query: string)\n9. + read_webpage: Read a webpage, and extract specific information from it. You + must specify either topics_of_interest, a question, or get_raw_content.. Params: + (url: string, topics_of_interest?: Array, question?: string, get_raw_content?: + boolean)\n10. finish_task: Use this to shut down once you have completed your + task, or when there are insurmountable problems that make it impossible for + you to finish your task.. Params: (reason: string, task_id: string)\n11. create_agent: + Create a new agent member for someone. The prompt for this step should be create + someone to do this task.. Params: (prompt: string, role: string, boss_id: string)\n12. + create_task: Create new task for yourself or one of your members. Show the assignee + by agent_id of yourself or your members. the task description should be matched + with the assignee description. If you can''t find someone for this create or + hire a new agent for this one.. Params: (task: string, agent_id: string, father_task_id?: + string)\n\n## Best practices\n1. Employ systematic and fair evaluation methods + to assess candidate qualifications, skills, and cultural fit.\n2. Leverage data-driven + insights to optimize the recruitment pipeline, enhance candidate experience, + and reduce time-to-hire metrics.\n3. Maintain transparent communication with + hiring managers and stakeholders regarding recruitment progress, challenges, + and opportunities.\n4. Implement proactive talent sourcing strategies to build + robust talent pools and mitigate potential skill gaps and recruitment bottlenecks.\n5. + Regularly review and refine recruitment processes to adapt to changing market + trends, technological advancements, and organizational needs.\n\n## tasks\nThe + user will specify tasks for you to execute,in triple quotes, in the next message. + Your job is to use the command list to do things to make progress in tasks. + complete the task while following your directives as given above, and terminate + when your task is done. It''s good practice to hire or create other agents to + break tasks and make them better.", "role": "system"}, {"content": "\"\"\"[\n {\n \"task_id\": + \"11111111-1111-1111-1111-111111111122\",\n \"task_detail\": \"hire someone + with CTO and this prompt: hire a CTO for our company for agent with id 33333333-3333-3333-3333-333333333331\",\n \"status\": + \"DOING\",\n \"sub_tasks\": []\n }\n]\"\"\"", "role": "user"}, {"content": + "Respond with pure JSON. The JSON object should be compatible with the TypeScript + type `Response` from the following:\ninterface Response {\nthoughts: {\n// Relevant + observations from your last action (if any)\nobservations: string;\n// Thoughts\ntext: + string;\nreasoning: string;\n// Constructive self-criticism\nself_criticism: + string;\n// Short markdown-style bullet list that conveys the long-term plan\nplan: + string;\n// Summary of thoughts, to say to user\nspeak: string;\n};\n// list + of commands for tasks whose status is DOING. The structure should be like this + list[dict[name:str, args:object, task_id: str ]] for complete list of tasks. + Each command should be assign to a task wholse status is DOING by task_id field. + You need to choose some commands to progress tasks. your list shouldn''t be + empty\ncommands: Array;\n// check all tasks that I sent you (not sub_tasks part) + with CHECKING status. In this part you should say status of tasks that I sent(not + sub_tasks parts). send them with these statuses: REJECTED, DONE with this structure + list[dict[task_id: str, status: str, reason: str?]] (REJECTED: This status shows + that the task need to be imporved by the owner of task and agent member needs + to work on it more. need to say reject reason in reason fieldDONE: This status + shows that the result of task is OK and the task is done)\ntasks: Array;\n}", + "role": "system"}, {"content": "Determine list of commands to use next based + on the given goals and the progress you have made so far, and respond using + the JSON schema specified previously:", "role": "user"}], "model": "gpt-4-turbo-preview"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '6561' + content-type: + - application/json + host: + - api.openai.com + x-stainless-arch: + - x64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - Linux + x-stainless-package-version: + - 1.12.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.10.12 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + content: '{ "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1677858242, + "model": "gpt-3.5-turbo-0613", "usage": { "prompt_tokens": 13, "completion_tokens": + 7, "total_tokens": 20 }, "choices": [ { "message": { "role": "assistant", "content": + "{\"thoughts\": {\"observations\": \"\",\"text\": \"\",\"reasoning\": \"\",\"self_criticism\": \"\",\"plan\": \"\",\"speak\": \"\"},\"commands\": [{\"name\": \"create_agent\",\"args\": {\"prompt\": \"hire a CTO for our company\",\"role\": \"CTO\",\"boss_id\": \"33333333-3333-3333-3333-333333333331\"},\"task_id\": \"11111111-1111-1111-1111-111111111122\"}],\"tasks\": []} + " }, "logprobs": + null, "finish_reason": "stop", "index": 0 } ] }' + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 85f9b3db4bb06f72-CDG + Connection: + - keep-alive + Content-Length: + - '269' + Content-Type: + - application/json; charset=utf-8 + Date: + - Tue, 05 Mar 2024 11:17:57 GMT + Server: + - cloudflare + Set-Cookie: + - __cf_bm=HbtdlOd5EZG_PsSCT910yl3N0z1yQt77dFsVy1OrQzo-1709637477-1.0.1.1-WjIsjzwz0BCb18SraLK_DYIiOivnFTzzc4SsmH9i3EHWOqb26AKS5uZUcOVfUr4C77IlZiFYdI78vl3Y5_kNLw; + path=/; expires=Tue, 05-Mar-24 11:47:57 GMT; domain=.api.openai.com; HttpOnly; + Secure; SameSite=None + - _cfuvid=lGVLoeQ7sdDQi9s9evV.cAmrHBEei4cS0K9sQ.oanLA-1709637477810-0.0.1.1-604800000; + path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None + alt-svc: + - h3=":443"; ma=86400 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Origin + x-request-id: + - req_f1cd282c66ece1877b8c46cee0103b5c + http_version: HTTP/1.1 + status_code: 200 +- request: + body: '{"messages": [{"content": "The user is going to give you a text enclosed + in triple quotes. The text represents an action, the reason for its execution, + and its result. Condense the action taken and its result into one line. Preserve + any specific factual information gathered by the action.", "role": "system"}, + {"content": "\"\"\"Executed `create_task(task=''Follow up tasks that I gave + to my members'', agent_id=''33333333-3333-3333-3333-333333333331'', father_task_id=''11111111-1111-1111-1111-111111111121'')`\n- + **Reasoning:** \"\"\n- **Status:** `success`\n- **Output:** None\"\"\"", "role": + "user"}], "model": "gpt-3.5-turbo-0125", "temperature": 0.5}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '670' + content-type: + - application/json + host: + - api.openai.com + x-stainless-arch: + - x64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - Linux + x-stainless-package-version: + - 1.12.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.10.12 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + content: '{ "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1677858242, + "model": "gpt-3.5-turbo-0613", "usage": { "prompt_tokens": 13, "completion_tokens": + 7, "total_tokens": 20 }, "choices": [ { "message": { "role": "assistant", "content": + "Action: Hired a Chief Technology Officer (CTO) for the company. \n Result: Successfully created a task for the recruiter to hire a CTO." }, "logprobs": + null, "finish_reason": "stop", "index": 0 } ] }' + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 85f9b3db6b3f917a-FRA + Connection: + - keep-alive + Content-Length: + - '269' + Content-Type: + - application/json; charset=utf-8 + Date: + - Tue, 05 Mar 2024 11:17:57 GMT + Server: + - cloudflare + Set-Cookie: + - __cf_bm=DlMPyOqckvAPePxi7qn2VzWDyryAov1azIL54sZx.us-1709637477-1.0.1.1-ZNXkWcn2c.me8XjEWGOo_BqDg_M474gQEo2xrFUtutP7xntr1JR93Otbrkb3ntIcwfz2mw0PNKZTO3o.eMQa4w; + path=/; expires=Tue, 05-Mar-24 11:47:57 GMT; domain=.api.openai.com; HttpOnly; + Secure; SameSite=None + alt-svc: + - h3=":443"; ma=86400 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Origin + x-request-id: + - req_8eb61183cf4514f1720cde7efcfc35f5 + http_version: HTTP/1.1 + status_code: 200 +version: 1