ZacBl commited on
Commit
071bde7
·
verified ·
1 Parent(s): d35d54c

Upload 5 files

Browse files
Files changed (4) hide show
  1. app.py +12 -14
  2. doc_preprocessing.py +84 -41
  3. pyproject.toml +1 -0
  4. uv.lock +15 -0
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import streamlit as st
 
2
  import re
3
  import numpy as np
4
  from doc_preprocessing import process_files, get_embeddings
@@ -41,23 +42,20 @@ def process_query(query):
41
  return results
42
 
43
  def normalize_line_breaks(text):
44
- # Replace all \r\n (Windows line endings) with \n
45
- text = text.replace('\r\n', '\n')
46
- # Replace multiple \n with a single token to preserve paragraph breaks
47
- text = re.sub(r'\n{2,}', '<PARA_BREAK>', text)
48
- # Replace remaining single \n (i.e. line breaks) with double \n
49
- text = text.replace('\n', ' \n\n')
50
- # Restore original paragraph breaks
51
- text = text.replace('<PARA_BREAK>', ' \n\n')
52
  return text
53
 
54
  def display_results(results):
55
- for result in results:
56
- st.subheader("Answer")
57
- st.subheader("Source")
58
- st.write(f"File: {result['file_name']}, Chunk: {result['chunk_index']}")
59
  st.subheader("Citations depuis le document :")
60
- st.markdown(normalize_line_breaks(result["chunk_text"]))
61
- print(normalize_line_breaks(result["chunk_text"]))
 
 
62
  if __name__ == "__main__":
63
  main()
 
1
  import streamlit as st
2
+ from st_copy_to_clipboard import st_copy_to_clipboard
3
  import re
4
  import numpy as np
5
  from doc_preprocessing import process_files, get_embeddings
 
42
  return results
43
 
44
  def normalize_line_breaks(text):
45
+ # text = text.replace("\n", " \n ")
46
+ # text = text.replace('\n', ' \n ')
47
+ text = text.replace("\\n", " \n ")
48
+
 
 
 
 
49
  return text
50
 
51
  def display_results(results):
52
+ for i, result in enumerate(results):
53
+ st.subheader(f"Réponse {i} :")
54
+ st.write(f"Source File: {result['file_name']}, Chunk: {result['chunk_index']}")
 
55
  st.subheader("Citations depuis le document :")
56
+ st.write(normalize_line_breaks(result["chunk_text"]))
57
+ st_copy_to_clipboard(normalize_line_breaks(result["chunk_text"]))
58
+
59
+
60
  if __name__ == "__main__":
61
  main()
doc_preprocessing.py CHANGED
@@ -1,3 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import pypdf
2
  from docx import Document
3
  from transformers.pipelines import pipeline
@@ -73,50 +140,26 @@ def get_embeddings(texts)-> np.ndarray:
73
  st.error(f"Error generating embeddings: {e}")
74
  return []
75
 
76
- def process_files(uploaded_files):
77
  all_chunks = []
78
  all_embeddings = []
79
  chunks_metadata = []
80
 
81
- for uploaded_file in uploaded_files:
82
- # Create a temporary file within a writable directory
83
- # Hugging Face Spaces usually allows writing to /tmp/ or your app's directory
84
- with tempfile.NamedTemporaryFile(delete=False, suffix=f".{uploaded_file.type.split('/')[-1]}") as temp_file:
85
- temp_file.write(uploaded_file.getvalue())
86
- temp_file_path = temp_file.name
87
-
88
- try:
89
- # Now, use temp_file_path to process the file
90
- # Your existing process_files logic would go here,
91
- # reading from temp_file_path
92
- st.write(f"Processing file: {temp_file_path}")
93
- # Example: Replace this with your actual processing logic
94
- # For PDF/Word, you'd likely use a library like pypdf, python-docx, or langchain loaders
95
- if uploaded_file.type == "application/pdf":
96
- # Example for PDF:
97
- # from pypdf import PdfReader
98
- # reader = PdfReader(temp_file_path)
99
- # text = ""
100
- # for page in reader.pages:
101
- # text += page.extract_text() + "\n"
102
- pass # Replace with actual PDF processing
103
- elif uploaded_file.type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
104
- # Example for DOCX:
105
- # from docx import Document
106
- # document = Document(temp_file_path)
107
- # text = ""
108
- # for paragraph in document.paragraphs:
109
- # text += paragraph.text + "\n"
110
- pass # Replace with actual DOCX processing
111
-
112
- # Placeholder for chunking, embedding, and metadata extraction
113
- # (You'd replace this with your actual logic based on the processed file content)
114
- all_chunks.append(f"Content from {uploaded_file.name}")
115
- all_embeddings.append([0.1, 0.2, 0.3]) # Replace with actual embeddings
116
- chunks_metadata.append({"filename": uploaded_file.name})
117
-
118
- finally:
119
- # Clean up the temporary file
120
- os.remove(temp_file_path)
121
 
 
 
 
 
