From 05060257608b458860dd467f11fbb1689d533dc3 Mon Sep 17 00:00:00 2001 From: Milutin-S Date: Sat, 1 Jun 2024 23:29:18 +0200 Subject: [PATCH 1/6] Update app, add sidebar, relovace methods to utils --- app.py | 122 ++++++++++++++++----------------------------------------- 1 file changed, 34 insertions(+), 88 deletions(-) diff --git a/app.py b/app.py index 236a642..4fb7162 100644 --- a/app.py +++ b/app.py @@ -1,100 +1,41 @@ -import json -import os -from pathlib import Path - import streamlit as st -import yaml from dotenv import find_dotenv, load_dotenv -from langfuse.decorators import observe -from loguru import logger -from openai import OpenAI -from qdrant_client import QdrantClient - -from database.utils import embed_text, get_context, search -from llm.prompts import DEFAULT_CONTEXT, INTRODUCTION_MESSAGE -from llm.utils import get_answer, get_messages -from router.query_router import semantic_query_router -from router.router_prompt import DEFAULT_ROUTER_RESPONSE, ROUTER_PROMPT - -load_dotenv(find_dotenv()) -st.title("Legal ChatBot") - -qdrant_client = QdrantClient( - url=os.environ["QDRANT_CLUSTER_URL"], - api_key=os.environ["QDRANT_API_KEY"], +from llm.prompts import INTRODUCTION_MESSAGE +from utils import ( + generate_response, + init_clients, + load_config_from_yaml, + WARNING_MESSAGE, + QUERY_SUGGESTIONS, + AUTHORS, + LOGO_URL, ) -openai_client = OpenAI(api_key=os.environ["OPENAI_API_KEY"]) - -config_path = Path("./config.yaml") -centroid_path = Path("./router/collection_centroids.json") - -with config_path.open("r") as file: - config = yaml.safe_load(file) - -# Delete this -with open(centroid_path, "r", encoding="utf-8") as file: - centroids = json.loads(file.read()) - - -@observe() -def response_generator(query: str): - st.session_state.messages = st.session_state.messages[ - -1 * config["openai"]["gpt_model"]["max_conversation"] : - ] - - # st.session_state.messages.append({"role": "user", "content": query}) - - embedding_response = embed_text( - client=openai_client, - text=query, - model=config["openai"]["embedding_model"]["name"], - ) - embedding = embedding_response.data[0].embedding +# Load environment variables. +load_dotenv(find_dotenv()) - # Rout query - collections = semantic_query_router( - client=openai_client, - model=config["openai"]["gpt_model"]["router"], - query=query, - prompt=ROUTER_PROMPT, - temperature=config["openai"]["gpt_model"]["temperature"], - ) - logger.info(f"Query routed to collections: {collections}") - if collections[0] == DEFAULT_ROUTER_RESPONSE: - context = DEFAULT_CONTEXT - else: - search_results = [] - for collection_name in collections: - search_results.extend( - search( - client=qdrant_client, - collection=collection_name, - query_vector=embedding, - limit=10, - with_vectors=True, - ) - ) +# Configure Streamlit page +st.set_page_config(page_title="Your Lawyer Assistant", page_icon=LOGO_URL) - top_k = 15 if len(collections) > 1 else 10 - context = get_context(search_results=search_results, top_k=top_k) +st.title("LegaBot") +st.divider() - stream = get_answer( - client=openai_client, - model=config["openai"]["gpt_model"]["llm"], - temperature=config["openai"]["gpt_model"]["temperature"], - messages=get_messages( - context=context, query=query, conversation=st.session_state.messages - ), - stream=True, - ) +st.logo(LOGO_URL, icon_image=LOGO_URL) +with st.sidebar: + st.subheader("💡 Query Suggestions") + with st.container(border=True, height=200): + st.markdown(QUERY_SUGGESTIONS) + st.subheader("⚠️ Warning") + with st.container(border=True): + st.markdown(WARNING_MESSAGE) + st.subheader("✍️ Authors") + with st.container(border=True): + st.markdown(AUTHORS) - for chunk in stream: - part = chunk.choices[0].delta.content - if part is not None: - yield part +openai_client, qdrant_client = init_clients() +config = load_config_from_yaml() if "messages" not in st.session_state: @@ -113,7 +54,12 @@ def response_generator(query: str): st.markdown(prompt) with st.chat_message("assistant"): - stream = response_generator(prompt) + stream = generate_response( + query=prompt, + openai_client=openai_client, + qdrant_client=qdrant_client, + config=config, + ) response = st.write_stream(stream) st.session_state.messages.append({"role": "assistant", "content": response}) From c2e4396dd4b740cd06f630830e2073fae7bf08cb Mon Sep 17 00:00:00 2001 From: Milutin-S Date: Sat, 1 Jun 2024 23:29:59 +0200 Subject: [PATCH 2/6] Update poetry files. --- poetry.lock | 10 +++++----- pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index eb8c882..52d2a72 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3383,13 +3383,13 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "streamlit" -version = "1.32.2" +version = "1.35.0" description = "A faster way to build and share data apps" optional = false -python-versions = ">=3.8, !=3.9.7" +python-versions = "!=3.9.7,>=3.8" files = [ - {file = "streamlit-1.32.2-py2.py3-none-any.whl", hash = "sha256:a0b8044e76fec364b07be145f8b40dbd8d083e20ebbb189ceb1fa9423f3dedea"}, - {file = "streamlit-1.32.2.tar.gz", hash = "sha256:1258b9cbc3ff957bf7d09b1bfc85cedc308f1065b30748545295a9af8d5577ab"}, + {file = "streamlit-1.35.0-py2.py3-none-any.whl", hash = "sha256:e17d1d86830a0d7687c37faf2fe47bffa752d0c95a306e96d7749bd3faa72a5b"}, + {file = "streamlit-1.35.0.tar.gz", hash = "sha256:679d55bb6189743f606abf0696623df0bfd223a6d0c8d96b8d60678d4891d2d6"}, ] [package.dependencies] @@ -3399,7 +3399,7 @@ cachetools = ">=4.0,<6" click = ">=7.0,<9" gitpython = ">=3.0.7,<3.1.19 || >3.1.19,<4" numpy = ">=1.19.3,<2" -packaging = ">=16.8,<24" +packaging = ">=16.8,<25" pandas = ">=1.3.0,<3" pillow = ">=7.1.0,<11" protobuf = ">=3.20,<5" diff --git a/pyproject.toml b/pyproject.toml index b836b1d..91c8a47 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.poetry] -name = "sais-hackathon" +name = "LegaBot" version = "0.1.0" description = "" authors = ["Milutin-S "] From 0736a561a1c4f563c68a941ce4db49b47d859d2a Mon Sep 17 00:00:00 2001 From: Milutin-S Date: Sat, 1 Jun 2024 23:30:21 +0200 Subject: [PATCH 3/6] Add const variable and move methods from app. --- utils.py | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/utils.py b/utils.py index 6ea4683..2d0eec3 100644 --- a/utils.py +++ b/utils.py @@ -1,5 +1,37 @@ +import os +from typing import Generator, List, Tuple + +import streamlit as st import yaml +from langfuse.decorators import observe +from loguru import logger +from openai import OpenAI from pydantic import BaseModel +from qdrant_client import QdrantClient + +from database.utils import embed_text, get_context, search +from llm.prompts import DEFAULT_CONTEXT +from llm.utils import get_answer, get_messages +from router.query_router import semantic_query_router +from router.router_prompt import DEFAULT_ROUTER_RESPONSE, ROUTER_PROMPT + +LOGO_URL = "https://www.app.nl/wp-content/uploads/2019/01/Blendle.png" + +WARNING_MESSAGE = """ +_Please note that LegaBot may make **mistakes**. For critical legal information, always **verify** with a qualified legal professional. LegaBot is here to assist, not replace professional legal advice._ +""" + +QUERY_SUGGESTIONS = """ +Na koliko dana godisnjeg imam pravo?\n +Da li smem da koristim porodiljsko bolovanje zene umesto nje?\n +Koliko poreza treba da platim ako sam freelancer?\n +Da li mogu da trazim da se izbrisu moji podaci sa sajta ako ih nisam odborio? +""" + +AUTHORS = """ +**Attorney:** [Anja Berić](https://www.linkedin.com/in/anja-beric-150285vb/?originalSubdomain=rs)\n +**Research Engineer:** [Milutin Studen](https://www.linkedin.com/in/milutin-studen/) +""" class RouterConfig(BaseModel): @@ -28,7 +60,127 @@ class Config(BaseModel): openai: OpenAIConfig -def load_config_from_yaml(yaml_file_path: str) -> Config: +def load_config_from_yaml(yaml_file_path: str = "./config.yaml") -> Config: with open(yaml_file_path, "r") as file: yaml_content = yaml.safe_load(file) return Config(**yaml_content) + + +@st.cache_resource +def init_clients() -> Tuple[OpenAI, QdrantClient]: + """ + Initializes and returns the clients for OpenAI and Qdrant services. + + Returns: + - Tuple[OpenAI, QdrantClient]: A tuple containing the initialized OpenAI and Qdrant clients. + + Raises: + - EnvironmentError: If required environment variables are missing. + """ + try: + # Retrieve Qdrant client configuration from environment variables + qdrant_url = os.environ["QDRANT_CLUSTER_URL"] + qdrant_api_key = os.environ["QDRANT_API_KEY"] + qdrant_client = QdrantClient(url=qdrant_url, api_key=qdrant_api_key) + + # Retrieve OpenAI client configuration from environment variables + openai_api_key = os.environ["OPENAI_API_KEY"] + openai_client = OpenAI(api_key=openai_api_key) + + return openai_client, qdrant_client + except KeyError as e: + error_msg = f"Missing environment variable: {str(e)}" + logger.error(error_msg) + raise EnvironmentError(error_msg) + + +@observe() +def generate_response( + query: str, openai_client: OpenAI, qdrant_client: QdrantClient, config: Config +) -> Generator[str, None, None]: + """ + Generates a response for a given user query using a combination of semantic search and a chat model. + + Args: + - query (str): The user's query string. + - openai_client (OpenAI): Client to interact with OpenAI's API. + - qdrant_client (QdrantClient): Client to interact with Qdrant's API. + - config (Config): Configuration settings for API interaction and response handling. + + Yields: + - str: Parts of the generated response from the chat model. + """ + try: + # Limit the stored messages to the maximum conversation length defined in the configuration + st.session_state.messages = st.session_state.messages[ + -config.openai.chat.max_conversation : + ] + + # Embed the user query using the specified model in the configuration + embedding_response = embed_text( + client=openai_client, + text=query, + model=config.openai.embeddings.model, + ) + embedding = embedding_response.data[0].embedding + + # Determine the relevant collections to route the query to + collections = semantic_query_router( + client=openai_client, + model=config.openai.router.model, + query=query, + prompt=ROUTER_PROMPT, + temperature=config.openai.router.temperature, + ) + logger.info(f"Query routed to collections: {collections}") + + # Determine the context for the chat model based on the routed collections + context = determine_context(collections, embedding, qdrant_client) + + # Generate the response stream from the chat model + stream = get_answer( + client=openai_client, + model=config.openai.chat.model, + temperature=config.openai.chat.temperature, + messages=get_messages( + context=context, query=query, conversation=st.session_state.messages + ), + stream=True, + ) + + # Yield each part of the response as it becomes available + for chunk in stream: + part = chunk.choices[0].delta.content + if part is not None: + yield part + + except Exception as e: + logger.error(f"An error occurred while generating the response: {str(e)}") + yield "Sorry, an error occurred while processing your request." + + +def determine_context( + collections: List[str], embedding: List[float], qdrant_client: QdrantClient +) -> str: + """Determines the context for generating responses based on search results from collections.""" + try: + if collections[0] == DEFAULT_ROUTER_RESPONSE: + return DEFAULT_CONTEXT + else: + search_results = [] + for collection_name in collections: + search_results.extend( + search( + client=qdrant_client, + collection=collection_name, + query_vector=embedding, + limit=10, + with_vectors=True, + ) + ) + # Upgrade this with tokes length checking + top_k = 15 if len(collections) > 1 else 10 + return get_context(search_results=search_results, top_k=top_k) + except Exception as e: + logger.error(f"Error determining context: {str(e)}") + return DEFAULT_CONTEXT # Fallback to default context From 660976803a42f2a7a8552aaf2958b0535d33572a Mon Sep 17 00:00:00 2001 From: Milutin-S Date: Sat, 1 Jun 2024 23:30:52 +0200 Subject: [PATCH 4/6] Made minimal requirements for deployment. --- requirements.txt | 166 +++-------------------------------------------- 1 file changed, 9 insertions(+), 157 deletions(-) diff --git a/requirements.txt b/requirements.txt index 69a4680..fa6ef0b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,157 +1,9 @@ -aiohttp==3.9.3 ; python_version >= "3.10" and python_version < "4.0" -aiosignal==1.3.1 ; python_version >= "3.10" and python_version < "4.0" -altair==5.3.0 ; python_version >= "3.10" and python_version < "4.0" -annotated-types==0.6.0 ; python_version >= "3.10" and python_version < "4.0" -anyio==4.3.0 ; python_version >= "3.10" and python_version < "4.0" -appnope==0.1.4 ; python_version >= "3.10" and python_version < "4.0" and platform_system == "Darwin" -argon2-cffi-bindings==21.2.0 ; python_version >= "3.10" and python_version < "4.0" -argon2-cffi==23.1.0 ; python_version >= "3.10" and python_version < "4.0" -arrow==1.3.0 ; python_version >= "3.10" and python_version < "4.0" -asttokens==2.4.1 ; python_version >= "3.10" and python_version < "4.0" -async-lru==2.0.4 ; python_version >= "3.10" and python_version < "4.0" -async-timeout==4.0.3 ; python_version >= "3.10" and python_version < "3.11" -attrs==23.2.0 ; python_version >= "3.10" and python_version < "4.0" -babel==2.14.0 ; python_version >= "3.10" and python_version < "4.0" -backoff==2.2.1 ; python_version >= "3.10" and python_version < "4.0" -beautifulsoup4==4.12.3 ; python_version >= "3.10" and python_version < "4.0" -bleach==6.1.0 ; python_version >= "3.10" and python_version < "4.0" -blinker==1.7.0 ; python_version >= "3.10" and python_version < "4.0" -cachetools==5.3.3 ; python_version >= "3.10" and python_version < "4.0" -certifi==2024.2.2 ; python_version >= "3.10" and python_version < "4.0" -cffi==1.16.0 ; python_version >= "3.10" and python_version < "4.0" -charset-normalizer==3.3.2 ; python_version >= "3.10" and python_version < "4.0" -click==8.1.7 ; python_version >= "3.10" and python_version < "4.0" -colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0" and (platform_system == "Windows" or sys_platform == "win32") -comm==0.2.2 ; python_version >= "3.10" and python_version < "4.0" -debugpy==1.8.1 ; python_version >= "3.10" and python_version < "4.0" -decorator==5.1.1 ; python_version >= "3.10" and python_version < "4.0" -defusedxml==0.7.1 ; python_version >= "3.10" and python_version < "4.0" -distro==1.9.0 ; python_version >= "3.10" and python_version < "4.0" -exceptiongroup==1.2.0 ; python_version >= "3.10" and python_version < "3.11" -executing==2.0.1 ; python_version >= "3.10" and python_version < "4.0" -fastjsonschema==2.19.1 ; python_version >= "3.10" and python_version < "4.0" -fqdn==1.5.1 ; python_version >= "3.10" and python_version < "4" -frozenlist==1.4.1 ; python_version >= "3.10" and python_version < "4.0" -gitdb==4.0.11 ; python_version >= "3.10" and python_version < "4.0" -gitpython==3.1.43 ; python_version >= "3.10" and python_version < "4.0" -grpcio-tools==1.62.1 ; python_version >= "3.10" and python_version < "4.0" -grpcio==1.62.1 ; python_version >= "3.10" and python_version < "4.0" -h11==0.14.0 ; python_version >= "3.10" and python_version < "4.0" -h2==4.1.0 ; python_version >= "3.10" and python_version < "4.0" -hpack==4.0.0 ; python_version >= "3.10" and python_version < "4.0" -httpcore==1.0.4 ; python_version >= "3.10" and python_version < "4.0" -httpx==0.27.0 ; python_version >= "3.10" and python_version < "4.0" -httpx[http2]==0.27.0 ; python_version >= "3.10" and python_version < "4.0" -hyperframe==6.0.1 ; python_version >= "3.10" and python_version < "4.0" -idna==3.7 ; python_version >= "3.10" and python_version < "4.0" -ipykernel==6.29.3 ; python_version >= "3.10" and python_version < "4.0" -ipython==8.22.2 ; python_version >= "3.10" and python_version < "4.0" -ipywidgets==8.1.2 ; python_version >= "3.10" and python_version < "4.0" -isoduration==20.11.0 ; python_version >= "3.10" and python_version < "4.0" -jedi==0.19.1 ; python_version >= "3.10" and python_version < "4.0" -jinja2==3.1.3 ; python_version >= "3.10" and python_version < "4.0" -json5==0.9.24 ; python_version >= "3.10" and python_version < "4.0" -jsonpointer==2.4 ; python_version >= "3.10" and python_version < "4.0" -jsonschema-specifications==2023.12.1 ; python_version >= "3.10" and python_version < "4.0" -jsonschema==4.21.1 ; python_version >= "3.10" and python_version < "4.0" -jsonschema[format-nongpl]==4.21.1 ; python_version >= "3.10" and python_version < "4.0" -jupyter-client==8.6.1 ; python_version >= "3.10" and python_version < "4.0" -jupyter-console==6.6.3 ; python_version >= "3.10" and python_version < "4.0" -jupyter-core==5.7.2 ; python_version >= "3.10" and python_version < "4.0" -jupyter-events==0.10.0 ; python_version >= "3.10" and python_version < "4.0" -jupyter-lsp==2.2.4 ; python_version >= "3.10" and python_version < "4.0" -jupyter-server-terminals==0.5.3 ; python_version >= "3.10" and python_version < "4.0" -jupyter-server==2.13.0 ; python_version >= "3.10" and python_version < "4.0" -jupyter==1.0.0 ; python_version >= "3.10" and python_version < "4.0" -jupyterlab-pygments==0.3.0 ; python_version >= "3.10" and python_version < "4.0" -jupyterlab-server==2.25.4 ; python_version >= "3.10" and python_version < "4.0" -jupyterlab-widgets==3.0.10 ; python_version >= "3.10" and python_version < "4.0" -jupyterlab==4.1.5 ; python_version >= "3.10" and python_version < "4.0" -langfuse==2.27.2 ; python_version >= "3.10" and python_version < "4.0" -loguru==0.7.2 ; python_version >= "3.10" and python_version < "4.0" -lxml==5.1.0 ; python_version >= "3.10" and python_version < "4.0" -markdown-it-py==3.0.0 ; python_version >= "3.10" and python_version < "4.0" -markupsafe==2.1.5 ; python_version >= "3.10" and python_version < "4.0" -matplotlib-inline==0.1.6 ; python_version >= "3.10" and python_version < "4.0" -mdurl==0.1.2 ; python_version >= "3.10" and python_version < "4.0" -mistune==3.0.2 ; python_version >= "3.10" and python_version < "4.0" -multidict==6.0.5 ; python_version >= "3.10" and python_version < "4.0" -nbclient==0.10.0 ; python_version >= "3.10" and python_version < "4.0" -nbconvert==7.16.2 ; python_version >= "3.10" and python_version < "4.0" -nbformat==5.10.3 ; python_version >= "3.10" and python_version < "4.0" -nest-asyncio==1.6.0 ; python_version >= "3.10" and python_version < "4.0" -notebook-shim==0.2.4 ; python_version >= "3.10" and python_version < "4.0" -notebook==7.1.2 ; python_version >= "3.10" and python_version < "4.0" -numpy==1.26.4 ; python_version >= "3.10" and python_version < "4.0" -openai==1.14.2 ; python_version >= "3.10" and python_version < "4.0" -overrides==7.7.0 ; python_version >= "3.10" and python_version < "4.0" -packaging==23.2 ; python_version >= "3.10" and python_version < "4.0" -pandas==2.2.0 ; python_version >= "3.10" and python_version < "4.0" -pandocfilters==1.5.1 ; python_version >= "3.10" and python_version < "4.0" -parso==0.8.3 ; python_version >= "3.10" and python_version < "4.0" -pexpect==4.9.0 ; python_version >= "3.10" and python_version < "4.0" and (sys_platform != "win32" and sys_platform != "emscripten") -pillow==10.2.0 ; python_version >= "3.10" and python_version < "4.0" -platformdirs==4.2.0 ; python_version >= "3.10" and python_version < "4.0" -portalocker==2.8.2 ; python_version >= "3.10" and python_version < "4.0" -prometheus-client==0.20.0 ; python_version >= "3.10" and python_version < "4.0" -prompt-toolkit==3.0.43 ; python_version >= "3.10" and python_version < "4.0" -protobuf==4.25.3 ; python_version >= "3.10" and python_version < "4.0" -psutil==5.9.8 ; python_version >= "3.10" and python_version < "4.0" -ptyprocess==0.7.0 ; python_version >= "3.10" and python_version < "4.0" and (sys_platform != "win32" and sys_platform != "emscripten" or os_name != "nt") -pure-eval==0.2.2 ; python_version >= "3.10" and python_version < "4.0" -pyarrow==15.0.2 ; python_version >= "3.10" and python_version < "4.0" -pycparser==2.21 ; python_version >= "3.10" and python_version < "4.0" -pydantic-core==2.16.3 ; python_version >= "3.10" and python_version < "4.0" -pydantic==2.6.4 ; python_version >= "3.10" and python_version < "4.0" -pydeck==0.8.0 ; python_version >= "3.10" and python_version < "4.0" -pygments==2.17.2 ; python_version >= "3.10" and python_version < "4.0" -python-dateutil==2.9.0.post0 ; python_version >= "3.10" and python_version < "4.0" -python-dotenv==1.0.1 ; python_version >= "3.10" and python_version < "4.0" -python-json-logger==2.0.7 ; python_version >= "3.10" and python_version < "4.0" -pytz==2023.3.post1 ; python_version >= "3.10" and python_version < "4.0" -pywin32==306 ; python_version >= "3.10" and python_version < "4.0" and (platform_system == "Windows" or sys_platform == "win32") and (platform_system == "Windows" or platform_python_implementation != "PyPy") -pywinpty==2.0.13 ; python_version >= "3.10" and python_version < "4.0" and os_name == "nt" -pyyaml==6.0.1 ; python_version >= "3.10" and python_version < "4.0" -pyzmq==25.1.2 ; python_version >= "3.10" and python_version < "4.0" -qdrant-client==1.8.0 ; python_version >= "3.10" and python_version < "4.0" -qtconsole==5.5.1 ; python_version >= "3.10" and python_version < "4.0" -qtpy==2.4.1 ; python_version >= "3.10" and python_version < "4.0" -referencing==0.34.0 ; python_version >= "3.10" and python_version < "4.0" -regex==2023.12.25 ; python_version >= "3.10" and python_version < "4.0" -requests==2.31.0 ; python_version >= "3.10" and python_version < "4.0" -rfc3339-validator==0.1.4 ; python_version >= "3.10" and python_version < "4.0" -rfc3986-validator==0.1.1 ; python_version >= "3.10" and python_version < "4.0" -rich==13.7.1 ; python_version >= "3.10" and python_version < "4.0" -rpds-py==0.18.0 ; python_version >= "3.10" and python_version < "4.0" -send2trash==1.8.2 ; python_version >= "3.10" and python_version < "4.0" -setuptools==69.2.0 ; python_version >= "3.10" and python_version < "4.0" -six==1.16.0 ; python_version >= "3.10" and python_version < "4.0" -smmap==5.0.1 ; python_version >= "3.10" and python_version < "4.0" -sniffio==1.3.1 ; python_version >= "3.10" and python_version < "4.0" -soupsieve==2.5 ; python_version >= "3.10" and python_version < "4.0" -stack-data==0.6.3 ; python_version >= "3.10" and python_version < "4.0" -streamlit==1.32.2 ; python_version >= "3.10" and python_version < "4.0" -tenacity==8.2.3 ; python_version >= "3.10" and python_version < "4.0" -terminado==0.18.1 ; python_version >= "3.10" and python_version < "4.0" -tiktoken==0.6.0 ; python_version >= "3.10" and python_version < "4.0" -tinycss2==1.2.1 ; python_version >= "3.10" and python_version < "4.0" -toml==0.10.2 ; python_version >= "3.10" and python_version < "4.0" -tomli==2.0.1 ; python_version >= "3.10" and python_version < "3.11" -toolz==0.12.1 ; python_version >= "3.10" and python_version < "4.0" -tornado==6.4 ; python_version >= "3.10" and python_version < "4.0" -tqdm==4.66.2 ; python_version >= "3.10" and python_version < "4.0" -traitlets==5.14.2 ; python_version >= "3.10" and python_version < "4.0" -types-python-dateutil==2.9.0.20240316 ; python_version >= "3.10" and python_version < "4.0" -typing-extensions==4.10.0 ; python_version >= "3.10" and python_version < "4.0" -tzdata==2024.1 ; python_version >= "3.10" and python_version < "4.0" -uri-template==1.3.0 ; python_version >= "3.10" and python_version < "4.0" -urllib3==2.2.1 ; python_version >= "3.10" and python_version < "4.0" -watchdog==4.0.0 ; python_version >= "3.10" and python_version < "4.0" and platform_system != "Darwin" -wcwidth==0.2.13 ; python_version >= "3.10" and python_version < "4.0" -webcolors==1.13 ; python_version >= "3.10" and python_version < "4.0" -webencodings==0.5.1 ; python_version >= "3.10" and python_version < "4.0" -websocket-client==1.7.0 ; python_version >= "3.10" and python_version < "4.0" -widgetsnbextension==4.0.10 ; python_version >= "3.10" and python_version < "4.0" -win32-setctime==1.1.0 ; python_version >= "3.10" and python_version < "4.0" and sys_platform == "win32" -wrapt==1.16.0 ; python_version >= "3.10" and python_version < "4.0" -yarl==1.9.4 ; python_version >= "3.10" and python_version < "4.0" +qdrant-client==1.8.0 +python-dotenv==1.0.1 +tiktoken==0.6.0 +openai==1.14.2 +loguru==0.7.2 +pyyaml==6.0.1 +streamlit==1.32.2 +backoff==2.2.1 +langfuse==2.27.2 \ No newline at end of file From cd639e50ce6d76009d8dfb497d6bcffe47c94dbd Mon Sep 17 00:00:00 2001 From: Milutin-S Date: Sat, 1 Jun 2024 23:50:08 +0200 Subject: [PATCH 5/6] Add comments. --- app.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/app.py b/app.py index 4fb7162..e6931a7 100644 --- a/app.py +++ b/app.py @@ -4,62 +4,70 @@ from llm.prompts import INTRODUCTION_MESSAGE from utils import ( generate_response, - init_clients, - load_config_from_yaml, + initialize_clients, + load_config, WARNING_MESSAGE, QUERY_SUGGESTIONS, AUTHORS, LOGO_URL, ) -# Load environment variables. +# Load environment variables from the .env file. load_dotenv(find_dotenv()) -# Configure Streamlit page +# Set Streamlit page configuration with custom title and icon. st.set_page_config(page_title="Your Lawyer Assistant", page_icon=LOGO_URL) - st.title("LegaBot") st.divider() +# Initialize API clients for OpenAI and Qdrant and load configuration settings. +openai_client, qdrant_client = initialize_clients() +config = load_config() + +# Display the logo and set up the sidebar with useful information and links. st.logo(LOGO_URL, icon_image=LOGO_URL) with st.sidebar: st.subheader("💡 Query Suggestions") with st.container(border=True, height=200): st.markdown(QUERY_SUGGESTIONS) + st.subheader("⚠️ Warning") with st.container(border=True): st.markdown(WARNING_MESSAGE) - st.subheader("✍️ Authors") - with st.container(border=True): - st.markdown(AUTHORS) -openai_client, qdrant_client = init_clients() -config = load_config_from_yaml() + st.subheader("✍️ Authors") + st.markdown(AUTHORS) +# Initialize or update the session state for storing chat messages. if "messages" not in st.session_state: st.session_state.messages = [{"role": "assistant", "content": INTRODUCTION_MESSAGE}] +# Display all chat messages stored in the session state. for message in st.session_state.messages: with st.chat_message(message["role"]): st.markdown(message["content"]) - +# Handle user input and generate responses. if prompt := st.chat_input("Postavi pitanje iz prava..."): - # Generate and display the response + # Append user message to session state. st.session_state.messages.append({"role": "user", "content": prompt}) - # Display user message in chat message container + + # Display user message in chat container. with st.chat_message("user"): st.markdown(prompt) with st.chat_message("assistant"): + # Generate a response using the LLM and display it as a stream. stream = generate_response( query=prompt, openai_client=openai_client, qdrant_client=qdrant_client, config=config, ) + # Write the response stream to the chat. response = st.write_stream(stream) + # Append assistant's response to session state. st.session_state.messages.append({"role": "assistant", "content": response}) From e9dc48db5cda1a3b927fa437a2d113d42ac25be4 Mon Sep 17 00:00:00 2001 From: Milutin-S Date: Sat, 1 Jun 2024 23:50:28 +0200 Subject: [PATCH 6/6] Change method names. --- tests/test_router.py | 4 ++-- utils.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_router.py b/tests/test_router.py index 373edc7..5d9ad8e 100644 --- a/tests/test_router.py +++ b/tests/test_router.py @@ -7,7 +7,7 @@ from router.query_router import semantic_query_router from router.router_prompt import ROUTER_PROMPT -from utils import load_config_from_yaml +from utils import load_config class RouterTest(unittest.TestCase): @@ -15,7 +15,7 @@ class RouterTest(unittest.TestCase): def setUp(self) -> None: # Load configuration config_path = Path("./config.yaml") - self.config = load_config_from_yaml(yaml_file_path=config_path) + self.config = load_config(yaml_file_path=config_path) # Initialize OpenAI client self.openai_client = OpenAI(api_key=os.environ["OPENAI_API_KEY"]) diff --git a/utils.py b/utils.py index 2d0eec3..af4029e 100644 --- a/utils.py +++ b/utils.py @@ -60,14 +60,14 @@ class Config(BaseModel): openai: OpenAIConfig -def load_config_from_yaml(yaml_file_path: str = "./config.yaml") -> Config: +def load_config(yaml_file_path: str = "./config.yaml") -> Config: with open(yaml_file_path, "r") as file: yaml_content = yaml.safe_load(file) return Config(**yaml_content) @st.cache_resource -def init_clients() -> Tuple[OpenAI, QdrantClient]: +def initialize_clients() -> Tuple[OpenAI, QdrantClient]: """ Initializes and returns the clients for OpenAI and Qdrant services.