Dushyant4342 commited on
Commit
b27e9cd
·
verified ·
1 Parent(s): 693b4b6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +177 -46
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py — RAG PDF Chat (phi-2 + LlamaIndex) in **Gradio**
2
  # ------------------------------------------------------------------
3
  # • LLM: microsoft/phi-2
4
  # • Embedding: BAAI/bge-small-en-v1.5
@@ -6,76 +6,207 @@
6
  # • Retrieval: LlamaIndex VectorStoreIndex (one per PDF)
7
  # ------------------------------------------------------------------
8
 
9
- import gradio as gr, tempfile, gc
 
 
10
  from pathlib import Path
11
  from llama_index.core import SimpleDirectoryReader, VectorStoreIndex, Document
12
  from llama_index.core.settings import Settings
13
  from llama_index.llms.huggingface import HuggingFaceLLM
14
  from llama_index.embeddings.huggingface import HuggingFaceEmbedding
 
15
 
16
- # ---------------- LLM & Embeddings ----------------
17
- llm = HuggingFaceLLM(
18
- model_name="microsoft/phi-2",
19
- tokenizer_name="microsoft/phi-2",
20
- device_map="auto",
21
- generate_kwargs={"temperature": 0.2, "max_new_tokens": 256, "repetition_penalty": 1.2},
22
- )
23
- embed = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")
24
 
25
- Settings.llm = llm
26
- Settings.embed_model = embed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  # ---------------- Helpers ----------------
29
  def build_index(path: str) -> VectorStoreIndex:
30
  """Create a VectorStoreIndex from the PDF at path."""
31
- docs = SimpleDirectoryReader(input_files=[path]).load_data()
32
- return VectorStoreIndex.from_documents(docs)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
  # ---------------- Gradio logic ----------------
35
- def add_pdfs(files, state):
36
  """Handle file upload, build indexes, return updated dropdown choices."""
37
- indexes, chat_hist = state or ({}, [])
38
- for f in files:
39
- if f.name in indexes:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  continue
41
- with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
42
- tmp.write(f.read())
43
- idx = build_index(tmp.name)
44
- indexes[f.name] = idx
 
 
 
 
 
 
 
 
 
 
 
45
  choices = list(indexes.keys())
46
- state = (indexes, chat_hist)
47
- return gr.Dropdown.update(choices=choices, value=choices[0] if choices else None), state
48
-
49
- def chat(query, pdf_choice, state):
50
- indexes, chat_hist = state
51
- if pdf_choice not in indexes:
52
- return chat_hist, state
53
- engine = indexes[pdf_choice].as_query_engine(similarity_top_k=4)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  try:
55
- answer = engine.query(query).response
 
 
 
56
  except Exception as e:
57
- answer = f"⚠️ {e}"
 
 
58
  chat_hist = chat_hist + [[query, answer]]
59
- state = (indexes, chat_hist)
60
- return chat_hist, state
61
 
62
- with gr.Blocks(css="footer {display:none}") as demo:
63
- gr.Markdown("## 📄 Chat with any PDF   |   **microsoft phi-2 + LlamaIndex**")
 
 
64
 
65
- state = gr.State(()) # (indexes dict, chat history list)
 
 
 
 
 
 
66
 
67
  with gr.Row():
68
- with gr.Column(scale=1):
69
- file_box = gr.File(label="Upload PDF(s)", file_types=[".pdf"], file_count="multiple")
70
- pdf_select = gr.Dropdown(label="Choose a PDF to chat with")
71
- with gr.Column(scale=3):
72
- chatbot = gr.Chatbot(label="Conversation")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
- query_box = gr.Textbox(label="Ask a question…", placeholder="What does the document say?")
 
 
 
 
 
 
 
 
 
 
 
75
 
76
- file_box.upload(fn=add_pdfs, inputs=[file_box, state], outputs=[pdf_select, state])
77
- query_box.submit(fn=chat, inputs=[query_box, pdf_select, state], outputs=[chatbot, state])
78
 
79
  if __name__ == "__main__":
80
- demo.queue().launch(show_api=False)
 
 
 
 
 
81
 
 
1
+ # app.py — RAG PDF Chat (phi-2 + LlamaIndex) in Gradio
2
  # ------------------------------------------------------------------
3
  # • LLM: microsoft/phi-2
4
  # • Embedding: BAAI/bge-small-en-v1.5
 
6
  # • Retrieval: LlamaIndex VectorStoreIndex (one per PDF)
7
  # ------------------------------------------------------------------
8
 
9
+ import gradio as gr
10
+ import tempfile
11
+ import gc
12
  from pathlib import Path
13
  from llama_index.core import SimpleDirectoryReader, VectorStoreIndex, Document
14
  from llama_index.core.settings import Settings
15
  from llama_index.llms.huggingface import HuggingFaceLLM
16
  from llama_index.embeddings.huggingface import HuggingFaceEmbedding
17
+ import torch # Explicitly import torch to check availability early
18
 
19
+ print("Script starting...")
 
 
 
 
 
 
 
20
 
21
+ # ---------------- LLM & Embeddings ----------------
22
+ print("Initializing LLM and Embeddings...")
23
+ try:
24
+ Settings.llm = HuggingFaceLLM(
25
+ model_name="microsoft/phi-2",
26
+ tokenizer_name="microsoft/phi-2",
27
+ device_map="auto", # Requires accelerate
28
+ model_kwargs={"trust_remote_code": True}, # Often needed for Phi-2
29
+ generate_kwargs={"temperature": 0.2, "max_new_tokens": 256, "repetition_penalty": 1.2},
30
+ )
31
+ Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")
32
+ print("LLM and Embeddings initialized successfully.")
33
+ except Exception as e:
34
+ print(f"Error initializing LLM or Embeddings: {e}")
35
+ # Optionally, re-raise or handle as appropriate for your app
36
+ # For now, we'll let it proceed to see if Gradio UI can at least load
37
+ # to show the error, but a real app might stop here.
38
+ Settings.llm = None # Ensure it's None if failed
39
+ Settings.embed_model = None
40
 
41
  # ---------------- Helpers ----------------
42
  def build_index(path: str) -> VectorStoreIndex:
43
  """Create a VectorStoreIndex from the PDF at path."""
44
+ print(f"Building index for: {path}")
45
+ # Ensure SimpleDirectoryReader is robust
46
+ try:
47
+ docs = SimpleDirectoryReader(input_files=[path]).load_data()
48
+ if not docs:
49
+ print(f"No documents loaded from {path}. Check PDF content and reader.")
50
+ # Handle empty or unreadable PDF gracefully
51
+ return VectorStoreIndex.from_documents([Document(text="Error: Could not read PDF or PDF is empty.")])
52
+ index = VectorStoreIndex.from_documents(docs)
53
+ print(f"Index built successfully for: {path}")
54
+ return index
55
+ except Exception as e:
56
+ print(f"Error building index for {path}: {e}")
57
+ # Return a dummy index or raise an error that can be caught by the UI
58
+ return VectorStoreIndex.from_documents([Document(text=f"Error processing PDF: {e}")])
59
+
60
 
61
  # ---------------- Gradio logic ----------------
62
+ def add_pdfs(files, current_state):
63
  """Handle file upload, build indexes, return updated dropdown choices."""
64
+ print("Adding PDFs...")
65
+ indexes, chat_hist = current_state if current_state else ({}, [])
66
+
67
+ if files is None:
68
+ print("No files uploaded.")
69
+ choices = list(indexes.keys())
70
+ return gr.Dropdown.update(choices=choices, value=choices[0] if choices else None), (indexes, chat_hist)
71
+
72
+ for f_obj in files: # Gradio File component gives a list of tempfile._TemporaryFileWrapper
73
+ original_filename = f_obj.name # This is the path to the temporary file
74
+ # Use a more descriptive name if possible, or stick to the temp name if original not easily available
75
+ # For this example, we'll use the temp file's name as key, but ideally, you'd want the original upload name.
76
+ # Gradio's File component might not directly give original filename easily without custom JS.
77
+ # Let's assume f_obj.name is unique enough for this context or use a counter.
78
+ # For simplicity, we'll use the temp file path as the key, but this is not ideal for display.
79
+ # A better approach would be to get the original filename if the Gradio version supports it easily,
80
+ # or manage it via the UI.
81
+
82
+ # Let's use Path(original_filename).name to get just the filename part of the temp path
83
+ display_name = Path(original_filename).name
84
+
85
+ if display_name in indexes:
86
+ print(f"Index for {display_name} already exists. Skipping.")
87
  continue
88
+
89
+ # The file `f_obj` is already a file-like object pointing to the uploaded content.
90
+ # We need its path. `f_obj.name` gives the path to the temporary file Gradio creates.
91
+ try:
92
+ print(f"Processing file: {display_name} from path: {original_filename}")
93
+ # No need to write to another tempfile, Gradio already provides one.
94
+ idx = build_index(original_filename)
95
+ indexes[display_name] = idx # Use display_name as key
96
+ print(f"Index for {display_name} added.")
97
+ except Exception as e:
98
+ print(f"Failed to process file {display_name}: {e}")
99
+ # Optionally, inform the user via the UI
100
+ # For now, just log and skip.
101
+
102
+ gc.collect() # Clean up memory
103
  choices = list(indexes.keys())
