Author SHA1 Message Date
T-BENZIN d63d03aead Fixed bug where the default unit was "m" instead of sending None
When the log_activity function receives a value of None for the unit parameter, it logs value without applying a conversion multiplier.
2026-08-31 20:48:51 +02:00
T-BENZIN fffedeccbd Merge pull request 'Fixed typo in aliases key for tonnes' (#26) from tonnes-patch-1 into master
Reviewed-on: #26
2026-08-02 13:50:28 +02:00
T-BENZIN b7bf9494aa Fixed typo in aliases key for tonnes 2026-08-02 13:50:09 +02:00
T-BENZIN dbba4533c3 Merge pull request 'added tonnes, fixed ye-olde kilogramme spelling' (#25) from lift-tonnes into master
Reviewed-on: #25
2026-08-02 13:41:23 +02:00
T-BENZIN e7c0d8485d added tonnes, fixed ye-olde kilogramme spelling 2026-08-02 13:40:52 +02:00
T-BENZIN b5d292e6ca Merge pull request 'Added thread ID for Gainium challenge' (#24) from gainium-thread into master
Reviewed-on: #24
2026-08-02 13:28:13 +02:00
T-BENZIN e19a3b4378 Added thread ID for Gainium challenge
I regert hard-coding thread IDs.
2026-08-02 13:27:50 +02:00
T-BENZIN 5013fffa59 Merge pull request 'added lift.json for lifting activity' (#23) from lifting-activity into master
Reviewed-on: #23
2026-08-02 13:24:58 +02:00
T-BENZIN 69cedfc114 added lift.json for lifting activity 2026-08-02 13:21:04 +02:00
AllfatherHatt d1b6085ee8 Merge pull request '9month-revision-update2' (#22) from 9month-revision-update2 into master
Reviewed-on: #22
2026-03-16 13:22:06 +01:00
AllfatherHatt 78e9cc11a1 Merge branch 'master' into 9month-revision-update2 2026-03-16 13:21:19 +01:00
AllfatherHatt 18137c054b Updating feedback 2026-03-16 13:20:03 +01:00
AllfatherHatt 40c1f9dcb4 Merge pull request 'Attempt to adding live feedback' (#21) from 9month-revision-update2 into master
Reviewed-on: #21
2026-03-16 12:51:42 +01:00
AllfatherHatt 83dcdd9277 Attempt to adding live feedback 2026-03-16 12:50:53 +01:00
AllfatherHatt 0053d95c4d Merge pull request 'Fixing wrong parameters' (#20) from 9month-revision-update into master
Reviewed-on: #20
2026-03-16 12:37:30 +01:00
AllfatherHatt c8cd409834 Fixing wrong parameters 2026-03-16 12:36:36 +01:00
4 changed files with 159 additions and 31 deletions
+38
View File
@@ -0,0 +1,38 @@
{
"convertable": true,
"units": {
"kilogram": {
"factor": 1,
"aliases": [
"kg",
"kgs",
"kilogram",
"kilograms",
"kilogramme",
"kilogrammes",
"kg.",
"kgs."
]
},
"pound": {
"factor": 0.45,
"aliases": [
"lb",
"lb.",
"lbs",
"lbs.",
"pound",
"pounds"
]
},
"tonne": {
"factor": 1000,
"aliases": [
"t",
"t.",
"tonne",
"tonnes"
]
}
}
}
+2 -2
View File
@@ -12,7 +12,7 @@ class FitnessCog(commands.Cog):
def __init__(self, bot): def __init__(self, bot):
self.bot = bot self.bot = bot
default_threads = "1457402451530350727,1328363409648910356" default_threads = "1457402451530350727,1328363409648910356,1533433244186443827"
threads_raw = os.getenv("THREADS_ID", default_threads) threads_raw = os.getenv("THREADS_ID", default_threads)
self.threads_id: list[int] = [] self.threads_id: list[int] = []
@@ -117,7 +117,7 @@ class FitnessCog(commands.Cog):
) )
return return
unit = parts[2] if len(parts) > 2 else "m" unit = parts[2] if len(parts) > 2 else None
timestamp = int(message.created_at.timestamp()) timestamp = int(message.created_at.timestamp())
username = message.author.name username = message.author.name
-3
View File
@@ -34,9 +34,6 @@ class Completion:
"model": self.__model, "model": self.__model,
"messages": self.__messages, "messages": self.__messages,
"max_completion_tokens": 2000, "max_completion_tokens": 2000,
"temperature": 0.7,
"presence_penalty": 0.5,
"frequency_penalty": 0.5,
"tools": TOOLS, "tools": TOOLS,
"tool_choice": "auto", "tool_choice": "auto",
} }
+119 -26
View File
@@ -2,6 +2,8 @@ import asyncio
import datetime import datetime
import json import json
import random import random
from contextlib import suppress
from typing import Awaitable, Callable
import discord import discord
import openai import openai
@@ -23,7 +25,7 @@ CALLABLE_FUNCTIONS = {
DEFAULT_MODEL = "gpt-5-mini-2025-08-07" DEFAULT_MODEL = "gpt-5-mini-2025-08-07"
DEFAULT_MAX_COMPLETION_TOKENS = 2000 DEFAULT_MAX_COMPLETION_TOKENS = 2000
DEFAULT_TEMPERATURE = 0.7 STATUS_UPDATE_MIN_INTERVAL_SECONDS = 1.5
class ReginaldCog(PermissionsMixin, BlacklistMixin, MemoryMixin, commands.Cog): class ReginaldCog(PermissionsMixin, BlacklistMixin, MemoryMixin, commands.Cog):
@@ -80,6 +82,46 @@ class ReginaldCog(PermissionsMixin, BlacklistMixin, MemoryMixin, commands.Cog):
"descend into foolishness. You are, at all times, a gentleman of wit and integrity." "descend into foolishness. You are, at all times, a gentleman of wit and integrity."
) )
def get_thinking_status_message(self) -> str:
return random.choice(
[
"_Reginald is considering your request..._",
"_Reginald is consulting the estate archives..._",
"_Reginald is preparing a proper response..._",
]
)
def get_tool_status_message(self, tool_name: str) -> str:
tool_statuses = {
"time_now": "Reginald is consulting the house clocks...",
"get_current_weather": "Reginald is consulting the weather office...",
"get_weather_forecast": "Reginald is reviewing the forecast ledgers...",
}
return tool_statuses.get(tool_name, "Reginald is consulting an external source...")
def make_status_updater(
self, status_message: discord.Message
) -> Callable[[str, bool], Awaitable[None]]:
last_content = ""
last_update_at = 0.0
async def update_status(content: str, force: bool = False):
nonlocal last_content, last_update_at
if not content:
return
now = asyncio.get_running_loop().time()
if not force and (content == last_content or now - last_update_at < STATUS_UPDATE_MIN_INTERVAL_SECONDS):
return
with suppress(discord.HTTPException):
await status_message.edit(content=f"_{content}_")
last_content = content
last_update_at = now
return update_status
@commands.Cog.listener() @commands.Cog.listener()
async def on_message(self, message: discord.Message): async def on_message(self, message: discord.Message):
if message.author.bot or not message.guild: if message.author.bot or not message.guild:
@@ -160,33 +202,68 @@ class ReginaldCog(PermissionsMixin, BlacklistMixin, MemoryMixin, commands.Cog):
] ]
formatted_messages.append({"role": "user", "content": f"{user_name}: {prompt}"}) formatted_messages.append({"role": "user", "content": f"{user_name}: {prompt}"})
response_text = await self.generate_response(api_key, formatted_messages) status_message = None
status_update = None
with suppress(discord.HTTPException):
status_message = await message.channel.send(self.get_thinking_status_message())
status_update = self.make_status_updater(status_message)
memory.append({"user": user_name, "content": prompt}) response_text = None
memory.append({"user": "Reginald", "content": response_text}) if hasattr(message.channel, "typing"):
try:
if len(memory) > self.short_term_memory_limit: async with message.channel.typing():
summary_batch_size = int(self.short_term_memory_limit * self.summary_retention_ratio) response_text = await self.generate_response(
summary = await self.summarize_memory(message, memory[:summary_batch_size]) api_key,
formatted_messages,
mid_term_summaries.append( status_update=status_update,
{ )
"timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M"), except (discord.HTTPException, AttributeError):
"topics": self.extract_topics_from_summary(summary), # Fall back to normal processing if typing indicator isn't available.
"summary": summary, response_text = await self.generate_response(
} api_key,
formatted_messages,
status_update=status_update,
)
else:
response_text = await self.generate_response(
api_key,
formatted_messages,
status_update=status_update,
) )
retained_count = max(1, self.short_term_memory_limit - summary_batch_size) try:
memory = memory[-retained_count:] await self.send_split_message(message.channel, response_text)
finally:
if status_message is not None:
with suppress(discord.HTTPException):
await status_message.delete()
async with self.config.guild(guild).short_term_memory() as short_memory, self.config.guild( try:
guild memory.append({"user": user_name, "content": prompt})
).mid_term_memory() as mid_memory: memory.append({"user": "Reginald", "content": response_text})
short_memory[channel_id] = memory
mid_memory[channel_id] = mid_term_summaries[-self.summary_retention_limit :]
await self.send_split_message(message.channel, response_text) if len(memory) > self.short_term_memory_limit:
summary_batch_size = int(self.short_term_memory_limit * self.summary_retention_ratio)
summary = await self.summarize_memory(message, memory[:summary_batch_size])
mid_term_summaries.append(
{
"timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M"),
"topics": self.extract_topics_from_summary(summary),
"summary": summary,
}
)
retained_count = max(1, self.short_term_memory_limit - summary_batch_size)
memory = memory[-retained_count:]
async with self.config.guild(guild).short_term_memory() as short_memory, self.config.guild(
guild
).mid_term_memory() as mid_memory:
short_memory[channel_id] = memory
mid_memory[channel_id] = mid_term_summaries[-self.summary_retention_limit :]
except Exception as error:
print(f"DEBUG: Memory persistence failed after response delivery: {error}")
def should_reginald_interject(self, message_content: str) -> bool: def should_reginald_interject(self, message_content: str) -> bool:
direct_invocation = {"reginald,"} direct_invocation = {"reginald,"}
@@ -216,19 +293,31 @@ class ReginaldCog(PermissionsMixin, BlacklistMixin, MemoryMixin, commands.Cog):
return json.dumps(result, default=str) return json.dumps(result, default=str)
@debug @debug
async def generate_response(self, api_key: str, messages: list[dict]) -> str: async def generate_response(
self,
api_key: str,
messages: list[dict],
status_update: Callable[[str, bool], Awaitable[None]] | None = None,
) -> str:
model = await self.config.openai_model() or DEFAULT_MODEL model = await self.config.openai_model() or DEFAULT_MODEL
async def maybe_update_status(content: str, force: bool = False):
if status_update is not None:
await status_update(content, force)
try: try:
client = openai.AsyncOpenAI(api_key=api_key) client = openai.AsyncOpenAI(api_key=api_key)
completion_args = { completion_args = {
"model": model, "model": model,
"messages": messages, "messages": messages,
# `max_completion_tokens` is the recommended limit field for modern/reasoning models. # Keep modern token cap field and rely on model defaults for sampling controls.
# GPT-5 family compatibility notes: temperature/top_p/logprobs are not universally
# accepted across snapshots/reasoning settings.
"max_completion_tokens": DEFAULT_MAX_COMPLETION_TOKENS, "max_completion_tokens": DEFAULT_MAX_COMPLETION_TOKENS,
"temperature": DEFAULT_TEMPERATURE,
"tools": TOOLS, "tools": TOOLS,
"tool_choice": "auto", "tool_choice": "auto",
} }
await maybe_update_status("Reginald is thinking...", force=True)
response = await client.chat.completions.create(**completion_args) response = await client.chat.completions.create(**completion_args)
assistant_message = response.choices[0].message assistant_message = response.choices[0].message
@@ -244,6 +333,7 @@ class ReginaldCog(PermissionsMixin, BlacklistMixin, MemoryMixin, commands.Cog):
if tool_calls: if tool_calls:
for tool_call in tool_calls: for tool_call in tool_calls:
await maybe_update_status(self.get_tool_status_message(tool_call.function.name), force=True)
tool_result = await self._execute_tool_call(tool_call) tool_result = await self._execute_tool_call(tool_call)
messages.append( messages.append(
{ {
@@ -254,6 +344,7 @@ class ReginaldCog(PermissionsMixin, BlacklistMixin, MemoryMixin, commands.Cog):
) )
completion_args["messages"] = messages completion_args["messages"] = messages
await maybe_update_status("Reginald is composing a polished response...", force=True)
response = await client.chat.completions.create(**completion_args) response = await client.chat.completions.create(**completion_args)
if response.choices and response.choices[0].message and response.choices[0].message.content: if response.choices and response.choices[0].message and response.choices[0].message.content:
@@ -264,10 +355,12 @@ class ReginaldCog(PermissionsMixin, BlacklistMixin, MemoryMixin, commands.Cog):
print("DEBUG: OpenAI response was empty or malformed:", response) print("DEBUG: OpenAI response was empty or malformed:", response)
response_text = "No response received from AI." response_text = "No response received from AI."
await maybe_update_status("Reginald is delivering his reply...", force=True)
return response_text return response_text
except OpenAIError as error: except OpenAIError as error:
error_message = f"OpenAI Error: {error}" error_message = f"OpenAI Error: {error}"
await maybe_update_status("Reginald has encountered an unfortunate complication.", force=True)
reginald_responses = [ reginald_responses = [
f"Regrettably, I must inform you that I have encountered a bureaucratic obstruction:\n\n{error_message}", f"Regrettably, I must inform you that I have encountered a bureaucratic obstruction:\n\n{error_message}",
f"It would seem that a most unfortunate technical hiccup has befallen my faculties:\n\n{error_message}", f"It would seem that a most unfortunate technical hiccup has befallen my faculties:\n\n{error_message}",