Spaces:
Paused
Paused
Flavio Casadei Della Chiesa
commited on
Commit
·
ad14e53
1
Parent(s):
bfe0450
versione aggiornata con ollama
Browse files- app.py +94 -41
- ragpipeline.py +22 -38
- textutils.py +32 -5
app.py
CHANGED
|
@@ -1,18 +1,12 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
-
from ragpipeline import (RAGPipeline,Retriever,
|
| 3 |
import tempfile
|
| 4 |
import pandas as pd
|
| 5 |
-
from textutils import ParagraphDocumentProcessor,
|
| 6 |
from HFChatbot import HFBot
|
| 7 |
import os
|
| 8 |
|
| 9 |
-
|
| 10 |
def main():
|
| 11 |
-
UPLOAD_DIR = "/tmp/"
|
| 12 |
-
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
| 13 |
-
|
| 14 |
-
codice_tabella = f"<table><tr><td>💡AURA:</td><td> AI-Driven Unified Regulatory Audit</td></tr></table>"
|
| 15 |
-
st.markdown(codice_tabella, unsafe_allow_html=True)
|
| 16 |
|
| 17 |
if "faiss_builder" not in st.session_state:
|
| 18 |
ragpipeline = RAGPipeline(numero_frammenti=10)
|
|
@@ -26,47 +20,100 @@ def main():
|
|
| 26 |
if "indice_creato" not in st.session_state:
|
| 27 |
st.session_state["indice_creato"] = False
|
| 28 |
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
modelliOllama = [
|
| 37 |
-
|
| 38 |
-
'deepseek-r1:1.5b',
|
| 39 |
-
'qwen2.5:7b',
|
| 40 |
-
'deepseek-r1:7b',
|
| 41 |
-
'llama3.2:3b',
|
| 42 |
'Almawave/Velvet:2B',
|
| 43 |
'Almawave/Velvet:14b',
|
| 44 |
-
'
|
| 45 |
-
"vaiton/minerva",
|
| 46 |
-
'qwen2.5:0.5b',
|
| 47 |
-
'qwen3:4b',
|
| 48 |
-
'minerva',
|
| 49 |
-
'nemo',
|
| 50 |
-
'deepseek-r1:14b',
|
| 51 |
'qwen3:14b',
|
| 52 |
-
'
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
st.write(f"Hai selezionato: {modello_scelto}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
|
|
|
|
| 60 |
st.title("Suddivisione in paragrafi")
|
| 61 |
docprocessor_options = {
|
| 62 |
-
"
|
| 63 |
-
"
|
|
|
|
| 64 |
}
|
| 65 |
selected_docprocessor = st.selectbox("Divisione in paragrafi", docprocessor_options.keys())
|
| 66 |
-
docprocessor = docprocessor_options[selected_docprocessor]
|
| 67 |
st.write(f"Hai selezionato: **{selected_docprocessor}**")
|
| 68 |
|
| 69 |
-
|
|
|
|
|
|
|
| 70 |
if not st.session_state["indice_creato"]:
|
| 71 |
st.subheader("Carica l'atto principale (Determinazione)")
|
| 72 |
|
|
@@ -125,9 +172,15 @@ def main():
|
|
| 125 |
if domanda.strip().upper() == "FINE":
|
| 126 |
st.stop()
|
| 127 |
|
| 128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
-
cb = HFBot(model_name=modello_scelto)
|
| 131 |
|
| 132 |
ret = Retriever(
|
| 133 |
indice=ragpipeline.indice,
|
|
@@ -140,7 +193,7 @@ def main():
|
|
| 140 |
|
| 141 |
ret.esegui_query(top_k=3)
|
| 142 |
|
| 143 |
-
risposta =
|
| 144 |
query=domanda,
|
| 145 |
relevant_docs=ret.passaggi_rilevanti,
|
| 146 |
attributi_frammenti_rilevanti=ret.attributi_rilevanti,
|
|
@@ -158,7 +211,7 @@ def main():
|
|
| 158 |
id_frammenti_recuperati = ":".join(sorted(set(elemento['id'] for elemento in ret.attributi_rilevanti)))
|
| 159 |
dump = {
|
| 160 |
'timestamp': ragpipeline.timestamp,
|
| 161 |
-
"modello":
|
| 162 |
"documenti": st.session_state.get("main_pdf_nome", "non disponibile"),
|
| 163 |
"file_recuperati": "",
|
| 164 |
"file_gold": "",
|
|
@@ -167,7 +220,7 @@ def main():
|
|
| 167 |
"domanda":domanda,
|
| 168 |
"istruzioni":istruzione,
|
| 169 |
"risposta_gold": " ",
|
| 170 |
-
"risposta":
|
| 171 |
|
| 172 |
RAGPipeline.dump_excel(dizionario=dump,filename="dumpChatbot.xlsx")
|
| 173 |
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
+
from ragpipeline import (RAGPipeline,Retriever,OllamaChatbot)
|
| 3 |
import tempfile
|
| 4 |
import pandas as pd
|
| 5 |
+
from textutils import ParagraphDocumentProcessor, SmallFragmentDocumentProcessor,WholeTextDocumentProcessor
|
| 6 |
from HFChatbot import HFBot
|
| 7 |
import os
|
| 8 |
|
|
|
|
| 9 |
def main():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
if "faiss_builder" not in st.session_state:
|
| 12 |
ragpipeline = RAGPipeline(numero_frammenti=10)
|
|
|
|
| 20 |
if "indice_creato" not in st.session_state:
|
| 21 |
st.session_state["indice_creato"] = False
|
| 22 |
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
modelliVelvet = [
|
| 26 |
+
'Almawave/Velvet-2B',
|
| 27 |
+
'Almawave/Velvet-14B',
|
| 28 |
+
|
| 29 |
]
|
| 30 |
+
modelliLLM = [
|
| 31 |
+
'Almawave/Velvet-2B',
|
| 32 |
+
'Almawave/Velvet-14B',
|
| 33 |
+
|
| 34 |
+
'mistralai/Mistral-7B-Instruct-v0.1',
|
| 35 |
+
'Qwen/Qwen2.5-1.5B',
|
| 36 |
+
'BlackBeenie/Qwen3-30B-A3B-Q4_K_M-GGUF'
|
| 37 |
+
]
|
| 38 |
+
|
| 39 |
modelliOllama = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
'Almawave/Velvet:2B',
|
| 41 |
'Almawave/Velvet:14b',
|
| 42 |
+
'llama3.1:8b-instruct-q4_K_M',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
'qwen3:14b',
|
| 44 |
+
'qwen3:30b-a3b'
|
| 45 |
+
|
| 46 |
+
]
|
| 47 |
+
## indica se sono sullo spaces di HF (deve essere inserita uan variabile I_AM_ON_HF)
|
| 48 |
+
sono_su_hf =os.environ.get('I_AM_ON_HF', False)
|
| 49 |
+
## se sono su Hugginh Face non uso ollama
|
| 50 |
+
if not sono_su_hf:
|
| 51 |
+
modelliLLM.append("----- USARE SOLO CON OLLAMA -----")
|
| 52 |
+
for mollama in modelliOllama:
|
| 53 |
+
modelliLLM.append(mollama)
|
| 54 |
+
|
| 55 |
+
UPLOAD_DIR="/tmp/"
|
| 56 |
+
if "indice_creato" not in st.session_state:
|
| 57 |
+
st.session_state["indice_creato"] = False
|
| 58 |
+
if "faiss_builder" not in st.session_state:
|
| 59 |
+
ragpipeline = RAGPipeline( )
|
| 60 |
+
codice_tabella = f"<table><tr><td>💡AURA:</td><td> AI-Utilizzata per la Regolarità Amministrativa</td></tr></table>"
|
| 61 |
+
st.markdown(codice_tabella, unsafe_allow_html=True)
|
| 62 |
+
st.title("Cosa è AURA?")
|
| 63 |
+
st.write("""
|
| 64 |
+
Questo strumento, attualmente in fase sperimentale, è stato sviluppato per eseguire controlli di
|
| 65 |
+
regolarità amministrativa ai sensi dell’art. 147-bis del D.Lgs. 267/2000,
|
| 66 |
+
con riferimento agli atti relativi al PNRR.
|
| 67 |
+
È in continua evoluzione. Per testarne il funzionamento, è sufficiente caricare un file PDF contenente
|
| 68 |
+
una determinazione dirigenziale.
|
| 69 |
+
AURA analizzerà il documento e fornirà risposte basate su una check-list predefinita di domande.
|
| 70 |
+
<p>
|
| 71 |
+
AURA è un sistema RAG <em>Retrieval Augmented Generation</em> che dato un atto amministrativo ed eventuali allegati
|
| 72 |
+
ed una o più domande contenute in una <em>check list</em> di regolarità amministrativa, ricerca nei documenti
|
| 73 |
+
i frammenti rilevanti per la domanda; questi assieme ad alcune istruzioni (<em>In-context learning</em>)
|
| 74 |
+
vengono inviati ad un LLM <em>Large Language Model</em>
|
| 75 |
+
al fine di generare una risposta corretta e coerente con i frammenti rilevanti.
|
| 76 |
+
</p>
|
| 77 |
+
<p>
|
| 78 |
+
Questa versione di AURA utilizza Velvet:2B di Almawave, rilasciato sotto
|
| 79 |
+
<a href="https://www.apache.org/licenses/LICENSE-2.0">Licenza Apache 2.0</a> come LLM.
|
| 80 |
+
</p>
|
| 81 |
+
|
| 82 |
+
""" , unsafe_allow_html=True)
|
| 83 |
+
|
| 84 |
+
st.warning("Attenzione questo tool è sperimentale. AURA può sbagliare")
|
| 85 |
+
if not sono_su_hf:
|
| 86 |
+
modello_scelto = st.selectbox("Seleziona un modello:", modelliLLM, index=0)
|
| 87 |
+
else:
|
| 88 |
+
modello_scelto = st.selectbox("Seleziona un modello:", modelliVelvet, index=0)
|
| 89 |
st.write(f"Hai selezionato: {modello_scelto}")
|
| 90 |
+
|
| 91 |
+
if not sono_su_hf:
|
| 92 |
+
st.title("Generazione testo")
|
| 93 |
+
generatoriLLM = {
|
| 94 |
+
'Hugging Face Transformers': "HF",
|
| 95 |
+
'Ollama in locale' :"OLLAMA"
|
| 96 |
+
}
|
| 97 |
+
selected_generator= st.selectbox("Scegli lo strumento per interagire con LLM", generatoriLLM.keys())
|
| 98 |
+
chiave_LLM=generatoriLLM[selected_generator]
|
| 99 |
+
st.write(f"Hai selezionato {selected_generator}")
|
| 100 |
+
else:
|
| 101 |
+
chiave_LLM="HF"
|
| 102 |
|
| 103 |
+
|
| 104 |
st.title("Suddivisione in paragrafi")
|
| 105 |
docprocessor_options = {
|
| 106 |
+
"Small Fragments (più veloce ma poco preciso)": SmallFragmentDocumentProcessor(),
|
| 107 |
+
"ParagraphDocumentProcessor (più lento e leggermente più preciso)": ParagraphDocumentProcessor(),
|
| 108 |
+
"WholeText (viene generato un solo grande frammento, può confondere gli LLM)": WholeTextDocumentProcessor(),
|
| 109 |
}
|
| 110 |
selected_docprocessor = st.selectbox("Divisione in paragrafi", docprocessor_options.keys())
|
| 111 |
+
ragpipeline.docprocessor = docprocessor_options[selected_docprocessor]
|
| 112 |
st.write(f"Hai selezionato: **{selected_docprocessor}**")
|
| 113 |
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
|
| 117 |
if not st.session_state["indice_creato"]:
|
| 118 |
st.subheader("Carica l'atto principale (Determinazione)")
|
| 119 |
|
|
|
|
| 172 |
if domanda.strip().upper() == "FINE":
|
| 173 |
st.stop()
|
| 174 |
|
| 175 |
+
if chiave_LLM == "HF":
|
| 176 |
+
LLM=HFBot(model_name=modello_scelto)
|
| 177 |
+
elif chiave_LLM == "OLLAMA":
|
| 178 |
+
LLM=OllamaChatbot(model_name="flaollama",model_orig=modello_scelto)
|
| 179 |
+
else:
|
| 180 |
+
LLM=HFBot(model_name=modello_scelto)
|
| 181 |
+
|
| 182 |
+
|
| 183 |
|
|
|
|
| 184 |
|
| 185 |
ret = Retriever(
|
| 186 |
indice=ragpipeline.indice,
|
|
|
|
| 193 |
|
| 194 |
ret.esegui_query(top_k=3)
|
| 195 |
|
| 196 |
+
risposta = LLM.generate(
|
| 197 |
query=domanda,
|
| 198 |
relevant_docs=ret.passaggi_rilevanti,
|
| 199 |
attributi_frammenti_rilevanti=ret.attributi_rilevanti,
|
|
|
|
| 211 |
id_frammenti_recuperati = ":".join(sorted(set(elemento['id'] for elemento in ret.attributi_rilevanti)))
|
| 212 |
dump = {
|
| 213 |
'timestamp': ragpipeline.timestamp,
|
| 214 |
+
"modello": modello_scelto,
|
| 215 |
"documenti": st.session_state.get("main_pdf_nome", "non disponibile"),
|
| 216 |
"file_recuperati": "",
|
| 217 |
"file_gold": "",
|
|
|
|
| 220 |
"domanda":domanda,
|
| 221 |
"istruzioni":istruzione,
|
| 222 |
"risposta_gold": " ",
|
| 223 |
+
"risposta":LLM.pulisci_risposta(risposta)}
|
| 224 |
|
| 225 |
RAGPipeline.dump_excel(dizionario=dump,filename="dumpChatbot.xlsx")
|
| 226 |
|
ragpipeline.py
CHANGED
|
@@ -118,17 +118,32 @@ class Retriever:
|
|
| 118 |
self.attributi_rilevanti = [self.attributi_frammenti[j] for j in indices[0]] #passaggi rilevanti
|
| 119 |
|
| 120 |
|
|
|
|
| 121 |
class ChatBot:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
def __init__(self,
|
| 123 |
model_name: str = "flaollama",
|
| 124 |
model_orig: str = "mistral" ,
|
| 125 |
model_system=(
|
| 126 |
-
"Sei un esperto di diritto amministrativo che deve eseguire il
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
),
|
| 133 |
dump_filename="dump.csv"
|
| 134 |
):
|
|
@@ -141,31 +156,6 @@ class ChatBot:
|
|
| 141 |
from_=model_orig,
|
| 142 |
system = model_system
|
| 143 |
)
|
| 144 |
-
|
| 145 |
-
def dump_excel(self, dizionario, filename ):
|
| 146 |
-
RAGPipeline.dump_excel(dizionario=dizionario, filename=filename)
|
| 147 |
-
|
| 148 |
-
def dump_csv(self,dizionario):
|
| 149 |
-
"""Salva un dizionario in un file CSV con separatore '|' accodando i dati se il file esiste."""
|
| 150 |
-
file_esiste = os.path.isfile(self.dump_filename)
|
| 151 |
-
|
| 152 |
-
with open(self.dump_filename, mode="a", newline="", encoding="utf-8") as file:
|
| 153 |
-
writer = csv.writer(file, delimiter="|")
|
| 154 |
-
|
| 155 |
-
# Scrive l'intestazione solo se il file viene creato ex novo
|
| 156 |
-
if not file_esiste:
|
| 157 |
-
writer.writerow(dizionario.keys())
|
| 158 |
-
|
| 159 |
-
# Scrive i valori come una nuova riga
|
| 160 |
-
writer.writerow([str(val).replace("\n", "").replace("\r", "").replace("\t", "") for val in dizionario.values()])
|
| 161 |
-
|
| 162 |
-
def pulisci_risposta(self,
|
| 163 |
-
response: str):
|
| 164 |
-
retval=re.sub(r"<think>.*?</think>", "", response, flags=re.DOTALL).strip()
|
| 165 |
-
|
| 166 |
-
retval = retval.replace("\n", " ").replace("\t", " ").replace("|", " ")
|
| 167 |
-
|
| 168 |
-
return retval
|
| 169 |
def chat(self, domanda: str, istruzioni: str = None, frammenti =[]) -> str:
|
| 170 |
prompt = f"ISTRUZIONI: {istruzioni}\n\nCONTESTO:\n" + "\n".join(frammenti) + f"\n\nDOMANDA: {domanda}"
|
| 171 |
|
|
@@ -175,13 +165,7 @@ class ChatBot:
|
|
| 175 |
|
| 176 |
return response["message"]["content"]
|
| 177 |
|
| 178 |
-
def generate(self,
|
| 179 |
-
relevant_docs = [],
|
| 180 |
-
attributi_frammenti_rilevanti = [],
|
| 181 |
-
query="",
|
| 182 |
-
istruzioni :str = None ##togliere
|
| 183 |
-
):
|
| 184 |
-
|
| 185 |
i = 0
|
| 186 |
#print (f"DIMESIONE FILE {len(relevant_files)}")
|
| 187 |
#print (f"DIMESIONE TESTI {len(relevant_docs)}")
|
|
|
|
| 118 |
self.attributi_rilevanti = [self.attributi_frammenti[j] for j in indices[0]] #passaggi rilevanti
|
| 119 |
|
| 120 |
|
| 121 |
+
|
| 122 |
class ChatBot:
|
| 123 |
+
def pulisci_risposta(self,
|
| 124 |
+
response: str):
|
| 125 |
+
retval=re.sub(r"<think>.*?</think>", "", response, flags=re.DOTALL).strip()
|
| 126 |
+
|
| 127 |
+
retval = retval.replace("\n", " ").replace("\t", " ").replace("|", " ")
|
| 128 |
+
|
| 129 |
+
return retval
|
| 130 |
+
|
| 131 |
+
def chat(self, domanda: str, istruzioni: str = None, frammenti =[]) -> str:
|
| 132 |
+
raise NotImplementedError("Questo metodo deve essere implementato nelle sottoclassi.")
|
| 133 |
+
def generate(self, relevant_docs = [], attributi_frammenti_rilevanti = [], query="", istruzioni :str = None ) -> str:
|
| 134 |
+
raise NotImplementedError("Questo metodo deve essere implementato nelle sottoclassi.")
|
| 135 |
+
|
| 136 |
+
class OllamaChatbot(ChatBot):
|
| 137 |
def __init__(self,
|
| 138 |
model_name: str = "flaollama",
|
| 139 |
model_orig: str = "mistral" ,
|
| 140 |
model_system=(
|
| 141 |
+
"""Sei un esperto di diritto amministrativo che deve eseguire il
|
| 142 |
+
controllo di regolarità amministrativa su un atto amministrativo di un comune italiano.
|
| 143 |
+
Ti verranno forniti un atto amministrativo (determinazione dirigenziale) ed eventuali allegati, questi sono forniti come frammenti rilevanti.
|
| 144 |
+
Utilizza solamente i frammenti che ti verranno inviati.
|
| 145 |
+
Rispondi in Italiano usando al massimo 50 parole.
|
| 146 |
+
Basati esclusivamente sul seguente testo: """
|
| 147 |
),
|
| 148 |
dump_filename="dump.csv"
|
| 149 |
):
|
|
|
|
| 156 |
from_=model_orig,
|
| 157 |
system = model_system
|
| 158 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
def chat(self, domanda: str, istruzioni: str = None, frammenti =[]) -> str:
|
| 160 |
prompt = f"ISTRUZIONI: {istruzioni}\n\nCONTESTO:\n" + "\n".join(frammenti) + f"\n\nDOMANDA: {domanda}"
|
| 161 |
|
|
|
|
| 165 |
|
| 166 |
return response["message"]["content"]
|
| 167 |
|
| 168 |
+
def generate(self, relevant_docs = [], attributi_frammenti_rilevanti = [], query="", istruzioni :str = None ):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
i = 0
|
| 170 |
#print (f"DIMESIONE FILE {len(relevant_files)}")
|
| 171 |
#print (f"DIMESIONE TESTI {len(relevant_docs)}")
|
textutils.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
import pandas as pd
|
| 3 |
import re
|
| 4 |
import PyPDF2
|
| 5 |
-
|
| 6 |
import unicodedata
|
| 7 |
|
| 8 |
class DocumentProcessor:
|
|
@@ -57,8 +57,7 @@ class DocumentProcessor:
|
|
| 57 |
for page in reader.pages:
|
| 58 |
page_text = page.extract_text() or ""
|
| 59 |
full_text += page_text
|
| 60 |
-
|
| 61 |
-
full_text = full_text.replace(acr,espansione)
|
| 62 |
return full_text
|
| 63 |
|
| 64 |
def chunk_text_by_paragraph(self,text: str):
|
|
@@ -146,15 +145,43 @@ class ParagraphDocumentProcessor(DocumentProcessor):
|
|
| 146 |
|
| 147 |
class WholeTextDocumentProcessor(DocumentProcessor) :
|
| 148 |
def scomponi_in_frammenti(self, testo:str, numero_frammenti: int = 1):
|
| 149 |
-
print("WholeTextDocumeptProcessor !!!")
|
| 150 |
return [testo]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
|
| 152 |
|
| 153 |
|
| 154 |
|
| 155 |
-
|
| 156 |
|
| 157 |
|
| 158 |
|
|
|
|
|
|
|
| 159 |
|
| 160 |
|
|
|
|
| 2 |
import pandas as pd
|
| 3 |
import re
|
| 4 |
import PyPDF2
|
| 5 |
+
|
| 6 |
import unicodedata
|
| 7 |
|
| 8 |
class DocumentProcessor:
|
|
|
|
| 57 |
for page in reader.pages:
|
| 58 |
page_text = page.extract_text() or ""
|
| 59 |
full_text += page_text
|
| 60 |
+
|
|
|
|
| 61 |
return full_text
|
| 62 |
|
| 63 |
def chunk_text_by_paragraph(self,text: str):
|
|
|
|
| 145 |
|
| 146 |
class WholeTextDocumentProcessor(DocumentProcessor) :
|
| 147 |
def scomponi_in_frammenti(self, testo:str, numero_frammenti: int = 1):
|
|
|
|
| 148 |
return [testo]
|
| 149 |
+
|
| 150 |
+
class SmallFragmentDocumentProcessor(DocumentProcessor):
|
| 151 |
+
def scomponi_in_frammenti(self, testo:str, numero_frammenti: int = 1):
|
| 152 |
+
return self.dividi_testo_in_frammenti(testo)
|
| 153 |
+
|
| 154 |
+
def dividi_testo_in_frammenti(self,testo, lunghezza_massima=1000):
|
| 155 |
+
frammenti = []
|
| 156 |
+
inizio = 0
|
| 157 |
+
|
| 158 |
+
while inizio < len(testo):
|
| 159 |
+
fine = inizio + lunghezza_massima
|
| 160 |
+
|
| 161 |
+
# Se siamo alla fine del testo, aggiungiamo e usciamo
|
| 162 |
+
if fine >= len(testo):
|
| 163 |
+
frammenti.append(testo[inizio:].strip())
|
| 164 |
+
break
|
| 165 |
+
|
| 166 |
+
# Cerca l'ultimo spazio prima del limite per evitare di tagliare la parola
|
| 167 |
+
fine_corretto = testo.rfind(" ", inizio, fine)
|
| 168 |
+
if fine_corretto == -1 or fine_corretto <= inizio:
|
| 169 |
+
# Se non troviamo spazi, tagliamo brutalmente
|
| 170 |
+
fine_corretto = fine
|
| 171 |
+
|
| 172 |
+
frammento = testo[inizio:fine_corretto].strip()
|
| 173 |
+
frammenti.append(frammento)
|
| 174 |
+
inizio = fine_corretto
|
| 175 |
+
|
| 176 |
+
return frammenti
|
| 177 |
|
| 178 |
|
| 179 |
|
| 180 |
|
|
|
|
| 181 |
|
| 182 |
|
| 183 |
|
| 184 |
+
|
| 185 |
+
|
| 186 |
|
| 187 |
|