mazen248 commited on
Commit
181e9af
Β·
1 Parent(s): 935e6c6

docs: enhance README and improve Streamlit interface

Browse files
Files changed (3) hide show
  1. README.md +33 -7
  2. main.py +23 -20
  3. streamlit_app.py +19 -7
README.md CHANGED
@@ -8,6 +8,12 @@
8
  ![SentenceTransformers](https://img.shields.io/badge/SentenceTransformers-5.5-0A66C2)
9
  ![Groq](https://img.shields.io/badge/Groq-LLM-FF6B00)
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
- ### 2) Install dependencies
 
 
46
 
47
- Using pip:
48
 
 
 
 
49
  ```
50
- pip install -r requirements.txt
 
 
 
51
  ```
52
 
53
- ### 3) Run the API
 
 
 
54
 
 
 
 
 
 
 
 
55
  ```
 
 
 
56
  uvicorn main:app --reload --port 8000
57
  ```
58
 
59
- ### 4) Run the UI
60
-
61
- ```
62
  streamlit run streamlit_app.py
63
  ```
64
 
 
8
  ![SentenceTransformers](https://img.shields.io/badge/SentenceTransformers-5.5-0A66C2)
9
  ![Groq](https://img.shields.io/badge/Groq-LLM-FF6B00)
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
- print("[API] Action detected β†’ Triggering n8n workflow...")
97
-
98
- try:
99
- # Send POST request to n8n webhook
100
- res = requests.post(
101
- N8N_WEBHOOK_URL, # destination (n8n)
102
- json={ # data sent to n8n
103
- "query": request.query,
104
- "name": name,
105
- "answer": response["answer"],
106
- "sources": response["sources"]
107
- },
108
- timeout=5 # prevent hanging if n8n is slow
109
- )
110
-
111
- print(f"[API] n8n status: {res.status_code}")
112
- print(f"[API] n8n response: {res.text}")
113
-
114
- except Exception as e:
115
- print(f"[API] n8n error: {str(e)}")
 
 
 
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 Dashboard"],
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 Dashboard":
437
- st.markdown("### 🎫 Tickets Dashboard")
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, ttl=60)
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: