start api2
This commit is contained in:
parent
eeac17527f
commit
31dc6d1646
67
api/cache.py
67
api/cache.py
|
|
@ -8,44 +8,63 @@ import uuid
|
|||
from typing import List, Dict
|
||||
from langchain.schema import BaseMessage, Document
|
||||
from pydantic import BaseModel
|
||||
import sentry_sdk
|
||||
|
||||
# import sentry_sdk
|
||||
|
||||
|
||||
class Conversation:
|
||||
"Wrapper Class for storing contexts between channels. Using an object to pass by reference avoid additional cache hits"
|
||||
@sentry_sdk.trace
|
||||
def __init__(self, mediator: SupabaseMediator, user_id: str, session_id: str = str(uuid.uuid4()), location_id: str = "web", metadata: Dict = {}):
|
||||
|
||||
# @sentry_sdk.trace
|
||||
def __init__(
|
||||
self,
|
||||
mediator: SupabaseMediator,
|
||||
user_id: str,
|
||||
session_id: str = str(uuid.uuid4()),
|
||||
metadata: Dict = {},
|
||||
):
|
||||
self.mediator: SupabaseMediator = mediator
|
||||
self.user_id: str = user_id
|
||||
self.session_id: str = session_id
|
||||
self.location_id: str = location_id
|
||||
self.user_id = user_id
|
||||
self.session_id = session_id
|
||||
self.metadata: Dict = metadata
|
||||
|
||||
@sentry_sdk.trace
|
||||
def add_message(self, message_type: str, message: BaseMessage,) -> None:
|
||||
self.mediator.add_message(self.session_id, self.user_id, message_type, message)
|
||||
# @sentry_sdk.trace
|
||||
def add_message(
|
||||
self,
|
||||
message_type: str,
|
||||
message: BaseMessage,
|
||||
) -> None:
|
||||
self.mediator.add_message(self.session_id, message_type, message.content)
|
||||
|
||||
@sentry_sdk.trace
|
||||
def messages(self, message_type: str) -> List[BaseMessage]:
|
||||
return self.mediator.messages(self.session_id, self.user_id, message_type)
|
||||
# @sentry_sdk.trace
|
||||
def get_messages(self, message_type: str) -> List[BaseMessage]:
|
||||
return self.mediator.get_messages(self.session_id, message_type)
|
||||
|
||||
@sentry_sdk.trace
|
||||
# @sentry_sdk.trace
|
||||
def delete(self) -> None:
|
||||
self.mediator.delete_session(self.session_id)
|
||||
|
||||
@sentry_sdk.trace
|
||||
def restart(self) -> None:
|
||||
self.delete()
|
||||
representation = self.mediator.add_session(user_id=self.user_id, location_id=self.location_id)
|
||||
self.session_id: str = representation["id"]
|
||||
self.metadata = representation["metadata"]
|
||||
|
||||
# @sentry_sdk.trace
|
||||
# def restart(self) -> None:
|
||||
# self.delete()
|
||||
# representation = self.mediator.add_session(
|
||||
# user_id=self.user_id, location_id=self.location_id
|
||||
# )
|
||||
# self.session_id: str = representation["id"]
|
||||
# self.metadata = representation["metadata"]
|
||||
|
||||
# vector DB fn
|
||||
@sentry_sdk.trace
|
||||
# @sentry_sdk.trace
|
||||
def add_texts(self, texts: List[str]) -> None:
|
||||
metadatas = [{"session_id": self.session_id, "user_id": self.user_id} for _ in range(len(texts))]
|
||||
metadatas = [
|
||||
{"session_id": self.session_id, "user_id": self.user_id}
|
||||
for _ in range(len(texts))
|
||||
]
|
||||
self.mediator.vector_table.add_texts(texts, metadatas)
|
||||
|
||||
# vector DB fn
|
||||
@sentry_sdk.trace
|
||||
# @sentry_sdk.trace
|
||||
def similarity_search(self, query: str, match_count: int = 5) -> List[Document]:
|
||||
return self.mediator.vector_table.similarity_search(query=query, k=match_count, filter={"user_id": self.user_id})
|
||||
return self.mediator.vector_table.similarity_search(
|
||||
query=query, k=match_count, filter={"user_id": self.user_id}
|
||||
)
|
||||
|
|
|
|||
366
api/chain.py
366
api/chain.py
|
|
@ -1,191 +1,301 @@
|
|||
import os
|
||||
from langchain.chat_models import ChatOpenAI, AzureChatOpenAI
|
||||
from langchain.output_parsers.list import NumberedListOutputParser
|
||||
from langchain.prompts import load_prompt, ChatPromptTemplate, SystemMessagePromptTemplate
|
||||
from langchain.prompts import (
|
||||
load_prompt,
|
||||
ChatPromptTemplate,
|
||||
SystemMessagePromptTemplate,
|
||||
)
|
||||
from langchain.schema import AIMessage, HumanMessage, BaseMessage
|
||||
from dotenv import load_dotenv
|
||||
from collections.abc import AsyncIterator
|
||||
from cache import Conversation
|
||||
from typing import List
|
||||
import sentry_sdk
|
||||
|
||||
# import sentry_sdk
|
||||
|
||||
load_dotenv()
|
||||
|
||||
SYSTEM_THOUGHT = load_prompt(os.path.join(os.path.dirname(__file__), 'prompts/thought.yaml'))
|
||||
SYSTEM_RESPONSE = load_prompt(os.path.join(os.path.dirname(__file__), 'prompts/response.yaml'))
|
||||
SYSTEM_THOUGHT_REVISION = load_prompt(os.path.join(os.path.dirname(__file__), 'prompts/thought_revision.yaml'))
|
||||
SYSTEM_USER_PREDICTION_THOUGHT = load_prompt(os.path.join(os.path.dirname(__file__), 'prompts/user_prediction_thought.yaml'))
|
||||
SYSTEM_USER_PREDICTION_THOUGHT_REVISION = load_prompt(os.path.join(os.path.dirname(__file__), 'prompts/user_prediction_thought_revision.yaml'))
|
||||
SYSTEM_VOE_THOUGHT = load_prompt(os.path.join(os.path.dirname(__file__), 'prompts/voe_thought.yaml'))
|
||||
SYSTEM_VOE = load_prompt(os.path.join(os.path.dirname(__file__), 'prompts/voe.yaml'))
|
||||
SYSTEM_CHECK_VOE_LIST = load_prompt(os.path.join(os.path.dirname(__file__), 'prompts/check_voe_list.yaml'))
|
||||
SYSTEM_THOUGHT = load_prompt(
|
||||
os.path.join(os.path.dirname(__file__), "prompts/thought.yaml")
|
||||
)
|
||||
SYSTEM_RESPONSE = load_prompt(
|
||||
os.path.join(os.path.dirname(__file__), "prompts/response.yaml")
|
||||
)
|
||||
SYSTEM_THOUGHT_REVISION = load_prompt(
|
||||
os.path.join(os.path.dirname(__file__), "prompts/thought_revision.yaml")
|
||||
)
|
||||
SYSTEM_USER_PREDICTION_THOUGHT = load_prompt(
|
||||
os.path.join(os.path.dirname(__file__), "prompts/user_prediction_thought.yaml")
|
||||
)
|
||||
SYSTEM_USER_PREDICTION_THOUGHT_REVISION = load_prompt(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__), "prompts/user_prediction_thought_revision.yaml"
|
||||
)
|
||||
)
|
||||
SYSTEM_VOE_THOUGHT = load_prompt(
|
||||
os.path.join(os.path.dirname(__file__), "prompts/voe_thought.yaml")
|
||||
)
|
||||
SYSTEM_VOE = load_prompt(os.path.join(os.path.dirname(__file__), "prompts/voe.yaml"))
|
||||
SYSTEM_CHECK_VOE_LIST = load_prompt(
|
||||
os.path.join(os.path.dirname(__file__), "prompts/check_voe_list.yaml")
|
||||
)
|
||||
|
||||
|
||||
class BloomChain:
|
||||
"Wrapper class for encapsulating the multiple different chains used in reasoning for the tutor's thoughts"
|
||||
llm: AzureChatOpenAI = AzureChatOpenAI(deployment_name = os.environ['OPENAI_API_DEPLOYMENT_NAME'], temperature=1.2, model_kwargs={"top_p": 0.5})
|
||||
parser_llm: AzureChatOpenAI = AzureChatOpenAI(deployment_name = os.environ['OPENAI_API_DEPLOYMENT_NAME'])
|
||||
system_voe_thought: SystemMessagePromptTemplate = SystemMessagePromptTemplate(prompt=SYSTEM_VOE_THOUGHT)
|
||||
system_voe: SystemMessagePromptTemplate = SystemMessagePromptTemplate(prompt=SYSTEM_VOE)
|
||||
system_check_voe_list: SystemMessagePromptTemplate = SystemMessagePromptTemplate(prompt=SYSTEM_CHECK_VOE_LIST)
|
||||
system_thought: SystemMessagePromptTemplate = SystemMessagePromptTemplate(prompt=SYSTEM_THOUGHT)
|
||||
system_thought_revision: SystemMessagePromptTemplate = SystemMessagePromptTemplate(prompt=SYSTEM_THOUGHT_REVISION)
|
||||
system_response: SystemMessagePromptTemplate = SystemMessagePromptTemplate(prompt=SYSTEM_RESPONSE)
|
||||
system_user_prediction_thought: SystemMessagePromptTemplate = SystemMessagePromptTemplate(prompt=SYSTEM_USER_PREDICTION_THOUGHT)
|
||||
system_user_prediction_thought_revision: SystemMessagePromptTemplate = SystemMessagePromptTemplate(prompt=SYSTEM_USER_PREDICTION_THOUGHT_REVISION)
|
||||
llm: AzureChatOpenAI = AzureChatOpenAI(
|
||||
deployment_name=os.environ["OPENAI_API_DEPLOYMENT_NAME"],
|
||||
temperature=1.2,
|
||||
model_kwargs={"top_p": 0.5},
|
||||
)
|
||||
parser_llm: AzureChatOpenAI = AzureChatOpenAI(
|
||||
deployment_name=os.environ["OPENAI_API_DEPLOYMENT_NAME"]
|
||||
)
|
||||
system_voe_thought: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
|
||||
prompt=SYSTEM_VOE_THOUGHT
|
||||
)
|
||||
system_voe: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
|
||||
prompt=SYSTEM_VOE
|
||||
)
|
||||
system_check_voe_list: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
|
||||
prompt=SYSTEM_CHECK_VOE_LIST
|
||||
)
|
||||
system_thought: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
|
||||
prompt=SYSTEM_THOUGHT
|
||||
)
|
||||
system_thought_revision: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
|
||||
prompt=SYSTEM_THOUGHT_REVISION
|
||||
)
|
||||
system_response: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
|
||||
prompt=SYSTEM_RESPONSE
|
||||
)
|
||||
system_user_prediction_thought: SystemMessagePromptTemplate = (
|
||||
SystemMessagePromptTemplate(prompt=SYSTEM_USER_PREDICTION_THOUGHT)
|
||||
)
|
||||
system_user_prediction_thought_revision: SystemMessagePromptTemplate = (
|
||||
SystemMessagePromptTemplate(prompt=SYSTEM_USER_PREDICTION_THOUGHT_REVISION)
|
||||
)
|
||||
|
||||
output_parser = NumberedListOutputParser()
|
||||
|
||||
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
@classmethod
|
||||
@sentry_sdk.trace
|
||||
# @sentry_sdk.trace
|
||||
def think(cls, cache: Conversation, input: str):
|
||||
"""Generate Bloom's thought on the user."""
|
||||
# load message history
|
||||
thought_prompt = ChatPromptTemplate.from_messages([
|
||||
cls.system_thought,
|
||||
*cache.messages("thought"),
|
||||
HumanMessage(content=input)
|
||||
])
|
||||
chain = thought_prompt | cls.llm
|
||||
thought_prompt = ChatPromptTemplate.from_messages(
|
||||
[
|
||||
cls.system_thought,
|
||||
*cache.get_messages("thought"),
|
||||
HumanMessage(content=input),
|
||||
]
|
||||
)
|
||||
chain = thought_prompt | cls.llm
|
||||
|
||||
cache.add_message("thought", HumanMessage(content=input))
|
||||
|
||||
return Streamable(
|
||||
chain.astream({}, {"tags": ["thought"], "metadata": {"conversation_id": cache.conversation_id, "user_id": cache.user_id}}),
|
||||
lambda thought: cache.add_message("thought", AIMessage(content=thought))
|
||||
chain.astream(
|
||||
{},
|
||||
{
|
||||
"tags": ["thought"],
|
||||
"metadata": {
|
||||
"session_id": cache.session_id,
|
||||
"user_id": cache.user_id,
|
||||
},
|
||||
},
|
||||
),
|
||||
lambda thought: cache.add_message("thought", AIMessage(content=thought)),
|
||||
)
|
||||
|
||||
|
||||
@classmethod
|
||||
@sentry_sdk.trace
|
||||
# @sentry_sdk.trace
|
||||
def revise_thought(cls, cache: Conversation, input: str, thought: str):
|
||||
"""Revise Bloom's thought about the user with retrieved personal data"""
|
||||
|
||||
|
||||
# construct rag prompt, retrieve docs
|
||||
query = f"input: {input}\n thought: {thought}"
|
||||
docs = cache.similarity_search(query)
|
||||
|
||||
messages = ChatPromptTemplate.from_messages([
|
||||
cls.system_thought_revision,
|
||||
*cache.messages('thought_revision'),
|
||||
HumanMessage(content=input)
|
||||
])
|
||||
messages = ChatPromptTemplate.from_messages(
|
||||
[
|
||||
cls.system_thought_revision,
|
||||
*cache.get_messages("thought_revision"),
|
||||
HumanMessage(content=input),
|
||||
]
|
||||
)
|
||||
chain = messages | cls.llm
|
||||
|
||||
cache.add_message("thought_revision", HumanMessage(content=input))
|
||||
|
||||
return Streamable(
|
||||
chain.astream({ "thought": thought, "retrieved_vectors": "\n".join(doc.page_content for doc in docs)}, {"tags": ["thought_revision"], "metadata": {"conversation_id": cache.conversation_id, "user_id": cache.user_id}}),
|
||||
lambda thought_revision: cache.add_message("thought_revision", AIMessage(content=thought_revision)) # add the revised thought to thought memory
|
||||
chain.astream(
|
||||
{
|
||||
"thought": thought,
|
||||
"retrieved_vectors": "\n".join(doc.page_content for doc in docs),
|
||||
},
|
||||
{
|
||||
"tags": ["thought_revision"],
|
||||
"metadata": {
|
||||
"conversation_id": cache.conversation_id,
|
||||
"user_id": cache.user_id,
|
||||
},
|
||||
},
|
||||
),
|
||||
lambda thought_revision: cache.add_message(
|
||||
"thought_revision", AIMessage(content=thought_revision)
|
||||
), # add the revised thought to thought memory
|
||||
)
|
||||
|
||||
|
||||
@classmethod
|
||||
@sentry_sdk.trace
|
||||
# @sentry_sdk.trace
|
||||
def respond(cls, cache: Conversation, thought: str, input: str):
|
||||
"""Generate Bloom's response to the user."""
|
||||
response_prompt = ChatPromptTemplate.from_messages([
|
||||
cls.system_response,
|
||||
*cache.messages("response"),
|
||||
HumanMessage(content=input)
|
||||
])
|
||||
response_prompt = ChatPromptTemplate.from_messages(
|
||||
[
|
||||
cls.system_response,
|
||||
*cache.get_messages("response"),
|
||||
HumanMessage(content=input),
|
||||
]
|
||||
)
|
||||
chain = response_prompt | cls.llm
|
||||
|
||||
cache.add_message("response", HumanMessage(content=input))
|
||||
|
||||
return Streamable(
|
||||
chain.astream({ "thought": thought }, {"tags": ["response"], "metadata": {"conversation_id": cache.conversation_id, "user_id": cache.user_id}}),
|
||||
lambda response: cache.add_message("response", AIMessage(content=response))
|
||||
chain.astream(
|
||||
{"thought": thought},
|
||||
{
|
||||
"tags": ["response"],
|
||||
"metadata": {
|
||||
"conversation_id": cache.conversation_id,
|
||||
"user_id": cache.user_id,
|
||||
},
|
||||
},
|
||||
),
|
||||
lambda response: cache.add_message("response", AIMessage(content=response)),
|
||||
)
|
||||
|
||||
|
||||
@classmethod
|
||||
@sentry_sdk.trace
|
||||
# @sentry_sdk.trace
|
||||
async def think_user_prediction(cls, cache: Conversation, input: str):
|
||||
"""Generate a thought about what the user is going to say"""
|
||||
|
||||
messages = ChatPromptTemplate.from_messages([
|
||||
cls.system_user_prediction_thought,
|
||||
])
|
||||
messages = ChatPromptTemplate.from_messages(
|
||||
[
|
||||
cls.system_user_prediction_thought,
|
||||
]
|
||||
)
|
||||
chain = messages | cls.llm
|
||||
|
||||
history = unpack_messages(cache.messages('response'))
|
||||
history = unpack_messages(cache.get_messages("response"))
|
||||
|
||||
user_prediction_thought = await chain.ainvoke(
|
||||
{"history": history},
|
||||
{"tags": ["user_prediction_thought"], "metadata": {"conversation_id": cache.conversation_id, "user_id": cache.user_id}}
|
||||
{"history": history},
|
||||
{
|
||||
"tags": ["user_prediction_thought"],
|
||||
"metadata": {
|
||||
"conversation_id": cache.conversation_id,
|
||||
"user_id": cache.user_id,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
cache.add_message("user_prediction_thought", user_prediction_thought)
|
||||
|
||||
return user_prediction_thought.content
|
||||
|
||||
|
||||
@classmethod
|
||||
@sentry_sdk.trace
|
||||
async def revise_user_prediction_thought(cls, cache: Conversation, user_prediction_thought: str, input: str):
|
||||
# @sentry_sdk.trace
|
||||
async def revise_user_prediction_thought(
|
||||
cls, cache: Conversation, user_prediction_thought: str, input: str
|
||||
):
|
||||
"""Revise the thought about what the user is going to say based on retrieval of VoE facts"""
|
||||
|
||||
messages = ChatPromptTemplate.from_messages([
|
||||
cls.system_user_prediction_thought_revision,
|
||||
])
|
||||
messages = ChatPromptTemplate.from_messages(
|
||||
[
|
||||
cls.system_user_prediction_thought_revision,
|
||||
]
|
||||
)
|
||||
chain = messages | cls.llm
|
||||
|
||||
# construct rag prompt, retrieve docs
|
||||
query = f"input: {input}\n thought: {user_prediction_thought}"
|
||||
docs = cache.similarity_search(query)
|
||||
|
||||
history = unpack_messages(cache.messages('response'))
|
||||
history = unpack_messages(cache.get_messages("response"))
|
||||
|
||||
user_prediction_thought_revision = await chain.ainvoke(
|
||||
{"history": history, "user_prediction_thought": user_prediction_thought, "retrieved_vectors": "\n".join(doc.page_content for doc in docs)},
|
||||
config={"tags": ["user_prediction_thought_revision"], "metadata": {"conversation_id": cache.conversation_id, "user_id": cache.user_id}}
|
||||
{
|
||||
"history": history,
|
||||
"user_prediction_thought": user_prediction_thought,
|
||||
"retrieved_vectors": "\n".join(doc.page_content for doc in docs),
|
||||
},
|
||||
config={
|
||||
"tags": ["user_prediction_thought_revision"],
|
||||
"metadata": {
|
||||
"conversation_id": cache.conversation_id,
|
||||
"user_id": cache.user_id,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
cache.add_message("user_prediction_thought_revision", user_prediction_thought_revision)
|
||||
cache.add_message(
|
||||
"user_prediction_thought_revision", user_prediction_thought_revision
|
||||
)
|
||||
|
||||
return user_prediction_thought_revision.content
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
@sentry_sdk.trace
|
||||
async def think_violation_of_expectation(cls, cache: Conversation, inp: str, user_prediction_thought_revision: str) -> None:
|
||||
# @sentry_sdk.trace
|
||||
async def think_violation_of_expectation(
|
||||
cls, cache: Conversation, inp: str, user_prediction_thought_revision: str
|
||||
) -> None:
|
||||
"""Assess whether expectation was violated, derive and store facts"""
|
||||
|
||||
# format prompt
|
||||
messages = ChatPromptTemplate.from_messages([
|
||||
cls.system_voe_thought
|
||||
])
|
||||
messages = ChatPromptTemplate.from_messages([cls.system_voe_thought])
|
||||
chain = messages | cls.llm
|
||||
|
||||
voe_thought = await chain.ainvoke(
|
||||
{"user_prediction_thought_revision": user_prediction_thought_revision, "actual": inp},
|
||||
config={"tags": ["voe_thought"], "metadata": {"user_id": cache.user_id}}
|
||||
{
|
||||
"user_prediction_thought_revision": user_prediction_thought_revision,
|
||||
"actual": inp,
|
||||
},
|
||||
config={"tags": ["voe_thought"], "metadata": {"user_id": cache.user_id}},
|
||||
)
|
||||
|
||||
cache.add_message("voe_thought", voe_thought)
|
||||
|
||||
return voe_thought.content
|
||||
|
||||
|
||||
@classmethod
|
||||
@sentry_sdk.trace
|
||||
async def violation_of_expectation(cls, cache: Conversation, inp: str, user_prediction_thought_revision: str, voe_thought: str) -> None:
|
||||
# @sentry_sdk.trace
|
||||
async def violation_of_expectation(
|
||||
cls,
|
||||
cache: Conversation,
|
||||
inp: str,
|
||||
user_prediction_thought_revision: str,
|
||||
voe_thought: str,
|
||||
) -> None:
|
||||
"""Assess whether expectation was violated, derive and store facts"""
|
||||
|
||||
# format prompt
|
||||
messages = ChatPromptTemplate.from_messages([
|
||||
cls.system_voe
|
||||
])
|
||||
messages = ChatPromptTemplate.from_messages([cls.system_voe])
|
||||
chain = messages | cls.llm
|
||||
|
||||
voe = await chain.ainvoke(
|
||||
{
|
||||
"ai_message": cache.messages('response')[-1].content,
|
||||
"user_prediction_thought_revision": user_prediction_thought_revision,
|
||||
"actual": inp,
|
||||
"voe_thought": voe_thought
|
||||
},
|
||||
config={"tags": ["voe"], "metadata": {"user_id": cache.user_id}}
|
||||
"ai_message": cache.get_messages("response")[-1].content,
|
||||
"user_prediction_thought_revision": user_prediction_thought_revision,
|
||||
"actual": inp,
|
||||
"voe_thought": voe_thought,
|
||||
},
|
||||
config={"tags": ["voe"], "metadata": {"user_id": cache.user_id}},
|
||||
)
|
||||
|
||||
cache.add_message("voe", voe)
|
||||
|
|
@ -193,14 +303,12 @@ class BloomChain:
|
|||
return facts
|
||||
|
||||
@classmethod
|
||||
@sentry_sdk.trace
|
||||
# @sentry_sdk.trace
|
||||
async def check_voe_list(cls, cache: Conversation, facts: List[str]):
|
||||
"""Filter the facts to just new ones"""
|
||||
|
||||
# create the message object from prompt template
|
||||
messages = ChatPromptTemplate.from_messages([
|
||||
cls.system_check_voe_list
|
||||
])
|
||||
messages = ChatPromptTemplate.from_messages([cls.system_check_voe_list])
|
||||
chain = messages | cls.llm
|
||||
|
||||
# unpack the list of strings into one string for similarity search
|
||||
|
|
@ -212,10 +320,12 @@ class BloomChain:
|
|||
|
||||
filtered_facts = await chain.ainvoke(
|
||||
{
|
||||
"existing_facts": "\n".join(fact.page_content for fact in existing_facts),
|
||||
"facts": "\n".join(fact for fact in facts)
|
||||
},
|
||||
config={"tags": ["check_voe_list"], "metadata": {"user_id": cache.user_id}}
|
||||
"existing_facts": "\n".join(
|
||||
fact.page_content for fact in existing_facts
|
||||
),
|
||||
"facts": "\n".join(fact for fact in facts),
|
||||
},
|
||||
config={"tags": ["check_voe_list"], "metadata": {"user_id": cache.user_id}},
|
||||
)
|
||||
|
||||
data = cls.output_parser.parse(filtered_facts.content)
|
||||
|
|
@ -226,15 +336,21 @@ class BloomChain:
|
|||
else:
|
||||
cache.add_texts(data)
|
||||
|
||||
@classmethod
|
||||
@sentry_sdk.trace
|
||||
async def chat(cls, cache: Conversation, inp: str ) -> tuple[str, str]:
|
||||
@classmethod
|
||||
# @sentry_sdk.trace
|
||||
async def chat(cls, cache: Conversation, inp: str) -> tuple[str, str]:
|
||||
# VoE has to happen first. If there's user prediction history, derive and store fact(s)
|
||||
if cache.messages('user_prediction_thought_revision'):
|
||||
user_prediction_thought_revision = cache.messages('user_prediction_thought_revision')[-1].content
|
||||
if cache.get_messages("user_prediction_thought_revision"):
|
||||
user_prediction_thought_revision = cache.get_messages(
|
||||
"user_prediction_thought_revision"
|
||||
)[-1].content
|
||||
|
||||
voe_thought = await cls.think_violation_of_expectation(cache, inp, user_prediction_thought_revision)
|
||||
voe_facts = await cls.violation_of_expectation(cache, inp, user_prediction_thought_revision, voe_thought)
|
||||
voe_thought = await cls.think_violation_of_expectation(
|
||||
cache, inp, user_prediction_thought_revision
|
||||
)
|
||||
voe_facts = await cls.violation_of_expectation(
|
||||
cache, inp, user_prediction_thought_revision, voe_thought
|
||||
)
|
||||
|
||||
if not voe_facts or voe_facts[0] == "None":
|
||||
pass
|
||||
|
|
@ -251,20 +367,28 @@ class BloomChain:
|
|||
response = await response_iterator()
|
||||
|
||||
user_prediction_thought = await cls.think_user_prediction(cache, inp)
|
||||
user_prediction_thought_revision = await cls.revise_user_prediction_thought(cache, user_prediction_thought, inp)
|
||||
user_prediction_thought_revision = await cls.revise_user_prediction_thought(
|
||||
cache, user_prediction_thought, inp
|
||||
)
|
||||
|
||||
return thought, response
|
||||
|
||||
@classmethod
|
||||
@sentry_sdk.trace
|
||||
async def stream(cls, cache: Conversation, inp: str ):
|
||||
@classmethod
|
||||
# @sentry_sdk.trace
|
||||
async def stream(cls, cache: Conversation, inp: str):
|
||||
# VoE has to happen first. If there's user prediction history, derive and store fact(s)
|
||||
try:
|
||||
if cache.messages('user_prediction_thought_revision'):
|
||||
user_prediction_thought_revision = cache.messages('user_prediction_thought_revision')[-1].content
|
||||
if cache.get_messages("user_prediction_thought_revision"):
|
||||
user_prediction_thought_revision = cache.get_messages(
|
||||
"user_prediction_thought_revision"
|
||||
)[-1].content
|
||||
|
||||
voe_thought = await cls.think_violation_of_expectation(cache, inp, user_prediction_thought_revision)
|
||||
voe_facts = await cls.violation_of_expectation(cache, inp, user_prediction_thought_revision, voe_thought)
|
||||
voe_thought = await cls.think_violation_of_expectation(
|
||||
cache, inp, user_prediction_thought_revision
|
||||
)
|
||||
voe_facts = await cls.violation_of_expectation(
|
||||
cache, inp, user_prediction_thought_revision, voe_thought
|
||||
)
|
||||
|
||||
if not voe_facts or voe_facts[0] == "None":
|
||||
pass
|
||||
|
|
@ -288,17 +412,16 @@ class BloomChain:
|
|||
print("Finished Thought")
|
||||
print("=========================================")
|
||||
|
||||
|
||||
thought_revision_iterator = cls.revise_thought(cache, inp, thought)
|
||||
thought_revision = await thought_revision_iterator()
|
||||
|
||||
response_iterator = cls.respond(cache, thought_revision, inp)
|
||||
#response = ""
|
||||
# response = ""
|
||||
|
||||
async for item in response_iterator:
|
||||
# if "❀" in item:
|
||||
item = item.replace("❀", "🌸")
|
||||
#response += item
|
||||
# response += item
|
||||
yield item
|
||||
|
||||
print("=========================================")
|
||||
|
|
@ -306,7 +429,9 @@ class BloomChain:
|
|||
print("=========================================")
|
||||
|
||||
user_prediction_thought = await cls.think_user_prediction(cache, inp)
|
||||
user_prediction_thought_revision = await cls.revise_user_prediction_thought(cache, user_prediction_thought, inp)
|
||||
user_prediction_thought_revision = await cls.revise_user_prediction_thought(
|
||||
cache, user_prediction_thought, inp
|
||||
)
|
||||
|
||||
print("=========================================")
|
||||
print("Finished User Prediction")
|
||||
|
|
@ -315,8 +440,6 @@ class BloomChain:
|
|||
yield "❀"
|
||||
|
||||
|
||||
|
||||
|
||||
class Streamable:
|
||||
"A async iterator wrapper for langchain streams that saves on completion via callback"
|
||||
|
||||
|
|
@ -325,10 +448,10 @@ class Streamable:
|
|||
self.callback = callback
|
||||
# self.content: List[Awaitable[BaseMessage]] = []
|
||||
self.content = ""
|
||||
|
||||
|
||||
def __aiter__(self):
|
||||
return self
|
||||
|
||||
|
||||
async def __anext__(self):
|
||||
try:
|
||||
data = await self.iterator.__anext__()
|
||||
|
|
@ -339,12 +462,13 @@ class Streamable:
|
|||
raise StopAsyncIteration
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
async def __call__(self):
|
||||
async for _ in self:
|
||||
pass
|
||||
return self.content
|
||||
|
||||
|
||||
def unpack_messages(messages):
|
||||
unpacked = ""
|
||||
for message in messages:
|
||||
|
|
|
|||
48
api/main.py
48
api/main.py
|
|
@ -105,7 +105,7 @@ class Message(BaseModel):
|
|||
async def get_messages(user_id, session_id):
|
||||
"""Return messages associated with a session"""
|
||||
async with LOCK:
|
||||
data: str = MEDIATOR.get_messages(session_id, "response")
|
||||
data: str = MEDIATOR.get_messages(session_id)
|
||||
return JSONResponse(status_code=200, content=data)
|
||||
|
||||
|
||||
|
|
@ -117,18 +117,27 @@ async def add_message(user_id, session_id, inp: Message):
|
|||
return JSONResponse(status_code=200, content={"message": "OK"})
|
||||
|
||||
|
||||
class SessionInput(BaseModel):
|
||||
user_id: str
|
||||
session_id: str
|
||||
class ChatInput(BaseModel):
|
||||
message: str
|
||||
message_type: str
|
||||
|
||||
|
||||
class Session(BaseModel):
|
||||
session_id: str
|
||||
user_id: str
|
||||
location_id: Optional[str]
|
||||
metadata: Optional[Dict]
|
||||
@app.post("/users/{user_id}/sessions/{session_id}/chat")
|
||||
async def voe(user_id, session_id, inp: ChatInput):
|
||||
async with LOCK:
|
||||
session = Conversation(MEDIATOR, user_id, session_id)
|
||||
if session is None:
|
||||
raise HTTPException(status_code=404, detail="Item not found")
|
||||
thought, response = await BloomChain.chat(session, inp.message)
|
||||
return {"thought": thought, "response": response}
|
||||
|
||||
|
||||
# @app.post("/stream")
|
||||
# async def stream(inp: SessionInput):
|
||||
# async with LOCK:
|
||||
# session = Conversation(MEDIATOR, user_id=inp.user_id, session_id=inp.session_id)
|
||||
# if session is None:
|
||||
# raise HTTPException(status_code=404, detail="Item not found")
|
||||
# return StreamingResponse(BloomChain.stream(session, inp.message))
|
||||
|
||||
|
||||
### API Fundamentals
|
||||
|
|
@ -140,22 +149,3 @@ class Session(BaseModel):
|
|||
## Honcho Utilities
|
||||
# @app.get('/theoryofmind')
|
||||
# @app.get('/voe')
|
||||
|
||||
|
||||
@app.post("/chat")
|
||||
async def voe(inp: SessionInput):
|
||||
async with LOCK:
|
||||
session = Conversation(MEDIATOR, user_id=inp.user_id, session_id=inp.session_id)
|
||||
if session is None:
|
||||
raise HTTPException(status_code=404, detail="Item not found")
|
||||
thought, response = await BloomChain.chat(session, inp.message)
|
||||
return {"thought": thought, "response": response}
|
||||
|
||||
|
||||
@app.post("/stream")
|
||||
async def stream(inp: SessionInput):
|
||||
async with LOCK:
|
||||
session = Conversation(MEDIATOR, user_id=inp.user_id, session_id=inp.session_id)
|
||||
if session is None:
|
||||
raise HTTPException(status_code=404, detail="Item not found")
|
||||
return StreamingResponse(BloomChain.stream(session, inp.message))
|
||||
|
|
|
|||
|
|
@ -134,17 +134,19 @@ class SupabaseMediator(Mediator):
|
|||
# Session Usage Read & Write
|
||||
|
||||
@sentry_sdk.trace
|
||||
def get_messages(self, session_id: str) -> List[BaseMessage]: # type: ignore
|
||||
def get_messages(self, session_id: str, type: str | None = None) -> List[BaseMessage]: # type: ignore
|
||||
response = (
|
||||
self.supabase.table(self.memory_table)
|
||||
.select("*")
|
||||
.eq("session_id", session_id)
|
||||
.order("created_at", desc=True)
|
||||
)
|
||||
response = (
|
||||
(response if type is None else response.eq("type", type))
|
||||
.order("created_at")
|
||||
.execute()
|
||||
)
|
||||
items = [record["message"] for record in response.data]
|
||||
messages = messages_from_dict(items)
|
||||
return messages[::-1]
|
||||
print(response)
|
||||
return response.data
|
||||
|
||||
@sentry_sdk.trace
|
||||
def add_message(self, session_id: str, message_type: str, content: str) -> None:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
from sqlalchemy.orm import Session
|
||||
|
||||
from . import models, schemas
|
||||
|
||||
import json
|
||||
|
||||
|
||||
def get_session(db: Session, session_id: int):
|
||||
return db.query(models.Session).filter(models.Session.id == session_id).first()
|
||||
|
||||
|
||||
def get_sessions(db: Session, user_id: str, location_id: str | None = None):
|
||||
filtered_by_user = db.query(models.Session).filter(
|
||||
models.Session.user_id == user_id
|
||||
)
|
||||
filtered_by_location = (
|
||||
filtered_by_user.filter(models.Session.location_id == location_id)
|
||||
if location_id is not None
|
||||
else filtered_by_user
|
||||
)
|
||||
return (
|
||||
filtered_by_location.filter(models.Session.is_active == True)
|
||||
.order_by(models.Session.created_at.desc())
|
||||
.all()
|
||||
)
|
||||
|
||||
|
||||
def create_session(db: Session, user_id: str, session: schemas.SessionCreate):
|
||||
db_session = models.Session(
|
||||
user_id=user_id,
|
||||
location_id=session.location_id,
|
||||
session_data=json.dumps(session.session_data),
|
||||
)
|
||||
db.add(db_session)
|
||||
db.commit()
|
||||
db.refresh(db_session)
|
||||
return db_session
|
||||
|
||||
|
||||
def create_message(db: Session, message: schemas.MessageCreate, session_id: int):
|
||||
db_message = models.Message(
|
||||
session_id=session_id,
|
||||
message_type=message.message_type,
|
||||
content=message.content,
|
||||
)
|
||||
db.add(db_message)
|
||||
db.commit()
|
||||
db.refresh(db_message)
|
||||
return db_message
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
engine = create_engine(
|
||||
"sqlite:///./sql_db.sqlite", connect_args={"check_same_thread": False}, echo=True
|
||||
)
|
||||
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
Base = declarative_base()
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from . import crud, models, schemas
|
||||
from .db import SessionLocal, engine
|
||||
|
||||
models.Base.metadata.create_all(bind=engine)
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@app.get("/users/{user_id}/sessions", response_model=list[schemas.Session])
|
||||
def get_sessions(user_id: str, db: Session = Depends(get_db)):
|
||||
return crud.get_sessions(db, user_id)
|
||||
|
||||
|
||||
@app.post("/users/{user_id}/sessions", response_model=schemas.Session)
|
||||
def create_session(
|
||||
user_id: str, session: schemas.SessionCreate, db: Session = Depends(get_db)
|
||||
):
|
||||
return crud.create_session(db, user_id, session)
|
||||
|
||||
|
||||
@app.get("/users/{user_id}/sessions/{session_id}", response_model=schemas.Session)
|
||||
def get_session(user_id: str, session_id: int, db: Session = Depends(get_db)):
|
||||
db_session = crud.get_session(db, session_id)
|
||||
if db_session is None:
|
||||
raise HTTPException(status_code=404, detail="Session not found")
|
||||
return db_session
|
||||
|
||||
|
||||
@app.post(
|
||||
"/users/{user_id}/sessions/{session_id}/messages/", response_model=schemas.Message
|
||||
)
|
||||
def create_message_for_session(
|
||||
user_id: str,
|
||||
session_id: int,
|
||||
message: schemas.MessageCreate,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
return crud.create_message(db, message, session_id)
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, DateTime
|
||||
import datetime
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from .db import Base
|
||||
|
||||
|
||||
class Session(Base):
|
||||
__tablename__ = "sessions"
|
||||
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||
user_id = Column(String, index=True)
|
||||
location_id = Column(String, index=True)
|
||||
is_active = Column(Boolean, default=True)
|
||||
session_data = Column(String)
|
||||
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
|
||||
messages = relationship("Message", back_populates="session")
|
||||
|
||||
|
||||
class Message(Base):
|
||||
__tablename__ = "messages"
|
||||
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||
session_id = Column(Integer, ForeignKey("sessions.id"))
|
||||
message_type = Column(String, index=True)
|
||||
content = Column(String)
|
||||
|
||||
session = relationship("Session", back_populates="messages")
|
||||
|
||||
|
||||
# TODO: add metacognitive data to messages
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class MessageBase(BaseModel):
|
||||
content: str
|
||||
message_type: str
|
||||
|
||||
|
||||
class MessageCreate(MessageBase):
|
||||
pass
|
||||
|
||||
|
||||
class Message(MessageBase):
|
||||
session_id: int
|
||||
id: int
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class SessionBase(BaseModel):
|
||||
pass
|
||||
|
||||
|
||||
class SessionCreate(SessionBase):
|
||||
location_id: str
|
||||
session_data: dict | None = None
|
||||
|
||||
|
||||
class Session(SessionBase):
|
||||
id: int
|
||||
messages: list[Message]
|
||||
is_active: bool
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,18 @@
|
|||
[tool.poetry]
|
||||
name = "api2"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["hyusap <paulayush@gmail.com>"]
|
||||
readme = "README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.11"
|
||||
fastapi = "^0.105.0"
|
||||
uvicorn = "^0.24.0.post1"
|
||||
langchain = "^0.0.350"
|
||||
openai = "^1.3.9"
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
Binary file not shown.
Loading…
Reference in New Issue