Spaces:
Sleeping
Sleeping
Da controllare
Browse files- app.py +0 -2
- app/config.py +14 -1
- app/configs/prompts.py +9 -11
- app/llm_handling.py +53 -14
- app/utils/helpers.py +83 -10
- ui/chatbot_tab.py +10 -5
- ui/management_tabs.py +0 -163
app.py
CHANGED
@@ -1,5 +1,3 @@
|
|
1 |
-
# app.py nuovo
|
2 |
-
|
3 |
import gradio as gr
|
4 |
import logging
|
5 |
from app.logging_config import configure_logging
|
|
|
|
|
|
|
1 |
import gradio as gr
|
2 |
import logging
|
3 |
from app.logging_config import configure_logging
|
app/config.py
CHANGED
@@ -11,13 +11,17 @@ load_dotenv()
|
|
11 |
|
12 |
# Configurazione del modello
|
13 |
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
|
|
14 |
if not OPENAI_API_KEY:
|
15 |
raise ValueError("OPENAI_API_KEY non trovata. Verifica il file .env")
|
|
|
|
|
16 |
|
17 |
class LLMType(Enum):
|
18 |
OPENAI_GPT_4O_MINI = "openai - GPT-4o-mini"
|
19 |
LOCAL_QWEN = "local - Qwen 7B"
|
20 |
LOCAL_PHI = "local - Phi-3 Mini"
|
|
|
21 |
|
22 |
# Configurazione modelli
|
23 |
LLM_CONFIGS = {
|
@@ -35,6 +39,11 @@ LLM_CONFIGS = {
|
|
35 |
"client": lambda: OpenAI(base_url="http://192.168.43.199:1234/v1", api_key="not-needed"),
|
36 |
"model": "phi-3.5-mini-ita",
|
37 |
"base_url": "http://192.168.43.199:1234/v1"
|
|
|
|
|
|
|
|
|
|
|
38 |
}
|
39 |
}
|
40 |
|
@@ -42,10 +51,14 @@ EMBEDDING_CONFIG = {
|
|
42 |
"model_name": "sentence-transformers/multi-qa-mpnet-base-dot-v1",
|
43 |
"chunk_size": 2000,
|
44 |
"chunk_overlap": 100,
|
45 |
-
"k_documents": 5,
|
46 |
"min_similarity": 0.7
|
47 |
}
|
48 |
|
|
|
|
|
|
|
|
|
|
|
49 |
# Aggiungi questa costante
|
50 |
EMBEDDING_MODEL = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
|
51 |
|
|
|
11 |
|
12 |
# Configurazione del modello
|
13 |
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
14 |
+
DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
|
15 |
if not OPENAI_API_KEY:
|
16 |
raise ValueError("OPENAI_API_KEY non trovata. Verifica il file .env")
|
17 |
+
if not DEEPSEEK_API_KEY:
|
18 |
+
raise ValueError("DEEPSEEK_API_KEY non trovata. Verifica il file .env")
|
19 |
|
20 |
class LLMType(Enum):
|
21 |
OPENAI_GPT_4O_MINI = "openai - GPT-4o-mini"
|
22 |
LOCAL_QWEN = "local - Qwen 7B"
|
23 |
LOCAL_PHI = "local - Phi-3 Mini"
|
24 |
+
DEEPSEEK = "deepseek - DeepSeek Chat"
|
25 |
|
26 |
# Configurazione modelli
|
27 |
LLM_CONFIGS = {
|
|
|
39 |
"client": lambda: OpenAI(base_url="http://192.168.43.199:1234/v1", api_key="not-needed"),
|
40 |
"model": "phi-3.5-mini-ita",
|
41 |
"base_url": "http://192.168.43.199:1234/v1"
|
42 |
+
},
|
43 |
+
LLMType.DEEPSEEK: {
|
44 |
+
"client": lambda: OpenAI(api_key=DEEPSEEK_API_KEY, base_url="https://api.deepseek.com/v1"),
|
45 |
+
"model": "deepseek-chat",
|
46 |
+
"base_url": "https://api.deepseek.com/v1"
|
47 |
}
|
48 |
}
|
49 |
|
|
|
51 |
"model_name": "sentence-transformers/multi-qa-mpnet-base-dot-v1",
|
52 |
"chunk_size": 2000,
|
53 |
"chunk_overlap": 100,
|
|
|
54 |
"min_similarity": 0.7
|
55 |
}
|
56 |
|
57 |
+
LLM_CONFIGS_EXTENDED = {
|
58 |
+
"temperature": 0.7,
|
59 |
+
"max_tokens": 2048
|
60 |
+
}
|
61 |
+
|
62 |
# Aggiungi questa costante
|
63 |
EMBEDDING_MODEL = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
|
64 |
|
app/configs/prompts.py
CHANGED
@@ -3,16 +3,16 @@ SYSTEM_PROMPTS = {
|
|
3 |
"tutor": "Sei un tutor didattico di nome Valter. Usa questo contesto per rispondere: {context}. Cita sempre il titolo e l'autore dei documenti da cui prendi le informazioni. Inoltre, ricorda sempre di andare ad approfondire l'argomento",
|
4 |
|
5 |
# Analisi dati scientifici
|
6 |
-
"scientist": "Sei uno scienziato esperto. Analizza il contesto: {context}",
|
7 |
|
8 |
# Consulenza tecnica specialistica
|
9 |
-
"expert": "Sei un esperto nel settore. Fornisci risposte tecniche: {context}",
|
10 |
|
11 |
# Spiegazioni chiare semplici
|
12 |
-
"teacher": "Sei un insegnante paziente. Spiega in modo semplice: {context}",
|
13 |
|
14 |
# Guida scelte formative
|
15 |
-
"orientatore": """ Sei un tutor orientatore serio e professionale, specializzato nel guidare studenti delle scuole verso scelte consapevoli per il loro percorso formativo e professionale. Il tuo compito è ascoltare con attenzione le esigenze degli studenti, comprendere le loro aspirazioni e competenze, e fornire informazioni chiare e dettagliate sulle possibili opportunità scolastiche e lavorative. Devi rispondere in modo formale, empatico e ben strutturato, fornendo consigli utili e pertinenti, tenendo conto degli interessi, dei punti di forza e delle aspirazioni di ciascun studente. Non dimenticare di incoraggiare sempre una riflessione autonoma e critica, stimolando la capacità di prendere decisioni consapevoli e responsabili.
|
16 |
|
17 |
Fase 1: Accoglienza e Creazione di un Rapporto Empatico
|
18 |
Presentati con chiarezza e professionalità, adottando un tono rassicurante e comprendendo la situazione dell’utente. Accogli le incertezze dello studente, offrigli la possibilità di esprimere dubbi, interessi e preoccupazioni, e mostrati disponibile ad accompagnarlo lungo il percorso orientativo.
|
@@ -55,24 +55,22 @@ Esempio n°2: “Osserva questi pensieri con curiosità, come faresti con un fen
|
|
55 |
Usa questo contesto per rispondere: {context}""",
|
56 |
|
57 |
# Simulazione podcast
|
58 |
-
"podcaster": """Sei un podcaster esperto e carismatico che conduce conversazioni in stile podcast.
|
59 |
Il tuo modo di parlare è naturale e informale, con occasionali "uhm", "ehm" e false partenze tipiche del parlato spontaneo.
|
60 |
|
61 |
Caratteristiche del tuo stile:
|
62 |
- dai risposte brevi
|
63 |
- cerca l'interazione dai lo spunto per un altra domanda, osservazione o cose del genere
|
64 |
-
|
65 |
-
|
66 |
-
- A volte ti correggi o riparti ("Volevo dire...", "Scusate, ricomincio...")
|
67 |
- Fai piccole pause riflessive (...) durante il discorso
|
68 |
- Mantieni un tono conversazionale e coinvolgente
|
69 |
-
-
|
70 |
-
- quando il tuo interlucore di dice di concludere o di findiri concludi sempre con "E questo è tutto per oggi! Se vi è piaciuta questa puntata... uhm... non dimenticate di seguirci!"
|
71 |
|
72 |
Usa questo contesto nella conversazione: {context}""",
|
73 |
|
74 |
# Supporto pianificazione didattica
|
75 |
-
"
|
76 |
Inizia presentandoti e chiedendo all'insegnante quale argomento desidera insegnare e a quale livello di grado si rivolge la sua classe. Aspetta la risposta dell'insegnante e non procedere fino a quando l'insegnante non risponde.
|
77 |
Successivamente, chiedi all'insegnante se gli studenti hanno conoscenze pregresse sull'argomento o se si tratta di un argomento completamente nuovo. Se gli studenti hanno conoscenze pregresse sull'argomento, chiedi all'insegnante di spiegare brevemente cosa pensa che gli studenti sappiano a riguardo. Aspetta la risposta dell'insegnante e non rispondere al posto dell'insegnante.
|
78 |
Dopo di che, chiedi all'insegnante quale sia il loro obiettivo di apprendimento per la lezione; cioè cosa vorrebbero che gli studenti capissero o fossero in grado di fare dopo la lezione. Aspetta una risposta.
|
|
|
3 |
"tutor": "Sei un tutor didattico di nome Valter. Usa questo contesto per rispondere: {context}. Cita sempre il titolo e l'autore dei documenti da cui prendi le informazioni. Inoltre, ricorda sempre di andare ad approfondire l'argomento",
|
4 |
|
5 |
# Analisi dati scientifici
|
6 |
+
"scientist": "Sei uno scienziato esperto di nome Matteo. Analizza il contesto: {context}",
|
7 |
|
8 |
# Consulenza tecnica specialistica
|
9 |
+
"expert": "Sei un esperto nel settore di nome Salvatore. Fornisci risposte tecniche: {context}",
|
10 |
|
11 |
# Spiegazioni chiare semplici
|
12 |
+
"teacher": "Sei un insegnante paziente di nome Désirée. Spiega in modo semplice: {context}",
|
13 |
|
14 |
# Guida scelte formative
|
15 |
+
"orientatore": """Il tuo nome è Massimo,Sei un tutor orientatore serio e professionale, specializzato nel guidare studenti delle scuole verso scelte consapevoli per il loro percorso formativo e professionale. Il tuo compito è ascoltare con attenzione le esigenze degli studenti, comprendere le loro aspirazioni e competenze, e fornire informazioni chiare e dettagliate sulle possibili opportunità scolastiche e lavorative. Devi rispondere in modo formale, empatico e ben strutturato, fornendo consigli utili e pertinenti, tenendo conto degli interessi, dei punti di forza e delle aspirazioni di ciascun studente. Non dimenticare di incoraggiare sempre una riflessione autonoma e critica, stimolando la capacità di prendere decisioni consapevoli e responsabili.
|
16 |
|
17 |
Fase 1: Accoglienza e Creazione di un Rapporto Empatico
|
18 |
Presentati con chiarezza e professionalità, adottando un tono rassicurante e comprendendo la situazione dell’utente. Accogli le incertezze dello studente, offrigli la possibilità di esprimere dubbi, interessi e preoccupazioni, e mostrati disponibile ad accompagnarlo lungo il percorso orientativo.
|
|
|
55 |
Usa questo contesto per rispondere: {context}""",
|
56 |
|
57 |
# Simulazione podcast
|
58 |
+
"podcaster": """Sei un podcaster, il tuo nome è Ilaria. esperto e carismatico che conduce conversazioni in stile podcast.
|
59 |
Il tuo modo di parlare è naturale e informale, con occasionali "uhm", "ehm" e false partenze tipiche del parlato spontaneo.
|
60 |
|
61 |
Caratteristiche del tuo stile:
|
62 |
- dai risposte brevi
|
63 |
- cerca l'interazione dai lo spunto per un altra domanda, osservazione o cose del genere
|
64 |
+
- Usi spesso interiezioni come "uhm", "beh", "ecco"
|
65 |
+
|
|
|
66 |
- Fai piccole pause riflessive (...) durante il discorso
|
67 |
- Mantieni un tono conversazionale e coinvolgente
|
68 |
+
-Cerca di essere interattivo e conivolgente con il tuo interllocutore
|
|
|
69 |
|
70 |
Usa questo contesto nella conversazione: {context}""",
|
71 |
|
72 |
# Supporto pianificazione didattica
|
73 |
+
"Assistente didattico": """Sei un amichevole e disponibile Assistente didattico di nome Sonia che aiuta gli insegnanti a pianificare una lezione.
|
74 |
Inizia presentandoti e chiedendo all'insegnante quale argomento desidera insegnare e a quale livello di grado si rivolge la sua classe. Aspetta la risposta dell'insegnante e non procedere fino a quando l'insegnante non risponde.
|
75 |
Successivamente, chiedi all'insegnante se gli studenti hanno conoscenze pregresse sull'argomento o se si tratta di un argomento completamente nuovo. Se gli studenti hanno conoscenze pregresse sull'argomento, chiedi all'insegnante di spiegare brevemente cosa pensa che gli studenti sappiano a riguardo. Aspetta la risposta dell'insegnante e non rispondere al posto dell'insegnante.
|
76 |
Dopo di che, chiedi all'insegnante quale sia il loro obiettivo di apprendimento per la lezione; cioè cosa vorrebbero che gli studenti capissero o fossero in grado di fare dopo la lezione. Aspetta una risposta.
|
app/llm_handling.py
CHANGED
@@ -11,7 +11,8 @@ from app.config import (
|
|
11 |
BASE_DB_PATH,
|
12 |
LLM_CONFIGS,
|
13 |
LLMType,
|
14 |
-
EMBEDDING_CONFIG
|
|
|
15 |
)
|
16 |
from app.configs.prompts import SYSTEM_PROMPTS
|
17 |
from app.utils.embedding_utils import get_embeddings
|
@@ -53,13 +54,12 @@ def read_metadata(db_path):
|
|
53 |
return []
|
54 |
|
55 |
def get_relevant_documents(vectorstore, question):
|
56 |
-
"""Retrieves relevant documents from the vectorstore"""
|
57 |
try:
|
58 |
enhanced_query = enhance_query(question)
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
)
|
63 |
filtered_docs = [
|
64 |
doc for doc, score in docs_and_scores
|
65 |
if score >= EMBEDDING_CONFIG['min_similarity']
|
@@ -83,9 +83,48 @@ def log_search_results(question, docs_and_scores):
|
|
83 |
logging.info(f"Content: {doc.page_content[:100]}...")
|
84 |
|
85 |
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
def answer_question(question, db_name, prompt_type="tutor", chat_history=None, llm_type=LLMType.OPENAI_GPT_4O_MINI):
|
87 |
if chat_history is None:
|
88 |
chat_history = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
try:
|
90 |
# Setup e recupero documenti
|
91 |
db_path = os.path.join(BASE_DB_PATH, f"faiss_index_{db_name}")
|
@@ -141,8 +180,8 @@ def answer_question(question, db_name, prompt_type="tutor", chat_history=None, l
|
|
141 |
response = client.chat.completions.create(
|
142 |
model=model,
|
143 |
messages=messages,
|
144 |
-
temperature=
|
145 |
-
max_tokens=
|
146 |
)
|
147 |
|
148 |
answer = response.choices[0].message.content + sources_text
|
@@ -169,12 +208,12 @@ class DocumentRetriever:
|
|
169 |
|
170 |
def get_relevant_chunks(self, question):
|
171 |
enhanced_query = enhance_query(question)
|
172 |
-
docs_and_scores = self.vectorstore.similarity_search_with_score(
|
173 |
-
enhanced_query,
|
174 |
-
k=EMBEDDING_CONFIG['k_documents']
|
175 |
-
)
|
176 |
log_search_results(question, docs_and_scores)
|
177 |
-
return [
|
|
|
|
|
|
|
178 |
|
179 |
if __name__ == "__main__":
|
180 |
-
pass
|
|
|
11 |
BASE_DB_PATH,
|
12 |
LLM_CONFIGS,
|
13 |
LLMType,
|
14 |
+
EMBEDDING_CONFIG,
|
15 |
+
LLM_CONFIGS_EXTENDED
|
16 |
)
|
17 |
from app.configs.prompts import SYSTEM_PROMPTS
|
18 |
from app.utils.embedding_utils import get_embeddings
|
|
|
54 |
return []
|
55 |
|
56 |
def get_relevant_documents(vectorstore, question):
|
57 |
+
"""Retrieves relevant documents from the vectorstore based on similarity threshold"""
|
58 |
try:
|
59 |
enhanced_query = enhance_query(question)
|
60 |
+
# Get all documents with their similarity scores
|
61 |
+
docs_and_scores = vectorstore.similarity_search_with_score(enhanced_query)
|
62 |
+
# Filter documents based on similarity threshold
|
|
|
63 |
filtered_docs = [
|
64 |
doc for doc, score in docs_and_scores
|
65 |
if score >= EMBEDDING_CONFIG['min_similarity']
|
|
|
83 |
logging.info(f"Content: {doc.page_content[:100]}...")
|
84 |
|
85 |
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
|
86 |
+
def summarize_context(messages):
|
87 |
+
"""Crea un riassunto del contesto mantenendo le informazioni chiave"""
|
88 |
+
summary = []
|
89 |
+
key_info = set()
|
90 |
+
|
91 |
+
for msg in messages:
|
92 |
+
if msg["role"] == "system":
|
93 |
+
continue
|
94 |
+
|
95 |
+
# Estrai informazioni chiave
|
96 |
+
content = msg["content"]
|
97 |
+
if "fonte" in content.lower() or "fonti" in content.lower():
|
98 |
+
key_info.add(content)
|
99 |
+
elif "importante" in content.lower() or "nota" in content.lower():
|
100 |
+
key_info.add(content)
|
101 |
+
|
102 |
+
if key_info:
|
103 |
+
summary.append({
|
104 |
+
"role": "system",
|
105 |
+
"content": "Contesto riassunto:\n" + "\n".join(f"- {info}" for info in key_info)
|
106 |
+
})
|
107 |
+
|
108 |
+
return summary
|
109 |
+
|
110 |
def answer_question(question, db_name, prompt_type="tutor", chat_history=None, llm_type=LLMType.OPENAI_GPT_4O_MINI):
|
111 |
if chat_history is None:
|
112 |
chat_history = []
|
113 |
+
|
114 |
+
# Configurazione dinamica della cronologia
|
115 |
+
MAX_HISTORY_TOKENS = int(LLM_CONFIGS_EXTENDED["max_tokens"] * 0.4) # 40% dei token totali
|
116 |
+
MIN_HISTORY_ITEMS = 2 # Mantieni almeno l'ultimo scambio
|
117 |
+
|
118 |
+
# Calcola la lunghezza della cronologia attuale
|
119 |
+
current_tokens = sum(len(m["content"].split()) for m in chat_history)
|
120 |
+
|
121 |
+
# Se superiamo il limite, creiamo un riassunto
|
122 |
+
if current_tokens > MAX_HISTORY_TOKENS:
|
123 |
+
summary = summarize_context(chat_history)
|
124 |
+
# Manteniamo l'ultimo scambio completo
|
125 |
+
last_exchange = chat_history[-MIN_HISTORY_ITEMS:]
|
126 |
+
chat_history = summary + last_exchange
|
127 |
+
|
128 |
try:
|
129 |
# Setup e recupero documenti
|
130 |
db_path = os.path.join(BASE_DB_PATH, f"faiss_index_{db_name}")
|
|
|
180 |
response = client.chat.completions.create(
|
181 |
model=model,
|
182 |
messages=messages,
|
183 |
+
temperature= LLM_CONFIGS_EXTENDED["temperature"],
|
184 |
+
max_tokens=LLM_CONFIGS_EXTENDED["max_tokens"]
|
185 |
)
|
186 |
|
187 |
answer = response.choices[0].message.content + sources_text
|
|
|
208 |
|
209 |
def get_relevant_chunks(self, question):
|
210 |
enhanced_query = enhance_query(question)
|
211 |
+
docs_and_scores = self.vectorstore.similarity_search_with_score(enhanced_query)
|
|
|
|
|
|
|
212 |
log_search_results(question, docs_and_scores)
|
213 |
+
return [
|
214 |
+
doc for doc, score in docs_and_scores
|
215 |
+
if score >= EMBEDDING_CONFIG['min_similarity']
|
216 |
+
]
|
217 |
|
218 |
if __name__ == "__main__":
|
219 |
+
pass
|
app/utils/helpers.py
CHANGED
@@ -1,7 +1,9 @@
|
|
1 |
-
# utils/helpers.py
|
2 |
-
|
3 |
import logging
|
4 |
from app.document_handling import extract_text_from_pdf, extract_text_from_docx
|
|
|
|
|
|
|
|
|
5 |
|
6 |
def extract_text_from_files(files):
|
7 |
"""
|
@@ -13,16 +15,87 @@ def extract_text_from_files(files):
|
|
13 |
Returns:
|
14 |
str: Testo concatenato estratto dai file.
|
15 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
text = ""
|
17 |
for file in files:
|
18 |
try:
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
text += f.read()
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
except Exception as e:
|
27 |
-
logging.error(f"Errore durante
|
28 |
-
|
|
|
|
|
|
|
|
|
|
1 |
import logging
|
2 |
from app.document_handling import extract_text_from_pdf, extract_text_from_docx
|
3 |
+
import tempfile
|
4 |
+
import os
|
5 |
+
from datetime import datetime
|
6 |
+
import shutil
|
7 |
|
8 |
def extract_text_from_files(files):
|
9 |
"""
|
|
|
15 |
Returns:
|
16 |
str: Testo concatenato estratto dai file.
|
17 |
"""
|
18 |
+
if not files:
|
19 |
+
logging.warning("Nessun file fornito")
|
20 |
+
return ""
|
21 |
+
|
22 |
+
logging.info(f"Ricevuti {len(files)} file da elaborare")
|
23 |
+
|
24 |
+
# Crea la cartella Temp_file se non esiste
|
25 |
+
temp_dir = os.path.join(os.path.dirname(__file__), '..', 'Temp_file')
|
26 |
+
os.makedirs(temp_dir, exist_ok=True)
|
27 |
+
logging.info(f"Cartella Temp_file: {temp_dir}")
|
28 |
+
|
29 |
text = ""
|
30 |
for file in files:
|
31 |
try:
|
32 |
+
file_path = None
|
33 |
+
|
34 |
+
# Gestione degli oggetti NamedString di Gradio
|
35 |
+
if type(file).__name__ == 'NamedString':
|
36 |
+
original_name = getattr(file, 'name', 'file')
|
37 |
+
_, ext = os.path.splitext(original_name)
|
38 |
+
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
39 |
+
temp_path = os.path.join(temp_dir, f"temp_{timestamp}{ext if ext else '.txt'}")
|
40 |
+
|
41 |
+
with open(temp_path, 'wb') as tmp_file:
|
42 |
+
if hasattr(file, 'encode'):
|
43 |
+
tmp_file.write(file.encode())
|
44 |
+
elif hasattr(file, 'read'):
|
45 |
+
tmp_file.write(file.read())
|
46 |
+
else:
|
47 |
+
raise ValueError("Impossibile convertire l'oggetto in bytes")
|
48 |
+
file_path = temp_path
|
49 |
+
|
50 |
+
logging.info(f"File temporaneo creato: {file_path}")
|
51 |
+
|
52 |
+
try:
|
53 |
+
if file_path.lower().endswith('.pdf'):
|
54 |
+
text += extract_text_from_pdf(file_path)
|
55 |
+
elif file_path.lower().endswith('.docx'):
|
56 |
+
text += extract_text_from_docx(file_path)
|
57 |
+
elif file_path.lower().endswith('.txt'):
|
58 |
+
with open(file_path, 'r', encoding='utf-8') as f:
|
59 |
+
text += f.read()
|
60 |
+
else:
|
61 |
+
logging.warning(f"Formato file non supportato: {file_path}")
|
62 |
+
except Exception as e:
|
63 |
+
logging.error(f"Errore durante l'elaborazione del file {file_path}: {str(e)}")
|
64 |
+
|
65 |
+
continue
|
66 |
+
|
67 |
+
# Gestione file direttamente caricati
|
68 |
+
if not hasattr(file, 'name') or not hasattr(file, 'read'):
|
69 |
+
logging.error(f"Oggetto file non valido: {type(file)}")
|
70 |
+
continue
|
71 |
+
|
72 |
+
# Salva il file originale in Temp_file
|
73 |
+
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
74 |
+
file_name = f"{timestamp}_{os.path.basename(file.name)}"
|
75 |
+
temp_path = os.path.join(temp_dir, file_name)
|
76 |
+
|
77 |
+
# Salva il contenuto del file
|
78 |
+
with open(temp_path, 'wb') as f:
|
79 |
+
f.write(file.read())
|
80 |
+
|
81 |
+
logging.info(f"File salvato in Temp_file: {temp_path}")
|
82 |
+
file_path = temp_path
|
83 |
+
|
84 |
+
if file_path.lower().endswith('.pdf'):
|
85 |
+
text += extract_text_from_pdf(file_path)
|
86 |
+
elif file_path.lower().endswith('.docx'):
|
87 |
+
text += extract_text_from_docx(file_path)
|
88 |
+
elif file_path.lower().endswith('.txt'):
|
89 |
+
with open(file_path, 'r', encoding='utf-8') as f:
|
90 |
text += f.read()
|
91 |
+
else:
|
92 |
+
logging.warning(f"Formato file non supportato: {file_path}")
|
93 |
+
|
94 |
+
if text and not text.endswith('\n\n'):
|
95 |
+
text += '\n\n'
|
96 |
+
|
97 |
except Exception as e:
|
98 |
+
logging.error(f"Errore durante l'elaborazione del file {file_path if file_path else 'unknown'}: {str(e)}")
|
99 |
+
continue
|
100 |
+
|
101 |
+
return text.strip()
|
ui/chatbot_tab.py
CHANGED
@@ -21,11 +21,15 @@ def create_chatbot_tab():
|
|
21 |
if chat_history is None:
|
22 |
chat_history = []
|
23 |
|
|
|
|
|
|
|
|
|
24 |
text = extract_text_from_files(files)
|
25 |
|
26 |
chat_history.append({
|
27 |
"role": "assistant",
|
28 |
-
"content": f"📄
|
29 |
})
|
30 |
|
31 |
return chat_history
|
@@ -38,7 +42,8 @@ def create_chatbot_tab():
|
|
38 |
llm_mapping = {
|
39 |
"openai - GPT-4o-Mini": LLMType.OPENAI_GPT_4O_MINI,
|
40 |
"local - Qwen 7B": LLMType.LOCAL_QWEN,
|
41 |
-
"local - Phi-3 Mini": LLMType.LOCAL_PHI
|
|
|
42 |
}
|
43 |
|
44 |
selected_llm = llm_mapping.get(llm_type, LLMType.OPENAI_GPT_4O_MINI)
|
@@ -173,8 +178,9 @@ def create_chatbot_tab():
|
|
173 |
llm_selector = gr.Dropdown(
|
174 |
choices=[
|
175 |
"openai - GPT-4o-Mini",
|
176 |
-
"local - Qwen 7B",
|
177 |
-
"local - Phi-3 Mini"
|
|
|
178 |
],
|
179 |
label="Seleziona Modello",
|
180 |
value="openai - GPT-4o-Mini"
|
@@ -252,4 +258,3 @@ def create_chatbot_tab():
|
|
252 |
|
253 |
# Ritorna il riferimento al dropdown corretto
|
254 |
return {"db_selector": db_name_chat}
|
255 |
-
|
|
|
21 |
if chat_history is None:
|
22 |
chat_history = []
|
23 |
|
24 |
+
if files is None:
|
25 |
+
files = []
|
26 |
+
|
27 |
+
file_names = "\n".join(file.name.split('/')[-1] for file in files if hasattr(file, 'name'))
|
28 |
text = extract_text_from_files(files)
|
29 |
|
30 |
chat_history.append({
|
31 |
"role": "assistant",
|
32 |
+
"content": f"📄 File caricati:\n{file_names}\n\nContenuto dei documenti caricati.\n{text}"
|
33 |
})
|
34 |
|
35 |
return chat_history
|
|
|
42 |
llm_mapping = {
|
43 |
"openai - GPT-4o-Mini": LLMType.OPENAI_GPT_4O_MINI,
|
44 |
"local - Qwen 7B": LLMType.LOCAL_QWEN,
|
45 |
+
"local - Phi-3 Mini": LLMType.LOCAL_PHI,
|
46 |
+
"deepseek - DeepSeek Chat": LLMType.DEEPSEEK
|
47 |
}
|
48 |
|
49 |
selected_llm = llm_mapping.get(llm_type, LLMType.OPENAI_GPT_4O_MINI)
|
|
|
178 |
llm_selector = gr.Dropdown(
|
179 |
choices=[
|
180 |
"openai - GPT-4o-Mini",
|
181 |
+
"local - Qwen 7B",
|
182 |
+
"local - Phi-3 Mini",
|
183 |
+
"deepseek - DeepSeek Chat"
|
184 |
],
|
185 |
label="Seleziona Modello",
|
186 |
value="openai - GPT-4o-Mini"
|
|
|
258 |
|
259 |
# Ritorna il riferimento al dropdown corretto
|
260 |
return {"db_selector": db_name_chat}
|
|
ui/management_tabs.py
DELETED
@@ -1,163 +0,0 @@
|
|
1 |
-
import gradio as gr
|
2 |
-
import logging
|
3 |
-
from app.document_handling import upload_and_index, list_indexed_files, delete_file_from_database
|
4 |
-
from app.utils.database_handling import (
|
5 |
-
create_database,
|
6 |
-
modify_database,
|
7 |
-
delete_database,
|
8 |
-
list_databases
|
9 |
-
)
|
10 |
-
|
11 |
-
class ManagementTabs:
|
12 |
-
def __init__(self, update_all_dropdowns):
|
13 |
-
self.update_all_dropdowns = update_all_dropdowns
|
14 |
-
self.databases = list_databases()
|
15 |
-
|
16 |
-
def create_tabs(self):
|
17 |
-
with gr.Tabs():
|
18 |
-
self._create_db_management_tab()
|
19 |
-
self._create_document_management_tab()
|
20 |
-
|
21 |
-
def _create_db_management_tab(self):
|
22 |
-
with gr.Tab("Gestione Database"):
|
23 |
-
gr.Markdown("## Operazioni sui Database")
|
24 |
-
|
25 |
-
with gr.Row():
|
26 |
-
# Creazione Database
|
27 |
-
with gr.Column():
|
28 |
-
gr.Markdown("### Crea Database")
|
29 |
-
self.db_name_input = gr.Textbox(label="Nome Nuovo Database")
|
30 |
-
self.create_db_button = gr.Button("Crea Database")
|
31 |
-
self.create_output = gr.Textbox(label="Stato Creazione")
|
32 |
-
|
33 |
-
# Modifica Database
|
34 |
-
with gr.Column():
|
35 |
-
gr.Markdown("### Rinomina Database")
|
36 |
-
self.modify_db_old_name = gr.Dropdown(
|
37 |
-
choices=self.databases,
|
38 |
-
label="Database da Rinominare"
|
39 |
-
)
|
40 |
-
self.modify_db_new_name = gr.Textbox(label="Nuovo Nome")
|
41 |
-
self.modify_db_button = gr.Button("Rinomina Database")
|
42 |
-
self.modify_output = gr.Textbox(label="Stato Modifica")
|
43 |
-
|
44 |
-
# Eliminazione Database
|
45 |
-
with gr.Column():
|
46 |
-
gr.Markdown("### Elimina Database")
|
47 |
-
self.delete_db_dropdown = gr.Dropdown(
|
48 |
-
choices=self.databases,
|
49 |
-
label="Database da Eliminare"
|
50 |
-
)
|
51 |
-
self.delete_db_button = gr.Button("Elimina Database")
|
52 |
-
self.delete_output = gr.Textbox(label="Stato Eliminazione")
|
53 |
-
|
54 |
-
self._setup_db_events()
|
55 |
-
|
56 |
-
def _create_document_management_tab(self):
|
57 |
-
with gr.Tab("Gestione Documenti"):
|
58 |
-
with gr.Column():
|
59 |
-
# Upload Documenti
|
60 |
-
gr.Markdown("### Carica Documenti")
|
61 |
-
with gr.Row():
|
62 |
-
self.file_input = gr.File(
|
63 |
-
label="Carica i tuoi documenti",
|
64 |
-
file_types=[".txt", ".pdf", ".docx"],
|
65 |
-
file_count="multiple"
|
66 |
-
)
|
67 |
-
self.db_name_upload = gr.Dropdown(
|
68 |
-
choices=self.databases,
|
69 |
-
label="Seleziona Database",
|
70 |
-
value="default_db"
|
71 |
-
)
|
72 |
-
|
73 |
-
with gr.Row():
|
74 |
-
self.title_input = gr.Textbox(label="Titolo del documento")
|
75 |
-
self.author_input = gr.Textbox(label="Autore")
|
76 |
-
|
77 |
-
self.upload_button = gr.Button("Indicizza Documenti")
|
78 |
-
self.upload_output = gr.Textbox(label="Stato Upload")
|
79 |
-
|
80 |
-
# Gestione File
|
81 |
-
self._setup_file_management()
|
82 |
-
|
83 |
-
self._setup_document_events()
|
84 |
-
|
85 |
-
def _setup_file_management(self):
|
86 |
-
gr.Markdown("### Gestione File")
|
87 |
-
with gr.Row():
|
88 |
-
self.db_name_list = gr.Dropdown(
|
89 |
-
choices=self.databases,
|
90 |
-
label="Database",
|
91 |
-
value="default_db"
|
92 |
-
)
|
93 |
-
self.list_button = gr.Button("Lista File")
|
94 |
-
self.list_output = gr.Textbox(label="File nel Database")
|
95 |
-
|
96 |
-
with gr.Row():
|
97 |
-
self.delete_file_input = gr.Textbox(label="Nome File da Eliminare")
|
98 |
-
self.delete_file_button = gr.Button("Elimina File")
|
99 |
-
self.delete_file_output = gr.Textbox(label="Stato Eliminazione")
|
100 |
-
|
101 |
-
def _setup_db_events(self):
|
102 |
-
# Eventi per la gestione del database
|
103 |
-
self.create_db_button.click(
|
104 |
-
fn=create_database,
|
105 |
-
inputs=self.db_name_input,
|
106 |
-
outputs=self.create_output
|
107 |
-
).then(fn=self.update_all_dropdowns)
|
108 |
-
|
109 |
-
self.modify_db_button.click(
|
110 |
-
fn=modify_database,
|
111 |
-
inputs=[self.modify_db_old_name, self.modify_db_new_name],
|
112 |
-
outputs=self.modify_output
|
113 |
-
).then(fn=self.update_all_dropdowns)
|
114 |
-
|
115 |
-
self.delete_db_button.click(
|
116 |
-
fn=delete_database,
|
117 |
-
inputs=self.delete_db_dropdown,
|
118 |
-
outputs=self.delete_output
|
119 |
-
).then(fn=self.update_all_dropdowns)
|
120 |
-
|
121 |
-
def _setup_document_events(self):
|
122 |
-
# Eventi per la gestione dei documenti
|
123 |
-
self.upload_button.click(
|
124 |
-
fn=self._upload_and_index_callback,
|
125 |
-
inputs=[self.file_input, self.title_input, self.author_input, self.db_name_upload],
|
126 |
-
outputs=self.upload_output
|
127 |
-
).then(fn=self.update_all_dropdowns).then(
|
128 |
-
fn=self._list_files_callback,
|
129 |
-
inputs=[self.db_name_list],
|
130 |
-
outputs=self.list_output
|
131 |
-
)
|
132 |
-
|
133 |
-
self.list_button.click(
|
134 |
-
fn=self._list_files_callback,
|
135 |
-
inputs=[self.db_name_list],
|
136 |
-
outputs=self.list_output
|
137 |
-
)
|
138 |
-
|
139 |
-
self.delete_file_button.click(
|
140 |
-
fn=self._delete_file_callback,
|
141 |
-
inputs=[self.delete_file_input, self.db_name_list],
|
142 |
-
outputs=self.delete_file_output
|
143 |
-
).then(fn=self.update_all_dropdowns)
|
144 |
-
|
145 |
-
def _upload_and_index_callback(self, files, title, author, db_name):
|
146 |
-
try:
|
147 |
-
status = upload_and_index(files, title, author, db_name)
|
148 |
-
logging.info(f"Upload completato: {status}")
|
149 |
-
return status
|
150 |
-
except Exception as e:
|
151 |
-
logging.error(f"Errore durante l'upload: {str(e)}")
|
152 |
-
return f"Errore: {str(e)}"
|
153 |
-
|
154 |
-
def _list_files_callback(self, db_name):
|
155 |
-
return list_indexed_files(db_name)
|
156 |
-
|
157 |
-
def _delete_file_callback(self, file_name, db_name):
|
158 |
-
return delete_file_from_database(file_name, db_name)
|
159 |
-
|
160 |
-
def create_management_tabs(update_all_dropdowns):
|
161 |
-
tabs = ManagementTabs(update_all_dropdowns)
|
162 |
-
tabs.create_tabs()
|
163 |
-
return tabs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|