104
+ updated_value = choices[0] if choices else None
105
+ print(f"PDFs processed. Choices: {choices}, Selected: {updated_value}")
106
+ return gr.Dropdown.update(choices=choices, value=updated_value), (indexes, chat_hist)
107
+
108
+ def chat(query, pdf_choice, current_state):
109
+ """Handle chat query with the selected PDF."""
110
+ print(f"Chat query: '{query}' for PDF: '{pdf_choice}'")
111
+ indexes, chat_hist = current_state
112
+
113
+ if not Settings.llm or not Settings.embed_model:
114
+ answer = "⚠️ LLM or Embedding model not initialized. Please check server logs."
115
+ chat_hist = chat_hist + [[query, answer]]
116
+ return chat_hist, (indexes, chat_hist)
117
+
118
+ if not pdf_choice or pdf_choice not in indexes:
119
+ answer = "⚠️ Please select a PDF to chat with, or the selected PDF index is not available."
120
+ if not pdf_choice:
121
+ print("No PDF selected for chat.")
122
+ else:
123
+ print(f"PDF choice '{pdf_choice}' not found in indexes: {list(indexes.keys())}")
124
+ chat_hist = chat_hist + [[query, answer]]
125
+ return chat_hist, (indexes, chat_hist)
126
+
127
+ query_engine = indexes[pdf_choice].as_query_engine(similarity_top_k=4)
128
  try:
129
+ print(f"Querying engine for PDF: {pdf_choice}...")
130
+ response = query_engine.query(query)
131
+ answer = response.response
132
+ print("Query successful.")
133
  except Exception as e:
134
+ answer = f"⚠️ Error during query: {e}"
135
+ print(f"Exception during query: {e}")
136
+
137
  chat_hist = chat_hist + [[query, answer]]
138
+ return chat_hist, (indexes, chat_hist)
 
139
 
140
+ def clear_chat_and_query(current_state):
141
+ """Clears the chatbot and the query box."""
142
+ indexes, _ = current_state # Keep indexes
143
+ return [], (indexes, []), "" # Clear chatbot, new empty chat_hist, clear query_box
144
 
145
+ print("Building Gradio interface...")
146
+ with gr.Blocks(theme=gr.themes.Soft(), css="footer {display:none}") as demo:
147
+ gr.Markdown("## 📄 Chat with any PDF   |   **microsoft/phi-2 + LlamaIndex**")
148
+
149
+ # (indexes dict: {filename: VectorStoreIndex}, chat_history list: [[user_msg, bot_msg], ...])
150
+ # Initialize with empty dict for indexes and empty list for chat_hist
151
+ app_state = gr.State(({}, []))
152
 
153
  with gr.Row():
154
+ with gr.Column(scale=1, min_width=300):
155
+ file_box = gr.File(
156
+ label="Upload PDF(s)",
157
+ file_types=[".pdf"],
158
+ file_count="multiple"
159
+ )
160
+ pdf_select = gr.Dropdown(
161
+ label="Choose a PDF to chat with",
162
+ interactive=True
163
+ )
164
+ with gr.Column(scale=3, min_width=500):
165
+ chatbot = gr.Chatbot(
166
+ label="Conversation",
167
+ bubble_full_width=False,
168
+ height=500
169
+ )
170
+ query_box = gr.Textbox(
171
+ label="Ask a question…",
172
+ placeholder="Type your question here and press Enter.",
173
+ scale=4
174
+ )
175
+ clear_button = gr.Button("Clear Chat")
176
+
177
+
178
+ # Event handlers
179
+ file_box.upload(
180
+ fn=add_pdfs,
181
+ inputs=[file_box, app_state],
182
+ outputs=[pdf_select, app_state]
183
+ )
184
+
185
+ # When a PDF is selected from dropdown, or when files are uploaded and dropdown is updated
186
+ # you might want to clear the chat history for the new PDF.
187
+ # This can be chained or handled in add_pdfs if desired.
188
+ # For now, chat is persistent until "Clear Chat" is pressed.
189
 
190
+ query_box.submit(
191
+ fn=chat,
192
+ inputs=[query_box, pdf_select, app_state],
193
+ outputs=[chatbot, app_state]
194
+ )
195
+
196
+ # Clear button functionality
197
+ clear_button.click(
198
+ fn=clear_chat_and_query,
199
+ inputs=[app_state],
200
+ outputs=[chatbot, app_state, query_box] # chatbot, app_state (to reset chat_hist), query_box
201
+ )
202
 
203
+ print("Gradio Blocks defined.")
 
204
 
205
  if __name__ == "__main__":
206
+ print("Launching Gradio app...")
207
+ # For Hugging Face Spaces, demo.launch() is usually sufficient.
208
+ # queue() is good for handling multiple users.
209
+ # Ensure share=False (default) or not set, as Spaces handles public access.
210
+ demo.queue().launch()
211
+ print("Gradio app launched.")
212