josequinonez commited on
Commit
a8ca560
1 Parent(s): 2fcd4d4

Update app.py, Dockerfile, and requirements.txt to latest versions

Browse files
Files changed (3) hide show
  1. Dockerfile +6 -3
  2. app.py +189 -0
  3. requirements.txt +8 -3
Dockerfile CHANGED
@@ -1,4 +1,5 @@
1
- FROM python:3.13.5-slim
 
2
 
3
  WORKDIR /app
4
 
@@ -9,7 +10,9 @@ RUN apt-get update && apt-get install -y \
9
  && rm -rf /var/lib/apt/lists/*
10
 
11
  COPY requirements.txt ./
12
- COPY src/ ./src/
 
 
13
 
14
  RUN pip3 install -r requirements.txt
15
 
@@ -17,4 +20,4 @@ EXPOSE 8501
17
 
18
  HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
19
 
20
- ENTRYPOINT ["streamlit", "run", "src/streamlit_app.py", "--server.port=8501", "--server.address=0.0.0.0"]
 
1
+
2
+ FROM python:3.9-slim
3
 
4
  WORKDIR /app
5
 
 
10
  && rm -rf /var/lib/apt/lists/*
11
 
12
  COPY requirements.txt ./
13
+ COPY app.py ./
14
+ COPY data/ ./data/ # Added this line to copy the 'data' directory into the Docker image
15
+ #COPY src/ ./src/ # Commented out as src/ folder might not exist or be needed
16
 
17
  RUN pip3 install -r requirements.txt
18
 
 
20
 
21
  HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
22
 
23
+ ENTRYPOINT ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0","--server.enableXsrfProtection=false"]
app.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import streamlit as st
3
+ import os
4
+ from glob import glob # Added glob for finding files
5
+ #import json # No longer directly reading config.json in app.py
6
+ #import requests
7
+ from langchain_community.document_loaders import PyMuPDFLoader
8
+ from openai import OpenAI
9
+ import tiktoken
10
+ #import pandas as pd
11
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
12
+ from langchain_community.embeddings.openai import OpenAIEmbeddings
13
+ from langchain_community.vectorstores import Chroma
14
+ #import tempfile # No longer needed for files already on disk
15
+
16
+ # Retrieve secrets from environment variables
17
+ # Ensure these are set in your deployment environment (e.g., Streamlit Cloud secrets)
18
+ OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
19
+ OPENAI_API_BASE = os.environ.get("OPENAI_API_BASE")
20
+
21
+ # Initialize OpenAI client
22
+ client = OpenAI(
23
+ api_key=OPENAI_API_KEY,
24
+ base_url=OPENAI_API_BASE
25
+ )
26
+
27
+ # Define the system prompt for the model
28
+ qna_system_message = """
29
+ You are Customer service AI assistant designed to support customers in efficiently reviewing operational manuals. Your task is to provide evidence-based, concise, and relevant summaries based on the context provided from documents.
30
+
31
+ User input will include the necessary context for you to answer their questions. This context will begin with the token:
32
+
33
+ ###Context
34
+ The context contains excerpts from one or more documents in spanish, along with associated metadata such as titles, authors, abstracts, keywords, and specific sections relevant to the query.
35
+
36
+ When crafting your response
37
+ -Use only the provided context to answer the question.
38
+ -If the answer is found in the context, respond with concise and insight-focused summaries.
39
+ -Include the paper title and, where applicable, arXiv ID or section reference as the source.
40
+ -If the question is unrelated to the context or the context is empty, clearly respond with: "Sorry, this is out of my knowledge base."
41
+
42
+ Please adhere to the following response guidelines:
43
+ -Provide clear, direct answers in spanish using only the given context.
44
+ -Do not include any additional information outside of the context.
45
+ -Avoid rephrasing or generalizing unless explicitly relevant to the question.
46
+ -If no relevant answer exists in the context, respond with: "Disculpa, no tengo el conocimiento para responder a esa pregunta."
47
+ -If the context is not provided, your response should also be: "Disculpa, no tengo el conocimiento para responder a esa pregunta."
48
+
49
+
50
+ Here is an example of how to structure your response:
51
+
52
+ Respuesta:
53
+ [Answer based on context]
54
+
55
+ Fuente:
56
+ [Source details with page or section]
57
+ """
58
+
59
+ # Define the user message template
60
+ qna_user_message_template = """
61
+ ###Context
62
+ Here are some excerpts from source documents that are relevant to the mentioned below:
63
+ {context}
64
+
65
+ ###Question
66
+ {question}
67
+ """
68
+
69
+ # System message for query expansion
70
+ expansion_system_message = """
71
+ You are a helpful assistant specialized in rephrasing user questions to improve the relevance of document retrieval. Your task is to take a user's original question and generate an expanded or rephrased version that is more likely to match relevant document sections, especially considering temporal contexts or implied information. Do not answer the question; only rephrase or expand it.
72
+
73
+ For example:
74
+ Original Question: "驴cu谩l modalidad de retiro le corresponde a una persona empez贸 a trabajar en 1990?"
75
+ Expanded Question: "驴cu谩l modalidad de retiro le corresponde a una persona que empez贸 a trabajar antes de 1997 o en 1990, estaba afiliada al IMSS, espec铆ficamente bajo la Ley del Seguro Social de 1973?"
76
+
77
+ Provide only the expanded question, without any conversational filler.
78
+ """
79
+
80
+ @st.cache_resource
81
+ def load_and_process_pdfs(pdf_file_paths): # Changed parameter to accept file paths
82
+ all_documents = []
83
+ for pdf_file_path in pdf_file_paths:
84
+ loader = PyMuPDFLoader(pdf_file_path)
85
+ documents = loader.load()
86
+ all_documents.extend(documents)
87
+ text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
88
+ encoding_name='cl100k_base',
89
+ chunk_size=1000,
90
+ chunk_overlap=100
91
+ )
92
+ document_chunks = text_splitter.split_documents(all_documents)
93
+
94
+ embedding_model = OpenAIEmbeddings(
95
+ openai_api_key=OPENAI_API_KEY,
96
+ openai_api_base=OPENAI_API_BASE
97
+ )
98
+
99
+ # Create an in-memory vector store (or use a persistent one if needed)
100
+ vectorstore = Chroma.from_documents(
101
+ document_chunks,
102
+ embedding_model
103
+ )
104
+ return vectorstore.as_retriever(search_type='similarity', search_kwargs={'k': 5})
105
+
106
+ def generate_rag_response(user_input, retriever, max_tokens=500, temperature=0, top_p=0.95):
107
+ # Retrieve relevant document chunks
108
+ relevant_document_chunks = retriever.get_relevant_documents(query=user_input)
109
+ context_list = [d.page_content for d in relevant_document_chunks]
110
+
111
+ # Combine document chunks into a single context
112
+ context_for_query = ". ".join(context_list)
113
+
114
+ user_message = qna_user_message_template.replace('{context}', context_for_query)
115
+ user_message = user_message.replace('{question}', user_input)
116
+
117
+ # Generate the response
118
+ try:
119
+ response = client.chat.completions.create(
120
+ model="gpt-4o-mini",
121
+ messages=[
122
+ {"role": "system", "content": qna_system_message},
123
+ {"role": "user", "content": user_message}
124
+ ],
125
+ max_tokens=max_tokens,
126
+ temperature=temperature,
127
+ top_p=top_p
128
+ )
129
+ response = response.choices[0].message.content.strip()
130
+ except Exception as e:
131
+ response = f'Sorry, I encountered the following error: \n {e}'
132
+
133
+ return response
134
+
135
+ def query_expansion(user_question, model_name="gpt-4o-mini", temperature=0.3):
136
+ global client, expansion_system_message
137
+ try:
138
+ response = client.chat.completions.create(
139
+ model=model_name,
140
+ messages=[
141
+ {"role": "system", "content": expansion_system_message},
142
+ {"role": "user", "content": user_question}
143
+ ],
144
+ temperature=temperature,
145
+ max_tokens=150
146
+ )
147
+ expanded_query = response.choices[0].message.content.strip()
148
+ return expanded_query
149
+ except Exception as e:
150
+ st.error(f"Error during query expansion: {e}") # Use st.error for Streamlit app
151
+ return user_question # Fallback to original question if expansion fails
152
+
153
+ def generate_rag_response_complete(user_question, retriever, k=5, max_tokens=500, temperature=0.3, top_p=0.95):
154
+ # First, try to generate a response with the original question
155
+ response = generate_rag_response(user_question, retriever, max_tokens, temperature, top_p)
156
+
157
+ # If the initial response indicates no knowledge, try query expansion
158
+ if "Disculpa, no tengo el conocimiento para responder a esa pregunta." in response:
159
+ st.info("Initial RAG failed. Attempting query expansion...") # Use st.info for Streamlit app
160
+ expanded_question = query_expansion(user_question, temperature=temperature) # Pass temperature
161
+ st.info(f"Expanded Question: {expanded_question}")
162
+ # Call RAG again with the expanded question
163
+ response = generate_rag_response(expanded_question, retriever, max_tokens, temperature, top_p)
164
+
165
+ return response
166
+
167
+ # Streamlit App
168
+ st.title("LLM-Powered Research Assistant")
169
+
170
+ # Define the path to the data directory within the app's context
171
+ DATA_DIR = "data"
172
+ # Find all PDF files in the data directory
173
+ pdf_files = glob(os.path.join(DATA_DIR, "*.pdf"))
174
+
175
+ retriever = None
176
+ if pdf_files: # Check if any PDF files were found
177
+ st.info(f"Processing {len(pdf_files)} PDFs from the '{DATA_DIR}' directory...")
178
+ retriever = load_and_process_pdfs(pdf_files) # Call with the list of file paths
179
+ st.success("PDFs processed and ready for questioning!")
180
+ else:
181
+ st.warning(f"No PDF files found in the '{DATA_DIR}' directory. Please ensure your PDFs are in this folder and rebuild the Docker image if deploying.")
182
+
183
+
184
+ if retriever:
185
+ user_question = st.text_input("Ask a question about the documents:")
186
+ if user_question:
187
+ with st.spinner("Generating response..."):
188
+ rag_response = generate_rag_response_complete(user_question, retriever) # Use the complete function
189
+ st.write(rag_response)
requirements.txt CHANGED
@@ -1,3 +1,8 @@
1
- altair
2
- pandas
3
- streamlit
 
 
 
 
 
 
1
+ langchain_community==0.3.27
2
+ langchain==0.3.27
3
+ chromadb==1.0.15
4
+ pymupdf==1.26.3
5
+ tiktoken==0.9.0
6
+ streamlit==1.35.0
7
+ openai==1.99.1
8
+ langchain_openai==0.3.28