Add Quart web interface and prompt handling; update queue state and logging

This commit is contained in:
pacnpal
2025-02-11 22:02:36 -05:00
parent 44d4ad1dfd
commit 9efd4454fe
10 changed files with 399 additions and 37 deletions

View File

@@ -192,11 +192,10 @@ class DiscordBot:
await internal_site.start()
logger.info("Internal API server started")
# Start HTTP server for backend prompts
http_port = int(os.getenv('WEB_PORT', '8080'))
self.http_server = HTTPServer(self.event_handler)
await self.http_server.start(port=http_port)
logger.info(f"Web server started on port {http_port}")
# Initialize and start web interface
from discord_glhf.web.app import init_app
self.web_app = init_app(self.event_handler)
logger.info("Web interface initialized with event handler")
# Set bot status
activity = Game(name="with roller coasters")

View File

@@ -0,0 +1,4 @@
2025-02-11 20:23:21 - INFO - discord_bot - <module>:211 - Using database path: conversation_history.db
2025-02-11 20:23:21 - INFO - discord_bot - load_responses:250 - Loaded responses from file
2025-02-11 20:23:21 - DEBUG - asyncio - __init__:64 - Using selector: KqueueSelector
2025-02-11 20:23:21 - INFO - hypercorn.error - info:106 - Running on http://0.0.0.0:8080 (CTRL + C to quit)

View File

@@ -44,6 +44,69 @@ class EventHandler:
# Replace all mention formats with the proper mention
return re.sub(pattern, mention, response)
async def send_prompt_to_channel(self, prompt: str, channel_id: int) -> None:
"""Send a prompt to the specified channel."""
try:
channel = self.bot.get_channel(channel_id)
if not channel:
logger.error(f"Could not find channel {channel_id}")
return
# Build context for the API call
messages = [
{
"role": "system",
"content": SYSTEM_PROMPT,
"metadata": {
"bot_owner_id": str(BOT_OWNER_ID),
"current_user": {
"user_id": "0" # System user
}
}
},
{
"role": "user",
"content": prompt,
"context": {
"timeout_env": "GLHF_TIMEOUT"
}
}
]
# Get response from API
response = await self.api_manager.get_completion(messages)
if not response:
logger.error("No response received from API")
return
# Parse tool calls and get processed response
tool_calls, final_response, mentioned_users = self.tool_handler.parse_tool_calls(
response, channel_id=channel_id
)
# Execute tool calls
for tool_name, args in tool_calls:
try:
if tool_name == "create_embed":
await self.tool_handler.create_embed(
channel=channel, content=args["content"]
)
elif tool_name == "create_thread":
await self.tool_handler.create_thread(
channel.id, args["name"]
)
except Exception as e:
logger.error(f"Error executing tool {tool_name}: {e}")
# Send the response
if final_response:
logger.info(f"Bot response to prompt: {final_response}")
await self.message_handler.safe_send(channel, final_response)
except Exception as e:
logger.error(f"Error processing prompt: {e}")
raise
async def handle_reaction(self, payload: RawReactionActionEvent) -> None:
"""Handle reaction events on bot messages."""
# Ignore our own reactions

View File

@@ -4,7 +4,11 @@
# Function to stop background processes on exit
cleanup() {
echo "Stopping processes..."
if [ ! -z "$WEB_PID" ]; then
kill $WEB_PID 2>/dev/null
fi
kill $(jobs -p) 2>/dev/null
wait
exit
}
@@ -25,7 +29,11 @@ sleep 2
# Start the web interface
echo "Starting web interface on port ${WEB_PORT}..."
cd $(dirname "$0")
PYTHONPATH=/Volumes/macminissd/Projects/discord_glhf /Users/talor/Projects/discord_glhf/.venv/bin/python3 web/app.py
# Run web interface with proper Python path and virtualenv
PYTHONPATH=/Volumes/macminissd/Projects/discord_glhf /Users/talor/Projects/discord_glhf/.venv/bin/python3 web/app.py &
WEB_PID=$!
# Wait a moment for web interface to start
sleep 2
# This will be caught by the trap
wait

View File

@@ -1,55 +1,71 @@
#!/usr/bin/env python3
"""Web interface for sending prompts to the Discord bot."""
from flask import Flask, render_template, request, jsonify
import requests
from quart import Quart, render_template, request, jsonify
import os
from pathlib import Path
app = Flask(__name__)
app.template_folder = str(Path(__file__).parent / 'templates')
def async_route(f):
@wraps(f)
def wrapped(*args, **kwargs):
return asyncio.run(f(*args, **kwargs))
return wrapped
# Get configuration from environment variables
API_PORT = int(os.getenv('HTTP_PORT', '8000'))
BACKEND_API_KEY = os.getenv('BACKEND_API_KEY')
from discord_glhf.config import AUTO_RESPONSE_CHANNEL_ID
app = Quart(__name__)
app.template_folder = str(Path(__file__).parent / 'templates')
event_handler = None
def init_app(bot_event_handler):
"""Initialize the app with the bot's event handler."""
global event_handler
event_handler = bot_event_handler
return app
@app.route('/')
def index():
async def index():
"""Render the main interface."""
return render_template('index.html')
return await render_template('index.html')
@app.route('/api/prompt', methods=['POST'])
def send_prompt():
async def send_prompt():
"""Handle prompt submission."""
try:
if not event_handler:
return jsonify({'error': 'Bot not initialized'}), 503
data = request.get_json()
if not data or 'prompt' not in data:
return jsonify({'error': 'Missing prompt'}), 400
# Forward the request to the bot's HTTP server
headers = {'Content-Type': 'application/json'}
if BACKEND_API_KEY:
headers['X-API-Key'] = BACKEND_API_KEY
response = requests.post(
'http://127.0.0.1:8000/api/prompt', # Use fixed internal API port
json=data,
headers=headers
)
return response.json(), response.status_code
channel_id = data.get('channel_id', AUTO_RESPONSE_CHANNEL_ID)
await event_handler.send_prompt_to_channel(data['prompt'], channel_id)
return jsonify({'status': 'processing'}), 200
except Exception as e:
return jsonify({'error': str(e)}), 500
def run_webserver(port=5000):
"""Run the web server."""
app.run(host='0.0.0.0', port=port, debug=True)
import hypercorn.asyncio
from hypercorn.config import Config
def run():
"""Run the web server."""
port = int(os.getenv('WEB_PORT', '5000'))
app.run(host='0.0.0.0', port=port)
config = Config()
config.bind = [f"0.0.0.0:{port}"]
config.use_reloader = True
if __name__ == '__main__':
run()
import asyncio
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
loop.run_until_complete(hypercorn.asyncio.serve(app, config))
except KeyboardInterrupt:
pass
finally:
loop.close()
if __name__ == "__main__":
from discord_glhf.config import AUTO_RESPONSE_CHANNEL_ID
port = int(os.getenv('WEB_PORT', '8080'))
run_webserver(port)