122
  return all_chunks, all_embeddings, chunks_metadata
 
 
1
+ # from pypdf import PdfReader
2
+ # import docx
3
+ # from transformers.pipelines import pipeline
4
+ # import streamlit as st
5
+
6
+ # def extract_text(file):
7
+ # text = ""
8
+ # if file.name.endswith(".pdf"):
9
+ # try:
10
+ # reader = PdfReader(file)
11
+ # for page in reader.pages:
12
+ # text += page.extract_text() + "\n"
13
+ # except Exception as e:
14
+ # st.error(f"Error reading PDF {file.name}: {e}")
15
+ # return ""
16
+ # elif file.name.endswith(".docx"):
17
+ # try:
18
+ # document = docx.Document(file)
19
+ # for paragraph in document.paragraphs:
20
+ # text += paragraph.text + "\n"
21
+ # except Exception as e:
22
+ # st.error(f"Error reading DOCX {file.name}: {e}")
23
+ # return ""
24
+ # return text
25
+
26
+ # def chunk_text(text, chunk_size=500, overlap=50):
27
+ # chunks = []
28
+ # start = 0
29
+ # while start < len(text):
30
+ # end = start + chunk_size
31
+ # chunk = text[start:end]
32
+ # chunks.append(chunk)
33
+ # start = end - overlap
34
+ # return chunks
35
+
36
+ # def get_embeddings(texts):
37
+ # try:
38
+ # embedding_model = pipeline(
39
+ # 'document-question-answering',
40
+ # "sentence-transformers/all-MiniLM-L6-v2"
41
+ # ) # Example model
42
+ # embeddings = embedding_model(texts)
43
+ # return embeddings
44
+ # except Exception as e:
45
+ # st.error(f"Error generating embeddings: {e}")
46
+ # return []
47
+
48
+ # def process_files(files):
49
+ # all_chunks = []
50
+ # all_embeddings = []
51
+ # chunks_metadata = []
52
+
53
+ # for file in files:
54
+ # text = extract_text(file)
55
+ # if not text: # Skip files that failed to process
56
+ # continue
57
+ # chunks = chunk_text(text)
58
+ # embeddings = get_embeddings(chunks)
59
+ # if not embeddings: # Skip files that failed to embed
60
+ # continue
61
+
62
+ # all_chunks.extend(chunks)
63
+ # all_embeddings.extend(embeddings)
64
+ # for i, chunk in enumerate(chunks):
65
+ # chunks_metadata.append({"file_name": file.name, "chunk_index": i})
66
+ # print(f"Processed {len(files)} files, {len(all_chunks)} chunks generated.")
67
+ # return all_chunks, all_embeddings, chunks_metadata
68
  import pypdf
69
  from docx import Document
70
  from transformers.pipelines import pipeline
 
140
  st.error(f"Error generating embeddings: {e}")
141
  return []
142
 
143
+ def process_files(files):
144
  all_chunks = []
145
  all_embeddings = []
146
  chunks_metadata = []
147
 
148
+ for file in files:
149
+ print(f"Processing file: {file.name if hasattr(file, 'name') else os.path.basename(file)}")
150
+ text = extract_text(file)
151
+ if not text: # Skip files that failed to process
152
+ print(f"Skipping file {file.name if hasattr(file, 'name') else os.path.basename(file)} due to extraction error.")
153
+ continue
154
+ print(f"Chunking text...{file.name if hasattr(file, 'name') else os.path.basename(file)}\n")
155
+ chunks = chunk_text(text)
156
+ embeddings = get_embeddings(chunks)
157
+ # if not embeddings: # Skip files that failed to embed
158
+ # continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
 
