Spaces:
Sleeping
Sleeping
| import os | |
| import re | |
| import json | |
| import logging | |
| import traceback | |
| from datetime import datetime, timedelta | |
| from flask import Flask, request, jsonify | |
| from flask_cors import CORS | |
| from google import genai | |
| from google.genai import types | |
| # ----------------------------------------------------------------------------- | |
| # 0. LOGGING | |
| # ----------------------------------------------------------------------------- | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' | |
| ) | |
| logger = logging.getLogger("KZNGBB_AI") | |
| # ----------------------------------------------------------------------------- | |
| # 1. CONFIGURATION & INITIALIZATION | |
| # ----------------------------------------------------------------------------- | |
| app = Flask(__name__) | |
| CORS(app) | |
| # --- Google GenAI Client --- | |
| try: | |
| logger.info("Initializing KZNGBB AI Intelligence Layer...") | |
| api_key = os.environ.get("GEMINI_API_KEY") | |
| if not api_key: | |
| raise ValueError("GEMINI_API_KEY environment variable is not set.") | |
| client = genai.Client(api_key=api_key) | |
| logger.info("Gemini client initialized successfully.") | |
| except Exception as e: | |
| logger.error(f"FATAL: Could not initialize Gemini: {e}") | |
| exit(1) | |
| GEMINI_FLASH = "gemini-3.1-flash-lite" | |
| GEMINI_PRO = "gemini-3.1-flash-lite" | |
| # ----------------------------------------------------------------------------- | |
| # 2. HELPERS | |
| # ----------------------------------------------------------------------------- | |
| def _strip_json_fences(s: str) -> str: | |
| s = (s or "").strip() | |
| if "```" in s: | |
| m = re.search(r"```(?:json)?\s*(.*?)\s*```", s, re.DOTALL) | |
| if m: | |
| return m.group(1).strip() | |
| return s | |
| def _now_iso() -> str: | |
| return datetime.utcnow().isoformat() + "Z" | |
| def _gemini_json(prompt: str, model: str = GEMINI_FLASH) -> dict: | |
| """Call Gemini and parse a JSON response. Raises on failure.""" | |
| res = client.models.generate_content( | |
| model=model, | |
| contents=prompt, | |
| config=types.GenerateContentConfig(response_mime_type="application/json") | |
| ) | |
| raw = _strip_json_fences(res.text) | |
| return json.loads(raw) | |
| def _gemini_text(prompt: str, model: str = GEMINI_FLASH) -> str: | |
| """Call Gemini and return plain text.""" | |
| res = client.models.generate_content(model=model, contents=prompt) | |
| return res.text.strip() | |
| def _gemini_text_with_search(prompt: str, model: str = GEMINI_FLASH) -> str: | |
| """Call Gemini with Google Search grounding.""" | |
| res = client.models.generate_content( | |
| model=model, | |
| contents=prompt, | |
| config=types.GenerateContentConfig(tools=[{"google_search": {}}]) | |
| ) | |
| return res.text.strip() | |
| # ----------------------------------------------------------------------------- | |
| # 3. AI FEATURE ENDPOINTS | |
| # ----------------------------------------------------------------------------- | |
| # ── 3.1 OPERATOR RISK INTELLIGENCE ────────────────────────────────────────── | |
| def operator_risk(): | |
| """ | |
| Analyses an operator record and returns a structured risk report. | |
| Input JSON: | |
| operator_name str required | |
| license_number str required | |
| operator_type str required (e.g. "Land-Based Casino", "Online", "Bookmaker") | |
| compliance_score float required (0-100) | |
| violations int required | |
| last_audit_date str required (ISO date) | |
| revenue_ggr_zar float optional | |
| district str optional | |
| Output JSON: | |
| risk_level str ("Low" | "Medium" | "High" | "Critical") | |
| risk_score int (0-100) | |
| flags list of str | |
| recommendations list of str | |
| predicted_next_violation_days int | null | |
| summary str | |
| generated_at str ISO timestamp | |
| """ | |
| data = request.get_json(silent=True) or {} | |
| required = ["operator_name", "license_number", "operator_type", "compliance_score", "violations", "last_audit_date"] | |
| missing = [f for f in required if f not in data] | |
| if missing: | |
| return jsonify({"error": f"Missing fields: {', '.join(missing)}"}), 400 | |
| prompt = f""" | |
| You are the KZNGBB (KwaZulu-Natal Gaming and Betting Board) AI Risk Intelligence Engine. | |
| Analyse the following operator record and produce a structured risk assessment. | |
| OPERATOR RECORD: | |
| - Name: {data['operator_name']} | |
| - License: {data['license_number']} | |
| - Type: {data['operator_type']} | |
| - Compliance Score: {data['compliance_score']}% | |
| - Violations to date: {data['violations']} | |
| - Last Audit: {data['last_audit_date']} | |
| - GGR (ZAR): {data.get('revenue_ggr_zar', 'Not provided')} | |
| - District: {data.get('district', 'Not specified')} | |
| TASK: | |
| 1. Assign a risk_level: "Low" (score 0-25), "Medium" (26-55), "High" (56-80), or "Critical" (81-100). | |
| 2. Assign a numeric risk_score (0-100). | |
| 3. List up to 5 specific compliance flags based on the data. | |
| 4. Provide 3-5 actionable regulatory recommendations. | |
| 5. Predict how many days until the next probable violation (null if Low risk). | |
| 6. Write a 2-sentence executive summary. | |
| Respond ONLY with this JSON schema: | |
| {{ | |
| "risk_level": "...", | |
| "risk_score": 0, | |
| "flags": ["..."], | |
| "recommendations": ["..."], | |
| "predicted_next_violation_days": null, | |
| "summary": "..." | |
| }} | |
| """ | |
| try: | |
| result = _gemini_json(prompt) | |
| result["generated_at"] = _now_iso() | |
| logger.info(f"Risk assessment complete for {data['operator_name']}: {result.get('risk_level')}") | |
| return jsonify(result), 200 | |
| except Exception as e: | |
| logger.error(f"operator-risk error: {e}\n{traceback.format_exc()}") | |
| return jsonify({"error": str(e)}), 500 | |
| # ── 3.2 APPLICATION SCREENING ──────────────────────────────────────────────── | |
| def screen_application(): | |
| """ | |
| Screens a new license application against Regulation 3 requirements. | |
| Input JSON: | |
| applicant_name str required | |
| applicant_type str required ("Individual" | "Company") | |
| application_type str required (e.g. "Land-Based Casino", "Online Betting") | |
| business_plan str required (text description of the business plan) | |
| id_verified bool required | |
| fee_paid bool required | |
| supporting_docs list optional list of document names submitted | |
| proposed_district str optional | |
| Output JSON: | |
| decision str ("APPROVE" | "CONDITIONAL" | "REJECT") | |
| confidence float (0.0-1.0) | |
| regulation_3_gaps list of str (missing Regulation 3 requirements) | |
| conditions list of str (conditions if CONDITIONAL) | |
| rejection_reasons list of str (reasons if REJECT) | |
| estimated_processing_days int | |
| summary str | |
| generated_at str | |
| """ | |
| data = request.get_json(silent=True) or {} | |
| required = ["applicant_name", "applicant_type", "application_type", "business_plan", "id_verified", "fee_paid"] | |
| missing = [f for f in required if f not in data] | |
| if missing: | |
| return jsonify({"error": f"Missing fields: {', '.join(missing)}"}), 400 | |
| prompt = f""" | |
| You are the KZNGBB (KwaZulu-Natal Gaming and Betting Board) Automated Application Screening System, operating under the KwaZulu-Natal | |
| Gambling and Betting Act, No. 4 of 2008, specifically Regulation 3. | |
| Regulation 3 requires: | |
| - Submission on prescribed application form | |
| - Payment of prescribed fee | |
| - Certified ID or company registration documents | |
| - Detailed business plan | |
| - Description of proposed gambling/betting activities | |
| APPLICATION DETAILS: | |
| - Applicant: {data['applicant_name']} ({data['applicant_type']}) | |
| - License Type Sought: {data['application_type']} | |
| - ID/Company Docs Verified: {data['id_verified']} | |
| - Fee Paid: {data['fee_paid']} | |
| - Supporting Documents Submitted: {data.get('supporting_docs', [])} | |
| - Proposed District: {data.get('proposed_district', 'Not specified')} | |
| - Business Plan Summary: {data['business_plan']} | |
| TASK: | |
| 1. Identify any Regulation 3 compliance gaps. | |
| 2. Recommend a decision: APPROVE, CONDITIONAL (approval with conditions), or REJECT. | |
| 3. If CONDITIONAL, list conditions to be met. | |
| 4. If REJECT, list specific rejection reasons. | |
| 5. Estimate processing days (standard is 14 days, complex cases may be 30+). | |
| 6. Provide confidence score (0.0-1.0) in your decision. | |
| 7. Write a 2-sentence summary for the applicant file. | |
| Respond ONLY with this JSON: | |
| {{ | |
| "decision": "...", | |
| "confidence": 0.0, | |
| "regulation_3_gaps": ["..."], | |
| "conditions": ["..."], | |
| "rejection_reasons": ["..."], | |
| "estimated_processing_days": 14, | |
| "summary": "..." | |
| }} | |
| """ | |
| try: | |
| result = _gemini_json(prompt) | |
| result["generated_at"] = _now_iso() | |
| logger.info(f"Application screening for {data['applicant_name']}: {result.get('decision')}") | |
| return jsonify(result), 200 | |
| except Exception as e: | |
| logger.error(f"screen-application error: {e}") | |
| return jsonify({"error": str(e)}), 500 | |
| # ── 3.3 COMPLIANCE NARRATIVE GENERATOR ────────────────────────────────────── | |
| def compliance_narrative(): | |
| """ | |
| Generates a formal compliance report narrative for a given operator audit. | |
| Input JSON: | |
| operator_name str required | |
| audit_date str required | |
| compliance_score float required | |
| violations_detail list required list of violation description strings | |
| previous_score float optional | |
| auditor_notes str optional | |
| Output JSON: | |
| executive_summary str | |
| findings_narrative str | |
| corrective_actions str | |
| board_recommendation str | |
| formal_report_text str (full formal narrative, ready to paste into a report) | |
| generated_at str | |
| """ | |
| data = request.get_json(silent=True) or {} | |
| required = ["operator_name", "audit_date", "compliance_score", "violations_detail"] | |
| missing = [f for f in required if f not in data] | |
| if missing: | |
| return jsonify({"error": f"Missing fields: {', '.join(missing)}"}), 400 | |
| prompt = f""" | |
| You are the KZNGBB (KwaZulu-Natal Gaming and Betting Board) Chief Compliance Officer's AI writing assistant. | |
| Produce a formal regulatory compliance narrative for an operator audit. | |
| AUDIT DATA: | |
| - Operator: {data['operator_name']} | |
| - Audit Date: {data['audit_date']} | |
| - Compliance Score: {data['compliance_score']}% | |
| - Previous Score: {data.get('previous_score', 'N/A')}% | |
| - Violations Found: {json.dumps(data['violations_detail'])} | |
| - Auditor Notes: {data.get('auditor_notes', 'None')} | |
| TASK: | |
| Generate formal, professional regulatory language suitable for official KZNGBB records. | |
| Respond ONLY with this JSON: | |
| {{ | |
| "executive_summary": "2-3 sentences for the board.", | |
| "findings_narrative": "Detailed paragraph describing each violation with regulatory references.", | |
| "corrective_actions": "Bulleted list as prose: specific actions the operator must take.", | |
| "board_recommendation": "One clear recommendation: Maintain License | Issue Warning | Suspend | Revoke.", | |
| "formal_report_text": "The complete formal report text combining all sections, ready for official use." | |
| }} | |
| """ | |
| try: | |
| result = _gemini_json(prompt, model=GEMINI_PRO) | |
| result["generated_at"] = _now_iso() | |
| return jsonify(result), 200 | |
| except Exception as e: | |
| logger.error(f"compliance-narrative error: {e}") | |
| return jsonify({"error": str(e)}), 500 | |
| # ── 3.4 CSI IMPACT ANALYSER ───────────────────────────────────────────────── | |
| def csi_impact(): | |
| """ | |
| Evaluates a Corporate Social Investment (CSI) project and predicts community impact. | |
| Input JSON: | |
| project_name str required | |
| operator_sponsor str required | |
| category str required (e.g. "Education", "Health", "Sports") | |
| budget_zar float required | |
| target_beneficiaries int required | |
| district str required | |
| description str required | |
| duration_months int optional (default 12) | |
| Output JSON: | |
| impact_score int (0-100) | |
| impact_rating str ("Excellent" | "Good" | "Adequate" | "Poor") | |
| beneficiary_reach str (qualitative: "High" | "Medium" | "Low") | |
| roi_narrative str (social return on investment assessment) | |
| alignment_score int (alignment with KZNGBB CSI mandate, 0-100) | |
| risks list of str | |
| recommendations list of str | |
| projected_outcomes list of str | |
| generated_at str | |
| """ | |
| data = request.get_json(silent=True) or {} | |
| required = ["project_name", "operator_sponsor", "category", "budget_zar", "target_beneficiaries", "district", "description"] | |
| missing = [f for f in required if f not in data] | |
| if missing: | |
| return jsonify({"error": f"Missing fields: {', '.join(missing)}"}), 400 | |
| prompt = f""" | |
| You are the KZNGBB(KwaZulu-Natal Gaming and Betting Board) CSI (Corporate Social Investment) Evaluation Engine. | |
| Gambling operators in KwaZulu-Natal are required to reinvest in community development. | |
| CSI PROJECT SUBMISSION: | |
| - Project: {data['project_name']} | |
| - Sponsor: {data['operator_sponsor']} | |
| - Category: {data['category']} | |
| - Budget: R {data['budget_zar']:,.0f} | |
| - Target Beneficiaries: {data['target_beneficiaries']:,} | |
| - District: {data['district']} | |
| - Duration: {data.get('duration_months', 12)} months | |
| - Description: {data['description']} | |
| TASK: | |
| 1. Score the project impact (0-100) based on budget efficiency, reach, and community need. | |
| 2. Rate the impact as Excellent (80+), Good (60-79), Adequate (40-59), or Poor (<40). | |
| 3. Assess beneficiary reach (High/Medium/Low). | |
| 4. Write a social ROI narrative. | |
| 5. Score alignment with KZNGBB CSI mandate (0-100). | |
| 6. List 3 risks and 3 recommendations. | |
| 7. List 3 projected measurable outcomes. | |
| Respond ONLY with this JSON: | |
| {{ | |
| "impact_score": 0, | |
| "impact_rating": "...", | |
| "beneficiary_reach": "...", | |
| "roi_narrative": "...", | |
| "alignment_score": 0, | |
| "risks": ["..."], | |
| "recommendations": ["..."], | |
| "projected_outcomes": ["..."] | |
| }} | |
| """ | |
| try: | |
| result = _gemini_json(prompt) | |
| result["generated_at"] = _now_iso() | |
| return jsonify(result), 200 | |
| except Exception as e: | |
| logger.error(f"csi-impact error: {e}") | |
| return jsonify({"error": str(e)}), 500 | |
| # ── 3.5 REVENUE ANOMALY DETECTION ─────────────────────────────────────────── | |
| def revenue_anomaly(): | |
| """ | |
| Detects anomalies and patterns in an operator's monthly GGR submissions. | |
| Input JSON: | |
| operator_name str required | |
| operator_type str required | |
| monthly_ggr list required list of {month: str, ggr_zar: float, tax_zar: float} | |
| industry_avg_growth float optional (e.g. 0.08 for 8%) | |
| Output JSON: | |
| anomalies list of {month, type, severity, detail} | |
| trend str ("Growing" | "Declining" | "Stable" | "Volatile") | |
| tax_compliance_rate float (0.0-1.0) | |
| underreporting_flag bool | |
| underreporting_confidence float (0.0-1.0) | |
| narrative str | |
| audit_recommendation str ("Routine" | "Priority Review" | "Immediate Investigation") | |
| generated_at str | |
| """ | |
| data = request.get_json(silent=True) or {} | |
| required = ["operator_name", "operator_type", "monthly_ggr"] | |
| missing = [f for f in required if f not in data] | |
| if missing: | |
| return jsonify({"error": f"Missing fields: {', '.join(missing)}"}), 400 | |
| if not isinstance(data["monthly_ggr"], list) or len(data["monthly_ggr"]) < 2: | |
| return jsonify({"error": "monthly_ggr must be a list with at least 2 months of data"}), 400 | |
| prompt = f""" | |
| You are the KZNGBB(KwaZulu-Natal Gaming and Betting Board) Financial Intelligence Unit AI. | |
| Analyse the following Gross Gaming Revenue (GGR) data for anomalies, underreporting, | |
| and compliance patterns. The standard tax rate is 20% of GGR. | |
| OPERATOR: {data['operator_name']} ({data['operator_type']}) | |
| INDUSTRY AVG GROWTH: {data.get('industry_avg_growth', 0.08) * 100:.1f}% per month | |
| MONTHLY GGR DATA: | |
| {json.dumps(data['monthly_ggr'], indent=2)} | |
| TASK: | |
| 1. Identify anomalies (sudden drops >30%, spikes >50%, irregular tax ratios, flat reporting). | |
| For each anomaly: month, type, severity (Low/Medium/High/Critical), detail. | |
| 2. Classify the overall trend. | |
| 3. Calculate average tax compliance rate (tax_zar / ggr_zar should be ~0.20). | |
| 4. Flag potential underreporting if tax ratio is consistently below 0.18. | |
| 5. Write a 3-sentence narrative for the financial analytics report. | |
| 6. Recommend audit action level. | |
| Respond ONLY with this JSON: | |
| {{ | |
| "anomalies": [{{"month": "...", "type": "...", "severity": "...", "detail": "..."}}], | |
| "trend": "...", | |
| "tax_compliance_rate": 0.0, | |
| "underreporting_flag": false, | |
| "underreporting_confidence": 0.0, | |
| "narrative": "...", | |
| "audit_recommendation": "..." | |
| }} | |
| """ | |
| try: | |
| result = _gemini_json(prompt) | |
| result["generated_at"] = _now_iso() | |
| return jsonify(result), 200 | |
| except Exception as e: | |
| logger.error(f"revenue-anomaly error: {e}") | |
| return jsonify({"error": str(e)}), 500 | |
| # ── 3.6 REGULATORY QUERY ASSISTANT ────────────────────────────────────────── | |
| def regulatory_query(): | |
| """ | |
| Natural language Q&A on KwaZulu-Natal gambling regulation. | |
| Uses Gemini with Google Search grounding for current regulatory context. | |
| Input JSON: | |
| question str required | |
| context str optional (e.g. "Online Casino", "Bookmaker license renewal") | |
| user_role str optional ("Operator" | "Applicant" | "Board Member" | "Public") | |
| Output JSON: | |
| answer str | |
| regulatory_refs list of str (Act sections, Regulation numbers) | |
| confidence str ("High" | "Medium" | "Low") | |
| disclaimer str | |
| follow_up_questions list of str | |
| generated_at str | |
| """ | |
| data = request.get_json(silent=True) or {} | |
| if not data.get("question"): | |
| return jsonify({"error": "question is required"}), 400 | |
| prompt = f""" | |
| You are the KZNGBB(KwaZulu-Natal Gaming and Betting Board) AI Regulatory Assistant, an expert in the KwaZulu-Natal Gambling and Betting Act No. 4 of 2008 and all associated Regulations. | |
| USER ROLE: {data.get('user_role', 'General')} | |
| CONTEXT: {data.get('context', 'General regulatory query')} | |
| QUESTION: {data['question']} | |
| Search for the most current information on this regulatory topic and provide a clear, accurate answer. | |
| TASK: | |
| 1. Answer the question clearly and accurately. | |
| 2. Reference specific Act sections or Regulation numbers where applicable. | |
| 3. Rate your confidence (High/Medium/Low) based on how specific and current the information is. | |
| 4. Write a brief regulatory disclaimer. | |
| 5. Suggest 3 follow-up questions the user might find helpful. | |
| Respond ONLY with this JSON: | |
| {{ | |
| "answer": "...", | |
| "regulatory_refs": ["Act No. 4 of 2008, Section X", "Regulation 3", "..."], | |
| "confidence": "...", | |
| "disclaimer": "...", | |
| "follow_up_questions": ["...", "...", "..."] | |
| }} | |
| """ | |
| try: | |
| res = client.models.generate_content( | |
| model=GEMINI_FLASH, | |
| contents=prompt, | |
| config=types.GenerateContentConfig( | |
| tools=[{"google_search": {}}], | |
| response_mime_type="application/json" | |
| ) | |
| ) | |
| raw = _strip_json_fences(res.text) | |
| result = json.loads(raw) | |
| result["generated_at"] = _now_iso() | |
| return jsonify(result), 200 | |
| except Exception as e: | |
| logger.error(f"regulatory-query error: {e}") | |
| return jsonify({"error": str(e)}), 500 | |
| # ── 3.7 GEOGRAPHIC INTELLIGENCE SUMMARY ───────────────────────────────────── | |
| def geographic_intel(): | |
| """ | |
| Produces a strategic intelligence summary for a KZN district or municipality. | |
| Input JSON: | |
| district str required (e.g. "eThekwini", "Umgungundlovu") | |
| operators_in_district list required list of {name, type, compliance_score, violations} | |
| population int optional | |
| include_recommendations bool optional (default true) | |
| Output JSON: | |
| district_risk_level str ("Low" | "Medium" | "High" | "Critical") | |
| operator_density str | |
| dominant_type str (most common operator type) | |
| compliance_overview str | |
| hotspots list of str | |
| strategic_recommendations list of str | |
| resource_allocation_advice str | |
| narrative str | |
| generated_at str | |
| """ | |
| data = request.get_json(silent=True) or {} | |
| required = ["district", "operators_in_district"] | |
| missing = [f for f in required if f not in data] | |
| if missing: | |
| return jsonify({"error": f"Missing fields: {', '.join(missing)}"}), 400 | |
| prompt = f""" | |
| You are the KZNGBB(KwaZulu-Natal Gaming and Betting Board) Geographic Intelligence Analyst AI. | |
| Produce a strategic intelligence brief for a KwaZulu-Natal district. | |
| DISTRICT: {data['district']} | |
| POPULATION: {data.get('population', 'Unknown')} | |
| OPERATORS IN DISTRICT: | |
| {json.dumps(data['operators_in_district'], indent=2)} | |
| TASK: | |
| 1. Assess the overall district risk level. | |
| 2. Describe operator density relative to district context. | |
| 3. Identify the dominant operator type. | |
| 4. Summarise the compliance landscape. | |
| 5. List 3-5 hotspot concerns (areas or operators needing attention). | |
| 6. Provide 3-5 strategic recommendations for board resource allocation. | |
| 7. Advise on resource allocation priorities. | |
| 8. Write a 3-sentence intelligence brief narrative. | |
| Respond ONLY with this JSON: | |
| {{ | |
| "district_risk_level": "...", | |
| "operator_density": "...", | |
| "dominant_type": "...", | |
| "compliance_overview": "...", | |
| "hotspots": ["..."], | |
| "strategic_recommendations": ["..."], | |
| "resource_allocation_advice": "...", | |
| "narrative": "..." | |
| }} | |
| """ | |
| try: | |
| result = _gemini_json(prompt) | |
| result["generated_at"] = _now_iso() | |
| return jsonify(result), 200 | |
| except Exception as e: | |
| logger.error(f"geographic-intel error: {e}") | |
| return jsonify({"error": str(e)}), 500 | |
| # ── 3.8 EXPORT REPORT GENERATOR ───────────────────────────────────────────── | |
| def generate_report(): | |
| """ | |
| Generates a formatted executive report narrative for board export. | |
| Input JSON: | |
| report_type str required ("Monthly" | "Quarterly" | "Annual" | "Incident") | |
| period str required (e.g. "Q1 2025", "May 2025") | |
| total_licenses int required | |
| revenue_ggr_zar float required | |
| tax_collected_zar float required | |
| compliance_rate float required (0-100) | |
| pending_applications int required | |
| violations_this_period int required | |
| csi_spend_zar float optional | |
| key_events list optional list of str (notable events in the period) | |
| board_chair str optional | |
| Output JSON: | |
| report_title str | |
| executive_summary str | |
| performance_highlights list of str | |
| compliance_section str | |
| financial_section str | |
| outlook str | |
| full_report_text str | |
| generated_at str | |
| """ | |
| data = request.get_json(silent=True) or {} | |
| required = ["report_type", "period", "total_licenses", "revenue_ggr_zar", | |
| "tax_collected_zar", "compliance_rate", "pending_applications", "violations_this_period"] | |
| missing = [f for f in required if f not in data] | |
| if missing: | |
| return jsonify({"error": f"Missing fields: {', '.join(missing)}"}), 400 | |
| prompt = f""" | |
| You are the KZNGBB(KwaZulu-Natal Gaming and Betting Board) AI Report Drafting Engine. | |
| Draft a professional {data['report_type']} board report for the period: {data['period']}. | |
| DATA: | |
| - Total Active Licenses: {data['total_licenses']} | |
| - Gross Gaming Revenue: R {data['revenue_ggr_zar']:,.0f} | |
| - Tax Collected: R {data['tax_collected_zar']:,.0f} | |
| - Compliance Rate: {data['compliance_rate']}% | |
| - Pending Applications: {data['pending_applications']} | |
| - Violations This Period: {data['violations_this_period']} | |
| - CSI Spend: R {data.get('csi_spend_zar', 0):,.0f} | |
| - Key Events: {json.dumps(data.get('key_events', []))} | |
| - Board Chair: {data.get('board_chair', 'The Chairperson')} | |
| TASK: | |
| Generate a formal, professional board-quality report with: | |
| 1. A formal report title. | |
| 2. Executive summary (3 sentences). | |
| 3. 4-6 performance highlights as bullet points (prose list). | |
| 4. Compliance section paragraph. | |
| 5. Financial analytics section paragraph. | |
| 6. Forward-looking outlook paragraph. | |
| 7. A full_report_text that combines all sections into a polished, ready-to-use narrative. | |
| Respond ONLY with this JSON: | |
| {{ | |
| "report_title": "...", | |
| "executive_summary": "...", | |
| "performance_highlights": ["...", "..."], | |
| "compliance_section": "...", | |
| "financial_section": "...", | |
| "outlook": "...", | |
| "full_report_text": "..." | |
| }} | |
| """ | |
| try: | |
| result = _gemini_json(prompt, model=GEMINI_PRO) | |
| result["generated_at"] = _now_iso() | |
| return jsonify(result), 200 | |
| except Exception as e: | |
| logger.error(f"generate-report error: {e}") | |
| return jsonify({"error": str(e)}), 500 | |
| # ── 3.9 VIOLATION PATTERN ANALYSIS ───────────────────────────────────────── | |
| def violation_patterns(): | |
| """ | |
| Analyses historical violation records across operators to identify systemic patterns. | |
| Input JSON: | |
| violations list required list of {operator_name, type, violation_type, date, district, severity} | |
| period_label str optional (e.g. "2025 YTD") | |
| Output JSON: | |
| most_common_violation str | |
| highest_risk_type str (operator type with most violations) | |
| district_hotspot str | |
| seasonal_pattern str or null | |
| systemic_issues list of str | |
| enforcement_priorities list of str | |
| board_action_items list of str | |
| statistical_summary dict | |
| generated_at str | |
| """ | |
| data = request.get_json(silent=True) or {} | |
| if not data.get("violations") or not isinstance(data["violations"], list): | |
| return jsonify({"error": "violations list is required"}), 400 | |
| prompt = f""" | |
| You are the KZNGBB(KwaZulu-Natal Gaming and Betting Board) Enforcement Intelligence AI. | |
| Analyse the following violation records and identify systemic patterns. | |
| PERIOD: {data.get('period_label', 'Historical')} | |
| VIOLATION RECORDS: | |
| {json.dumps(data['violations'], indent=2)} | |
| TASK: | |
| 1. Identify the most common violation type. | |
| 2. Identify which operator type has the highest violation rate. | |
| 3. Identify the district with the most violations (hotspot). | |
| 4. Detect any seasonal or temporal patterns. | |
| 5. List 3-5 systemic issues revealed by the data. | |
| 6. List 3-5 specific enforcement priorities for the board. | |
| 7. List 3 concrete board action items. | |
| 8. Produce a statistical_summary dict with: total_violations, by_type (dict), by_district (dict), by_severity (dict). | |
| Respond ONLY with this JSON: | |
| {{ | |
| "most_common_violation": "...", | |
| "highest_risk_type": "...", | |
| "district_hotspot": "...", | |
| "seasonal_pattern": null, | |
| "systemic_issues": ["..."], | |
| "enforcement_priorities": ["..."], | |
| "board_action_items": ["..."], | |
| "statistical_summary": {{ | |
| "total_violations": 0, | |
| "by_type": {{}}, | |
| "by_district": {{}}, | |
| "by_severity": {{}} | |
| }} | |
| }} | |
| """ | |
| try: | |
| result = _gemini_json(prompt) | |
| result["generated_at"] = _now_iso() | |
| return jsonify(result), 200 | |
| except Exception as e: | |
| logger.error(f"violation-patterns error: {e}") | |
| return jsonify({"error": str(e)}), 500 | |
| # ── 3.10 LICENCE RENEWAL ADVISOR ──────────────────────────────────────────── | |
| def renewal_advisory(): | |
| """ | |
| Produces an AI renewal recommendation for an expiring operator license. | |
| Input JSON: | |
| operator_name str required | |
| license_number str required | |
| operator_type str required | |
| expiry_date str required (ISO date) | |
| compliance_history list required list of {year, score, violations} | |
| ggr_trend str optional ("growing" | "declining" | "stable") | |
| csi_compliant bool optional | |
| Output JSON: | |
| recommendation str ("RENEW" | "RENEW_WITH_CONDITIONS" | "DEFER" | "DECLINE") | |
| confidence float | |
| rationale str | |
| conditions list of str | |
| risk_flags list of str | |
| renewal_fee_category str ("Standard" | "Elevated" | "Premium Review") | |
| next_audit_priority str ("Routine" | "Priority" | "Immediate") | |
| generated_at str | |
| """ | |
| data = request.get_json(silent=True) or {} | |
| required = ["operator_name", "license_number", "operator_type", "expiry_date", "compliance_history"] | |
| missing = [f for f in required if f not in data] | |
| if missing: | |
| return jsonify({"error": f"Missing fields: {', '.join(missing)}"}), 400 | |
| prompt = f""" | |
| You are the KZNGBB(KwaZulu-Natal Gaming and Betting Board) License Renewal Advisory AI. | |
| Assess whether an operator's license should be renewed under the KZN Gambling and Betting Act. | |
| OPERATOR: {data['operator_name']} | {data['license_number']} | {data['operator_type']} | |
| EXPIRY DATE: {data['expiry_date']} | |
| GGR TREND: {data.get('ggr_trend', 'Unknown')} | |
| CSI COMPLIANT: {data.get('csi_compliant', 'Unknown')} | |
| COMPLIANCE HISTORY (last {len(data['compliance_history'])} years): | |
| {json.dumps(data['compliance_history'], indent=2)} | |
| TASK: | |
| 1. Recommend: RENEW, RENEW_WITH_CONDITIONS, DEFER (pending investigation), or DECLINE. | |
| 2. Provide confidence (0.0-1.0). | |
| 3. Write a clear rationale paragraph. | |
| 4. List any renewal conditions (if RENEW_WITH_CONDITIONS). | |
| 5. List risk flags identified from the compliance history. | |
| 6. Categorise renewal fee complexity (Standard/Elevated/Premium Review). | |
| 7. Set next audit priority. | |
| Respond ONLY with this JSON: | |
| {{ | |
| "recommendation": "...", | |
| "confidence": 0.0, | |
| "rationale": "...", | |
| "conditions": ["..."], | |
| "risk_flags": ["..."], | |
| "renewal_fee_category": "...", | |
| "next_audit_priority": "..." | |
| }} | |
| """ | |
| try: | |
| result = _gemini_json(prompt) | |
| result["generated_at"] = _now_iso() | |
| return jsonify(result), 200 | |
| except Exception as e: | |
| logger.error(f"renewal-advisory error: {e}") | |
| return jsonify({"error": str(e)}), 500 | |
| # ----------------------------------------------------------------------------- | |
| # 4. SYSTEM ENDPOINTS | |
| # ----------------------------------------------------------------------------- | |
| def health(): | |
| return jsonify({ | |
| "status": "operational", | |
| "service": "KZNGBB AI Intelligence Layer", | |
| "version": "1.0.0", | |
| "model": GEMINI_FLASH, | |
| "endpoints": [ | |
| "/api/ai/operator-risk", | |
| "/api/ai/screen-application", | |
| "/api/ai/compliance-narrative", | |
| "/api/ai/csi-impact", | |
| "/api/ai/revenue-anomaly", | |
| "/api/ai/regulatory-query", | |
| "/api/ai/geographic-intel", | |
| "/api/ai/generate-report", | |
| "/api/ai/violation-patterns", | |
| "/api/ai/renewal-advisory" | |
| ], | |
| "timestamp": _now_iso() | |
| }), 200 | |
| def root(): | |
| return jsonify({ | |
| "name": "KZNGBB AI Intelligence Layer", | |
| "description": "Gemini-powered regulatory intelligence for the KwaZulu-Natal Gambling and Betting Board", | |
| "status": "operational", | |
| "docs": "/health" | |
| }), 200 | |
| # ----------------------------------------------------------------------------- | |
| # 5. MAIN | |
| # ----------------------------------------------------------------------------- | |
| if __name__ == "__main__": | |
| logger.info("KZNGBB AI Intelligence Layer starting on port 7860...") | |
| app.run(debug=False, host="0.0.0.0", port=int(os.environ.get("PORT", 7860))) |