Spaces:
Running
Running
| import mlflow | |
| import os | |
| import structlog | |
| from typing import List | |
| from src.agent.state import AgentState | |
| log = structlog.get_logger() | |
| CATEGORY_MODEL = "customercore-category-classifier" | |
| PRIORITY_MODEL = "customercore-priority-classifier" | |
| CATEGORIES = [ | |
| "bug", "feature_request", "security", "performance", | |
| "billing", "auth", "docs", "question", "incident", "other" | |
| ] | |
| PRIORITIES = ["low", "medium", "high", "critical"] | |
| def _load_model(name: str): | |
| """Attempt to load classifier model from MLflow tracking server.""" | |
| tracking_uri = os.environ.get("MLFLOW_TRACKING_URI", "sqlite:///mlruns.db") | |
| mlflow.set_tracking_uri(tracking_uri) | |
| client = mlflow.MlflowClient() | |
| try: | |
| versions = client.get_latest_versions(name, stages=["Production", "None"]) | |
| if versions: | |
| return mlflow.sklearn.load_model(f"models:/{name}/{versions[0].version}") | |
| except Exception as e: | |
| log.warning("model_load_failed", name=name, error=str(e)) | |
| return None | |
| def heuristic_classify(body: str) -> tuple[str, str]: | |
| """ | |
| Fallback high-quality heuristic classification for categories and priorities. | |
| Matches industry support ticket routing logic. | |
| """ | |
| text = body.lower() | |
| # ββ Category Heuristics ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| category = "other" | |
| if any(kw in text for kw in ["hacked", "leak", "unauthorized", "vulnerability", "breach", "cve", "compromised", "exploit", "security", "sicherheit", "fuite", "breche", "brecha", "exposed", "exposΓ©", "expuesto", "divulgada", "divulgado"]): | |
| category = "security" | |
| elif any(kw in text for kw in ["outage", "offline", "completely down", "service unavailable", "is down", "not accessible", "500 error", "ausfall", "hors service", "panne", "caΓdo", "caido"]): | |
| category = "incident" | |
| elif any(kw in text for kw in ["login", "password", "oauth", "token", "signin", "sign-in", "signup", "mfa", "2fa", "authentication", "einloggen", "passwort", "passworts", "mot de passe", "connexion", "contraseΓ±a", "iniciar sesiΓ³n", "senha", "entrar", "konto gesperrt", "gesperrt"]): | |
| category = "auth" | |
| elif any(kw in text for kw in ["billing", "payment", "charge", "refund", "invoice", "stripe", "checkout", "cost", "pay", "rechnung", "zahlung", "facture", "paiement", "factura", "pago"]): | |
| category = "billing" | |
| elif any(kw in text for kw in ["slow", "latency", "lag", "timeout", "degraded", "delay", "performance", "high cpu"]): | |
| category = "performance" | |
| elif any(kw in text for kw in ["docs", "documentation", "how to", "guide", "tutorial", "where can i find"]): | |
| category = "docs" | |
| elif any(kw in text for kw in ["feature request", "would be nice", "can we add", "suggest", "improve", "request feature"]): | |
| category = "feature_request" | |
| elif any(kw in text for kw in ["bug", "error", "fail", "broken", "issue", "crash", "wrong", "incorrect", "exception"]): | |
| category = "bug" | |
| elif "?" in text: | |
| category = "question" | |
| # ββ Priority Heuristics ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| priority = "medium" | |
| # Critical criteria | |
| if category in ["security", "incident"] or any(kw in text for kw in ["completely down", "production down", "outage", "all users affected"]): | |
| priority = "critical" | |
| # High criteria | |
| elif any(kw in text for kw in ["urgent", "blocking", "failed", "broken", "asap", "invoice issue", "payment failed", "error", "dringend", "gesperrt", "fehlgeschlagen", "kaputt", "bloquΓ©", "bloque", "urgente", "bloqueado", "bloqueada"]): | |
| priority = "high" | |
| # Low criteria | |
| elif category in ["docs", "feature_request"] or any(kw in text for kw in ["minor", "typo", "cosmetic", "wont fix"]): | |
| priority = "low" | |
| return category, priority | |
| def classify_agent_node(state: AgentState) -> AgentState: | |
| ticket = state["ticket"] | |
| body = ticket["body"] | |
| # 1. Try MLflow ML models first | |
| category = None | |
| priority = None | |
| models_used: List[str] = state.get("models_used") or [] | |
| try: | |
| # Check if feature engineering module exists | |
| from src.ml.feature_engineering import create_structured_features | |
| import pandas as pd | |
| df = pd.DataFrame([{ | |
| "body": body, | |
| "priority": "medium", | |
| "customer_tier": ticket.get("customer_tier", "professional"), | |
| "reopen_count": 0, | |
| "ticket_age_hours": 24, | |
| }]) | |
| features = create_structured_features(df) | |
| # Predict Category | |
| cat_model = _load_model(CATEGORY_MODEL) | |
| if cat_model: | |
| cat_idx = cat_model.predict(features)[0] | |
| category = CATEGORIES[int(cat_idx) % len(CATEGORIES)] | |
| models_used.append(CATEGORY_MODEL) | |
| # Predict Priority | |
| pri_model = _load_model(PRIORITY_MODEL) | |
| if pri_model: | |
| pri_idx = pri_model.predict(features)[0] | |
| priority = PRIORITIES[int(pri_idx) % len(PRIORITIES)] | |
| models_used.append(PRIORITY_MODEL) | |
| except (ImportError, ModuleNotFoundError) as e: | |
| log.debug("ml_modules_missing_using_heuristics", error=str(e)) | |
| except Exception as e: | |
| log.warning("ml_prediction_failed_using_heuristics", error=str(e)) | |
| # 2. Fall back to high-quality heuristics if ML prediction failed or models not found | |
| if not category or not priority: | |
| h_cat, h_pri = heuristic_classify(body) | |
| category = category or h_cat | |
| priority = priority or h_pri | |
| models_used.append("heuristic-classifier-v1.0") | |
| # 3. Apply customer tier multiplier: VIP customer gets upgraded priority | |
| tier = ticket.get("customer_tier", "free").lower() | |
| if tier == "enterprise": | |
| if priority == "low": | |
| priority = "medium" | |
| elif priority == "medium": | |
| priority = "high" | |
| elif priority == "high": | |
| priority = "critical" | |
| log.info("classify_agent_done", category=category, priority=priority, models_used=models_used) | |
| return { | |
| "category": category, | |
| "priority": priority, | |
| "current_step": "classify_agent", | |
| "models_used": models_used, | |
| } | |