From ddbe356e2ae63af19374ebf61214da306f9f4517 Mon Sep 17 00:00:00 2001 From: Justin McPherson Date: Mon, 2 Jan 2023 23:39:51 +1000 Subject: [PATCH] Changes to environment loading. - Move Some environment loading code to EnvService. - Attempt loading of env file from multiple locations & names - Provide a path fetching + fallback function for reading paths from environment vars. - For Docker; - Create /opt/gpt3discord/{etc,bin,shared} - Copy .txt shared data into shared - Copy main .py into bin --- Dockerfile | 6 ++++-- README.md | 2 +- cogs/gpt_3_commands_and_converser.py | 6 ++++-- cogs/image_prompt_optimizer.py | 2 +- gpt3discord.py | 14 ++++++------ models/env_service_model.py | 32 ++++++++++++++++++++++++++-- 6 files changed, 48 insertions(+), 14 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8bd1a09..daf5f15 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,5 +23,7 @@ RUN pip install --target="/install" /src FROM python:${PY_VERSION}-slim ARG PY_VERSION COPY --from=builder /install /usr/local/lib/python${PY_VERSION}/site-packages -COPY gpt3discord.py /bin/gpt3discord -CMD ["python3", "/bin/gpt3discord"] +RUN mkdir -p /opt/gpt3discord/etc +COPY gpt3discord.py /opt/gpt3discord/bin/ +COPY image_optimizer_pretext.txt conversation_starter_pretext.txt conversation_starter_pretext_minimal.txt /opt/gpt3discord/share/ +CMD ["python3", "/opt/gpt3discord/bin/gpt3discord.py"] diff --git a/README.md b/README.md index 1873c72..0e3183e 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ To build: - Optional: Make a data directory + bind mount it - Add `DATA_DIR=/data` to env file - Run via docker: - - `docker run [-d] --name gpt3discord -v env_file:/bin/.env [-v /containers/gpt3discord:/data] gpt3discord` + - `docker run [-d] --name gpt3discord -v env_file:/opt/gpt3discord/etc/environment [-v /containers/gpt3discord:/data] gpt3discord` - You can also mount a second volume and set `DATA_DIR` in the env file to keep persistent data This can also be run via screen/tmux or detached like a daemon. diff --git a/cogs/gpt_3_commands_and_converser.py b/cogs/gpt_3_commands_and_converser.py index a729037..8b3052d 100644 --- a/cogs/gpt_3_commands_and_converser.py +++ b/cogs/gpt_3_commands_and_converser.py @@ -31,9 +31,11 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): DEBUG_GUILD, DEBUG_CHANNEL, data_path: Path, + share_path: Path, ): super().__init__() self.data_path = data_path + self.share_path = share_path self.debug_channel = None self.bot = bot self._last_member_ = None @@ -57,7 +59,7 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): self.awaiting_responses = [] try: - conversation_file_path = data_path / "conversation_starter_pretext.txt" + conversation_file_path = share_path / "conversation_starter_pretext.txt" # Attempt to read a conversation starter text string from the file. with conversation_file_path.open("r") as f: self.CONVERSATION_STARTER_TEXT = f.read() @@ -67,7 +69,7 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): assert self.CONVERSATION_STARTER_TEXT is not None conversation_file_path_minimal = ( - data_path / "conversation_starter_pretext_minimal.txt" + share_path / "conversation_starter_pretext_minimal.txt" ) with conversation_file_path_minimal.open("r") as f: self.CONVERSATION_STARTER_TEXT_MINIMAL = f.read() diff --git a/cogs/image_prompt_optimizer.py b/cogs/image_prompt_optimizer.py index f6974a8..9b56af0 100644 --- a/cogs/image_prompt_optimizer.py +++ b/cogs/image_prompt_optimizer.py @@ -36,7 +36,7 @@ class ImgPromptOptimizer(commands.Cog, name="ImgPromptOptimizer"): try: image_pretext_path = ( - self.converser_cog.data_path / "image_optimizer_pretext.txt" + self.converser_cog.share_path / "image_optimizer_pretext.txt" ) # Try to read the image optimizer pretext from # the file system diff --git a/gpt3discord.py b/gpt3discord.py index b42205c..fb4ad05 100644 --- a/gpt3discord.py +++ b/gpt3discord.py @@ -5,7 +5,6 @@ from pathlib import Path import discord from discord.ext import commands -from dotenv import load_dotenv import os if sys.platform == "win32": @@ -13,9 +12,6 @@ if sys.platform == "win32": else: separator = "/" -print("The environment file is located at " + os.getcwd() + separator + ".env") -load_dotenv(dotenv_path=os.getcwd() + separator + ".env") - from cogs.draw_image_generation import DrawDallEService from cogs.gpt_3_commands_and_converser import GPT3ComCon from cogs.image_prompt_optimizer import ImgPromptOptimizer @@ -23,6 +19,7 @@ from models.deletion_service_model import Deletion from models.message_model import Message from models.openai_model import Model from models.usage_service_model import UsageService +from models.env_service_model import EnvService __version__ = "2.1.3" @@ -67,12 +64,16 @@ async def on_application_command_error( async def main(): - data_path = Path(os.environ.get("DATA_DIR", os.getcwd())) + share_path = EnvService.environment_path_with_fallback("SHARE_DIR", "share") + data_path = EnvService.environment_path_with_fallback("DATA_DIR") debug_guild = int(os.getenv("DEBUG_GUILD")) debug_channel = int(os.getenv("DEBUG_CHANNEL")) if not data_path.exists(): - raise OSError(f"{data_path} does not exist ... create it?") + raise OSError(f"Data path: {data_path} does not exist ... create it?") + + if not share_path.exists(): + raise OSError(f"Share path: {share_path} does not exist ... create it?") # Load the main GPT3 Bot service bot.add_cog( @@ -85,6 +86,7 @@ async def main(): debug_guild, debug_channel, data_path, + share_path, ) ) diff --git a/models/env_service_model.py b/models/env_service_model.py index 3536aa4..c989443 100644 --- a/models/env_service_model.py +++ b/models/env_service_model.py @@ -1,7 +1,22 @@ +import os +import sys +from pathlib import Path from dotenv import load_dotenv -load_dotenv() -import os + +# /../../ +def app_root_path(): + try: + return Path(sys.argv[0]).resolve().parents[1] + except: + return Path() + +# None will let direnv do its' thing +env_paths = [Path() / ".env", app_root_path() / "etc/environment", None] + +for env_path in env_paths: + print("Loading environment from " + str(env_path)) + load_dotenv(dotenv_path=env_path) class EnvService: @@ -9,6 +24,19 @@ class EnvService: def __init__(self): self.env = {} + @staticmethod + def environment_path_with_fallback(env_name, relative_fallback = None): + dir = os.getenv(env_name) + if dir != None: + return Path(dir).resolve() + + if relative_fallback: + app_relative = (app_root_path() / relative_fallback).resolve() + if app_relative.exists: + return app_relative + + return Path() + @staticmethod def get_allowed_guilds(): # ALLOWED_GUILDS is a comma separated list of guild ids