Spaces:
Running
Running
| """Agent 1 — Intent Agent | |
| Uses Gemini 2.5 Flash Lite if GEMINI_API_KEY is valid. | |
| Falls back to rule-based parser if key is missing/invalid (demo mode). | |
| """ | |
| import os, json, re | |
| import google.generativeai as genai | |
| # ── Rule-based fallback (no API key needed) ────────────────────── | |
| SERVICE_KEYWORDS = { | |
| "AC Technician": ["ac", "air condition", "cooling", "hvac", "thanda"], | |
| "Plumber": ["plumber", "plumbing", "pipe", "leakage", "tap", "nal"], | |
| "Electrician": ["electric", "bijli", "wiring", "light", "switch", "fuse"], | |
| "Tutor": ["tutor", "teacher", "teacher", "teacher", "padhai", "math", "science", "english"], | |
| "Beautician": ["beauty", "parlour", "makeup", "salon", "facial", "wax"], | |
| "Carpenter": ["carpenter", "wood", "furniture", "darwaza", "door", "shelf"], | |
| "Painter": ["paint", "rang", "wall", "colour", "color"], | |
| "Driver": ["driver", "car", "ride", "airport", "drop", "pick"], | |
| "Maid": ["maid", "cleaning", "sweep", "jharo", "safai", "cook"], | |
| "Delivery Worker": ["delivery", "parcel", "courier", "deliver", "send"], | |
| } | |
| AREA_KEYWORDS = [ | |
| "G-13","G-11","G-10","G-9","G-6","G-7","G-8", | |
| "F-10","F-7","F-6","F-8","F-11", | |
| "E-11","E-7","I-8","I-10","I-9", | |
| ] | |
| TIME_KEYWORDS = { | |
| "morning": ["subah", "morning", "صبح", "savere"], | |
| "afternoon": ["dopahar", "afternoon", "دوپہر"], | |
| "evening": ["sham", "evening", "شام", "shaam"], | |
| "asap": ["aaj", "today", "abhi", "now", "jaldi", "asap", "فوری"], | |
| "tomorrow": ["kal", "tomorrow", "کل"], | |
| } | |
| def rule_based_parse(message: str) -> dict: | |
| msg = message.lower() | |
| service = None | |
| for svc, keywords in SERVICE_KEYWORDS.items(): | |
| if any(k in msg for k in keywords): | |
| service = svc | |
| break | |
| location = None | |
| for area in AREA_KEYWORDS: | |
| if area.lower() in msg: | |
| location = area | |
| break | |
| time_str = "tomorrow morning" | |
| for label, keywords in TIME_KEYWORDS.items(): | |
| if any(k in msg for k in keywords): | |
| time_str = label | |
| break | |
| lang = "roman_urdu" | |
| urdu_chars = set("ابتثجحخدذرزسشصضطظعغفقکگلمنوہیآ") | |
| if any(c in urdu_chars for c in message): | |
| lang = "urdu" | |
| elif all(ord(c) < 128 for c in message): | |
| lang = "english" if re.search(r'\b(i|need|want|please|the|a|an)\b', msg) else "roman_urdu" | |
| return { | |
| "service_type": service, | |
| "location": location or "G-13", | |
| "time": time_str, | |
| "language": lang, | |
| "confidence": 0.75, | |
| "mode": "rule_based_fallback", | |
| } | |
| def run(message: str) -> dict: | |
| api_key = os.environ.get("GEMINI_API_KEY", "GEMINI_API_KEY").strip() | |
| # ── Try Gemini first ───────────────────────────────────────── | |
| if api_key and api_key != "YOUR_GEMINI_API_KEY_HERE": | |
| try: | |
| genai.configure(api_key=api_key) | |
| model = genai.GenerativeModel("gemini-2.5-flash-lite") | |
| prompt = f"""You are a service request parser for Pakistan's informal economy. | |
| Extract structured info from the user message. Respond ONLY with valid JSON, no markdown, no explanation. | |
| User message: "{message}" | |
| Return exactly this JSON: | |
| {{ | |
| "service_type": "<one of: AC Technician, Plumber, Electrician, Tutor, Beautician, Carpenter, Painter, Driver, Maid, Delivery Worker>", | |
| "location": "<area name like G-13, F-10, etc. Extract from message>", | |
| "time": "<e.g. tomorrow morning, today evening, asap>", | |
| "language": "<urdu | roman_urdu | english | mixed>", | |
| "confidence": <0.0 to 1.0>, | |
| "mode": "gemini" | |
| }} | |
| If a field cannot be determined use null.""" | |
| response = model.generate_content(prompt) | |
| raw = response.text.strip() | |
| raw = re.sub(r"```json|```", "", raw).strip() | |
| result = json.loads(raw) | |
| result["mode"] = "gemini" | |
| return result | |
| except Exception as e: | |
| err = str(e) | |
| # Key invalid → fall through to rule-based, don't crash | |
| if "API_KEY_INVALID" in err or "API key not valid" in err: | |
| result = rule_based_parse(message) | |
| result["_warning"] = "Gemini API key invalid — using rule-based parser. Check HF Secrets." | |
| return result | |
| # Quota exceeded → same fallback | |
| if "quota" in err.lower() or "429" in err: | |
| result = rule_based_parse(message) | |
| result["_warning"] = "Gemini quota exceeded — using rule-based parser." | |
| return result | |
| raise # re-raise unexpected errors | |
| # ── No key set → rule-based ─────────────────────────────────── | |
| result = rule_based_parse(message) | |
| result["_warning"] = "GEMINI_API_KEY not set — using rule-based parser (demo mode)." | |
| return result | |