--- title: CustomerCore API emoji: πŸš€ colorFrom: blue colorTo: indigo sdk: docker app_port: 7860 pinned: false ---

🧠 CustomerCore

Enterprise AI Customer Support Platform β€” Multi-Agent Triage, Churn Prediction & Real-Time Observability

πŸ–₯️ Live Demo β€’ πŸ“Š MLflow Experiments β€’ πŸ“¦ Model Registry β€’ βš™οΈ CI/CD

--- ## πŸ“Œ What is CustomerCore? CustomerCore is a **production-grade, multi-tenant B2B AI platform** that automatically triages customer support tickets using a network of 6 specialized AI agents. It classifies tickets, predicts customer churn risk, detects infrastructure outages, retrieves similar past cases, and routes everything to the right team β€” all in under 5 seconds. **Not just a prototype** β€” it has CI/CD pipelines, containerized deployment on Hugging Face Spaces, MLOps model tracking via DagsHub, LLM observability via Langfuse, a privacy vault for PII masking, Constitutional AI safety guardrails, and a Human-in-the-Loop review system for high-risk decisions. --- ## πŸ”— Live Infrastructure | Service | Link | What it does | |:--------|:-----|:-------------| | πŸ–₯️ **Operations Console** | [HF Space](https://huggingface.co/spaces/saibalajiomg/customercore) | Live dashboard β€” submit tickets, view AI triage results | | πŸ“Š **MLflow Experiments** | [DagsHub](https://dagshub.com/saibalajinamburi/CustomerCore.mlflow) | Training runs, metrics, model comparison charts | | πŸ“¦ **Model Registry** | [DagsHub Models](https://dagshub.com/saibalajinamburi/CustomerCore/models) | Versioned churn model with Production/Staging labels | | πŸ“‚ **Dataset Versioning** | [DagsHub DVC](https://dagshub.com/saibalajinamburi/CustomerCore) | Raw + processed datasets tracked via DVC | | βš™οΈ **CI/CD Pipelines** | [GitHub Actions](https://github.com/saibalajinamburi/CustomerCore/actions) | Lint, test, train, deploy β€” on every push | --- ## πŸ—οΈ How It All Connects β€” End to End This is how a single support ticket flows through the entire system: ```mermaid sequenceDiagram autonumber actor User as Client / Dashboard participant API as FastAPI Server participant Vault as Privacy Vault participant Graph as LangGraph 6-Agent Pipeline participant LLM as LLM Router (Local/Cloud) participant ML as Churn Model (Random Forest) participant DB as Supabase PostgreSQL participant VDB as ChromaDB / pgvector participant Trace as Langfuse + Prometheus User->>API: Submit support ticket (JWT auth) API->>API: Verify JWT β†’ extract tenant_id + role API->>Vault: Mask PII (emails, phones, names, credit cards) Vault-->>API: Sanitized text API->>DB: Save ticket (status: pending) API-->>User: HTTP 202 Accepted Note over API,Graph: Agent pipeline runs in background API->>Graph: Dispatch ticket to 6-agent supervisor Graph->>LLM: Classify Agent β†’ categorize + assign priority LLM-->>Graph: category=billing, priority=high Graph->>DB: Memory Agent β†’ recall past customer interactions Graph->>VDB: RAG Agent β†’ BM25 + vector hybrid search VDB-->>Graph: Top 5 similar past resolutions Graph->>ML: Churn Agent β†’ predict churn probability ML-->>Graph: churn_risk=0.73 Graph->>Graph: Incident Agent β†’ check for outage patterns Graph->>Graph: HITL Agent β†’ should human review? Graph->>LLM: Generate resolution using retrieved context Graph->>Vault: Constitutional Policy check on AI response Graph->>DB: Save final triage result + audit log Graph->>Trace: Log full trace (latency, tokens, cost, decisions) ``` --- ## 🧠 The 6-Agent Triage Pipeline CustomerCore uses **LangGraph** to orchestrate 6 specialized agents in a **parallel + sequential hybrid pipeline** with conditional Human-in-the-Loop gating. The graph topology uses a **fan-out / fan-in pattern** where the Memory, RAG, Churn, and Incident agents execute concurrently after classification, then converge at the HITL gate: ```mermaid graph LR A[πŸ“© Ticket In] --> B[🏷️ Classify] B --> C[🧠 Memory] B --> D[πŸ” RAG] B --> E[πŸ“‰ Churn] B --> F[🚨 Incident] C & D & E & F --> G{πŸ›‘ HITL Check} G -->|Safe| H[βœ… Finalize] G -->|Risky| I[⏸️ Human Review] I --> H ``` | # | Agent | What it does | Key detail | |---|-------|-------------|------------| | 1 | **Classify Agent** | Categorizes ticket (Billing, Technical, Security, Account) and assigns priority (Low β†’ Critical) | Uses SLA-Aware LLM Router β€” local Gemma for routine, cloud frontier for critical | | 2 | **Memory Agent** | Recalls past interactions for this customer | Uses **Mem0** with tenant-scoped identifiers | | 3 | **RAG Agent** | Hybrid search β€” BM25 keyword + ChromaDB vector retrieval with **Reciprocal Rank Fusion** (RRF) | Optional cross-encoder reranking via `ms-marco-MiniLM-L-6-v2` | | 4 | **Churn Agent** | Runs customer features through our trained **Random Forest** model | Features: monthly spend, tenure, ticket count, contract months | | 5 | **Incident Agent** | Analyzes recent ticket frequency to detect systemic outages | Spike detection across the same tenant | | 6 | **HITL Agent** | Flags tickets for human review | Triggers when: confidence < 0.65, priority is critical, or safety policy violated | When HITL flags a ticket, LangGraph's **`interrupt_before` checkpoint** pauses execution. The state is saved in memory. A human operator reviews from the dashboard and calls `resume_triage()` to continue. The LangGraph `MemorySaver` checkpointer makes this possible β€” it stores the full agent state at the interrupt point so execution can resume exactly where it stopped. > **Performance gain**: Running Memory, RAG, Churn, and Incident agents concurrently instead of sequentially reduced pipeline latency by **~45%** (from ~7 seconds to ~3–4 seconds for complex tickets). --- ## πŸ” RAG β€” How Knowledge Retrieval Works CustomerCore doesn't use plain vector search. It uses a **3-layer Graph-RAG** system: ```mermaid graph TD Q[User Query] --> V[Layer 1: Vector + BM25 Hybrid Search] Q --> G[Layer 2: B2B Knowledge Graph - NetworkX] Q --> S[Layer 3: SQL Analytics - DuckDB Gold Marts] V --> |Top 5 similar tickets| M[Combined Context Builder] G --> |Tenant profile + category trends| M S --> |Ticket funnel + customer health| M M --> LLM[LLM Prompt Injection] ``` | Layer | What it does | Why it matters | |:------|:-------------|:---------------| | **Vector + BM25** | ChromaDB dense search + BM25 sparse keyword search, merged via **Reciprocal Rank Fusion** | Dense catches semantics ("frustrated" β‰ˆ "angry"), BM25 catches exact terms ("error code 5012") | | **Knowledge Graph** | NetworkX DiGraph connecting `Ticket β†’ Tenant β†’ Category` nodes | Answers "Why has acme-corp been escalating?" β€” pure vector search can't do this | | **DuckDB Gold** | SQL queries on dbt-transformed Parquet marts | Returns structured analytics: ticket funnel, customer health daily, agent performance | **Tenant Isolation**: Every ChromaDB query has a mandatory `where={"tenant_id": current_tenant}` filter. BM25 index is partitioned per tenant. Cross-tenant leakage is architecturally impossible. --- ## πŸ€– Models β€” What We Built vs What We Call ### Models We Trained (Custom ML) | Model | Algorithm | Task | Tracked on | |:------|:----------|:-----|:-----------| | **Churn Classifier** | Random Forest, Logistic Regression, Gradient Boosting | Predict customer churn probability from account features | [DagsHub MLflow](https://dagshub.com/saibalajinamburi/CustomerCore.mlflow) | | **Support LLM (QLoRA)** | Llama 3 8B Instruct (PEFT / QLoRA) | Predict professional, tenant-specific ticket resolutions | Hugging Face Adapter Registry | * **Churn Classifier**: The training pipeline (`src/ml/train_churn.py`) trains all 3 models, compares F1 scores, and **auto-promotes the best one to "Production"** in the DagsHub Model Registry. Logged artifacts include ROC curves, confusion matrices, and feature importance plots. * **Support LLM (QLoRA)**: The serverless training pipeline (`src/ml/modal_train.py`) executes on-demand on an Nvidia A10G GPU using **Modal**, pulls historical resolved support tickets (`raw_text` and `suggested_resolution`) from Supabase, performs QLoRA fine-tuning in 4-bit NF4 double-quantization, and publishes the resulting LoRA adapter (`customercore-llama3-adapter`) to the Hugging Face Hub. ### Models We Call via API (LLM) | Model | Provider | When it's used | |:------|:---------|:---------------| | **Gemma 3 4B** | Ollama (runs locally) | Classification, extraction, routine reasoning | | **Gemini 2.5 Flash** | OpenRouter (cloud API) | Fast-tier cloud fallback β€” when Ollama unavailable | | **Claude 3.5 Sonnet** | OpenRouter (cloud API) | Frontier-tier reasoning for critical/complex tickets | ### SLA-Aware LLM Router β€” Smart Model Selection The router (`src/rag/router.py`) automatically picks the right model based on task type, priority, and runtime environment: | Task | Low/Medium Priority | High/Critical Priority | |:-----|:-------------------|:----------------------| | Classify | 🟒 Local Gemma (~150ms, $0) | 🟒 Local Gemma | | Extract | 🟒 Local Gemma | 🟒 Local Gemma | | Reason | 🟒 Local Gemma | πŸ”΅ Cloud Frontier (Claude 3.5 Sonnet) | | Action | πŸ”΅ Cloud Fast (Gemini 2.5 Flash) | πŸ”΅ Cloud Frontier | > **Result**: ~80% of tickets are handled locally at **$0 cost** and **<200ms latency**. Only complex/high-risk tickets go to the frontier cloud model. **Dynamic Local β†’ Cloud Fallback**: The `LLMClient` detects the runtime environment at startup. If `SPACE_ID` (Hugging Face) or `APP_ENV=production` is set, local Ollama calls are transparently redirected to `openrouter/google/gemini-2.5-flash` with zero timeout delay. If local Ollama fails on a development machine (e.g., not running), the same fallback activates automatically. SLA latency targets per priority: Critical ≀200ms, High ≀500ms, Medium ≀1000ms, Low ≀2000ms. Violations are tracked in Prometheus metrics. ### L2 Semantic Cache β€” Avoid Redundant LLM Calls On top of model routing, a **two-layer semantic cache** (`src/rag/router.py`) intercepts calls before they reach the LLM: | Layer | Storage | Hit condition | Benefit | |:------|:--------|:--------------|:--------| | **L1 Exact** | Redis in-memory dict | Same prompt hash | Sub-millisecond, $0 cost | | **L2 Semantic** | Redis + embedding similarity | Cosine similarity > 0.92 | ~85% of near-duplicate tickets served from cache | Cache hit ratio is tracked as a Grafana metric. Each cache hit saves ~$0.002–$0.04 in API costs and 150–800ms of latency. --- ## πŸ“Š Data Pipeline β€” How Data Flows ### Data Ingestion Strategy We use the **stream-to-database-then-download** approach (industry standard): ``` HuggingFace Datasets (API) β†’ Stream to Redpanda (Kafka-compatible broker) β†’ Bronze Consumer (raw events) β†’ PII Masking (Presidio + spaCy) β†’ Silver Layer (cleaned) β†’ dbt transforms (DuckDB) β†’ Gold Layer (analytics-ready marts) ``` **Why not download directly?** In production, data comes from APIs, webhooks, and real-time events β€” not static files. Streaming through a message broker (Redpanda) decouples producers from consumers, handles backpressure, and enables replay. Supabase acts as the persistent storage layer (like S3 in AWS architectures). ### Datasets Used | Dataset | Rows | License | Language | Purpose | |:--------|:-----|:--------|:---------|:--------| | Bitext Customer Support | 26,872 | CDLA-Sharing-1.0 | English | SaaS support Q&A (billing, technical, account) | | Bitext Retail Banking | 25,545 | CDLA-Sharing-1.0 | English | Financial support Q&A (card, loan, compliance) | | Amazon MASSIVE Intent | 34,542 | Apache 2.0 | DE/FR/ES | Real multilingual customer utterances | | **Total** | **~87,000** | | **4 languages** | | ### Medallion Architecture (Bronze β†’ Silver β†’ Gold) ``` Bronze (raw events from Redpanda) β”‚ β”œβ”€β”€ PII masking applied (Presidio) β”‚ Silver (cleaned, PII-scrubbed events) β”‚ β”œβ”€β”€ dbt transformations (DuckDB adapter) β”‚ Gold (analytics-ready marts): β”œβ”€β”€ customer_health_daily β€” daily tenant health scores β”œβ”€β”€ ticket_funnel_daily β€” intake/processing/resolved funnel β”œβ”€β”€ support_agent_performance β€” agent resolution metrics β”œβ”€β”€ billing_failure_summary β€” payment failure patterns β”œβ”€β”€ incident_severity_hourly β€” outage severity tracking β”œβ”€β”€ retention_cohort_metrics β€” cohort-based churn analysis └── product_adoption_features β€” feature usage tracking ``` --- ## πŸ–₯️ The Dashboard (Hugging Face Space) The live dashboard at [huggingface.co/spaces/saibalajiomg/customercore](https://huggingface.co/spaces/saibalajiomg/customercore) is a **single-page app** built with vanilla HTML/CSS/JS, served directly from FastAPI. ### What you see: - **Session Context Bar** β€” Tenant, Role, and Auth status at the top. Changing either instantly re-generates a JWT and enforces access rules - **AI Triage Pipeline** β€” Submit tickets or use 1-click demo presets (billing issue, outage report, PII leak, etc.) - **Prediction Cards** β€” Priority, Routing Team, Churn Risk %, Outage Detection - **Suggested Resolution** β€” AI-generated response with KB citations from RAG - **HITL Workspace** β€” Role-gated: `manager`+ sees the flagged ticket review queue; `support_agent` sees an explicit "Access Denied" panel. Switching roles instantly clears any previously loaded data β€” no stale reviews persist - **System Health Panel** β€” Service status (Supabase βœ…, Redis, ChromaDB) ### Understanding the Session Context: | Field | What it means | |:------|:-------------| | **Tenant** | The company/organization (e.g. "acme-corp", "globex", "initech"). All data is isolated per tenant β€” one tenant can never see another's tickets or triage history | | **Role** | `support_agent` can submit tickets and view results. `manager` can additionally access the HITL Workspace to review and approve/override flagged AI decisions | | **Session / Token** | Active JWT token status. The token carries `tenant_id` + `role` claims, signed with HS256. Auto-generated on the dashboard for demo purposes β€” in production this would come from Supabase Auth or Auth0 | ### How Authentication Works: 1. Dashboard lets you pick a tenant + role β†’ auto-generates a JWT (dev mode) 2. JWT payload: `{"tenant_id": "acme-corp", "role": "support_agent", "exp": ...}` 3. Every API call includes `Authorization: Bearer ` 4. Server verifies signature + expiry β†’ extracts `tenant_id` β†’ uses it for ALL data isolation 5. In production, tokens would come from Supabase Auth, Auth0, or similar --- ## πŸ” Privacy & Security ### PII Masking β€” Privacy Vault Every ticket goes through PII masking **before** it hits the database or any LLM: ``` Input: "My email is john@acme.com and card 4111-1111-1111-1111, call me at +49 170 1234567" Output: "My email is [EMAIL_REDACTED] and card [CREDIT_CARD_REDACTED], call me at [PHONE_REDACTED]" ``` - Uses **Microsoft Presidio** + **spaCy** NER running locally in-process - Detects: emails, phone numbers, credit cards, names, SSNs, IBANs - Supports reversible tokenization via **AES-256-GCM** encryption (authorized users can decrypt) - Even Langfuse traces get PII-masked before being sent to cloud ### Constitutional AI Safety Engine An 8-rule safety engine that checks every AI response **before** it reaches the customer: | Rule | Severity | What it catches | |:-----|:---------|:----------------| | PII Protection | πŸ”΄ CRITICAL | Response leaks raw PII back | | No Commitments | 🟑 VIOLATION | "We will refund $200 by Friday" β€” creates legal liability | | Language Consistency | ⚠️ WARNING | Response in English when ticket was in German | | Toxicity Guard | πŸ”΄ CRITICAL | Harmful, discriminatory, or abusive language | | AI Identity | πŸ”΄ CRITICAL | AI denies being an AI when asked directly | | Scope Limitation | 🟑 VIOLATION | Gives legal, medical, or financial advice | | No Hallucination | 🟑 VIOLATION | Cites KB articles that don't exist | | Competitor Neutral | ⚠️ WARNING | Disparages competitors by name | Two execution paths: **Fast path** (regex, <5ms) catches obvious violations. **Slow path** (LLM-based, ~500ms) handles nuanced cases like coded language or context-dependent toxicity. ### Multi-Tenant Data Isolation - **JWT tokens** carry `tenant_id` β€” every API request is cryptographically bound to a tenant - **ChromaDB**: Metadata filter `where={"tenant_id": X}` on every query - **BM25 Index**: Physically partitioned per tenant (separate corpora) - **Supabase**: Row-Level Security (RLS) policies enforce isolation at the database layer - **Role-Based Access Control**: `support_agent` < `manager` < `admin` β€” HITL resume requires `manager`+ - **Frontend RBAC enforcement**: The HITL Workspace instantly clears previously loaded reviews and displays an "Access Denied" message the moment the active role is switched to `support_agent` β€” no stale data persists across role changes --- ## πŸ“Š MLOps Pipeline ### Training Flow ``` src/ml/train_churn.py β”œβ”€β”€ Load dataset (DVC-tracked CSV from DagsHub) β”œβ”€β”€ Feature engineering (monthly_spend, tenure_months, ticket_count, contract_months) β”œβ”€β”€ Train 3 models: β”‚ β”œβ”€β”€ Logistic Regression (baseline) β”‚ β”œβ”€β”€ Random Forest (best performer) β”‚ └── Gradient Boosting β”œβ”€β”€ Log params + metrics + artifact plots to MLflow @ DagsHub β”œβ”€β”€ Compare F1 scores across all models β”œβ”€β”€ Register best model β†’ DagsHub Model Registry └── Auto-promote to "Production" stage ``` ### What gets logged to DagsHub MLflow: - **Parameters**: n_estimators, max_depth, criterion, test_size, random_state - **Metrics**: Accuracy, F1-Score, Recall, Precision, AUC-ROC - **Artifacts**: ROC Curve, Confusion Matrix Heatmap, Feature Importance Chart - **Dataset**: Registered as an MLflow dataset input for full reproducibility ### Dataset Versioning (DVC) Datasets are tracked with **DVC** and stored on DagsHub's S3-compatible remote. `dvc push` / `dvc pull` to sync. The `.dvc` files in the repo are pointers to the actual data files. --- ## πŸ›οΈ Architecture β€” Local vs Cloud CustomerCore runs in two modes **without changing any application code** β€” the same business logic works in both: | Component | πŸ’» Local Dev Mode | ☁️ Cloud Mode (HF Spaces) | |:----------|:------------------|:--------------------------| | **API Server** | `uvicorn` on `localhost:8080` | Docker container on HF Space (port 7860) | | **Database** | SQLite file (`customercore.db`) | **Supabase PostgreSQL** (cloud-managed) | | **Vector Store** | In-process ChromaDB (Docker) | **Supabase pgvector** | | **LLM (routine)** | Ollama β†’ Gemma 3 4B (local GPU/CPU) | Ollama β†’ Gemma 3 4B | | **LLM (complex)** | OpenRouter β†’ Llama 3.1 8B | OpenRouter β†’ Llama 3.1 8B | | **PII Masking** | Local Presidio + spaCy | Local Presidio + spaCy (in-container) | | **Cache** | Redis (Docker container) | **Upstash Redis** (serverless) | | **Event Streaming** | **Redpanda** broker (Docker) | Async background tasks | | **Object Storage** | **MinIO** (S3-compatible, Docker) | DagsHub DVC remote | | **Metrics** | Prometheus + Grafana (Docker) | OpenTelemetry β†’ Grafana Cloud | | **LLM Tracing** | Console debug logs | **Langfuse Cloud** (full trace UI) | | **ML Tracking** | Local MLflow server | **DagsHub MLflow** (cloud) | | **Secrets** | `.env` file | **Doppler** (secrets management) + HF Space Secrets | | **Deployment** | `uvicorn --reload` | GitHub Actions β†’ HF Space (auto-deploy) | ### How Cloud Mode Works (HF Spaces): 1. Push to `main` branch triggers GitHub Actions 2. `hf_deploy.yml` workflow syncs code to HF Space repo 3. HF builds the Docker image from `Dockerfile.hf` 4. Container starts: runs database migrations β†’ starts uvicorn β†’ serves dashboard 5. Secrets (Supabase URL, API keys, etc.) are injected as HF Space secrets 6. Supabase handles persistent data β€” the HF container is stateless 7. Readiness check: `/ready` endpoint validates Supabase connectivity ### Why Some Services Are "Degraded" in Cloud: In the HF Space, Redis and ChromaDB show as "offline" in the health check β€” this is expected. The cloud mode uses Supabase pgvector instead of ChromaDB, and Upstash Redis instead of local Redis. The `/ready` endpoint returns 200 as long as Supabase (the critical service) is connected. --- ## βš™οΈ CI/CD β€” GitHub Actions Three workflows run automatically: | Workflow | Trigger | What it does | |:---------|:--------|:------------| | **CI Pipeline** (`ci.yml`) | Push to `main` or PR | Install deps β†’ Lint with **Ruff** β†’ Run **pytest** (307 tests) β†’ Upload failure logs | | **ML Training** (`train.yml`) | Push to `main` | Train churn models β†’ Log to DagsHub MLflow β†’ Post **CML report** as PR comment with metrics | | **HF Deploy** (`hf_deploy.yml`) | Push to `main` | Sync entire repo to Hugging Face Space β†’ Triggers Docker rebuild β†’ Live in ~2 min | > **Note**: All 3 workflows trigger on every push to `main`, including README changes. The ML training re-run ensures model registry stays in sync. If you only want to update docs without triggering training, you can push to a non-main branch first. --- ## πŸ” Observability Stack ### Langfuse β€” LLM Observability Every LLM call is traced with a hierarchical structure: ``` Trace: "ticket-triage" (one per request) └── Span: "classify_agent" └── Generation: "gemma3-4b" (model, prompt, completion, tokens, cost, latency) └── Span: "rag_agent" └── Event: "rag_retrieval" (query, num_results, cache_hit) └── Generation: "llama-3.1-8b" └── Score: constitutional_compliance = 0.95 └── Score: resolution_quality = 0.82 └── Score: rag_grounding = 1.0 ``` - PII is stripped from all trace data before sending to Langfuse - **LiteLLM integration**: `litellm.success_callback = ["langfuse"]` auto-traces every LLM call β€” zero code changes needed - Graceful degradation: when Langfuse keys are absent, NoOp stubs are used β€” tests pass offline ### Prometheus + Grafana β€” System Metrics ```bash docker compose -f docker-compose.yml -f docker-compose.monitoring.yml up -d # Grafana: http://localhost:3001 | Prometheus: http://localhost:9090 ``` Pre-built dashboard panels: - πŸ“ˆ **Triage ingestion rate** β€” tickets/second (success vs pending vs error) - ⏱️ **p95 latency profiles** β€” duration distribution by priority - πŸ€– **LLM call reliability** β€” volume by model type and status - πŸ’° **LLM cost tracker** β€” cumulative API spend in USD - 🎯 **Semantic cache hit ratio** β€” L1/L2 cache performance - πŸ›‘ **HITL reason distribution** β€” why tickets are being flagged ### OpenTelemetry Collector The OTel collector (`infra/otel-collector.yaml`) scrapes Prometheus metrics from the FastAPI `/metrics` endpoint and forwards them to Grafana Cloud for cloud-mode observability. --- ## 🌐 Multilingual Support CustomerCore supports **4 languages** out of the box: English, German, French, Spanish. - **Language detection**: `langdetect` library identifies the ticket language - **Translation**: Multilingual model translates non-English tickets before classification - **Response language**: Constitutional AI policy ensures the response matches the ticket language - **Training data**: Amazon MASSIVE dataset provides real customer utterances in DE/FR/ES (not translations β€” real native utterances) --- ## πŸ§ͺ Testing ### Test Categories | Type | Files | What it covers | |:-----|:------|:--------------| | **Unit Tests** | 13 files in `tests/unit/` | Every module: API routes, agents, RAG, vault, policy, router, multilingual, supervisor, streaming | | **Integration Tests** | `tests/integration/` | End-to-end API flows with real HTTP calls | | **Adversarial Red Team** | `tests/adversarial_red_team.py` | Prompt injection, jailbreak attempts, PII extraction attacks | ### Running Tests ```bash pytest tests/ -v # All tests pytest tests/unit/ -v # Unit only pytest tests/adversarial_red_team.py -v # Security tests ``` --- ## πŸ—οΈ Infrastructure ### Docker Compose Stack (Local) ```bash docker compose up -d # Start everything ``` | Service | Port | Purpose | |:--------|:-----|:--------| | **Redpanda** | 9092 | Kafka-compatible message broker | | **Redpanda Console** | 8080 | Visual dashboard for topics/messages | | **MinIO** | 9000/9001 | S3-compatible object storage | | **ChromaDB** | 8000 | Vector database for RAG | | **Redis** | 6379 | Semantic cache + rate limiter | | **OTel Collector** | 4317/4318 | Telemetry pipeline | ### Kubernetes (Production-Ready) K8s manifests in `infra/k8s/`: - `namespace.yaml` β€” `customercore` namespace - `fastapi-deployment.yaml` β€” 2-replica deployment with health probes - `secrets.yaml` β€” Doppler-injected secrets - `ingress.yaml` β€” NGINX ingress for external access - `pdb.yaml` β€” Pod Disruption Budget (min 1 available) Can be deployed to a local Kind cluster (`infra/kind-config.yaml`) or any cloud K8s. --- ## πŸ”‘ Secrets Management All secrets are managed via **Doppler** (cloud secrets manager): ```bash doppler run -- uvicorn src.api.main:app # Injects all secrets as env vars doppler run -- python src/ml/train_churn.py # Same for training ``` Required secrets (set in Doppler or as env vars): - `SUPABASE_URL`, `SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY` - `OPENROUTER_API_KEY` (for cloud LLM calls) - `LITELLM_MASTER_KEY` (also used as JWT signing key) - `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY` - `MLFLOW_TRACKING_USERNAME`, `MLFLOW_TRACKING_PASSWORD` (DagsHub) - `HF_TOKEN` (for Hugging Face deployment) - `UPSTASH_REDIS_URL` (cloud Redis) --- ## πŸ“ Project Structure ``` CustomerCore/ β”œβ”€β”€ src/ β”‚ β”œβ”€β”€ agent/ # LangGraph multi-agent triage β”‚ β”‚ β”œβ”€β”€ supervisor.py # StateGraph: 6 nodes + HITL interrupt + MemorySaver β”‚ β”‚ β”œβ”€β”€ state.py # AgentState TypedDict (shared state across all agents) β”‚ β”‚ β”œβ”€β”€ schemas.py # TriageOutput Pydantic model (validated output) β”‚ β”‚ └── nodes/ β”‚ β”‚ β”œβ”€β”€ classify_agent.py # Ticket categorization + priority assignment β”‚ β”‚ β”œβ”€β”€ memory_agent.py # Mem0 tenant-scoped customer memory recall β”‚ β”‚ β”œβ”€β”€ rag_agent.py # Hybrid retrieval + resolution generation β”‚ β”‚ β”œβ”€β”€ churn_agent.py # ML model inference for churn prediction β”‚ β”‚ β”œβ”€β”€ incident_agent.py # Outage spike detection β”‚ β”‚ └── hitl_agent.py # Human-in-the-loop gating logic β”‚ β”œβ”€β”€ api/ # FastAPI application layer β”‚ β”‚ β”œβ”€β”€ main.py # App factory, CORS, lifespan events β”‚ β”‚ β”œβ”€β”€ auth.py # JWT verification + RBAC + tenant extraction β”‚ β”‚ β”œβ”€β”€ ui.py # SPA dashboard (HTML/CSS/JS served from Python) β”‚ β”‚ └── routers/ β”‚ β”‚ β”œβ”€β”€ triage.py # POST /triage, GET /triage/{id}, POST /triage/{id}/resume β”‚ β”‚ β”œβ”€β”€ health.py # /health (liveness), /ready (readiness) β”‚ β”‚ β”œβ”€β”€ metrics.py # Prometheus /metrics endpoint β”‚ β”‚ └── stream.py # SSE streaming for real-time triage status β”‚ β”œβ”€β”€ rag/ # Retrieval-Augmented Generation engine β”‚ β”‚ β”œβ”€β”€ hybrid_retriever.py # BM25 + ChromaDB + Reciprocal Rank Fusion + cross-encoder reranking β”‚ β”‚ β”œβ”€β”€ graph_rag.py # 3-layer Graph-RAG: Vector + NetworkX Graph + DuckDB SQL β”‚ β”‚ β”œβ”€β”€ router.py # SLA-aware multi-model LLM router with cost tracking β”‚ β”‚ β”œβ”€β”€ llm_client.py # LiteLLM wrapper: Ollama (local) + OpenRouter (cloud) β”‚ β”‚ └── multilingual.py # Language detection + cross-lingual translation β”‚ β”œβ”€β”€ ml/ β”‚ β”‚ β”œβ”€β”€ train_churn.py # Multi-model training + MLflow + auto-promote to Production β”‚ β”‚ └── deploy_to_hf.py # Automated Hugging Face Space code sync β”‚ β”œβ”€β”€ responsible_ai/ β”‚ β”‚ β”œβ”€β”€ privacy_vault.py # AES-256-GCM PII masking (Presidio + spaCy NER) β”‚ β”‚ β”œβ”€β”€ constitutional_policy.py # 8-rule Constitutional AI safety engine β”‚ β”‚ β”œβ”€β”€ audit_log.py # Durable audit trail (every decision logged) β”‚ β”‚ β”œβ”€β”€ key_manager.py # Encryption key lifecycle management β”‚ β”‚ └── model_cards/ # ML model documentation cards β”‚ β”œβ”€β”€ db/ β”‚ β”‚ β”œβ”€β”€ repository.py # Supabase CRUD (tickets, customers, audit events) β”‚ β”‚ └── migrations.py # Auto-DDL: creates tables on startup if missing β”‚ β”œβ”€β”€ monitoring/ β”‚ β”‚ └── langfuse_tracer.py # Full LLM observability: Trace β†’ Span β†’ Generation β†’ Score β”‚ β”œβ”€β”€ streaming/ β”‚ β”‚ β”œβ”€β”€ data_loader.py # Downloads 87K records from HuggingFace β†’ Redpanda β”‚ β”‚ β”œβ”€β”€ producers/ # Synthetic event generators for testing β”‚ β”‚ β”œβ”€β”€ producer_helper.py # Kafka producer wrapper β”‚ β”‚ β”œβ”€β”€ bronze_consumer.py # Raw event consumer from Redpanda β”‚ β”‚ β”œβ”€β”€ bronze_to_silver.py # PII masking + data cleaning pipeline β”‚ β”‚ β”œβ”€β”€ triage_consumer.py # Triggers agent pipeline from stream events β”‚ β”‚ └── minio_setup.py # S3 bucket initialization β”‚ └── dbt/ # Data transformations (DuckDB adapter) β”‚ β”œβ”€β”€ dbt_project.yml β”‚ β”œβ”€β”€ profiles.yml β”‚ └── models/gold/ # 8 Gold mart models (health, funnel, agents, billing, etc.) β”œβ”€β”€ tests/ β”‚ β”œβ”€β”€ unit/ # 13 test files, 300+ tests β”‚ β”œβ”€β”€ integration/ # E2E API tests β”‚ β”œβ”€β”€ adversarial_red_team.py # Prompt injection + jailbreak test suite β”‚ └── conftest.py # Shared fixtures β”œβ”€β”€ infra/ β”‚ β”œβ”€β”€ k8s/ # Kubernetes: deployment, ingress, PDB, secrets β”‚ β”œβ”€β”€ monitoring/ # Grafana dashboard JSON + Prometheus config β”‚ β”œβ”€β”€ kind-config.yaml # Local multi-node K8s cluster β”‚ └── otel-collector.yaml # OpenTelemetry pipeline config β”œβ”€β”€ .github/workflows/ β”‚ β”œβ”€β”€ ci.yml # Lint (Ruff) + Test (pytest) pipeline β”‚ β”œβ”€β”€ train.yml # ML training + CML report β”‚ └── hf_deploy.yml # Auto-deploy to Hugging Face Spaces β”œβ”€β”€ docker-compose.yml # Full local stack (Redpanda, MinIO, ChromaDB, Redis, OTel) β”œβ”€β”€ docker-compose.monitoring.yml # Prometheus + Grafana β”œβ”€β”€ Dockerfile # Full production image β”œβ”€β”€ Dockerfile.hf # Slim image for Hugging Face Spaces β”œβ”€β”€ requirements.txt # Production dependencies β”œβ”€β”€ requirements-ci.txt # CI/test dependencies (CPU-only PyTorch) └── data/ # DVC-tracked datasets (.dvc pointer files) ``` --- ## πŸ› οΈ Full Tech Stack | Category | Tools | |:---------|:------| | **Language** | Python 3.12 | | **API Framework** | FastAPI, Uvicorn, Pydantic v2, SlowAPI (rate limiting) | | **AI Agents** | LangGraph (StateGraph, MemorySaver, interrupt checkpoints) | | **LLM Orchestration** | LangChain, LiteLLM (unified interface for local + cloud) | | **LLMs** | Ollama (Gemma 3 4B β€” local), OpenRouter (Llama 3.1 8B β€” cloud) | | **ML Training** | scikit-learn, MLflow, DagsHub, DVC | | **RAG** | ChromaDB (dense), rank-bm25 (sparse), Reciprocal Rank Fusion, sentence-transformers (reranker) | | **Graph RAG** | NetworkX (knowledge graph), DuckDB (SQL analytics) | | **NLP / PII** | spaCy (NER), Microsoft Presidio (PII detection), langdetect | | **Database** | Supabase (PostgreSQL + pgvector + RLS), SQLite (local fallback), DuckDB (analytics) | | **Auth** | PyJWT (HS256), RBAC, Supabase Row-Level Security | | **Event Streaming** | Redpanda (Kafka-compatible, no JVM, no ZooKeeper) | | **Object Storage** | MinIO (local S3), DagsHub DVC remote (cloud) | | **Caching** | Redis / Upstash Redis β€” L1 exact cache + L2 semantic cache (cosine similarity > 0.92) | | **Observability** | Prometheus, Grafana, OpenTelemetry Collector, Langfuse | | **Infrastructure** | Docker, Docker Compose, Kubernetes (Kind), Hugging Face Spaces | | **CI/CD** | GitHub Actions, CML (Continuous Machine Learning), Ruff (linter) | | **Data Transforms** | dbt (DuckDB adapter) β€” 8 Gold mart models | | **Security** | AES-256-GCM encryption, Constitutional AI (8-rule safety engine), frontend RBAC enforcement | | **Secrets** | Doppler (cloud secrets manager), Hugging Face Space Secrets | | **Logging** | structlog (structured JSON logs) | --- ## πŸš€ Quick Start ### 1. Clone & Install ```bash git clone https://github.com/saibalajinamburi/CustomerCore.git cd CustomerCore python -m venv .venv .venv\Scripts\activate # Windows pip install -r requirements.txt ``` ### 2. Set Environment Variables Create a `.env` file or use Doppler: ```bash # Required for cloud features (optional for local-only mode) SUPABASE_URL=https://your-project.supabase.co SUPABASE_ANON_KEY=your-anon-key LITELLM_MASTER_KEY=your-key # Also used as JWT signing secret OPENROUTER_API_KEY=your-key # For cloud LLM calls ``` ### 3. Start Local Infrastructure ```bash docker compose up -d # Redpanda, ChromaDB, Redis, MinIO, OTel ``` ### 4. Run the API ```bash uvicorn src.api.main:app --host 0.0.0.0 --port 8080 --reload ``` ### 5. Open Dashboard - **Dashboard**: `http://localhost:8080` - **Swagger API Docs**: `http://localhost:8080/docs` ### 6. (Optional) Load Training Data ```bash python -m src.streaming.data_loader --sources all --limit 500 ``` ### 7. (Optional) Train ML Models ```bash doppler run -- python -X utf8 src/ml/train_churn.py ``` ### 8. Run Tests ```bash pytest tests/ -v ``` ### 9. (Optional) Run Serverless GPU LLM Fine-Tuning ```bash doppler run -- modal run src/ml/modal_train.py ``` --- ## ⚠️ Challenges Faced & How We Solved Them | Challenge | Root Cause | Solution | |:----------|:-----------|:---------| | **Langfuse SDK v4 broke LiteLLM** | LiteLLM v1.85 calls `langfuse.version` and `Langfuse.trace()` which don't exist in SDK v4 | Wrote monkeypatches in `langfuse_tracer.py` that shim the old API onto the new SDK β€” `mock_trace()`, `mock_span_method()`, `mock_generation_method()` | | **CI failing on PyTorch** | Full PyTorch is 2GB+ and slows CI to 15 min | Created `requirements-ci.txt` with CPU-only PyTorch from `download.pytorch.org/whl/cpu` | | **HF Space shows "degraded"** | Health check expects Redis + ChromaDB locally, but cloud uses Upstash + pgvector | Modified `/ready` endpoint to return 200 as long as Supabase (critical path) is connected | | **Cross-tenant data leakage risk** | Naive vector search returns results from all tenants | Enforced `where={"tenant_id": X}` on every ChromaDB query + physically partitioned BM25 index per tenant | | **PII in LLM traces** | Langfuse receives full prompt text which could contain customer PII | Added `_mask_pii()` regex pipeline that strips emails, phones, SSNs, IBANs, credit cards before any data leaves the process | | **UTF-8 encoding on Windows** | Python on Windows defaults to CP1252 β€” MLflow logging fails with emoji/special chars | Set `PYTHONIOENCODING=utf-8` and use `python -X utf8` flag | | **CML reports on every push** | ML training workflow triggers even on README changes | Accepted trade-off: ensures model registry is always in sync. Can be scoped to `src/ml/**` paths if needed | | **Database schema drift** | New columns added in code but HF Space container has old schema | Added `migrations.py` that runs `ALTER TABLE ADD COLUMN IF NOT EXISTS` on every startup | | **Supabase direct port 5432 blocked in cloud** | IPv6-only resolution on direct PostgreSQL connections times out on HF Spaces (IPv4 only) | Switched migrations to Supabase **Connection Pooler** on port `6543` β€” IPv4-compatible, stable in all cloud environments | | **Ollama unavailable in cloud containers** | HF Spaces don't ship with Ollama β€” local model calls time out after several seconds | `LLMClient` detects `SPACE_ID` / `APP_ENV=production` at startup and transparently redirects local tier calls to `openrouter/google/gemini-2.5-flash` | | **Stale HITL data persists after role switch** | Switching from `manager` to `support_agent` in the demo left previously fetched review rows visible | Added an immediate role guard in `generateToken()` that clears the HITL table and shows an "Access Denied" banner the instant the role changes to `support_agent` | | **Misleading DB timeout error messages** | When Supabase was unreachable, the UI catch block printed "Ensure role is manager" β€” wrong root cause | Updated catch blocks to distinguish `NetworkError` / `ENOTFOUND` from auth errors and display an accurate "Database unreachable" message with guidance | | **Gated base model downloads on HF** | Hugging Face gated models (like `meta-llama/Meta-Llama-3-8B-Instruct`) returned a `403 Forbidden` error on serverless containers without user authorization | Switched base model to `NousResearch/Meta-Llama-3-8B-Instruct`, a public non-gated clone with identical architecture and weights | | **Missing dependency in Python 3.12 container** | The remote training process crashed with `ModuleNotFoundError: No module named 'setuptools'` | Added `setuptools` to the container `pip_install` settings in `modal_train.py` for Python 3.12 compatibility | | **Schema mapping & status issues in training script** | Direct queries failed due to non-existent columns (`text`) and incorrect status mapping (`completed` vs check constraints) | Replaced the column selection with `raw_text` and changed the status filter criteria to `'complete'` | --- ## πŸ”’ EU AI Act Compliance | Article | Requirement | Implementation | |:--------|:-----------|:---------------| | **Art. 10** | Data governance & PII masking | Privacy Vault: Presidio + spaCy masks all PII before storage/LLM calls. AES-256-GCM reversible tokenization | | **Art. 12** | Audit traceability | Every decision logged to `audit_events` table with timestamp, tenant, input, output, model used | | **Art. 14** | Human oversight | LangGraph interrupt checkpoint pauses high-risk decisions for human review before execution | | **Art. 15** | Fairness & security | Adversarial red-team tests, model cards, 8-rule Constitutional AI safety engine, RBAC | --- ## πŸ“„ License MIT License β€” see [LICENSE](LICENSE) for details.