Pablo276 commited on
Commit
7da65b4
·
verified ·
1 Parent(s): dc99d90

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +195 -0
app.py ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import time
4
+ from dotenv import load_dotenv
5
+ from pinecone import Pinecone, ServerlessSpec
6
+ from langchain_pinecone import PineconeVectorStore
7
+ from langchain_openai import OpenAIEmbeddings
8
+ from langchain_core.documents import Document
9
+ from langchain_community.document_loaders import PyPDFLoader
10
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
11
+ import io
12
+ import pandas as pd
13
+
14
+ # Load environment variables from a .env file
15
+ load_dotenv()
16
+
17
+ # --- Backend Functions ---
18
+
19
+ def get_stored_files():
20
+ """Retrieve a list of files currently stored in the Pinecone index"""
21
+ try:
22
+ pc = Pinecone(api_key=os.environ.get("PINECONE_API_KEY"))
23
+ index_name = os.environ.get("PINECONE_INDEX_NAME")
24
+
25
+ existing_indexes = [index_info["name"] for index_info in pc.list_indexes()]
26
+ if index_name not in existing_indexes:
27
+ return []
28
+
29
+ index = pc.Index(index_name)
30
+ # Query with a dummy vector to fetch metadata. Increase top_k if you have more files.
31
+ results = index.query(vector=[0.0] * 3072, top_k=10000, include_metadata=True)
32
+
33
+ unique_files = set()
34
+ if results.matches:
35
+ for match in results.matches:
36
+ if hasattr(match, 'metadata') and match.metadata and 'source' in match.metadata:
37
+ unique_files.add(match.metadata['source'])
38
+
39
+ return sorted(list(unique_files))
40
+ except Exception as e:
41
+ print(f"Error retrieving stored files: {str(e)}")
42
+ return []
43
+
44
+ def delete_file_from_vectorstore(filename):
45
+ """Deletes all vectors associated with a specific filename from Pinecone."""
46
+ if not filename:
47
+ return "No file selected for deletion.", get_files_df()
48
+ try:
49
+ pc = Pinecone(api_key=os.environ.get("PINECONE_API_KEY"))
50
+ index_name = os.environ.get("PINECONE_INDEX_NAME")
51
+ index = pc.Index(index_name)
52
+ index.delete(filter={"source": {"$eq": filename}})
53
+
54
+ return f"Successfully deleted {filename}.", get_files_df()
55
+ except Exception as e:
56
+ return f"Error while deleting the file: {str(e)}", get_files_df()
57
+
58
+
59
+ def embedder(uploaded_file_path):
60
+ """Handles embedding of the uploaded PDF file."""
61
+ if uploaded_file_path is None:
62
+ return "No file uploaded. Please upload a PDF.", get_files_df()
63
+
64
+ try:
65
+ original_filename = os.path.basename(uploaded_file_path)
66
+
67
+ pc = Pinecone(api_key=os.environ.get("PINECONE_API_KEY"))
68
+ index_name = os.environ.get("PINECONE_INDEX_NAME")
69
+
70
+ existing_indexes = [index_info["name"] for index_info in pc.list_indexes()]
71
+ if index_name not in existing_indexes:
72
+ pc.create_index(
73
+ name=index_name,
74
+ dimension=3072,
75
+ metric="cosine",
76
+ spec=ServerlessSpec(cloud="aws", region="us-east-1"),
77
+ )
78
+ while not pc.describe_index(index_name).status["ready"]:
79
+ time.sleep(1)
80
+
81
+ index = pc.Index(index_name)
82
+ embeddings = OpenAIEmbeddings(model="text-embedding-3-large", api_key=os.environ.get("OPENAI_API_KEY"))
83
+ vector_store = PineconeVectorStore(index=index, embedding=embeddings)
84
+
85
+ loader = PyPDFLoader(uploaded_file_path)
86
+ raw_documents = loader.load()
87
+
88
+ for doc in raw_documents:
89
+ doc.metadata['source'] = original_filename
90
+
91
+ text_splitter = RecursiveCharacterTextSplitter(
92
+ chunk_size=800,
93
+ chunk_overlap=400,
94
+ length_function=len,
95
+ is_separator_regex=False,
96
+ )
97
+ documents = text_splitter.split_documents(raw_documents)
98
+
99
+ uuids = [f"{original_filename.replace('.pdf', '')}_{i+1}" for i in range(len(documents))]
100
+
101
+ batch_size = 100
102
+ for i in range(0, len(documents), batch_size):
103
+ batch_docs = documents[i:i+batch_size]
104
+ batch_ids = uuids[i:i+batch_size]
105
+ vector_store.add_documents(documents=batch_docs, ids=batch_ids)
106
+
107
+ return f"File '{original_filename}' successfully embedded!", get_files_df()
108
+
109
+ except Exception as e:
110
+ return f"Unable to create embeddings: {str(e)}", get_files_df()
111
+
112
+
113
+ # --- Gradio Interface Functions ---
114
+ def get_files_df():
115
+ files = get_stored_files()
116
+ if files:
117
+ return pd.DataFrame({"Stored Files": files})
118
+ else:
119
+ return pd.DataFrame({"Stored Files": []})
120
+
121
+ # CORRECTION: Updated function to handle the select event correctly
122
+ def handle_file_selection(evt: gr.SelectData):
123
+ """
124
+ Handles the file selection event from the DataFrame.
125
+ evt.value contains the value of the selected cell.
126
+ """
127
+ if evt.value:
128
+ return evt.value
129
+ return ""
130
+
131
+ # --- Gradio UI ---
132
+ with gr.Blocks(theme=gr.themes.Soft(), title="PDF Uploader") as demo:
133
+ gr.Markdown("# PDF File Uploader for Chatbot")
134
+ gr.Markdown("Upload PDF files to add their content to the chatbot's knowledge base.")
135
+
136
+ with gr.Row():
137
+ with gr.Column(scale=1):
138
+ gr.Markdown("## 📤 Upload New File")
139
+ file_uploader = gr.File(
140
+ label="Upload your PDF file",
141
+ file_types=[".pdf"],
142
+ type="filepath"
143
+ )
144
+ upload_button = gr.Button("Upload to Chatbot Memory", variant="primary")
145
+ upload_status = gr.Markdown("")
146
+
147
+ with gr.Column(scale=1):
148
+ gr.Markdown("## 🗂️ Stored Files")
149
+
150
+ refresh_button = gr.Button("Refresh File List")
151
+
152
+ file_df = gr.DataFrame(
153
+ value=get_files_df,
154
+ headers=["Stored Files"],
155
+ interactive=True
156
+ )
157
+
158
+ selected_file_text = gr.Textbox(
159
+ label="Selected File",
160
+ interactive=False,
161
+ placeholder="Click on a file above to select it"
162
+ )
163
+
164
+ delete_button = gr.Button("🗑️ Delete Selected File", variant="stop")
165
+ delete_status = gr.Markdown("")
166
+
167
+ # --- Event Handlers ---
168
+ upload_button.click(
169
+ fn=embedder,
170
+ inputs=[file_uploader],
171
+ outputs=[upload_status, file_df]
172
+ )
173
+
174
+ refresh_button.click(
175
+ fn=get_files_df,
176
+ inputs=[],
177
+ outputs=[file_df]
178
+ )
179
+
180
+ # CORRECTION: Removed the 'inputs' argument.
181
+ # The event data 'evt' is now passed automatically to 'handle_file_selection'.
182
+ file_df.select(
183
+ fn=handle_file_selection,
184
+ inputs=None, # Explicitly setting to None or removing this line works
185
+ outputs=[selected_file_text]
186
+ )
187
+
188
+ delete_button.click(
189
+ fn=delete_file_from_vectorstore,
190
+ inputs=[selected_file_text],
191
+ outputs=[delete_status, file_df]
192
+ )
193
+
194
+ if __name__ == "__main__":
195
+ demo.launch()