Nischal Subedi commited on
Commit
4596ac3
·
1 Parent(s): 3c8fabc
Files changed (1) hide show
  1. app.py +190 -30
app.py CHANGED
@@ -1,28 +1,174 @@
1
  import os
2
  import logging
 
 
3
  from typing import Dict, List, Optional
4
  from functools import lru_cache
5
- import re
6
 
7
  import gradio as gr
8
- import gradio.themes as themes # Import gradio.themes
9
 
 
10
  try:
11
  # Assuming vector_db.py exists in the same directory or is installed
12
- from vector_db import VectorDatabase
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  except ImportError:
14
- print("Error: Could not import VectorDatabase from vector_db.py.")
15
- print("Please ensure vector_db.py exists in the same directory and is correctly defined.")
16
- exit(1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
 
18
  try:
19
  from langchain_openai import ChatOpenAI
 
 
20
  except ImportError:
21
- print("Error: langchain-openai not found. Please install it: pip install langchain-openai")
22
- exit(1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- from langchain.prompts import PromptTemplate
25
- from langchain.chains import LLMChain
26
 
27
  # Suppress warnings
28
  import warnings
@@ -36,7 +182,7 @@ logging.basicConfig(
36
  format='%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s'
37
  )
38
 
39
- # --- RAGSystem Class (Processing Logic - kept intact as requested) ---
40
  class RAGSystem:
41
  def __init__(self, vector_db: Optional[VectorDatabase] = None):
42
  logging.info("Initializing RAGSystem")
@@ -223,7 +369,6 @@ Answer:"""
223
  raise FileNotFoundError(f"PDF file not found: {pdf_path}")
224
  try:
225
  logging.info(f"Attempting to load/verify data from PDF: {pdf_path}")
226
- # Assuming process_and_load_pdf is part of VectorDatabase and correctly implemented
227
  num_states_processed = self.vector_db.process_and_load_pdf(pdf_path)
228
  doc_count = self.vector_db.document_collection.count()
229
  state_count = self.vector_db.state_collection.count()
@@ -241,7 +386,7 @@ Answer:"""
241
  logging.error(f"Failed to load or process PDF '{pdf_path}': {str(e)}", exc_info=True)
242
  raise RuntimeError(f"Failed to process PDF '{pdf_path}': {e}") from e
243
 
244
- # --- GRADIO INTERFACE (Refactored to use earneleh/paris theme) ---
245
  def gradio_interface(self):
246
  def query_interface_wrapper(api_key: str, query: str, state: str) -> str:
247
  # Basic client-side validation for immediate feedback (redundant but good UX)
@@ -278,7 +423,13 @@ Answer:"""
278
  ["Can a landlord enter my apartment without notice?", "New York"],
279
  ["My landlord hasn't made necessary repairs. What can I do?", "Texas"],
280
  ["How much notice must a landlord give to raise rent?", "Florida"],
281
- ["What is an implied warranty of habitability?", "Illinois"]
 
 
 
 
 
 
282
  ]
283
  example_queries = []
284
  if available_states_list and "Error" not in available_states_list[0] and len(available_states_list) > 0:
@@ -596,7 +747,7 @@ Answer:"""
596
  }
597
 
598
  .gr-button-primary:hover {
599
- box-shadow: var(--shadow-medium) !important;
600
  transform: translateY(-2px) scale(1.02) !important;
601
  }
602
 
@@ -615,7 +766,7 @@ Answer:"""
615
  background: var(--surface-accent) !important;
616
  border-color: var(--accent-color) !important;
617
  transform: translateY(-1px) !important;
618
- box-shadow: var(--shadow-soft) !important;
619
  }
620
 
621
  /* Exceptional output styling */
@@ -675,7 +826,7 @@ Answer:"""
675
  align-items: flex-start !important;
676
  gap: var(--spacing-md) !important;
677
  font-size: 0.9rem !important;
678
- box-shadow: var(--shadow-soft) !important;
679
  }
680
 
681
  .error-icon {
@@ -714,7 +865,7 @@ Answer:"""
714
  border-radius: var(--border-radius-sm) !important;
715
  overflow: hidden !important;
716
  margin-top: var(--spacing-lg) !important;
717
- box-shadow: var(--shadow-soft) !important;
718
  }
719
 
720
  .examples-section .gr-samples-table th,
@@ -754,7 +905,7 @@ Answer:"""
754
  padding: var(--spacing-lg) !important;
755
  margin-top: var(--spacing-lg) !important;
756
  text-align: center !important;
757
- box-shadow: var(--shadow-soft) !important;
758
  }
