File size: 5,814 Bytes
ee1f7e6
034a1e6
ee1f7e6
034a1e6
 
 
ee1f7e6
4c52594
 
 
 
1575481
034a1e6
 
 
21119e3
034a1e6
70bec3c
 
 
034a1e6
a1f8fd1
70bec3c
034a1e6
8c772f5
4c52594
034a1e6
 
70bec3c
 
c191446
 
 
 
034a1e6
70bec3c
3031ece
034a1e6
e623027
c191446
 
 
 
 
 
 
 
 
 
 
 
 
 
70bec3c
c191446
 
baa2201
c191446
 
70bec3c
c191446
70bec3c
 
034a1e6
baa2201
034a1e6
3b0ec68
 
 
 
ee1f7e6
034a1e6
70bec3c
 
034a1e6
70bec3c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aa07e9d
034a1e6
 
70bec3c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1575481
034a1e6
70bec3c
 
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import json
import os
from pathlib import Path
from huggingface_hub import InferenceClient
from rdflib import Graph
from pydantic import BaseModel
from fastapi import FastAPI, HTTPException
import logging

logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)

API_KEY = os.getenv("HF_API_KEY")
client = InferenceClient(api_key=API_KEY)

RDF_FILE = "Ontologia.rdf"

# Funzione di validazione
def validate_sparql_query(query: str, rdf_file_path: str) -> bool:
    g = Graph()
    try:
        g.parse(rdf_file_path, format="xml")
        g.query(query)  # Se c'è un errore di sintassi o referenza, solleva eccezione
        return True
    except Exception as e:
        logger.error(f"Errore durante la validazione della query SPARQL: {e}")
        return False

# Prompt di sistema "stringente"
def create_system_message(rdf_context: str) -> str:
    """
    Prompt di sistema estremamente rigido, che forza il modello
    a usare solo il prefisso definito e a generare la query in un'unica riga.
    """
    return f"""
Sei un assistente esperto nella generazione di query SPARQL basate su un'ontologia RDF.
Ecco un riassunto dell'ontologia su cui devi lavorare:
{rdf_context}

DI SEGUITO LE REGOLE TASSATIVE:
1. DEVI usare ESCLUSIVAMENTE questo prefisso di base (e NON modificarlo in nessun modo):
   PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/2024/11/untitled-ontology-39/>
2. La query deve stare in UNA SOLA RIGA, senza andare a capo.
3. La query deve INIZIARE con:
   PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/2024/11/untitled-ontology-39/> SELECT
   oppure
   PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/2024/11/untitled-ontology-39/> ASK
4. Se devi indicare una classe, usa: ?qualcosa a base:NomeClasse .
5. Se devi indicare una proprietà, usa: ?s base:NomeProprieta ?o .
6. NON generare alcun altro prefisso.
7. NON utilizzare URI lunghe senza < > e NON inventare prefissi o risorse inesistenti.
8. Se non puoi rispondere con una query SPARQL valida secondo questi criteri, scrivi:
   "Non posso generare una query SPARQL per questa richiesta."

Esempio di query corretta (fittizia) in una sola riga:
PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/2024/11/untitled-ontology-39/> SELECT ?stanza WHERE { ?stanza a base:Stanza . } LIMIT 10

RISPONDI ESCLUSIVAMENTE CON LA QUERY O IL MESSAGGIO DI IMPOSSIBILITA'. 
NON SCRIVERE NESSUN COMMENTO AGGIUNTIVO.
"""
    
# Funzione per chiamare il modello su Hugging Face
async def call_model(messages, temperature=0.7, max_tokens=2048):
    try:
        response = client.chat.completions.create(
            model="Qwen/Qwen2.5-72B-Instruct",
            messages=messages,
            temperature=temperature,
            max_tokens=max_tokens,
            top_p=0.7,
            stream=False
        )
        raw_text = response["choices"][0]["message"]["content"]
        return raw_text.replace("\n", " ").strip()
    except Exception as e:
        logger.error(f"Errore nel modello: {e}")
        raise HTTPException(status_code=500, detail=str(e))

# Caricamento di un riassunto dell'ontologia (semplificato per brevità)
def load_rdf_summary() -> str:
    # Qui un caricamento minimo o un testo statico
    return "Classi e proprietà dell'ontologia: base:Stanza, base:Contiene, base:Opera, ecc."

app = FastAPI()
rdf_context = load_rdf_summary()

class QueryRequest(BaseModel):
    message: str
    max_tokens: int = 2048
    temperature: float = 0.7

@app.post("/generate-query/")
async def generate_query(request: QueryRequest):
    # 1) Prima iterazione
    system_msg = create_system_message(rdf_context)
    user_msg = request.message
    messages = [
        {"role": "system", "content": system_msg},
        {"role": "user", "content": user_msg},
    ]
    response1 = await call_model(messages, request.temperature, request.max_tokens)
    logger.info(f"[Prima iterazione] Risposta generata dal modello: {response1}")

    # 2) Validazione
    if not (response1.startswith("PREFIX") and ("SELECT" in response1 or "ASK" in response1)):
        # Fallimento immediato
        return {"query": None, "explanation": "La query non rispetta le regole base (PREFIX + SELECT/ASK)."}
    
    if validate_sparql_query(response1, RDF_FILE):
        # Query valida! Restituisco la prima
        return {"query": response1, "explanation": "Query valida alla prima iterazione."}
    else:
        # 3) Seconda iterazione “correttiva”
        correction_msg = create_correction_message(rdf_context, "Errore di validazione (prima iterazione).")
        messages2 = [
            {"role": "system", "content": system_msg},  # Sistema invariato
            {"role": "assistant", "content": response1}, # Metti la risposta errata in contesto
            {"role": "system", "content": correction_msg}, # Istruzione di correzione
        ]
        response2 = await call_model(messages2, request.temperature, request.max_tokens)
        logger.info(f"[Seconda iterazione] Risposta generata dal modello: {response2}")

        if not (response2.startswith("PREFIX") and ("SELECT" in response2 or "ASK" in response2)):
            return {"query": None, "explanation": "Anche la seconda query non rispetta le regole (PREFIX + SELECT/ASK)."}
        
        if validate_sparql_query(response2, RDF_FILE):
            return {"query": response2, "explanation": "Query valida alla seconda iterazione (corretta)."}
        else:
            return {
                "query": None,
                "explanation": "Anche la seconda iterazione ha prodotto una query non valida. Interrompo."
            }

@app.get("/")
async def root():
    return {"message": "Server attivo per generare query SPARQL"}