one_shot_flow.ipynb + edits to make it work

This commit is contained in:
Reinier van der Leer
2024-05-10 20:03:40 +02:00
parent ed5f12c02b
commit ca7ca226ff
5 changed files with 756 additions and 3 deletions

View File

@@ -0,0 +1,682 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Setup"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import logging\n",
"\n",
"from dotenv import load_dotenv\n",
"\n",
"from autogpt.logs.config import configure_logging\n",
"from autogpt.core.resource.model_providers import MultiProvider\n",
"\n",
"load_dotenv()\n",
"configure_logging(debug=True)\n",
"\n",
"llm = MultiProvider()\n",
"logger = logging.getLogger()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Prompt"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2024-05-10 19:14:26,312 \u001b[90mDEBUG\u001b[0m cmd.py:1057 \u001b[90mPopen(['git', 'version'], cwd=/home/reinier/code/agpt/Auto-GPT/autogpts/autogpt/autogpt/agents/prompt_strategies, stdin=None, shell=False, universal_newlines=False)\u001b[0m\n",
"2024-05-10 19:14:26,320 \u001b[90mDEBUG\u001b[0m cmd.py:1057 \u001b[90mPopen(['git', 'version'], cwd=/home/reinier/code/agpt/Auto-GPT/autogpts/autogpt/autogpt/agents/prompt_strategies, stdin=None, shell=False, universal_newlines=False)\u001b[0m\n"
]
}
],
"source": [
"import json\n",
"import logging\n",
"import re\n",
"\n",
"from pydantic import BaseModel, Field\n",
"\n",
"from autogpt.config.ai_directives import AIDirectives\n",
"from autogpt.config.ai_profile import AIProfile\n",
"from autogpt.core.prompting import PromptStrategy\n",
"from autogpt.core.prompting.schema import ChatPrompt, LanguageModelClassification\n",
"from autogpt.core.resource.model_providers import ChatMessage\n",
"from autogpt.core.resource.model_providers.schema import AssistantChatMessage, CompletionModelFunction\n",
"from autogpt.core.utils.json_schema import JSONSchema\n",
"from autogpt.core.utils.json_utils import extract_dict_from_json\n",
"from autogpt.models.utils import ModelWithSummary\n",
"from autogpt.prompts.utils import format_numbered_list\n",
"from autogpt.utils.exceptions import InvalidAgentResponseError\n",
"\n",
"_RESPONSE_INTERFACE_NAME = \"AssistantResponse\"\n",
"\n",
"\n",
"class AssistantThoughts(ModelWithSummary):\n",
" observations: str = Field(\n",
" ..., description=\"Relevant observations from your last action (if any)\"\n",
" )\n",
" self_criticism: str = Field(..., description=\"Constructive self-criticism\")\n",
" reasoning: str = Field(..., description=\"Reasoning towards a new plan\")\n",
" plan: list[str] = Field(\n",
" ..., description=\"Short list that conveys your long-term plan. Parallelize where possible.\"\n",
" )\n",
"\n",
" def summary(self) -> str:\n",
" return self.reasoning\n",
"\n",
"\n",
"class OneShotFlowAgentActionProposal(BaseModel):\n",
" thoughts: AssistantThoughts\n",
" python_code: str = Field(\n",
" ..., description=(\n",
" \"Write Python code to execute your plan as efficiently as possible. \"\n",
" \"Try to do as much as possible without making any uninformed guesses. \"\n",
" \"Use ONLY the listed available functions and built-in Python features. \"\n",
" \"Leverage the given magic functions to implement function calls for which the \"\n",
" \"arguments can't be determined yet.\"\n",
" )\n",
" )\n",
"\n",
"FINAL_INSTRUCTION: str = (\n",
" # \"Determine exactly one command to use next based on the given goals \"\n",
" # \"and the progress you have made so far, \"\n",
" # \"and respond using the JSON schema specified previously:\"\n",
" \"Write Python code to execute your plan as efficiently as possible. \"\n",
" \"Your code will be executed directly without any editing: \"\n",
" \"if it doesn't work you will be held responsible. \"\n",
" \"Use ONLY the listed available functions and built-in Python features. \"\n",
" \"Do not make uninformed assumptions (e.g. about the content or format of an unknown file). \"\n",
" \"Leverage the given magic functions to implement function calls for which the \"\n",
" \"arguments can't be determined yet. Reduce the amount of unnecessary data passed into \"\n",
" \"these magic functions where possible, because magic costs money and magically \"\n",
" \"processing large amounts of data is expensive.\"\n",
")\n",
"\n",
"\n",
"class OneShotFlowAgentPromptStrategy(PromptStrategy):\n",
" def __init__(self):\n",
" self.response_schema = JSONSchema.from_dict(OneShotFlowAgentActionProposal.schema())\n",
" self.logger = logging.getLogger(self.__class__.__name__)\n",
"\n",
" @property\n",
" def model_classification(self) -> LanguageModelClassification:\n",
" return LanguageModelClassification.FAST_MODEL # FIXME: dynamic switching\n",
"\n",
" def build_prompt(\n",
" self,\n",
" *,\n",
" messages: list[ChatMessage],\n",
" task: str,\n",
" ai_profile: AIProfile,\n",
" ai_directives: AIDirectives,\n",
" functions: list[CompletionModelFunction],\n",
" **extras,\n",
" ) -> ChatPrompt:\n",
" \"\"\"Constructs and returns a prompt with the following structure:\n",
" 1. System prompt\n",
" 3. `cycle_instruction`\n",
" \"\"\"\n",
" system_prompt, response_prefill = self.build_system_prompt(\n",
" ai_profile=ai_profile,\n",
" ai_directives=ai_directives,\n",
" functions=functions,\n",
" )\n",
"\n",
" final_instruction_msg = ChatMessage.user(FINAL_INSTRUCTION)\n",
"\n",
" return ChatPrompt(\n",
" messages=[\n",
" ChatMessage.system(system_prompt),\n",
" ChatMessage.user(f'\"\"\"{task}\"\"\"'),\n",
" *messages,\n",
" final_instruction_msg,\n",
" ],\n",
" prefill_response=response_prefill,\n",
" )\n",
"\n",
" def build_system_prompt(\n",
" self,\n",
" ai_profile: AIProfile,\n",
" ai_directives: AIDirectives,\n",
" functions: list[CompletionModelFunction],\n",
" ) -> tuple[str, str]:\n",
" \"\"\"\n",
" Builds the system prompt.\n",
"\n",
" Returns:\n",
" str: The system prompt body\n",
" str: The desired start for the LLM's response; used to steer the output\n",
" \"\"\"\n",
" response_fmt_instruction, response_prefill = self.response_format_instruction()\n",
" system_prompt_parts = (\n",
" self._generate_intro_prompt(ai_profile)\n",
" + [\n",
" \"## Your Task\\n\"\n",
" \"The user will specify a task for you to execute, in triple quotes,\"\n",
" \" in the next message. Your job is to complete the task, \"\n",
" \"and terminate when your task is done.\"\n",
" ]\n",
" + [\n",
" \"## Available Functions\\n\"\n",
" + self._generate_function_headers(functions)\n",
" ]\n",
" + [\"## RESPONSE FORMAT\\n\" + response_fmt_instruction]\n",
" )\n",
"\n",
" # Join non-empty parts together into paragraph format\n",
" return (\n",
" \"\\n\\n\".join(filter(None, system_prompt_parts)).strip(\"\\n\"),\n",
" response_prefill,\n",
" )\n",
"\n",
" def response_format_instruction(self) -> tuple[str, str]:\n",
" response_schema = self.response_schema.copy(deep=True)\n",
"\n",
" # Unindent for performance\n",
" response_format = re.sub(\n",
" r\"\\n\\s+\",\n",
" \"\\n\",\n",
" response_schema.to_typescript_object_interface(_RESPONSE_INTERFACE_NAME),\n",
" )\n",
" response_prefill = f'{{\\n \"{list(response_schema.properties.keys())[0]}\":'\n",
"\n",
" return (\n",
" (\n",
" f\"YOU MUST ALWAYS RESPOND WITH A JSON OBJECT OF THE FOLLOWING TYPE:\\n\"\n",
" f\"{response_format}\"\n",
" ),\n",
" response_prefill,\n",
" )\n",
"\n",
" def _generate_intro_prompt(self, ai_profile: AIProfile) -> list[str]:\n",
" \"\"\"Generates the introduction part of the prompt.\n",
"\n",
" Returns:\n",
" list[str]: A list of strings forming the introduction part of the prompt.\n",
" \"\"\"\n",
" return [\n",
" f\"You are {ai_profile.ai_name}, {ai_profile.ai_role.rstrip('.')}.\",\n",
" # \"Your decisions must always be made independently without seeking \"\n",
" # \"user assistance. Play to your strengths as an LLM and pursue \"\n",
" # \"simple strategies with no legal complications.\",\n",
" ]\n",
"\n",
" def _generate_function_headers(self, funcs: list[CompletionModelFunction]) -> str:\n",
" return \"\\n\\n\".join(f.fmt_header() for f in funcs)\n",
"\n",
" def parse_response_content(\n",
" self,\n",
" response: AssistantChatMessage,\n",
" ) -> OneShotFlowAgentActionProposal:\n",
" if not response.content:\n",
" raise InvalidAgentResponseError(\"Assistant response has no text content\")\n",
"\n",
" self.logger.debug(\n",
" \"LLM response content:\"\n",
" + (\n",
" f\"\\n{response.content}\"\n",
" if \"\\n\" in response.content\n",
" else f\" '{response.content}'\"\n",
" )\n",
" )\n",
" assistant_reply_dict = extract_dict_from_json(response.content)\n",
" self.logger.debug(\n",
" \"Parsing object extracted from LLM response:\\n\"\n",
" f\"{json.dumps(assistant_reply_dict, indent=4)}\"\n",
" )\n",
"\n",
" parsed_response = OneShotFlowAgentActionProposal.parse_obj(assistant_reply_dict)\n",
" if not parsed_response.python_code:\n",
" raise ValueError(\"python_code is empty\")\n",
" return parsed_response"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"tools = [\n",
" CompletionModelFunction(\n",
" name=\"web_search\",\n",
" description=\"Searches the web\",\n",
" parameters={\n",
" \"query\": JSONSchema(\n",
" type=JSONSchema.Type.STRING,\n",
" description=\"The search query\",\n",
" required=True,\n",
" ),\n",
" \"num_results\": JSONSchema(\n",
" type=JSONSchema.Type.INTEGER,\n",
" description=\"The number of results to return\",\n",
" minimum=1,\n",
" maximum=10,\n",
" required=False,\n",
" ),\n",
" },\n",
" ),\n",
" CompletionModelFunction(\n",
" name=\"read_webpage\",\n",
" description=(\n",
" \"Read a webpage, and extract specific information from it.\"\n",
" \" You must specify either topics_of_interest,\"\n",
" \" a question, or get_raw_content.\"\n",
" ),\n",
" parameters={\n",
" \"url\": JSONSchema(\n",
" type=JSONSchema.Type.STRING,\n",
" description=\"The URL to visit\",\n",
" required=True,\n",
" ),\n",
" \"topics_of_interest\": JSONSchema(\n",
" type=JSONSchema.Type.ARRAY,\n",
" items=JSONSchema(type=JSONSchema.Type.STRING),\n",
" description=(\n",
" \"A list of topics about which you want to extract information \"\n",
" \"from the page.\"\n",
" ),\n",
" required=False,\n",
" ),\n",
" \"question\": JSONSchema(\n",
" type=JSONSchema.Type.STRING,\n",
" description=(\n",
" \"A question you want to answer using the content of the webpage.\"\n",
" ),\n",
" required=False,\n",
" ),\n",
" \"get_raw_content\": JSONSchema(\n",
" type=JSONSchema.Type.BOOLEAN,\n",
" description=(\n",
" \"If true, the unprocessed content of the webpage will be returned. \"\n",
" \"This consumes a lot of tokens, so use it with caution.\"\n",
" ),\n",
" required=False,\n",
" ),\n",
" },\n",
" ),\n",
" CompletionModelFunction(\n",
" name=\"read_file\",\n",
" description=\"Read a file and return the contents\",\n",
" parameters={\n",
" \"filename\": JSONSchema(\n",
" type=JSONSchema.Type.STRING,\n",
" description=\"The path of the file to read\",\n",
" required=True,\n",
" )\n",
" },\n",
" ),\n",
" CompletionModelFunction(\n",
" name=\"write_file\",\n",
" description=\"Write a file, creating it if necessary. If the file exists, it is overwritten.\",\n",
" parameters={\n",
" \"filename\": JSONSchema(\n",
" type=JSONSchema.Type.STRING,\n",
" description=\"The name of the file to write to\",\n",
" required=True,\n",
" ),\n",
" \"contents\": JSONSchema(\n",
" type=JSONSchema.Type.STRING,\n",
" description=\"The contents to write to the file\",\n",
" required=True,\n",
" ),\n",
" },\n",
" ),\n",
" CompletionModelFunction(\n",
" name=\"list_folder\",\n",
" description=\"List files in a folder recursively\",\n",
" parameters={\n",
" \"folder\": JSONSchema(\n",
" type=JSONSchema.Type.STRING,\n",
" description=\"The folder to list files in\",\n",
" required=True,\n",
" )\n",
" },\n",
" ),\n",
" CompletionModelFunction(\n",
" name=\"ask_user\",\n",
" description=\"If you need more details or information regarding the given task, \"\n",
" \"you can ask the user for input.\",\n",
" parameters={\n",
" \"question\": JSONSchema(\n",
" type=JSONSchema.Type.STRING,\n",
" description=\"The question or prompt to the user\",\n",
" required=True,\n",
" )\n",
" },\n",
" ),\n",
" CompletionModelFunction(\n",
" name=\"extract_value_with_ai\",\n",
" description=\"Magic function to extract information from a body with arbitrary format \"\n",
" \"into a variable of the specified type.\",\n",
" parameters={\n",
" \"input\": JSONSchema(\n",
" type=JSONSchema.Type.STRING,\n",
" required=True,\n",
" ),\n",
" \"instruction\": JSONSchema(\n",
" type=JSONSchema.Type.STRING,\n",
" description=\"Instruction on what information to extract and how to format the output\",\n",
" required=True,\n",
" ),\n",
" \"output_type\": JSONSchema(\n",
" type=JSONSchema.Type.TYPE,\n",
" default=str,\n",
" ),\n",
" },\n",
" ),\n",
" CompletionModelFunction(\n",
" name=\"extract_information\",\n",
" description=\"Extract information from a document\",\n",
" parameters={\n",
" \"document_content\": JSONSchema(\n",
" type=JSONSchema.Type.STRING,\n",
" required=True,\n",
" ),\n",
" \"topics_of_interest\": JSONSchema(\n",
" type=JSONSchema.Type.ARRAY,\n",
" items=JSONSchema(type=JSONSchema.Type.STRING),\n",
" required=True,\n",
" ),\n",
" },\n",
" ),\n",
" CompletionModelFunction(\n",
" name=\"implement_function_arguments\",\n",
" description=(\n",
" \"Magic function which, IF provided enough information, \"\n",
" \"will generate the keyword arguments necessary for a specific function.\"\n",
" ),\n",
" parameters={\n",
" \"target_function\": JSONSchema(\n",
" type=JSONSchema.Type.STRING,\n",
" description=\"The function for which to generate arguments\",\n",
" ),\n",
" \"**kwargs\": JSONSchema(\n",
" description=(\n",
" \"Any keyword arguments containing information that should be used \"\n",
" \"to determine the arguments for target_function.\"\n",
" )\n",
" ),\n",
" },\n",
" )\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"============== list ==============\n",
"Length: 4 messages\n",
"----------------- SYSTEM -----------------\n",
"You are AutoGPT, a seasoned digital assistant: capable, intelligent, considerate and assertive. You have extensive research and development skills, and you don't shy away from writing some code to solve a problem. You are pragmatic and make the most out of the tools available to you.\n",
"\n",
"## Your Task\n",
"The user will specify a task for you to execute, in triple quotes, in the next message. Your job is to complete the task, and terminate when your task is done.\n",
"\n",
"## Available Functions\n",
"def web_search(query: str, num_results: int = None):\n",
" \"\"\"\n",
" Searches the web\n",
" \n",
" Params:\n",
" query: The search query\n",
" num_results: The number of results to return\n",
" \"\"\"\n",
" ...\n",
"\n",
"def read_webpage(url: str, topics_of_interest: list[str] = None, question: str = None, get_raw_content: bool = None):\n",
" \"\"\"\n",
" Read a webpage, and extract specific information from it. You must specify either topics_of_interest, a question, or get_raw_content.\n",
" \n",
" Params:\n",
" url: The URL to visit\n",
" topics_of_interest: A list of topics about which you want to extract information from the page.\n",
" question: A question you want to answer using the content of the webpage.\n",
" get_raw_content: If true, the unprocessed content of the webpage will be returned. This consumes a lot of tokens, so use it with caution.\n",
" \"\"\"\n",
" ...\n",
"\n",
"def read_file(filename: str):\n",
" \"\"\"\n",
" Read a file and return the contents\n",
" \n",
" Params:\n",
" filename: The path of the file to read\n",
" \"\"\"\n",
" ...\n",
"\n",
"def write_file(filename: str, contents: str):\n",
" \"\"\"\n",
" Write a file, creating it if necessary. If the file exists, it is overwritten.\n",
" \n",
" Params:\n",
" filename: The name of the file to write to\n",
" contents: The contents to write to the file\n",
" \"\"\"\n",
" ...\n",
"\n",
"def list_folder(folder: str):\n",
" \"\"\"\n",
" List files in a folder recursively\n",
" \n",
" Params:\n",
" folder: The folder to list files in\n",
" \"\"\"\n",
" ...\n",
"\n",
"def ask_user(question: str):\n",
" \"\"\"\n",
" If you need more details or information regarding the given task, you can ask the user for input.\n",
" \n",
" Params:\n",
" question: The question or prompt to the user\n",
" \"\"\"\n",
" ...\n",
"\n",
"def extract_value_with_ai(input: str, instruction: str, output_type: type= <class 'str'>):\n",
" \"\"\"\n",
" Magic function to extract information from a body with arbitrary format into a variable of the specified type.\n",
" \n",
" Params:\n",
" instruction: Instruction on what information to extract and how to format the output\n",
" \"\"\"\n",
" ...\n",
"\n",
"def extract_information(document_content: str, topics_of_interest: list[str]):\n",
" \"\"\"\n",
" Extract information from a document\n",
" \n",
" Params:\n",
" \n",
" \"\"\"\n",
" ...\n",
"\n",
"def implement_function_arguments(target_function: str = None, **kwargs: Any = None):\n",
" \"\"\"\n",
" Magic function which, IF provided enough information, will generate the keyword arguments necessary for a specific function.\n",
" \n",
" Params:\n",
" target_function: The function for which to generate arguments\n",
" **kwargs: Any keyword arguments containing information that should be used to determine the arguments for target_function.\n",
" \"\"\"\n",
" ...\n",
"\n",
"## RESPONSE FORMAT\n",
"YOU MUST ALWAYS RESPOND WITH A JSON OBJECT OF THE FOLLOWING TYPE:\n",
"interface AssistantResponse {\n",
"thoughts: {\n",
"// Relevant observations from your last action (if any)\n",
"observations: string;\n",
"// Constructive self-criticism\n",
"self_criticism: string;\n",
"// Reasoning towards a new plan\n",
"reasoning: string;\n",
"// Short list that conveys your long-term plan. Parallelize where possible.\n",
"plan: Array<string>;\n",
"};\n",
"// Write Python code to execute your plan as efficiently as possible. Try to do as much as possible without making any uninformed guesses. Use ONLY the listed available functions and built-in Python features. Leverage the given magic functions to implement function calls for which the arguments can't be determined yet.\n",
"python_code: string;\n",
"}\n",
"------------------ USER ------------------\n",
"\"\"\"Figure out from file1.csv and file2.csv how much was spent on utilities\"\"\"\n",
"----------------- SYSTEM -----------------\n",
"## Context\n",
"<FolderContextItem description=\"The contents of the folder '.' in the workspace\" src=\".\" index=\"1\">\n",
"<file>file1.csv</file>\n",
"<file>file2.csv</file>\n",
"</FolderContextItem>\n",
"\n",
"When a context item is no longer needed and you are not done yet, you can hide the item by specifying its index in the list above to `close_context_item`.\n",
"------------------ USER ------------------\n",
"Write Python code to execute your plan as efficiently as possible. Your code will be executed directly without any editing: if it doesn't work you will be held responsible. Use ONLY the listed available functions and built-in Python features. Do not make uninformed assumptions (e.g. about the content or format of an unknown file). Leverage the given magic functions to implement function calls for which the arguments can't be determined yet. Reduce the amount of unnecessary data passed into these magic functions where possible, because magic costs money and magically processing large amounts of data is expensive.\n",
"==========================================\n",
"\n",
"2024-05-10 20:02:15,017 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90mclose.started\u001b[0m\n",
"2024-05-10 20:02:15,023 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90mclose.complete\u001b[0m\n",
"2024-05-10 20:02:15,027 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90mconnect_tcp.started host='api.openai.com' port=443 local_address=None timeout=5.0 socket_options=None\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"2024-05-10 20:02:15,670 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90mconnect_tcp.complete return_value=<httpcore._backends.anyio.AnyIOStream object at 0x7f4c4b5ff210>\u001b[0m\n",
"2024-05-10 20:02:15,674 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90mstart_tls.started ssl_context=<ssl.SSLContext object at 0x7f4c4b1ebf50> server_hostname='api.openai.com' timeout=5.0\u001b[0m\n",
"2024-05-10 20:02:15,867 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90mstart_tls.complete return_value=<httpcore._backends.anyio.AnyIOStream object at 0x7f4c4a7f3490>\u001b[0m\n",
"2024-05-10 20:02:15,870 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90msend_request_headers.started request=<Request [b'POST']>\u001b[0m\n",
"2024-05-10 20:02:15,874 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90msend_request_headers.complete\u001b[0m\n",
"2024-05-10 20:02:15,876 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90msend_request_body.started request=<Request [b'POST']>\u001b[0m\n",
"2024-05-10 20:02:15,880 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90msend_request_body.complete\u001b[0m\n",
"2024-05-10 20:02:15,882 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90mreceive_response_headers.started request=<Request [b'POST']>\u001b[0m\n",
"2024-05-10 20:02:27,268 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90mreceive_response_headers.complete return_value=(b'HTTP/1.1', 200, b'OK', [(b'Date', b'Fri, 10 May 2024 18:02:26 GMT'), (b'Content-Type', b'application/json'), (b'Transfer-Encoding', b'chunked'), (b'Connection', b'keep-alive'), (b'openai-organization', b'significant-gravitas'), (b'openai-processing-ms', b'11153'), (b'openai-version', b'2020-10-01'), (b'strict-transport-security', b'max-age=15724800; includeSubDomains'), (b'x-ratelimit-limit-requests', b'10000'), (b'x-ratelimit-limit-tokens', b'2000000'), (b'x-ratelimit-remaining-requests', b'9999'), (b'x-ratelimit-remaining-tokens', b'1998752'), (b'x-ratelimit-reset-requests', b'6ms'), (b'x-ratelimit-reset-tokens', b'37ms'), (b'x-request-id', b'req_afca7c19c817b1461b037f3ac5ded1ab'), (b'CF-Cache-Status', b'DYNAMIC'), (b'Server', b'cloudflare'), (b'CF-RAY', b'881bd6d27c0b427c-EWR'), (b'Content-Encoding', b'gzip'), (b'alt-svc', b'h3=\":443\"; ma=86400')])\u001b[0m\n",
"2024-05-10 20:02:27,270 \u001b[34mINFO\u001b[0m _client.py:1729 HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n",
"2024-05-10 20:02:27,273 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90mreceive_response_body.started request=<Request [b'POST']>\u001b[0m\n",
"2024-05-10 20:02:27,276 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90mreceive_response_body.complete\u001b[0m\n",
"2024-05-10 20:02:27,277 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90mresponse_closed.started\u001b[0m\n",
"2024-05-10 20:02:27,280 \u001b[90mDEBUG\u001b[0m _trace.py:85 \u001b[90mresponse_closed.complete\u001b[0m\n",
"2024-05-10 20:02:27,283 \u001b[90mDEBUG\u001b[0m openai.py:638 \u001b[90mCompletion usage: 1050 input, 196 output - $0.01638\u001b[0m\n",
"2024-05-10 20:02:27,288 \u001b[90mDEBUG\u001b[0m 1160824689.py:182 \u001b[90mLLM response content:\n",
"{\n",
" \"thoughts\": {\n",
" \"observations\": \"The task requires extracting the amount spent on utilities from two CSV files, file1.csv and file2.csv.\",\n",
" \"self_criticism\": \"Direct assumptions about the format and data organization in the CSV files were avoided, which is necessary.\",\n",
" \"reasoning\": \"First, we need to read and analyze the contents of both files. Once we understand the format and where the relevant data (utilities) is located, we can then extract the specific details and sum them up to get the total utilities expenditure.\",\n",
" \"plan\": [\n",
" \"Read contents of file1.csv\",\n",
" \"Read contents of file2.csv\",\n",
" \"Extract information about utilities expenditure from both files\",\n",
" \"Sum up the amounts spent on utilities from both files\"\n",
" ]\n",
" },\n",
" \"python_code\": \"file1_content = read_file('file1.csv')\\nfile2_content = read_file('file2.csv')\"\n",
"}\u001b[0m\n",
"2024-05-10 20:02:27,294 \u001b[90mDEBUG\u001b[0m 1160824689.py:191 \u001b[90mParsing object extracted from LLM response:\n",
"{\n",
" \"thoughts\": {\n",
" \"observations\": \"The task requires extracting the amount spent on utilities from two CSV files, file1.csv and file2.csv.\",\n",
" \"self_criticism\": \"Direct assumptions about the format and data organization in the CSV files were avoided, which is necessary.\",\n",
" \"reasoning\": \"First, we need to read and analyze the contents of both files. Once we understand the format and where the relevant data (utilities) is located, we can then extract the specific details and sum them up to get the total utilities expenditure.\",\n",
" \"plan\": [\n",
" \"Read contents of file1.csv\",\n",
" \"Read contents of file2.csv\",\n",
" \"Extract information about utilities expenditure from both files\",\n",
" \"Sum up the amounts spent on utilities from both files\"\n",
" ]\n",
" },\n",
" \"python_code\": \"file1_content = read_file('file1.csv')\\nfile2_content = read_file('file2.csv')\"\n",
"}\u001b[0m\n",
"file1_content = read_file('file1.csv')\n",
"file2_content = read_file('file2.csv')\n"
]
}
],
"source": [
"from autogpt.core.resource.model_providers.anthropic import AnthropicModelName\n",
"from autogpt.core.resource.model_providers.openai import OpenAIModelName\n",
"from autogpt.core.runner.client_lib.logging.helpers import dump_prompt\n",
"\n",
"prompter = OneShotFlowAgentPromptStrategy()\n",
"\n",
"prompt = prompter.build_prompt(\n",
" task=\"Figure out from file1.csv and file2.csv how much was spent on utilities\",\n",
" messages=[\n",
" ChatMessage.system(\n",
" \"## Context\\n\"\n",
" '<FolderContextItem '\n",
" 'description=\"The contents of the folder \\'.\\' in the workspace\" '\n",
" 'src=\".\" index=\"1\">\\n'\n",
" \"<file>file1.csv</file>\\n\"\n",
" \"<file>file2.csv</file>\\n\"\n",
" \"</FolderContextItem>\"\n",
" \"\\n\\n\"\n",
" \"When a context item is no longer needed and you are not done yet, \"\n",
" \"you can hide the item by specifying its index in the list above \"\n",
" f\"to `close_context_item`.\",\n",
" ),\n",
" ],\n",
" ai_profile=AIProfile(),\n",
" ai_directives=AIDirectives(),\n",
" functions=tools,\n",
")\n",
"print(dump_prompt(prompt))\n",
"\n",
"result = await llm.create_chat_completion(\n",
" # model_name=AnthropicModelName.CLAUDE3_OPUS_v1,\n",
" model_name=OpenAIModelName.GPT4_TURBO,\n",
" model_prompt=prompt.messages,\n",
" prefill_response=prompt.prefill_response,\n",
" completion_parser=prompter.parse_response_content,\n",
")\n",
"\n",
"print(result.parsed_result.python_code)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@@ -30,7 +30,7 @@ class UserInteractionComponent(CommandProvider):
},
)
def ask_user(self, question: str) -> str:
"""If you need more details or information regarding the given goals,
"""If you need more details or information regarding the given task,
you can ask the user for input."""
print(f"\nQ: {question}")
resp = clean_input(self.config, "A:")

View File

@@ -69,7 +69,7 @@ class Config(SystemSettings, arbitrary_types_allowed=True):
default=AI_SETTINGS_FILE, from_env="AI_SETTINGS_FILE"
)
prompt_settings_file: Path = UserConfigurable(
default=PROMPT_SETTINGS_FILE,
default=project_root / PROMPT_SETTINGS_FILE,
from_env="PROMPT_SETTINGS_FILE",
)

View File

@@ -29,6 +29,7 @@ from autogpt.core.resource.schema import (
)
from autogpt.core.utils.json_schema import JSONSchema
from autogpt.logs.utils import fmt_kwargs
from autogpt.prompts.utils import indent
if TYPE_CHECKING:
from jsonschema import ValidationError
@@ -158,6 +159,38 @@ class CompletionModelFunction(BaseModel):
)
return f"{self.name}: {self.description}. Params: ({params})"
def fmt_header(self) -> str:
"""
Formats and returns the function header as a string with types and descriptions.
Returns:
str: The formatted function header.
"""
params = ", ".join(
f"{name}: {p.python_type}{f'= {str(p.default)}' if p.default else ' = None' if not p.required else ''}"
for name, p in self.parameters.items()
)
return (
f"def {self.name}({params}):\n" # TODO: add return type
+ indent(
(
'"""\n'
f"{self.description}\n\n"
"Params:\n"
+ indent(
"\n".join(
f"{name}: {param.description}"
for name, param in self.parameters.items()
if param.description
)
) + "\n"
'"""\n'
"..."
),
4,
)
)
def validate_call(
self, function_call: AssistantFunctionCall
) -> tuple[bool, list["ValidationError"]]:

