Spaces:
Running
Running
Commit
·
ce282d0
1
Parent(s):
40fe4f8
Add application file
Browse files- Dockerfile +31 -0
- app.py +200 -0
- data/bible_genesis_en.html +0 -0
- pyproject.toml +20 -0
- uv.lock +0 -0
Dockerfile
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
# Get a distribution that has uv already installed
|
3 |
+
FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim
|
4 |
+
|
5 |
+
# Add user - this is the user that will run the app
|
6 |
+
# If you do not set user, the app will run as root (undesirable)
|
7 |
+
RUN useradd -m -u 1000 user
|
8 |
+
USER user
|
9 |
+
|
10 |
+
# Set the home directory and path
|
11 |
+
ENV HOME=/home/user \
|
12 |
+
PATH=/home/user/.local/bin:$PATH
|
13 |
+
|
14 |
+
ENV UVICORN_WS_PROTOCOL=websockets
|
15 |
+
|
16 |
+
|
17 |
+
# Set the working directory
|
18 |
+
WORKDIR $HOME/app
|
19 |
+
|
20 |
+
# Copy the app to the container
|
21 |
+
COPY --chown=user . $HOME/app
|
22 |
+
|
23 |
+
# Install the dependencies
|
24 |
+
# RUN uv sync --frozen
|
25 |
+
RUN uv sync
|
26 |
+
|
27 |
+
# Expose the port
|
28 |
+
EXPOSE 7860
|
29 |
+
|
30 |
+
# Run the app
|
31 |
+
CMD ["uv", "run", "chainlit", "run", "app.py", "--host", "0.0.0.0", "--port", "7860"]
|
app.py
ADDED
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from dotenv import load_dotenv
|
3 |
+
from chainlit.types import AskFileResponse
|
4 |
+
import chainlit as cl
|
5 |
+
|
6 |
+
import pandas as pd
|
7 |
+
from langchain_community.vectorstores import FAISS
|
8 |
+
from langchain_core.documents import Document
|
9 |
+
from langchain_community.document_loaders import DirectoryLoader
|
10 |
+
from langchain_community.document_loaders import BSHTMLLoader
|
11 |
+
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
12 |
+
from langchain_huggingface import HuggingFaceEmbeddings
|
13 |
+
from langchain_qdrant import QdrantVectorStore
|
14 |
+
from qdrant_client import QdrantClient
|
15 |
+
from qdrant_client.http.models import Distance, VectorParams
|
16 |
+
from langchain.retrievers.contextual_compression import ContextualCompressionRetriever
|
17 |
+
from langchain_cohere import CohereRerank
|
18 |
+
from langchain.prompts import ChatPromptTemplate
|
19 |
+
from langchain_openai import ChatOpenAI
|
20 |
+
from langchain.chat_models import init_chat_model
|
21 |
+
from langchain_core.rate_limiters import InMemoryRateLimiter
|
22 |
+
from langgraph.graph import START, StateGraph, END
|
23 |
+
from typing_extensions import List, TypedDict
|
24 |
+
from langchain_core.documents import Document
|
25 |
+
from langchain_core.messages import HumanMessage
|
26 |
+
from langchain_core.tools import tool
|
27 |
+
from langgraph.prebuilt import ToolNode
|
28 |
+
|
29 |
+
|
30 |
+
#Load API Keys
|
31 |
+
load_dotenv()
|
32 |
+
|
33 |
+
#Load downloaded html pages of the book Genesis in Bible
|
34 |
+
path = "data/"
|
35 |
+
loader = DirectoryLoader(path, glob="*.html")
|
36 |
+
docs = loader.load()
|
37 |
+
|
38 |
+
#Building RAG Graph with LangGraph
|
39 |
+
text_splitter = RecursiveCharacterTextSplitter(
|
40 |
+
chunk_size = 750,
|
41 |
+
chunk_overlap = 100
|
42 |
+
)
|
43 |
+
|
44 |
+
split_documents = text_splitter.split_documents(docs)
|
45 |
+
len(split_documents)
|
46 |
+
#fine tuned embedding model
|
47 |
+
huggingface_embeddings = HuggingFaceEmbeddings(model_name="kcheng0816/finetuned_arctic_genesis")
|
48 |
+
|
49 |
+
client = QdrantClient(":memory:")
|
50 |
+
client.create_collection(
|
51 |
+
collection_name="genesis_bible",
|
52 |
+
vectors_config=VectorParams(size=1024, distance=Distance.COSINE),
|
53 |
+
)
|
54 |
+
|
55 |
+
vector_store = QdrantVectorStore(
|
56 |
+
client=client,
|
57 |
+
collection_name="genesis_bible",
|
58 |
+
embedding=huggingface_embeddings,
|
59 |
+
)
|
60 |
+
|
61 |
+
_ = vector_store.add_documents(documents=split_documents)
|
62 |
+
#Retrieve
|
63 |
+
retriever = vector_store.as_retriever(search_kwargs={"k": 5})
|
64 |
+
|
65 |
+
|
66 |
+
def retrieve_adjusted(state):
|
67 |
+
compressor = CohereRerank(model="rerank-v3.5")
|
68 |
+
compression_retriever = ContextualCompressionRetriever(
|
69 |
+
base_compressor=compressor, base_retriever=retriever, search_kwargs={"k": 5}
|
70 |
+
)
|
71 |
+
retrieved_docs = compression_retriever.invoke(state["question"])
|
72 |
+
return {"context" : retrieved_docs}
|
73 |
+
|
74 |
+
|
75 |
+
RAG_PROMPT = """\
|
76 |
+
You are a helpful assistant who answers questions based on provided context. You must only use the provided context, and cannot use your own knowledge.
|
77 |
+
|
78 |
+
### Question
|
79 |
+
{question}
|
80 |
+
|
81 |
+
### Context
|
82 |
+
{context}
|
83 |
+
"""
|
84 |
+
rag_prompt = ChatPromptTemplate.from_template(RAG_PROMPT)
|
85 |
+
|
86 |
+
|
87 |
+
#llm
|
88 |
+
rate_limiter = InMemoryRateLimiter(
|
89 |
+
requests_per_second=1, # <-- make a request once every 1 seconds!!
|
90 |
+
check_every_n_seconds=0.1, # Wake up every 100 ms to check whether allowed to make a request,
|
91 |
+
max_bucket_size=10, # Controls the maximum burst size.
|
92 |
+
)
|
93 |
+
llm = init_chat_model("gpt-4o-mini", rate_limiter=rate_limiter)
|
94 |
+
|
95 |
+
|
96 |
+
def generate(state):
|
97 |
+
docs_content = "\n\n".join(doc.page_content for doc in state["context"])
|
98 |
+
messages = rag_prompt.format_messages(question=state["question"], context=docs_content)
|
99 |
+
response = llm.invoke(messages)
|
100 |
+
return {"response" : response.content}
|
101 |
+
|
102 |
+
|
103 |
+
|
104 |
+
|
105 |
+
class State(TypedDict):
|
106 |
+
question: str
|
107 |
+
context: List[Document]
|
108 |
+
response: str
|
109 |
+
|
110 |
+
graph_builder = StateGraph(State).add_sequence([retrieve_adjusted, generate])
|
111 |
+
graph_builder.add_edge(START, "retrieve_adjusted")
|
112 |
+
graph = graph_builder.compile()
|
113 |
+
|
114 |
+
|
115 |
+
|
116 |
+
|
117 |
+
@tool
|
118 |
+
def ai_rag_tool(question: str) -> str:
|
119 |
+
"""Useful for when you need to answer questions about Bible """
|
120 |
+
response = graph.invoke({"question": question})
|
121 |
+
return {
|
122 |
+
"message": [HumanMessage(content=response["response"])],
|
123 |
+
"context": response["context"]
|
124 |
+
}
|
125 |
+
|
126 |
+
tool_belt = [
|
127 |
+
ai_rag_tool
|
128 |
+
]
|
129 |
+
|
130 |
+
|
131 |
+
llm = init_chat_model("gpt-4o", temperature=0, rate_limiter=rate_limiter)
|
132 |
+
llm_with_tools = llm.bind_tools(tool_belt)
|
133 |
+
|
134 |
+
|
135 |
+
from langgraph.graph import END
|
136 |
+
from langchain_core.messages import AnyMessage
|
137 |
+
from langgraph.graph.message import add_messages
|
138 |
+
from typing import TypedDict, Annotated
|
139 |
+
from langchain_core.documents import Document
|
140 |
+
|
141 |
+
|
142 |
+
class AgentState(TypedDict):
|
143 |
+
messages: Annotated[list[AnyMessage], add_messages]
|
144 |
+
context:List[Document]
|
145 |
+
|
146 |
+
|
147 |
+
|
148 |
+
def call_mode(state):
|
149 |
+
messages = state["messages"]
|
150 |
+
response = llm_with_tools.invoke(messages)
|
151 |
+
return {
|
152 |
+
"messages": [response],
|
153 |
+
"context": state.get("context",[])
|
154 |
+
}
|
155 |
+
|
156 |
+
tool_node = ToolNode(tool_belt)
|
157 |
+
|
158 |
+
def should_continue(state):
|
159 |
+
last_message = state["messages"][-1]
|
160 |
+
|
161 |
+
if last_message.tool_calls:
|
162 |
+
return "action"
|
163 |
+
|
164 |
+
return END
|
165 |
+
|
166 |
+
|
167 |
+
#
|
168 |
+
uncompiled_graph = StateGraph(AgentState)
|
169 |
+
|
170 |
+
uncompiled_graph.add_node("agent", call_mode)
|
171 |
+
uncompiled_graph.add_node("action", tool_node)
|
172 |
+
|
173 |
+
uncompiled_graph.set_entry_point("agent")
|
174 |
+
|
175 |
+
uncompiled_graph.add_conditional_edges(
|
176 |
+
"agent",
|
177 |
+
should_continue
|
178 |
+
)
|
179 |
+
|
180 |
+
uncompiled_graph.add_edge("action", "agent")
|
181 |
+
|
182 |
+
# Compile and display the graph for a visual overview
|
183 |
+
compiled_graph = uncompiled_graph.compile()
|
184 |
+
|
185 |
+
|
186 |
+
|
187 |
+
|
188 |
+
|
189 |
+
@cl.on_chat_start
|
190 |
+
async def on_chat_start():
|
191 |
+
cl.user_session.set("graph", compiled_graph)
|
192 |
+
|
193 |
+
|
194 |
+
|
195 |
+
@cl.on_message
|
196 |
+
async def handle(message: cl.Message):
|
197 |
+
graph = cl.user_session.get("graph")
|
198 |
+
state = {"messages": [HumanMessage(content=message.content)]}
|
199 |
+
response = await graph.ainvoke(state)
|
200 |
+
await cl.Message(content=response["messages"][-1].content).send()
|
data/bible_genesis_en.html
ADDED
The diff for this file is too large to render.
See raw diff
|
|
pyproject.toml
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[project]
|
2 |
+
name = "biblestudy-agentic-rag"
|
3 |
+
version = "0.1.0"
|
4 |
+
description = "AIE5 midterm"
|
5 |
+
readme = "README.md"
|
6 |
+
requires-python = ">=3.13"
|
7 |
+
dependencies = [
|
8 |
+
"jupyter>=1.1.1",
|
9 |
+
"langchain>=0.3.15",
|
10 |
+
"langchain-community>=0.3.15",
|
11 |
+
"langchain-openai>=0.3.2",
|
12 |
+
"langgraph>=0.2.67",
|
13 |
+
"chainlit>=2.0.4",
|
14 |
+
"pandas>=2.2.3",
|
15 |
+
"langchain-qdrant>=0.2.0",
|
16 |
+
"langchain-cohere>=0.4.2",
|
17 |
+
"unstructured>=0.14.8",
|
18 |
+
"langchain-huggingface>=0.1.2",
|
19 |
+
"websockets>=15.0",
|
20 |
+
]
|
uv.lock
ADDED
The diff for this file is too large to render.
See raw diff
|
|