|
| 1 | +import os |
| 2 | +from typing import Any, Dict, List |
| 3 | + |
1 | 4 | import requests |
2 | 5 | import streamlit as st |
3 | 6 |
|
4 | | -API_URL = "http://127.0.0.1:8000/query" |
| 7 | +# -------------------------------------------------- |
| 8 | +# Configuration |
| 9 | +# -------------------------------------------------- |
| 10 | + |
| 11 | +API_URL = os.getenv("LEGAL_API_URL", "http://127.0.0.1:8000/query") |
| 12 | +REQUEST_TIMEOUT = 120 |
| 13 | +TOP_K = 5 |
| 14 | + |
| 15 | +session = requests.Session() |
| 16 | + |
| 17 | + |
| 18 | +# -------------------------------------------------- |
| 19 | +# Page Setup |
| 20 | +# -------------------------------------------------- |
| 21 | + |
| 22 | +st.set_page_config( |
| 23 | + page_title="Ethiopian Legal Assistant", page_icon="⚖️", layout="centered" |
| 24 | +) |
| 25 | + |
| 26 | +st.title("⚖️ Ethiopian Legal Assistant") |
| 27 | +st.caption("Ask questions about Ethiopian law") |
| 28 | + |
| 29 | +st.markdown( |
| 30 | + """ |
| 31 | + <style> |
| 32 | + .block-container { |
| 33 | + max-width: 800px; |
| 34 | + } |
| 35 | + </style> |
| 36 | + """, |
| 37 | + unsafe_allow_html=True, |
| 38 | +) |
| 39 | + |
| 40 | +# -------------------------------------------------- |
| 41 | +# Session State |
| 42 | +# -------------------------------------------------- |
| 43 | + |
| 44 | +if "messages" not in st.session_state: |
| 45 | + st.session_state.messages = [] |
| 46 | + |
| 47 | + |
| 48 | +# -------------------------------------------------- |
| 49 | +# Sidebar |
| 50 | +# -------------------------------------------------- |
| 51 | + |
| 52 | +with st.sidebar: |
| 53 | + |
| 54 | + st.header("Settings") |
| 55 | + |
| 56 | + top_k = st.slider("Number of legal sources", min_value=1, max_value=10, value=TOP_K) |
5 | 57 |
|
6 | | -st.title("⚖️ የኢትዮጵያ ሕግ አጋዥ(ነጋሪት ጋዜጣ)") |
| 58 | + if st.button("Clear Chat"): |
| 59 | + st.session_state.messages = [] |
| 60 | + st.rerun() |
7 | 61 |
|
8 | | -query = st.text_input("የሕግ ጥያቄዎን ያስገቡ") |
9 | 62 |
|
10 | | -if st.button("ፈልግ") and query: |
| 63 | +# -------------------------------------------------- |
| 64 | +# API Client |
| 65 | +# -------------------------------------------------- |
11 | 66 |
|
12 | | - payload = {"query": query, "top_k": 5} |
| 67 | + |
| 68 | +def fetch_legal_results(query: str, top_k: int) -> List[Dict[str, Any]]: |
| 69 | + |
| 70 | + payload = {"query": query, "top_k": top_k} |
13 | 71 |
|
14 | 72 | try: |
15 | | - response = requests.post(API_URL, json=payload) |
16 | 73 |
|
17 | | - if response.status_code == 200: |
18 | | - results = response.json() |
| 74 | + response = session.post(API_URL, json=payload, timeout=REQUEST_TIMEOUT) |
| 75 | + |
| 76 | + response.raise_for_status() |
| 77 | + |
| 78 | + data = response.json() |
| 79 | + |
| 80 | + if not isinstance(data, list): |
| 81 | + raise RuntimeError("Invalid API response format") |
| 82 | + |
| 83 | + return data |
| 84 | + |
| 85 | + except requests.exceptions.Timeout: |
| 86 | + raise RuntimeError("⏱️ The legal search service timed out.") |
| 87 | + |
| 88 | + except requests.exceptions.ConnectionError: |
| 89 | + raise RuntimeError("🚨 Cannot connect to the legal API.") |
| 90 | + |
| 91 | + except requests.exceptions.HTTPError as exc: |
| 92 | + raise RuntimeError(f"❌ API error {exc.response.status_code}") |
| 93 | + |
| 94 | + except ValueError: |
| 95 | + raise RuntimeError("⚠️ Invalid response from API.") |
| 96 | + |
| 97 | + |
| 98 | +# -------------------------------------------------- |
| 99 | +# Format Answer |
| 100 | +# -------------------------------------------------- |
| 101 | + |
| 102 | + |
| 103 | +def build_answer(results: List[Dict[str, Any]]) -> str: |
| 104 | + |
| 105 | + if not results: |
| 106 | + return "⚠️ ይቅርታ፣ ከጥያቄዎ ጋር የሚዛመድ ህግ አልተገኘም።" |
| 107 | + |
| 108 | + results = sorted(results, key=lambda x: x.get("relevance_score", 0), reverse=True) |
| 109 | + |
| 110 | + answer = [] |
| 111 | + |
| 112 | + for doc in results: |
| 113 | + |
| 114 | + content = doc.get("content", "").strip() |
| 115 | + meta = doc.get("metadata", {}) or {} |
| 116 | + |
| 117 | + article = meta.get("article", "—") |
| 118 | + law_name = meta.get("law_name", "Unknown Law") |
| 119 | + page = meta.get("page_number", "—") |
| 120 | + |
| 121 | + score = doc.get("relevance_score", 0) |
| 122 | + score_pct = f"{score*100:.1f}%" |
| 123 | + |
| 124 | + block = f""" |
| 125 | + ### 📜 {law_name} |
| 126 | + **አንቀጽ:** {article} |
| 127 | + **ገጽ:** {page} |
| 128 | + **Relevance:** {score_pct} |
| 129 | + {content} |
| 130 | + --- |
| 131 | + """ |
| 132 | + |
| 133 | + answer.append(block) |
| 134 | + |
| 135 | + return "\n".join(answer) |
| 136 | + |
| 137 | + |
| 138 | +# -------------------------------------------------- |
| 139 | +# Display Chat History |
| 140 | +# -------------------------------------------------- |
| 141 | + |
| 142 | +for message in st.session_state.messages: |
| 143 | + |
| 144 | + with st.chat_message(message["role"]): |
| 145 | + st.markdown(message["content"]) |
| 146 | + |
| 147 | + |
| 148 | +# -------------------------------------------------- |
| 149 | +# User Input |
| 150 | +# -------------------------------------------------- |
| 151 | + |
| 152 | +prompt = st.chat_input("Ask a question about Ethiopian law...") |
| 153 | + |
| 154 | +if prompt: |
| 155 | + |
| 156 | + st.session_state.messages.append({"role": "user", "content": prompt}) |
| 157 | + |
| 158 | + with st.chat_message("user"): |
| 159 | + st.markdown(prompt) |
| 160 | + |
| 161 | + with st.chat_message("assistant"): |
| 162 | + |
| 163 | + with st.spinner("Searching Ethiopian law..."): |
| 164 | + |
| 165 | + try: |
| 166 | + |
| 167 | + results = fetch_legal_results(prompt, top_k) |
| 168 | + |
| 169 | + answer = build_answer(results) |
| 170 | + |
| 171 | + placeholder = st.empty() |
| 172 | + |
| 173 | + # simulate streaming response |
| 174 | + displayed = "" |
| 175 | + for chunk in answer.split("\n"): |
| 176 | + displayed += chunk + "\n" |
| 177 | + placeholder.markdown(displayed) |
| 178 | + |
| 179 | + st.session_state.messages.append( |
| 180 | + {"role": "assistant", "content": answer} |
| 181 | + ) |
| 182 | + |
| 183 | + except RuntimeError as err: |
| 184 | + |
| 185 | + st.error(str(err)) |
19 | 186 |
|
20 | | - if not results: |
21 | | - st.warning("ውጤት አልተገኘም") |
22 | | - else: |
23 | | - st.success(f"{len(results)} ውጤቶች ተገኝተዋል") |
| 187 | + st.session_state.messages.append( |
| 188 | + {"role": "assistant", "content": str(err)} |
| 189 | + ) |
24 | 190 |
|
25 | | - for i, doc in enumerate(results, 1): |
26 | | - st.subheader(f"ውጤት {i}") |
27 | | - st.write(doc["content"]) |
| 191 | + except Exception: |
28 | 192 |
|
29 | | - with st.expander("መረጃ (Metadata)"): |
30 | | - st.json(doc["metadata"]) |
| 193 | + error_msg = "⚠️ Unexpected system error." |
31 | 194 |
|
32 | | - else: |
33 | | - st.error(f"የAPI ስህተት: {response.text}") |
| 195 | + st.error(error_msg) |
34 | 196 |
|
35 | | - except requests.exceptions.RequestException as e: |
36 | | - st.error(f"የግንኙነት ስህተት: {e}") |
| 197 | + st.session_state.messages.append( |
| 198 | + {"role": "assistant", "content": error_msg} |
| 199 | + ) |
0 commit comments