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
AllfatherHatt 7186c4ca32 Merge pull request '9month-revision-reginald' (#19) from 9month-revision-reginald into master
Reviewed-on: #19
2026-03-16 12:21:17 +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):
self.bot = bot
default_threads = "1457402451530350727,1328363409648910356"
default_threads = "1457402451530350727,1328363409648910356,1533433244186443827"
threads_raw = os.getenv("THREADS_ID", default_threads)
self.threads_id: list[int] = []
@@ -117,7 +117,7 @@ class FitnessCog(commands.Cog):
)
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())
username = message.author.name
-3
View File
@@ -34,9 +34,6 @@ class Completion:
"model": self.__model,
"messages": self.__messages,
"max_completion_tokens": 2000,
"temperature": 0.7,
"presence_penalty": 0.5,
"frequency_penalty": 0.5,
"tools": TOOLS,
"tool_choice": "auto",
}
+119 -26
View File
@@ -2,6 +2,8 @@ import asyncio
import datetime
import json
import random
from contextlib import suppress
from typing import Awaitable, Callable
import discord
import openai
@@ -23,7 +25,7 @@ CALLABLE_FUNCTIONS = {
DEFAULT_MODEL = "gpt-5-mini-2025-08-07"
DEFAULT_MAX_COMPLETION_TOKENS = 2000
DEFAULT_TEMPERATURE = 0.7
STATUS_UPDATE_MIN_INTERVAL_SECONDS = 1.5
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."
)
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()
async def on_message(self, message: discord.Message):
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}"})
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})
memory.append({"user": "Reginald", "content": 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,
}
response_text = None
if hasattr(message.channel, "typing"):
try:
async with message.channel.typing():
response_text = await self.generate_response(
api_key,
formatted_messages,
status_update=status_update,
)
except (discord.HTTPException, AttributeError):
# Fall back to normal processing if typing indicator isn't available.
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)
memory = memory[-retained_count:]
try:
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(
guild
).mid_term_memory() as mid_memory:
short_memory[channel_id] = memory
mid_memory[channel_id] = mid_term_summaries[-self.summary_retention_limit :]
try:
memory.append({"user": user_name, "content": prompt})
memory.append({"user": "Reginald", "content": response_text})
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:
direct_invocation = {"reginald,"}
@@ -216,19 +293,31 @@ class ReginaldCog(PermissionsMixin, BlacklistMixin, MemoryMixin, commands.Cog):
return json.dumps(result, default=str)
@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
async def maybe_update_status(content: str, force: bool = False):
if status_update is not None:
await status_update(content, force)
try:
client = openai.AsyncOpenAI(api_key=api_key)
completion_args = {
"model": model,
"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,
"temperature": DEFAULT_TEMPERATURE,
"tools": TOOLS,
"tool_choice": "auto",
}
await maybe_update_status("Reginald is thinking...", force=True)
response = await client.chat.completions.create(**completion_args)
assistant_message = response.choices[0].message
@@ -244,6 +333,7 @@ class ReginaldCog(PermissionsMixin, BlacklistMixin, MemoryMixin, commands.Cog):
if 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)
messages.append(
{
@@ -254,6 +344,7 @@ class ReginaldCog(PermissionsMixin, BlacklistMixin, MemoryMixin, commands.Cog):
)
completion_args["messages"] = messages
await maybe_update_status("Reginald is composing a polished response...", force=True)
response = await client.chat.completions.create(**completion_args)
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)
response_text = "No response received from AI."
await maybe_update_status("Reginald is delivering his reply...", force=True)
return response_text
except OpenAIError as error:
error_message = f"OpenAI Error: {error}"
await maybe_update_status("Reginald has encountered an unfortunate complication.", force=True)
reginald_responses = [
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}",