From 9d6541ce75244e402caab89f0e81862519075de8 Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Thu, 2 Jan 2025 17:53:16 -0800 Subject: [PATCH] upgrading script to v2 with history --- scripts/discordbot.py | 137 +++++++++++++++++++----------------------- v2/bot.py | 36 ++++++----- 2 files changed, 84 insertions(+), 89 deletions(-) diff --git a/scripts/discordbot.py b/scripts/discordbot.py index de00566..9edf967 100644 --- a/scripts/discordbot.py +++ b/scripts/discordbot.py @@ -1,111 +1,100 @@ +import os import discord from discord.ext import commands +import openai from openai import OpenAI -import os + +from collections import deque from dotenv import load_dotenv # Load environment variables load_dotenv() +# Get environment variables +DISCORD_TOKEN = os.getenv('DISCORD_TOKEN') +OPENAI_API_KEY = os.getenv('OPENAI_API_KEY') +OPENWEBUI_API_BASE = os.getenv('OPENWEBUI_API_BASE') +MODEL_NAME = os.getenv('MODEL_NAME') + # Configure OpenAI client to point to OpenWebUI client = OpenAI( api_key=os.getenv('OPENAI_API_KEY'), base_url=os.getenv('OPENWEBUI_API_BASE') # e.g., "http://localhost:8080/v1" ) +# Configure OpenAI +# TODO: The 'openai.api_base' option isn't read in the client API. You will need to pass it when you instantiate the client, e.g. 'OpenAI(base_url=OPENWEBUI_API_BASE)' +# openai.api_base = OPENWEBUI_API_BASE + # Initialize Discord bot intents = discord.Intents.default() intents.message_content = True -bot = commands.Bot(command_prefix="!", intents=intents) +intents.messages = True +bot = commands.Bot(command_prefix='!', intents=intents) -# Add a dictionary to store conversation histories -conversation_histories = {} -DEFAULT_HISTORY_LIMIT = 50 -MAX_HISTORY_LIMIT = 200 +# Message history cache +channel_history = {} + +async def get_chat_history(channel, limit=100): + messages = [] + async for message in channel.history(limit=limit): + messages.append(f"{message.author.name}: {message.content}") + return "\n".join(reversed(messages)) + +async def get_ai_response(context, user_message): + formatted_prompt = f"##CONTEXT##\n{context}\n##ENDCONTEXT##\n\n{user_message}" -async def get_ai_response(prompt, channel_id=None): try: - messages = [] - - # Always include history if it exists for this channel - if channel_id in conversation_histories: - history = conversation_histories[channel_id][-400:] # Get last 400 messages (200 exchanges) - - # Format the history as a context block - history_text = "\n".join([ - f"{'User: ' if msg['role'] == 'user' else 'Assistant: '}{msg['content']}" - for msg in history - ]) - - formatted_prompt = f"{prompt}\n##CONTEXT##\n{history_text}\n##ENDCONTEXT##" - else: - formatted_prompt = prompt - - response = client.chat.completions.create( - model=os.getenv('MODEL_NAME') or "us.anthropic.claude-3-5-sonnet-20241022-v2:0", - messages=[{"role": "user", "content": formatted_prompt}], - temperature=0.7, - max_tokens=500 - ) - - # Store the conversation history if channel_id is provided - if channel_id: - if channel_id not in conversation_histories: - conversation_histories[channel_id] = [] - conversation_histories[channel_id].extend([ - {"role": "user", "content": prompt}, - {"role": "assistant", "content": response.choices[0].message.content} - ]) - - # Always keep last 400 messages (200 exchanges) - if len(conversation_histories[channel_id]) > 400: - conversation_histories[channel_id] = conversation_histories[channel_id][-400:] - + response = client.chat.completions.create(model=MODEL_NAME, + messages=[ + {"role": "user", "content": formatted_prompt} + ]) return response.choices[0].message.content except Exception as e: - print(f"Error getting AI response: {e}") - return "Sorry, I encountered an error while processing your request." + return f"Error: {str(e)}" + +@bot.event +async def on_ready(): + print(f'{bot.user} has connected to Discord!') @bot.event async def on_message(message): + # Ignore messages from the bot itself if message.author == bot.user: return - # Respond to DMs or when mentioned in a server - if isinstance(message.channel, discord.DMChannel) or bot.user in message.mentions: - # For mentions, remove the bot mention from the message - if bot.user in message.mentions: - prompt = message.content.replace(f'<@{bot.user.id}>', '').strip() - else: - prompt = message.content.strip() - - if not prompt: - await message.channel.send("Hello! How can I help you?") - return + should_respond = False + # Check if bot was mentioned + if bot.user in message.mentions: + should_respond = True + + # Check if message is a DM + if isinstance(message.channel, discord.DMChannel): + should_respond = True + + if should_respond: async with message.channel.typing(): - response = await get_ai_response( - prompt, - channel_id=str(message.channel.id) - ) - - if len(response) > 2000: - chunks = [response[i:i+2000] for i in range(0, len(response), 2000)] - for chunk in chunks: - await message.channel.send(chunk) - else: - await message.channel.send(response) + # Get chat history + history = await get_chat_history(message.channel) + + # Remove bot mention from the message + user_message = message.content.replace(f'<@{bot.user.id}>', '').strip() + + # Get AI response + response = await get_ai_response(history, user_message) + + # Send response + await message.reply(response) await bot.process_commands(message) def main(): - # Get the Discord token from environment variables - discord_token = os.getenv('DISCORD_TOKEN') - if not discord_token: - raise ValueError("Discord token not found in environment variables") + if not all([DISCORD_TOKEN, OPENAI_API_KEY, OPENWEBUI_API_BASE, MODEL_NAME]): + print("Error: Missing required environment variables") + return - # Run the bot - bot.run(discord_token) + bot.run(DISCORD_TOKEN) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/v2/bot.py b/v2/bot.py index e33a9c2..9edf967 100644 --- a/v2/bot.py +++ b/v2/bot.py @@ -2,6 +2,8 @@ import os import discord from discord.ext import commands import openai +from openai import OpenAI + from collections import deque from dotenv import load_dotenv @@ -14,9 +16,15 @@ OPENAI_API_KEY = os.getenv('OPENAI_API_KEY') OPENWEBUI_API_BASE = os.getenv('OPENWEBUI_API_BASE') MODEL_NAME = os.getenv('MODEL_NAME') +# Configure OpenAI client to point to OpenWebUI +client = OpenAI( + api_key=os.getenv('OPENAI_API_KEY'), + base_url=os.getenv('OPENWEBUI_API_BASE') # e.g., "http://localhost:8080/v1" +) + # Configure OpenAI -openai.api_key = OPENAI_API_KEY -openai.api_base = OPENWEBUI_API_BASE +# TODO: The 'openai.api_base' option isn't read in the client API. You will need to pass it when you instantiate the client, e.g. 'OpenAI(base_url=OPENWEBUI_API_BASE)' +# openai.api_base = OPENWEBUI_API_BASE # Initialize Discord bot intents = discord.Intents.default() @@ -35,14 +43,12 @@ async def get_chat_history(channel, limit=100): async def get_ai_response(context, user_message): formatted_prompt = f"##CONTEXT##\n{context}\n##ENDCONTEXT##\n\n{user_message}" - + try: - response = openai.ChatCompletion.create( - model=MODEL_NAME, - messages=[ - {"role": "user", "content": formatted_prompt} - ] - ) + response = client.chat.completions.create(model=MODEL_NAME, + messages=[ + {"role": "user", "content": formatted_prompt} + ]) return response.choices[0].message.content except Exception as e: return f"Error: {str(e)}" @@ -58,11 +64,11 @@ async def on_message(message): return should_respond = False - + # Check if bot was mentioned if bot.user in message.mentions: should_respond = True - + # Check if message is a DM if isinstance(message.channel, discord.DMChannel): should_respond = True @@ -71,13 +77,13 @@ async def on_message(message): async with message.channel.typing(): # Get chat history history = await get_chat_history(message.channel) - + # Remove bot mention from the message user_message = message.content.replace(f'<@{bot.user.id}>', '').strip() - + # Get AI response response = await get_ai_response(history, user_message) - + # Send response await message.reply(response) @@ -87,7 +93,7 @@ def main(): if not all([DISCORD_TOKEN, OPENAI_API_KEY, OPENWEBUI_API_BASE, MODEL_NAME]): print("Error: Missing required environment variables") return - + bot.run(DISCORD_TOKEN) if __name__ == "__main__":