View File

@@ -1,6 +1,7 @@
import ast
import enum
from textwrap import indent
from typing import Optional
from typing import Any, Optional
from jsonschema import Draft7Validator, ValidationError
from pydantic import BaseModel
@@ -14,12 +15,14 @@ class JSONSchema(BaseModel):
NUMBER = "number"
INTEGER = "integer"
BOOLEAN = "boolean"
TYPE = "type"
# TODO: add docstrings
description: Optional[str] = None
type: Optional[Type] = None
enum: Optional[list] = None
required: bool = False
default: Any = None
items: Optional["JSONSchema"] = None
properties: Optional[dict[str, "JSONSchema"]] = None
minimum: Optional[int | float] = None
@@ -31,6 +34,7 @@ class JSONSchema(BaseModel):
schema: dict = {
"type": self.type.value if self.type else None,
"description": self.description,
"default": repr(self.default),
}
if self.type == "array":
if self.items:
@@ -85,6 +89,7 @@ class JSONSchema(BaseModel):
return JSONSchema(
description=schema.get("description"),
type=schema["type"],
default=ast.literal_eval(d) if (d := schema.get("default")) else None,
enum=schema.get("enum"),
items=JSONSchema.from_dict(schema["items"]) if "items" in schema else None,
properties=JSONSchema.parse_properties(schema)
@@ -145,6 +150,35 @@ class JSONSchema(BaseModel):
f"interface {interface_name} " if interface_name else ""
) + f"{{\n{indent(attributes_string, ' ')}\n}}"
@property
def python_type(self) -> str:
if self.type == JSONSchema.Type.BOOLEAN:
return "bool"
elif self.type in {JSONSchema.Type.INTEGER}:
return "int"
elif self.type == JSONSchema.Type.NUMBER:
return "float"
elif self.type == JSONSchema.Type.STRING:
return "str"
elif self.type == JSONSchema.Type.ARRAY:
return f"list[{self.items.python_type}]" if self.items else "list"
elif self.type == JSONSchema.Type.OBJECT:
if not self.properties:
return "dict"
raise NotImplementedError(
"JSONSchema.python_type doesn't support TypedDicts yet"
)
elif self.enum:
return "Union[" + ", ".join(repr(v) for v in self.enum) + "]"
elif self.type == JSONSchema.Type.TYPE:
return "type"
elif self.type is None:
return "Any"
else:
raise NotImplementedError(
f"JSONSchema.python_type does not support Type.{self.type.name} yet"
)
@property
def typescript_type(self) -> str:
if self.type == JSONSchema.Type.BOOLEAN:
@@ -161,6 +195,10 @@ class JSONSchema(BaseModel):
return self.to_typescript_object_interface()
elif self.enum:
return " | ".join(repr(v) for v in self.enum)
elif self.type == JSONSchema.Type.TYPE:
return "type"
elif self.type is None:
return "any"
else:
raise NotImplementedError(
f"JSONSchema.typescript_type does not support Type.{self.type.name} yet"