mirror of
https://github.com/corca-ai/EVAL
synced 2024-10-30 09:20:44 +00:00
feat: refactor bot name (#2)
This commit is contained in:
parent
20165ec0a0
commit
f7892be801
@ -1,3 +1,4 @@
|
||||
BOT_NAME=<your-bot-name>
|
||||
AWS_ACCESS_KEY_ID=***
|
||||
AWS_SECRET_ACCESS_KEY=***
|
||||
AWS_REGION=***
|
||||
|
@ -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"]
|
47
README.md
47
README.md
@ -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
|
||||
|
@ -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
25
agents/parser.py
Normal 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"]}
|
@ -1,7 +1,9 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
awesomegpt:
|
||||
eval:
|
||||
container_name: eval
|
||||
image: eval
|
||||
build:
|
||||
dockerfile: Dockerfile
|
||||
context: .
|
||||
|
5
env.py
5
env.py
@ -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"),
|
||||
|
@ -1,6 +1,6 @@
|
||||
import os
|
||||
import requests
|
||||
import uuid
|
||||
import requests
|
||||
from typing import Dict
|
||||
from enum import Enum
|
||||
|
||||
|
@ -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)
|
||||
|
@ -4,6 +4,7 @@ from transformers import (
|
||||
BlipProcessor,
|
||||
BlipForConditionalGeneration,
|
||||
)
|
||||
|
||||
from prompts.file import IMAGE_PROMPT
|
||||
|
||||
from .base import BaseHandler
|
||||
|
5
main.py
5
main.py
@ -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")
|
||||
|
@ -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}}}}}}}}"""
|
||||
|
@ -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>"]
|
||||
|
43
tools/cpu.py
43
tools/cpu.py
@ -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}"
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
2
utils.py
2
utils.py
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user