""" MODULE: SCHEMA EXTRACTOR (TYPE AWARE) ===================================== Responsabilité : Extraire le schéma réel DU GRAPHE pour informer l'Agent des types de données disponibles. """ from rdflib import Graph class SchemaExtractor: def __init__(self, rdf_store): self.rdf_store = rdf_store def get_real_schema(self): """Récupère les classes, les propriétés et leurs types de données observés""" g = self.rdf_store.g schema = { "classes": {}, "predicates": [], "properties_by_class": {} } # 1. Compter les entités par classe q_classes = "SELECT ?type (COUNT(?s) as ?count) WHERE { ?s a ?type } GROUP BY ?type" for row in g.query(q_classes): cls_name = str(row[0]).split('#')[-1] schema["classes"][cls_name] = int(row[1]) # 2. Extraire les propriétés et deviner leur type for cls in schema["classes"]: # Cette requête regarde la donnée réelle (?o) et demande son type (datatype(?o)) q_props = f""" SELECT DISTINCT ?p ?datatype WHERE {{ ?s a vortex:{cls} . ?s ?p ?o . BIND(datatype(?o) AS ?datatype) FILTER(STRSTARTS(STR(?p), "http://vortex.ai/ontology#")) }} """ props = [] for row in g.query(q_props): pred = str(row[0]).split('#')[-1] # Si datatype est vide, c'est probablement un String ou une Relation dtype = str(row[1]).split('#')[-1] if row[1] else "string/obj" # On ajoute le prédicat à la liste globale if pred not in schema["predicates"]: schema["predicates"].append(pred) props.append(f"{pred} ({dtype})") schema["properties_by_class"][cls] = props return schema def generate_prompt_schema(self): """Génère le texte de contexte pour le LLM""" schema = self.get_real_schema() lines = ["--- RÉALITÉ DU GRAPH RDF (TYPÉE) ---"] for cls, count in schema["classes"].items(): props = ", ".join(schema["properties_by_class"].get(cls, [])) lines.append(f"📦 {cls} ({count} items) : {props}") return "\n".join(lines)