Pablo276 commited on
Commit
6c9bb5c
·
verified ·
1 Parent(s): c4e4c70

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +47 -55
app.py CHANGED
@@ -1,16 +1,17 @@
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
- import re # Make sure to add this import at the top of your file
14
 
15
  # Load environment variables from a .env file
16
  load_dotenv()
@@ -18,23 +19,24 @@ load_dotenv()
18
  # --- Backend Functions ---
19
 
20
  def get_stored_files():
21
- """Retrieve a list of files currently stored in the Pinecone index"""
22
  try:
23
  pc = Pinecone(api_key=os.environ.get("PINECONE_API_KEY"))
24
  index_name = os.environ.get("PINECONE_INDEX_NAME")
25
 
26
- existing_indexes = [index_info["name"] for index_info in pc.list_indexes()]
27
- if index_name not in existing_indexes:
28
  return []
29
 
30
  index = pc.Index(index_name)
31
- # Query with a dummy vector to fetch metadata. Increase top_k if you have more files.
 
32
  results = index.query(vector=[0.0] * 3072, top_k=10000, include_metadata=True)
33
 
34
  unique_files = set()
35
  if results.matches:
36
  for match in results.matches:
37
- if hasattr(match, 'metadata') and match.metadata and 'source' in match.metadata:
38
  unique_files.add(match.metadata['source'])
39
 
40
  return sorted(list(unique_files))
@@ -43,23 +45,26 @@ def get_stored_files():
43
  return []
44
 
45
  def delete_file_from_vectorstore(filename):
46
- """Deletes all vectors associated with a specific filename from Pinecone."""
47
  if not filename:
48
  return "No file selected for deletion.", get_files_df()
49
  try:
50
  pc = Pinecone(api_key=os.environ.get("PINECONE_API_KEY"))
51
  index_name = os.environ.get("PINECONE_INDEX_NAME")
52
  index = pc.Index(index_name)
 
 
53
  index.delete(filter={"source": {"$eq": filename}})
54
 
55
  return f"Successfully deleted {filename}.", get_files_df()
56
  except Exception as e:
57
  return f"Error while deleting the file: {str(e)}", get_files_df()
58
 
59
-
60
-
61
  def embedder(uploaded_file_path):
62
- """Handles embedding of the uploaded PDF file."""
 
 
 
63
  if uploaded_file_path is None:
64
  return "No file uploaded. Please upload a PDF.", get_files_df()
65
 
@@ -68,12 +73,13 @@ def embedder(uploaded_file_path):
68
 
69
  pc = Pinecone(api_key=os.environ.get("PINECONE_API_KEY"))
70
  index_name = os.environ.get("PINECONE_INDEX_NAME")
 
71
 
72
- existing_indexes = [index_info["name"] for index_info in pc.list_indexes()]
73
- if index_name not in existing_indexes:
74
  pc.create_index(
75
  name=index_name,
76
- dimension=3072,
77
  metric="cosine",
78
  spec=ServerlessSpec(cloud="aws", region="us-east-1"),
79
  )
@@ -81,45 +87,41 @@ def embedder(uploaded_file_path):
81
  time.sleep(1)
82
 
83
  index = pc.Index(index_name)
84
- embeddings = OpenAIEmbeddings(model="text-embedding-3-large", api_key=os.environ.get("OPENAI_API_KEY"))
85
- vector_store = PineconeVectorStore(index=index, embedding=embeddings)
86
 
 
87
  loader = PyPDFLoader(uploaded_file_path)
88
  raw_documents = loader.load()
89
 
90
- for doc in raw_documents:
91
- doc.metadata['source'] = original_filename
92
-
93
  text_splitter = RecursiveCharacterTextSplitter(
94
  chunk_size=800,
95
  chunk_overlap=400,
96
  length_function=len,
97
- is_separator_regex=False,
98
  )
99
  documents = text_splitter.split_documents(raw_documents)
100
 
101
- # --- INIZIO: CODICE CORRETTO ---
102
- # 1. Rimuove l'estensione, gli spazi iniziali/finali e converte in minuscolo.
103
- sanitized_filename = original_filename.replace('.pdf', '').strip().lower()
104
-
105
- # 2. Sostituisce qualsiasi carattere non alfanumerico con un trattino.
106
- sanitized_filename = re.sub(r'[^a-z0-9]', '-', sanitized_filename)
107
-
108
- # 3. Sostituisce trattini multipli consecutivi con un singolo trattino.
109
- sanitized_filename = re.sub(r'-+', '-', sanitized_filename)
110
-
111
- # 4. (Opzionale ma consigliato) Rimuove eventuali trattini all'inizio o alla fine.
112
- sanitized_filename = sanitized_filename.strip('-')
113
-
114
- # 5. Usa il nome sanificato per generare gli ID.
115
- uuids = [f"{sanitized_filename}-{i}" for i in range(len(documents))]
116
- # --- FINE: CODICE CORRETTO ---
117
-
 
 
118
  batch_size = 100
