"""
app.py — RoCodex (Streamlit version)
────────────────────────────────────────
Run locally:
streamlit run app.py
Deploy to HuggingFace Spaces:
- Set SDK to "Streamlit" in your Space settings
- Upload this file + rag.py + data/ folder
- Set GROQ_API_KEY as a Space secret
Install:
pip install streamlit groq
"""
import os
import random
import streamlit as st
from rag import answer as rag_answer
# ── Page config ───────────────────────────────────────────────────────────────
st.set_page_config(
page_title="RoCodex — Asistent Juridic",
page_icon="⚖️",
layout="centered",
)
GROQ_API_KEY = os.environ.get("GROQ_API_KEY", "")
# ── SVG logo ──────────────────────────────────
# Used in: hero screen, sidebar, assistant chat avatar
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
with open(os.path.join(BASE_DIR, "app_logo.svg"), "r", encoding="utf-8") as f:
LOGO_SVG = f.read()
import base64
LOGO_B64 = base64.b64encode(LOGO_SVG.encode()).decode()
LOGO_URI = f"data:image/svg+xml;base64,{LOGO_B64}"
# ── Welcome messages ──────────────────────────────────────────────────────────
WELCOME_MESSAGES = [
"Bine ai venit! Pune orice întrebare despre legislația română.",
"Salut! Sunt aici să te ajut să înțelegi legile din România.",
"Salut! Întreabă-mă orice despre drepturile tale legale.",
"Salut! Explorează legislația română cu ajutorul meu.",
]
EXAMPLES = [
"Ce drepturi are un salariat la concediu de odihnă?",
"Care sunt obligațiile angajatorului față de salariat?",
"Ce este prezumția de nevinovăție?",
"Cum se calculează indemnizația de concediu?",
]
# ── CSS ───────────────────────────────────────────────────────────────────────
st.markdown(f"""
""", unsafe_allow_html=True)
# ── Sidebar ───────────────────────────────────────────────────────────────────
with st.sidebar:
st.markdown(f"""
RoCodex
""", unsafe_allow_html=True)
st.divider()
api_key = st.text_input(
"🔑 Groq API Key",
value=GROQ_API_KEY,
type="password",
placeholder="gsk_...",
help="Cheie gratuită la console.groq.com",
)
st.caption("Cheia nu este salvată nicăieri.")
st.divider()
st.markdown("""
**Legislație acoperită:**
- 📋 Codul Muncii
- 📋 Codul Civil
- ⚖️ Codul Penal
- 🏛️ Cod Procedură Civilă
- 🏛️ Cod Procedură Penală
- 🏢 Legea Societăților
""")
st.divider()
st.caption(
"⚠️ RoCodex oferă informații juridice generale. "
"Nu constituie consultanță juridică. "
"Consultați un avocat pentru situații specifice."
)
# ── Session state ─────────────────────────────────────────────────────────────
if "messages" not in st.session_state:
# Each message: {"role": "user"|"assistant", "content": str, "sources": list|None}
st.session_state.messages = []
if "welcome_msg" not in st.session_state:
st.session_state.welcome_msg = random.choice(WELCOME_MESSAGES)
# ── Helper: render one assistant message (text + collapsible sources) ─────────
def render_assistant(content: str, sources: list):
st.markdown(content)
if sources:
with st.expander("📚 Surse folosite", expanded=False):
for i, src in enumerate(sources, 1):
score_pct = int(src["score"] * 100)
st.markdown(
f"**[{i}] {src['law_title']} — {src['article_number']}** "
f"`{score_pct}% relevanță`"
)
st.caption(
src["text"][:300] + ("…" if len(src["text"]) > 300 else "")
)
if i < len(sources):
st.divider()
# ── HERO SCREEN ───────────────────────────────────────────────────────────────
if not st.session_state.messages:
st.markdown(f"""
RoCodex
Asistent juridic bazat pe legislația română
{st.session_state.welcome_msg}
""", unsafe_allow_html=True)
st.markdown("**Încearcă una din întrebările de mai jos:**")
cols = st.columns(2)
for i, example in enumerate(EXAMPLES):
with cols[i % 2]:
if st.button(example, key=f"ex_{i}", use_container_width=True):
st.session_state.prefill = example
st.rerun()
# ── CHAT SCREEN ───────────────────────────────────────────────────────────────
else:
for msg in st.session_state.messages:
if msg["role"] == "user":
with st.chat_message("user"):
st.markdown(msg["content"])
else:
with st.chat_message("assistant"):
render_assistant(msg["content"], msg.get("sources", []))
# ── Chat input ────────────────────────────────────────────────────────────────
prefill_value = st.session_state.pop("prefill", "")
user_input = st.chat_input("Scrie întrebarea ta juridică...")
question = user_input or prefill_value
if question:
question = question.strip()
# Save & display user message
st.session_state.messages.append({
"role": "user", "content": question, "sources": None
})
# Check key
key = api_key.strip() if api_key.strip() else GROQ_API_KEY
if not key:
st.session_state.messages.append({
"role": "assistant",
"content": "⚠️ Lipsește Groq API key. Introdu cheia în sidebar.",
"sources": [],
})
st.rerun()
# Call RAG
with st.spinner("Caut în legislație..."):
try:
result = rag_answer(question, groq_api_key=key)
reply = result["answer"]
sources = result["sources"]
except Exception as e:
reply = f"❌ Eroare: {e}"
sources = []
# Save assistant message WITH sources separately
st.session_state.messages.append({
"role": "assistant",
"content": reply,
"sources": sources,
})
st.rerun()