Spaces:
Running
Running
| import re | |
| from app.data_loader import loader | |
| class IntentParser: | |
| def __init__(self): | |
| pass | |
| def parse(self, text): | |
| text = text.lower() | |
| response = { | |
| "intent": "general_chat", # Default to LLM | |
| "item_code": None, | |
| "location": None, | |
| "horizon_days": None, | |
| "extra": {} | |
| } | |
| # 1. Detect Intent (order matters - more specific checks first) | |
| if any(x in text for x in ["list items", "show catalog", "what items", "available items", "list available", "show items"]): | |
| response["intent"] = "list_items" | |
| elif any(x in text for x in ["forecast", "demand", "predict", "future", "sales"]): | |
| response["intent"] = "demand_forecast" | |
| elif any(x in text for x in ["details", "info", "about", "price", "description"]): | |
| response["intent"] = "item_details" | |
| elif any(x in text for x in ["inventory", "stock", "how many", "available", "on hand"]): | |
| response["intent"] = "check_inventory" | |
| elif any(x in text for x in ["supplier", "vendor", "who supplies", "lead time"]): | |
| response["intent"] = "supplier_info" | |
| elif any(x in text for x in ["buy", "order", "purchase", "requisition"]): | |
| response["intent"] = "create_requisition" | |
| elif any(x in text for x in ["status", "alerts", "dashboard", "overview"]): | |
| response["intent"] = "system_status" | |
| elif any(x in text for x in ["pdf", "download", "report", "file"]): | |
| response["intent"] = "generate_report" | |
| # 2. Extract Item Code | |
| # Look for patterns like FS1234, BMS0001, etc. (2-3 letters + 4 digits) | |
| item_match = re.search(r'\b([a-z]{2,3}\d{4})\b', text) | |
| if item_match: | |
| response["item_code"] = item_match.group(1).upper() | |
| else: | |
| # Fallback: check against known items in DB | |
| if loader.items_df is not None: | |
| known_items = loader.items_df['item_code'].tolist() | |
| for item in known_items: | |
| if item.lower() in text: | |
| response["item_code"] = item | |
| break | |
| # 2.5 Extract Quantity (for orders) | |
| qty_match = re.search(r'\b(\d+)\s*(?:units|pcs|pieces)?\b', text) | |
| if qty_match: | |
| # Avoid confusing horizon days with quantity if possible | |
| # If "days" is not next to it, assume qty | |
| if "day" not in text[qty_match.end():qty_match.end()+5]: | |
| response["extra"]["qty"] = int(qty_match.group(1)) | |
| # 3. Extract Location | |
| if re.search(r'\b(na|north america|usa|us)\b', text): | |
| response["location"] = "NA" | |
| elif re.search(r'\b(eu|europe|uk|germany)\b', text): | |
| response["location"] = "EU" | |
| elif re.search(r'\b(apac|asia|china|india)\b', text): | |
| response["location"] = "APAC" | |
| # 4. Extract Horizon | |
| response["horizon_days"] = self._parse_horizon(text) | |
| return response | |
| def _parse_horizon(self, text): | |
| days = None | |
| # Explicit days | |
| days_match = re.search(r'(\d+)\s*days?', text) | |
| if days_match: | |
| return int(days_match.group(1)) | |
| # Weeks | |
| weeks_match = re.search(r'(\d+)\s*weeks?', text) | |
| if weeks_match: | |
| return int(weeks_match.group(1)) * 7 | |
| # Months | |
| months_match = re.search(r'(\d+)\s*months?', text) | |
| if months_match: | |
| return int(months_match.group(1)) * 30 | |
| # Keywords | |
| if "next week" in text: | |
| return 7 | |
| if "next month" in text: | |
| return 30 | |
| if "next year" in text: | |
| return 365 | |
| if "tomorrow" in text: | |
| return 1 | |
| return days | |
| parser = IntentParser() | |