Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,12 +1,126 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from groq import Groq
|
| 3 |
+
import gradio as gr
|
| 4 |
+
import pypdf,numpy as np
|
| 5 |
+
from docx import Document
|
| 6 |
+
from pptx import Presentation
|
| 7 |
+
from sentence_transformers import SentenceTransformer
|
| 8 |
+
from sklearn.metrics.pairwise import cosine_similarity
|
| 9 |
+
from reportlab.platypus import SimpleDocTemplate,Paragraph,Spacer,Table,TableStyle
|
| 10 |
+
from reportlab.lib.styles import getSampleStyleSheet
|
| 11 |
+
from reportlab.lib.pagesizes import A4
|
| 12 |
+
from reportlab.lib.units import inch
|
| 13 |
+
from reportlab.lib import colors
|
| 14 |
+
from io import BytesIO
|
| 15 |
+
|
| 16 |
+
os.environ["HF_HOME"] = "/tmp/huggingface"
|
| 17 |
+
client = Groq(api_key=os.environ.get("GROQ_API_KEY"))
|
| 18 |
+
embed = SentenceTransformer("all-MiniLM-L6-v2","cpu")
|
| 19 |
+
|
| 20 |
+
MODELS = ["llama-3.3-70b-versatile","gpt-oss-120b","gpt-oss-20b","llama-3.1-8b-instant"]
|
| 21 |
+
|
| 22 |
+
ECB_RENTE = "**ECB depositorente 2026**\nHuidig: **2,00 %** (feb 2026)\nVerwachting: geen verandering in 2026 (85–90 %)\nStabiel tot eind 2026 / begin 2027\nEerste verhoging waarschijnlijk 2027"
|
| 23 |
+
ECB_INFLATIE = "**Eurozone inflatie 2026**\nECB staff: **1,9 %** headline\nSPF/Reuters: **1,8–1,9 %**\nCore: **2,0–2,2 %**\nLanger termijn: stabiel ~2,0 %"
|
| 24 |
+
|
| 25 |
+
def read(f):
|
| 26 |
+
t=""
|
| 27 |
+
for x in f or []:
|
| 28 |
+
try:
|
| 29 |
+
e=os.path.splitext(x.name)[1].lower()
|
| 30 |
+
if e==".pdf":t+="".join(p.extract_text()or""for p in pypdf.PdfReader(x.name).pages)
|
| 31 |
+
elif e==".docx":t+="\n".join(p.text for p in Document(x.name).paragraphs)
|
| 32 |
+
elif e==".pptx":t+="\n".join(sh.text for s in Presentation(x.name).slides for sh in s.shapes if hasattr(sh,"text"))
|
| 33 |
+
elif e in(".txt",".md"):t+=open(x.name,"r",encoding="utf-8",errors="ignore").read()
|
| 34 |
+
except:pass
|
| 35 |
+
return t.strip()
|
| 36 |
+
|
| 37 |
+
def analyse(q,f,l,m):
|
| 38 |
+
if not q.strip()or not f:return "Vraag + bestanden verplicht.",None,gr.update(visible=False)
|
| 39 |
+
if not client.api_key:return "GROQ_API_KEY ontbreekt.",None,gr.update(visible=False)
|
| 40 |
+
|
| 41 |
+
txt=read(f)
|
| 42 |
+
if len(txt)<50:return "Te weinig tekst.",None,gr.update(visible=False)
|
| 43 |
+
|
| 44 |
+
ch=[txt[i:i+600].strip()for i in range(0,len(txt),450)if len(txt[i:i+600].strip())>30]
|
| 45 |
+
if not ch:return "Geen bruikbare tekst.",None,gr.update(visible=False)
|
| 46 |
+
|
| 47 |
+
sc=cosine_similarity(embed.encode([q]),embed.encode(ch)).flatten()
|
| 48 |
+
idx=np.argsort(sc)[-3:][::-1]
|
| 49 |
+
ctx="\n\n".join(ch[i]for i in idx)
|
| 50 |
+
|
| 51 |
+
msgs=[{"role":"system","content":f"Strategische analist. Antwoord alleen in {l}."},
|
| 52 |
+
{"role":"user","content":f"Context:\n{ctx}\n\nVraag: {q}"}]
|
| 53 |
+
|
| 54 |
+
s=client.chat.completions.create(model=m,messages=msgs,temperature=0.65,max_tokens=4500,stream=True)
|
| 55 |
+
u=m
|
| 56 |
+
if isinstance(s,str):
|
| 57 |
+
for fb in[m for m in MODELS if m!=m]:
|
| 58 |
+
s=client.chat.completions.create(model=fb,messages=msgs,temperature=0.65,max_tokens=4500,stream=True)
|
| 59 |
+
if not isinstance(s,str):u=fb;break
|
| 60 |
+
else:return f"Alle modellen faalden.\n\n{s}",None,gr.update(visible=False)
|
| 61 |
+
|
| 62 |
+
a=""
|
| 63 |
+
for c in s:a+=c.choices[0].delta.content or ""
|
| 64 |
+
|
| 65 |
+
pdf=BytesIO()
|
| 66 |
+
try:
|
| 67 |
+
doc=SimpleDocTemplate(pdf,pagesize=A4)
|
| 68 |
+
st=getSampleStyleSheet()
|
| 69 |
+
d=[["Match","Tekst"]]+[[f"{sc[i]:.1%}",Paragraph(ch[i].replace("\n","<br/>"),st["BodyText"])]for i in idx]
|
| 70 |
+
t=Table(d,colWidths=[0.6*inch,6.7*inch])
|
| 71 |
+
t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,0),colors.HexColor("#004080")),
|
| 72 |
+
("GRID",(0,0),(-1,-1),0.3,colors.grey),
|
| 73 |
+
("VALIGN",(0,0),(-1,-1),"TOP"),
|
| 74 |
+
("FONTSIZE",(0,0),(-1,-1),6.5)]))
|
| 75 |
+
story=[Paragraph("RAPPORT",st["Title"]),Spacer(1,0.08*inch),
|
| 76 |
+
Paragraph(f"{q} • {u}",st["Heading2"]),Spacer(1,0.08*inch),
|
| 77 |
+
Paragraph(a.replace("\n","<br/>"),st["Normal"]),Spacer(1,0.15*inch),t]
|
| 78 |
+
doc.build(story)
|
| 79 |
+
pdf.seek(0)
|
| 80 |
+
return f"<div style='white-space:pre-wrap;line-height:1.7;font-size:17px;color:#00f0ff;text-shadow:0 0 10px #00f0ff;user-select:text;'>{a.replace('\n','<br>')}</div>", \
|
| 81 |
+
{"value": pdf.getvalue(), "orig_name": "rapport.pdf", "mime_type": "application/pdf"}, \
|
| 82 |
+
gr.update(visible=True)
|
| 83 |
+
except Exception as e:
|
| 84 |
+
print("PDF-fout:",e)
|
| 85 |
+
return f"<div style='white-space:pre-wrap;line-height:1.7;font-size:17px;color:#00f0ff;text-shadow:0 0 10px #00f0ff;user-select:text;'>{a.replace('\n','<br>')}</div>", None, gr.update(visible=False)
|
| 86 |
+
|
| 87 |
+
css="""
|
| 88 |
+
.gradio-container{background:#001122;color:#00f0ff;font-family:system-ui,sans-serif;max-width:1400px;margin:1.5rem auto;border:1px solid #00408088;border-radius:12px;}
|
| 89 |
+
h1,h2,h3{color:#00f0ff !important;text-shadow:0 0 16px #00f0ffcc;font-weight:700;}
|
| 90 |
+
.gr-markdown,.gr-markdown *{color:#00f0ff !important;text-shadow:0 0 14px #00f0ffaa !important;}
|
| 91 |
+
.gr-file-label,.gr-file-description,.gr-label,.gr-input-label,.gr-dropdown-label,.gr-file-upload-text{color:#00f0ff !important;text-shadow:0 0 10px #00f0ff;font-weight:bold;}
|
| 92 |
+
.gr-dropdown,.gr-dropdown *{color:#00f0ff !important;}
|
| 93 |
+
button{background:linear-gradient(135deg,#00aaff,#004080) !important;color:#000 !important;font-weight:600;border:none;border-radius:8px;box-shadow:0 4px 15px #004080aa;}
|
| 94 |
+
button:hover{box-shadow:0 8px 25px #00aaffdd;}
|
| 95 |
+
input,textarea{background:#002244 !important;border:1px solid #004080 !important;color:#00f0ff !important;border-radius:8px;}
|
| 96 |
+
.gr-html{background:#0a1421;padding:1.5rem;border-radius:8px;border:1px solid #00408088;}
|
| 97 |
+
footer{display:none;}
|
| 98 |
+
"""
|
| 99 |
+
|
| 100 |
+
with gr.Blocks(title="Strategische Analyse")as d:
|
| 101 |
+
with gr.Tabs():
|
| 102 |
+
with gr.Tab("Document Analyse"):
|
| 103 |
+
with gr.Row():
|
| 104 |
+
with gr.Column(scale=1,min_width=320):
|
| 105 |
+
f=gr.File(file_count="multiple",file_types=[".pdf",".docx",".pptx",".txt",".md"],label="Bestanden")
|
| 106 |
+
l=gr.Dropdown(["Nederlands","English","Español","Deutsch"],value="Nederlands",label="Taal")
|
| 107 |
+
gr.Markdown("**Model suggesties** \n• Diep/vergelijken → gpt-oss-120b \n• Snel samenvatten → gpt-oss-20b \n• Strategisch → llama-3.3-70b-versatile \n• Testen → llama-3.1-8b-instant")
|
| 108 |
+
m=gr.Dropdown(MODELS,value=MODELS[0],label="Kies een model...",interactive=True)
|
| 109 |
+
q=gr.Textbox(label="Vraag",lines=3,placeholder="Wat wil je weten?")
|
| 110 |
+
with gr.Row():r=gr.Button("Analyseren",variant="primary");c=gr.Button("Reset")
|
| 111 |
+
dl=gr.DownloadButton("Opslaan als PDF",visible=False)
|
| 112 |
+
with gr.Column(scale=2):
|
| 113 |
+
o=gr.HTML("<div style='padding:1rem;color:#00f0ff;text-shadow:0 0 10px #00f0ff;'>Klaar…</div>")
|
| 114 |
+
|
| 115 |
+
def rst():return None,"Nederlands",MODELS[0],"","<div style='padding:1rem;color:#00f0ff;text-shadow:0 0 10px #00f0ff;'>Klaar…</div>",gr.update(visible=False)
|
| 116 |
+
|
| 117 |
+
c.click(rst,outputs=[f,l,m,q,o,dl])
|
| 118 |
+
r.click(analyse,inputs=[q,f,l,m],outputs=[o,dl])
|
| 119 |
+
|
| 120 |
+
with gr.Tab("Dashboard (ECB 2026)"):
|
| 121 |
+
gr.Markdown("### Actuele ECB Verwachtingen – Februari 2026")
|
| 122 |
+
gr.Markdown(ECB_RENTE)
|
| 123 |
+
gr.Markdown(ECB_INFLATIE)
|
| 124 |
+
gr.Markdown("**Bron:** ECB projections, SPF Q1 2026, Reuters-poll. Data-dependent beleid.")
|
| 125 |
+
|
| 126 |
+
d.launch(css=css)
|