feat: refactor bot name (#2)

This commit is contained in:
이태호 2023-03-20 17:27:20 +09:00 committed by GitHub
parent 20165ec0a0
commit f7892be801
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 165 additions and 78 deletions

View File

@ -1,3 +1,4 @@
BOT_NAME=<your-bot-name>
AWS_ACCESS_KEY_ID=***
AWS_SECRET_ACCESS_KEY=***
AWS_REGION=***

View File

@ -20,4 +20,4 @@ RUN poetry install --with tools
COPY . .
ENTRYPOINT ["poetry", "run", "python3", "-m", "uvicorn", "main:app", "--reload", "--host=0.0.0.0", "--port=8000"]
ENTRYPOINT ["poetry", "run", "python3", "-m", "uvicorn", "main:app", "--host=0.0.0.0", "--port=8000"]

View File

@ -1,27 +1,12 @@
# AwesomeGPT
# EVAL
Everything you can do with your computer is possible through AwesomeGPT. It can understand and generate data formats for text, image, dataframe, audio (TODO), video (TODO). It must be run inside a container that is only available to it, and it can build your container environment directly through Terminal Tools.
Everything you can do with a computer through EVAL is executable like the eval method. It can understand and generate data formats for text, image, dataframe, audio (TODO), video (TODO). It must be run inside a container that is only available to it, and it can build your container environment directly through Terminal Tools.
## Usage
1. environments settings
2. S3 Settings
3. Execute `docker-compose up --build -d awesomegpt`
### Environment
You must need this environments.
```
OPENAI_API_KEY
```
You need this environments.
```
serpapi: SERPAPI_API_KEY
bing-search: BING_SEARCH_URL, BING_SUBSCRIPTION_KEY
```
1. S3 Settings
2. environments settings
3. Execute `docker-compose up -d`
### S3
@ -45,6 +30,27 @@ bing-search: BING_SEARCH_URL, BING_SUBSCRIPTION_KEY
}
```
### Environment
These environmental variables are essential, so please set them.
```
BOT_NAME: your custom bot name
OPENAI_API_KEY: openai api key
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_REGION
AWS_S3_BUCKET
```
These environment variables are necessary to use the following tools:
If you want to use it, set it up, and if you don't need it, you don't have to set it up.
```
SERPAPI_API_KEY: need to append google search tool
BING_SEARCH_URL, BING_SUBSCRIPTION_KEY: need to append bing search tool
```
## Tools
## TODO
@ -54,6 +60,7 @@ bing-search: BING_SEARCH_URL, BING_SUBSCRIPTION_KEY
- [ ] convert to alpaca
- [ ] requests.get refactoring
- [ ] prompt upgrade
- [ ] give a tool to create tools
- [ ] etc.
## Reference

View File

@ -4,21 +4,24 @@ from llm import ChatOpenAI
from langchain.agents.agent import AgentExecutor
from langchain.agents.initialize import initialize_agent
from langchain.chains.conversation.memory import ConversationBufferMemory
from langchain.memory.chat_memory import BaseChatMemory
from langchain.chat_models.base import BaseChatModel
from langchain.memory.chat_memory import BaseChatMemory
from langchain.output_parsers.base import BaseOutputParser
from prompts.input import AWESOMEGPT_PREFIX, AWESOMEGPT_SUFFIX
from prompts.input import EVAL_PREFIX, EVAL_SUFFIX
from env import settings
from agents.parser import EvalOutputParser
from tools.base import BaseToolSet
from tools.factory import ToolsFactory
from handlers.base import BaseHandler, FileHandler, FileType
from env import settings
class AgentBuilder:
def __init__(self):
self.llm: BaseChatModel = None
self.memory: BaseChatMemory = None
self.parser: BaseOutputParser = None
self.tools: list = None
self.handler: FileHandler = None
@ -30,6 +33,9 @@ class AgentBuilder:
memory_key="chat_history", return_messages=True
)
def build_parser(self):
self.parser = EvalOutputParser()
def build_tools(self, toolsets: list[BaseToolSet] = []):
if self.llm is None:
raise ValueError("LLM must be initialized before tools")
@ -50,16 +56,20 @@ class AgentBuilder:
self.handler = FileHandler(handlers)
def get_agent(self):
print("Initializing AwesomeGPT")
print(f"Initializing {settings['BOT_NAME']}")
if self.llm is None:
raise ValueError("LLM must be initialized before agent")
if self.tools is None:
raise ValueError("Tools must be initialized before agent")
if self.memory is None:
raise ValueError("Memory must be initialized before agent")
if self.parser is None:
raise ValueError("Parser must be initialized before agent")
if self.tools is None:
raise ValueError("Tools must be initialized before agent")
return initialize_agent(
self.tools,
self.llm,
@ -67,8 +77,9 @@ class AgentBuilder:
verbose=True,
memory=self.memory,
agent_kwargs={
"system_message": AWESOMEGPT_PREFIX,
"human_message": AWESOMEGPT_SUFFIX,
"system_message": EVAL_PREFIX.format(bot_name=settings["BOT_NAME"]),
"human_message": EVAL_SUFFIX.format(bot_name=settings["BOT_NAME"]),
"output_parser": self.parser,
},
)
@ -85,6 +96,7 @@ class AgentBuilder:
builder = AgentBuilder()
builder.build_llm()
builder.build_memory()
builder.build_parser()
builder.build_tools(toolsets)
builder.build_handler(handlers)

25
agents/parser.py Normal file
View File

@ -0,0 +1,25 @@
import json
from typing import Dict
from langchain.output_parsers.base import BaseOutputParser
from prompts.input import EVAL_FORMAT_INSTRUCTIONS
class EvalOutputParser(BaseOutputParser):
def get_format_instructions(self) -> str:
return EVAL_FORMAT_INSTRUCTIONS
def parse(self, text: str) -> Dict[str, str]:
cleaned_output = text.strip()
if "```json" in cleaned_output:
_, cleaned_output = cleaned_output.split("```json")
if cleaned_output.startswith("```json"):
cleaned_output = cleaned_output[len("```json") :]
if cleaned_output.startswith("```"):
cleaned_output = cleaned_output[len("```") :]
if cleaned_output.endswith("```"):
cleaned_output = cleaned_output[: -len("```")]
cleaned_output = cleaned_output.strip()
response = json.loads(cleaned_output)
return {"action": response["action"], "action_input": response["action_input"]}