119
- for i in range(0, len(documents), batch_size):
120
- batch_docs = documents[i:i+batch_size]
121
- batch_ids = uuids[i:i+batch_size]
122
- vector_store.add_documents(documents=batch_docs, ids=batch_ids)
123
 
124
  return f"File '{original_filename}' successfully embedded!", get_files_df()
125
 
@@ -129,18 +131,15 @@ def embedder(uploaded_file_path):
129
 
130
  # --- Gradio Interface Functions ---
131
  def get_files_df():
 
132
  files = get_stored_files()
133
  if files:
134
  return pd.DataFrame({"Stored Files": files})
135
  else:
136
  return pd.DataFrame({"Stored Files": []})
137
 
138
- # CORRECTION: Updated function to handle the select event correctly
139
  def handle_file_selection(evt: gr.SelectData):
140
- """
141
- Handles the file selection event from the DataFrame.
142
- evt.value contains the value of the selected cell.
143
- """
144
  if evt.value:
145
  return evt.value
146
  return ""
@@ -163,21 +162,17 @@ with gr.Blocks(theme=gr.themes.Soft(), title="PDF Uploader") as demo:
163
 
164
  with gr.Column(scale=1):
165
  gr.Markdown("## 🗂️ Stored Files")
166
-
167
  refresh_button = gr.Button("Refresh File List")
168
-
169
  file_df = gr.DataFrame(
170
  value=get_files_df,
171
  headers=["Stored Files"],
172
  interactive=True
173
  )
174
-
175
  selected_file_text = gr.Textbox(
176
  label="Selected File",
177
  interactive=False,
178
  placeholder="Click on a file above to select it"
179
  )
180
-
181
  delete_button = gr.Button("🗑️ Delete Selected File", variant="stop")
182
  delete_status = gr.Markdown("")
183
 
@@ -194,11 +189,8 @@ with gr.Blocks(theme=gr.themes.Soft(), title="PDF Uploader") as demo:
194
  outputs=[file_df]
195
  )
196
 
197
- # CORRECTION: Removed the 'inputs' argument.
198
- # The event data 'evt' is now passed automatically to 'handle_file_selection'.
199
  file_df.select(
200
  fn=handle_file_selection,
201
- inputs=None, # Explicitly setting to None or removing this line works
202
  outputs=[selected_file_text]
203
  )
204
 
 
1
  import gradio as gr
2
  import os
3
  import time
4
+ import re
5
+ import pandas as pd
6
  from dotenv import load_dotenv
7
  from pinecone import Pinecone, ServerlessSpec
8
+
9
+ # --- Langchain components for document processing and embedding ---
10
  from langchain_openai import OpenAIEmbeddings
11
  from langchain_core.documents import Document
12
  from langchain_community.document_loaders import PyPDFLoader
13
  from langchain_text_splitters import RecursiveCharacterTextSplitter
14
+
 
 
15
 
16
  # Load environment variables from a .env file
17
  load_dotenv()
 
19
  # --- Backend Functions ---
20
 
21
  def get_stored_files():
22
+ """Retrieve a list of files currently stored in the Pinecone index using the pinecone library."""
23
  try:
24
  pc = Pinecone(api_key=os.environ.get("PINECONE_API_KEY"))
25
  index_name = os.environ.get("PINECONE_INDEX_NAME")
26
 
27
+ # Check if the index exists. If not, there are no files.
28
+ if index_name not in [index_info["name"] for index_info in pc.list_indexes()]:
29
  return []
30
 
31
  index = pc.Index(index_name)
32
+ # A dummy query to fetch metadata from all vectors.
33
+ # Increase top_k if you expect to have more than 1000 chunks.
34
  results = index.query(vector=[0.0] * 3072, top_k=10000, include_metadata=True)
35
 
36
  unique_files = set()
37
  if results.matches:
38
  for match in results.matches:
39
+ if 'metadata' in match and 'source' in match.metadata:
40
  unique_files.add(match.metadata['source'])
41
 
42
  return sorted(list(unique_files))
 
45
  return []
46
 
47
  def delete_file_from_vectorstore(filename):
48
+ """Deletes all vectors associated with a specific filename from Pinecone using the pinecone library."""
49
  if not filename:
50
  return "No file selected for deletion.", get_files_df()
51
  try:
52
  pc = Pinecone(api_key=os.environ.get("PINECONE_API_KEY"))
53
  index_name = os.environ.get("PINECONE_INDEX_NAME")
