Spaces:
Runtime error
Runtime error
docs: enhance README and improve Streamlit interface
Browse files- README.md +33 -7
- main.py +23 -20
- streamlit_app.py +19 -7
README.md
CHANGED
|
@@ -8,6 +8,12 @@
|
|
| 8 |

|
| 9 |

|
| 10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
RAG-based telecom support assistant for Arabic and English with hybrid retrieval, query rewriting, and ticket automation. The UI is built with Streamlit and the API runs on FastAPI. Ticket creation is triggered automatically via n8n when the answer implies an action, and the UI asks for the user name only in those cases.
|
| 12 |
|
| 13 |
## Features
|
|
@@ -42,23 +48,43 @@ Then edit `.env`:
|
|
| 42 |
GROQ_API_KEY=your_key_here
|
| 43 |
```
|
| 44 |
|
| 45 |
-
###
|
|
|
|
|
|
|
| 46 |
|
| 47 |
-
|
| 48 |
|
|
|
|
|
|
|
|
|
|
| 49 |
```
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
| 51 |
```
|
| 52 |
|
| 53 |
-
|
|
|
|
|
|
|
|
|
|
| 54 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
```
|
|
|
|
|
|
|
|
|
|
| 56 |
uvicorn main:app --reload --port 8000
|
| 57 |
```
|
| 58 |
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
```
|
| 62 |
streamlit run streamlit_app.py
|
| 63 |
```
|
| 64 |
|
|
|
|
| 8 |

|
| 9 |

|
| 10 |
|
| 11 |
+
## π Live Demo
|
| 12 |
+
|
| 13 |
+
Experience the NileTel Arabic AI Assistant in action!
|
| 14 |
+
|
| 15 |
+
π <b><a href="https://telecom-rag-system.streamlit.app/" style="color: #007bff; text-decoration: none;">Launch the Live Demo Here</a></b>
|
| 16 |
+
|
| 17 |
RAG-based telecom support assistant for Arabic and English with hybrid retrieval, query rewriting, and ticket automation. The UI is built with Streamlit and the API runs on FastAPI. Ticket creation is triggered automatically via n8n when the answer implies an action, and the UI asks for the user name only in those cases.
|
| 18 |
|
| 19 |
## Features
|
|
|
|
| 48 |
GROQ_API_KEY=your_key_here
|
| 49 |
```
|
| 50 |
|
| 51 |
+
### Installation & Running Options
|
| 52 |
+
|
| 53 |
+
Choose one of the following methods to run the project. We recommend using **Option A** for significantly faster dependency installation and execution.
|
| 54 |
|
| 55 |
+
#### Option A: Fast Setup with `uv` (Recommended)
|
| 56 |
|
| 57 |
+
**1) Install dependencies:**
|
| 58 |
+
```bash
|
| 59 |
+
uv pip install -r requirements.txt
|
| 60 |
```
|
| 61 |
+
|
| 62 |
+
**2) Run the API:**
|
| 63 |
+
```bash
|
| 64 |
+
uv run uvicorn main:app --reload --port 8000
|
| 65 |
```
|
| 66 |
|
| 67 |
+
**3) Run the UI:**
|
| 68 |
+
```bash
|
| 69 |
+
uv run streamlit run streamlit_app.py
|
| 70 |
+
```
|
| 71 |
|
| 72 |
+
---
|
| 73 |
+
|
| 74 |
+
#### Option B: Standard Setup with `pip`
|
| 75 |
+
|
| 76 |
+
**1) Install dependencies:**
|
| 77 |
+
```bash
|
| 78 |
+
pip install -r requirements.txt
|
| 79 |
```
|
| 80 |
+
|
| 81 |
+
**2) Run the API:**
|
| 82 |
+
```bash
|
| 83 |
uvicorn main:app --reload --port 8000
|
| 84 |
```
|
| 85 |
|
| 86 |
+
**3) Run the UI:**
|
| 87 |
+
```bash
|
|
|
|
| 88 |
streamlit run streamlit_app.py
|
| 89 |
```
|
| 90 |
|
main.py
CHANGED
|
@@ -93,26 +93,29 @@ def ask(request: QueryRequest):
|
|
| 93 |
# 2. IF ACTION NEEDED β CALL n8n
|
| 94 |
# --------------------------------------------------------
|
| 95 |
if response["needs_action"] == "YES":
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
| 116 |
|
| 117 |
else:
|
| 118 |
print("[API] No action needed")
|
|
|
|
| 93 |
# 2. IF ACTION NEEDED β CALL n8n
|
| 94 |
# --------------------------------------------------------
|
| 95 |
if response["needs_action"] == "YES":
|
| 96 |
+
if not name:
|
| 97 |
+
print("[API] Action detected but no name provided β Skipping n8n.")
|
| 98 |
+
else:
|
| 99 |
+
print("[API] Action detected β Triggering n8n workflow...")
|
| 100 |
+
|
| 101 |
+
try:
|
| 102 |
+
# Send POST request to n8n webhook
|
| 103 |
+
res = requests.post(
|
| 104 |
+
N8N_WEBHOOK_URL, # destination (n8n)
|
| 105 |
+
json={ # data sent to n8n
|
| 106 |
+
"query": request.query,
|
| 107 |
+
"name": name,
|
| 108 |
+
"answer": response["answer"],
|
| 109 |
+
"sources": response["sources"]
|
| 110 |
+
},
|
| 111 |
+
timeout=5 # prevent hanging if n8n is slow
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
print(f"[API] n8n status: {res.status_code}")
|
| 115 |
+
print(f"[API] n8n response: {res.text}")
|
| 116 |
+
|
| 117 |
+
except Exception as e:
|
| 118 |
+
print(f"[API] n8n error: {str(e)}")
|
| 119 |
|
| 120 |
else:
|
| 121 |
print("[API] No action needed")
|
streamlit_app.py
CHANGED
|
@@ -19,6 +19,10 @@ if "last_response" not in st.session_state:
|
|
| 19 |
st.session_state.last_response = None
|
| 20 |
st.session_state.last_query = ""
|
| 21 |
st.session_state.ticket_name = ""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
# Injecting Custom CSS for a VERY GOOD UI
|
| 24 |
st.markdown(
|
|
@@ -299,7 +303,7 @@ with st.sidebar:
|
|
| 299 |
st.markdown("## Navigation")
|
| 300 |
page = st.radio(
|
| 301 |
"Choose a View",
|
| 302 |
-
["π¬ Chat Assistant", "π« Tickets
|
| 303 |
label_visibility="collapsed"
|
| 304 |
)
|
| 305 |
|
|
@@ -309,13 +313,16 @@ with st.sidebar:
|
|
| 309 |
<div class="sidebar-card">
|
| 310 |
<div class="sidebar-card-title">System Status</div>
|
| 311 |
<div class="sidebar-row"><span class="status-dot"></span> Live API</div>
|
| 312 |
-
<div class="sidebar-row">Data refresh: 60s</div>
|
| 313 |
<div class="sidebar-row">Languages: AR / EN</div>
|
| 314 |
</div>
|
| 315 |
""",
|
| 316 |
unsafe_allow_html=True
|
| 317 |
)
|
| 318 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 319 |
|
| 320 |
# =========================
|
| 321 |
# π MAIN VIEW ROUTING
|
|
@@ -433,17 +440,22 @@ if page == "π¬ Chat Assistant":
|
|
| 433 |
chips_html = "".join([f"<span class='source-chip'>π {html.escape(str(s))}</span>" for s in sources])
|
| 434 |
st.markdown(f"<div>{chips_html}</div>", unsafe_allow_html=True)
|
| 435 |
|
| 436 |
-
elif page == "π« Tickets
|
| 437 |
-
st.markdown("### π« Tickets
|
| 438 |
st.markdown("<p style='color:#5a5a5a; margin-bottom: 20px;'>View and manage all telecom support tickets fetched live from the central system.</p>", unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 439 |
|
| 440 |
-
@st.cache_data(show_spinner=False
|
| 441 |
-
def _load_tickets(url):
|
| 442 |
return pd.read_csv(url)
|
| 443 |
|
| 444 |
with st.spinner("Loading tickets from live database..."):
|
| 445 |
try:
|
| 446 |
-
df = _load_tickets(SHEET_URL)
|
| 447 |
if df.empty:
|
| 448 |
st.info("No tickets available at the moment.")
|
| 449 |
else:
|
|
|
|
| 19 |
st.session_state.last_response = None
|
| 20 |
st.session_state.last_query = ""
|
| 21 |
st.session_state.ticket_name = ""
|
| 22 |
+
if "last_page" not in st.session_state:
|
| 23 |
+
st.session_state.last_page = ""
|
| 24 |
+
if "tickets_refresh" not in st.session_state:
|
| 25 |
+
st.session_state.tickets_refresh = 0
|
| 26 |
|
| 27 |
# Injecting Custom CSS for a VERY GOOD UI
|
| 28 |
st.markdown(
|
|
|
|
| 303 |
st.markdown("## Navigation")
|
| 304 |
page = st.radio(
|
| 305 |
"Choose a View",
|
| 306 |
+
["π¬ Chat Assistant", "π« Tickets Table"],
|
| 307 |
label_visibility="collapsed"
|
| 308 |
)
|
| 309 |
|
|
|
|
| 313 |
<div class="sidebar-card">
|
| 314 |
<div class="sidebar-card-title">System Status</div>
|
| 315 |
<div class="sidebar-row"><span class="status-dot"></span> Live API</div>
|
|
|
|
| 316 |
<div class="sidebar-row">Languages: AR / EN</div>
|
| 317 |
</div>
|
| 318 |
""",
|
| 319 |
unsafe_allow_html=True
|
| 320 |
)
|
| 321 |
|
| 322 |
+
if page == "π« Tickets Table" and st.session_state.last_page != page:
|
| 323 |
+
st.session_state.tickets_refresh += 1
|
| 324 |
+
st.session_state.last_page = page
|
| 325 |
+
|
| 326 |
|
| 327 |
# =========================
|
| 328 |
# π MAIN VIEW ROUTING
|
|
|
|
| 440 |
chips_html = "".join([f"<span class='source-chip'>π {html.escape(str(s))}</span>" for s in sources])
|
| 441 |
st.markdown(f"<div>{chips_html}</div>", unsafe_allow_html=True)
|
| 442 |
|
| 443 |
+
elif page == "π« Tickets Table":
|
| 444 |
+
st.markdown("### π« Tickets Table")
|
| 445 |
st.markdown("<p style='color:#5a5a5a; margin-bottom: 20px;'>View and manage all telecom support tickets fetched live from the central system.</p>", unsafe_allow_html=True)
|
| 446 |
+
|
| 447 |
+
col1, col2 = st.columns([8, 2])
|
| 448 |
+
with col2:
|
| 449 |
+
if st.button("π Refresh Data", use_container_width=True):
|
| 450 |
+
st.session_state.tickets_refresh += 1
|
| 451 |
|
| 452 |
+
@st.cache_data(show_spinner=False)
|
| 453 |
+
def _load_tickets(url, refresh_token):
|
| 454 |
return pd.read_csv(url)
|
| 455 |
|
| 456 |
with st.spinner("Loading tickets from live database..."):
|
| 457 |
try:
|
| 458 |
+
df = _load_tickets(SHEET_URL, st.session_state.tickets_refresh)
|
| 459 |
if df.empty:
|
| 460 |
st.info("No tickets available at the moment.")
|
| 461 |
else:
|