Refactor/file store (#18)

* feat: change port variable name

* feat: chdir to playground

* refactor: etc

* feat: add verify decorator
This commit is contained in:
Taeho 2023-04-07 11:11:24 +09:00 committed by GitHub
parent 4cd1d35914
commit e5165d6f90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 64 additions and 69 deletions

View File

@ -77,7 +77,7 @@ Manatory envs are required in order to serve EVAL.
Each optional env has default value, so you don't need to set unless you want to change it. Each optional env has default value, so you don't need to set unless you want to change it.
- `PORT` - port (default: 8000) - `EVAL_PORT` - port (default: 8000)
- `SERVER` - server address (default: http://localhost:8000) - `SERVER` - server address (default: http://localhost:8000)
- `LOG_LEVEL` - INFO | DEBUG (default: INFO) - `LOG_LEVEL` - INFO | DEBUG (default: INFO)
- `BOT_NAME` - give it a name! (default: Orca) - `BOT_NAME` - give it a name! (default: Orca)

View File

@ -1,3 +1,4 @@
import os
import re import re
from typing import Dict, List, TypedDict from typing import Dict, List, TypedDict
@ -23,7 +24,7 @@ app = FastAPI()
app.mount("/static", StaticFiles(directory=StaticUploader.STATIC_DIR), name="static") app.mount("/static", StaticFiles(directory=StaticUploader.STATIC_DIR), name="static")
uploader = StaticUploader.from_settings(settings) uploader = StaticUploader.from_settings(settings)
os.chdir(settings["PLAYGROUND_DIR"])
toolsets: List[BaseToolSet] = [ toolsets: List[BaseToolSet] = [
Terminal(), Terminal(),
@ -91,7 +92,7 @@ async def command(request: Request) -> Response:
except Exception as e: except Exception as e:
return {"response": str(e), "files": []} return {"response": str(e), "files": []}
files = re.findall("(image/\S*png)|(dataframe/\S*csv)", res["output"]) files = re.findall("image/\S*png|dataframe/\S*csv", res["output"])
return { return {
"response": res["output"], "response": res["output"],
@ -100,4 +101,4 @@ async def command(request: Request) -> Response:
def serve(): def serve():
uvicorn.run("api.main:app", host="0.0.0.0", port=settings["PORT"]) uvicorn.run("api.main:app", host="0.0.0.0", port=settings["EVAL_PORT"])

View File

@ -1,10 +1,13 @@
import os import os
import uuid import uuid
from enum import Enum from enum import Enum
from pathlib import Path
from typing import Dict from typing import Dict
import requests import requests
from env import settings
class FileType(Enum): class FileType(Enum):
IMAGE = "image" IMAGE = "image"
@ -64,6 +67,7 @@ class FileHandler:
local_filename = os.path.join( local_filename = os.path.join(
filetype.value, str(uuid.uuid4())[0:8] + filetype.to_extension() filetype.value, str(uuid.uuid4())[0:8] + filetype.to_extension()
) )
os.makedirs(os.path.dirname(local_filename), exist_ok=True)
with open(local_filename, "wb") as f: with open(local_filename, "wb") as f:
size = f.write(data) size = f.write(data)
print(f"Inputs: {url} ({size//1000}MB) => {local_filename}") print(f"Inputs: {url} ({size//1000}MB) => {local_filename}")

View File

@ -9,7 +9,7 @@ class CsvToDataframe(BaseHandler):
def handle(self, filename: str): def handle(self, filename: str):
df = pd.read_csv(filename) df = pd.read_csv(filename)
description = ( description = (
f"Dataframe with {len(df)} rows and {len(df.columns)} columns." f"Dataframe with {len(df)} rows and {len(df.columns)} columns. "
"Columns are: " "Columns are: "
f"{', '.join(df.columns)}" f"{', '.join(df.columns)}"
) )

View File

@ -2,6 +2,9 @@ IMAGE_PROMPT = """
provide a figure named {filename}. The description is: {description}. provide a figure named {filename}. The description is: {description}.
Please understand and answer the image based on this information. The image understanding is complete, so don't try to understand the image again. Please understand and answer the image based on this information. The image understanding is complete, so don't try to understand the image again.
USER INPUT
============
""" """
@ -9,12 +12,18 @@ AUDIO_PROMPT = """
provide a audio named {filename}. The description is: {description}. provide a audio named {filename}. The description is: {description}.
Please understand and answer the audio based on this information. The audio understanding is complete, so don't try to understand the audio again. Please understand and answer the audio based on this information. The audio understanding is complete, so don't try to understand the audio again.
USER INPUT
============
""" """
VIDEO_PROMPT = """ VIDEO_PROMPT = """
provide a video named {filename}. The description is: {description}. provide a video named {filename}. The description is: {description}.
Please understand and answer the video based on this information. The video understanding is complete, so don't try to understand the video again. Please understand and answer the video based on this information. The video understanding is complete, so don't try to understand the video again.
USER INPUT
============
""" """
DATAFRAME_PROMPT = """ DATAFRAME_PROMPT = """
@ -22,4 +31,7 @@ provide a dataframe named {filename}. The description is: {description}.
You are able to use the dataframe to answer the question. You are able to use the dataframe to answer the question.
You have to act like an data analyst who can do an effective analysis through dataframe. You have to act like an data analyst who can do an effective analysis through dataframe.
USER INPUT
============
""" """

View File

@ -1,7 +1,7 @@
EVAL_PREFIX = """{bot_name} can execute any user's request. EVAL_PREFIX = """{bot_name} can execute any user's request.
{bot_name} has permission to handle one instance and can handle the environment in it at will. File creation is only possible in playground folder, and other folders and files can never be modified under any circumstances. {bot_name} has permission to handle one instance and can handle the environment in it at will.
You can code, run, debug, and test yourself. You can correct the code appropriately by looking at the error message. Please do TDD. You can code, run, debug, and test yourself. You can correct the code appropriately by looking at the error message.
I can understand, process, and create various types of files. Images must be stored in the ./image/, audio in the ./audio/, video in the ./video/, and dataframes must be stored in the ./dataframe/. I can understand, process, and create various types of files. Images must be stored in the ./image/, audio in the ./audio/, video in the ./video/, and dataframes must be stored in the ./dataframe/.

View File

@ -55,16 +55,12 @@ class CodeEditor(BaseToolSet):
"Input should be filename and code to append. " "Input should be filename and code to append. "
"Input code must be the code that should be appended, NOT whole code. " "Input code must be the code that should be appended, NOT whole code. "
"ex. test.py\nprint('hello world')\n " "ex. test.py\nprint('hello world')\n "
"and the output will be last 3 line.", "and the output will be last 3 lines.",
) )
def append(self, inputs: str) -> str: def append(self, inputs: str) -> str:
try: try:
code = CodeWriter.append(inputs) code = CodeWriter.append(inputs)
output = ( output = "Last 3 line was:\n" + "\n".join(code.split("\n")[-3:])
"Last 3 line was:\n"
+ "\n".join(code.split("\n")[-3:])
+ "\nYou can use CodeEditor.APPEND tool to append the code if this file is not completed."
)
except Exception as e: except Exception as e:
output = str(e) output = str(e)
@ -80,16 +76,12 @@ class CodeEditor(BaseToolSet):
"If the code is completed, use the Terminal tool to execute it, if not, append the code through the CodeEditor.APPEND tool. " "If the code is completed, use the Terminal tool to execute it, if not, append the code through the CodeEditor.APPEND tool. "
"Input should be filename and code. This file must be in playground folder. " "Input should be filename and code. This file must be in playground folder. "
"ex. test.py\nprint('hello world')\n " "ex. test.py\nprint('hello world')\n "
"and the output will be last 3 line.", "and the output will be last 3 lines.",
) )
def write(self, inputs: str) -> str: def write(self, inputs: str) -> str:
try: try:
code = CodeWriter.write(inputs) code = CodeWriter.write(inputs)
output = ( output = "Last 3 line was:\n" + "\n".join(code.split("\n")[-3:])
"Last 3 line was:\n"
+ "\n".join(code.split("\n")[-3:])
+ "\nYou can use CodeEditor.APPEND tool to append the code if this file is not completed."
)
except Exception as e: except Exception as e:
output = str(e) output = str(e)
@ -136,7 +128,6 @@ class CodeEditor(BaseToolSet):
"Output will be success or error message.", "Output will be success or error message.",
) )
def delete(self, inputs: str) -> str: def delete(self, inputs: str) -> str:
filepath: str = str(Path(settings["PLAYGROUND_DIR"]) / Path(inputs))
try: try:
with open(filepath, "w") as f: with open(filepath, "w") as f:
f.write("") f.write("")

View File

@ -63,6 +63,8 @@ from typing import Tuple
from env import settings from env import settings
from .verify import verify
class Position: class Position:
separator = "," separator = ","
@ -84,7 +86,7 @@ class PatchCommand:
separator = "|" separator = "|"
def __init__(self, filepath: str, start: Position, end: Position, content: str): def __init__(self, filepath: str, start: Position, end: Position, content: str):
self.filepath: str = str(Path(settings["PLAYGROUND_DIR"]) / Path(filepath)) self.filepath: str = filepath
self.start: Position = start self.start: Position = start
self.end: Position = end self.end: Position = end
self.content: str = content self.content: str = content
@ -99,13 +101,8 @@ class PatchCommand:
f.writelines(lines) f.writelines(lines)
return sum([len(line) for line in lines]) return sum([len(line) for line in lines])
@verify
def execute(self) -> Tuple[int, int]: def execute(self) -> Tuple[int, int]:
# make sure the directory exists
if not str(Path(self.filepath).resolve()).startswith(
str(Path(settings["PLAYGROUND_DIR"]).resolve())
):
return "You can't write file outside of current directory."
os.makedirs(os.path.dirname(self.filepath), exist_ok=True) os.makedirs(os.path.dirname(self.filepath), exist_ok=True)
lines = self.read_lines() lines = self.read_lines()
before = sum([len(line) for line in lines]) before = sum([len(line) for line in lines])

