Spaces:
Sleeping
Sleeping
Paolo De Gasperis
commited on
Update app.py
Browse files
app.py
CHANGED
@@ -1,120 +1,123 @@
|
|
1 |
-
import json
|
2 |
-
import numpy as np
|
3 |
-
import gradio as gr
|
4 |
-
import logging
|
5 |
-
from openai import OpenAI
|
6 |
-
from tenacity import retry, wait_random_exponential, stop_after_attempt
|
7 |
-
import os
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
return
|
43 |
-
except
|
44 |
-
logging.error(f"Errore di
|
45 |
-
return f"Errore: {e}"
|
46 |
-
except
|
47 |
-
logging.error(f"Errore
|
48 |
-
return f"Errore: {e}"
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
similarity =
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
# Ordina in ordine
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
html += "</
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
#
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import numpy as np
|
3 |
+
import gradio as gr
|
4 |
+
import logging
|
5 |
+
from openai import OpenAI
|
6 |
+
from tenacity import retry, wait_random_exponential, stop_after_attempt
|
7 |
+
import os
|
8 |
+
|
9 |
+
# Recupero della chiave API dalla variabile d'ambiente "api"
|
10 |
+
api_key = os.environ.get("api")
|
11 |
+
if not api_key:
|
12 |
+
raise ValueError("La variabile d'ambiente 'api' non è stata impostata. Verifica la configurazione dei secrets.")
|
13 |
+
client = OpenAI(api_key=api_key)
|
14 |
+
|
15 |
+
# Configurazione del logging per errori
|
16 |
+
logging.basicConfig(filename="error.log", level=logging.ERROR,
|
17 |
+
format='%(asctime)s - %(levelname)s - %(message)s')
|
18 |
+
|
19 |
+
# Funzione per ottenere l'embedding da OpenAI con la nuova sintassi
|
20 |
+
@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))
|
21 |
+
def get_embedding(text, model="text-embedding-3-small"):
|
22 |
+
if not isinstance(text, str) or not text.strip():
|
23 |
+
raise ValueError("Il testo di input deve essere una stringa non vuota.")
|
24 |
+
text = text.replace("\n", " ")
|
25 |
+
response = client.embeddings.create(input=[text], model=model)
|
26 |
+
return response.data[0].embedding
|
27 |
+
|
28 |
+
# Funzione per calcolare la similarità coseno
|
29 |
+
def cosine_similarity(vec_a, vec_b):
|
30 |
+
dot_product = np.dot(vec_a, vec_b)
|
31 |
+
norm_a = np.linalg.norm(vec_a)
|
32 |
+
norm_b = np.linalg.norm(vec_b)
|
33 |
+
if norm_a == 0 or norm_b == 0:
|
34 |
+
return 0.0
|
35 |
+
return dot_product / (norm_a * norm_b)
|
36 |
+
|
37 |
+
# Funzione per caricare gli embeddings dal file JSON
|
38 |
+
def load_embeddings(file_path):
|
39 |
+
try:
|
40 |
+
with open(file_path, 'r', encoding='utf-8') as file:
|
41 |
+
data = json.load(file)
|
42 |
+
return data
|
43 |
+
except UnicodeDecodeError as e:
|
44 |
+
logging.error(f"Errore di codifica nel caricamento del file JSON: {e}")
|
45 |
+
return f"Errore: {e}"
|
46 |
+
except json.JSONDecodeError as e:
|
47 |
+
logging.error(f"Errore di parsing JSON: {e}")
|
48 |
+
return f"Errore: {e}"
|
49 |
+
except Exception as e:
|
50 |
+
logging.error(f"Errore generico nel caricamento del file JSON: {e}")
|
51 |
+
return f"Errore: {e}"
|
52 |
+
|
53 |
+
# Funzione per trovare gli articoli simili a partire da una frase chiave
|
54 |
+
def find_similar_articles_from_query(query, embeddings_data):
|
55 |
+
try:
|
56 |
+
query_embedding = get_embedding(query)
|
57 |
+
except Exception as e:
|
58 |
+
error_message = f"Errore nel calcolo dell'embedding per la query: {e}"
|
59 |
+
logging.error(error_message)
|
60 |
+
return None, None, error_message
|
61 |
+
|
62 |
+
similarities = []
|
63 |
+
# Calcola la similarità tra l'embedding della query e ciascun articolo
|
64 |
+
for article in embeddings_data:
|
65 |
+
try:
|
66 |
+
similarity = cosine_similarity(query_embedding, article['embedding'])
|
67 |
+
except Exception as e:
|
68 |
+
logging.error(f"Errore nel calcolo della similarità per l'articolo {article.get('titolo_articolo', 'Sconosciuto')}: {e}")
|
69 |
+
similarity = 0.0
|
70 |
+
# Costruzione del link per il download del PDF
|
71 |
+
pdf_url = f"https://storiadellarterivista.it/data/pdf/{article['testo_pdf']}"
|
72 |
+
pdf_link = f'<a href="{pdf_url}" download>{article["testo_pdf"]}</a>'
|
73 |
+
similarities.append({
|
74 |
+
"titolo_articolo": article['titolo_articolo'],
|
75 |
+
"similarity": similarity,
|
76 |
+
"pdf_link": pdf_link
|
77 |
+
})
|
78 |
+
|
79 |
+
# Ordina gli articoli in ordine decrescente per similarità
|
80 |
+
similarities_sorted = sorted(similarities, key=lambda x: x['similarity'], reverse=True)
|
81 |
+
top_5 = similarities_sorted[:5]
|
82 |
+
# Ordina in ordine crescente per ottenere i 5 articoli con minore similarità
|
83 |
+
bottom_5 = sorted(similarities, key=lambda x: x['similarity'])[:5]
|
84 |
+
return top_5, bottom_5, None
|
85 |
+
|
86 |
+
# Funzione per generare una tabella HTML a partire da una lista di articoli
|
87 |
+
def generate_html_table(articles, title):
|
88 |
+
html = f"<h3>{title}</h3>"
|
89 |
+
html += '<table border="1" style="border-collapse: collapse; width:100%;">'
|
90 |
+
html += "<tr><th>Titolo Articolo</th><th>Similarità</th><th>PDF</th></tr>"
|
91 |
+
for art in articles:
|
92 |
+
html += f"<tr><td>{art['titolo_articolo']}</td><td>{art['similarity']:.3f}</td><td>{art['pdf_link']}</td></tr>"
|
93 |
+
html += "</table>"
|
94 |
+
return html
|
95 |
+
|
96 |
+
# Funzione principale chiamata dall'interfaccia GRADIO
|
97 |
+
def search_articles(query):
|
98 |
+
top_5, bottom_5, error = find_similar_articles_from_query(query, embeddings_data)
|
99 |
+
if error:
|
100 |
+
return error, error
|
101 |
+
top_table = generate_html_table(top_5, "Top 5 Articoli più simili")
|
102 |
+
bottom_table = generate_html_table(bottom_5, "Bottom 5 Articoli meno simili")
|
103 |
+
return top_table, bottom_table
|
104 |
+
|
105 |
+
# Caricamento degli embeddings dal file JSON
|
106 |
+
file_path = 'embedded_articles.json' # Percorso del file JSON contenente gli embeddings
|
107 |
+
embeddings_data = load_embeddings(file_path)
|
108 |
+
|
109 |
+
# Controllo di eventuali errori nel caricamento degli embeddings
|
110 |
+
if isinstance(embeddings_data, str):
|
111 |
+
logging.error(embeddings_data)
|
112 |
+
print(embeddings_data)
|
113 |
+
else:
|
114 |
+
iface = gr.Interface(
|
115 |
+
fn=search_articles,
|
116 |
+
inputs=gr.Textbox(label="Inserisci una frase chiave", placeholder="Scrivi qui la tua frase di ricerca..."),
|
117 |
+
outputs=[gr.HTML(label="Articoli più simili"), gr.HTML(label="Articoli meno simili")],
|
118 |
+
title="Ricerca Articoli Simili da Frase Chiave",
|
119 |
+
description=("Inserisci una frase chiave per trovare gli articoli semanticamente simili. "
|
120 |
+
"Vengono mostrati i 5 articoli con maggiore similarità e i 5 con minore similarità, "
|
121 |
+
"con il coefficiente di similarità e un link per il download del PDF.")
|
122 |
+
)
|
123 |
+
iface.launch(share=True)
|