Add Dockerfile + concept of a DATA_DIR

- Add a Dockerfile so people can run this bot in a docker container
- Stuck with recommendation of running with python3.9 for now
  - Will later test with 3.11 + supply fixes if I get this working ...
- Added a DATA_DIR env param to use to choose the directory to write data we want persistent across docker container restarts to be written to
  - We default to CWD like the code does today - we just explicitly pass it to functions / classes

Test:
- `docker build -t gpt3discord .`
```
crl-m1:GPT3Discord cooper$ docker image ls
REPOSITORY                     TAG               IMAGE ID       CREATED          SIZE
gpt3discord                    latest            6d2832af2450   69 seconds ago   356MB
```
- Try run it ... I would guess if I had correct tokens things would work ...
  - To do so I plan to bind mount over /bin/.env on my docker container when I run this ...
```
crl-m1:GPT3Discord cooper$ docker run gpt3discord
None of PyTorch, TensorFlow >= 2.0, or Flax have been found. Models won't be available and only tokenizers, configuration and file/data utilities can be used.
Downloading: 100%|██████████| 1.04M/1.04M [00:02<00:00, 516kB/s]
Downloading: 100%|██████████| 456k/456k [00:01<00:00, 319kB/s]
Downloading: 100%|██████████| 1.36M/1.36M [00:03<00:00, 443kB/s]
Downloading: 100%|██████████| 665/665 [00:00<00:00, 740kB/s]
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/discord/http.py", line 413, in static_login
    data = await self.request(Route("GET", "/users/@me"))
  File "/usr/local/lib/python3.9/site-packages/discord/http.py", line 366, in request
    raise HTTPException(response, data)
discord.errors.HTTPException: 401 Unauthorized (error code: 0): 401: Unauthorized

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/bin/gpt3discord", line 79, in <module>
    asyncio.get_event_loop().run_until_complete(main())
  File "/usr/local/lib/python3.9/asyncio/base_events.py", line 647, in run_until_complete
    return future.result()
  File "/bin/gpt3discord", line 63, in main
    await bot.start(os.getenv("DISCORD_TOKEN"))
  File "/usr/local/lib/python3.9/site-packages/discord/client.py", line 658, in start
    await self.login(token)
  File "/usr/local/lib/python3.9/site-packages/discord/client.py", line 514, in login
    data = await self.http.static_login(token.strip())
  File "/usr/local/lib/python3.9/site-packages/discord/http.py", line 417, in static_login
    raise LoginFailure("Improper token has been passed.") from exc
discord.errors.LoginFailure: Improper token has been passed.
Wrote PID to file the file bot.pid
The debug channel and guild IDs are 755420092027633774 and 907974109084942396
Improper token has been passed.
Removing PID file
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0xffff721a2dc0>
Unclosed connector
connections: ['[(<aiohttp.client_proto.ResponseHandler object at 0xffff718fe0a0>, 170230.336548951)]']
connector: <aiohttp.connector.TCPConnector object at 0xffff721a2fd0>
```
Cooper Ry Lees 2 years ago
parent 639c10643c
commit d6885b3c35

@ -0,0 +1,24 @@
ARG PY_VERSION=3.9
FROM python:${PY_VERSION} as base
FROM base as builder
ARG PY_VERSION
RUN mkdir /install
WORKDIR /install
RUN pip install --target="/install" --upgrade pip setuptools wheel
ADD requirements.txt /install
RUN pip install --target="/install" -r requirements.txt
FROM python:${PY_VERSION}-slim
ARG PY_VERSION
COPY --from=builder /install /usr/local/lib/python${PY_VERSION}/site-packages
COPY cogs /usr/local/lib/python${PY_VERSION}/site-packages/cogs
COPY models /usr/local/lib/python${PY_VERSION}/site-packages/models
COPY main.py /bin/gpt3discord
CMD ["python3", "/bin/gpt3discord"]

@ -115,12 +115,29 @@ After login, we need to install the various dependencies that the bot needs. To
21 screen -r
```
## Docker Installation
We now have a `Dockerfile` in the repository. This will build / install all dependencies and put a `gpt3discord` binary (main.py) into path.
To build:
- [Install docker](https://docs.docker.com/get-docker/)
- Clone repository and build *(hopefully eventually we'll add CI to automatically build + push to docker hub)*
- `docker build -t gpt3discord .`
- *From repository root or supply path to repository*
- Make a env file to bind mount to /bin/.env
- 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`
- 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.
## Bot on discord:
- Create a new Bot on Discord Developer Portal:
- Applications -> New Application
- Generate Toker for the app (discord_bot_token)
- Generate Token for the app (discord_bot_token)
- Select App (Bot) -> Bot -> Reset Token
- Toogle PRESENCE INTENT:
- Select App (Bot) -> Bot -> PRESENCE INTENT, SERVER MEMBERS INTENT, MESSAGES INTENT, (basically turn on all intents)

