UZMAilyas/CSReview
1
1#from langchain_ollama import ChatOllama #IMPORT2from dotenv import load_dotenv3from langchain_core.tools import tool4import math5from datetime import datetime, timedelta6from langchain_core.tools import tool7import matplotlib.pyplot as plt8from langchain.agents import create_agent #IMPORT9from langchain_core.tools import tool10from datetime import datetime11from typing import Dict, Union12from langchain_google_genai import ChatGoogleGenerativeAI #IMPORT13import math 14import random15import os16import gradio as gr17 18from datetime import datetime, timedelta19 20def parse_date(date_text):21 for fmt in ("%d-%m-%Y", "%d/%m/%Y", "%Y-%m-%d"):22 try:23 return datetime.strptime(date_text.strip(), fmt)24 except ValueError:25 continue26 27 raise ValueError(28 f"Invalid date '{date_text}'. Use DD-MM-YYYY."29 )30#load 31load_dotenv()32 33#*******MODAL PROVIDE*************************34 35#api_key = os.getenv("GEMINI_API_KEY")36#llm = ChatGoogleGenerativeAI(model= "gemini-flash-lite-latest" , api_key=api_key) #model="gemini-2.5-flash",api_key=api_key)37#CORRECT_PASSWORD =os.getenv("passCS")38api_key = os.getenv("GEMINI_API_KEY")39 40MODEL_PROVIDER = os.getenv("MODEL_PROVIDER", "gemini").lower().strip()41 42#OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "deepseek-r1:1.5b")43 44CORRECT_PASSWORD = os.getenv("passCS")45 46# ==========================================================47# MEDPHY-NEXUS DUAL LLM CONFIGURATION48# ==========================================================49 50# Gemini Cloud LLM51gemini_llm = ChatGoogleGenerativeAI(52 model="gemini-flash-lite-latest",53 api_key=api_key54)55 56# Local Ollama LLM57#ollama_llm = ChatOllama(58# model=OLLAMA_MODEL,59# temperature=060#)61 62# Select active LLM63if MODEL_PROVIDER == "ollama":64 llm = ollama_llm65 print(f"π₯οΈ MEDPHY-NEXUS running with LOCAL OLLAMA: {OLLAMA_MODEL}")66else:67 llm = gemini_llm68 print("βοΈ MEDPHY-NEXUS running with GEMINI")69 70# Dictionary of dose limits (units in mSv)71OCCUPATIONAL_LIMITS = {72 "Annual dose limit to Workers": 50, # mSv per year73 "Annual Exposure Limit to Workers (5-year avg)": 20, # mSv per year74 "Monthly dose Limit to Worker": 4.16, # mSv per month (approx.)75 "Lens of Eye Dose Limit to Worker": 50, # mSv per year76 "Extremity Dose Limit to Worker": 500, # mSv per year77 "Annual Dose Limit to Public": 1, # mSv per year78 "Lens of Eye Dose Limit to Public": 15, # mSv per year79 "Extremity Dose Limit to Public": 50, # mSv per year80 "Annual Dose Limit to Caregivers": 5, # mSv per year81 "Annual Dose Limit to Comforter": 5, # mSv per year82 "PNRA release limit(dose)": 50, # USv 83 "PNRA release limit(activity)": 30, # mci 84 85 "Discharge TEDE Threshold":0.5, #rem86 "Occupancy factor (6/24)":0.25, #if patient expose 6hr public a day87 "Occupancy factor (3/24)":0.125, #if public expose 3hr a day from patient88 89 }90# β
Dictionary of Radioactive Nuclide & Their Half-Lives (in Days)91from datetime import datetime92import math93RADIOACTIVE_NUCLIDES = {94 "Uranium-238": 4.47e9 * 365, # Billion years converted to days95 "Uranium-235": 704e6 * 365, # Million years converted to days96 "Thorium-232": 14.05e9 * 365,97 "Plutonium-239": 24100,98 "Plutonium-238": 87.7,99 "Radon-222": 3.8,100 "Radium-226": 1600,101 "Polonium-210": 138,102 "Americium-241": 432,103 "Carbon-14": 5730,104 "Strontium-90": 28.8 * 365,105 "Iodine-131 (I-131)": 8.02,106 "I-131": 8.02,107 "Iodine-125(I-125)": 59.4,108 "I-125": 59.4,109 "Cesium-137": 30.2 * 365,110 "Cs-137": 30.2 * 365,111 "Technetium-99m": 0.25, # 6 hours converted to days112 "Tc-99m": 0.25, # 6 hours converted to days113 "Cobalt-60": 5.27 * 365,114 "Co-60": 5.27 * 365,115 "Cobalt-57": 271.8,116 "Co-57": 271.8,117 "Tritium (H-3)": 12.32 * 365,118 "Krypton-85": 10.76 * 365,119 "Ruthenium-106": 373.6,120 "Molybdenum (MO-99)": 66/24,121 "Cesium Cs-137": 10950, # ~30 years122 "Cobalt Co-60": 1925, # ~5.3 years123 "Cobalt Co-57": 271.8, # ~9 months124 "Barium Ba-133": 3843, # ~10.5 years125}126# Half-life values (in days)127HALF_LIFE = {128 "Cs-137": 10950, # ~30 years129 "Co-60": 1925, # ~5.3 years130 "Co-57": 271.8, # ~9 months131 "Ba-133": 3843, # ~10.5 years132}133 134# Source purchase dates (YYYY-MM-DD)135SOURCE_PURCHASE = {136 "Cs-137": "2019-09-20",137 "Co-60": "2019-09-20",138 "Co-57": "2022-04-21",139 "Ba-133": "2019-09-20",140}141 142# Initial activities (in MBq) at the time of purchase (Example Values)143INITIAL_ACTIVITY = {144 "Cs-137": 370, # Example values, replace with actual145 "Co-60": 1000,146 "Co-57": 740,147 "Ba-133": 500,148}149 150 151# Tool 1: Unit Conversion152@tool153def unit_conversion_tool(value, from_unit, to_unit):154 """155 Converts radiation measurement units.156 """157 value = float(value)158 conversion_factors = {159 "Gy_to_rad": 100, "rad_to_Gy": 0.01,160 "Sv_to_rem": 100, "rem_to_Sv": 0.01,161 "Bq_to_Ci": 2.7e-11, "Ci_to_Bq": 3.7e10,162 "C/kg_to_R": 2.58e-4, "R_to_C/kg": 0.387e4,163 }164 key = f"{from_unit}_to_{to_unit}"165 if key in conversion_factors:166 result = value * conversion_factors[key]167 return f"**Conversion:** {value} {from_unit} β {to_unit}\n**Result:** {result:.4f} {to_unit}"168 else:169 return "β οΈ Invalid conversion request."170 171# Tool 2: Radioactive Decay Calculation172@tool173# Function to Calculate Radioactive Decay174def radioactive_decay_tool(nuclide, initial_activity, elapsed_days):175 """176 Computes remaining radioactive activity based on the nuclide, initial activity, and elapsed time.177 Generates a Medical Physics (MP) report in Google Doc style.178 Args:179 nuclide (str): Name of the radioactive nuclide.180 initial_activity (float): Initial activity in Bq or Ci or mCi.181 elapsed_days (float): Time elapsed in days .182 Returns:183 str: A formatted Google Doc-style report with computed decay results.184 """185 try:186 initial_activity = float(initial_activity)187 elapsed_days = float(elapsed_days)188 except (TypeError, ValueError):189 return "β οΈ Invalid decay input. Activity and elapsed time must be numeric."190 191 if initial_activity <= 0 or elapsed_days < 0:192 return "β οΈ Initial activity must be positive and elapsed time cannot be negative."193 194 initial_activity = float(initial_activity) 195 elapsed_days = int(elapsed_days) 196 197 # Now comparisons will work correctly198 if initial_activity <= 0 or elapsed_days < 0:199 return "Error: Initial activity must be positive, and elapsed days cannot be negative."200 # Retrieve half-life from the dictionary201 half_life = RADIOACTIVE_NUCLIDES.get(nuclide, None)202 H_L=HALF_LIFE.get(nuclide,None)203 204 #if half_life is None:205 if H_L is None:206 return f"β οΈ Error: Nuclide '{nuclide}' not found in database. Please check spelling or add it."207 208 # Compute decay constant209 #decay_constant = math.log(2) / half_life210 decay_constant = math.log(2) / H_L211 212 213 # Compute remaining activity214 remaining_activity = initial_activity * math.exp(-decay_constant * elapsed_days)215 216 # Generate Google Doc-style Report217 current_date = datetime.now().strftime("%Y-%m-%d")218 219 report = f"""220# π **Medical Physics Report**221### π
Date: {current_date}222---223## **πΉ Radioactive Decay Calculation**224π¬ **Radionuclide:** {nuclide} 225π **Initial Activity:** {initial_activity} Bq or Ci or mCi226β³ 227 **HalfLife:**{H_L}228π°οΈ **Elapsed Time:** {elapsed_days} days 229βοΈ **Remaining Activity:** {remaining_activity:.4f} Bq or Ci or mCi230---231π **End of Report**232"""233 #**Half-life:** {half_life:.2f} days234#return report235 # Tool 3: print list of exposure limits236@tool237def print_exposure_limits(query: str = "all") -> str:238 """239 Returns a SNIF Google Docβstyle report of exposure/dose limits for workers, public, or caregivers.240 Args:241 query (str): A string indicating which limits to display. Acceptable values:"workers", "public", "caregivers","comforter" or "all" (default prints all limits).242 Returns:243 str: A formatted report with the requested dose limits.244 """245 query_lower = query.lower().strip()246 247 # Filter keys based on the query248 if query_lower == "workers":249 # Include keys related to workers (e.g., any key with "worker")250 keys = [k for k in OCCUPATIONAL_LIMITS if "worker" in k.lower()]251 elif query_lower == "public":252 keys = [k for k in OCCUPATIONAL_LIMITS if "public" in k.lower()]253 elif query_lower == "caregivers":254 keys = [k for k in OCCUPATIONAL_LIMITS if "caregiver" in k.lower()]255 elif query_lower == "comforter":256 keys = [k for k in OCCUPATIONAL_LIMITS if "comforter" in k.lower()]257 else:258 # If query is "all" or unrecognized, print all limits.259 keys = list(OCCUPATIONAL_LIMITS.keys())260 261 # Generate a SNIF Google Doc-style report262 current_date = datetime.now().strftime("%Y-%m-%d")263 report = f"""264# **Exposure/Dose Limits Report**265### π
Date: {current_date}266---267"""268 for key in keys:269 report += f"\n- **{key}**: {OCCUPATIONAL_LIMITS[key]} mSv\n"270 271 report += "\n---\nπ **End of Report**"272 return report273# Tool 4:patient release criteria274@tool275 276def patient_release_decision(neck_dose_microSv_hr: float, exposure_duration_hr: float, initial_activity_mCi: float, sef: str) -> str:277 """278 Evaluates whether a patient treated with I-131 can be released based on their neck dose,279 remaining activity, and Socio-Economic Factor (SEF). Generates a structured SNIF Google Doc-style report.280 281 Args:282 neck_dose_microSv_hr (float): Measured neck dose in ΞΌSv/hr.283 exposure_duration_hr (float): Elapsed time in hours since administration.284 initial_activity_mCi (float): Administered activity in mCi.285 sef (str): Socio-Economic Factor, which should be either "good" or "bad".286 287 Returns:288 str: A formatted SNIF Google Doc-style report with the calculated TEDE and release recommendation.289 """290 # Validate input291 if neck_dose_microSv_hr < 0 or exposure_duration_hr < 0 or initial_activity_mCi <= 0:292 return "β οΈ Invalid input! Neck dose, exposure duration, and initial activity must be positive numbers."293 294 sef = sef.lower().strip()295 if sef not in ["good", "bad"]:296 return "β οΈ Invalid SEF! Please provide 'GOOD' or 'BAD' for the socio-economic factor."297 298 # Retrieve Constants299 occupancy_factor = OCCUPATIONAL_LIMITS.get("Occupancy factor (6/24)", 0.25)300 discharge_threshold = OCCUPATIONAL_LIMITS.get("Discharge TEDE Threshold", 0.5)301 pnra_limit = OCCUPATIONAL_LIMITS.get("PNRA release limit(dose)", 50) # Default 50 Β΅Sv302 pnra_limit1= OCCUPATIONAL_LIMITS.get("PNRA release limit(activity)", 30)#default 30 mci303 half_life_days = RADIOACTIVE_NUCLIDES.get("I-131", 8.02) # Half-life in days304 305 # Convert exposure duration to days306 exposure_duration_days = exposure_duration_hr / 24307 308 # Calculate Decay Constant (Ξ») in per day309 decay_constant = math.log(2) / half_life_days # Ξ» = ln(2) / T_half310 311 # Compute Remaining Activity After Decay312 remaining_activity_mCi = initial_activity_mCi * math.exp(-decay_constant * exposure_duration_days)313 314 # Compute TEDE (Dose-Based) from Measured Neck Dose315 tede_dose_rem = (neck_dose_microSv_hr * 1.44 * 24 * half_life_days * occupancy_factor) / 1000 * 0.1 # Convert to rem316 317 # Compute TEDE (Activity-Based) from Remaining I-131 Activity318 tede_activity_rem = (remaining_activity_mCi * 1.44 * 24 * 2.2 * half_life_days * occupancy_factor) / 1000 * 0.1 # Convert to rem319 320 # Debugging Print Statements321 print(f"Remaining Activity (mCi): {remaining_activity_mCi:.3f}")322 print(f"TEDE from Dose Measurement (rem): {tede_dose_rem:.3f}")323 print(f"TEDE from Activity Calculation (rem): {tede_activity_rem:.3f}")324 print(f"PNRA release Limit: on Measured Dose{pnra_limit} Β΅Sv")325 print(f"PNRA release Limit: on Residual Activity{pnra_limit1} mCi") 326 327 # **Decision-Making Based on NRC Guidelines**328 if tede_dose_rem < discharge_threshold and tede_activity_rem < discharge_threshold:329 recommendation = "β
Immediate release recommended."330 elif tede_dose_rem < discharge_threshold and tede_activity_rem > discharge_threshold:331 if sef == "good":332 recommendation = "β οΈ Discharge allowed with strict home isolation guidelines."333 else:334 recommendation = "β Hospital stay recommended until TEDE (activity) < 0.5 rem."335 else:336 recommendation = "β Patient should remain hospitalized until both TEDE values are below 0.5 rem."337 338 # Generate SNIF Google Doc-style Report339 current_date = datetime.now().strftime("%Y-%m-%d")340 report = f"""341# π **Medical Physics Patient Release Report**342### π
Date: {current_date}343---344## **πΉ Patient Exposure Assessment**345- **Neck Dose Rate:** {neck_dose_microSv_hr:.2f} ΞΌSv/hr 346- **Total Effective Dose Equivalent (TEDE from Measured Dose):** {tede_dose_rem:.3f} rem347- **Total Effective Dose Equivalent (TEDE from Activity Calculation):** {tede_activity_rem:.3f} rem348- **PNRA Release Limit:** on Measured dose {pnra_limit} Β΅Sv and on Residual Activity {pnra_limit1}mCi349- **Remaining Activity After {exposure_duration_days:.2f} Days:** {remaining_activity_mCi:.3f} mCi350## **πΈ Definitions**351- **TEDE (Activity-Based):** Estimated radiation exposure based on the remaining I-131 activity in the body which contribute to exposure to public.352- **TEDE (Dose-Based):** Radiation exposure measured from the patient's emitted dose using a survey meter.353## **πΉ Discharge Recommendation**354- **Threshold for Release:** {discharge_threshold} rem355- **Occupancy Factor:** {occupancy_factor}356- **Half-Life (I-131):** {half_life_days} days357- **Decision:** {recommendation}358---359π **End of Report**360"""361 return report362 363# β
Example Function Call364#print(patient_release_decision(12.5, 48, 30, "good")) # Example with 30 mCi initial dose, 48 hours exposure365 366# Tool-5:AI Agent to Call Tools367@tool368def medical_physics_agent(query):369 """370 An AI agent that takes user queries and calls the appropriate MP tool.371 """372 current_date = datetime.now().strftime("%Y-%m-%d")373 374 # Identify Query Type375 if "convert" in query:376 value = float(query.split()[1]) # Extract first number377 from_unit, to_unit = query.split()[2], query.split()[4] # Extract units378 result = unit_conversion_tool(value, from_unit, to_unit)379 title = "Unit Conversion"380 381 elif "decay" in query:382 values = [float(i) for i in query.split() if i.replace('.', '', 1).isdigit()]383 if len(values) == 3:384 result = radioactive_decay_tool(values[0], values[1], values[2])385 title = "Radioactive Decay Calculation"386 else:387 return "β οΈ Invalid input format for decay calculation."388 389 elif "dose" in query:390 dose = float(query.split()[1])391 result = radiation_protection_advice(dose)392 title = "Occupational Dose Assessment"393 elif "predicted yield" in query:394 activity = float(query.split()[1])395 result = predict_tc99m_yield(datetime,activity)396 title = "Predicted Yield of Tc-99m" 397 398 elif "nm_test_protocol" in query:399 nm_test = float(query.split()[1])400 result = nm_test_protocol(nmTest,weight,height)401 title = "nm_test_protocol" 402 elif "QC Tests DOSE CALIBRATOR" in query:403 # Example Usage404 405 QC_test = float(query.split()[1])406 result = get_qc_test_details (QC_test)407 title = "QC_test_protocol" 408 elif "QC Tests GAMMA CAMERA" in query:409 # Example Usage410 411 QC_test = float(query.split()[1])412 result = get_gammaqc_test_details (QC_test)413 title = "QC_test_protocol" 414 else:415 return "β οΈ Sorry, I didn't understand your request."416 # Format Response in Google Doc SNIF Style417 report = f"""418# **π Medical Physics (MP) Report**419### **π
Date:** {current_date}420---421## **{title}**422{result}423---424**End of Report**425"""426 return report427 428 429#Tool-6 RP-Advice430@tool431def radiation_protection_advice(daily_exposure_microSv: Union[float, int, str]) -> str:432 """433 Provides radiation protection advice based on daily occupational434 radiation exposure in microSv.435 """436 437 try:438 # Convert LLM-provided string/number to float439 daily_exposure_microSv = float(daily_exposure_microSv)440 441 except (ValueError, TypeError):442 return (443 "β Invalid exposure value. "444 "Please provide the daily exposure as a numeric value in Β΅Sv."445 )446 447 # Convert Β΅Sv to mSv448 daily_exposure_mSv = daily_exposure_microSv / 1000449 450 # Annualized estimate451 annual_exposure_mSv = daily_exposure_mSv * 365452 453 # Radiation protection assessment454 if daily_exposure_mSv < 0.08:455 assessment = "LOW"456 advice = (457 "Daily exposure is relatively low. Continue routine radiation "458 "protection practices, ALARA principles and personnel monitoring."459 )460 461 elif daily_exposure_mSv < 0.17:462 assessment = "MODERATE"463 advice = (464 "Exposure is moderate. Review work practices, distance, shielding "465 "and time spent near radiation sources. Continue dosimetry monitoring."466 )467 468 else:469 assessment = "HIGH"470 advice = (471 "Exposure is relatively high. Review the radiation work practice "472 "immediately, investigate the source of exposure and consult the "473 "Radiation Protection Officer/Medical Physicist."474 )475 476 return f"""477### β’οΈ Radiation Protection Assessment478 479**Daily Exposure:** {daily_exposure_microSv:.2f} Β΅Sv 480**Daily Exposure:** {daily_exposure_mSv:.4f} mSv 481**Estimated Annual Exposure:** {annual_exposure_mSv:.2f} mSv 482 483**Assessment:** **{assessment}**484 485**Advice:** 486{advice}487 488**Important:** This is a screening assessment. Personnel dose records,489dosimeter results, work patterns and applicable PNRA/institutional490requirements should be reviewed for formal radiation-protection decisions.491"""492# Tool7-Predicted Yield Tc-99m493@tool494def predict_tc99m_yield(arrival_date: str, initial_activity: float) -> str:495 """496 Predicts the daily yield of a Tc-99m generator for the next 7 days.497 Parameters:498 arrival_date (str): Date when the generator arrives (format: "DD-MM-YYYY").499 initial_activity (float): Initial activity of Mo-99 in mCi.500 Returns:501 dict: Dictionary with dates as keys and predicted yields as values.502 """503 received_date = parse_date(arrival_date)504 try:505 initial_activity = float(initial_activity)506 except (TypeError, ValueError):507 return "β Generator activity must be a numeric value in mCi."508 # Constants509 #half_life_Mo99 = 66 # Mo-99 half-life in hours510 half_life_Mo99=RADIOACTIVE_NUCLIDES["Molybdenum (MO-99)"]*24511 #print(f"half life ofMo-99{half_life_Mo99}")512 decay_constant = math.log(2) / half_life_Mo99 # Decay constant513 extraction_efficiency = 0.87 # Typical Tc-99m extraction efficiency514 515 # Convert string date to datetime object516 arrival_datetime = datetime.strptime(arrival_date, "%d-%m-%Y")517 518 # Predict yield for the next 7 days519 predicted_yields = {}520 521 for day in range(1, 8): # Next 7 days522 current_date = arrival_datetime + timedelta(days=day)523 time_elapsed = day * 24 # Convert days to hours524 remaining_activity = initial_activity * math.exp(-decay_constant * time_elapsed)525 tc99m_yield = remaining_activity * extraction_efficiency # Apply efficiency526 predicted_yields[current_date.strftime("%d-%m-%Y")] = round(tc99m_yield, 2)527 528 return predicted_yields529###TOOL8NM-SCAN PROTOCOL as per SNMMI530@tool531def nm_test_protocol(test_name, weight=None, height=None, age=None):532 """533 Function to return the nuclear medicine test protocol based on user query.534 535 Parameters:536 - test_name (str): Name of the NM test537 - weight (float): Patient weight in kg (for pediatric cases)538 - height (float): Patient height in cm (optional, not used for dosing)539 - age (int): Patient age in years (to determine adult vs pediatric dose)540 541 Returns:542 - A formatted string with test details.543 """544 545 # Database of NM test protocols546 nm_tests = {547 "bone scan": {548 "radiopharmaceutical": "99mTc-MDP/HDP",549 "dose_adult": "10β25 mCi (370β925 MBq)",550 "dose_pediatric": lambda w: f"{round(min(0.3 * w, 25), 2)} mCi ({round(min(11.1 * w, 925), 2)} MBq)",551 "imaging_time": "2β4 hrs",552 "preparation": "Hydrate well",553 "max_ped_dose": "25 mCi"554 },555 "mag3 renal scan": {556 "radiopharmaceutical": "99mTc-MAG3",557 "dose_adult": "3β10 mCi (111β370 MBq)",558 "dose_pediatric": lambda w: f"{round(min(0.1 * w, 10), 2)} mCi ({round(min(3.7 * w, 370), 2)} MBq)",559 "imaging_time": "Immediate",560 "preparation": "Hydrate well",561 "max_ped_dose": "10 mCi"562 },563 "dtpa renal scan": {564 "radiopharmaceutical": "99mTc-DTPA",565 "dose_adult": "3β10 mCi (111β370 MBq)",566 "dose_pediatric": lambda w: f"{round(min(0.2 * w, 10), 2)} mCi ({round(min(7.4 * w, 370), 2)} MBq)",567 "imaging_time": "Immediate",568 "preparation": "Hydrate well",569 "max_ped_dose": "10 mCi"570 },571 "dmsa renal scan": {572 "radiopharmaceutical": "99mTc-DMSA",573 "dose_adult": "1β5 mCi (37β185 MBq)",574 "dose_pediatric": lambda w: f"{round(min(0.3 * w, 5), 2)} mCi ({round(min(11.1 * w, 185), 2)} MBq)",575 "imaging_time": "2β4 hrs",576 "preparation": "None",577 "max_ped_dose": "5 mCi"578 },579 "hida scan": {580 "radiopharmaceutical": "99mTc-DISIDA/MeBrofenin",581 "dose_adult": "3β8 mCi (111β296 MBq)",582 "dose_pediatric": lambda w: f"{round(min(0.1 * w, 8), 2)} mCi ({round(min(3.7 * w, 296), 2)} MBq)",583 "imaging_time": "Immediate",584 "preparation": "NPO 4β6 hrs",585 "max_ped_dose": "8 mCi"586 },587 "cardiac MIBI scan": {588 "radiopharmaceutical": "99mTc-MIBI/Tetrofosmin",589 "dose_adult": "8β36 mCi (296β1332 MBq)",590 "dose_pediatric": lambda w: f"{round(min(0.3 * w, 36), 2)} mCi ({round(min(11.1 * w, 1332), 2)} MBq)",591 "imaging_time": "15β60 min",592 "preparation": "NPO 4β6 hrs, Avoid caffeine",593 "max_ped_dose": "36 mCi"594 }595 }596 597 # Normalize the test name to lowercase for matching598 test_name = test_name.lower()599 600 # Check if the test exists601 if test_name not in nm_tests:602 return "β Test not found. Please enter a valid NM test name."603 604 # Get test details605 test_details = nm_tests[test_name]606 607 # Determine dose based on age608 if age is None or age >= 18:609 dose_info = f"**Dose (Adult):** {test_details['dose_adult']}"610 elif weight is not None:611 dose_info = f"**Dose (Pediatric - Weight {weight} kg):** {test_details['dose_pediatric'](weight)} (Max: {test_details['max_ped_dose']})"612 else:613 dose_info = "**Pediatric dose requires weight input.**"614 615 # Format output616 result = f"""617 π¬**Nuclear Medicine Test Protocol at AEMCK: {test_name.title()}** 618 ______As per SNMMI619 - **Radiopharmaceutical:** {test_details['radiopharmaceutical']}620 {dose_info}621 - **Imaging Time:** {test_details['imaging_time']}622 - **Patient Preparation:** {test_details['preparation']}623 """624 625 return result.strip()626 627# Example Usage628#print(nm_test_protocol("Bone Scan", weight=15, age=5))629#print(nm_test_protocol("Cardiac MIBI Scan", age=25))630#print(nm_test_protocol("HIDA Scan", weight=30, age=10))631 632 633#Curent activity Tool9634@tool635def calculate_current_activity(source_name):636 """637 Calculates the current activity of a radioactive source based on the decay formula.638 Args:639 source_name (str): Name of the isotope (e.g., "Cs-137", "Co-60").640 Returns:641 float: Current activity in MBq.642 """643 if source_name not in HALF_LIFE or source_name not in SOURCE_PURCHASE:644 return "Unknown Source"645 646 # Get today's date and calculate decay647 today = datetime.today()648 purchase_date = datetime.strptime(SOURCE_PURCHASE[source_name], "%Y-%m-%d")649 days_elapsed = (today - purchase_date).days650 651 # Decay formula: A = A0 * (1/2)^(t/T)652 A0 = INITIAL_ACTIVITY[source_name]653 T = HALF_LIFE[source_name]654 A = A0 * math.pow(0.5, days_elapsed / T)655 656 return round(A, 2) # Return current activity in MBq657 658 659#For QC-TESTTOOL#10660 661@tool662def get_qc_test_details(test_name: str) -> str:663 """664 Retrieves Dose Calibrator CRC-25R QC test details including665 frequency, acceptance criteria, instructions and good practices.666 667 The tool accepts flexible test names such as:668 Accuracy669 Accuracy QC670 Dose Calibrator Accuracy671 Constancy Test672 Zero Adjustment673 Background674 Contamination675 Linearity676 Geometry677 Chamber Voltage678 Data Check679 Introduction680 """681 682 # ---------------------------------------------------------683 # 1. Validate input684 # ---------------------------------------------------------685 686 if test_name is None:687 return "β Please specify the Dose Calibrator QC test."688 689 test_name = str(test_name).strip()690 691 if not test_name:692 return "β Please specify the Dose Calibrator QC test."693 694 # ---------------------------------------------------------695 # 2. Normalize test name696 # ---------------------------------------------------------697 698 normalized = (699 test_name700 .lower()701 .replace("-", " ")702 .replace("_", " ")703 .replace(",", " ")704 .replace(".", " ")705 )706 707 normalized = " ".join(normalized.split())708 709 # ---------------------------------------------------------710 # 3. Identify requested QC test711 # ---------------------------------------------------------712 713 if "introduction" in normalized:714 selected_test = "Introduction"715 716 elif "chamber voltage" in normalized or "voltage" in normalized:717 selected_test = "Chamber Voltage"718 719 elif "accuracy" in normalized:720 selected_test = "Accuracy"721 722 elif "constancy" in normalized:723 selected_test = "Constancy Test"724 725 elif "zero adjustment" in normalized or "zero" in normalized:726 selected_test = "Zero Adjustment"727 728 elif "background" in normalized:729 selected_test = "Background"730 731 elif "data check" in normalized or "data" in normalized:732 selected_test = "Data Check"733 734 elif "contamination" in normalized or "wipe" in normalized:735 selected_test = "Contamination"736 737 elif "linearity" in normalized:738 selected_test = "Linearity"739 740 elif "geometry" in normalized:741 selected_test = "Geometry"742 743 else:744 return f"""745### β QC Test Not Found746 747I could not identify **"{test_name}"** as a Dose Calibrator CRC-25R748QC test.749 750### Available QC Tests751 752- Chamber Voltage753- Accuracy754- Constancy Test755- Zero Adjustment756- Background757- Data Check758- Contamination759- Linearity760- Geometry761- Introduction762 763Please specify one of the above tests.764"""765 766 # ---------------------------------------------------------767 # 4. QC DATABASE768 # ---------------------------------------------------------769 770 qc_tests = {771 772 "Introduction": {773 "title": "Dose Calibrator CRC-25R β QC Program",774 "description": [775 "AEMCK (Atomic Energy Medical Centre Karachi) has one CRC-25R Dose Calibrator.",776 "The centre also has three Gamma Cameras used for diagnostic and therapeutic Nuclear Medicine applications.",777 "Dose Calibrator QC should be performed according to the approved institutional QC program, applicable regulatory requirements and manufacturer's recommendations."778 ],779 "good_practices": [780 "Maintain documented QC schedules and records.",781 "Use appropriate calibrated and traceable reference sources.",782 "Perform QC under consistent environmental and operating conditions.",783 "Investigate abnormal trends rather than relying only on individual pass/fail results.",784 "Ensure corrective actions are documented.",785 "Keep QC records available for audit and regulatory inspection."786 ]787 },788 789 "Chamber Voltage": {790 "Frequency": "Daily",791 "Acceptance Value": "Β±1%",792 "Instructions": [793 "Turn on the dose calibrator and allow it to warm up according to the manufacturer's instructions.",794 "Check the chamber voltage reading on the display.",795 "Compare the observed value with the approved reference/nominal value.",796 "If the deviation exceeds the approved acceptance criterion, investigate the cause and take corrective action."797 ],798 "Good Practices": [799 "Ensure a stable power supply.",800 "Perform the measurement under consistent operating conditions.",801 "Record the result in the QC log.",802 "Investigate abnormal trends promptly."803 ]804 },805 806 "Accuracy": {807 "Frequency": "Quarterly",808 "Acceptance Value": "Β±5%",809 "Instructions": [810 "Use an appropriate certified or traceable reference source.",811 "Select the correct radionuclide setting on the dose calibrator.",812 "Allow the dose calibrator to stabilize before measurement.",813 "Place the reference source in the appropriate and reproducible position.",814 "Record the measured activity.",815 "Calculate the percentage deviation from the expected/reference activity.",816 "Compare the result with the approved acceptance criterion.",817 "If the result is outside the acceptance criterion, investigate the cause and perform corrective action according to the approved procedure."818 ],819 "Good Practices": [820 "Use calibrated and traceable reference sources.",821 "Use appropriate radionuclides covering the clinical measurement range where required.",822 "Record source identification, reference activity, calibration date and measurement date.",823 "Use consistent source positioning and geometry.",824 "Perform measurements under stable environmental conditions.",825 "Trend results over time to identify gradual deterioration.",826 "Do not rely only on a single measurement when investigating an abnormal result.",827 "Document all corrective actions and repeat measurements when required."828 ]829 },830 831 "Constancy Test": {832 "Frequency": "Daily",833 "Acceptance Value": "Β±5% of reference value",834 "Instructions": [835 "Use the designated reference source for constancy testing.",836 "Verify the source identification and reference activity.",837 "Place the source in a reproducible position in the dose calibrator.",838 "Measure the activity.",839 "Compare the result with the established reference value.",840 "Record the result and investigate deviations outside the approved acceptance criterion."841 ],842 "Good Practices": [843 "Perform the test under consistent environmental conditions.",844 "Use the same source, geometry and positioning whenever possible.",845 "Allow the dose calibrator to warm up before testing.",846 "Maintain a historical QC trend.",847 "Investigate unexpected changes for possible contamination, source positioning errors, electronic drift or equipment malfunction."848 ]849 },850 851 "Zero Adjustment": {852 "Frequency": "Daily",853 "Acceptance Value": "Β±0.3 mV",854 "Instructions": [855 "Ensure that no radioactive source is present in the measurement chamber.",856 "Check the zero indication according to the manufacturer's procedure.",857 "Perform zero adjustment only according to the approved equipment procedure."858 ],859 "Good Practices": [860 "Ensure no external radiation source is influencing the measurement.",861 "Perform the check before routine measurements.",862 "Keep the chamber clean and free from contamination.",863 "Document abnormal zero readings."864 ]865 },866 867 "Background": {868 "Frequency": "Daily",869 "Acceptance Value": "Β±20% of current mean (<530 ΞΌCi)",870 "Instructions": [871 "Ensure that no radioactive source is inside or immediately adjacent to the chamber.",872 "Measure the background according to the approved procedure.",873 "Compare the result with the established baseline/current mean.",874 "Investigate significant changes in background."875 ],876 "Good Practices": [877 "Check for radioactive contamination in and around the measurement chamber.",878 "Ensure no radioactive source is nearby during the measurement.",879 "Maintain a historical background trend.",880 "Investigate sudden increases before using the calibrator for patient doses."881 ]882 },883 884 "Data Check": {885 "Frequency": "Daily",886 "Acceptance Value": "Correct and consistent readings",887 "Instructions": [888 "Verify that the correct radionuclide setting is selected.",889 "Check displayed activity and measurement units.",890 "Compare readings with the expected/reference value where applicable.",891 "Verify correct date/time and QC documentation."892 ],893 "Good Practices": [894 "Verify radionuclide selection before every measurement.",895 "Check units carefully to avoid mCi/MBq/ΞΌCi conversion errors.",896 "Maintain accurate QC documentation.",897 "Report discrepancies immediately."898 ]899 },900 901 "Contamination": {902 "Frequency": "Weekly",903 "Acceptance Value": "<3 ΞΌCi (0.12 MBq)",904 "Instructions": [905 "Perform contamination monitoring using the approved wipe-test procedure.",906 "Check accessible surfaces of the dose calibrator and measurement area.",907 "Measure the wipe sample using the appropriate radiation detection system.",908 "Compare the result with the approved contamination limit.",909 "Decontaminate and investigate if contamination is detected above the approved criterion."910 ],911 "Good Practices": [912 "Perform routine wipe testing according to the institutional radiation-safety program.",913 "Keep the measurement chamber and surrounding area clean.",914 "Prevent radioactive material from entering the chamber.",915 "Document contamination results and corrective actions."916 ]917 },918 919 "Linearity": {920 "Frequency": "Quarterly",921 "Acceptance Value": "Within 5% of expected value",922 "Instructions": [923 "Use the approved linearity testing method.",924 "Measure a suitable range of activities.",925 "Record measured activity at each level/time point.",926 "Compare measured values with expected values.",927 "Evaluate the deviation across the measurement range.",928 "Investigate and correct unacceptable deviations."929 ],930 "Good Practices": [931 "Cover the clinically relevant activity range.",932 "Use appropriate decay-based or other approved linearity methodology.",933 "Maintain consistent source geometry.",934 "Record all measurements.",935 "Trend linearity results over time."936 ]937 },938 939 "Geometry": {940 "Frequency": "Acceptance Test / After Major Repair / Periodically as specified by the approved QC program",941 "Acceptance Value": "Within Β±5% of expected/reference value",942 "Instructions": [943 "Ensure that the dose calibrator is properly installed and warmed up.",944 "Select the appropriate radionuclide setting.",945 "Prepare samples with different volumes using the same activity concentration.",946 "Use representative syringes and/or vials applicable to clinical practice.",947 "Measure each sample using reproducible positioning.",948 "Compare readings across different volumes and geometries.",949 "Evaluate whether geometry-dependent variation is within the approved acceptance criterion.",950 "Apply correction factors only when authorized by the approved procedure."951 ],952 "Good Practices": [953 "Use a well-mixed radioactive solution.",954 "Maintain consistent sample positioning.",955 "Avoid air bubbles where they could affect geometry.",956 "Use clinically representative syringes and vials.",957 "Document the geometry configuration used.",958 "Retain results as a baseline for future comparison."959 ]960 }961 }962 963 # ---------------------------------------------------------964 # 5. Get selected test965 # ---------------------------------------------------------966 967 test = qc_tests[selected_test]968 969 # ---------------------------------------------------------970 # 6. Introduction971 # ---------------------------------------------------------972 973 if selected_test == "Introduction":974 975 response = f"""976# β’οΈ Dose Calibrator CRC-25R β QC Program977 978### Facility979 980{chr(10).join("- " + item for item in test["description"])}981 982### Recommended Good Practices983 984{chr(10).join(f"{i+1}. {item}" for i, item in enumerate(test["good_practices"]))}985 986> **Note:** QC frequencies and acceptance criteria should be verified987> against the current manufacturer's specifications, applicable988> regulatory requirements and the approved institutional QC procedure.989"""990 991 return response992 993 # ---------------------------------------------------------994 # 7. Format normal QC test995 # ---------------------------------------------------------996 997 response = f"""998# β’οΈ Dose Calibrator CRC-25R β {selected_test}999 1000### QC Parameters1001 1002**Frequency:** {test.get("Frequency", "Not specified")}1003 1004**Acceptance Value:** {test.get("Acceptance Value", "Not specified")}1005 1006### Instructions1007 1008"""1009 1010 for i, instruction in enumerate(test.get("Instructions", []), 1):1011 response += f"{i}. {instruction}\n"1012 1013 response += "\n### Good Practices\n\n"1014 1015 for i, practice in enumerate(test.get("Good Practices", []), 1):1016 response += f"{i}. {practice}\n"1017 1018 # ---------------------------------------------------------1019 # 8. Add source information only when Accuracy is requested1020 # ---------------------------------------------------------1021 1022 if selected_test == "Accuracy":1023 1024 response += """1025 1026### Reference Sources1027 1028The following reference sources may be used where available and1029appropriate to the approved QC procedure:1030 1031- Cs-1371032- Co-601033- Co-571034- Ba-1331035 1036**Important:** Reference-source activity should be decay-corrected to1037the measurement date using the source certificate/reference date.1038Source identity, reference activity, calibration date and traceability1039should be documented.1040 1041### QC Documentation1042 1043Record at minimum:1044 1045- Date and time1046- Operator1047- Dose calibrator identification1048- Radionuclide1049- Reference source identification1050- Reference activity1051- Measurement result1052- Percentage deviation1053- Acceptance criterion1054- Pass/Fail status1055- Corrective action, if required1056"""1057 1058 response += """1059 1060---1061 1062**Medical Physics Note:** Acceptance criteria and QC frequency should1063be confirmed against the current CRC-25R manufacturer's documentation,1064applicable regulatory requirements and the approved institutional1065Medical Physics/QC procedure before being used for formal compliance1066decisions.1067"""1068 1069 return response1070@tool1071def get_gammaqc_test_details(test_name):1072 """1073 Retrieves QC test details of Gamma Camera, including Peak positioning, Energy Resolution etc.1074 1075 Args:1076 test_name (str): The name of the QC test (e.g, "Peak Positioning", "Energy Resolution,").1077 Returns:1078 dict: A dictionary containing test details or an error message if the test is invalid.1079 Example Usage:1080 >>> get_qc_test_details("Peak positioning")1081 """1082 1083 qc_tests = {1084 "Introduction":["AEMCK (Atomic Energy Medical Centre Karachi) has three Gamma Cameras for Diagnosis and Therapies.",1085 "Daily QC includes Peak Position , Energy Resolution, Background Test, and Image Quality Test.",1086 "Monthly QC includes Centre of Rotation (COR) and Intrinsic Uniformity Test."],1087 1088 "Peak Position": {1089 "Frequency": "Daily",1090 "Acceptance Value": "Tc-99m: 140 Β± 3 keV, Co-57: 122 Β± 3 keV",1091 "Instructions": [1092 "Ensure proper calibration of the Gamma camera.",1093 "Place the Tc-99m or Co-57 source appropriately.",1094 "Verify that the peak position falls within the acceptable range."1095 ],1096 "Good Practices": [1097 "Always use a well-calibrated source for Gamma cam.",1098 "Check for any sudden peak shifts indicating detector issues."1099 ]1100 },1101 "Energy Resolution": {1102 "Frequency": "Daily",1103 "Acceptance Value": "Tc-99m: < 11.0%, Co-57: < 12.0%",1104 "Instructions": [1105 "Acquire a spectrum using the Gamma camera.",1106 "Calculate the full-width at half maximum (FWHM).",1107 "Ensure that the energy resolution falls within the acceptance criteria."1108 ],1109 "Good Practices": [1110 "Use a high-quality energy calibration source.",1111 "Avoid fluctuations in environmental conditions."1112 ]1113 },1114 "Intrinsic Uniformity": {1115 "Frequency": "Monthly",1116 "Acceptance Value": "CFOV Integral Uniformity < 5%",1117 "Instructions": [1118 "Perform an intrinsic uniformity scan using Tc-99m for Gamma Cam",1119 "Analyze the image uniformity using processing software.",1120 "Ensure that uniformity deviations remain within the threshold."1121 ],1122 "Good Practices": [1123 "Use a uniform flood source.",1124 "Regularly check for detector malfunctions."1125 ]1126 },1127 "Centre of Rotation (COR)": {1128 "Frequency": "Monthly",1129 "Instructions": [1130 "Use a point source of Tc-99m at the center of rotation for Gamma cam.",1131 "Acquire multiple views and analyze COR deviation.",1132 "Ensure that misalignment does not exceed acceptable limits."1133 ],1134 "Good Practices": [1135 "Perform COR analysis after maintenance or detector adjustments.",1136 "Verify consistency over multiple acquisitions."1137 ]1138 } 1139 1140 }1141 1142 #return qc_tests.get(test_name, {"Error": "Invalid QC Test Name"})1143 return qc_tests.get(test_name, {"Error": "Invalid test name. Please choose a valid QC test."})1144 1145# Example Usage1146#if __name__ == "__main__":1147# test_name = input("Enter the QC test name: ")1148# result = get_qc_test_details(test_name)1149# # print(result)1150 1151 1152 1153def random_prompt():1154 return random.choice([1155 "Convert 1Gy into rem.",1156 "Tell me the half life of Iodine I-131.",1157 "A patient with 50mci administered on 27-2-2025 after 1 day he has neckdose 25USv with good SEF, can I release patient?.",1158 "List me all radiation dose limits for workers.",1159 "Tell me the radiation dose limits for General Public as per PNRA.",1160 "What is the predicted yield for next seven days of 600mci of Tc-99m generator which received on 10-2-2025.",1161 "A cobalt-57 (Co-57) source of 10mCi,what will be the remaining activity after 100days?",1162 "If a worker got 2mSv daily in working hours, what will be the advice of RPO in ALARA context.",1163 "What is the dose protocol of HIDA scan for Pedriatic patient of 7kg weight and 3 years age.",1164 "Tell me the good practices of Accuracy Qc test of Dose Callibrator.",1165 "Geometry Qc test for Dose Callibrator instructions",1166 1167 1168 1169 ])1170tools=[get_gammaqc_test_details,get_qc_test_details,calculate_current_activity,nm_test_protocol,radiation_protection_advice,predict_tc99m_yield,print_exposure_limits,patient_release_decision,unit_conversion_tool,radioactive_decay_tool]1171 1172#agent=initialize_agent(tools,llm,agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION)1173#************************************Change2***************************************1174agent = create_agent(1175 model=llm,1176 tools=tools,1177 system_prompt="""1178You are MEDPHY-NEXUS, an AI Medical Physics and Radiation Protection assistant.1179 1180You assist qualified Medical Physicists and Radiation Protection Officers1181with Medical Physics, Nuclear Medicine, radiation protection, radioactive1182materials, dosimetry, QC, radiation surveys and related calculations.1183 1184Use the available tools whenever a calculation, lookup or technical1185assessment requires them.1186 1187Do not invent regulatory limits or clinical values. Clearly identify1188assumptions and recommend verification against applicable PNRA regulations,1189institutional SOPs and approved protocols where appropriate.1190"""1191)1192 1193#response=agent.invoke({"input":" Neck dose of iodine treated patient is 20 microSv he is staying 24 hrs and his sef is BAD. "})1194#print(f"\n{response}\n")1195# Define the function that handles user input1196#******************Change3********************************1197def process_input(user_input):1198 try:1199 response = agent.invoke({1200 "messages": [