ai memory increased

This commit is contained in:
esef committed 2026-06-30 19:19:13 +03:00
1 parent 6c77f14e42
commit 8b336ccced
6 files changed
+118 -32

No files matched your search

+76 -3
View File
@@ -6,13 +6,14 @@ import re
import time import time
import asyncio import asyncio
import discord import discord
import logging
from discord.ext import commands from discord.ext import commands
from collections import defaultdict, deque from collections import defaultdict, deque
# pyrefly: ignore [missing-import] # pyrefly: ignore [missing-import]
from openai import AsyncOpenAI from openai import AsyncOpenAI
#only last 12 messages on discord will be fed into the ai's memory #only last 12 messages on discord will be fed into the ai's memory
MEMORY_SIZE = 12 MEMORY_SIZE = 15
# These are the ONLY two valid DeepSeek V4 model IDs. deepseek-chat and # These are the ONLY two valid DeepSeek V4 model IDs. deepseek-chat and
# deepseek-reasoner are legacy aliases scheduled for retirement on # deepseek-reasoner are legacy aliases scheduled for retirement on
@@ -170,11 +171,51 @@ class AIRoleplay(commands.Cog):
self.thinking_channels = set() # channel_ids with thinking ON self.thinking_channels = set() # channel_ids with thinking ON
self.thinking_effort = {} # channel_id -> 'high' | 'max' self.thinking_effort = {} # channel_id -> 'high' | 'max'
self.temperature = 0.85 self.temperature = 0.85
self.channel_summary = {} # channel_id -> str
# ========================================== # ==========================================
# CORE AI LOGIC & LISTENERS # CORE AI LOGIC & LISTENERS
# ========================================== # ==========================================
#this helper summarizes evicted memory to maintain long-term context
async def update_summary(self, channel_id, evicted_message):
try:
current_summary = self.channel_summary.get(channel_id, "")
evicted_text = evicted_message.get('content', '')
# handle vision support list format
if isinstance(evicted_text, list):
evicted_text = next((item['text'] for item in evicted_text if item.get('type') == 'text'), '')
if not evicted_text:
return
sys_prompt = (
"You are a background memory summarizer. "
"Merge the new evicted message into the existing summary. "
"Keep the total summary under 150 words. "
"Only retain facts relevant to story/relationship continuity, ignore minor chitchat."
)
user_prompt = f"Existing Summary:\n{current_summary}\n\nEvicted Message:\n{evicted_text}"
kwargs = {
"model": MODEL_FLASH,
"messages": [
{"role": "system", "content": sys_prompt},
{"role": "user", "content": user_prompt}
],
"max_tokens": 300,
"extra_body": {"thinking": {"type": "disabled"}},
}
resp = await self.client.chat.completions.create(**kwargs)
new_summary = (resp.choices[0].message.content or '').strip()
if new_summary:
self.channel_summary[channel_id] = new_summary
except Exception as e:
logging.error(f"Summarization failed for channel {channel_id}: {e}")
#only accepts me and hizuki #only accepts me and hizuki
def is_allowed(self, user): def is_allowed(self, user):
return user.id in self.allowed_users return user.id in self.allowed_users
@@ -296,9 +337,21 @@ class AIRoleplay(commands.Cog):
# DeepSeek and older models prefer pure strings if there are no images # DeepSeek and older models prefer pure strings if there are no images
final_content = user_content if has_images else user_content[0]["text"] final_content = user_content if has_images else user_content[0]["text"]
# summarize evicted memory before it drops off the deque
if len(mem) >= mem.maxlen:
await self.update_summary(cid, mem[0])
mem.append({'role': 'user', 'content': final_content}) mem.append({'role': 'user', 'content': final_content})
api_msgs = [{'role': 'system', 'content': prompt}] + list(mem) api_msgs = [{'role': 'system', 'content': prompt}]
# inject the background summary into the system prompt if it exists
summary = self.channel_summary.get(cid)
if summary:
api_msgs[0]['content'] += f"\n\nMEMORY (earlier context, may be approximate):\n{summary}"
api_msgs += list(mem)
model_to_use = self.active_model # always MODEL_FLASH or MODEL_PRO, never swapped model_to_use = self.active_model # always MODEL_FLASH or MODEL_PRO, never swapped
#VISION SUPPORT END #VISION SUPPORT END
@@ -478,6 +531,26 @@ class AIRoleplay(commands.Cog):
embed.set_footer(text=f'model: {self.active_model} · this channel only') embed.set_footer(text=f'model: {self.active_model} · this channel only')
await ctx.reply(embed=embed, view=ThinkingView(self, cid)) await ctx.reply(embed=embed, view=ThinkingView(self, cid))
#this shows the current memory summary for the channel
@commands.command()
async def summary(self, ctx):
if not self.is_allowed(ctx.author):
return
summary_text = self.channel_summary.get(ctx.channel.id)
if not summary_text:
return await ctx.reply("No summary yet.")
embed = discord.Embed(title='🧠 Channel Memory Summary', description=summary_text, color=0x2b2d31)
await ctx.reply(embed=embed)
#this wipes the memory summary for the channel
@commands.command()
async def clearsummary(self, ctx):
if not self.is_allowed(ctx.author):
return
self.channel_summary.pop(ctx.channel.id, None)
await ctx.reply("Channel summary cleared.")
#this checks thinking status without opening the menu, only usable by me and hizu #this checks thinking status without opening the menu, only usable by me and hizu
@commands.command() @commands.command()
async def thinkstatus(self, ctx): async def thinkstatus(self, ctx):
@@ -499,7 +572,7 @@ class AIRoleplay(commands.Cog):
return await ctx.reply("temperature must be between 0.0 and 2.0.") return await ctx.reply("temperature must be between 0.0 and 2.0.")
self.temperature = value self.temperature = value
await ctx.reply(f"🌡️ **Temperature set to {value}**\n*(Lower = stricter/robotic, Higher = creative/chaotic)*") await ctx.reply(f"**Temperature set to {value}**\n*(Lower = stricter/robotic, Higher = creative/chaotic)*")
async def setup(bot): async def setup(bot):
await bot.add_cog(AIRoleplay(bot)) await bot.add_cog(AIRoleplay(bot))
-29
View File
@@ -15,36 +15,7 @@ class General(commands.Cog):
async def yuuka(self, ctx): async def yuuka(self, ctx):
await ctx.send('kanpeki!') await ctx.send('kanpeki!')
#will soon be removed from here when im not lazy
@commands.command()
async def leaks(self, ctx):
rss_url = os.getenv('TWITTER_RSS_URL')
if not rss_url:
return await ctx.reply('rss url not configured.')
try:
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'}
async with aiohttp.ClientSession(headers=headers) as session:
async with session.get(rss_url) as resp:
text = await resp.text()
feed = feedparser.parse(text)
if not feed.entries:
return await ctx.reply('no tweets found.')
response = "**Latest 5 Leaks:**\n"
for entry in feed.entries[:5]:
title = entry.get('title', '')
link = entry.get('link', '')
if 'nitter.net' in link:
link = link.replace('nitter.net', 'twitter.com').replace('#m', '')
response += f"\n{title}\n{link}\n"
await ctx.reply(response[:2000])
except Exception as e:
await ctx.reply(f'failed to fetch leaks: {e}')
@commands.command() @commands.command()
async def weather(self, ctx, *, city: str = "kayseri"): async def weather(self, ctx, *, city: str = "kayseri"):
View File
Whitespace-only changes.
Binary file not shown.
+1
View File
@@ -56,6 +56,7 @@ async def main():
await bot.load_extension('cogs.ffmpeg_things') await bot.load_extension('cogs.ffmpeg_things')
await bot.load_extension('cogs.currency_converter') await bot.load_extension('cogs.currency_converter')
await bot.load_extension('ai.ai_roleplay') await bot.load_extension('ai.ai_roleplay')
await bot.load_extension('twitterbridge.leaks_cmd')
try: try:
await bot.start(os.getenv('DISCORD_TOKEN')) await bot.start(os.getenv('DISCORD_TOKEN'))
+41
View File
@@ -0,0 +1,41 @@
import os
import aiohttp
import feedparser
from discord.ext import commands
class TwitterLeaks(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.command()
async def leaks(self, ctx):
rss_url = os.getenv('TWITTER_RSS_URL')
if not rss_url:
return await ctx.reply('rss url not configured.')
try:
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'}
async with aiohttp.ClientSession(headers=headers) as session:
async with session.get(rss_url) as resp:
text = await resp.text()
feed = feedparser.parse(text)
if not feed.entries:
return await ctx.reply('no tweets found.')
response = "**Latest 5 Leaks:**\n"
for entry in feed.entries[:5]:
title = entry.get('title', '')
link = entry.get('link', '')
if 'nitter.net' in link:
link = link.replace('nitter.net', 'twitter.com').replace('#m', '')
response += f"\n{title}\n{link}\n"
await ctx.reply(response[:2000])
except Exception as e:
await ctx.reply(f'failed to fetch leaks: {e}')
async def setup(bot):
await bot.add_cog(TwitterLeaks(bot))