54
  index = pc.Index(index_name)
55
+
56
+ # Use metadata filtering to delete all vectors associated with the file.
57
  index.delete(filter={"source": {"$eq": filename}})
58
 
59
  return f"Successfully deleted {filename}.", get_files_df()
60
  except Exception as e:
61
  return f"Error while deleting the file: {str(e)}", get_files_df()
62
 
 
 
63
  def embedder(uploaded_file_path):
64
+ """
65
+ Handles the embedding of the uploaded PDF file using langchain for processing
66
+ and the pinecone library for vector store operations.
67
+ """
68
  if uploaded_file_path is None:
69
  return "No file uploaded. Please upload a PDF.", get_files_df()
70
 
 
73
 
74
  pc = Pinecone(api_key=os.environ.get("PINECONE_API_KEY"))
75
  index_name = os.environ.get("PINECONE_INDEX_NAME")
76
+ embedding_dimension = 3072 # As specified for text-embedding-3-large
77
 
78
+ # Create the index if it doesn't exist
79
+ if index_name not in [index_info["name"] for index_info in pc.list_indexes()]:
80
  pc.create_index(
81
  name=index_name,
82
+ dimension=embedding_dimension,
83
  metric="cosine",
84
  spec=ServerlessSpec(cloud="aws", region="us-east-1"),
85
  )
 
87
  time.sleep(1)
88
 
89
  index = pc.Index(index_name)
 
 
90
 
91
+ # 1. Load and Split Document
92
  loader = PyPDFLoader(uploaded_file_path)
93
  raw_documents = loader.load()
94
 
 
 
 
95
  text_splitter = RecursiveCharacterTextSplitter(
96
  chunk_size=800,
97
  chunk_overlap=400,
98
  length_function=len,
 
99
  )
100
  documents = text_splitter.split_documents(raw_documents)
101
 
102
+ # 2. Create Embeddings
103
+ embeddings_model = OpenAIEmbeddings(model="text-embedding-3-large", api_key=os.environ.get("OPENAI_API_KEY"))
104
+ texts_to_embed = [doc.page_content for doc in documents]
105
+ embeddings = embeddings_model.embed_documents(texts_to_embed)
106
+
107
+ # 3. Sanitize filename and prepare vectors for upsert
108
+ sanitized_filename = re.sub(r'[^a-z0-9]', '-', original_filename.replace('.pdf', '').strip().lower())
109
+ sanitized_filename = re.sub(r'-+', '-', sanitized_filename).strip('-')
110
+
111
+ vectors_to_upsert = []
112
+ for i, (doc, vec) in enumerate(zip(documents, embeddings)):
113
+ vector_id = f"{sanitized_filename}-{i}"
114
+ metadata = {
115
+ "text": doc.page_content,
116
+ "source": original_filename
117
+ }
118
+ vectors_to_upsert.append({"id": vector_id, "values": vec, "metadata": metadata})
119
+
120
+ # 4. Upsert vectors to Pinecone in batches
121
  batch_size = 100
122
+ for i in range(0, len(vectors_to_upsert), batch_size):
123
+ batch = vectors_to_upsert[i:i+batch_size]
124
+ index.upsert(vectors=batch)
 
125
 
126
  return f"File '{original_filename}' successfully embedded!", get_files_df()
127
 
 
131
 
132
  # --- Gradio Interface Functions ---
133
  def get_files_df():
134
+ """Creates a DataFrame from the list of stored files for Gradio display."""
135
  files = get_stored_files()
136
  if files:
137
  return pd.DataFrame({"Stored Files": files})
138
  else:
139
  return pd.DataFrame({"Stored Files": []})
140
 
 
141
  def handle_file_selection(evt: gr.SelectData):
142
+ """Handles the file selection event from the DataFrame."""
 
 
 
143
  if evt.value:
144
  return evt.value
145
  return ""
 
162
 
163
  with gr.Column(scale=1):
164
  gr.Markdown("## 🗂️ Stored Files")
 
165
  refresh_button = gr.Button("Refresh File List")
 
166
  file_df = gr.DataFrame(
167
  value=get_files_df,
168
  headers=["Stored Files"],
169
  interactive=True
170
  )
 
171
  selected_file_text = gr.Textbox(
172
  label="Selected File",
173
  interactive=False,
174
  placeholder="Click on a file above to select it"
175
  )
 
176
  delete_button = gr.Button("🗑️ Delete Selected File", variant="stop")
177
  delete_status = gr.Markdown("")
178
 
 
189
  outputs=[file_df]
190
  )
191
 
 
 
192
  file_df.select(
193
  fn=handle_file_selection,
 
194
  outputs=[selected_file_text]
195
  )
196