160
+ all_chunks.extend(chunks)
161
+ all_embeddings.extend(embeddings)
162
+ for i, chunk in enumerate(chunks):
163
+ chunks_metadata.append({"file_name": file.name if hasattr(file, 'name') else os.path.basename(file), "chunk_index": i})
164
  return all_chunks, all_embeddings, chunks_metadata
165
+
pyproject.toml CHANGED
@@ -12,6 +12,7 @@ dependencies = [
12
  "pypdf>=5.5.0",
13
  "python-docx>=1.1.2",
14
  "sentence-transformers>=4.1.0",
 
15
  "streamlit>=1.45.1",
16
  "torch==2.2.0",
17
  "transformers>=4.51.3",
 
12
  "pypdf>=5.5.0",
13
  "python-docx>=1.1.2",
14
  "sentence-transformers>=4.1.0",
15
+ "st-copy-to-clipboard>=0.1.6",
16
  "streamlit>=1.45.1",
17
  "torch==2.2.0",
18
  "transformers>=4.51.3",
uv.lock CHANGED
@@ -477,6 +477,7 @@ dependencies = [
477
  { name = "pypdf" },
478
  { name = "python-docx" },
479
  { name = "sentence-transformers" },
 
480
  { name = "streamlit" },
481
  { name = "torch" },
482
  { name = "transformers" },
@@ -491,6 +492,7 @@ requires-dist = [
491
  { name = "pypdf", specifier = ">=5.5.0" },
492
  { name = "python-docx", specifier = ">=1.1.2" },
493
  { name = "sentence-transformers", specifier = ">=4.1.0" },
 
494
  { name = "streamlit", specifier = ">=1.45.1" },
495
  { name = "torch", specifier = "==2.2.0" },
496
  { name = "transformers", specifier = ">=4.51.3" },
@@ -1228,6 +1230,19 @@ wheels = [
1228
  { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" },
1229
  ]
1230
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1231
  [[package]]
1232
  name = "streamlit"
1233
  version = "1.45.1"
 
477
  { name = "pypdf" },
478
  { name = "python-docx" },
479
  { name = "sentence-transformers" },
480
+ { name = "st-copy-to-clipboard" },
481
  { name = "streamlit" },
482
  { name = "torch" },
483
  { name = "transformers" },
 
492
  { name = "pypdf", specifier = ">=5.5.0" },
493
  { name = "python-docx", specifier = ">=1.1.2" },
494
  { name = "sentence-transformers", specifier = ">=4.1.0" },
495
+ { name = "st-copy-to-clipboard", specifier = ">=0.1.6" },
496
  { name = "streamlit", specifier = ">=1.45.1" },
497
  { name = "torch", specifier = "==2.2.0" },
498
  { name = "transformers", specifier = ">=4.51.3" },
 
1230
  { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" },
1231
  ]
1232
 
1233
+ [[package]]
1234
+ name = "st-copy-to-clipboard"
1235
+ version = "0.1.6"
1236
+ source = { registry = "https://pypi.org/simple" }
1237
+ dependencies = [
1238
+ { name = "jinja2" },
1239
+ { name = "streamlit" },
1240
+ ]
1241
+ sdist = { url = "https://files.pythonhosted.org/packages/3d/6c/bb8ba23b226259974c3a57d122ceccdfd298e6fc3dd9da193716577c8042/st-copy-to-clipboard-0.1.6.tar.gz", hash = "sha256:76634e74384335f64d80469bfe2ca31d15806a140cb89490321764b235acaae6", size = 5046, upload-time = "2024-03-30T16:41:11.581Z" }
1242
+ wheels = [
1243
+ { url = "https://files.pythonhosted.org/packages/40/9b/aca2dfca2bebe2850f83bb0536bfb8836ad39ec7f8086299ae8a60c41558/st_copy_to_clipboard-0.1.6-py3-none-any.whl", hash = "sha256:2a3eed0beb550548b04ac8a4e12c0fb09726654a15c302acb035454948b01cd1", size = 6024, upload-time = "2024-03-30T16:41:09.414Z" },
1244
+ ]
1245
+
1246
  [[package]]
1247
  name = "streamlit"
1248
  version = "1.45.1"