File size: 5,474 Bytes
46b2f94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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
121
122
123
124
import json
import numpy as np
import gradio as gr
import logging
from openai import OpenAI
from tenacity import retry, wait_random_exponential, stop_after_attempt
import os

# Recupero della chiave API dalla variabile d'ambiente "api"
api_key = os.environ.get("api")
if not api_key:
    raise ValueError("La variabile d'ambiente 'api' non è stata impostata. Verifica la configurazione dei secrets.")
client = OpenAI(api_key=api_key)

# Configurazione del logging per errori
logging.basicConfig(filename="error.log", level=logging.ERROR, 
                    format='%(asctime)s - %(levelname)s - %(message)s')

# Funzione per ottenere l'embedding da OpenAI con la nuova sintassi
@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))
def get_embedding(text, model="text-embedding-3-small"):
    if not isinstance(text, str) or not text.strip():
        raise ValueError("Il testo di input deve essere una stringa non vuota.")
    text = text.replace("\n", " ")
    response = client.embeddings.create(input=[text], model=model)
    return response.data[0].embedding

# Funzione per calcolare la similarità coseno
def cosine_similarity(vec_a, vec_b):
    dot_product = np.dot(vec_a, vec_b)
    norm_a = np.linalg.norm(vec_a)
    norm_b = np.linalg.norm(vec_b)
    if norm_a == 0 or norm_b == 0:
        return 0.0
    return dot_product / (norm_a * norm_b)

# Funzione per caricare gli embeddings dal file JSON
def load_embeddings(file_path):
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            data = json.load(file)
        return data
    except UnicodeDecodeError as e:
        logging.error(f"Errore di codifica nel caricamento del file JSON: {e}")
        return f"Errore: {e}"
    except json.JSONDecodeError as e:
        logging.error(f"Errore di parsing JSON: {e}")
        return f"Errore: {e}"
    except Exception as e:
        logging.error(f"Errore generico nel caricamento del file JSON: {e}")
        return f"Errore: {e}"

# Funzione per trovare gli articoli simili a partire da una frase chiave
def find_similar_articles_from_query(query, embeddings_data):
    try:
        query_embedding = get_embedding(query)
    except Exception as e:
        error_message = f"Errore nel calcolo dell'embedding per la query: {e}"
        logging.error(error_message)
        return None, None, error_message

    similarities = []
    # Calcola la similarità tra l'embedding della query e ciascun articolo
    for article in embeddings_data:
        try:
            similarity = cosine_similarity(query_embedding, article['embedding'])
        except Exception as e:
            logging.error(f"Errore nel calcolo della similarità per l'articolo {article.get('titolo_articolo', 'Sconosciuto')}: {e}")
            similarity = 0.0
        # Costruzione del link per il download del PDF
        pdf_url = f"https://storiadellarterivista.it/data/pdf/{article['testo_pdf']}"
        pdf_link = f'<a href="{pdf_url}" download>{article["testo_pdf"]}</a>'
        similarities.append({
            "titolo_articolo": article['titolo_articolo'],
            "similarity": similarity,
            "pdf_link": pdf_link
        })

    # Ordina gli articoli in ordine decrescente per similarità
    similarities_sorted = sorted(similarities, key=lambda x: x['similarity'], reverse=True)
    top_5 = similarities_sorted[:5]
    # Ordina in ordine crescente per ottenere i 5 articoli con minore similarità
    bottom_5 = sorted(similarities, key=lambda x: x['similarity'])[:5]
    return top_5, bottom_5, None

# Funzione per generare una tabella HTML a partire da una lista di articoli
def generate_html_table(articles, title):
    html = f"<h3>{title}</h3>"
    html += '<table border="1" style="border-collapse: collapse; width:100%;">'
    html += "<tr><th>Titolo Articolo</th><th>Similarità</th><th>PDF</th></tr>"
    for art in articles:
        html += f"<tr><td>{art['titolo_articolo']}</td><td>{art['similarity']:.3f}</td><td>{art['pdf_link']}</td></tr>"
    html += "</table>"
    return html

# Funzione principale chiamata dall'interfaccia GRADIO
def search_articles(query):
    top_5, bottom_5, error = find_similar_articles_from_query(query, embeddings_data)
    if error:
        return error, error
    top_table = generate_html_table(top_5, "Top 5 Articoli più simili")
    bottom_table = generate_html_table(bottom_5, "Bottom 5 Articoli meno simili")
    return top_table, bottom_table

# Caricamento degli embeddings dal file JSON
file_path = 'embedded_articles.json'  # Percorso del file JSON contenente gli embeddings
embeddings_data = load_embeddings(file_path)

# Controllo di eventuali errori nel caricamento degli embeddings
if isinstance(embeddings_data, str):
    logging.error(embeddings_data)
    print(embeddings_data)
else:
    iface = gr.Interface(
        fn=search_articles,
        inputs=gr.Textbox(label="Inserisci una frase chiave", placeholder="Scrivi qui la tua frase di ricerca..."),
        outputs=[gr.HTML(label="Articoli più simili"), gr.HTML(label="Articoli meno simili")],
        title="Ricerca Articoli Simili da Frase Chiave",
        description=("Inserisci una frase chiave per trovare gli articoli semanticamente simili. "
                     "Vengono mostrati i 5 articoli con maggiore similarità e i 5 con minore similarità, "
                     "con il coefficiente di similarità e un link per il download del PDF.")
    )
    iface.launch(share=True)