Spaces:
Runtime error
Runtime error
File size: 8,786 Bytes
862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d cd7dd69 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 3b8535d 862dd53 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
import os
import streamlit as st
import requests
import zipfile
from langchain_groq import ChatGroq
from langchain_huggingface import HuggingFaceEmbeddings
from qdrant_client import QdrantClient
from langchain import PromptTemplate
from typing_extensions import TypedDict
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_core.prompts import ChatPromptTemplate
from langgraph.graph import StateGraph, END
from langchain.schema.output_parser import StrOutputParser
from langchain_core.output_parsers import StrOutputParser
from langchain.vectorstores import Qdrant
# Streamlit app title and description
st.title("Zoning Resolution Chatbot")
st.write("Powered by LLAMA3.1 70B. Ask me anything about the Zoning Resolution!")
# Helper function to get the language model
def get_llm():
llm = ChatGroq(
model="llama-3.1-70b-versatile",
temperature=0,
max_tokens=1024,
top_p=1,
stream=False,
stop=None,
api_key='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'
)
return llm
# Helper function to get the embeddings
def get_embeddings():
if "embeddings" not in st.session_state:
with st.spinner("Loading embeddings..."):
st.session_state.embeddings = HuggingFaceEmbeddings(model_name="dunzhang/stella_en_1.5B_v5")
st.success("Embeddings loaded successfully.")
return st.session_state.embeddings
# Helper function to get the vector store
def get_vector_store():
if "vector_store" not in st.session_state:
with st.spinner("Loading vector store..."):
embeddings = get_embeddings()
url = "https://01e87bb9-38e4-4af2-bc56-e1fa251e9888.us-east4-0.gcp.cloud.qdrant.io:6333"
api_key = "AUZLtuWlSaK3shi__AE7676-sA7r933Lv38z690sYuE_j2LHU8yqSQ"
client = QdrantClient(url=url, api_key=api_key, prefer_grpc=True)
st.session_state.vector_store = Qdrant(client=client, collection_name="my_documents", embeddings=embeddings)
st.success("Vector store loaded successfully.")
return st.session_state.vector_store.as_retriever()
# Define the agent state class
class AgentState(TypedDict):
question: str
grades: list[str]
llm_output: str
documents: list[str]
on_topic: bool
# Function to retrieve documents based on the question
def retrieve_docs(state: AgentState):
question = state["question"]
documents = retriever.get_relevant_documents(query=question)
state["documents"] = [doc.page_content for doc in documents]
return state
# Define the question grading class
class GradeQuestion(BaseModel):
score: str = Field(description="Question is about Zoning Resolution? If yes -> 'Yes' if not -> 'No'")
# Function to classify the question
def question_classifier(state: AgentState):
question = state["question"]
system = """
You are a grader assessing the topic of a user question.
Only answer if the question is about one of the following topics related to zoning resolutions:
1. Zoning laws and regulations.
2. Land use planning and development.
3. Zoning permits and approvals.
4. Variances and special zoning exceptions.
Examples: What are the zoning laws for residential areas? -> Yes
How do I apply for a zoning variance? -> Yes
What is the zoning for my property? -> Yes
What is the capital of France? -> No
If the question IS about these topics respond with "Yes", otherwise respond with "No".
"""
grade_prompt = ChatPromptTemplate.from_messages([
("system", system),
("human", "User question: {question}"),
])
llm = get_llm()
structured_llm = llm.with_structured_output(GradeQuestion)
grader_llm = grade_prompt | structured_llm
result = grader_llm.invoke({"question": question})
state["on_topic"] = result.score
return state
# Function to route based on the topic classification
def on_topic_router(state: AgentState):
on_topic = state["on_topic"]
if on_topic.lower() == "yes":
return "on_topic"
return "off_topic"
# Function for off-topic response
def off_topic_response(state: AgentState):
state["llm_output"] = "I can't respond to that!"
return state
# Define the document grading class
class GradeDocuments(BaseModel):
score: str = Field(description="Documents are relevant to the question, 'Yes' or 'No'")
# Function to grade documents based on their relevance
def document_grader(state: AgentState):
docs = state["documents"]
question = state["question"]
system = """
You are a grader assessing relevance of a retrieved document to a user question.
If the document contains keyword(s) or semantic meaning related to the question, grade it as relevant.
Give a binary score 'Yes' or 'No' score to indicate whether the document is relevant to the question.
"""
grade_prompt = ChatPromptTemplate.from_messages([
("system", system),
("human", "Retrieved document: \n\n {document} \n\n User question: {question}"),
])
llm = get_llm()
structured_llm = llm.with_structured_output(GradeDocuments)
grader_llm = grade_prompt | structured_llm
scores = []
for doc in docs:
result = grader_llm.invoke({"document": doc, "question": question})
scores.append(result.score)
state["grades"] = scores
return state
# Function to route based on document grades
def gen_router(state: AgentState):
grades = state["grades"]
if any(grade.lower() == "yes" for grade in grades):
return "generate"
return "rewrite_query"
# Function to rewrite the query for better results
def rewriter(state: AgentState):
question = state["question"]
system = """
You are a question re-writer that converts an input question to a better version that is optimized for retrieval.
Look at the input and try to reason about the underlying semantic intent/meaning.
"""
re_write_prompt = ChatPromptTemplate.from_messages([
("system", system),
("human", "Here is the initial question: \n\n {question} \n Formulate an improved question."),
])
llm = get_llm()
question_rewriter = re_write_prompt | llm | StrOutputParser()
output = question_rewriter.invoke({"question": question})
state["question"] = output
return state
# Function to generate the final answer
def generate_answer(state: AgentState):
llm = get_llm()
question = state["question"]
context = state["documents"]
template = """Answer the question based only on the following context:
{context}
Question: {question}
"""
prompt = ChatPromptTemplate.from_template(template=template)
chain = prompt | llm | StrOutputParser()
result = chain.invoke({"question": question, "context": context})
state["llm_output"] = result
return state
# Define the workflow state graph
workflow = StateGraph(AgentState)
workflow.add_node("topic_decision", question_classifier)
workflow.add_node("off_topic_response", off_topic_response)
workflow.add_node("retrieve_docs", retrieve_docs)
workflow.add_node("rewrite_query", rewriter)
workflow.add_node("generate_answer", generate_answer)
workflow.add_node("document_grader", document_grader)
workflow.add_edge("off_topic_response", END)
workflow.add_edge("retrieve_docs", "document_grader")
workflow.add_conditional_edges("topic_decision", on_topic_router, {
"on_topic": "retrieve_docs",
"off_topic": "off_topic_response",
})
workflow.add_conditional_edges("document_grader", gen_router, {
"generate": "generate_answer",
"rewrite_query": "rewrite_query",
})
workflow.add_edge("rewrite_query", "retrieve_docs")
workflow.add_edge("generate_answer", END)
workflow.set_entry_point("topic_decision")
# Compile the workflow
app = workflow.compile()
# Load embeddings and vector store
embeddings = get_embeddings()
retriever = get_vector_store()
# Streamlit UI for chat interaction
if "messages" not in st.session_state:
st.session_state.messages = []
# Display previous messages
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# Input field for user question
if prompt := st.chat_input("Ask about Zoning Resolution:"):
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
# Initialize state and invoke workflow
state = {"question": prompt}
result = app.invoke(state)
full_response = result["llm_output"]
# Display response from the assistant
with st.chat_message("assistant"):
st.markdown(full_response)
st.session_state.messages.append({"role": "assistant", "content": full_response})
|