Spaces:
Sleeping
Sleeping
File size: 10,242 Bytes
5de3226 d9e60f8 5de3226 d9e60f8 5de3226 17a39ef 5de3226 d9e60f8 f795c5f d9e60f8 bc1e3be c3b37a6 bc1e3be 0ca6195 bc1e3be d9e60f8 c07c21e d9e60f8 c07c21e d9e60f8 5de3226 d9e60f8 5de3226 d9e60f8 17a39ef d9e60f8 5de3226 d9e60f8 5de3226 d9e60f8 5de3226 d9e60f8 4de725f d9e60f8 5de3226 d9e60f8 0b61966 d9e60f8 0b61966 d9e60f8 17a39ef 8901bbe eee152b d9e60f8 17a39ef 6f5d074 eee152b 6f5d074 17a39ef eee152b 6f5d074 d9e60f8 eee152b d9e60f8 5de3226 d9e60f8 eee152b 6f5d074 d9e60f8 c04f6ea 6f5d074 d9e60f8 8901bbe 3c56ae4 8901bbe 6f5d074 d9e60f8 8901bbe 5de3226 17a39ef 6f5d074 8901bbe eee152b 3c56ae4 eee152b 17a39ef |
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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
import streamlit as st
import pandas as pd
import os
import json
import time
from pathlib import Path
from pyvis.network import Network
import streamlit.components.v1 as components
from core.docling_engine import IngestionEngine
from core.extractor import ExtractorEngine
#Link for the app : https://klydekushy-ocr-prospectus.hf.space/
# --- CONFIGURATION DE LA PAGE ---
st.set_page_config(
page_title="PrõspectusVéritas | Intelligence Platform",
page_icon="🔵",
layout="wide",
initial_sidebar_state="expanded"
)
# --- MOT DE PASSE ---
def check_password():
if "password_correct" not in st.session_state:
st.session_state.password_correct = False
if st.session_state.password_correct:
return True
st.title("Accès Restreint - Veritas")
password = st.text_input("Veuillez saisir le code d'accès", type="password")
if st.button("Se connecter"):
if password == "ok": #Veritas2025
st.session_state.password_correct = True
st.rerun()
else:
st.error("Mot de passe incorrect")
return False
if not check_password():
st.stop()
# --- CSS "GOTHAM STYLE" ---
st.markdown("""
<style>
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;700&display=swap');
html, body, .stApp, h1, h2, h3, h4, .stText, .stMarkdown, .stTextInput, .stTextArea {
font-family: 'Space Grotesk', sans-serif !important;
}
.stApp { background-color: #0b0d11; }
[data-testid="stSidebar"] { background-color: #12151e; border-right: 1px solid #30363d; }
div[data-testid="stMetricValue"] { font-size: 24px; color: #29b5e8; }
div[data-testid="metric-container"] { background-color: #1c2128; border: 1px solid #30363d; padding: 15px; border-radius: 4px; }
.stButton>button { background-color: #29b5e8; color: white; border-radius: 0px; text-transform: uppercase; font-weight: bold; }
h1, h2, h3 { color: #e6edf3; font-weight: 300; text-transform: uppercase; }
</style>
""", unsafe_allow_html=True)
# --- INITIALISATION ---
INPUT_DIR = Path("input_data")
OUTPUT_DIR = Path("output_json")
INPUT_DIR.mkdir(exist_ok=True)
OUTPUT_DIR.mkdir(exist_ok=True)
if 'engine' not in st.session_state:
st.session_state.engine = IngestionEngine()
if 'extractor' not in st.session_state:
st.session_state.extractor = ExtractorEngine()
# --- SIDEBAR ---
with st.sidebar:
st.title("PrõspectùsV-ritas")
st.markdown("---")
st.caption("PARAMÈTRES IA")
# AJOUT DU CURSEUR DE TEMPÉRATURE
ia_temp = st.slider("Température Créative", 0.1, 1.0, 0.2, help="0.1 = Précis, 0.8 = Créatif")
if st.button("PURGER LE SYSTÈME"):
for f in list(INPUT_DIR.glob("*")) + list(OUTPUT_DIR.glob("*")):
os.remove(f)
st.success("Système nettoyé.")
st.rerun()
st.markdown("---")
st.caption("PARAMÈTRES SYSTÈME")
st.checkbox("OCR Amélioré", value=True)
st.checkbox("Extraction Entités", value=True)
# --- DASHBOARD HEADER ---
col1, col2, col3, col4 = st.columns(4)
col1.metric("Documents Ingestés", len(list(OUTPUT_DIR.glob("*.json"))))
col2.metric("Statut Système", "DOCKER-HF")
col3.metric("Moteur OCR", "Docling v2")
col4.metric("Confiance IA", "98.4%")
st.markdown("---")
# --- NAVIGATION PAR ONGLETS (STYLE PALANTIR) ---
tab_ingestion, tab_entities, tab_visualisation = st.tabs([
"INGESTION & OCR",
"ENTITÉS & RELATIONS",
"VISUALISATION GRAPHE"
])
# --- TAB 1: INGESTION ---
with tab_ingestion:
col_u1, col_u2 = st.columns(2)
with col_u1:
st.subheader("◯⎯| CHARGEMENT DOCUMENTS")
uploaded_files = st.file_uploader("Fichiers PDF/IMG", accept_multiple_files=True)
if uploaded_files and st.button("INITIER LA SÉQUENCE OCR"):
for uploaded_file in uploaded_files:
file_path = INPUT_DIR / uploaded_file.name
with open(file_path, "wb") as f:
f.write(uploaded_file.getbuffer())
with st.spinner(f"Traitement: {uploaded_file.name}"):
st.session_state.engine.process_document(file_path, OUTPUT_DIR)
st.success("Traitement terminé.")
st.rerun()
with col_u2:
st.subheader("◯⎯| TEXTE LIBRE")
free_text = st.text_area("Coller du texte ici", height=150)
if st.button("INITIER LA SÉQUENCE TEXTE"):
temp_path = INPUT_DIR / f"text_{int(time.time())}.md"
with open(temp_path, "w", encoding="utf-8") as f: f.write(free_text)
st.session_state.engine.process_document(temp_path, OUTPUT_DIR)
st.rerun()
# --- TAB 2: ENTITÉS & RELATIONS ---
with tab_entities:
json_files = list(OUTPUT_DIR.glob("*.json"))
if not json_files:
st.info("Aucun document analysé disponible. Allez dans l'onglet INGESTION.")
else:
# Barre d'outils (Sélection et Suppression)
col_select, col_delete = st.columns([3, 1])
with col_select:
selected_file = st.selectbox("Sélectionner un artefact", json_files, format_func=lambda x: x.name, key="select_entity")
with col_delete:
st.write("")
if st.button("SUPPRIMER", key="del_entity", use_container_width=True):
os.remove(selected_file)
st.rerun()
st.markdown("---")
# Chargement du texte extrait
with open(selected_file, 'r', encoding='utf-8') as f:
data = json.load(f)
text_extracted = " ".join([t.get("text", "") for t in data.get("texts", [])])
col_inf1, col_inf2 = st.columns([1, 1])
with col_inf1:
st.markdown("### TEXTE SOURCE")
st.text_area("Données issues de l'OCR", text_extracted, height=500)
with col_inf2:
st.markdown("### EXTRACTION HYBRIDE")
# 1. Bouton de lancement
if st.button("GÉNÉRER L'INTELLIGENCE SÉMANTIQUE", key="btn_run_hybrid", use_container_width=True):
# 2. Préparation de la barre de progression
progress_bar = st.progress(0)
status_text = st.empty()
# On utilise un spinner pour le chargement global
with st.spinner("Initialisation de GLiNER & Qwen..."):
# NOTE: Pour afficher la progression, nous allons légèrement modifier
# l'appel pour traiter les morceaux ici ou s'assurer que
# extract_long_text mette à jour un callback.
# Pour faire simple, on lance l'extraction :
status_text.text("Analyse des segments en cours...")
progress_bar.progress(25) # Simulation d'étape 1
graph_data = st.session_state.extractor.extract_long_text(
text_extracted,
temperature=ia_temp
)
progress_bar.progress(100)
status_text.text("Extraction terminée.")
if graph_data:
st.session_state.last_graph = graph_data
st.success(f"Réussite : {len(graph_data.get('entities', []))} entités identifiées.")
else:
st.error("L'IA n'a pas pu structurer les données.")
# 3. Affichage du JSON
if 'last_graph' in st.session_state:
st.markdown("#### FORMAT JSON (BRUT)")
st.json(st.session_state.last_graph)
# --- TAB 3: VISUALISATION GRAPHE ---
with tab_visualisation:
st.subheader("◯⎯| INTERFACE CINÉTIQUE VISUELLE")
if 'last_graph' in st.session_state and st.session_state.last_graph:
try:
# Initialisation du graphe PyVis
net = Network(height="700px", width="100%", bgcolor="#0b0d11", font_color="#e6edf3", directed=True)
import hashlib
def auto_color(text):
hash_hex = hashlib.md5(text.lower().encode()).hexdigest()
return f"#{hash_hex[:6]}"
found_types = {}
# Ajout des Noeuds
for ent in st.session_state.last_graph.get("entities", []):
e_type = ent.get("type", "Unknown")
e_color = auto_color(e_type)
found_types[e_type] = e_color
net.add_node(
ent["id"],
label=ent["name"],
title=f"TYPE: {e_type}\n{ent.get('description')}",
color=e_color,
shape="dot",
size=25
)
# Ajout des Relations
for rel in st.session_state.last_graph.get("relationships", []):
net.add_edge(
rel["from"],
rel["to"],
label=rel.get("type", "LINK"),
color="#30363d",
arrows="to"
)
net.set_options('{"physics": {"forceAtlas2Based": {"gravitationalConstant": -100, "centralGravity": 0.01}, "solver": "forceAtlas2Based"}}')
# Affichage de la légende
st.write("**Légende détectée :**")
leg_cols = st.columns(len(found_types) if len(found_types) > 0 else 1)
for idx, (t_name, t_color) in enumerate(found_types.items()):
leg_cols[idx % len(leg_cols)].markdown(f"<span style='color:{t_color}'>●</span> {t_name}", unsafe_allow_html=True)
# Rendu du graphe
path = "temp_graph_viz.html"
net.save_graph(path)
with open(path, 'r', encoding='utf-8') as f:
st.components.v1.html(f.read(), height=750)
except Exception as e:
st.error(f"Erreur de rendu visuel : {e}")
else:
st.warning("⚠️ Aucune donnée disponible. Veuillez d'abord générer l'intelligence dans l'onglet 'ENTITÉS & RELATIONS'.")
|