346 lines
11 KiB
Python
346 lines
11 KiB
Python
import logging
|
|
import re
|
|
|
|
from dotenv import load_dotenv
|
|
from rich import print as rprint
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from .. import crud, models, schemas
|
|
from ..db import SessionLocal
|
|
from .voe import (
|
|
CheckVoeList,
|
|
UserPredictionThought,
|
|
UserPredictionThoughtRevision,
|
|
VoeDeriveFacts,
|
|
VoeThought,
|
|
)
|
|
|
|
load_dotenv()
|
|
|
|
# Turn of SQLAlchemy Echo logging
|
|
logging.getLogger("sqlalchemy.engine.Engine").disabled = True
|
|
|
|
|
|
# FIXME see if this is SAFE
|
|
async def add_metamessage(db, message_id, metamessage_type, content):
|
|
metamessage = models.Metamessage(
|
|
message_id=message_id,
|
|
metamessage_type=metamessage_type,
|
|
content=content,
|
|
h_metadata={},
|
|
)
|
|
db.add(metamessage)
|
|
|
|
|
|
async def process_item(db: AsyncSession, payload: dict):
|
|
collection: models.Collection
|
|
collection = await crud.get_collection_by_name(
|
|
db, payload["app_id"], payload["user_id"], "honcho"
|
|
)
|
|
if collection is None:
|
|
collection_create = schemas.CollectionCreate(name="honcho", metadata={})
|
|
collection = await crud.create_collection(
|
|
db,
|
|
collection=collection_create,
|
|
app_id=payload["app_id"],
|
|
user_id=payload["user_id"],
|
|
)
|
|
collection_id = collection.id
|
|
processing_args = [
|
|
payload["content"],
|
|
payload["app_id"],
|
|
payload["user_id"],
|
|
payload["session_id"],
|
|
collection_id,
|
|
payload["message_id"],
|
|
db,
|
|
]
|
|
if payload["is_user"]:
|
|
await process_user_message(*processing_args)
|
|
else:
|
|
await process_ai_message(*processing_args)
|
|
return
|
|
|
|
|
|
async def process_ai_message(
|
|
content: str,
|
|
app_id: str,
|
|
user_id: str,
|
|
session_id: str,
|
|
collection_id: str,
|
|
message_id: str,
|
|
db: AsyncSession,
|
|
):
|
|
"""
|
|
Process an AI message. Make a prediction about what the user is going to say to it.
|
|
"""
|
|
rprint(f"[green]Processing AI message: {content}[/green]")
|
|
|
|
subquery = (
|
|
select(models.Message.created_at)
|
|
.where(models.Message.id == message_id)
|
|
.scalar_subquery()
|
|
)
|
|
messages_stmt = (
|
|
select(models.Message)
|
|
.where(models.Message.session_id == session_id)
|
|
.order_by(models.Message.created_at.desc())
|
|
.where(models.Message.created_at < subquery)
|
|
.limit(10)
|
|
)
|
|
|
|
result = await db.execute(messages_stmt)
|
|
messages = result.scalars().all()[::-1]
|
|
|
|
chat_history_str = "\n".join(
|
|
[f"human: {m.content}" if m.is_user else f"ai: {m.content}" for m in messages]
|
|
)
|
|
# append current message to chat history
|
|
chat_history_str = f"{chat_history_str}\nai: {content}"
|
|
|
|
# user prediction thought
|
|
user_prediction_thought = UserPredictionThought(chat_history=chat_history_str)
|
|
user_prediction_thought_response = user_prediction_thought.call()
|
|
|
|
## query the collection to build the context
|
|
additional_data = re.findall(
|
|
r"\d+\.\s([^\n]+)", user_prediction_thought_response.content
|
|
)
|
|
additional_data_set = set()
|
|
additional_data_list = []
|
|
for d in additional_data:
|
|
response = await crud.query_documents(
|
|
db,
|
|
app_id=app_id,
|
|
user_id=user_id,
|
|
collection_id=collection_id,
|
|
query=d,
|
|
top_k=3,
|
|
)
|
|
for document in response:
|
|
additional_data_set.add(document)
|
|
additional_data_list = list(additional_data_set)
|
|
|
|
context_str = "\n".join([document.content for document in additional_data_list])
|
|
|
|
# user prediction thought revision given the context
|
|
user_prediction_thought_revision = UserPredictionThoughtRevision(
|
|
user_prediction_thought=user_prediction_thought_response.content,
|
|
retrieved_context=context_str,
|
|
chat_history=chat_history_str,
|
|
)
|
|
user_prediction_thought_revision_response = user_prediction_thought_revision.call()
|
|
|
|
if user_prediction_thought_revision_response.content == "None":
|
|
rprint("[blue]Model predicted no changes to the user prediction thought")
|
|
await add_metamessage(
|
|
db,
|
|
message_id,
|
|
"user_prediction_thought",
|
|
user_prediction_thought_response.content,
|
|
)
|
|
await add_metamessage(
|
|
db,
|
|
message_id,
|
|
"user_prediction_thought_revision",
|
|
user_prediction_thought_response.content,
|
|
)
|
|
else:
|
|
await add_metamessage(
|
|
db,
|
|
message_id,
|
|
"user_prediction_thought",
|
|
user_prediction_thought_response.content,
|
|
)
|
|
await add_metamessage(
|
|
db,
|
|
message_id,
|
|
"user_prediction_thought_revision",
|
|
user_prediction_thought_revision_response.content,
|
|
)
|
|
|
|
await db.commit()
|
|
|
|
# debugging
|
|
rprint("[blue]=================")
|
|
|
|
rprint("[blue]User Prediction Thought Prompt:")
|
|
content_lines = str(user_prediction_thought)
|
|
rprint(f"[blue]{content_lines}")
|
|
|
|
rprint("[blue]User Prediction Thought:")
|
|
content_lines = str(user_prediction_thought_response.content)
|
|
rprint(f"[blue]{content_lines}")
|
|
|
|
rprint("[blue]=================")
|
|
|
|
rprint("[medium_purple1]=================")
|
|
|
|
rprint("[medium_purple1]User Prediction Thought Revision:")
|
|
content_lines = str(user_prediction_thought_revision)
|
|
rprint(f"[medium_purple1]{content_lines}")
|
|
|
|
rprint("[medium_purple1]User Prediction Thought Revision Response:")
|
|
content_lines = str(user_prediction_thought_revision_response.content)
|
|
rprint(f"[medium_purple1]{content_lines}")
|
|
|
|
rprint("[medium_purple1]=================")
|
|
|
|
|
|
async def process_user_message(
|
|
content: str,
|
|
app_id: str,
|
|
user_id: str,
|
|
session_id: str,
|
|
collection_id: str,
|
|
message_id: str,
|
|
db: AsyncSession,
|
|
):
|
|
"""
|
|
Process a user message. If there are revised user predictions to run VoE against, run it. Otherwise pass.
|
|
"""
|
|
rprint(f"[orange1]Processing User Message: {content}")
|
|
|
|
# Get the AI message directly preceding this User message
|
|
subquery = (
|
|
select(models.Message.created_at)
|
|
.where(models.Message.id == message_id)
|
|
.scalar_subquery()
|
|
)
|
|
|
|
messages_stmt = (
|
|
select(models.Message)
|
|
.where(models.Message.session_id == session_id)
|
|
.where(models.Message.is_user == False)
|
|
.order_by(models.Message.created_at.desc())
|
|
.where(models.Message.created_at < subquery)
|
|
.limit(1)
|
|
)
|
|
|
|
response = await db.execute(messages_stmt)
|
|
ai_message = response.scalar_one_or_none()
|
|
|
|
if ai_message and ai_message.content:
|
|
rprint(f"[orange1]AI Message: {ai_message.content}")
|
|
# Get the User Thought Revision Associated with this AI Message
|
|
metamessages_stmt = (
|
|
select(models.Metamessage)
|
|
.where(models.Metamessage.message_id == ai_message.id)
|
|
.where(
|
|
models.Metamessage.metamessage_type
|
|
== "user_prediction_thought_revision"
|
|
)
|
|
.order_by(models.Metamessage.created_at.asc())
|
|
.limit(1)
|
|
)
|
|
response = await db.execute(metamessages_stmt)
|
|
metamessage = response.scalar_one_or_none()
|
|
|
|
if metamessage and metamessage.content:
|
|
rprint(f"[orange1]Metamessage: {metamessage.content}")
|
|
|
|
# VoE thought
|
|
voe_thought = VoeThought(
|
|
user_prediction_thought_revision=metamessage.content, actual=content
|
|
)
|
|
voe_thought_response = voe_thought.call()
|
|
|
|
# VoE derive facts
|
|
voe_derive_facts = VoeDeriveFacts(
|
|
ai_message=ai_message.content,
|
|
user_prediction_thought_revision=metamessage.content,
|
|
actual=content,
|
|
voe_thought=voe_thought_response.content,
|
|
)
|
|
voe_derive_facts_response = voe_derive_facts.call()
|
|
|
|
# debugging
|
|
rprint("[orange1]=================")
|
|
rprint("[orange1]Voe Thought Prompt:")
|
|
content_lines = str(voe_thought)
|
|
rprint(f"[orange1]{content_lines}")
|
|
rprint("[orange1]Voe Thought:")
|
|
content_lines = str(voe_thought_response.content)
|
|
rprint(f"[orange1]{content_lines}")
|
|
rprint("[orange1]=================")
|
|
|
|
rprint("[orange1]================")
|
|
rprint("[orange1]Voe Derive Facts Prompt:")
|
|
content_lines = str(voe_derive_facts)
|
|
rprint(f"[orange1]{content_lines}")
|
|
|
|
rprint("[orange1]Voe Derive Facts Response:")
|
|
content_lines = str(voe_derive_facts_response.content)
|
|
rprint(f"[orange1]{content_lines}")
|
|
rprint("[orange1]=================")
|
|
|
|
facts = re.findall(r"\d+\.\s([^\n]+)", voe_derive_facts_response.content)
|
|
rprint("[orange1]=================")
|
|
rprint("[orange1]The Facts Themselves:")
|
|
rprint(facts)
|
|
new_facts = await check_dups(app_id, user_id, collection_id, facts)
|
|
|
|
for fact in new_facts:
|
|
create_document = schemas.DocumentCreate(content=fact)
|
|
async with SessionLocal() as db:
|
|
doc = await crud.create_document(
|
|
db,
|
|
document=create_document,
|
|
app_id=app_id,
|
|
user_id=user_id,
|
|
collection_id=collection_id,
|
|
)
|
|
rprint(f"[orange1]Returned Document: {doc.content}")
|
|
else:
|
|
rprint("[red] No Prediction Associated with this Message")
|
|
return
|
|
else:
|
|
rprint("[red]No AI message before this user message[/red]")
|
|
return
|
|
|
|
|
|
async def check_dups(app_id: str, user_id: str, collection_id: str, facts: list[str]):
|
|
"""Check that we're not storing duplicate facts"""
|
|
|
|
check_duplication = CheckVoeList(existing_facts=[], new_fact="")
|
|
result = None
|
|
new_facts = []
|
|
# global_existing_facts = [] # for debugging
|
|
for fact in facts:
|
|
async with SessionLocal() as db:
|
|
result = await crud.query_documents(
|
|
db=db,
|
|
app_id=app_id,
|
|
user_id=user_id,
|
|
collection_id=collection_id,
|
|
query=fact,
|
|
top_k=5,
|
|
)
|
|
existing_facts = [document.content for document in result]
|
|
if len(existing_facts) == 0:
|
|
new_facts.append(fact)
|
|
rprint(f"[light_steel_blue]New Fact: {fact}")
|
|
continue
|
|
|
|
# global_existing_facts.extend(existing_facts) # for debugging
|
|
|
|
check_duplication.existing_facts = existing_facts
|
|
check_duplication.new_fact = fact
|
|
response = check_duplication.call()
|
|
rprint("[light_steel_blue]==================")
|
|
rprint(f"[light_steel_blue]Dedupe Responses: {response.content}")
|
|
rprint("[light_steel_blue]==================")
|
|
if response.content == "true":
|
|
new_facts.append(fact)
|
|
rprint(f"[light_steel_blue]New Fact: {fact}")
|
|
continue
|
|
|
|
rprint("[light_steel_blue]===================")
|
|
# rprint("[light_steel_blue]Existing Facts:")
|
|
# rprint(global_existing_facts)
|
|
rprint("[light_steel_blue]Net New Facts:")
|
|
rprint(new_facts)
|
|
rprint("[light_steel_blue]===================")
|
|
return new_facts
|