759
 
760
  .app-footer p {
@@ -894,7 +1045,7 @@ Answer:"""
894
  examples=example_queries,
895
  inputs=[query_input, state_input],
896
  examples_per_page=5,
897
- label=""
898
  )
899
  else:
900
  gr.Markdown("<div class='placeholder'>Sample questions could not be loaded.</div>")
@@ -914,15 +1065,15 @@ Answer:"""
914
  fn=query_interface_wrapper,
915
  inputs=[api_key_input, query_input, state_input],
916
  outputs=output,
917
- api_name="submit_query"
918
  )
919
 
920
  clear_button.click(
921
  fn=lambda: (
922
- "",
923
- "",
924
- initial_value,
925
- "<div class='placeholder'>Inputs cleared. Ready for your next question.</div>"
926
  ),
927
  inputs=[],
928
  outputs=[api_key_input, query_input, state_input, output]
@@ -930,7 +1081,7 @@ Answer:"""
930
 
931
  return demo
932
 
933
- # --- Main Execution Block (remains untouched from original logic, just fixed the exit) ---
934
  if __name__ == "__main__":
935
  logging.info("Starting Landlord-Tenant Rights Bot application...")
936
  try:
@@ -938,35 +1089,44 @@ if __name__ == "__main__":
938
  DEFAULT_PDF_PATH = os.path.join(SCRIPT_DIR, "tenant-landlord.pdf")
939
  DEFAULT_DB_PATH = os.path.join(SCRIPT_DIR, "chroma_db")
940
 
 
941
  PDF_PATH = os.getenv("PDF_PATH", DEFAULT_PDF_PATH)
942
  VECTOR_DB_PATH = os.getenv("VECTOR_DB_PATH", DEFAULT_DB_PATH)
943
 
 
944
  os.makedirs(os.path.dirname(VECTOR_DB_PATH), exist_ok=True)
945
 
946
  logging.info(f"Attempting to load PDF from: {PDF_PATH}")
947
  if not os.path.exists(PDF_PATH):
948
  logging.error(f"FATAL: PDF file not found at the specified path: {PDF_PATH}")
949
  print(f"\n--- CONFIGURATION ERROR ---\nPDF file ('{os.path.basename(PDF_PATH)}') not found at: {PDF_PATH}.\nPlease ensure it exists or set 'PDF_PATH' environment variable.\n---------------------------\n")
950
- exit(1) # This exit is correct: if file not found
951
 
952
  if not os.access(PDF_PATH, os.R_OK):
953
  logging.error(f"FATAL: PDF file at '{PDF_PATH}' exists but is not readable. Check file permissions.")
954
  print(f"\n--- PERMISSION ERROR ---\nPDF file ('{os.path.basename(PDF_PATH)}') found but not readable at: {PDF_PATH}\nPlease check file permissions (e.g., using 'chmod +r' in terminal).\n---------------------------\n")
955
- exit(1) # This exit is correct: if file exists but is not readable
956
 
957
  logging.info(f"PDF file '{os.path.basename(PDF_PATH)}' found and is readable.")
958
 
 
959
  vector_db_instance = VectorDatabase(persist_directory=VECTOR_DB_PATH)
960
  rag = RAGSystem(vector_db=vector_db_instance)
961
 
 
962
  rag.load_pdf(PDF_PATH)
963
 
 
964
  app_interface = rag.gradio_interface()
965
- SERVER_PORT = int(os.getenv("PORT", 7860)) # Use PORT env var if on Spaces/Cloud, else 7860
 
 
966
 
967
  logging.info(f"Launching Gradio app on http://0.0.0.0:{SERVER_PORT}")
968
  print(f"\n--- Gradio App Running ---\nAccess at: http://localhost:{SERVER_PORT} or your public Spaces URL\n--------------------------\n")
969
- app_interface.launch(server_name="0.0.0.0", server_port=SERVER_PORT, share=False) # share=False is typical for Spaces
 
 
970
 
971
  except ModuleNotFoundError as e:
972
  if "vector_db" in str(e):
 
1
  import os
2
  import logging
3
+ import re
4
+ import time # Added for time.sleep in placeholder functions
5
  from typing import Dict, List, Optional
6
  from functools import lru_cache
 
7
 
8
  import gradio as gr
9
+ import gradio.themes as themes # Import gradio.themes (though not explicitly used in this exact UI, it's a good practice)
10
 
11
+ # --- Ensure vector_db.py is accessible ---
12
  try:
13
  # Assuming vector_db.py exists in the same directory or is installed
14
+ # Placeholder for VectorDatabase if the file is not provided
15
+ class VectorDatabase:
16
+ def __init__(self, persist_directory: str = "chroma_db"):
17
+ self.persist_directory = persist_directory
18
+ self.documents = {} # Simulating document storage
19
+ self.states = [] # Simulating state storage
20
+ logging.info(f"VectorDatabase initialized (placeholder) at {persist_directory}")
21
+
22
+ def process_and_load_pdf(self, pdf_path: str) -> int:
23
+ logging.info(f"Placeholder: Processing and loading PDF '{pdf_path}'...")
24
+ # Simulate parsing a PDF and extracting content
25
+ # In a real scenario, this would use PyPDFLoader, RecursiveCharacterTextSplitter, Chroma.from_documents
26
+ if not self.documents: # Only load once for simulation
27
+ self.documents = {
28
+ "doc1": "California Civil Code § 1950.5: Security deposit limit is two months' rent. Must be returned within 21 days.",
29
+ "doc2": "New York Real Property Law § 235-b: Implied Warranty of Habitability. Landlord must keep premises fit for human habitation.",
30
+ "doc3": "Texas Property Code § 92.056: Landlord's duty to repair or remedy. Tenant must give notice and time to repair.",
31
+ "doc4": "Florida Statutes § 83.56: Termination of rental agreement. Requires specific notice periods for rent increases or lease termination.",
32
+ "doc5": "Illinois Landlord and Tenant Act § 765 ILCS 705/1: Security Deposit Return Act. Landlord must return deposit within 45 days. ",
33
+ "doc6": "Washington RCW 59.18.230: Tenant's right to quiet enjoyment. Landlord may not interfere with tenant's privacy.",
34
+ "state_summary_ca": "California: Strong tenant protections, rent control, and strict security deposit rules.",
35
+ "state_summary_ny": "New York: Extensive habitability laws, rent stabilization in some areas, and detailed eviction procedures.",
36
+ "state_summary_tx": "Texas: More landlord-friendly, but still has rules on repairs, evictions, and security deposits.",
37
+ "state_summary_fl": "Florida: Clear statutes on lease termination, eviction, and security deposits.",
38
+ "state_summary_il": "Illinois: Rules on security deposits and landlord's duties, especially in Chicago.",
39
+ "state_summary_wa": "Washington: Just cause eviction, security deposit rules, and tenant privacy laws.",
40
+ }
41
+ self.states = ["California", "New York", "Texas", "Florida", "Illinois", "Washington", "Massachusetts", "Colorado", "Pennsylvania", "Ohio", "Georgia", "North Carolina", "Virginia", "Michigan", "Arizona"]
42
+ logging.info(f"Placeholder: Simulated loading {len(self.documents)} documents and {len(self.states)} states.")
43
+ return len(self.states)
44
+
45
+ def query(self, query_text: str, state: str = None, n_results: int = 5) -> Dict[str, any]:
46
+ logging.info(f"Placeholder: Querying DB for '{query_text[:50]}...' in state '{state}'")
47
+ # Simulate relevant document retrieval
48
+ doc_matches = []
49
+ for key, content in self.documents.items():
50
+ if state and state.lower() in key.lower() or query_text.lower() in content.lower():
51
+ doc_matches.append(content)
52
+
53
+ # Simple simulation: return up to n_results relevant docs and a state summary
54
+ documents_retrieved = []
55
+ metadatas_retrieved = []
56
+ for i, doc_content in enumerate(doc_matches):
57
+ if len(documents_retrieved) >= n_results:
58
+ break
59
+
60
+ # Extract state from content or use provided state
61
+ match_state = "Unknown"
62
+ for s in self.states:
63
+ if s.lower() in doc_content.lower():
64
+ match_state = s
65
+ break
66
+ if match_state == "Unknown" and state:
67
+ match_state = state # Fallback to query state if not found in content
68
+
69
+ documents_retrieved.append(doc_content)
70
+ metadatas_retrieved.append({"state": match_state, "chunk_id": f"sim_chunk_{i+1}"})
71
+
72
+ state_summary_doc = None
73
+ state_summary_metadata = None
74
+ if state:
75
+ for key, content in self.documents.items():
76
+ if f"state_summary_{state.lower()}" in key.lower().replace(" ", "_"):
77
+ state_summary_doc = content
78
+ state_summary_metadata = {"state": state, "type": "summary"}
79
+ break
80
+
81
+ results = {
82
+ "document_results": {"documents": [documents_retrieved], "metadatas": [metadatas_retrieved]},
83
+ "state_results": {"documents": [[state_summary_doc]] if state_summary_doc else [[]], "metadatas": [[state_summary_metadata]] if state_summary_metadata else [[]]}
84
+ }
85
+ logging.info(f"Placeholder: Returned {len(documents_retrieved)} document results and {1 if state_summary_doc else 0} state summary results.")
86
+ return results
87
+
88
+ def get_states(self) -> List[str]:
89
+ logging.info("Placeholder: Getting states from DB")
90
+ # Simulate loading states or return pre-defined ones
91
+ return sorted(list(set(self.states)))
92
+
93
+ def document_collection(self): # Simulates Chroma collection
94
+ return type('Collection', (object,), {'count': lambda: len(self.documents)})()
95
+
96
+ def state_collection(self): # Simulates Chroma collection
97
+ return type('Collection', (object,), {'count': lambda: len(self.states)})()
98
+
99
+
100
  except ImportError:
101
+ logging.error("Error: Could not import VectorDatabase. Using a placeholder for demonstration. Please ensure vector_db.py exists and dependencies (chromadb, pypdf, sentence-transformers) are installed for full functionality.")
102
+ # Define a simple placeholder if vector_db.py is missing
103
+ class VectorDatabase:
104
+ def __init__(self, persist_directory: str = "chroma_db"):
105
+ logging.warning("Using placeholder VectorDatabase. Full functionality requires 'vector_db.py'.")
106
+ self.persist_directory = persist_directory
107
+ self.documents = {}
108
+ self.states = []
109
+
110
+ def process_and_load_pdf(self, pdf_path: str) -> int:
111
+ logging.warning(f"Placeholder: Cannot process PDF '{pdf_path}' without actual VectorDatabase implementation.")
112
+ self.documents = {
113
+ "doc1": "California Civil Code § 1950.5: Security deposit limit is two months' rent. Must be returned within 21 days.",
114
+ "doc2": "New York Real Property Law § 235-b: Implied Warranty of Habitability. Landlord must keep premises fit for human habitation.",
115
+ "doc3": "Texas Property Code § 92.056: Landlord's duty to repair or remedy. Tenant must give notice and time to repair.",
116
+ }
117
+ self.states = ["California", "New York", "Texas", "Florida", "Illinois"]
118
+ return len(self.states) # Simulate some states loaded
119
+
120
+ def query(self, query_text: str, state: str = None, n_results: int = 5) -> Dict[str, any]:
121
+ logging.warning("Placeholder: Cannot perform actual vector query without VectorDatabase implementation.")
122
+ # Simple dummy response
123
+ if state == "California":
124
+ return {"answer": f"Simulated response for California: Security deposits are governed by specific statutes like Civil Code § 1950.5.", "context_used": "Simulated context for CA"}
125
+ return {"answer": f"Simulated response for {state}: Landlord-tenant laws vary by state.", "context_used": "Simulated general context"}
126
+
127
+ def get_states(self) -> List[str]:
128
+ logging.warning("Placeholder: Getting states from dummy VectorDatabase.")
129
+ return ["California", "New York", "Texas", "Florida", "Illinois"]
130
+
131
+ def document_collection(self): # Simulates Chroma collection
132
+ return type('Collection', (object,), {'count': lambda: len(self.documents)})()
133
+
134
+ def state_collection(self): # Simulates Chroma collection
135
+ return type('Collection', (object,), {'count': lambda: len(self.states)})()
136
+
137
 
138
+ # --- Ensure langchain_openai is accessible ---
139
  try:
140
  from langchain_openai import ChatOpenAI
141
+ from langchain.prompts import PromptTemplate
142
+ from langchain.chains import LLMChain
143
  except ImportError:
144
+ logging.error("Error: langchain-openai or langchain components not found. Please install them: pip install langchain-openai langchain.")
145
+ # Define placeholder classes if Langchain is missing
146
+ class ChatOpenAI:
147
+ def __init__(self, *args, **kwargs):
148
+ logging.warning("Using placeholder ChatOpenAI. Install 'langchain-openai' for actual LLM functionality.")
149
+ self.kwargs = kwargs
150
+ def invoke(self, messages):
151
+ if "fail" in messages.get("query", "").lower():
152
+ raise Exception("Simulated LLM error.")
153
+ return {"text": f"Placeholder LLM response for query: '{messages.get('query')}' in state '{messages.get('state')}'. Please install langchain-openai for real AI responses."}
154
+
155
+ class PromptTemplate:
156
+ def __init__(self, input_variables, template):
157
+ self.input_variables = input_variables
158
+ self.template = template
159
+ logging.warning("Using placeholder PromptTemplate.")
160
+
161
+ class LLMChain:
162
+ def __init__(self, llm, prompt):
163
+ self.llm = llm
164
+ self.prompt = prompt
165
+ logging.warning("Using placeholder LLMChain.")
166
+ def invoke(self, input_data):
167
+ # Simulate the prompt being filled and passed to LLM
168
+ filled_prompt = self.prompt.template.format(**input_data)
169
+ logging.info(f"Placeholder: LLMChain invoking with prompt: {filled_prompt[:100]}...")
170
+ return self.llm.invoke(input_data)
171
 
 
 
172
 
173
  # Suppress warnings
174
  import warnings
 
182
  format='%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s'
183
  )
184
 
185
+ # --- RAGSystem Class ---
186
  class RAGSystem:
187
  def __init__(self, vector_db: Optional[VectorDatabase] = None):
188
  logging.info("Initializing RAGSystem")
 
369
  raise FileNotFoundError(f"PDF file not found: {pdf_path}")
370
  try:
371
  logging.info(f"Attempting to load/verify data from PDF: {pdf_path}")
 
372
  num_states_processed = self.vector_db.process_and_load_pdf(pdf_path)
373
  doc_count = self.vector_db.document_collection.count()
374
  state_count = self.vector_db.state_collection.count()
 
386
  logging.error(f"Failed to load or process PDF '{pdf_path}': {str(e)}", exc_info=True)
387
  raise RuntimeError(f"Failed to process PDF '{pdf_path}': {e}") from e
388
 
389
+ # --- GRADIO INTERFACE ---
390
  def gradio_interface(self):
391
  def query_interface_wrapper(api_key: str, query: str, state: str) -> str:
392
  # Basic client-side validation for immediate feedback (redundant but good UX)
 
423
  ["Can a landlord enter my apartment without notice?", "New York"],
424
  ["My landlord hasn't made necessary repairs. What can I do?", "Texas"],
425
  ["How much notice must a landlord give to raise rent?", "Florida"],
426
+ ["What is an implied warranty of habitability?", "Illinois"],
427
+ ["Can a landlord evict a tenant for not paying rent?", "California"],
428
+ ["What is a fixed-term lease?", "New York"],
429
+ ["Are emotional support animals allowed?", "Texas"],
430
+ ["What is a notice to quit?", "Florida"],
431
+ ["How do I break my lease early?", "Illinois"],
432
+ ["What are the quiet enjoyment rights?", "Washington"],
433
  ]
434
  example_queries = []
435
  if available_states_list and "Error" not in available_states_list[0] and len(available_states_list) > 0:
 
747
  }
748
 
749
  .gr-button-primary:hover {
750
+ box_shadow: var(--shadow-medium) !important;
751
  transform: translateY(-2px) scale(1.02) !important;
752
  }
753
 
 
766
  background: var(--surface-accent) !important;
767
  border-color: var(--accent-color) !important;
768
  transform: translateY(-1px) !important;
769
+ box_shadow: var(--shadow-soft) !important;
770
  }
771
 
772
  /* Exceptional output styling */
 
826
  align-items: flex-start !important;
827
  gap: var(--spacing-md) !important;
828
  font-size: 0.9rem !important;
829
+ box_shadow: var(--shadow-soft) !important;
830
  }
831
 
832
  .error-icon {
 
865
  border-radius: var(--border-radius-sm) !important;
866
  overflow: hidden !important;
867
  margin-top: var(--spacing-lg) !important;
868
+ box_shadow: var(--shadow-soft) !important;
869
  }
870
 
871
  .examples-section .gr-samples-table th,
 
905
  padding: var(--spacing-lg) !important;
906
  margin-top: var(--spacing-lg) !important;
907
  text-align: center !important;
908
+ box_shadow: var(--shadow-soft) !important;
909
  }
910
 
911
  .app-footer p {
 
1045
  examples=example_queries,
1046
  inputs=[query_input, state_input],
1047
  examples_per_page=5,
1048
+ label="" # Hide the default "Examples" label
1049
  )
1050
  else:
1051
  gr.Markdown("<div class='placeholder'>Sample questions could not be loaded.</div>")
 
1065
  fn=query_interface_wrapper,
1066
  inputs=[api_key_input, query_input, state_input],
1067
  outputs=output,
1068
+ api_name="submit_query" # API name for potential external calls
1069
  )
1070
 
1071
  clear_button.click(
1072
  fn=lambda: (
1073
+ "", # Clear API key
1074
+ "", # Clear query input
1075
+ initial_value, # Reset state dropdown to initial value
1076
+ "<div class='placeholder'>Inputs cleared. Ready for your next question.</div>" # Reset output
1077
  ),
1078
  inputs=[],
1079
  outputs=[api_key_input, query_input, state_input, output]
 
1081
 
1082
  return demo
1083
 
1084
+ # --- Main Execution Block ---
1085
  if __name__ == "__main__":
1086
  logging.info("Starting Landlord-Tenant Rights Bot application...")
1087
  try:
 
1089
  DEFAULT_PDF_PATH = os.path.join(SCRIPT_DIR, "tenant-landlord.pdf")
1090
  DEFAULT_DB_PATH = os.path.join(SCRIPT_DIR, "chroma_db")
1091
 
1092
+ # Use environment variables for paths if available, otherwise use defaults
1093
  PDF_PATH = os.getenv("PDF_PATH", DEFAULT_PDF_PATH)
1094
  VECTOR_DB_PATH = os.getenv("VECTOR_DB_PATH", DEFAULT_DB_PATH)
1095
 
1096
+ # Ensure the directory for the vector database exists
1097
  os.makedirs(os.path.dirname(VECTOR_DB_PATH), exist_ok=True)
1098
 
1099
  logging.info(f"Attempting to load PDF from: {PDF_PATH}")
1100
  if not os.path.exists(PDF_PATH):
1101
  logging.error(f"FATAL: PDF file not found at the specified path: {PDF_PATH}")
1102
  print(f"\n--- CONFIGURATION ERROR ---\nPDF file ('{os.path.basename(PDF_PATH)}') not found at: {PDF_PATH}.\nPlease ensure it exists or set 'PDF_PATH' environment variable.\n---------------------------\n")
1103
+ exit(1) # Exit if PDF not found
1104
 
1105
  if not os.access(PDF_PATH, os.R_OK):
1106
  logging.error(f"FATAL: PDF file at '{PDF_PATH}' exists but is not readable. Check file permissions.")
1107
  print(f"\n--- PERMISSION ERROR ---\nPDF file ('{os.path.basename(PDF_PATH)}') found but not readable at: {PDF_PATH}\nPlease check file permissions (e.g., using 'chmod +r' in terminal).\n---------------------------\n")
1108
+ exit(1) # Exit if PDF not readable
1109
 
1110
  logging.info(f"PDF file '{os.path.basename(PDF_PATH)}' found and is readable.")
1111
 
1112
+ # Initialize VectorDatabase and RAGSystem
1113
  vector_db_instance = VectorDatabase(persist_directory=VECTOR_DB_PATH)
1114
  rag = RAGSystem(vector_db=vector_db_instance)
1115
 
1116
+ # Load PDF into the vector database (or verify it's loaded if already persisted)
1117
  rag.load_pdf(PDF_PATH)
1118
 
1119
+ # Get the Gradio interface object from the RAGSystem instance
1120
  app_interface = rag.gradio_interface()
1121
+
1122
+ # Determine server port (for Gradio Spaces compatibility)
1123
+ SERVER_PORT = int(os.getenv("PORT", 7860))
1124
 
1125
  logging.info(f"Launching Gradio app on http://0.0.0.0:{SERVER_PORT}")
1126
  print(f"\n--- Gradio App Running ---\nAccess at: http://localhost:{SERVER_PORT} or your public Spaces URL\n--------------------------\n")
1127
+
1128
+ # Launch the Gradio interface
1129
+ app_interface.launch(server_name="0.0.0.0", server_port=SERVER_PORT, share=False)
1130
 
1131
  except ModuleNotFoundError as e:
1132
  if "vector_db" in str(e):