upgrading script to v2 with history
All checks were successful
OpenWebUI Discord Bot / Build-and-Push (push) Successful in 1m3s

This commit is contained in:
Josh Knapp 2025-01-02 17:53:16 -08:00
parent 37b363b317
commit 6c57a8591f
3 changed files with 88 additions and 89 deletions

View File

@ -1,111 +1,100 @@
import os
import discord import discord
from discord.ext import commands from discord.ext import commands
import openai
from openai import OpenAI from openai import OpenAI
import os
from collections import deque
from dotenv import load_dotenv from dotenv import load_dotenv
# Load environment variables # Load environment variables
load_dotenv() 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 # Configure OpenAI client to point to OpenWebUI
client = OpenAI( client = OpenAI(
api_key=os.getenv('OPENAI_API_KEY'), api_key=os.getenv('OPENAI_API_KEY'),
base_url=os.getenv('OPENWEBUI_API_BASE') # e.g., "http://localhost:8080/v1" 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 # Initialize Discord bot
intents = discord.Intents.default() intents = discord.Intents.default()
intents.message_content = True 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 # Message history cache
conversation_histories = {} channel_history = {}
DEFAULT_HISTORY_LIMIT = 50
MAX_HISTORY_LIMIT = 200 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: try:
messages = [] response = client.chat.completions.create(model=MODEL_NAME,
messages=[
# Always include history if it exists for this channel {"role": "user", "content": formatted_prompt}
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:]
return response.choices[0].message.content return response.choices[0].message.content
except Exception as e: except Exception as e:
print(f"Error getting AI response: {e}") return f"Error: {str(e)}"
return "Sorry, I encountered an error while processing your request."
@bot.event
async def on_ready():
print(f'{bot.user} has connected to Discord!')
@bot.event @bot.event
async def on_message(message): async def on_message(message):
# Ignore messages from the bot itself
if message.author == bot.user: if message.author == bot.user:
return return
# Respond to DMs or when mentioned in a server should_respond = False
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
# 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(): async with message.channel.typing():
response = await get_ai_response( # Get chat history
prompt, history = await get_chat_history(message.channel)
channel_id=str(message.channel.id)
) # Remove bot mention from the message
user_message = message.content.replace(f'<@{bot.user.id}>', '').strip()
if len(response) > 2000:
chunks = [response[i:i+2000] for i in range(0, len(response), 2000)] # Get AI response
for chunk in chunks: response = await get_ai_response(history, user_message)
await message.channel.send(chunk)
else: # Send response
await message.channel.send(response) await message.reply(response)
await bot.process_commands(message) await bot.process_commands(message)
def main(): def main():
# Get the Discord token from environment variables if not all([DISCORD_TOKEN, OPENAI_API_KEY, OPENWEBUI_API_BASE, MODEL_NAME]):
discord_token = os.getenv('DISCORD_TOKEN') print("Error: Missing required environment variables")
if not discord_token: return
raise ValueError("Discord token not found in environment variables")
# Run the bot bot.run(DISCORD_TOKEN)
bot.run(discord_token)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

4
v2/.env Normal file
View File

@ -0,0 +1,4 @@
DISCORD_TOKEN="MTMxOTQ2NjUwMDM2MDUwMzM4OA.GHRFj6.xb-SgxDr6s_XnxqJvu_dlwKWEjvRNoCfxFNDu4"
OPENAI_API_KEY="sk-2cdddfca57424dbfae6e9f474b239eb4"
OPENWEBUI_API_BASE="https://chat.dnspegasus.net/api"
MODEL_NAME="discord-ai-bot"

View File

@ -2,6 +2,8 @@ import os
import discord import discord
from discord.ext import commands from discord.ext import commands
import openai import openai
from openai import OpenAI
from collections import deque from collections import deque
from dotenv import load_dotenv 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') OPENWEBUI_API_BASE = os.getenv('OPENWEBUI_API_BASE')
MODEL_NAME = os.getenv('MODEL_NAME') 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 # Configure OpenAI
openai.api_key = OPENAI_API_KEY # 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 # openai.api_base = OPENWEBUI_API_BASE
# Initialize Discord bot # Initialize Discord bot
intents = discord.Intents.default() intents = discord.Intents.default()
@ -35,14 +43,12 @@ async def get_chat_history(channel, limit=100):
async def get_ai_response(context, user_message): async def get_ai_response(context, user_message):
formatted_prompt = f"##CONTEXT##\n{context}\n##ENDCONTEXT##\n\n{user_message}" formatted_prompt = f"##CONTEXT##\n{context}\n##ENDCONTEXT##\n\n{user_message}"
try: try:
response = openai.ChatCompletion.create( response = client.chat.completions.create(model=MODEL_NAME,
model=MODEL_NAME, messages=[
messages=[ {"role": "user", "content": formatted_prompt}
{"role": "user", "content": formatted_prompt} ])
]
)
return response.choices[0].message.content return response.choices[0].message.content
except Exception as e: except Exception as e:
return f"Error: {str(e)}" return f"Error: {str(e)}"
@ -58,11 +64,11 @@ async def on_message(message):
return return
should_respond = False should_respond = False
# Check if bot was mentioned # Check if bot was mentioned
if bot.user in message.mentions: if bot.user in message.mentions:
should_respond = True should_respond = True
# Check if message is a DM # Check if message is a DM
if isinstance(message.channel, discord.DMChannel): if isinstance(message.channel, discord.DMChannel):
should_respond = True should_respond = True
@ -71,13 +77,13 @@ async def on_message(message):
async with message.channel.typing(): async with message.channel.typing():
# Get chat history # Get chat history
history = await get_chat_history(message.channel) history = await get_chat_history(message.channel)
# Remove bot mention from the message # Remove bot mention from the message
user_message = message.content.replace(f'<@{bot.user.id}>', '').strip() user_message = message.content.replace(f'<@{bot.user.id}>', '').strip()
# Get AI response # Get AI response
response = await get_ai_response(history, user_message) response = await get_ai_response(history, user_message)
# Send response # Send response
await message.reply(response) await message.reply(response)
@ -87,7 +93,7 @@ def main():
if not all([DISCORD_TOKEN, OPENAI_API_KEY, OPENWEBUI_API_BASE, MODEL_NAME]): if not all([DISCORD_TOKEN, OPENAI_API_KEY, OPENWEBUI_API_BASE, MODEL_NAME]):
print("Error: Missing required environment variables") print("Error: Missing required environment variables")
return return
bot.run(DISCORD_TOKEN) bot.run(DISCORD_TOKEN)
if __name__ == "__main__": if __name__ == "__main__":