start api2

This commit is contained in:
hyusap 2023-12-13 22:47:00 -05:00
parent eeac17527f
commit 31dc6d1646
13 changed files with 1899 additions and 179 deletions

View File

@ -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}
)

View File

@ -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:

View File

@ -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))

View File

@ -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
api2/api/__init__.py Normal file
View File

49
api2/api/crud.py Normal file
View File

@ -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

10
api2/api/db.py Normal file
View File

@ -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()

49
api2/api/main.py Normal file
View File

@ -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)

30
api2/api/models.py Normal file
View File

@ -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

36
api2/api/schemas.py Normal file
View File

@ -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

1393
api2/poetry.lock generated Normal file

File diff suppressed because it is too large Load Diff

18
api2/pyproject.toml Normal file
View File

@ -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"

BIN
api2/sql_db.sqlite Normal file

Binary file not shown.