From 010778ff0f76320b73e9ebbaaedc725aa35d21fe Mon Sep 17 00:00:00 2001 From: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com> Date: Mon, 18 Sep 2023 23:33:37 -0400 Subject: [PATCH] Sentry Support --- .env.template | 5 +++++ cache.py | 6 ++++++ chain.py | 11 +++++++++++ main.py | 9 +++++++++ mediator.py | 9 ++++++++- poetry.lock | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 7 files changed, 88 insertions(+), 2 deletions(-) diff --git a/.env.template b/.env.template index a286e07..06422a6 100644 --- a/.env.template +++ b/.env.template @@ -13,3 +13,8 @@ OPENAI_API_BASE= OPENAI_API_VERSION= OPENAI_API_DEPLOYMENT_NAME= +# Sentry +SENTRY_DSN= +SENTRY_ENVIRONMENT= +SENTRY_RELEASE= + diff --git a/cache.py b/cache.py index 1b60e9a..fb48f24 100644 --- a/cache.py +++ b/cache.py @@ -8,26 +8,32 @@ from typing import List from langchain.schema import BaseMessage, Document from pydantic import BaseModel +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, conversation_id: str = str(uuid.uuid4()), location_id: str = "web"): self.mediator: SupabaseMediator = mediator self.user_id: str = user_id self.conversation_id: str = conversation_id self.location_id: str = location_id + @sentry_sdk.trace def add_message(self, message_type: str, message: BaseMessage,) -> None: self.mediator.add_message(self.conversation_id, self.user_id, message_type, message) + @sentry_sdk.trace def messages(self, message_type: str) -> List[BaseMessage]: return self.mediator.messages(self.conversation_id, self.user_id, message_type) # vector DB fn + @sentry_sdk.trace def add_texts(self, texts: List[str]) -> None: metadatas = [{"conversation_id": self.conversation_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 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}) diff --git a/chain.py b/chain.py index 375dd20..d406584 100644 --- a/chain.py +++ b/chain.py @@ -7,6 +7,7 @@ from collections.abc import AsyncIterator from cache import Conversation from typing import List +import sentry_sdk load_dotenv() @@ -40,6 +41,7 @@ def __init__(self) -> None: pass @classmethod + @sentry_sdk.trace def think(cls, cache: Conversation, input: str): """Generate Bloom's thought on the user.""" # load message history @@ -58,6 +60,7 @@ def think(cls, cache: Conversation, input: str): ) @classmethod + @sentry_sdk.trace def revise_thought(cls, cache: Conversation, input: str, thought: str): """Revise Bloom's thought about the user with retrieved personal data""" @@ -80,6 +83,7 @@ def revise_thought(cls, cache: Conversation, input: str, thought: str): ) @classmethod + @sentry_sdk.trace def respond(cls, cache: Conversation, thought: str, input: str): """Generate Bloom's response to the user.""" response_prompt = ChatPromptTemplate.from_messages([ @@ -97,6 +101,7 @@ def respond(cls, cache: Conversation, thought: str, input: str): ) @classmethod + @sentry_sdk.trace async def think_user_prediction(cls, cache: Conversation, input: str): """Generate a thought about what the user is going to say""" @@ -117,6 +122,7 @@ async def think_user_prediction(cls, cache: Conversation, input: str): return user_prediction_thought.content @classmethod + @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""" @@ -142,6 +148,7 @@ async def revise_user_prediction_thought(cls, cache: Conversation, user_predicti @classmethod + @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""" @@ -161,6 +168,7 @@ async def think_violation_of_expectation(cls, cache: Conversation, inp: str, use 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: """Assess whether expectation was violated, derive and store facts""" @@ -185,6 +193,7 @@ async def violation_of_expectation(cls, cache: Conversation, inp: str, user_pred return facts @classmethod + @sentry_sdk.trace async def check_voe_list(cls, cache: Conversation, facts: List[str]): """Filter the facts to just new ones""" @@ -218,6 +227,7 @@ async def check_voe_list(cls, cache: Conversation, facts: List[str]): cache.add_texts(data) @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'): @@ -246,6 +256,7 @@ async def chat(cls, cache: Conversation, inp: str ) -> tuple[str, str]: return thought, response @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: diff --git a/main.py b/main.py index 29dc75e..f55348d 100644 --- a/main.py +++ b/main.py @@ -8,9 +8,18 @@ from mediator import SupabaseMediator from cache import Conversation +import os from dotenv import load_dotenv +import sentry_sdk load_dotenv() +rate = 0.2 if os.getenv("SENTRY_ENVIRONMENT") == "production" else 1.0 +sentry_sdk.init( + dsn=os.environ['SENTRY_DSN'], + traces_sample_rate=rate, + profiles_sample_rate=rate +) + app = FastAPI() MEDIATOR = SupabaseMediator() diff --git a/mediator.py b/mediator.py index 1ff8ff6..780a141 100644 --- a/mediator.py +++ b/mediator.py @@ -5,7 +5,7 @@ from langchain.embeddings.base import Embeddings from langchain.embeddings.openai import OpenAIEmbeddings import uuid -import urllib +import sentry_sdk import os from dotenv import load_dotenv # Supabase for Postgres Management @@ -16,6 +16,8 @@ load_dotenv() class SupabaseMediator: + + @sentry_sdk.trace def __init__(self): self.supabase: Client = create_client(os.environ['SUPABASE_URL'], os.environ['SUPABASE_KEY']) self.memory_table = os.environ["MEMORY_TABLE"] @@ -42,15 +44,18 @@ def __init__(self): # self.vector_table.add_documents(seed_docs) + @sentry_sdk.trace def messages(self, session_id: str, user_id: str, message_type: str) -> List[BaseMessage]: # type: ignore response = self.supabase.table(self.memory_table).select("message").eq("session_id", session_id).eq("user_id", user_id).eq("message_type", message_type).order("id", desc=True).limit(10).execute() items = [record["message"] for record in response.data] messages = messages_from_dict(items) return messages[::-1] + @sentry_sdk.trace def add_message(self, session_id: str, user_id: str, message_type: str, message: BaseMessage) -> None: self.supabase.table(self.memory_table).insert({"session_id": session_id, "user_id": user_id, "message_type": message_type, "message": _message_to_dict(message)}).execute() + @sentry_sdk.trace def conversations(self, location_id: str, user_id: str) -> str | None: response = self.supabase.table(self.conversation_table).select("id").eq("location_id", location_id).eq("user_id", user_id).eq("isActive", True).maybe_single().execute() if response: @@ -58,11 +63,13 @@ def conversations(self, location_id: str, user_id: str) -> str | None: return conversation_id return None + @sentry_sdk.trace def add_conversation(self, location_id: str, user_id: str) -> str: conversation_id = str(uuid.uuid4()) self.supabase.table(self.conversation_table).insert({"id": conversation_id, "user_id": user_id, "location_id": location_id}).execute() return conversation_id + @sentry_sdk.trace def delete_conversation(self, conversation_id: str) -> None: self.supabase.table(self.conversation_table).update({"isActive": False}).eq("id", conversation_id).execute() diff --git a/poetry.lock b/poetry.lock index 2493b4f..5aa1054 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1289,6 +1289,53 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "sentry-sdk" +version = "1.31.0" +description = "Python client for Sentry (https://sentry.io)" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.31.0.tar.gz", hash = "sha256:6de2e88304873484207fed836388e422aeff000609b104c802749fd89d56ba5b"}, + {file = "sentry_sdk-1.31.0-py2.py3-none-any.whl", hash = "sha256:64a7141005fb775b9db298a30de93e3b83e0ddd1232dc6f36eb38aebc1553291"}, +] + +[package.dependencies] +certifi = "*" +fastapi = {version = ">=0.79.0", optional = true, markers = "extra == \"fastapi\""} +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +grpcio = ["grpcio (>=1.21.1)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +loguru = ["loguru (>=0.5)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + [[package]] name = "six" version = "1.16.0" @@ -1798,4 +1845,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "8ccc4a9baa19404ab5a5c62d06ad07b760f17df6a7d6bc045f34bc38a45ab2bc" +content-hash = "79b717a6de797aab0ceb534b8ecf0b99ad17c2687ed0a2c8d6d97766ddf6d0e5" diff --git a/pyproject.toml b/pyproject.toml index 53b5622..8ab8206 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ openai = "^0.28.0" pydantic = "^2.3.0" python-dotenv = "^1.0.0" uvicorn = "^0.23.2" +sentry-sdk = {extras = ["fastapi"], version = "^1.31.0"} [build-system]