This cookbook details how to deploy a localized, containerized urology, urologic oncology, and endourology decision-support engine for outpatient urology clinics, cystoscopy surveillance suites, and surgical oncology tumor boards to ingest transurethral resection of bladder tumor ($\text{TURBT}$) histopathology, tumor multifocality and diameter metrics, carcinoma in situ ($\text{CIS}$) status, prior intravesical immunotherapy chronologies, and recurrence intervals, classify tumors according to the Revised EAU 2024 & AUA / SUO NMIBC Risk Stratification (Low, Intermediate, High, Very High Risk), generate SWOG 8507 Intravesical Bacillus Calmette-Guérin ($\text{BCG}$) Induction & 3-Year Maintenance Schedules, audit FDA / AUA BCG-Unresponsive Status, guide Second-Line Salvage Therapy (Early Radical Cystectomy vs Sequential Gemcitabine-Docetaxel vs Pembrolizumab vs Nadofaragene Firadenovec vs Nogapendekin Alfa Inbakicept), and enforce the BCG Mycobacterial Sepsis Absolute Contraindication Sentinel according to European Association of Urology ($\text{EAU 2024}$), AUA / SUO, and NCCN consensus guidelines without external cloud API reliance.
Non-Muscle Invasive Bladder Cancer ($\text{NMIBC}$), comprising stages $\text{Ta}$, $\text{T1}$, and $\text{CIS}$, accounts for $75 - 80\%$ of newly diagnosed urothelial carcinomas of the bladder:
[Patient Telemetry: TURBT Histology, T-Stage, Grade, CIS, Size, LVI, Prior BCG Doses]
│
â–¼
[EAU 2024 & AUA Risk Stratifier: Low vs Intermediate vs High vs Very High Risk]
│
â–¼
[Adequate BCG Assessment: Evaluates Induction >= 5/6 + Maintenance >= 2/3 Doses]
│
â–¼
[FDA/AUA BCG-Unresponsive Classifier: Persistent CIS <= 12m or HG Ta/T1 <= 6m]
│
â–¼
[Salvage Strategy Generator: Early Radical Cystectomy vs Gem/Doce vs Pembrolizumab]
│
â–¼
[BCG Sepsis Absolute Contraindication Sentinel: Hematuria/Trauma/Early Post-Op Gate]
Install required scientific Python and urologic oncology modeling packages:
pip install numpy scipy pandas torch torchvision matplotlib
"""
Cookbook 347: Offline Urology NMIBC EAU Risk Stratification & BCG Unresponsive Engine
OpenPHR Clinical AI Working Group (https://openphr.org)
"""
import math
import numpy as np
import pandas as pd
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass
@dataclass
class NMIBCPatientTelemetry:
patient_id: str
age_years: float = 68.0
sex: str = "Male"
# TURBT Pathological & Cystoscopic Telemetry
t_stage: str = "T1" # "Ta", "T1"
who_grade: str = "High Grade (HG)" # "Low Grade (LG)", "High Grade (HG)"
carcinoma_in_situ_present: bool = True # Concurrent or isolated CIS
tumor_diameter_cm: float = 3.5 # >= 3.0 cm is high risk
is_multifocal: bool = True # >= 3 tumors
lymphovascular_invasion_present: bool = True # LVI = Very High Risk!
variant_histology_present: bool = False # e.g. Micropapillary, Plasmacytoid, Nested
prostatic_urethra_involvement: bool = False
# Intravesical BCG Exposure Telemetry
prior_bcg_induction_doses: int = 6 # Completed 6/6 induction
prior_bcg_maintenance_doses: int = 3 # Completed 3/3 maintenance cycle #1 (Adequate BCG!)
months_since_last_bcg_exposure: float = 5.0 # Recurrence at 5 months post-BCG
current_cystoscopy_finding: str = "Persistent High-Grade T1 + CIS"
# Safety Telemetry on Day of Planned Instillation
gross_hematuria_present: bool = False
traumatic_catheterization_today: bool = False
days_since_turbt_resection: int = 24 # > 14 days (Safe mucosal healing)
@dataclass
class NMIBCEvaluationReport:
patient_id: str
eau_2024_risk_category: str # "VERY HIGH RISK NMIBC"
aua_suo_risk_category: str # "High Risk"
risk_factors_detected: List[str]
bcg_unresponsive_status: str # "🚨 CONFIRMED BCG-UNRESPONSIVE NMIBC (FDA / AUA Definition Met)"
treatment_recommendations: List[str]
safety_sentinels: List[str]
clinical_eau_aua_directive: str
class NMIBCDecisionEngine:
"""
Offline clinical engine for EAU 2024 / AUA risk stratification,
BCG-unresponsive classification, and second-line salvage gating.
"""
def stratify_eau_risk(self, d: NMIBCPatientTelemetry) -> Tuple[str, str, List[str]]:
factors = []
is_t1 = d.t_stage == "T1"
is_hg = "High Grade" in d.who_grade
has_cis = d.carcinoma_in_situ_present
is_large = d.tumor_diameter_cm >= 3.0
is_multi = d.is_multifocal
has_lvi = d.lymphovascular_invasion_present
has_variant = d.variant_histology_present
has_prostate = d.prostatic_urethra_involvement
if has_lvi: factors.append("Lymphovascular invasion (LVI) present")
if has_variant: factors.append("Variant urothelial histology (micropapillary/plasmacytoid)")
if has_cis: factors.append("Carcinoma in Situ (CIS) present")
if is_t1 and is_hg: factors.append("T1 High-Grade invasive tumor")
if is_large: factors.append(f"Large tumor diameter ({d.tumor_diameter_cm:.1f} cm >= 3.0 cm)")
if is_multi: factors.append("Multifocal tumor burden")
# 1. Very High Risk (EAU 2024)
if (is_t1 and is_hg and has_cis) or has_lvi or has_variant or (is_t1 and is_hg and (is_large or is_multi)) or has_prostate:
eau_risk = "VERY HIGH RISK NMIBC"
aua_risk = "High Risk NMIBC"
# 2. High Risk (EAU 2024)
elif (is_t1 and is_hg) or has_cis or (not is_t1 and is_hg and (is_large or is_multi)):
eau_risk = "HIGH RISK NMIBC"
aua_risk = "High Risk NMIBC"
# 3. Intermediate Risk
elif is_large or is_multi or (not is_t1 and is_hg and not is_large and not is_multi):
eau_risk = "INTERMEDIATE RISK NMIBC"
aua_risk = "Intermediate Risk NMIBC"
# 4. Low Risk
else:
eau_risk = "LOW RISK NMIBC"
aua_risk = "Low Risk NMIBC"
return eau_risk, aua_risk, factors
def evaluate_bcg_unresponsive_status(self, d: NMIBCPatientTelemetry) -> str:
received_adequate_bcg = (d.prior_bcg_induction_doses >= 5 and d.prior_bcg_maintenance_doses >= 2) or (d.prior_bcg_induction_doses >= 10)
if not received_adequate_bcg:
return "BCG Incomplete / Inadequate Exposure (< 5/6 induction or < 2/3 maintenance). Does not meet BCG-unresponsive criteria."
is_hg = "High Grade" in d.who_grade or "CIS" in d.current_cystoscopy_finding
has_cis = d.carcinoma_in_situ_present or "CIS" in d.current_cystoscopy_finding
if has_cis and d.months_since_last_bcg_exposure <= 12.0:
return f"🚨 CONFIRMED BCG-UNRESPONSIVE NMIBC: Persistent/recurrent CIS at {d.months_since_last_bcg_exposure:.0f} months following adequate BCG (FDA/AUA Definition Met)."
elif is_hg and d.months_since_last_bcg_exposure <= 6.0:
return f"🚨 CONFIRMED BCG-UNRESPONSIVE NMIBC: Recurrent High-Grade Ta/T1 tumor at {d.months_since_last_bcg_exposure:.0f} months following adequate BCG (FDA/AUA Definition Met)."
elif d.months_since_last_bcg_exposure > 12.0 and is_hg:
return f"Late High-Grade BCG Relapse ({d.months_since_last_bcg_exposure:.0f} months). May consider repeat BCG challenge vs salvage therapy."
else:
return "BCG-Responsive / No Active Failure."
def generate_treatment_plan(self, eau_risk: str, bcg_status: str, d: NMIBCPatientTelemetry) -> Tuple[List[str], List[str]]:
plan = []
sentinels = []
# Safety Sentinel: BCG Sepsis Risk on Day of Instillation
if d.gross_hematuria_present or d.traumatic_catheterization_today or d.days_since_turbt_resection < 14:
sentinels.append("🚨 ABSOLUTE BCG CONTRAINDICATION: Instilling BCG in the presence of gross hematuria, traumatic catheterization, or early post-TURBT (< 2 weeks) causes systemic intravasation of live Mycobacterium bovis resulting in FATAL MYCOBACTERIAL SEPSIS & SHOCK. STAT CANCEL/POSTPONE BCG INSTILLATION!")
# Treatment Strategy
if "CONFIRMED BCG-UNRESPONSIVE" in bcg_status or "VERY HIGH RISK" in eau_risk:
plan.append("1. PRIMARY FIRST-LINE RECOMMENDATION: EARLY RADICAL CYSTECTOMY with Bilateral Pelvic Lymphadenectomy (Offers >80-90% cure before muscle invasion).")
plan.append("2. BLADDER-PRESERVATION SECOND-LINE SALVAGE (If cystectomy ineligible or patient refuses):")
plan.append(" • OPTION A: Sequential Intravesical Gemcitabine (1.0g in 50mL x 90 min) + Docetaxel (37.5mg in 50mL x 120 min) weekly x 6 induction, then monthly maintenance.")
plan.append(" • OPTION B (FDA Approved for CIS): IV Pembrolizumab 200 mg q3w (or 400 mg q6w) for up to 24 months.")
plan.append(" • OPTION C (Gene Therapy): Intravesical Nadofaragene Firadenovec (Adstiladrin 75 mL) once every 3 months.")
plan.append(" • OPTION D: Nogapendekin Alfa Inbakicept (Anktiva 400 mcg) + BCG intravesical weekly x 6, then maintenance.")
elif "HIGH RISK" in eau_risk:
plan.append("1. FULL-DOSE INTRAVESICAL BCG IMMUNOTHERAPY (SWOG 8507 Protocol):")
plan.append(" • Induction: 6 weekly instillations initiated 2-4 weeks post-TURBT.")
plan.append(" • 3-Year Maintenance: 3 weekly instillations at Months 3, 6, 12, 18, 24, 30, and 36.")
elif "INTERMEDIATE RISK" in eau_risk:
plan.append("1. Intravesical Chemotherapy (e.g. Mitomycin C or Gemcitabine) or BCG for 1 year.")
else:
plan.append("1. Single immediate post-operative instillation of intravesical chemotherapy (Gemcitabine 2.0g) within 6 hours of TURBT; no maintenance needed.")
return plan, sentinels
def evaluate_case(self, data: NMIBCPatientTelemetry) -> NMIBCEvaluationReport:
eau_risk, aua_risk, factors = self.stratify_eau_risk(data)
bcg_status = self.evaluate_bcg_unresponsive_status(data)
rx_plan, sentinels = self.generate_treatment_plan(eau_risk, bcg_status, data)
directives = []
directives.append(f"STRATIFICATION: {eau_risk} ({aua_risk}).")
directives.append(f"BCG STATUS: {bcg_status}.")
directives.append("RECOMMENDATION: Early radical cystectomy vs sequential Gemcitabine-Docetaxel salvage.")
return NMIBCEvaluationReport(
patient_id=data.patient_id,
eau_2024_risk_category=eau_risk,
aua_suo_risk_category=aua_risk,
risk_factors_detected=factors,
bcg_unresponsive_status=bcg_status,
treatment_recommendations=rx_plan,
safety_sentinels=sentinels,
clinical_eau_aua_directive=" ".join(directives)
)
# Example Execution & Verification
if __name__ == "__main__":
engine = NMIBCDecisionEngine()
print("=" * 80)
print("OpenPHR Clinical Urology NMIBC EAU Risk & BCG Unresponsive Engine")
print("=" * 80)
# Test Case 1: 68-year-old male with recurrent High-Grade T1 bladder cancer + CIS + LVI (Very High Risk).
# Prior BCG: Completed 6/6 induction + 3/3 maintenance (Adequate BCG).
# Recurrence: High-Grade T1 + CIS at 5 months post-BCG -> CONFIRMED BCG-UNRESPONSIVE!
# Triage: Early Radical Cystectomy vs Sequential Gemcitabine-Docetaxel / Pembrolizumab / Adstiladrin!
nmibc1 = NMIBCPatientTelemetry(
patient_id="URO-NMIBC-8801",
age_years=68.0,
t_stage="T1",
who_grade="High Grade (HG)",
carcinoma_in_situ_present=True,
tumor_diameter_cm=3.5,
is_multifocal=True,
lymphovascular_invasion_present=True,
variant_histology_present=False,
prior_bcg_induction_doses=6,
prior_bcg_maintenance_doses=3,
months_since_last_bcg_exposure=5.0,
current_cystoscopy_finding="Persistent High-Grade T1 + CIS",
gross_hematuria_present=False,
traumatic_catheterization_today=False,
days_since_turbt_resection=24
)
rep1 = engine.evaluate_case(nmibc1)
print(f"\n[Patient {rep1.patient_id} - Urologic Oncology Assessment]")
print(f"EAU 2024 Risk Category: {rep1.eau_2024_risk_category}")
print(f"AUA / SUO Risk Category: {rep1.aua_suo_risk_category}")
print("\nRisk Factors Detected:")
for rf in rep1.risk_factors_detected:
print(f" • {rf}")
print(f"\nBCG Response Status:\n {rep1.bcg_unresponsive_status}")
print("\nTherapeutic Recommendations:")
for rx in rep1.treatment_recommendations:
print(f" {rx}")
if rep1.safety_sentinels:
print("\nSafety Sentinels:")
for s in rep1.safety_sentinels:
print(f" 🚨 {s}")
print(f"\nEAU / AUA Consensus Directive:\n{rep1.clinical_eau_aua_directive}")