@ -7,6 +7,7 @@ import re
import threading
import time
import traceback
from pathlib import Path
import discord
from discord.ext import commands
@ -32,7 +33,9 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"):
deletion_queue,
DEBUG_GUILD,
DEBUG_CHANNEL,
data_path: Path,
):
self.data_path = data_path
self.debug_channel = None
self.bot = bot
self._last_member_ = None
@ -57,13 +60,14 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"):
self.redo_users = {}
try:
conversation_file_path = data_path / "conversation_starter_pretext.txt"
# Attempt to read a conversation starter text string from the file.
with open("conversation_starter_pretext.txt", "r") as f:
with conversation_file_path.open("r") as f:
self.CONVERSATION_STARTER_TEXT = f.read()
print("Conversation starter text loaded from file.")
print(f"Conversation starter text loaded from {conversation_file_path}.")
assert self.CONVERSATION_STARTER_TEXT is not None
except:
except Exception:
self.CONVERSATION_STARTER_TEXT = (
"You are an artificial intelligence that is able to do anything, and answer any question,"
"I want you to be my personal assistant and help me with some tasks. "

@ -34,11 +34,12 @@ class ImgPromptOptimizer(commands.Cog, name="ImgPromptOptimizer"):
self.deletion_queue = deletion_queue
try:
image_pretext_path = self.converser_cog.data_path / "image_optimizer_pretext.txt"
# Try to read the image optimizer pretext from
# the file system
with open("image_optimizer_pretext.txt", "r") as file:
with image_pretext_path.open("r") as file:
self.OPTIMIZER_PRETEXT = file.read()
print("Loaded image optimizer pretext from file system")
print(f"Loaded image optimizer pretext from {image_pretext_path}")
except:
traceback.print_exc()
self.OPTIMIZER_PRETEXT = self._OPTIMIZER_PRETEXT

@ -1,6 +1,7 @@
import asyncio
import sys
import traceback
from pathlib import Path
import discord
from discord.ext import commands
@ -29,7 +30,7 @@ asyncio.ensure_future(Deletion.process_deletion_queue(deletion_queue, 1, 1))
Settings for the bot
"""
bot = commands.Bot(intents=discord.Intents.all(), command_prefix="!")
usage_service = UsageService()
usage_service = UsageService(Path(os.environ.get("DATA_DIR", os.getcwd())))
model = Model(usage_service)
@ -44,9 +45,13 @@ async def on_ready(): # I can make self optional by
async def main():
data_path = Path(os.environ.get("DATA_DIR", os.getcwd()))
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?")
# Load the main GPT3 Bot service
bot.add_cog(
GPT3ComCon(
@ -57,6 +62,7 @@ async def main():
deletion_queue,
debug_guild,
debug_channel,
data_path,
)
)

@ -1,13 +1,15 @@
import os
from pathlib import Path
from transformers import GPT2TokenizerFast
class UsageService:
def __init__(self):
def __init__(self, data_dir: Path):
self.usage_file_path = data_dir / "usage.txt"
# If the usage.txt file doesn't currently exist in the directory, create it and write 0.00 to it.
if not os.path.exists("usage.txt"):
with open("usage.txt", "w") as f:
if not self.usage_file_path.exists():
with self.usage_file_path.open("w") as f:
f.write("0.00")
f.close()
self.tokenizer = GPT2TokenizerFast.from_pretrained("gpt2")
@ -18,17 +20,17 @@ class UsageService:
print("This request cost " + str(price) + " credits")
usage = self.get_usage()
print("The current usage is " + str(usage) + " credits")
with open("usage.txt", "w") as f:
with self.usage_file_path.open("w") as f:
f.write(str(usage + float(price)))
f.close()
def set_usage(self, usage):
with open("usage.txt", "w") as f:
with self.usage_file_path.open("w") as f:
f.write(str(usage))
f.close()
def get_usage(self):
with open("usage.txt", "r") as f:
with self.usage_file_path.open("r") as f:
usage = float(f.read().strip())
f.close()
return usage
@ -53,6 +55,6 @@ class UsageService:
usage = self.get_usage()
with open("usage.txt", "w") as f:
with self.usage_file_path.open("w") as f:
f.write(str(usage + float(price)))
f.close()

Loading…
Cancel
Save