CoderHassan commited on
Commit
940f39c
·
verified ·
1 Parent(s): 77b78bf

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -452
app.py DELETED
@@ -1,452 +0,0 @@
1
- """
2
- KhidmatAI — AI Service Orchestrator for Informal Economy
3
- """
4
-
5
- import streamlit as st
6
- import json, time, os, sys
7
- from datetime import datetime
8
- from pathlib import Path
9
-
10
- # ── path so agents/ is importable ───────────────────────────────────
11
- sys.path.insert(0, str(Path(__file__).parent))
12
- from agents import intent as intent_agent
13
- from agents import discovery as discovery_agent
14
- from agents import recommendation as recommendation_agent
15
- from agents import booking as booking_agent
16
-
17
- # ════════════════════════════════════════════════════════════════════
18
- # PAGE CONFIG
19
- # ════════════════════════════════════════════════════════════════════
20
- st.set_page_config(
21
- page_title="KhidmatAI",
22
- page_icon="🔧",
23
- layout="centered",
24
- )
25
-
26
- # ════════════════════════════════════════════════════════════════════
27
- # CUSTOM CSS
28
- # ════════════════════════════════════════════════════════════════════
29
- st.markdown("""
30
- <style>
31
- /* ── Global ─────────────────────────────────────────────── */
32
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
33
- html, body, [class*="css"] { font-family: 'Inter', sans-serif; }
34
-
35
- /* ── Hero banner ────────────────────────────────────────── */
36
- .hero {
37
- background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
38
- border-radius: 16px;
39
- padding: 32px 28px 24px;
40
- text-align: center;
41
- margin-bottom: 24px;
42
- border: 1px solid rgba(255,255,255,0.08);
43
- }
44
- .hero h1 { color: #ffffff; font-size: 2rem; font-weight: 700; margin: 0; letter-spacing: -0.5px; }
45
- .hero p { color: rgba(255,255,255,0.65); font-size: 0.95rem; margin: 8px 0 0; }
46
- .hero-badge {
47
- display: inline-block;
48
- background: rgba(99,179,237,0.15);
49
- border: 1px solid rgba(99,179,237,0.3);
50
- color: #90cdf4;
51
- font-size: 0.75rem;
52
- padding: 3px 10px;
53
- border-radius: 20px;
54
- margin-bottom: 12px;
55
- font-weight: 500;
56
- }
57
-
58
- /* ── Agent step card ────────────────────────────────────── */
59
- .agent-card {
60
- background: #f8fafc;
61
- border: 1px solid #e2e8f0;
62
- border-left: 4px solid #4299e1;
63
- border-radius: 10px;
64
- padding: 14px 16px;
65
- margin-bottom: 10px;
66
- }
67
- .agent-card.done { border-left-color: #48bb78; background: #f0fff4; }
68
- .agent-card.error { border-left-color: #fc8181; background: #fff5f5; }
69
- .agent-title { font-weight: 600; font-size: 0.9rem; color: #2d3748; margin-bottom: 4px; }
70
- .agent-body { font-size: 0.82rem; color: #4a5568; line-height: 1.5; }
71
-
72
- /* ── Provider card ──────────────────────────────────────── */
73
- .provider-card {
74
- background: #ffffff;
75
- border: 1.5px solid #e2e8f0;
76
- border-radius: 12px;
77
- padding: 16px 18px;
78
- margin-bottom: 10px;
79
- transition: border-color 0.2s;
80
- }
81
- .provider-card.best { border-color: #48bb78; background: #f0fff4; }
82
- .provider-name { font-size: 1rem; font-weight: 600; color: #1a202c; }
83
- .provider-meta { font-size: 0.8rem; color: #718096; margin-top: 4px; }
84
- .badge-best { background: #c6f6d5; color: #276749; font-size: 0.72rem;
85
- padding: 2px 8px; border-radius: 8px; font-weight: 600; margin-left: 8px; }
86
- .score-bar { height: 5px; background: #e2e8f0; border-radius: 3px; margin-top: 8px; }
87
- .score-fill { height: 5px; background: #48bb78; border-radius: 3px; }
88
-
89
- /* ── Booking receipt ────────────────────────────────────── */
90
- .receipt {
91
- background: linear-gradient(135deg, #f0fff4, #e6fffa);
92
- border: 2px solid #9ae6b4;
93
- border-radius: 16px;
94
- padding: 24px;
95
- text-align: center;
96
- }
97
- .receipt-id { font-size: 0.78rem; color: #68d391; letter-spacing: 2px; font-weight: 600; }
98
- .receipt-name { font-size: 1.4rem; font-weight: 700; color: #1a202c; margin: 8px 0 4px; }
99
- .receipt-row { display: flex; justify-content: space-between; padding: 8px 0;
100
- border-bottom: 1px solid rgba(0,0,0,0.06); font-size: 0.88rem; }
101
- .receipt-row:last-child { border-bottom: none; }
102
- .receipt-label { color: #718096; }
103
- .receipt-value { color: #1a202c; font-weight: 500; }
104
- .reminder-box {
105
- background: #ebf8ff; border: 1px solid #90cdf4;
106
- border-radius: 10px; padding: 12px 16px; margin-top: 14px;
107
- font-size: 0.85rem; color: #2b6cb0;
108
- }
109
-
110
- /* ── Trace log ──────────────────────────────────────────── */
111
- .trace-header { font-size: 0.75rem; font-weight: 600; color: #a0aec0;
112
- text-transform: uppercase; letter-spacing: 1px; margin-bottom: 6px; }
113
-
114
- /* ── Input box ──────────────────────────────────────────── */
115
- .stTextArea textarea {
116
- font-size: 1rem !important;
117
- border-radius: 10px !important;
118
- border: 1.5px solid #cbd5e0 !important;
119
- }
120
-
121
- /* ── Example chips ──────────────────────────────────────── */
122
- .chip {
123
- display: inline-block;
124
- background: #edf2f7;
125
- border: 1px solid #e2e8f0;
126
- border-radius: 20px;
127
- padding: 4px 12px;
128
- font-size: 0.78rem;
129
- color: #4a5568;
130
- margin: 3px;
131
- cursor: pointer;
132
- }
133
- </style>
134
- """, unsafe_allow_html=True)
135
-
136
- # ════════════════════════════════════════════════════════════════════
137
- # SESSION STATE
138
- # ════════════════════════════════════════════════════════════════════
139
- for key in ["result", "trace_log", "running"]:
140
- if key not in st.session_state:
141
- st.session_state[key] = None if key != "running" else False
142
-
143
- # ════════════════════════════════════════════════════════════════════
144
- # HERO
145
- # ════════════════════════════════════════════════════════════════════
146
- st.markdown("""
147
- <div class="hero">
148
- <div class="hero-badge">🤖 Powered by Google Antigravity + Gemini API</div>
149
- <h1>🔧 KhidmatAI</h1>
150
- <p>AI Service Orchestrator for Pakistan's Informal Economy<br>
151
- Type your request in <strong>Urdu · Roman Urdu · English</strong></p>
152
- </div>
153
- """, unsafe_allow_html=True)
154
-
155
- # ════════════════════════════════════════════════════════════════════
156
- # CHECK API KEY
157
- # ════════════════════════════════════════════════════════════════════
158
- api_key = os.environ.get("GEMINI_API_KEY", "your_MapAI")
159
- if not api_key:
160
- st.error(
161
- "⚠️ **GEMINI_API_KEY not set.**\n\n"
162
- "Go to your HuggingFace Space → **Settings → Variables and Secrets** → "
163
- "add a secret named `GEMINI_API_KEY` with your Gemini API key.\n\n"
164
- "Get a free key at: https://aistudio.google.com/apikey"
165
- )
166
- st.stop()
167
-
168
- # ════════════════════════════════════════════════════════════════════
169
- # EXAMPLE INPUTS
170
- # ════════════════════════════════════════════════════════════════════
171
- st.markdown("**Try an example:**")
172
- examples = [
173
- "Mujhe kal subah G-13 mein AC technician chahiye",
174
- "I need a plumber in F-10 today",
175
- "G-9 mein electrician chahiye aaj evening",
176
- "Maths tutor chahiye G-11 mein kal",
177
- "Home maid service chahiye F-7 mein",
178
- ]
179
- cols = st.columns(3)
180
- selected_example = None
181
- for i, ex in enumerate(examples):
182
- if cols[i % 3].button(f"💬 {ex[:30]}…", key=f"ex_{i}", use_container_width=True):
183
- selected_example = ex
184
-
185
- # ════════════════════════════════════════════════════════════════════
186
- # INPUT
187
- # ════════════════════════════════════════════════════════════════════
188
- default_text = selected_example or ""
189
- user_input = st.text_area(
190
- "Your service request:",
191
- value=default_text,
192
- height=90,
193
- placeholder="e.g. Mujhe kal subah G-13 mein AC technician chahiye",
194
- label_visibility="collapsed",
195
- )
196
-
197
- col1, col2 = st.columns([3, 1])
198
- with col1:
199
- run_btn = st.button("🚀 Find & Book Service", type="primary", use_container_width=True)
200
- with col2:
201
- clear_btn = st.button("🔄 Clear", use_container_width=True)
202
-
203
- if clear_btn:
204
- st.session_state.result = None
205
- st.session_state.trace_log = None
206
- st.rerun()
207
-
208
- # ════════════════════════════════════════════════════════════════════
209
- # AGENT PIPELINE
210
- # ════════════════════════════════════════════════════════════════════
211
- def run_pipeline(message: str):
212
- trace = []
213
- st.markdown("---")
214
- st.markdown("### 🤖 Agent Pipeline Running...")
215
-
216
- # ── Agent 1: Intent ───────────────────────────────────────────
217
- with st.container():
218
- st.markdown("""<div class="agent-card">
219
- <div class="agent-title">🧠 Agent 1 — Intent Agent</div>
220
- <div class="agent-body">Parsing your request with Gemini API...</div>
221
- </div>""", unsafe_allow_html=True)
222
- t0 = time.time()
223
- try:
224
- intent = intent_agent.run(message)
225
- ms = int((time.time() - t0) * 1000)
226
- trace.append({
227
- "agent": "IntentAgent", "step": 1,
228
- "input": {"message": message},
229
- "output": intent, "duration_ms": ms,
230
- "reasoning": f"Detected language: {intent.get('language')}. "
231
- f"Extracted service={intent.get('service_type')}, "
232
- f"location={intent.get('location')}, time={intent.get('time')}."
233
- })
234
- st.markdown(f"""<div class="agent-card done">
235
- <div class="agent-title">✅ Agent 1 — Intent Agent <small style="color:#718096">({ms}ms)</small></div>
236
- <div class="agent-body">
237
- 🏷️ <b>Service:</b> {intent.get('service_type') or '?'} &nbsp;|&nbsp;
238
- 📍 <b>Location:</b> {intent.get('location') or '?'} &nbsp;|&nbsp;
239
- 🕐 <b>Time:</b> {intent.get('time') or '?'} &nbsp;|&nbsp;
240
- 🌐 <b>Language:</b> {intent.get('language') or '?'}
241
- </div>
242
- </div>""", unsafe_allow_html=True)
243
- except Exception as e:
244
- st.markdown(f"""<div class="agent-card error">
245
- <div class="agent-title">❌ Agent 1 Failed</div>
246
- <div class="agent-body">{e}</div>
247
- </div>""", unsafe_allow_html=True)
248
- return None, trace
249
-
250
- # ── Agent 2: Discovery ────────────────────────────────────────
251
- with st.container():
252
- t0 = time.time()
253
- try:
254
- disc = discovery_agent.run(intent)
255
- ms = int((time.time() - t0) * 1000)
256
- trace.append({
257
- "agent": "DiscoveryAgent", "step": 2,
258
- "input": intent, "output": {"total_found": disc["total_found"]},
259
- "duration_ms": ms,
260
- "reasoning": f"Searched providers.json for '{intent.get('service_type')}' near {disc['user_location']}. "
261
- f"Found {disc['total_found']} provider(s). Distances computed via haversine formula."
262
- })
263
- st.markdown(f"""<div class="agent-card done">
264
- <div class="agent-title">✅ Agent 2 — Discovery Agent <small style="color:#718096">({ms}ms)</small></div>
265
- <div class="agent-body">
266
- Found <b>{disc['total_found']}</b> provider(s) for <b>{intent.get('service_type')}</b>
267
- near <b>{disc['user_location']}</b>.
268
- </div>
269
- </div>""", unsafe_allow_html=True)
270
- except Exception as e:
271
- st.markdown(f"""<div class="agent-card error">
272
- <div class="agent-title">❌ Agent 2 Failed</div>
273
- <div class="agent-body">{e}</div>
274
- </div>""", unsafe_allow_html=True)
275
- return None, trace
276
-
277
- if disc["total_found"] == 0:
278
- st.warning("😕 No providers found for this service in your area. Try a different location or service.")
279
- return None, trace
280
-
281
- # ── Agent 3: Recommendation ───────────────────────────────────
282
- with st.container():
283
- t0 = time.time()
284
- try:
285
- rec = recommendation_agent.run(disc)
286
- ms = int((time.time() - t0) * 1000)
287
- trace.append({
288
- "agent": "RecommendationAgent", "step": 3,
289
- "input": {"providers_found": disc["total_found"]},
290
- "output": {
291
- "selected": rec["best_provider"]["name"],
292
- "score": rec["best_provider"]["score"]
293
- },
294
- "duration_ms": ms,
295
- "reasoning": rec["reasoning"]
296
- })
297
- st.markdown(f"""<div class="agent-card done">
298
- <div class="agent-title">✅ Agent 3 — Recommendation Agent <small style="color:#718096">({ms}ms)</small></div>
299
- <div class="agent-body">
300
- {rec['reasoning']}
301
- </div>
302
- </div>""", unsafe_allow_html=True)
303
- except Exception as e:
304
- st.markdown(f"""<div class="agent-card error">
305
- <div class="agent-title">❌ Agent 3 Failed</div>
306
- <div class="agent-body">{e}</div>
307
- </div>""", unsafe_allow_html=True)
308
- return None, trace
309
-
310
- # ── Agent 4: Booking ──────────────────────────────────────────
311
- with st.container():
312
- t0 = time.time()
313
- try:
314
- bk = booking_agent.run(rec, intent)
315
- ms = int((time.time() - t0) * 1000)
316
- trace.append({
317
- "agent": "BookingAgent", "step": 4,
318
- "input": {"provider": rec["best_provider"]["name"]},
319
- "output": {"booking_id": bk["booking_id"], "slot": bk["confirmed_slot"]},
320
- "duration_ms": ms,
321
- "reasoning": f"Picked slot {bk['confirmed_slot']} from available morning slots. "
322
- f"Wrote to bookings.json. Booking ID: {bk['booking_id']}."
323
- })
324
- st.markdown(f"""<div class="agent-card done">
325
- <div class="agent-title">✅ Agent 4 — Booking Agent <small style="color:#718096">({ms}ms)</small></div>
326
- <div class="agent-body">
327
- Booking <b>{bk['booking_id']}</b> confirmed for <b>{bk['confirmed_slot']}</b>.
328
- Reminder set for <b>{bk['reminder_time']}</b>.
329
- </div>
330
- </div>""", unsafe_allow_html=True)
331
- except Exception as e:
332
- st.markdown(f"""<div class="agent-card error">
333
- <div class="agent-title">❌ Agent 4 Failed</div>
334
- <div class="agent-body">{e}</div>
335
- </div>""", unsafe_allow_html=True)
336
- return None, trace
337
-
338
- return {"intent": intent, "discovery": disc, "recommendation": rec, "booking": bk}, trace
339
-
340
-
341
- # ════════════════════════════════════════════════════════════════════
342
- # RUN
343
- # ════════════════════════════════════════════════════════════════════
344
- if run_btn and user_input.strip():
345
- st.session_state.result = None
346
- st.session_state.trace_log = None
347
- result, trace = run_pipeline(user_input.strip())
348
- st.session_state.result = result
349
- st.session_state.trace_log = trace
350
-
351
- elif run_btn and not user_input.strip():
352
- st.warning("Please type a service request first.")
353
-
354
- # ════════════════════════════════════════════════════════════════════
355
- # RESULTS
356
- # ════════════════════════════════════════════════════════════════════
357
- if st.session_state.result:
358
- result = st.session_state.result
359
- rec = result["recommendation"]
360
- bk = result["booking"]
361
- disc = result["discovery"]
362
-
363
- st.markdown("---")
364
-
365
- # ── Tabs ────────────────────────────────────────────────────
366
- tab1, tab2, tab3 = st.tabs(["📋 Providers Found", "✅ Booking Receipt", "🔍 Agent Trace"])
367
-
368
- # ── Tab 1: Providers ─────────────────────────────────────────
369
- with tab1:
370
- st.markdown(f"**{disc['total_found']} provider(s) found** — ranked by distance · rating · availability")
371
- for i, p in enumerate(rec["all_ranked"]):
372
- is_best = i == 0
373
- badge = '<span class="badge-best">⭐ TOP PICK</span>' if is_best else ""
374
- avail = "🟢 Available" if p.get("available") else "🔴 Unavailable"
375
- score_w = int(p["score"] * 100)
376
- st.markdown(f"""
377
- <div class="provider-card {'best' if is_best else ''}">
378
- <div class="provider-name">#{i+1} {p['name']}{badge}</div>
379
- <div class="provider-meta">
380
- 📍 {p['location']['area']} ·
381
- 📏 {p['distance_km']} km ·
382
- ⭐ {p['rating']} ({p['total_reviews']} reviews) ·
383
- {avail} ·
384
- 💰 {p.get('price_range', 'N/A')}
385
- </div>
386
- <div class="score-bar"><div class="score-fill" style="width:{score_w}%"></div></div>
387
- <div style="font-size:0.75rem;color:#a0aec0;margin-top:3px;">Score: {p['score']}</div>
388
- </div>""", unsafe_allow_html=True)
389
-
390
- # ── Tab 2: Receipt ────────────────────────────────────────────
391
- with tab2:
392
- st.markdown(f"""
393
- <div class="receipt">
394
- <div class="receipt-id">BOOKING ID: {bk['booking_id']}</div>
395
- <div class="receipt-name">🎉 {bk['provider_name']}</div>
396
- <div style="color:#48bb78;font-size:0.85rem;margin-bottom:16px;">Booking Confirmed</div>
397
- <div class="receipt-row">
398
- <span class="receipt-label">🔧 Service</span>
399
- <span class="receipt-value">{bk['service']}</span>
400
- </div>
401
- <div class="receipt-row">
402
- <span class="receipt-label">📍 Location</span>
403
- <span class="receipt-value">{bk['location']}</span>
404
- </div>
405
- <div class="receipt-row">
406
- <span class="receipt-label">📅 Date</span>
407
- <span class="receipt-value">{bk['booked_date']}</span>
408
- </div>
409
- <div class="receipt-row">
410
- <span class="receipt-label">⏰ Slot</span>
411
- <span class="receipt-value">{bk['confirmed_slot']}</span>
412
- </div>
413
- <div class="receipt-row">
414
- <span class="receipt-label">📞 Contact</span>
415
- <span class="receipt-value">{bk['provider_phone']}</span>
416
- </div>
417
- <div class="reminder-box">
418
- 🔔 <b>Reminder</b> set for <b>{bk['reminder_time']}</b> — 1 hour before your appointment.
419
- </div>
420
- </div>
421
- """, unsafe_allow_html=True)
422
-
423
- # ── Tab 3: Agent Trace ────────────────────────────────────────
424
- with tab3:
425
- st.markdown('<div class="trace-header">Full Agent Decision Log</div>', unsafe_allow_html=True)
426
- for step in st.session_state.trace_log:
427
- with st.expander(
428
- f"Step {step['step']} — {step['agent']} ({step['duration_ms']}ms)",
429
- expanded=False
430
- ):
431
- st.markdown(f"**Reasoning:** {step['reasoning']}")
432
- col_a, col_b = st.columns(2)
433
- with col_a:
434
- st.markdown("**Input**")
435
- st.json(step["input"])
436
- with col_b:
437
- st.markdown("**Output**")
438
- st.json(step["output"])
439
-
440
- st.markdown("**Full trace JSON:**")
441
- st.json(st.session_state.trace_log)
442
-
443
- # ════════════════════════════════════════════════════════════════════
444
- # FOOTER
445
- # ════════════════════════════════════════════════════════════════════
446
- st.markdown("---")
447
- st.markdown(
448
- "<div style='text-align:center;color:#a0aec0;font-size:0.78rem'>"
449
- "KhidmatAI · Built with Google Antigravity · Gemini API · Streamlit · "
450
- "</div>",
451
- unsafe_allow_html=True
452
- )