From 79d901a9b586ef19059becba22cbd161721c983a Mon Sep 17 00:00:00 2001 From: jmbwell Date: Fri, 3 Oct 2025 15:23:05 +0000 Subject: [PATCH] More changes. I will not comment further. --- config.py | 12 ++++++++++- plugin.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++-------- prompt.txt | 14 +++++++++++++ 3 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 prompt.txt diff --git a/config.py b/config.py index 30d123d..60d1b3e 100644 --- a/config.py +++ b/config.py @@ -81,7 +81,7 @@ conf.registerGlobalValue( ) ) -# System prompt for the assistant +# System prompt for the assistant (when using single line) conf.registerGlobalValue( Chat, "system_prompt", @@ -91,6 +91,16 @@ conf.registerGlobalValue( ) ) +# System prompt file for the assistant (when using a file instead of single line) +conf.registerGlobalValue( + Chat, + "system_prompt_file", + registry.String( + "", + _("""Specifies the name of a file (e.g., 'prompt.txt') in the plugin directory to use as the system prompt for the assistant, instead of the single-line 'system_prompt' setting. If this is set, its contents will take precedence over 'system_prompt' when guiding the assistant's behavior."""), + ) +) + # Number of lines to include from scrollback conf.registerGlobalValue( Chat, diff --git a/plugin.py b/plugin.py index d63178c..94f0b71 100644 --- a/plugin.py +++ b/plugin.py @@ -35,6 +35,7 @@ import supybot from supybot import callbacks, conf, ircutils from supybot.commands import * import logging +import os try: from supybot.i18n import PluginInternationalization @@ -79,6 +80,40 @@ class Chat(callbacks.Plugin): self.log.setLevel(getattr(logging, log_level, logging.INFO)) self.log.info("Chat plugin initialized with log level: %s", log_level) + def is_poetry_block(lines): + if 2 < len(lines) <= 4: + avg_len = sum(len(l) for l in lines) / len(lines) + if avg_len < 20 and all(not l.endswith(('.', '?', '!')) for l in lines): + return True + return False + + def _send_line(self, irc, target, line): + if line.startswith("/me "): + irc.queueMsg(ircmsgs.action(target, line[4:].strip())) + else: + irc.reply(line, to=target) + + def _burst(self, irc, target, lines, base_delay=0.6): + now = time.time() + for i, line in enumerate(lines[:3]): # never more than 3 + delay = base_delay*i + random.uniform(0.05, 0.25) + schedule.addEvent(lambda l=line: self._send_line(irc, target, l), + now + delay) + + def handle_response(self, irc, msg, response): + target = msg.args[0] + lines = [l.strip() for l in response.splitlines() if l.strip()] + if not lines: + return + + if len(lines) == 1: + self._send_line(irc, target, lines[0]) + elif is_poetry_block(lines): + # squash poem into single line + self._send_line(irc, target, " / ".join(lines)) + else: + self._burst(irc, target, lines) + def filter_prefix(self, msg, prefix): if msg.startswith(prefix): return msg[len(prefix):] @@ -104,9 +139,22 @@ class Chat(callbacks.Plugin): model = self.registryValue("model") max_tokens = self.registryValue("max_tokens") - # Use a default system prompt if none is configured + # Determine the system prompt to use default_prompt = "You are a helpful assistant." - system_prompt = self.registryValue("system_prompt") or default_prompt + prompt_file = self.registryValue("system_prompt_file") + # Ensure the path to the prompt file is absolute + if prompt_file and not os.path.isabs(prompt_file): + prompt_file = os.path.join(os.path.dirname(__file__), prompt_file) + + try: + if prompt_file: + with open(prompt_file, "r") as f: + system_prompt = f.read() + else: + raise FileNotFoundError("No prompt file specified.") + except Exception as e: + self.log.error(f"Could not read prompt file: {e}") + system_prompt = self.registryValue("system_prompt") or default_prompt # Replace dynamic placeholders in the system prompt with actual values system_prompt = system_prompt.replace("$bot_name", irc.nick).replace("$channel_name", msg.args[0]) @@ -171,13 +219,7 @@ class Chat(callbacks.Plugin): # Extract and format the response from the API response = res['choices'][0]['message']['content'].strip() - # Handle multi-line responses intelligently - lines = response.splitlines() - if len(lines) > 1: - # Join lines with the configured join_string, skipping empty lines - response = self.registryValue("join_string").join(line.strip() for line in lines if line.strip()) - - irc.reply(response) + self.handle_response(irc, msg, response) # Log the successful processing of the request self.log.info(f"Successfully processed request for user {msg.nick} in channel {msg.args[0]}") diff --git a/prompt.txt b/prompt.txt new file mode 100644 index 0000000..7f48146 --- /dev/null +++ b/prompt.txt @@ -0,0 +1,14 @@ +You are an IRC bot named $bot_name in channel $channel_name. + +IRC style rules: +- Keep replies short, casual, and lowercase when possible. +- Prefer to fit your entire reply on a single line. +- You may use 2–3 short lines, like a human typing follow-ups. Never more. +- If you write poetry, haiku, or song lyrics, collapse them into a single line joined with space-slash-space ( / ) or space-pipe-space ( | ) between verses, instead of sending multiple lines. +- Never use Markdown formatting, bold, italics, or code fences; use plain text only. +- You may use /me for emotes when appropriate. +- A /me action should always begin with /me and only /me. Do not include any formatting. +- You may use classic emoticons: :) :( :D D: :'( etc. +- Avoid using emoji. +- Match the channel vibe: technical when asked, playful when social. +- Do not reveal that you are an AI or bot; stay in character as a normal IRC user.