diff --git a/systems/docgpt/.env.test.example b/systems/docgpt/.env.test.example new file mode 100644 index 0000000..360e6b0 --- /dev/null +++ b/systems/docgpt/.env.test.example @@ -0,0 +1,18 @@ +# Gemini API key (or set GOOGLE_API_KEY) +AI_GEMINI_APIKEY= + +# Discord bot token (use a dedicated test bot token) +APP_DISCORD_TOKEN= + +# Force pgvector backend for isolated test database runs +STORAGE_VECTOR_BACKEND=pgvector + +# PostgreSQL test database (docker-compose.test.yml) +# Format: postgresql+psycopg://USER:PASSWORD@HOST:PORT/DATABASE +STORAGE_VECTOR_URL=postgresql+psycopg://root:example@localhost:55432/docgpt_test + +# MongoDB test database (docker-compose.test.yml) +STORAGE_MEMORY_URL=mongodb://root:example@localhost:27018/docgpt_test + +# Optional +LOG_LEVEL=INFO diff --git a/systems/docgpt/README.md b/systems/docgpt/README.md index 2bbd56f..7e22a9d 100644 --- a/systems/docgpt/README.md +++ b/systems/docgpt/README.md @@ -25,3 +25,70 @@ Ask about the R data.table package documentation and contribution guide. 2. Run ```docker compose up``` 3. Ingest data (once): ```uv run python main.py --ingest``` 4. Start the Discord bot: ```uv run python main.py``` + +## Manual bot testing with separate test DB + +Use this flow when you want to interact with the bot in Discord without touching normal dev data. + +1. Create a test env file from the template: + ```powershell + Copy-Item .env.test.example .env.test + ``` +2. Fill in `AI_GEMINI_APIKEY` and `APP_DISCORD_TOKEN` in `.env.test`. +3. Start isolated test databases: + ```powershell + docker compose -f docker-compose.test.yml up -d + ``` +4. Load `.env.test` into the current PowerShell session: + ```powershell + Get-Content .env.test | ForEach-Object { + if ($_ -match '^\s*#' -or $_ -match '^\s*$') { return } + $name, $value = $_ -split '=', 2 + Set-Item -Path "Env:$name" -Value $value + } + ``` +5. Ingest documents into the test vector database: + ```powershell + uv run python main.py --ingest + ``` +6. Run the Discord bot using the test DB settings: + ```powershell + uv run python main.py + ``` +7. Tear down and wipe test data when done: + ```powershell + docker compose -f docker-compose.test.yml down -v + ``` + +This keeps vector data and chat memory isolated to test services (`localhost:55432`, `localhost:27018`) and removes persisted test data on teardown. + +## Run a second bot on EC2 (side-by-side with prod) + +Use this when your production bot is already running and you want a separate test bot process. + +1. Create a second Discord bot application/token (test-only) and add it to a test server. +2. Prepare test environment values: + ```bash + cp .env.test.example .env.test + ``` +3. Edit `.env.test` and set: + - `APP_DISCORD_TOKEN` to the test bot token + - `AI_GEMINI_APIKEY` +4. Ingest test data once: + ```bash + set -a && source .env.test && set +a + docker compose -f docker-compose.test.yml up -d + uv run python main.py --ingest + ``` +5. Run the second bot: + ```bash + ./run-test-bot.sh + ``` + +Notes: +- Do not reuse the production bot token for the test bot. +- Production bot keeps using `.env`; test bot uses `.env.test`. +- Stop and wipe test data when finished: + ```bash + docker compose -f docker-compose.test.yml down -v + ``` diff --git a/systems/docgpt/docker-compose.test.yml b/systems/docgpt/docker-compose.test.yml new file mode 100644 index 0000000..fe3794f --- /dev/null +++ b/systems/docgpt/docker-compose.test.yml @@ -0,0 +1,31 @@ +version: "3" + +services: + vector_storage_test: + image: ankane/pgvector + container_name: vector_storage_test + restart: always + ports: + - "55432:5432" + environment: + - POSTGRES_USER=root + - POSTGRES_PASSWORD=example + - POSTGRES_DB=docgpt_test + volumes: + - docgpt_test_postgres_data:/var/lib/postgresql/data + + memory_storage_test: + image: mongo + container_name: memory_storage_test + restart: always + ports: + - "27018:27017" + environment: + MONGO_INITDB_ROOT_USERNAME: root + MONGO_INITDB_ROOT_PASSWORD: example + volumes: + - docgpt_test_mongo_data:/data/db + +volumes: + docgpt_test_postgres_data: + docgpt_test_mongo_data: diff --git a/systems/docgpt/run-test-bot.sh b/systems/docgpt/run-test-bot.sh new file mode 100644 index 0000000..68af4a9 --- /dev/null +++ b/systems/docgpt/run-test-bot.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ ! -f ".env.test" ]]; then + echo ".env.test not found. Create it from .env.test.example first." + exit 1 +fi + +# Export all variables from .env.test for this shell. +set -a +source .env.test +set +a + +echo "Starting isolated test databases..." +docker compose -f docker-compose.test.yml up -d + +echo "Running second (test) bot with .env.test configuration..." +uv run python main.py diff --git a/systems/docgpt/src/app/discord.py b/systems/docgpt/src/app/discord.py index 21d21db..202a687 100644 --- a/systems/docgpt/src/app/discord.py +++ b/systems/docgpt/src/app/discord.py @@ -147,8 +147,20 @@ async def on_message( add_start_index=True, ).split_text(result.answer) + first_reply_message: discord.Message | None = None for reply in response_chunks: - await user_message.reply(reply) + sent = await user_message.reply(reply) + if first_reply_message is None: + first_reply_message = sent + + # Add feedback reactions to the assistant's first reply (the answer), + # not to the original user question message. + if first_reply_message is not None: + try: + await first_reply_message.add_reaction("👍") + await first_reply_message.add_reaction("👎") + except Exception: + log.exception("Failed to add feedback reactions to assistant reply") if channel.name.lower() == NEW_THREAD_NAME.lower(): title_result = assistant.prompt(