View File

@ -8,6 +8,8 @@ from typing import List, Optional, Tuple
from env import settings from env import settings
from .verify import verify
class Line: class Line:
def __init__(self, content: str, line_number: int, depth: int): def __init__(self, content: str, line_number: int, depth: int):
@ -104,16 +106,12 @@ class ReadCommand:
separator = "|" separator = "|"
def __init__(self, filepath: str, start: int, end: int): def __init__(self, filepath: str, start: int, end: int):
self.filepath: str = str(Path(settings["PLAYGROUND_DIR"]) / Path(filepath)) self.filepath: str = filepath
self.start: int = start self.start: int = start
self.end: int = end self.end: int = end
@verify
def execute(self) -> str: def execute(self) -> str:
if not str(Path(self.filepath).resolve()).startswith(
str(Path(settings["PLAYGROUND_DIR"]).resolve())
):
return "You can't write file outside of current directory."
with open(self.filepath, "r") as f: with open(self.filepath, "r") as f:
code = f.readlines() code = f.readlines()
@ -134,16 +132,12 @@ class SummaryCommand:
separator = "|" separator = "|"
def __init__(self, filepath: str, depth: int, parent_content: Optional[str] = None): def __init__(self, filepath: str, depth: int, parent_content: Optional[str] = None):
self.filepath: str = str(Path(settings["PLAYGROUND_DIR"]) / Path(filepath)) self.filepath: str = filepath
self.depth: int = depth self.depth: int = depth
self.parent_content: Optional[str] = parent_content self.parent_content: Optional[str] = parent_content
@verify
def execute(self) -> str: def execute(self) -> str:
if not str(Path(self.filepath).resolve()).startswith(
str(Path(settings["PLAYGROUND_DIR"]).resolve())
):
return "You can't write file outside of current directory."
with open(self.filepath, "r") as f: with open(self.filepath, "r") as f:
code = f.readlines() code = f.readlines()
@ -152,8 +146,6 @@ class SummaryCommand:
if line.strip() != "": if line.strip() != "":
code_tree.append(line, i + 1) code_tree.append(line, i + 1)
# code_tree.print()
if self.parent_content is None: if self.parent_content is None:
lines = code_tree.find_from_root(self.depth) lines = code_tree.find_from_root(self.depth)
else: else:

