Skip to content

Commit

Permalink
Working honchofied discord bot
Browse files Browse the repository at this point in the history
  • Loading branch information
VVoruganti committed Sep 1, 2024
1 parent 71e9ec4 commit 60e5705
Show file tree
Hide file tree
Showing 5 changed files with 1,214 additions and 150 deletions.
105 changes: 0 additions & 105 deletions agent/agent/cache.py

This file was deleted.

10 changes: 4 additions & 6 deletions bot/app.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import discord
import sentry_sdk
import os
import asyncio
from honcho import Honcho

from common import init
from dotenv import load_dotenv

load_dotenv()
Expand All @@ -16,11 +15,10 @@
profiles_sample_rate=rate,
)

CACHE, LOCK, _, (THOUGHT_CHANNEL, TOKEN) = init()

THOUGHT_CHANNEL = os.environ["THOUGHT_CHANNEL_ID"]
TOKEN = os.environ["BOT_TOKEN"]
LOCK = asyncio.Lock()
honcho = Honcho()
app = honcho.apps.get_or_create("Tutor-GPT") # TODO use environment variable

intents = discord.Intents.default()
intents.messages = True
Expand All @@ -30,7 +28,7 @@
bot = discord.Bot(intents=intents)


bot.load_extension("bot.core")
bot.load_extension("core")


bot.run(TOKEN)
159 changes: 120 additions & 39 deletions bot/core.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,67 @@
# core functionality

import discord
import os
from __main__ import (
CACHE,
LOCK,
THOUGHT_CHANNEL,
)
from __main__ import THOUGHT_CHANNEL, honcho, app
from discord.ext import commands
from typing import Optional
from agent.chain import BloomChain
from langchain.schema import AIMessage

from agent.chain import ThinkCall, RespondCall
from honcho.types.apps import User
from honcho.types.apps.users import Session

import sentry_sdk


def chat(message: str, user: User, session: Session):
thought: str = (
ThinkCall(
user_input=message,
app_id=app.id,
user_id=user.id,
session_id=session.id,
honcho=honcho,
)
.call()
.content
)
yield thought
response = (
RespondCall(
user_input=message,
thought=thought,
app_id=app.id,
user_id=user.id,
session_id=session.id,
honcho=honcho,
)
.call()
.content
)
yield response
new_message = honcho.apps.users.sessions.messages.create(
is_user=True,
session_id=session.id,
app_id=app.id,
user_id=user.id,
content=message,
)
honcho.apps.users.sessions.metamessages.create(
app_id=app.id,
session_id=session.id,
user_id=user.id,
message_id=new_message.id,
metamessage_type="thought",
content=thought,
)
honcho.apps.users.sessions.messages.create(
is_user=False,
session_id=session.id,
app_id=app.id,
user_id=user.id,
content=response,
)


class Core(commands.Cog):
def __init__(self, bot) -> None:
self.bot = bot
Expand Down Expand Up @@ -51,11 +99,45 @@ async def on_message(self, message):
return

user_id = f"discord_{str(message.author.id)}"
user = honcho.apps.users.get_or_create(name=user_id, app_id=app.id)
# Get cache for conversation
async with LOCK:
CONVERSATION = CACHE.get_or_create(
location_id=str(message.channel.id), user_id=user_id
# async with LOCK:
# CONVERSATION = CACHE.get_or_create(
# location_id=str(message.channel.id), user_id=user_id
# )

# Use the channel ID as the location_id (for DMs, this will be unique to the user)
location_id = str(message.channel.id)

sessions_iter = honcho.apps.users.sessions.list(
app_id=app.id, user_id=user.id, reverse=True
)
sessions = list(sessions_iter)
session = None
if sessions:
# find the right session
for s in sessions:
if s.metadata.get("location_id") == location_id:
session = s
print(session.id)
break
# if no session is found after the for loop, create a new one
if not session:
print("No session found amongst existing ones, creating new one")
session = honcho.apps.users.sessions.create(
user_id=user.id,
app_id=app.id,
metadata={"location_id": location_id},
)
print(session.id)
else:
print("No active session found")
session = honcho.apps.users.sessions.create(
user_id=user.id,
app_id=app.id,
metadata={"location_id": location_id},
)
print(session.id)

# Get the message content but remove any mentions
inp = message.content.replace(str("<@" + str(self.bot.user.id) + ">"), "")
Expand All @@ -64,21 +146,9 @@ async def on_message(self, message):
async def respond(reply=True, forward_thought=True):
"Generate response too user"
async with message.channel.typing():
# thought = ""
# response = ""
# if (CONVERSATION.metadata is not None and "A/B" in CONVERSATION.metadata and CONVERSATION.metadata["A/B"] == True):
# async with httpx.AsyncClient() as client:
# response = await client.post(f'{os.environ["HONCHO_URL"]}/chat', json={
# "user_id": CONVERSATION.user_id,
# "conversation_id": CONVERSATION.conversation_id,
# "message": inp
# }, timeout=None)
# response_text = response.json()
# thought = response_text["thought"]
# response = response_text["response"]
# else:
thought, response = await BloomChain.chat(CONVERSATION, inp)
turn = chat(inp, user, session)

thought = next(turn)
# sanitize thought by adding zero width spaces to triple backticks
thought = thought.replace("```", "`\u200b`\u200b`")

Expand All @@ -100,6 +170,8 @@ async def respond(reply=True, forward_thought=True):
f"{link}\n```\nThought: {thought}\n```"
)

response = next(turn)

# Response Forwarding
if len(response) > n:
chunks = [
Expand All @@ -117,16 +189,16 @@ async def respond(reply=True, forward_thought=True):
await message.channel.send(response)

# if the message came from a DM channel...
if isinstance(message.channel, discord.channel.DMChannel):
if isinstance(message.channel, discord.DMChannel):
await respond(reply=False, forward_thought=False)

# If the bot was mentioned in the message
if not isinstance(message.channel, discord.channel.DMChannel):
if not isinstance(message.channel, discord.DMChannel):
if str(self.bot.user.id) in message.content:
await respond(forward_thought=True)

# If the bot was replied to in the message
if not isinstance(message.channel, discord.channel.DMChannel):
if not isinstance(message.channel, discord.DMChannel):
if message.reference is not None:
reply_msg = await self.bot.get_channel(
message.channel.id
Expand Down Expand Up @@ -182,19 +254,28 @@ async def restart(
Args:
ctx: context, necessary for bot commands
"""
async with LOCK:
CONVERSATION = CACHE.get_or_create(
location_id=str(ctx.channel_id),
user_id=f"discord_{str(ctx.author.id)}",
restart=True,
)
user_id = f"discord_{str(ctx.author.id)}"
user = honcho.apps.users.get_or_create(name=user_id, app_id=app.id)
location_id = str(ctx.channel_id)

if respond:
msg = "Great! The conversation has been restarted. What would you like to talk about?"
CONVERSATION.add_message("response", AIMessage(content=msg))
await ctx.respond(msg)
sessions = honcho.apps.users.sessions.list(
app_id=app.id, user_id=user.id, reverse=True
)

sessions_list = list(sessions)
if sessions_list:
# find the right session to delete
for session in sessions_list:
if session.metadata.get("location_id") == location_id:
honcho.apps.users.sessions.delete(
app_id=app.id, user_id=user.id, session_id=session.id
)
break
msg = "The conversation has been restarted."
else:
return
msg = "No active conversation found to restart."

await ctx.respond(msg)


def setup(bot):
Expand Down
Loading

0 comments on commit 60e5705

Please sign in to comment.