Compare commits
No commits in common. "863d3e3c8827957bee3bd8cb1977a0870dcb4e80" and "69354229d626dcf0bb77c6351d3339605e9aba03" have entirely different histories.
863d3e3c88
...
69354229d6
9 changed files with 18 additions and 1189 deletions
1042
bot.log
1042
bot.log
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
24
src/ai.py
24
src/ai.py
|
|
@ -82,33 +82,23 @@ def get_current_model():
|
|||
return get_model_name()
|
||||
|
||||
# Main LLM interaction — injects personality and sends prompt to Ollama
|
||||
def get_ai_response(user_prompt, context=None):
|
||||
def get_ai_response(user_prompt):
|
||||
model_name = get_model_name()
|
||||
load_model(model_name)
|
||||
load_model(model_name) # Ensures the model is pulled and ready
|
||||
|
||||
persona = load_persona()
|
||||
full_prompt = ""
|
||||
|
||||
# Inject persona first if available
|
||||
if persona:
|
||||
# Clean fancy quotes and build final prompt with character injection
|
||||
safe_inject = persona["prompt_inject"].replace("“", "\"").replace("”", "\"").replace("’", "'")
|
||||
full_prompt += f"{safe_inject}\n"
|
||||
|
||||
# Add recent conversation context, if available
|
||||
if context:
|
||||
logger.info("🧠 Injected context block (pre-prompt):\n" + context)
|
||||
full_prompt += f"[Recent Conversation]\n{context}\n\n"
|
||||
|
||||
# Add user prompt + character or plain ending
|
||||
if persona:
|
||||
full_prompt += f"User: {user_prompt}\n{persona['name']}:"
|
||||
full_prompt = f"{safe_inject}\nUser: {user_prompt}\n{persona['name']}:"
|
||||
else:
|
||||
full_prompt += user_prompt
|
||||
full_prompt = user_prompt # fallback to raw prompt if no persona loaded
|
||||
|
||||
payload = {
|
||||
"model": model_name,
|
||||
"model": model_name, # 🔧 Suggested fix: previously hardcoded to MODEL_NAME
|
||||
"prompt": full_prompt,
|
||||
"stream": False
|
||||
# optional: add "keep_alive": 300 to keep model warm
|
||||
}
|
||||
|
||||
logger.info("🛰️ SENDING TO OLLAMA /generate")
|
||||
|
|
|
|||
68
src/bot.py
68
src/bot.py
|
|
@ -9,12 +9,10 @@ from dotenv import load_dotenv
|
|||
import random
|
||||
import yaml
|
||||
from scheduler import start_scheduler
|
||||
from profilepic import set_avatar_from_bytes
|
||||
from context import fetch_recent_context, format_context
|
||||
from logger import setup_logger
|
||||
logger = setup_logger("bot")
|
||||
|
||||
from ai import unload_model, load_model, get_current_model, get_ai_response
|
||||
from ai import unload_model, load_model, get_current_model
|
||||
|
||||
dotenv_path = os.path.join(os.path.dirname(__file__), '..', '.env')
|
||||
load_dotenv(dotenv_path)
|
||||
|
|
@ -35,6 +33,16 @@ else:
|
|||
|
||||
logger.info(f"✅ Final model in use: {MODEL_NAME}")
|
||||
|
||||
from ai import get_ai_response, load_model
|
||||
MODEL_NAME = os.getenv("MODEL_NAME", "llama3:latest")
|
||||
|
||||
if load_model(MODEL_NAME):
|
||||
logger.info(f"🚀 Model `{MODEL_NAME}` preloaded on startup.")
|
||||
else:
|
||||
logger.warning(f"⚠️ Failed to preload model `{MODEL_NAME}`.")
|
||||
|
||||
logger.info(f"✅ Final model in use: {MODEL_NAME}")
|
||||
|
||||
from personality import apply_personality, set_persona
|
||||
from discord.ext.commands import (
|
||||
cooldown,
|
||||
|
|
@ -87,41 +95,6 @@ async def global_command_cooldown(ctx):
|
|||
raise CommandOnCooldown(bucket, retry_after, BucketType.user)
|
||||
return True
|
||||
|
||||
@bot.event
|
||||
async def on_message(message):
|
||||
if message.author == bot.user:
|
||||
return
|
||||
|
||||
if bot.user.mentioned_in(message):
|
||||
prompt = message.content.replace(f"<@{bot.user.id}>", "").strip()
|
||||
context_msgs = await fetch_recent_context(message.channel)
|
||||
formatted_context = format_context(context_msgs)
|
||||
|
||||
logger.info("🧠 Injected context block:\n" + formatted_context)
|
||||
|
||||
async with message.channel.typing():
|
||||
reply = get_ai_response(prompt, context=formatted_context)
|
||||
await message.channel.send(reply)
|
||||
|
||||
await bot.process_commands(message)
|
||||
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
print(f"✅ Logged in as {bot.user.name}")
|
||||
logger.info(f"Logged in as {bot.user.name}")
|
||||
|
||||
# Optional: rename itself in servers (if it has permission)
|
||||
for guild in bot.guilds:
|
||||
me = guild.me
|
||||
if me.nick != "Delta":
|
||||
try:
|
||||
await me.edit(nick="Delta")
|
||||
logger.info(f"🔄 Renamed self to Delta in {guild.name}")
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠️ Failed to rename in {guild.name}: {e}")
|
||||
|
||||
bot.loop.create_task(start_scheduler(bot))
|
||||
|
||||
@bot.command()
|
||||
async def ping(ctx):
|
||||
await ctx.send("🏓 Pong!")
|
||||
|
|
@ -227,25 +200,6 @@ async def list_models(ctx):
|
|||
except Exception as e:
|
||||
await ctx.send(f"❌ Failed to fetch models: {e}")
|
||||
|
||||
@bot.command(name="setavatar")
|
||||
@commands.is_owner() # Only the bot owner can run this
|
||||
async def set_avatar(ctx):
|
||||
if not ctx.message.attachments:
|
||||
return await ctx.send("❌ Please attach an image (PNG) to use as the new avatar.")
|
||||
|
||||
image = ctx.message.attachments[0]
|
||||
image_bytes = await image.read()
|
||||
|
||||
token = os.getenv("DISCORD_TOKEN")
|
||||
if not token:
|
||||
return await ctx.send("❌ Bot token not found in environment.")
|
||||
|
||||
success = set_avatar_from_bytes(image_bytes, token)
|
||||
if success:
|
||||
await ctx.send("✅ Avatar updated successfully!")
|
||||
else:
|
||||
await ctx.send("❌ Failed to update avatar.")
|
||||
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
print(f"✅ Logged in as {bot.user.name}")
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
# context.py
|
||||
|
||||
import os
|
||||
import yaml
|
||||
import discord
|
||||
|
||||
base_dir = os.path.dirname(__file__)
|
||||
with open(os.path.join(base_dir, "settings.yml"), "r", encoding="utf-8") as f:
|
||||
settings = yaml.safe_load(f)
|
||||
|
||||
CONTEXT_LIMIT = settings["context"].get("max_messages", 15)
|
||||
|
||||
async def fetch_recent_context(channel, limit=CONTEXT_LIMIT):
|
||||
messages = []
|
||||
async for message in channel.history(limit=100):
|
||||
# Skip other bots (but not Delta herself)
|
||||
if message.author.bot and message.author.id != channel.guild.me.id:
|
||||
continue
|
||||
|
||||
raw = message.clean_content
|
||||
clean = raw.strip().replace("\n", " ").replace("\r", "")
|
||||
clean = " ".join(clean.split()) # Collapse all extra whitespace
|
||||
|
||||
if not clean:
|
||||
continue
|
||||
|
||||
if clean.startswith("!"):
|
||||
continue
|
||||
|
||||
line = f"{message.created_at.strftime('%Y-%m-%d %H:%M')} - {message.author.display_name}: {clean}"
|
||||
messages.append(line)
|
||||
|
||||
if len(messages) >= limit:
|
||||
break
|
||||
|
||||
messages.reverse()
|
||||
return messages
|
||||
|
||||
def format_context(lines: list[str]) -> str:
|
||||
return "\n".join(lines)
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
# profilepic.py
|
||||
|
||||
import base64
|
||||
import requests
|
||||
import logging
|
||||
import os
|
||||
|
||||
logger = logging.getLogger("bot")
|
||||
|
||||
DISCORD_API = "https://discord.com/api/v10"
|
||||
|
||||
def set_avatar_from_bytes(image_bytes: bytes, token: str) -> bool:
|
||||
try:
|
||||
b64_avatar = base64.b64encode(image_bytes).decode("utf-8")
|
||||
payload = {
|
||||
"avatar": f"data:image/png;base64,{b64_avatar}"
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bot {token}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
response = requests.patch(f"{DISCORD_API}/users/@me", json=payload, headers=headers)
|
||||
logger.info(f"🖼️ Avatar update status: {response.status_code} - {response.text}")
|
||||
return response.status_code == 200
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Failed to update avatar: {str(e)}")
|
||||
return False
|
||||
|
|
@ -6,10 +6,6 @@ messages:
|
|||
cooldown:
|
||||
- "🕒 Chill, wait {seconds}s before trying again."
|
||||
|
||||
context:
|
||||
enabled: true
|
||||
max_messages: 30
|
||||
|
||||
scheduler:
|
||||
enabled: false
|
||||
mode: simple # <- this activates simple mode
|
||||
|
|
|
|||
Loading…
Reference in a new issue