View File

@ -1,7 +1,9 @@
version: "3"
services:
awesomegpt:
eval:
container_name: eval
image: eval
build:
dockerfile: Dockerfile
context: .

5
env.py
View File

@ -1,11 +1,13 @@
import os
from dotenv import load_dotenv
from typing import TypedDict
from dotenv import load_dotenv
load_dotenv()
class DotEnv(TypedDict):
BOT_NAME: str
AWS_ACCESS_KEY_ID: str
AWS_SECRET_ACCESS_KEY: str
AWS_REGION: str
@ -19,6 +21,7 @@ class DotEnv(TypedDict):
settings: DotEnv = {
"BOT_NAME": os.getenv("BOT_NAME", "Orca"),
"AWS_ACCESS_KEY_ID": os.getenv("AWS_ACCESS_KEY_ID"),
"AWS_SECRET_ACCESS_KEY": os.getenv("AWS_SECRET_ACCESS_KEY"),
"AWS_REGION": os.getenv("AWS_REGION"),

View File

@ -1,6 +1,6 @@
import os
import requests
import uuid
import requests
from typing import Dict
from enum import Enum

View File

@ -1,4 +1,5 @@
import pandas as pd
from prompts.file import DATAFRAME_PROMPT
from .base import BaseHandler
@ -7,5 +8,14 @@ from .base import BaseHandler
class CsvToDataframe(BaseHandler):
def handle(self, filename: str):
df = pd.read_csv(filename)
description = str(df.describe())
description = (
f"Dataframe with {len(df)} rows and {len(df.columns)} columns."
"Columns are: "
f"{', '.join(df.columns)}"
)
print(
f"\nProcessed CsvToDataframe, Input CSV: {filename}, Output Description: {description}"
)
return DATAFRAME_PROMPT.format(filename=filename, description=description)

View File

@ -4,6 +4,7 @@ from transformers import (
BlipProcessor,
BlipForConditionalGeneration,
)
from prompts.file import IMAGE_PROMPT
from .base import BaseHandler

View File

@ -3,11 +3,12 @@ import re
from fastapi import FastAPI
from pydantic import BaseModel
from s3 import upload
from env import settings
from prompts.error import ERROR_PROMPT
from agent import AgentBuilder
from agents.builder import AgentBuilder
from tools.base import BaseToolSet
from tools.cpu import (
Terminal,
@ -63,7 +64,7 @@ class Response(TypedDict):
@app.get("/")
async def index():
return {"message": "Hello World. I'm AwesomeGPT."}
return {"message": f"Hello World. I'm {settings['BOT_NAME']}."}
@app.post("/command")

View File

@ -1,33 +1,59 @@
AWESOMEGPT_PREFIX = """Awesome GPT is designed to be able to assist with a wide range of text, visual related tasks, data analysis related tasks, auditory related tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics.
Awesome GPT is able to generate human-like text based on the input it receives, allowing it to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.
Awesome GPT is able to process and understand large amounts of various types of files(image, audio, video, dataframe, etc.). As a language model, Awesome GPT can not directly read various types of files(text, image, audio, video, dataframe, etc.), but it has a list of tools to finish different visual tasks.
EVAL_PREFIX = """{bot_name} is designed to be able to assist with a wide range of text, visual related tasks, data analysis related tasks, auditory related tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics.
{bot_name} is able to generate human-like text based on the input it receives, allowing it to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.
{bot_name} is able to process and understand large amounts of various types of files(image, audio, video, dataframe, etc.). As a language model, {bot_name} can not directly read various types of files(text, image, audio, video, dataframe, etc.), but it has a list of tools to finish different visual tasks.
Each image will have a file name formed as "image/xxx.png"
Each audio will have a file name formed as "audio/xxx.mp3"
Each video will have a file name formed as "video/xxx.mp4"
Each dataframe will have a file name formed as "dataframe/xxx.csv"
Awesome GPT can invoke different tools to indirectly understand files(image, audio, video, dataframe, etc.). When talking about files(image, audio, video, dataframe, etc.), Awesome GPT is very strict to the file name and will never fabricate nonexistent files.
When using tools to generate new files, Awesome GPT is also known that the file(image, audio, video, dataframe, etc.) may not be the same as the user's demand, and will use other visual question answering tools or description tools to observe the real file.
Awesome GPT is able to use tools in a sequence, and is loyal to the tool observation outputs rather than faking the file content and file name. It will remember to provide the file name from the last tool observation, if a new file is generated.
Human may provide new figures to Awesome GPT with a description. The description helps Awesome GPT to understand this file, but Awesome GPT should use tools to finish following tasks, rather than directly imagine from the description.
{bot_name} can invoke different tools to indirectly understand files(image, audio, video, dataframe, etc.). When talking about files(image, audio, video, dataframe, etc.), {bot_name} is very strict to the file name and will never fabricate nonexistent files.
When using tools to generate new files, {bot_name} is also known that the file(image, audio, video, dataframe, etc.) may not be the same as the user's demand, and will use other visual question answering tools or description tools to observe the real file.
{bot_name} is able to use tools in a sequence, and is loyal to the tool observation outputs rather than faking the file content and file name. It will remember to provide the file name from the last tool observation, if a new file is generated.
Human may provide new figures to {bot_name} with a description. The description helps {bot_name} to understand this file, but {bot_name} should use tools to finish following tasks, rather than directly imagine from the description.
Overall, Awesome GPT is a powerful visual dialogue assistant tool that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics."""
Overall, {bot_name} is a powerful visual dialogue assistant tool that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics."""
AWESOMEGPT_SUFFIX = """TOOLS
EVAL_FORMAT_INSTRUCTIONS = """RESPONSE FORMAT INSTRUCTIONS
----------------------------
When responding to me please, please output a response in one of two formats:
**Option 1:**
Use this if you want the human to use a tool.
Markdown code snippet formatted in the following schema:
```json
{{{{
"action": string \\ The action to take. Must be one of {tool_names}
"action_input": string \\ The input to the action
}}}}
```
**Option #2:**
Use this if you want to respond directly to the human. Markdown code snippet formatted in the following schema:
```json
{{{{
"action": "Final Answer",
"action_input": string \\ You should put what you want to return to use here
}}}}
```"""
EVAL_SUFFIX = """TOOLS
------
Awesome GPT can ask the user to use tools to look up information that may be helpful in answering the users original question.
{bot_name} can ask the user to use tools to look up information that may be helpful in answering the users original question.
You are very strict to the filename correctness and will never fake a file name if it does not exist.
You will remember to provide the file name loyally if it's provided in the last tool observation.
The tools the human can use are:
{{tools}}
{{{{tools}}}}
{format_instructions}
{{format_instructions}}
USER'S INPUT
--------------------
Here is the user's input (remember to respond with a markdown code snippet of a json blob with a single action, and NOTHING else):
{{{{input}}}}"""
{{{{{{{{input}}}}}}}}"""

View File

@ -1,5 +1,5 @@
[tool.poetry]
name = "awesomegpt"
name = "eval"
version = "0.1.0"
description = ""
authors = ["Taeho Lee <taeho@corca.ai>", "Chung Hwan Han <hanch@corca.ai>"]

View File

@ -10,7 +10,6 @@ from langchain.memory.chat_memory import BaseChatMemory
"""Wrapper around subprocess to run commands."""
import subprocess
from typing import List, Union
from .base import tool, BaseToolSet
@ -18,36 +17,28 @@ from .base import tool, BaseToolSet
class Terminal(BaseToolSet):
"""Executes bash commands and returns the output."""
def __init__(self, strip_newlines: bool = False, return_err_output: bool = False):
"""Initialize with stripping newlines."""
self.strip_newlines = strip_newlines
self.return_err_output = return_err_output
@tool(
name="Terminal",
description="Executes commands in a terminal."
"Input should be valid commands, "
"and the output will be any output from running that command. This result should always be wrapped in a code block.",
"and the output will be any output from running that command.",
)
def inference(self, commands: Union[str, List[str]]) -> str:
def inference(self, commands: str) -> str:
"""Run commands and return final output."""
if isinstance(commands, str):
commands = [commands]
commands = ";".join(commands)
try:
output = subprocess.run(
commands,
shell=True,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
).stdout.decode()
except Exception as e:
if self.return_err_output:
return e.stdout.decode()
return str(e)
if self.strip_newlines:
output = output.strip()
output = str(e)
print(
f"\nProcessed Terminal, Input Commands: {commands} "
f"Output Answer: {output}"
)
return output
@ -74,6 +65,11 @@ class RequestsGet(BaseToolSet):
if len(content) > 300:
content = content[:300] + "..."
print(
f"\nProcessed RequestsGet, Input Url: {url} " f"Output Contents: {content}"
)
return content
@ -94,7 +90,6 @@ class WineDB(BaseToolSet):
Concat({concat_columns})
FROM wine
"""
# CAST(type AS VARCHAR), 'nameEn', 'nameKo', vintage, nationality, province, CAST(size AS VARCHAR), 'grapeVariety', price, image, description, code, winery, alcohol, pairing
documents = db.load_data(query=query)
self.index = GPTSimpleVectorIndex(documents)
@ -116,7 +111,11 @@ class WineDB(BaseToolSet):
)
]
)
return results.response + "\n\n" + wine
output = results.response + "\n\n" + wine
print(f"\nProcessed WineDB, Input Query: {query} " f"Output Wine: {wine}")
return output
class ExitConversation(BaseToolSet):
@ -124,11 +123,13 @@ class ExitConversation(BaseToolSet):
name="exit_conversation",
description="A tool to exit the conversation. "
"Use this when you want to end the conversation. "
"Input should be a user's query and user's session."
"Input should be a user's query."
"The output will be a message that the conversation is over.",
)
def inference(self, query: str, session: str) -> str:
def inference(self, query: str) -> str:
"""Run the tool."""
# session.clear() # TODO
print(f"\nProcessed ExitConversation, Input Query: {query} ")
return f"My original question was: {query}"

View File

@ -1,8 +1,8 @@
from typing import Optional
from langchain.llms.base import BaseLLM
from langchain.agents import load_tools
from langchain.agents.tools import BaseTool
from langchain.llms.base import BaseLLM
from .base import BaseToolSet

View File

@ -1,10 +1,8 @@
import os
import torch
import uuid
from PIL import Image
import numpy as np
from utils import get_new_image_name
import torch
from PIL import Image
from transformers import (
CLIPSegProcessor,
@ -12,7 +10,6 @@ from transformers import (
)
from transformers import (
BlipProcessor,
BlipForConditionalGeneration,
BlipForQuestionAnswering,
)
@ -23,6 +20,7 @@ from diffusers import (
)
from diffusers import EulerAncestralDiscreteScheduler
from utils import get_new_image_name
from .base import tool, BaseToolSet

View File

@ -31,7 +31,7 @@ def prompts(name, description):
def cut_dialogue_history(history_memory, keep_last_n_words=500):
tokens = history_memory.split()
n_tokens = len(tokens)
print(f"hitory_memory:{history_memory}, n_tokens: {n_tokens}")
print(f"history_memory:{history_memory}, n_tokens: {n_tokens}")
if n_tokens < keep_last_n_words:
return history_memory
else: