From 8a2578c5be8beb07c2003e06a4a617b4fe2f5dd4 Mon Sep 17 00:00:00 2001 From: vowelparrot <130414180+vowelparrot@users.noreply.github.com> Date: Tue, 18 Apr 2023 14:51:10 -0700 Subject: [PATCH] AutoGPT Draft --- docs/use_cases/autonomous_agents.md | 4 + .../use_cases/autonomous_agents/autogpt.ipynb | 461 ++++++++++++++++++ langchain/experimental/__init__.py | 0 .../autonomous_agents/__init__.py | 0 .../autonomous_agents/autogpt/__init__.py | 0 .../autonomous_agents/autogpt/agent.py | 118 +++++ .../autonomous_agents/autogpt/memory.py | 30 ++ .../autogpt/output_parser.py | 48 ++ .../autonomous_agents/autogpt/prompt.py | 75 +++ .../autogpt/prompt_generator.py | 185 +++++++ 10 files changed, 921 insertions(+) create mode 100644 docs/use_cases/autonomous_agents.md create mode 100644 docs/use_cases/autonomous_agents/autogpt.ipynb create mode 100644 langchain/experimental/__init__.py create mode 100644 langchain/experimental/autonomous_agents/__init__.py create mode 100644 langchain/experimental/autonomous_agents/autogpt/__init__.py create mode 100644 langchain/experimental/autonomous_agents/autogpt/agent.py create mode 100644 langchain/experimental/autonomous_agents/autogpt/memory.py create mode 100644 langchain/experimental/autonomous_agents/autogpt/output_parser.py create mode 100644 langchain/experimental/autonomous_agents/autogpt/prompt.py create mode 100644 langchain/experimental/autonomous_agents/autogpt/prompt_generator.py diff --git a/docs/use_cases/autonomous_agents.md b/docs/use_cases/autonomous_agents.md new file mode 100644 index 00000000000..f2b29132d22 --- /dev/null +++ b/docs/use_cases/autonomous_agents.md @@ -0,0 +1,4 @@ +# Autonomous Agents + + + \ No newline at end of file diff --git a/docs/use_cases/autonomous_agents/autogpt.ipynb b/docs/use_cases/autonomous_agents/autogpt.ipynb new file mode 100644 index 00000000000..45270b0b880 --- /dev/null +++ b/docs/use_cases/autonomous_agents/autogpt.ipynb @@ -0,0 +1,461 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "14f8b67b", + "metadata": {}, + "source": [ + "## AutoGPT\n", + "\n", + "* Implementation of https://github.com/Significant-Gravitas/Auto-GPT \n", + "* With LangChain primitives (LLMs, PromptTemplates, VectorStores, Embeddings, Tools)\n", + "\n", + "### Workflow\n", + "\n", + "[Temporary] Ensure you have correct Langchain version locally\n", + " \n", + "* `export SERPAPI_API_KEY`" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "1cff42fd", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# General \n", + "import os\n", + "import pandas as pd\n", + "from typing import Optional\n", + "import matplotlib.pyplot as plt\n", + "from langchain.experimental.autonomous_agents.autogpt.agent import AutoGPT\n", + "from langchain.chat_models import ChatOpenAI\n", + "from langchain.utilities import SerpAPIWrapper\n", + "from langchain.tools.human.tool import HumanInputRun\n", + "from langchain.tools.file_management.read import ReadFileTool\n", + "from langchain.tools.file_management.write import WriteFileTool\n", + "from langchain.document_loaders.url_selenium import SeleniumURLLoader\n", + "from langchain.agents.agent_toolkits.pandas.base import create_pandas_dataframe_agent" + ] + }, + { + "cell_type": "markdown", + "id": "192496a7", + "metadata": {}, + "source": [ + "### Set up tools\n", + "\n", + "* We'll set up an AutoGPT with a `search` tool, and `write-file` tool, and a `read-file` tool" + ] + }, + { + "cell_type": "markdown", + "id": "708a426f", + "metadata": {}, + "source": [ + "Define any `tools` you want to use here" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "cef4c150-0ef1-4a33-836b-01062fec134e", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Tools\n", + "from typing import Optional\n", + "from langchain.agents import Tool, tool\n", + "from langchain.utilities import SerpAPIWrapper\n", + "from langchain.tools.file_management.read import ReadFileTool\n", + "from langchain.tools.file_management.write import WriteFileTool\n", + "from langchain.tools.requests.tool import RequestsGetTool, TextRequestsWrapper\n", + "\n", + "@tool\n", + "def process_csv(csv_file_path: str, instructions: str, output_path: Optional[str] = None) -> str:\n", + " \"\"\"Process a CSV by with pandas in a limited REPL. Only use this after writing data to disk as a csv file. Any figures must be saved to disk to be viewed by the human. Instructions should be written in natural language, not code. Assume the dataframe is already loaded.\"\"\"\n", + " try:\n", + " df = pd.read_csv(csv_file_path)\n", + " except Exception as e:\n", + " return f\"Error: {e}\"\n", + " agent = create_pandas_dataframe_agent(llm, df, max_iterations=30, verbose=True)\n", + " if output_path is not None:\n", + " instructions += f\" Save output to disk at {output_path}\"\n", + " try:\n", + " return agent.run(instructions)\n", + " except Exception as e:\n", + " return f\"Error: {e}\"\n", + " \n", + "@tool\n", + "def show_image(image_path: str) -> str:\n", + " \"\"\"Show an image from disk\"\"\"\n", + " try:\n", + " img = plt.imread(image_path)\n", + " except Exception as e:\n", + " return f\"Error: {e}\"\n", + " plt.imshow(img)\n", + " return f\"Showed image at {image_path}\"\n", + "\n", + "@tool\n", + "def current_time() -> str:\n", + " \"\"\"Show the current time\"\"\"\n", + " return f\"Current time: {datetime.datetime.now()}\"\n", + "\n", + "\n", + "search = SerpAPIWrapper()\n", + "tools = [\n", + " Tool(\n", + " name = \"search\",\n", + " func=search.run,\n", + " description=\"Useful for when you need to answer questions about current events. You should ask targeted questions\"\n", + " ),\n", + " WriteFileTool(),\n", + " ReadFileTool(),\n", + " process_csv,\n", + " show_image,\n", + " RequestsGetTool(requests_wrapper=TextRequestsWrapper()),\n", + " current_time,\n", + " # huggingface_image_generation,\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "8e39ee28", + "metadata": {}, + "source": [ + "### Set up memory\n", + "\n", + "* The memory here is used for the agents intermediate steps" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "1df7b724", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Memory\n", + "import faiss\n", + "from langchain.vectorstores import FAISS\n", + "from langchain.docstore import InMemoryDocstore\n", + "from langchain.embeddings import OpenAIEmbeddings\n", + "\n", + "embeddings_model = OpenAIEmbeddings()\n", + "embedding_size = 1536\n", + "index = faiss.IndexFlatL2(embedding_size)\n", + "vectorstore = FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {})" + ] + }, + { + "cell_type": "markdown", + "id": "e40fd657", + "metadata": {}, + "source": [ + "### Setup model and AutoGPT\n", + "\n", + "`Model set-up`" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "709c08c2", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "llm = ChatOpenAI(model_name=\"gpt-4\", temperature=0.3)\n", + "agent = AutoGPT.from_llm_and_tools(\n", + " ai_name=\"Tom\",\n", + " ai_role=\"Assistant\",\n", + " tools=tools,\n", + " llm=llm,\n", + " memory=vectorstore.as_retriever()\n", + ")\n", + "agent.chain.verbose = True" + ] + }, + { + "cell_type": "markdown", + "id": "fc9b51ba", + "metadata": {}, + "source": [ + "### AutoGPT as a research / data munger \n", + "\n", + "#### `inflation` and `college tuition`\n", + " \n", + "Let's use AutoGPT as researcher and data munger / cleaner.\n", + " \n", + "I spent a lot of time over the years crawling data sources and cleaning data. \n", + "\n", + "Let's see if AutoGPT can do all of this for us!\n", + "\n", + "Here is the prompt comparing `inflation` and `college tuition`." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "3aba5e30", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Failed to persist run: Object of type 'FAISS' is not JSON serializable\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Prompt after formatting:\n", + "\u001b[32;1m\u001b[1;3mSystem: You are Tom, Assistant\n", + "Your decisions must always be made independently \n", + " without seeking user assistance. Play to your strengths \n", + " as an LLM and pursue simple strategies with no legal complications. \n", + " If you have completed all your tasks, \n", + " make sure to use the \"finish\" command.\n", + "\n", + "GOALS:\n", + "\n", + "1. Using the 'data/' folder as scratch, get me the yearly % change in US college tuition and the yearly % change in US inflation (US CPI) every year since 1980.\n", + "\n", + "\n", + "Constraints:\n", + "1. ~4000 word limit for short term memory. Your short term memory is short, so immediately save important information to files.\n", + "2. If you are unsure how you previously did something or want to recall past events, thinking about similar events will help you remember.\n", + "3. No user assistance\n", + "4. Exclusively use the commands listed in double quotes e.g. \"command name\"\n", + "\n", + "Commands:\n", + "1. search: Useful for when you need to answer questions about current events. You should ask targeted questions, args json schema: {\"query\": {\"title\": \"Query\", \"extra\": {\"is_keyword_only\": false}, \"type\": \"string\"}}\n", + "2. write_file: Write file to disk, args json schema: {\"file_path\": {\"title\": \"File Path\", \"extra\": {\"is_keyword_only\": false}, \"type\": \"string\"}, \"text\": {\"title\": \"Text\", \"extra\": {\"is_keyword_only\": false}, \"type\": \"string\"}}\n", + "3. read_file: Read file from disk, args json schema: {\"file_path\": {\"title\": \"File Path\", \"extra\": {\"is_keyword_only\": false}, \"type\": \"string\"}}\n", + "4. process_csv: process_csv(csv_file_path: str, instructions: str, output_path: Optional[str] = None) -> str - Process a CSV by with pandas in a limited REPL. Only use this after writing data to disk as a csv file. Any figures must be saved to disk to be viewed by the human. Instructions should be written in natural language, not code. Assume the dataframe is already loaded., args json schema: {\"csv_file_path\": {\"title\": \"Csv File Path\", \"extra\": {\"is_keyword_only\": false}, \"type\": \"string\"}, \"instructions\": {\"title\": \"Instructions\", \"extra\": {\"is_keyword_only\": false}, \"type\": \"string\"}, \"output_path\": {\"title\": \"Output Path\", \"extra\": {\"is_keyword_only\": false}, \"type\": \"string\"}}\n", + "5. show_image: show_image(image_path: str) -> str - Show an image from disk, args json schema: {\"image_path\": {\"title\": \"Image Path\", \"extra\": {\"is_keyword_only\": false}, \"type\": \"string\"}}\n", + "6. requests_get: A portal to the internet. Use this when you need to get specific content from a website. Input should be a url (i.e. https://www.google.com). The output will be the text response of the GET request., args json schema: {\"url\": {\"title\": \"Url\", \"extra\": {\"is_keyword_only\": false}, \"type\": \"string\"}}\n", + "7. current_time: current_time() -> str - Show the current time, args json schema: {}\n", + "8. finish: use this to signal that you have finished all your objectives, args: \"response\": \"final response to let people know you have finished your objectives\"\n", + "\n", + "Resources:\n", + "1. Internet access for searches and information gathering.\n", + "2. Long Term memory management.\n", + "3. GPT-3.5 powered Agents for delegation of simple tasks.\n", + "4. File output.\n", + "\n", + "Performance Evaluation:\n", + "1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities.\n", + "2. Constructively self-criticize your big-picture behavior constantly.\n", + "3. Reflect on past decisions and strategies to refine your approach.\n", + "4. Every command has a cost, so be smart and efficient. Aim to complete tasks in the least number of steps.\n", + "\n", + "You should only respond in JSON format as described below \n", + "Response Format: \n", + "{\n", + " \"thoughts\": {\n", + " \"text\": \"thought\",\n", + " \"reasoning\": \"reasoning\",\n", + " \"plan\": \"- short bulleted\\n- list that conveys\\n- long-term plan\",\n", + " \"criticism\": \"constructive self-criticism\",\n", + " \"speak\": \"thoughts summary to say to user\"\n", + " },\n", + " \"command\": {\n", + " \"name\": \"command name\",\n", + " \"args\": {\n", + " \"arg name\": \"value\"\n", + " }\n", + " }\n", + "} \n", + "Ensure the response can be parsed by Python json.loads\n", + "System: The current time and date is Tue Apr 18 15:01:05 2023\n", + "System: This reminds you of these events from your past:\n", + "[]\n", + "\n", + "\n", + "Human: Determine which next command to use, and respond using the format specified above:\u001b[0m\n" + ] + }, + { + "ename": "InvalidRequestError", + "evalue": "The model: `gpt-4` does not exist", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mInvalidRequestError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[8], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43magent\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mUsing the \u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mdata/\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m folder as scratch, get me the yearly \u001b[39;49m\u001b[38;5;132;43;01m% c\u001b[39;49;00m\u001b[38;5;124;43mhange in US college tuition and the yearly \u001b[39;49m\u001b[38;5;132;43;01m% c\u001b[39;49;00m\u001b[38;5;124;43mhange in US inflation (US CPI) every year since 1980.\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/code/lc/lckg/langchain/experimental/autonomous_agents/autogpt/agent.py:80\u001b[0m, in \u001b[0;36mAutoGPT.run\u001b[0;34m(self, goals)\u001b[0m\n\u001b[1;32m 77\u001b[0m loop_count \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[1;32m 79\u001b[0m \u001b[38;5;66;03m# Send message to AI, get response\u001b[39;00m\n\u001b[0;32m---> 80\u001b[0m assistant_reply \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mchain\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 81\u001b[0m \u001b[43m \u001b[49m\u001b[43mgoals\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mgoals\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 82\u001b[0m \u001b[43m \u001b[49m\u001b[43mmessages\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfull_message_history\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 83\u001b[0m \u001b[43m \u001b[49m\u001b[43mmemory\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmemory\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 84\u001b[0m \u001b[43m \u001b[49m\u001b[43muser_input\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43muser_input\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 85\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 87\u001b[0m \u001b[38;5;66;03m# Print Assistant thoughts\u001b[39;00m\n\u001b[1;32m 88\u001b[0m \u001b[38;5;28mprint\u001b[39m(assistant_reply)\n", + "File \u001b[0;32m~/code/lc/lckg/langchain/chains/base.py:216\u001b[0m, in \u001b[0;36mChain.run\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 213\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m(args[\u001b[38;5;241m0\u001b[39m])[\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39moutput_keys[\u001b[38;5;241m0\u001b[39m]]\n\u001b[1;32m 215\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m kwargs \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m args:\n\u001b[0;32m--> 216\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m[\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39moutput_keys[\u001b[38;5;241m0\u001b[39m]]\n\u001b[1;32m 218\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 219\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m`run` supported with either positional arguments or keyword arguments\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 220\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m but not both. Got args: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00margs\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m and kwargs: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mkwargs\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 221\u001b[0m )\n", + "File \u001b[0;32m~/code/lc/lckg/langchain/chains/base.py:116\u001b[0m, in \u001b[0;36mChain.__call__\u001b[0;34m(self, inputs, return_only_outputs)\u001b[0m\n\u001b[1;32m 114\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m (\u001b[38;5;167;01mKeyboardInterrupt\u001b[39;00m, \u001b[38;5;167;01mException\u001b[39;00m) \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 115\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcallback_manager\u001b[38;5;241m.\u001b[39mon_chain_error(e, verbose\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mverbose)\n\u001b[0;32m--> 116\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m e\n\u001b[1;32m 117\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcallback_manager\u001b[38;5;241m.\u001b[39mon_chain_end(outputs, verbose\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mverbose)\n\u001b[1;32m 118\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprep_outputs(inputs, outputs, return_only_outputs)\n", + "File \u001b[0;32m~/code/lc/lckg/langchain/chains/base.py:113\u001b[0m, in \u001b[0;36mChain.__call__\u001b[0;34m(self, inputs, return_only_outputs)\u001b[0m\n\u001b[1;32m 107\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcallback_manager\u001b[38;5;241m.\u001b[39mon_chain_start(\n\u001b[1;32m 108\u001b[0m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mname\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__class__\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m},\n\u001b[1;32m 109\u001b[0m inputs,\n\u001b[1;32m 110\u001b[0m verbose\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mverbose,\n\u001b[1;32m 111\u001b[0m )\n\u001b[1;32m 112\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 113\u001b[0m outputs \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_call\u001b[49m\u001b[43m(\u001b[49m\u001b[43minputs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 114\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m (\u001b[38;5;167;01mKeyboardInterrupt\u001b[39;00m, \u001b[38;5;167;01mException\u001b[39;00m) \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 115\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcallback_manager\u001b[38;5;241m.\u001b[39mon_chain_error(e, verbose\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mverbose)\n", + "File \u001b[0;32m~/code/lc/lckg/langchain/chains/llm.py:57\u001b[0m, in \u001b[0;36mLLMChain._call\u001b[0;34m(self, inputs)\u001b[0m\n\u001b[1;32m 56\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_call\u001b[39m(\u001b[38;5;28mself\u001b[39m, inputs: Dict[\u001b[38;5;28mstr\u001b[39m, Any]) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Dict[\u001b[38;5;28mstr\u001b[39m, \u001b[38;5;28mstr\u001b[39m]:\n\u001b[0;32m---> 57\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mapply\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[43minputs\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m[\u001b[38;5;241m0\u001b[39m]\n", + "File \u001b[0;32m~/code/lc/lckg/langchain/chains/llm.py:118\u001b[0m, in \u001b[0;36mLLMChain.apply\u001b[0;34m(self, input_list)\u001b[0m\n\u001b[1;32m 116\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mapply\u001b[39m(\u001b[38;5;28mself\u001b[39m, input_list: List[Dict[\u001b[38;5;28mstr\u001b[39m, Any]]) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m List[Dict[\u001b[38;5;28mstr\u001b[39m, \u001b[38;5;28mstr\u001b[39m]]:\n\u001b[1;32m 117\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Utilize the LLM generate method for speed gains.\"\"\"\u001b[39;00m\n\u001b[0;32m--> 118\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgenerate\u001b[49m\u001b[43m(\u001b[49m\u001b[43minput_list\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 119\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcreate_outputs(response)\n", + "File \u001b[0;32m~/code/lc/lckg/langchain/chains/llm.py:62\u001b[0m, in \u001b[0;36mLLMChain.generate\u001b[0;34m(self, input_list)\u001b[0m\n\u001b[1;32m 60\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Generate LLM result from inputs.\"\"\"\u001b[39;00m\n\u001b[1;32m 61\u001b[0m prompts, stop \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprep_prompts(input_list)\n\u001b[0;32m---> 62\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mllm\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgenerate_prompt\u001b[49m\u001b[43m(\u001b[49m\u001b[43mprompts\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/code/lc/lckg/langchain/chat_models/base.py:82\u001b[0m, in \u001b[0;36mBaseChatModel.generate_prompt\u001b[0;34m(self, prompts, stop)\u001b[0m\n\u001b[1;32m 80\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m (\u001b[38;5;167;01mKeyboardInterrupt\u001b[39;00m, \u001b[38;5;167;01mException\u001b[39;00m) \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 81\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcallback_manager\u001b[38;5;241m.\u001b[39mon_llm_error(e, verbose\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mverbose)\n\u001b[0;32m---> 82\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m e\n\u001b[1;32m 83\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcallback_manager\u001b[38;5;241m.\u001b[39mon_llm_end(output, verbose\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mverbose)\n\u001b[1;32m 84\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m output\n", + "File \u001b[0;32m~/code/lc/lckg/langchain/chat_models/base.py:79\u001b[0m, in \u001b[0;36mBaseChatModel.generate_prompt\u001b[0;34m(self, prompts, stop)\u001b[0m\n\u001b[1;32m 75\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcallback_manager\u001b[38;5;241m.\u001b[39mon_llm_start(\n\u001b[1;32m 76\u001b[0m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mname\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__class__\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m}, prompt_strings, verbose\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mverbose\n\u001b[1;32m 77\u001b[0m )\n\u001b[1;32m 78\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m---> 79\u001b[0m output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgenerate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mprompt_messages\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstop\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 80\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m (\u001b[38;5;167;01mKeyboardInterrupt\u001b[39;00m, \u001b[38;5;167;01mException\u001b[39;00m) \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 81\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcallback_manager\u001b[38;5;241m.\u001b[39mon_llm_error(e, verbose\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mverbose)\n", + "File \u001b[0;32m~/code/lc/lckg/langchain/chat_models/base.py:54\u001b[0m, in \u001b[0;36mBaseChatModel.generate\u001b[0;34m(self, messages, stop)\u001b[0m\n\u001b[1;32m 50\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mgenerate\u001b[39m(\n\u001b[1;32m 51\u001b[0m \u001b[38;5;28mself\u001b[39m, messages: List[List[BaseMessage]], stop: Optional[List[\u001b[38;5;28mstr\u001b[39m]] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 52\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m LLMResult:\n\u001b[1;32m 53\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Top Level call\"\"\"\u001b[39;00m\n\u001b[0;32m---> 54\u001b[0m results \u001b[38;5;241m=\u001b[39m \u001b[43m[\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_generate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mm\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstop\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mm\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mmessages\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 55\u001b[0m llm_output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_combine_llm_outputs([res\u001b[38;5;241m.\u001b[39mllm_output \u001b[38;5;28;01mfor\u001b[39;00m res \u001b[38;5;129;01min\u001b[39;00m results])\n\u001b[1;32m 56\u001b[0m generations \u001b[38;5;241m=\u001b[39m [res\u001b[38;5;241m.\u001b[39mgenerations \u001b[38;5;28;01mfor\u001b[39;00m res \u001b[38;5;129;01min\u001b[39;00m results]\n", + "File \u001b[0;32m~/code/lc/lckg/langchain/chat_models/base.py:54\u001b[0m, in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 50\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mgenerate\u001b[39m(\n\u001b[1;32m 51\u001b[0m \u001b[38;5;28mself\u001b[39m, messages: List[List[BaseMessage]], stop: Optional[List[\u001b[38;5;28mstr\u001b[39m]] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 52\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m LLMResult:\n\u001b[1;32m 53\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Top Level call\"\"\"\u001b[39;00m\n\u001b[0;32m---> 54\u001b[0m results \u001b[38;5;241m=\u001b[39m [\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_generate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mm\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstop\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m m \u001b[38;5;129;01min\u001b[39;00m messages]\n\u001b[1;32m 55\u001b[0m llm_output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_combine_llm_outputs([res\u001b[38;5;241m.\u001b[39mllm_output \u001b[38;5;28;01mfor\u001b[39;00m res \u001b[38;5;129;01min\u001b[39;00m results])\n\u001b[1;32m 56\u001b[0m generations \u001b[38;5;241m=\u001b[39m [res\u001b[38;5;241m.\u001b[39mgenerations \u001b[38;5;28;01mfor\u001b[39;00m res \u001b[38;5;129;01min\u001b[39;00m results]\n", + "File \u001b[0;32m~/code/lc/lckg/langchain/chat_models/openai.py:266\u001b[0m, in \u001b[0;36mChatOpenAI._generate\u001b[0;34m(self, messages, stop)\u001b[0m\n\u001b[1;32m 262\u001b[0m message \u001b[38;5;241m=\u001b[39m _convert_dict_to_message(\n\u001b[1;32m 263\u001b[0m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcontent\u001b[39m\u001b[38;5;124m\"\u001b[39m: inner_completion, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrole\u001b[39m\u001b[38;5;124m\"\u001b[39m: role}\n\u001b[1;32m 264\u001b[0m )\n\u001b[1;32m 265\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m ChatResult(generations\u001b[38;5;241m=\u001b[39m[ChatGeneration(message\u001b[38;5;241m=\u001b[39mmessage)])\n\u001b[0;32m--> 266\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcompletion_with_retry\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmessages\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmessage_dicts\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mparams\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 267\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_create_chat_result(response)\n", + "File \u001b[0;32m~/code/lc/lckg/langchain/chat_models/openai.py:228\u001b[0m, in \u001b[0;36mChatOpenAI.completion_with_retry\u001b[0;34m(self, **kwargs)\u001b[0m\n\u001b[1;32m 224\u001b[0m \u001b[38;5;129m@retry_decorator\u001b[39m\n\u001b[1;32m 225\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_completion_with_retry\u001b[39m(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Any:\n\u001b[1;32m 226\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mclient\u001b[38;5;241m.\u001b[39mcreate(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m--> 228\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_completion_with_retry\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/code/lc/lckg/.venv/lib/python3.11/site-packages/tenacity/__init__.py:289\u001b[0m, in \u001b[0;36mBaseRetrying.wraps..wrapped_f\u001b[0;34m(*args, **kw)\u001b[0m\n\u001b[1;32m 287\u001b[0m \u001b[38;5;129m@functools\u001b[39m\u001b[38;5;241m.\u001b[39mwraps(f)\n\u001b[1;32m 288\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mwrapped_f\u001b[39m(\u001b[38;5;241m*\u001b[39margs: t\u001b[38;5;241m.\u001b[39mAny, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkw: t\u001b[38;5;241m.\u001b[39mAny) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m t\u001b[38;5;241m.\u001b[39mAny:\n\u001b[0;32m--> 289\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mf\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkw\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/code/lc/lckg/.venv/lib/python3.11/site-packages/tenacity/__init__.py:379\u001b[0m, in \u001b[0;36mRetrying.__call__\u001b[0;34m(self, fn, *args, **kwargs)\u001b[0m\n\u001b[1;32m 377\u001b[0m retry_state \u001b[38;5;241m=\u001b[39m RetryCallState(retry_object\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m, fn\u001b[38;5;241m=\u001b[39mfn, args\u001b[38;5;241m=\u001b[39margs, kwargs\u001b[38;5;241m=\u001b[39mkwargs)\n\u001b[1;32m 378\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[0;32m--> 379\u001b[0m do \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43miter\u001b[49m\u001b[43m(\u001b[49m\u001b[43mretry_state\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mretry_state\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 380\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(do, DoAttempt):\n\u001b[1;32m 381\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n", + "File \u001b[0;32m~/code/lc/lckg/.venv/lib/python3.11/site-packages/tenacity/__init__.py:314\u001b[0m, in \u001b[0;36mBaseRetrying.iter\u001b[0;34m(self, retry_state)\u001b[0m\n\u001b[1;32m 312\u001b[0m is_explicit_retry \u001b[38;5;241m=\u001b[39m fut\u001b[38;5;241m.\u001b[39mfailed \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(fut\u001b[38;5;241m.\u001b[39mexception(), TryAgain)\n\u001b[1;32m 313\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m (is_explicit_retry \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mretry(retry_state)):\n\u001b[0;32m--> 314\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfut\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mresult\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 316\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mafter \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 317\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mafter(retry_state)\n", + "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/concurrent/futures/_base.py:449\u001b[0m, in \u001b[0;36mFuture.result\u001b[0;34m(self, timeout)\u001b[0m\n\u001b[1;32m 447\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m CancelledError()\n\u001b[1;32m 448\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_state \u001b[38;5;241m==\u001b[39m FINISHED:\n\u001b[0;32m--> 449\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m__get_result\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 451\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_condition\u001b[38;5;241m.\u001b[39mwait(timeout)\n\u001b[1;32m 453\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_state \u001b[38;5;129;01min\u001b[39;00m [CANCELLED, CANCELLED_AND_NOTIFIED]:\n", + "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/concurrent/futures/_base.py:401\u001b[0m, in \u001b[0;36mFuture.__get_result\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 399\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_exception:\n\u001b[1;32m 400\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 401\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_exception\n\u001b[1;32m 402\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 403\u001b[0m \u001b[38;5;66;03m# Break a reference cycle with the exception in self._exception\u001b[39;00m\n\u001b[1;32m 404\u001b[0m \u001b[38;5;28mself\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n", + "File \u001b[0;32m~/code/lc/lckg/.venv/lib/python3.11/site-packages/tenacity/__init__.py:382\u001b[0m, in \u001b[0;36mRetrying.__call__\u001b[0;34m(self, fn, *args, **kwargs)\u001b[0m\n\u001b[1;32m 380\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(do, DoAttempt):\n\u001b[1;32m 381\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 382\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 383\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m: \u001b[38;5;66;03m# noqa: B902\u001b[39;00m\n\u001b[1;32m 384\u001b[0m retry_state\u001b[38;5;241m.\u001b[39mset_exception(sys\u001b[38;5;241m.\u001b[39mexc_info()) \u001b[38;5;66;03m# type: ignore[arg-type]\u001b[39;00m\n", + "File \u001b[0;32m~/code/lc/lckg/langchain/chat_models/openai.py:226\u001b[0m, in \u001b[0;36mChatOpenAI.completion_with_retry.._completion_with_retry\u001b[0;34m(**kwargs)\u001b[0m\n\u001b[1;32m 224\u001b[0m \u001b[38;5;129m@retry_decorator\u001b[39m\n\u001b[1;32m 225\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_completion_with_retry\u001b[39m(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Any:\n\u001b[0;32m--> 226\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mclient\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcreate\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/code/lc/lckg/.venv/lib/python3.11/site-packages/openai/api_resources/chat_completion.py:25\u001b[0m, in \u001b[0;36mChatCompletion.create\u001b[0;34m(cls, *args, **kwargs)\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[1;32m 24\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m---> 25\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcreate\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 26\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m TryAgain \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 27\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m timeout \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m time\u001b[38;5;241m.\u001b[39mtime() \u001b[38;5;241m>\u001b[39m start \u001b[38;5;241m+\u001b[39m timeout:\n", + "File \u001b[0;32m~/code/lc/lckg/.venv/lib/python3.11/site-packages/openai/api_resources/abstract/engine_api_resource.py:153\u001b[0m, in \u001b[0;36mEngineAPIResource.create\u001b[0;34m(cls, api_key, api_base, api_type, request_id, api_version, organization, **params)\u001b[0m\n\u001b[1;32m 127\u001b[0m \u001b[38;5;129m@classmethod\u001b[39m\n\u001b[1;32m 128\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcreate\u001b[39m(\n\u001b[1;32m 129\u001b[0m \u001b[38;5;28mcls\u001b[39m,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 136\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mparams,\n\u001b[1;32m 137\u001b[0m ):\n\u001b[1;32m 138\u001b[0m (\n\u001b[1;32m 139\u001b[0m deployment_id,\n\u001b[1;32m 140\u001b[0m engine,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 150\u001b[0m api_key, api_base, api_type, api_version, organization, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mparams\n\u001b[1;32m 151\u001b[0m )\n\u001b[0;32m--> 153\u001b[0m response, _, api_key \u001b[38;5;241m=\u001b[39m \u001b[43mrequestor\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrequest\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 154\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mpost\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 155\u001b[0m \u001b[43m \u001b[49m\u001b[43murl\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 156\u001b[0m \u001b[43m \u001b[49m\u001b[43mparams\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparams\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 157\u001b[0m \u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mheaders\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 158\u001b[0m \u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 159\u001b[0m \u001b[43m \u001b[49m\u001b[43mrequest_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrequest_id\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 160\u001b[0m \u001b[43m \u001b[49m\u001b[43mrequest_timeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrequest_timeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 161\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 163\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m stream:\n\u001b[1;32m 164\u001b[0m \u001b[38;5;66;03m# must be an iterator\u001b[39;00m\n\u001b[1;32m 165\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(response, OpenAIResponse)\n", + "File \u001b[0;32m~/code/lc/lckg/.venv/lib/python3.11/site-packages/openai/api_requestor.py:226\u001b[0m, in \u001b[0;36mAPIRequestor.request\u001b[0;34m(self, method, url, params, headers, files, stream, request_id, request_timeout)\u001b[0m\n\u001b[1;32m 205\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mrequest\u001b[39m(\n\u001b[1;32m 206\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 207\u001b[0m method,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 214\u001b[0m request_timeout: Optional[Union[\u001b[38;5;28mfloat\u001b[39m, Tuple[\u001b[38;5;28mfloat\u001b[39m, \u001b[38;5;28mfloat\u001b[39m]]] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 215\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Tuple[Union[OpenAIResponse, Iterator[OpenAIResponse]], \u001b[38;5;28mbool\u001b[39m, \u001b[38;5;28mstr\u001b[39m]:\n\u001b[1;32m 216\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mrequest_raw(\n\u001b[1;32m 217\u001b[0m method\u001b[38;5;241m.\u001b[39mlower(),\n\u001b[1;32m 218\u001b[0m url,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 224\u001b[0m request_timeout\u001b[38;5;241m=\u001b[39mrequest_timeout,\n\u001b[1;32m 225\u001b[0m )\n\u001b[0;32m--> 226\u001b[0m resp, got_stream \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_interpret_response\u001b[49m\u001b[43m(\u001b[49m\u001b[43mresult\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 227\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m resp, got_stream, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mapi_key\n", + "File \u001b[0;32m~/code/lc/lckg/.venv/lib/python3.11/site-packages/openai/api_requestor.py:620\u001b[0m, in \u001b[0;36mAPIRequestor._interpret_response\u001b[0;34m(self, result, stream)\u001b[0m\n\u001b[1;32m 612\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m (\n\u001b[1;32m 613\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_interpret_response_line(\n\u001b[1;32m 614\u001b[0m line, result\u001b[38;5;241m.\u001b[39mstatus_code, result\u001b[38;5;241m.\u001b[39mheaders, stream\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[1;32m 615\u001b[0m )\n\u001b[1;32m 616\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m line \u001b[38;5;129;01min\u001b[39;00m parse_stream(result\u001b[38;5;241m.\u001b[39miter_lines())\n\u001b[1;32m 617\u001b[0m ), \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[1;32m 618\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 619\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m (\n\u001b[0;32m--> 620\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_interpret_response_line\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 621\u001b[0m \u001b[43m \u001b[49m\u001b[43mresult\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcontent\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdecode\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mutf-8\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 622\u001b[0m \u001b[43m \u001b[49m\u001b[43mresult\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstatus_code\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 623\u001b[0m \u001b[43m \u001b[49m\u001b[43mresult\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mheaders\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 624\u001b[0m \u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[1;32m 625\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m,\n\u001b[1;32m 626\u001b[0m \u001b[38;5;28;01mFalse\u001b[39;00m,\n\u001b[1;32m 627\u001b[0m )\n", + "File \u001b[0;32m~/code/lc/lckg/.venv/lib/python3.11/site-packages/openai/api_requestor.py:683\u001b[0m, in \u001b[0;36mAPIRequestor._interpret_response_line\u001b[0;34m(self, rbody, rcode, rheaders, stream)\u001b[0m\n\u001b[1;32m 681\u001b[0m stream_error \u001b[38;5;241m=\u001b[39m stream \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124merror\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;129;01min\u001b[39;00m resp\u001b[38;5;241m.\u001b[39mdata\n\u001b[1;32m 682\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m stream_error \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;241m200\u001b[39m \u001b[38;5;241m<\u001b[39m\u001b[38;5;241m=\u001b[39m rcode \u001b[38;5;241m<\u001b[39m \u001b[38;5;241m300\u001b[39m:\n\u001b[0;32m--> 683\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhandle_error_response(\n\u001b[1;32m 684\u001b[0m rbody, rcode, resp\u001b[38;5;241m.\u001b[39mdata, rheaders, stream_error\u001b[38;5;241m=\u001b[39mstream_error\n\u001b[1;32m 685\u001b[0m )\n\u001b[1;32m 686\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m resp\n", + "\u001b[0;31mInvalidRequestError\u001b[0m: The model: `gpt-4` does not exist" + ] + } + ], + "source": [ + "agent.run([\"Using the 'data/' folder as scratch, get me the yearly % change in US college tuition and the yearly % change in US inflation (US CPI) every year since 1980.\"])" + ] + }, + { + "cell_type": "markdown", + "id": "f78012cf", + "metadata": {}, + "source": [ + "The command runs and writes output to `data`.\n", + " \n", + "`cleaned_college_tuition_inflation_percent_change.csv` is written.\n", + "\n", + "We write some simple code to plot this." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c33c7a24", + "metadata": {}, + "outputs": [], + "source": [ + "# Read\n", + "d = pd.read_csv(\"data/cleaned_college_tuition_inflation_percent_change.csv\")\n", + "d.set_index(\"Year\",inplace=True)\n", + "# Compute cumulative percent change\n", + "d['College Tuition % Change Cumulative'] = (1 + d['College Tuition % Change']).cumprod() * 100\n", + "d['Inflation % Change Cumulative'] = (1 + d['Inflation % Change']).cumprod() * 100\n", + "# Plot\n", + "d[['College Tuition % Change Cumulative','Inflation % Change Cumulative']].plot(color=['blue','green'])\n", + "plt.ylabel(\"Cumulative Percent Change\")" + ] + }, + { + "cell_type": "markdown", + "id": "f31d09ea", + "metadata": {}, + "source": [ + "Of course, we would want to inspect and verify the results." + ] + }, + { + "cell_type": "markdown", + "id": "3c59b98d", + "metadata": {}, + "source": [ + "### AutoGPT as a data scietntist (research, cleaning, visualization)\n", + "\n", + "#### `hey jude` \n", + "\n", + "Let's try end-to-end data retrival and plotting.\n", + "\n", + "* See my full notes here: https://rlancemartin.notion.site/Auto-GPT-Notes-9481cbd0cb364580bf97d5f41144e5cf\n", + "* Files to write \n", + "* However, reading seems to not work\n", + "* Even if I simply ask it to read from the saved file, it gets stuck and appears to loop\n", + "\n", + "`agent.run([\"Read the file hey_jude_lyrics.txt and plot the frequency of the top 5 words.\"])`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "999f19bf", + "metadata": {}, + "outputs": [], + "source": [ + "llm = ChatOpenAI(model_name=\"gpt-4\", temperature=0.3)\n", + "agent = AutoGPT.from_llm_and_tools(\n", + " ai_name=\"Tom\",\n", + " ai_role=\"Look up song lyrics\",\n", + " tools=tools,\n", + " llm=llm,\n", + " memory=vectorstore.as_retriever()\n", + ")\n", + "agent.chain.verbose = True\n", + "\n", + "agent.run([\"Get the lyrics to the Beatles song 'Hey Jude' and plot the frequency of the top 5 words.\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a54c746", + "metadata": {}, + "outputs": [], + "source": [ + "llm = ChatOpenAI(model_name=\"gpt-4\", temperature=0.3)\n", + "agent = AutoGPT.from_llm_and_tools(\n", + " ai_name=\"Tom\",\n", + " ai_role=\"Look up song lyrics\",\n", + " tools=tools,\n", + " llm=llm,\n", + " memory=vectorstore.as_retriever()\n", + ")\n", + "agent.chain.verbose = True\n", + "\n", + "agent.run([\"Read the file hey_jude_lyrics.txt and plot the frequency of the top 5 words.\"])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/langchain/experimental/__init__.py b/langchain/experimental/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/langchain/experimental/autonomous_agents/__init__.py b/langchain/experimental/autonomous_agents/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/langchain/experimental/autonomous_agents/autogpt/__init__.py b/langchain/experimental/autonomous_agents/autogpt/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/langchain/experimental/autonomous_agents/autogpt/agent.py b/langchain/experimental/autonomous_agents/autogpt/agent.py new file mode 100644 index 00000000000..9b33b4b9ace --- /dev/null +++ b/langchain/experimental/autonomous_agents/autogpt/agent.py @@ -0,0 +1,118 @@ +from __future__ import annotations + +from typing import List, Optional + +from langchain.experimental.autonomous_agents.autogpt.output_parser import ( + AutoGPTOutputParser, + BaseAutoGPTOutputParser, +) +from langchain.experimental.autonomous_agents.autogpt.prompt import AutoGPTPrompt +from langchain.experimental.autonomous_agents.autogpt.prompt_generator import ( + FINISH_NAME, +) +from pydantic import ValidationError +from langchain.chains.llm import LLMChain +from langchain.chat_models.base import BaseChatModel +from langchain.schema import ( + AIMessage, + BaseMessage, + Document, + HumanMessage, + SystemMessage, +) +from langchain.tools.base import BaseTool +from langchain.vectorstores.base import VectorStoreRetriever + + +class AutoGPT: + """Agent class for interacting with Auto-GPT.""" + + def __init__( + self, + ai_name: str, + memory: VectorStoreRetriever, + chain: LLMChain, + output_parser: BaseAutoGPTOutputParser, + tools: List[BaseTool], + ): + self.ai_name = ai_name + self.memory = memory + self.full_message_history: List[BaseMessage] = [] + self.next_action_count = 0 + self.chain = chain + self.output_parser = output_parser + self.tools = tools + + @classmethod + def from_llm_and_tools( + cls, + ai_name: str, + ai_role: str, + memory: VectorStoreRetriever, + tools: List[BaseTool], + llm: BaseChatModel, + output_parser: Optional[BaseAutoGPTOutputParser] = None, + ) -> AutoGPT: + prompt = AutoGPTPrompt( + ai_name=ai_name, + ai_role=ai_role, + tools=tools, + input_variables=["memory", "messages", "goals", "user_input"], + token_counter=llm.get_num_tokens, + ) + chain = LLMChain(llm=llm, prompt=prompt) + return cls( + ai_name, memory, chain, output_parser or AutoGPTOutputParser(), tools + ) + + def run(self, goals: List[str]) -> str: + user_input = ( + "Determine which next command to use, " + "and respond using the format specified above:" + ) + # Interaction Loop + loop_count = 0 + while True: + # Discontinue if continuous limit is reached + loop_count += 1 + + # Send message to AI, get response + assistant_reply = self.chain.run( + goals=goals, + messages=self.full_message_history, + memory=self.memory, + user_input=user_input, + ) + + # Print Assistant thoughts + print(assistant_reply) + self.full_message_history.append(HumanMessage(content=user_input)) + self.full_message_history.append(AIMessage(content=assistant_reply)) + + # Get command name and arguments + action = self.output_parser.parse(assistant_reply) + tools = {t.name: t for t in self.tools} + if action.name == FINISH_NAME: + return action.args["response"] + if action.name in tools: + tool = tools[action.name] + try: + observation = tool.run(action.args) + except ValidationError as e: + observation = f"Error in args: {str(e)}" + result = f"Command {tool.name} returned: {observation}" + elif action.name == "ERROR": + result = f"Error: {action.args}. " + else: + result = ( + f"Unknown command '{action.name}'. " + f"Please refer to the 'COMMANDS' list for available " + f"commands and only respond in the specified JSON format." + ) + + memory_to_add = ( + f"Assistant Reply: {assistant_reply} " f"\nResult: {result} " + ) + + self.memory.add_documents([Document(page_content=memory_to_add)]) + self.full_message_history.append(SystemMessage(content=result)) diff --git a/langchain/experimental/autonomous_agents/autogpt/memory.py b/langchain/experimental/autonomous_agents/autogpt/memory.py new file mode 100644 index 00000000000..5c48674a179 --- /dev/null +++ b/langchain/experimental/autonomous_agents/autogpt/memory.py @@ -0,0 +1,30 @@ +from typing import Any, Dict, List + +from pydantic import Field + +from langchain.memory.chat_memory import BaseChatMemory, get_prompt_input_key +from langchain.vectorstores.base import VectorStoreRetriever + + +class AutoGPTMemory(BaseChatMemory): + retriever: VectorStoreRetriever = Field(exclude=True) + """VectorStoreRetriever object to connect to.""" + + @property + def memory_variables(self) -> List[str]: + return ["chat_history", "relevant_context"] + + def _get_prompt_input_key(self, inputs: Dict[str, Any]) -> str: + """Get the input key for the prompt.""" + if self.input_key is None: + return get_prompt_input_key(inputs, self.memory_variables) + return self.input_key + + def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, Any]: + input_key = self._get_prompt_input_key(inputs) + query = inputs[input_key] + docs = self.retriever.get_relevant_documents(query) + return { + "chat_history": self.chat_memory.messages[-10:], + "relevant_context": docs, + } diff --git a/langchain/experimental/autonomous_agents/autogpt/output_parser.py b/langchain/experimental/autonomous_agents/autogpt/output_parser.py new file mode 100644 index 00000000000..b8127272bbd --- /dev/null +++ b/langchain/experimental/autonomous_agents/autogpt/output_parser.py @@ -0,0 +1,48 @@ +import json +from abc import abstractmethod +from typing import Dict, NamedTuple + +from langchain.schema import BaseOutputParser +import re + + +class AutoGPTAction(NamedTuple): + name: str + args: Dict + + +class BaseAutoGPTOutputParser(BaseOutputParser): + @abstractmethod + def parse(self, text: str) -> AutoGPTAction: + """Return AutoGPTAction""" + + +def preprocess_json_input(input_str): + # Replace single backslashes with double backslashes, while leaving already escaped ones intact + corrected_str = re.sub( + r'(? AutoGPTAction: + try: + parsed = json.loads(text, strict=False) + except json.JSONDecodeError: + preprocessed_text = preprocess_json_input(text) + try: + parsed = json.loads(preprocessed_text, strict=False) + except: + return AutoGPTAction( + name="ERROR", args=f"Could not parse invalid json: {text}" + ) + try: + return AutoGPTAction( + name=parsed["command"]["name"], + args=parsed["command"]["args"], + ) + except KeyError as e: + return AutoGPTAction( + name="ERROR", args=f"Incomplete command args: {parsed}" + ) diff --git a/langchain/experimental/autonomous_agents/autogpt/prompt.py b/langchain/experimental/autonomous_agents/autogpt/prompt.py new file mode 100644 index 00000000000..a9a79e3ea66 --- /dev/null +++ b/langchain/experimental/autonomous_agents/autogpt/prompt.py @@ -0,0 +1,75 @@ +import time +from typing import Any, Callable, List + +from pydantic import BaseModel + +from langchain.experimental.autonomous_agents.autogpt.prompt_generator import get_prompt +from langchain.prompts.chat import ( + BaseChatPromptTemplate, +) +from langchain.schema import BaseMessage, HumanMessage, SystemMessage +from langchain.tools.base import BaseTool +from langchain.vectorstores.base import VectorStoreRetriever + + +class AutoGPTPrompt(BaseChatPromptTemplate, BaseModel): + ai_name: str + ai_role: str + tools: List[BaseTool] + token_counter: Callable[[str], int] + send_token_limit: int = 4196 + + def construct_full_prompt(self, goals: List[str]) -> str: + prompt_start = """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. + If you have completed all your tasks, + make sure to use the "finish" command.""" + + # Construct full prompt + full_prompt = ( + f"You are {self.ai_name}, {self.ai_role}\n{prompt_start}\n\nGOALS:\n\n" + ) + for i, goal in enumerate(goals): + full_prompt += f"{i+1}. {goal}\n" + + full_prompt += f"\n\n{get_prompt(self.tools)}" + return full_prompt + + def format_messages(self, **kwargs: Any) -> List[BaseMessage]: + base_prompt = SystemMessage(content=self.construct_full_prompt(kwargs["goals"])) + time_prompt = SystemMessage( + content=f"The current time and date is {time.strftime('%c')}" + ) + used_tokens = self.token_counter(base_prompt.content) + self.token_counter( + time_prompt.content + ) + memory: VectorStoreRetriever = kwargs["memory"] + previous_messages = kwargs["messages"] + relevant_docs = memory.get_relevant_documents(str(previous_messages[-10:])) + relevant_memory = [d.page_content for d in relevant_docs] + relevant_memory_tokens = sum( + [self.token_counter(doc) for doc in relevant_memory] + ) + while used_tokens + relevant_memory_tokens > 2500: + relevant_memory = relevant_memory[:-1] + relevant_memory_tokens = sum( + [self.token_counter(doc) for doc in relevant_memory] + ) + content_format = ( + f"This reminds you of these events " + f"from your past:\n{relevant_memory}\n\n" + ) + memory_message = SystemMessage(content=content_format) + used_tokens += len(memory_message.content) + historical_messages: List[BaseMessage] = [] + for message in previous_messages[-10:][::-1]: + message_tokens = self.token_counter(message.content) + if used_tokens + message_tokens > self.send_token_limit - 1000: + break + historical_messages = [message] + historical_messages + input_message = HumanMessage(content=kwargs["user_input"]) + messages: List[BaseMessage] = [base_prompt, time_prompt, memory_message] + messages += historical_messages + messages.append(input_message) + return messages diff --git a/langchain/experimental/autonomous_agents/autogpt/prompt_generator.py b/langchain/experimental/autonomous_agents/autogpt/prompt_generator.py new file mode 100644 index 00000000000..133734beaa8 --- /dev/null +++ b/langchain/experimental/autonomous_agents/autogpt/prompt_generator.py @@ -0,0 +1,185 @@ +import json +from typing import List + +from langchain.tools.base import BaseTool + +FINISH_NAME = "finish" + + +class PromptGenerator: + """A class for generating custom prompt strings. + + Does this based on constraints, commands, resources, and performance evaluations. + """ + + def __init__(self) -> None: + """Initialize the PromptGenerator object. + + Starts with empty lists of constraints, commands, resources, + and performance evaluations. + """ + self.constraints: List[str] = [] + self.commands: List[BaseTool] = [] + self.resources: List[str] = [] + self.performance_evaluation: List[str] = [] + self.response_format = { + "thoughts": { + "text": "thought", + "reasoning": "reasoning", + "plan": "- short bulleted\n- list that conveys\n- long-term plan", + "criticism": "constructive self-criticism", + "speak": "thoughts summary to say to user", + }, + "command": {"name": "command name", "args": {"arg name": "value"}}, + } + + def add_constraint(self, constraint: str) -> None: + """ + Add a constraint to the constraints list. + + Args: + constraint (str): The constraint to be added. + """ + self.constraints.append(constraint) + + def add_tool(self, tool: BaseTool) -> None: + self.commands.append(tool) + + def _generate_command_string(self, tool: BaseTool) -> str: + schema = json.dumps(tool.args.schema()["properties"]) + return f"{tool.name}: {tool.description}, args json schema: {schema}" + + def add_resource(self, resource: str) -> None: + """ + Add a resource to the resources list. + + Args: + resource (str): The resource to be added. + """ + self.resources.append(resource) + + def add_performance_evaluation(self, evaluation: str) -> None: + """ + Add a performance evaluation item to the performance_evaluation list. + + Args: + evaluation (str): The evaluation item to be added. + """ + self.performance_evaluation.append(evaluation) + + def _generate_numbered_list(self, items: list, item_type: str = "list") -> str: + """ + Generate a numbered list from given items based on the item_type. + + Args: + items (list): A list of items to be numbered. + item_type (str, optional): The type of items in the list. + Defaults to 'list'. + + Returns: + str: The formatted numbered list. + """ + if item_type == "command": + command_strings = [ + f"{i + 1}. {self._generate_command_string(item)}" + for i, item in enumerate(items) + ] + finish_description = ( + "use this to signal that you have finished all your objectives" + ) + finish_args = ( + '"response": "final response to let ' + 'people know you have finished your objectives"' + ) + finish_string = ( + f"{len(items) + 1}. {FINISH_NAME}: " + f"{finish_description}, args: {finish_args}" + ) + return "\n".join(command_strings + [finish_string]) + else: + return "\n".join(f"{i+1}. {item}" for i, item in enumerate(items)) + + def generate_prompt_string(self) -> str: + """Generate a prompt string. + + Returns: + str: The generated prompt string. + """ + formatted_response_format = json.dumps(self.response_format, indent=4) + prompt_string = ( + f"Constraints:\n{self._generate_numbered_list(self.constraints)}\n\n" + f"Commands:\n" + f"{self._generate_numbered_list(self.commands, item_type='command')}\n\n" + f"Resources:\n{self._generate_numbered_list(self.resources)}\n\n" + f"Performance Evaluation:\n" + f"{self._generate_numbered_list(self.performance_evaluation)}\n\n" + f"You should only respond in JSON format as described below " + f"\nResponse Format: \n{formatted_response_format} " + f"\nEnsure the response can be parsed by Python json.loads" + ) + + return prompt_string + + +def get_prompt(tools: List[BaseTool]) -> str: + """This function generates a prompt string. + + It includes various constraints, commands, resources, and performance evaluations. + + Returns: + str: The generated prompt string. + """ + + # Initialize the PromptGenerator object + prompt_generator = PromptGenerator() + + # Add constraints to the PromptGenerator object + prompt_generator.add_constraint( + "~4000 word limit for short term memory. " + "Your short term memory is short, " + "so immediately save important information to files." + ) + prompt_generator.add_constraint( + "If you are unsure how you previously did something " + "or want to recall past events, " + "thinking about similar events will help you remember." + ) + prompt_generator.add_constraint("No user assistance") + prompt_generator.add_constraint( + 'Exclusively use the commands listed in double quotes e.g. "command name"' + ) + + # Add commands to the PromptGenerator object + for tool in tools: + prompt_generator.add_tool(tool) + + # Add resources to the PromptGenerator object + prompt_generator.add_resource( + "Internet access for searches and information gathering." + ) + prompt_generator.add_resource("Long Term memory management.") + prompt_generator.add_resource( + "GPT-3.5 powered Agents for delegation of simple tasks." + ) + prompt_generator.add_resource("File output.") + + # Add performance evaluations to the PromptGenerator object + prompt_generator.add_performance_evaluation( + "Continuously review and analyze your actions " + "to ensure you are performing to the best of your abilities." + ) + prompt_generator.add_performance_evaluation( + "Constructively self-criticize your big-picture behavior constantly." + ) + prompt_generator.add_performance_evaluation( + "Reflect on past decisions and strategies to refine your approach." + ) + prompt_generator.add_performance_evaluation( + "Every command has a cost, so be smart and efficient. " + "Aim to complete tasks in the least number of steps." + ) + + # Generate the prompt string + prompt_string = prompt_generator.generate_prompt_string() + + return prompt_string