-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add context to conversations #20
Changes from all commits
2400649
26beccc
ffae17d
1684097
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import discord | ||
|
||
from . import botconf as _botconf | ||
|
||
|
||
class MessageHistory: | ||
message_histories: dict[int, list[dict[str, str]]] | ||
"""A dictionary that associates channel IDs to their histories""" | ||
|
||
def __init__(self) -> None: | ||
self.message_histories = {} | ||
|
||
# NOTE: This method can add messages out of order | ||
def add_message( | ||
self, | ||
message_list: list[discord.Message], | ||
is_bot: bool = False | ||
) -> None: | ||
""" | ||
Concatenate the contents of a list of messages into a | ||
single dictionary, and add it to the history. | ||
The resulting message dictionary will be added to the | ||
history of the first message's channel. | ||
The channel's message history will then be truncated | ||
to the length specified by `history_length` in the | ||
bot's config. | ||
If `is_bot` is True, the message's role will be | ||
"assistant", otherwise it will be "user". | ||
If `is_bot` is False, the message's content will also | ||
be prepended with `(user_name <@user_id>)`, where | ||
`user_name` is the author of the first message's display | ||
name, and `<@user_id>` is the text used to mention the | ||
author of the first message. | ||
""" | ||
|
||
if len(message_list) < 1: | ||
return | ||
|
||
author = message_list[0].author | ||
content = ( | ||
# Add author name and mention to content if it is not a bot | ||
"" if is_bot else f"({author.display_name} {author.mention}) " | ||
) | ||
content += " ".join([m.content for m in message_list]) | ||
|
||
# Create message dict | ||
msg_dict = { | ||
"role": "assistant" if is_bot else "user", | ||
"content": content, | ||
} | ||
|
||
chan_id = message_list[0].channel.id | ||
hist_len = _botconf.bot_config.history_length | ||
if chan_id in self.message_histories: | ||
self.message_histories[chan_id].append(msg_dict) | ||
if len(self.message_histories[chan_id]) > hist_len: | ||
# Truncate the channel's history to the last | ||
# `history_length` messages | ||
self.message_histories[chan_id] = ( | ||
self.message_histories[chan_id][-hist_len:] | ||
) | ||
else: | ||
self.message_histories[chan_id] = [msg_dict] | ||
|
||
|
||
bot_history = MessageHistory() |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
|
||
import globalconf as _globalconf | ||
from . import botconf as _botconf | ||
from . import bothist as _bothist | ||
|
||
import discord | ||
|
||
|
@@ -16,9 +17,9 @@ | |
|
||
async def generate_response( | ||
message: discord.Message, | ||
system_prompt: str = _botconf.botconfig.system_prompt, | ||
system_prompt: str = _botconf.bot_config.system_prompt, | ||
# TODO: Use this again | ||
auto_pull_model: bool = _botconf.botconfig.auto_pull_model | ||
auto_pull_model: bool = _botconf.bot_config.auto_pull_model | ||
) -> str | None: | ||
url = f"http://{_globalconf.LLM_HOST}:{_globalconf.LLM_PORT}/api/chat" | ||
logger.info(f"url: {url}") | ||
|
@@ -31,17 +32,22 @@ async def generate_response( | |
f"User prompt:\n{message.content}" | ||
) | ||
|
||
messages: list[dict[str, str]] = [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm. Well, it's not the most efficient, but this does let you set the system prompt dynamically. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What would a more efficient way be? This is the exact same format that the api needs, so we would have to convert it to this eventually There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're recalculating the message array. Could store the whole thing rather than concatenating the system prompt every time. Not a big deal though |
||
{"role": "system", "content": system_prompt}, | ||
*_bothist.bot_history.message_histories[message.channel.id], | ||
] | ||
|
||
logger.info(", ".join([ | ||
f"{{{m['role'][0:1]}: '{m['content'][0:10]}'}}" | ||
for m in messages | ||
])) | ||
|
||
async with aiohttp.ClientSession() as cs: | ||
try: | ||
async with cs.post(url, json={ | ||
"model": _botconf.botconfig.llm_model, | ||
"model": _botconf.bot_config.llm_model, | ||
"stream": False, | ||
"messages": [ | ||
{"role": "system", | ||
"content": system_prompt}, | ||
{"role": "user", | ||
"content": message.content}, | ||
], | ||
"messages": messages, | ||
}) as res: | ||
data = await res.json() | ||
if "error" in data: | ||
|
@@ -55,6 +61,6 @@ async def generate_response( | |
return data["message"]["content"] | ||
|
||
except Exception as e: | ||
logger.error(f"{e}\nType: {type(e)}") | ||
logger.error(f"{type(e)}: {e}") | ||
|
||
return None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't make a lot of sense with how chat system prompts work, afaik. Essentially the first message in the chat history will be your system prompt, with a role of
system
, and the content of your system prompt.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I add the system prompt to the messages before generating the response:
https://github.com/btc-raspberrypiclub/piclub-bot/pull/20/files#diff-de86459da278c0199e14e2432900816166309e862e9dee0dd929ae3307acfec4R36