V2 / app.py
Amdreas's picture
Update app.py
e2ec151 verified
Raw History Blame Contribute Delete
5.17 kB
import os, io, re
os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1"
import gradio as gr
from pdfminer.high_level import extract_text
from sentence_transformers import SentenceTransformer
import numpy as np
EMBED_MODEL_ID = "intfloat/multilingual-e5-small" # malý, multi-lingual
_embedder = None
# In-memory knižnica
library_chunks = [] # list[str]
library_meta = [] # list[{"source":..., "idx":...}]
library_embeddings = None # np.ndarray [N, D] (normované)
def get_embedder():
global _embedder
if _embedder is None:
_embedder = SentenceTransformer(EMBED_MODEL_ID)
return _embedder
def clean_text(t):
return re.sub(r"\s+", " ", t).strip()
def chunk_text(text, size=1500, overlap=200):
text = clean_text(text)
chunks, i = [], 0
step = max(1, size - overlap)
L = len(text)
while i < L:
seg = text[i:i+size]
if seg.strip():
chunks.append(seg)
i += step
return chunks
def embed_passages(texts):
emb = get_embedder()
vecs = emb.encode([f"passage: {t}" for t in texts], normalize_embeddings=True)
return np.asarray(vecs, dtype=np.float32)
def embed_query(q):
emb = get_embedder()
vec = emb.encode([f"query: {q}"], normalize_embeddings=True)[0]
return np.asarray(vec, dtype=np.float32)
def add_to_library(chunks, source_name):
global library_chunks, library_meta, library_embeddings
vecs = embed_passages(chunks)
if library_embeddings is None:
library_embeddings = vecs
else:
library_embeddings = np.vstack([library_embeddings, vecs])
start = len(library_chunks)
library_chunks.extend(chunks)
library_meta.extend({"source": source_name, "idx": i} for i in range(start, start+len(chunks)))
return len(chunks)
def reset_library():
global library_chunks, library_meta, library_embeddings
library_chunks = []
library_meta = []
library_embeddings = None
def top_k(query, k=5):
if library_embeddings is None or len(library_chunks) == 0:
return []
qv = embed_query(query)
scores = library_embeddings @ qv # cosine, lebo normované
idx = np.argsort(-scores)[:k]
return [(float(scores[i]), library_chunks[i], library_meta[i]) for i in idx]
def build_answer(question, hits):
lines = [f"**Otázka:** {question}", "\n**Odpoveď (zo zdrojov):**"]
if not hits:
lines.append("_Nenašli sa relevantné pasáže. Nahraj PDF alebo skús inú formuláciu._")
for i,(s,txt,meta) in enumerate(hits, start=1):
snippet = clean_text(txt)[:400]
src = meta.get("source", "PDF")
lines.append(f"[{i}] {snippet} … (score {s:.2f}, zdroj: {src})")
return "\n".join(lines)
# ---------- UI (Gradio 3.x) ----------
with gr.Blocks(title="EDM & Fanuc – PDF RAG (bez Qdrant)") as demo:
gr.Markdown("# 🔧 EDM & Fanuc – PDF RAG (bez Qdrant)")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### 📥 Ingest PDF (textové, nie skeny)")
topic_dd = gr.Dropdown(choices=["EDM","FANUC","general"], value="EDM", label="Topic (len štítok)")
chunk_chars = gr.Slider(800, 2500, value=1500, step=100, label="Chunk size")
overlap = gr.Slider(0, 400, value=200, step=20, label="Overlap")
upload = gr.File(file_count="multiple", file_types=[".pdf"], label="📁 PDF súbory")
ingest_btn = gr.Button("📥 Pridať do knižnice")
reset_btn = gr.Button("🗑️ Vyčistiť knižnicu")
ingest_out = gr.Markdown()
def _ingest(files, topic, size, ov):
if not files:
return "⚠️ Nahraj aspoň jedno PDF."
total = 0
for f in files:
name = getattr(f, "name", "document.pdf")
text = extract_text(io.BytesIO(f.read()))
chunks = chunk_text(text, size, ov)
total += add_to_library(chunks, f"{topic}:{name}")
return f"✅ Pridané chunky: {total}. V knižnici: {len(library_chunks)} pasáží."
def _reset():
reset_library()
return "✅ Knižnica vyčistená."
ingest_btn.click(_ingest, inputs=[upload, topic_dd, chunk_chars, overlap], outputs=ingest_out)
reset_btn.click(_reset, outputs=ingest_out)
with gr.Column(scale=2):
gr.Markdown("### 💬 Chat z lokálnej knižnice")
question = gr.Textbox(label="Otázka", placeholder="Napr. G41 vs G42 na Fanuc…", lines=3)
topk = gr.Slider(1, 10, value=5, step=1, label="Top-K pasáží")
ask_btn = gr.Button("💬 Spýtať sa")
answer_md = gr.Markdown()
def _ask(q, k):
return build_answer(q, top_k(q, int(k)))
ask_btn.click(_ask, inputs=[question, topk], outputs=answer_md)
if __name__ == "__main__":
# V 3.x netreba show_api=False; táto verzia je stabilná a bez "No API found"
demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))