View File

@ -0,0 +1,18 @@
from pathlib import Path
from env import settings
def verify(func):
def wrapper(*args, **kwargs):
try:
filepath = args[0].filepath
except:
raise Exception("This tool doesn't have filepath. Please check your code.")
if not str(Path(filepath).resolve()).startswith(
str(Path(settings["PLAYGROUND_DIR"]).resolve())
):
return "You can't access file outside of playground."
return func(*args, **kwargs)
return wrapper

View File

@ -9,12 +9,14 @@ from pathlib import Path
from env import settings from env import settings
from .verify import verify
class WriteCommand: class WriteCommand:
separator = "\n" separator = "\n"
def __init__(self, filepath: str, content: int): def __init__(self, filepath: str, content: int):
self.filepath: str = str(Path(settings["PLAYGROUND_DIR"]) / Path(filepath)) self.filepath: str = filepath
self.content: str = content self.content: str = content
self.mode: str = "w" self.mode: str = "w"
@ -22,14 +24,8 @@ class WriteCommand:
self.mode = mode self.mode = mode
return self return self
@verify
def execute(self) -> str: def execute(self) -> str:
# make sure the directory exists
if not str(Path(self.filepath).resolve()).startswith(
str(Path(settings["PLAYGROUND_DIR"]).resolve())
):
return "You can't write file outside of current directory."
os.makedirs(os.path.dirname(self.filepath), exist_ok=True)
with open(self.filepath, self.mode) as f: with open(self.filepath, self.mode) as f:
f.write(self.content) f.write(self.content)
return self.content return self.content

View File

@ -31,7 +31,6 @@ class Terminal(BaseToolSet):
process = subprocess.Popen( process = subprocess.Popen(
commands, commands,
shell=True, shell=True,
cwd=settings["PLAYGROUND_DIR"],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
) )

8
env.py
View File

@ -9,7 +9,7 @@ load_dotenv()
class DotEnv(TypedDict): class DotEnv(TypedDict):
OPENAI_API_KEY: str OPENAI_API_KEY: str
PORT: int EVAL_PORT: int
SERVER: str SERVER: str
USE_GPU: bool # optional USE_GPU: bool # optional
@ -27,10 +27,10 @@ class DotEnv(TypedDict):
SERPAPI_API_KEY: str # optional SERPAPI_API_KEY: str # optional
PORT = int(os.getenv("PORT", 8000)) EVAL_PORT = int(os.getenv("EVAL_PORT", 8000))
settings: DotEnv = { settings: DotEnv = {
"PORT": PORT, "EVAL_PORT": EVAL_PORT,
"SERVER": os.getenv("SERVER", f"http://localhost:{PORT}"), "SERVER": os.getenv("SERVER", f"http://localhost:{EVAL_PORT}"),
"USE_GPU": os.getenv("USE_GPU", "False").lower() == "true", "USE_GPU": os.getenv("USE_GPU", "False").lower() == "true",
"PLAYGROUND_DIR": os.getenv("PLAYGROUND_DIR", "playground"), "PLAYGROUND_DIR": os.getenv("PLAYGROUND_DIR", "playground"),
"OPENAI_API_KEY": os.getenv("OPENAI_API_KEY"), "OPENAI_API_KEY": os.getenv("OPENAI_API_KEY"),

View File

@ -4,12 +4,6 @@ import uuid
import numpy as np import numpy as np
os.makedirs("image", exist_ok=True)
os.makedirs("audio", exist_ok=True)
os.makedirs("video", exist_ok=True)
os.makedirs("dataframe", exist_ok=True)
os.makedirs("playground", exist_ok=True)
def seed_everything(seed): def seed_everything(seed):
random.seed(seed) random.seed(seed)
@ -24,15 +18,6 @@ def seed_everything(seed):
return seed return seed
def prompts(name, description):
def decorator(func):
func.name = name
func.description = description
return func
return decorator
def cut_dialogue_history(history_memory, keep_last_n_words=500): def cut_dialogue_history(history_memory, keep_last_n_words=500):
tokens = history_memory.split() tokens = history_memory.split()
n_tokens = len(tokens) n_tokens = len(tokens)