diff --git a/schemas/opnoteschema/src/opnoteschema/examples/example.json b/schemas/opnoteschema/src/opnoteschema/examples/example.json
index 9e26dfe..fa4e33e 100644
--- a/schemas/opnoteschema/src/opnoteschema/examples/example.json
+++ b/schemas/opnoteschema/src/opnoteschema/examples/example.json
@@ -1 +1,76 @@
-{}
\ No newline at end of file
+{
+ "content": "OPERATION NOTE\n\nPatient's Name: [redacted]\nDate of birth: [redacted]\nGender: female\nHospital number: [redacted]\nNHS number: [redacted]\n\nDate of Surgery: [redacted]\n\nPost-op Diagnosis: Displaced intracapsular fracture neck of femur, right\n\nProcedure(s): Right Hip Hemiarthroplasty; Cerclage wiring of intraoperative periprosthetic femoral fracture - Right\n\nPatient Class: Inpatient (trauma list)\n\nConsultant on Case: [redacted]\n\nSurgeons:\n* [redacted] - Consultant\n* [redacted] - Primary Operator (Registrar)\n* [redacted] - Assisting\n\nAnaesthetist: [redacted]\nResponsible Anaesthetist: [redacted]\nAnaesthetic Type: spinal, peripheral nerve block\nASA Grade: III\n\nIndication: 82 year old admitted after a fall at home with a displaced intracapsular fracture of the right neck of femur, confirmed on plain film and CT. Booked on the trauma list for hemiarthroplasty.\n\nDescription of Procedure\n\nWHO checklist completed. IV teicoplanin and gentamicin given at induction. Lateral position with front and back bolsters. Skin prepped with betadine and chlorhexidine, iodine-impregnated incise drape.\n\nAnterolateral approach to proximal right femur. Dissection through fat and fascia lata. Gluteus medius and minimus incised from the greater trochanter. T-shaped capsulotomy. Femoral neck cut 1cm above the lesser trochanter. Femoral head removed, measured 44mm.\n\nFemur prepared with box chisel, pencil reamers and raspers sequential to size 2. During broaching of the femoral canal a longitudinal crack was noted extending from the calcar down the medial cortex of the proximal femoral shaft, approximately 4cm in length, with no displacement. This was considered an intraoperative periprosthetic fracture. Two cerclage wires (18-gauge) were passed around the proximal femur proximal and distal to the crack and tensioned to secure the fracture prior to proceeding with stem insertion. No further propagation was seen on trialling.\n\nCanal prepared with pulsed lavage (1L) and dried with ribbon gauze and suction catheter. Canal cemented. Cemented stem with centraliser inserted, taking care to avoid further distraction of the cerclage-wired fracture. Trialled with standard +4mm head; equal leg lengths and a stable hip on internal and external rotation. 44mm bipolar head +4 inserted. Stable hip reduction confirmed, equal leg lengths, stable on internal and external rotation. Image intensifier check confirmed satisfactory wire position and no further fracture propagation. Saline wash.\n\nEstimated blood loss 450ml, no transfusion required intraoperatively.\n\nClosure\nCapsule - 5 Ethibond\nGluteus medius and minimus - 5 Ethibond inter-osseous followed by continuous suture in tendon\nFascia lata - 1 Vicryl\nFat - 1 Vicryl interrupted\nSubdermal - 2-0 Vicryl\nSkin - 3-0 Monocryl, skin glue, steristrips\n\nTotal operative time 105 minutes.\n\nPost op Plan\nToe-touch weight bearing on the right for 6 weeks given the periprosthetic fracture and cerclage wiring, then progress as tolerated with physiotherapy input.\nRepeat plain film in recovery to confirm implant and wire position.\nFBC, U+E tomorrow.\nNo further antibiotics needed beyond the two post-op doses.\nVTE prophylaxis as per protocol for NOF fractures.\nOrthopaedic follow up at 2 and 6 weeks with weight-bearing progression reviewed at 6 week appointment.\n\nImplant(s):\nImplant Name | Type | Manufacturer | Site | No. Used | Action\nPALACOS R+G 2x40g bone cement | Cement / Putty | Movianto UK Ltd | Right Hip | 1 | Implanted\nStryker Orthopaedics Exeter V40 Cemented Hip Stem, size 2 | Orthopaedic Prosthesis | Stryker UK Ltd | Right Hip | 1 | Implanted\nStryker Orthopaedics UHR Universal Head Bipolar Component, OD 44mm | Orthopaedic Prosthesis | Stryker UK Ltd | Right Hip | 1 | Implanted\nStryker Orthopaedics V40 Femoral Head, OD 28mm, OFFST +4 | Orthopaedic Prosthesis | Stryker UK Ltd | Right Hip | 1 | Implanted\n18-gauge cerclage wire | Fixation wire | DePuy Synthes | Right femur | 2 | Implanted",
+ "output": {
+ "is_operation_note": true,
+ "indication": "Displaced intracapsular fracture of the right neck of femur following a fall at home, confirmed on plain film and CT.",
+ "procedures": [
+ {
+ "procedure_type": "hip_hemiarthroplasty_cemented",
+ "procedure_desc": "Right Hip Hemiarthroplasty via anterolateral approach",
+ "laterality": "right",
+ "implants": [
+ {
+ "implant_desc": "PALACOS R+G 2x40g bone cement",
+ "device_type": "bone cement",
+ "manufacturer": "Movianto UK Ltd",
+ "size_or_specification": "2x40g",
+ "serial_or_lot_number": null
+ },
+ {
+ "implant_desc": "Stryker Orthopaedics Exeter V40 Cemented Hip Stem, size 2",
+ "device_type": "femoral stem",
+ "manufacturer": "Stryker UK Ltd",
+ "size_or_specification": "size 2",
+ "serial_or_lot_number": null
+ },
+ {
+ "implant_desc": "Stryker Orthopaedics UHR Universal Head Bipolar Component, OD 44mm",
+ "device_type": "bipolar head",
+ "manufacturer": "Stryker UK Ltd",
+ "size_or_specification": "OD 44mm",
+ "serial_or_lot_number": null
+ },
+ {
+ "implant_desc": "Stryker Orthopaedics V40 Femoral Head, OD 28mm, OFFST +4",
+ "device_type": "femoral head",
+ "manufacturer": "Stryker UK Ltd",
+ "size_or_specification": "OD 28mm, OFFST +4",
+ "serial_or_lot_number": null
+ }
+ ]
+ },
+ {
+ "procedure_type": "other",
+ "procedure_desc": "Cerclage wiring of intraoperative periprosthetic femoral fracture, right",
+ "laterality": "right",
+ "implants": [
+ {
+ "implant_desc": "18-gauge cerclage wire",
+ "device_type": "fixation wire",
+ "manufacturer": "DePuy Synthes",
+ "size_or_specification": "18-gauge",
+ "serial_or_lot_number": null
+ }
+ ]
+ }
+ ],
+ "findings": "During broaching of the femoral canal a longitudinal crack was noted extending from the calcar down the medial cortex of the proximal femoral shaft, approximately 4cm in length, with no displacement. Image intensifier check after cerclage wiring and stem insertion confirmed satisfactory wire position and no further fracture propagation.",
+ "complications": [
+ {
+ "complication_type": "iatrogenic_fracture",
+ "complication_desc": "Longitudinal crack noted extending from the calcar down the medial cortex of the proximal femoral shaft, approximately 4cm in length, with no displacement, during broaching of the femoral canal - an intraoperative periprosthetic fracture.",
+ "management": "Two 18-gauge cerclage wires passed around the proximal femur proximal and distal to the crack and tensioned to secure the fracture prior to stem insertion; no further propagation seen on trialling or on image intensifier check."
+ }
+ ],
+ "metadata": {
+ "urgency": "urgent",
+ "anaesthetic_type": "regional",
+ "operative_time_minutes": 105,
+ "estimated_blood_loss_ml": 450,
+ "tourniquet_time_minutes": null,
+ "asa_grade": 3,
+ "surgeon_grade": "registrar"
+ },
+ "operation_summary": "Right hip hemiarthroplasty for a displaced intracapsular neck of femur fracture, complicated intraoperatively by a periprosthetic crack of the proximal femoral shaft during canal broaching. This was secured with two cerclage wires before proceeding with cemented stem and bipolar head insertion, with no further fracture propagation on trialling or image intensifier check. Toe-touch weight bearing advised for 6 weeks given the fracture and wiring."
+ }
+}
diff --git a/schemas/opnoteschema/src/opnoteschema/prompt_datagen.txt b/schemas/opnoteschema/src/opnoteschema/prompt_datagen.txt
index adf8e24..0db2e2b 100644
--- a/schemas/opnoteschema/src/opnoteschema/prompt_datagen.txt
+++ b/schemas/opnoteschema/src/opnoteschema/prompt_datagen.txt
@@ -1,2 +1,42 @@
+OPERATION NOTE EXTRACTION
+
+You are an experienced surgeon and medical information expert. Extract operative information from operation notes / operative records into a structured JSON format according to the provided Pydantic schema:
+
{SCHEMA}
-{EXAMPLE}
\ No newline at end of file
+
+OUTPUT STRUCTURE:
+Return ONLY the extracted JSON wrapped in `` tags. The JSON must follow the OperationNote schema structure.
+
+MINIMAL INPUT/OUTPUT EXAMPLE:
+
+{EXAMPLE}
+
+CRITICAL REQUIREMENTS:
+1. Absolute precision is paramount - This is a medicolegal requirement. Only extract correct information that is explicit and unambiguous.
+2. Output format - Wrap the JSON in `` tags with no other text before or after.
+3. Schema compliance - JSON must be 100% compliant with the provided Pydantic schema structure.
+4. Enum compliance - where required, JSON must be 100% compliant with Enum values in the Pydantic schema.
+5. REPLACE ANY PII! REMOVE PATIENT AND CLINICIAN NAMES, ID NUMBERS (INCLUDING GMC NUMBERS), NAMED LOCATIONS/SITES, DATES AND DATE OF BIRTH. IN YOUR OUTPUT, ENSURE THESE ARE REPLACED WITH "[redacted]"
+
+WHAT NOT TO DO:
+1. Do not infer procedures, findings, or complications that are not explicitly documented
+2. Do not assume laterality, urgency, or metadata that is not stated or clearly inferable from context
+3. Do not create information to fill fields - use None/null for absent information and leave list fields empty when nothing applies
+4. Do not guess at operative times, blood loss, or implant details
+5. Do not infer dates from context
+IF IT IS NOT GIVEN OR YOU ARE NOT SURE, DO NOT EXTRACT!
+
+CLASSIFICATION RULE:
+Set is_operation_note = TRUE only if the document is an operative note or operation record describing a surgical/interventional procedure performed on a patient. If FALSE, procedures, complications and operation_summary should be empty/null.
+
+DOMAIN RULES (OPERATION NOTES):
+1. PROCEDURE_TYPE ENUM MATCHING. Match procedure_type to the closest listed OPCS-4-derived enum value based on what was actually performed, not the listed operation title alone (e.g. a note titled "Hip surgery" that describes a cemented total hip replacement should be coded TOTAL_HIP_REPLACEMENT_CEMENTED). Use OTHER with procedure_desc naming the procedure as documented when nothing in the enum reasonably matches.
+2. MULTIPLE PROCEDURES. List every distinct procedure performed in the order documented, including combined/staged procedures in the same operative episode (e.g. "laparoscopic cholecystectomy and appendicectomy" -> two Procedure entries).
+3. IMPLANTS. Only populate implants for the procedure during which they were inserted/used. Do not duplicate an implant across multiple procedure entries.
+4. COMPLICATIONS. complications captures only complications occurring during, or documented in immediate relation to, the procedure itself (intraoperative or immediate postoperative note) - not pre-existing comorbidities or unrelated past medical history. Use OTHER with complication_desc when nothing in the enum reasonably matches.
+5. FINDINGS vs INDICATION. indication is the reason/preoperative diagnosis for the operation. findings is what was observed/discovered intraoperatively. Do not conflate the two.
+6. METADATA. Populate ProcedureMetadata fields only where explicitly stated in the note (e.g. an ASA grade sheet, documented operative time, documented estimated blood loss). Redact surgeon names/identifiers from surgeon_grade, keeping only the grade/seniority (e.g. "consultant", "registrar").
+
+REMEMBER - IF INFORMATION IS NOT GIVEN OR YOU ARE NOT SURE, LEAVE EMPTY AND DO NOT EXTRACT!
+
+The operation note follows below:
diff --git a/schemas/opnoteschema/src/opnoteschema/prompt_main.txt b/schemas/opnoteschema/src/opnoteschema/prompt_main.txt
index cb13a4c..22d41d5 100644
--- a/schemas/opnoteschema/src/opnoteschema/prompt_main.txt
+++ b/schemas/opnoteschema/src/opnoteschema/prompt_main.txt
@@ -1 +1,38 @@
-{SCHEMA}
\ No newline at end of file
+OPERATION NOTE EXTRACTION
+
+You are an experienced surgeon and medical information expert. Extract operative information from operation notes / operative records into a structured JSON format according to the provided Pydantic schema:
+
+{SCHEMA}
+
+OUTPUT STRUCTURE:
+Return ONLY the extracted JSON wrapped in `` tags. The JSON must follow the OperationNote schema structure.
+
+CRITICAL REQUIREMENTS:
+1. Absolute precision is paramount - This is a medicolegal requirement. Only extract correct information that is explicit and unambiguous.
+2. Output format - Wrap the JSON in `` tags with no other text before or after.
+3. Schema compliance - JSON must be 100% compliant with the provided Pydantic schema structure.
+4. Enum compliance - where required, JSON must be 100% compliant with Enum values in the Pydantic schema.
+5. REPLACE ANY PII! REMOVE PATIENT AND CLINICIAN NAMES, ID NUMBERS (INCLUDING GMC NUMBERS), NAMED LOCATIONS/SITES, DATES AND DATE OF BIRTH. IN YOUR OUTPUT, ENSURE THESE ARE REPLACED WITH "[redacted]"
+
+WHAT NOT TO DO:
+1. Do not infer procedures, findings, or complications that are not explicitly documented
+2. Do not assume laterality, urgency, or metadata that is not stated or clearly inferable from context
+3. Do not create information to fill fields - use None/null for absent information and leave list fields empty when nothing applies
+4. Do not guess at operative times, blood loss, or implant details
+5. Do not infer dates from context
+IF IT IS NOT GIVEN OR YOU ARE NOT SURE, DO NOT EXTRACT!
+
+CLASSIFICATION RULE:
+Set is_operation_note = TRUE only if the document is an operative note or operation record describing a surgical/interventional procedure performed on a patient. If FALSE, procedures, complications and operation_summary should be empty/null.
+
+DOMAIN RULES (OPERATION NOTES):
+1. PROCEDURE_TYPE ENUM MATCHING. Match procedure_type to the closest listed OPCS-4-derived enum value based on what was actually performed, not the listed operation title alone (e.g. a note titled "Hip surgery" that describes a cemented total hip replacement should be coded TOTAL_HIP_REPLACEMENT_CEMENTED). Use OTHER with procedure_desc naming the procedure as documented when nothing in the enum reasonably matches.
+2. MULTIPLE PROCEDURES. List every distinct procedure performed in the order documented, including combined/staged procedures in the same operative episode (e.g. "laparoscopic cholecystectomy and appendicectomy" -> two Procedure entries).
+3. IMPLANTS. Only populate implants for the procedure during which they were inserted/used. Do not duplicate an implant across multiple procedure entries.
+4. COMPLICATIONS. complications captures only complications occurring during, or documented in immediate relation to, the procedure itself (intraoperative or immediate postoperative note) - not pre-existing comorbidities or unrelated past medical history. Use OTHER with complication_desc when nothing in the enum reasonably matches.
+5. FINDINGS vs INDICATION. indication is the reason/preoperative diagnosis for the operation. findings is what was observed/discovered intraoperatively. Do not conflate the two.
+6. METADATA. Populate ProcedureMetadata fields only where explicitly stated in the note (e.g. an ASA grade sheet, documented operative time, documented estimated blood loss). Redact surgeon names/identifiers from surgeon_grade, keeping only the grade/seniority (e.g. "consultant", "registrar").
+
+REMEMBER - IF INFORMATION IS NOT GIVEN OR YOU ARE NOT SURE, LEAVE EMPTY AND DO NOT EXTRACT!
+
+The operation note follows below:
diff --git a/schemas/opnoteschema/src/opnoteschema/schema.py b/schemas/opnoteschema/src/opnoteschema/schema.py
index 7305f9f..8737022 100644
--- a/schemas/opnoteschema/src/opnoteschema/schema.py
+++ b/schemas/opnoteschema/src/opnoteschema/schema.py
@@ -24,16 +24,22 @@ class ProcedureType(str, Enum):
EVACUATION_OF_INTRACEREBRAL_HAEMATOMA = "evacuation_of_intracerebral_haematoma"
DECOMPRESSIVE_CRANIECTOMY = "decompressive_craniectomy"
CLIPPING_OF_CEREBRAL_ANEURYSM = "clipping_of_cerebral_aneurysm"
- ENDOVASCULAR_COILING_OF_CEREBRAL_ANEURYSM = "endovascular_coiling_of_cerebral_aneurysm"
+ ENDOVASCULAR_COILING_OF_CEREBRAL_ANEURYSM = (
+ "endovascular_coiling_of_cerebral_aneurysm"
+ )
INSERTION_OF_VENTRICULOPERITONEAL_SHUNT = "insertion_of_ventriculoperitoneal_shunt"
REVISION_OF_VENTRICULOPERITONEAL_SHUNT = "revision_of_ventriculoperitoneal_shunt"
REMOVAL_OF_VENTRICULOPERITONEAL_SHUNT = "removal_of_ventriculoperitoneal_shunt"
EXTERNAL_VENTRICULAR_DRAIN_INSERTION = "external_ventricular_drain_insertion"
- INSERTION_OF_INTRACRANIAL_PRESSURE_MONITOR = "insertion_of_intracranial_pressure_monitor"
+ INSERTION_OF_INTRACRANIAL_PRESSURE_MONITOR = (
+ "insertion_of_intracranial_pressure_monitor"
+ )
ENDOSCOPIC_THIRD_VENTRICULOSTOMY = "endoscopic_third_ventriculostomy"
INSERTION_OF_DEEP_BRAIN_STIMULATOR = "insertion_of_deep_brain_stimulator"
VAGAL_NERVE_STIMULATOR_INSERTION = "vagal_nerve_stimulator_insertion"
- MICROVASCULAR_DECOMPRESSION_OF_CRANIAL_NERVE = "microvascular_decompression_of_cranial_nerve"
+ MICROVASCULAR_DECOMPRESSION_OF_CRANIAL_NERVE = (
+ "microvascular_decompression_of_cranial_nerve"
+ )
TRIGEMINAL_NERVE_RHIZOTOMY = "trigeminal_nerve_rhizotomy"
REPAIR_OF_DURAL_TEAR = "repair_of_dural_tear"
REPAIR_OF_SPINA_BIFIDA = "repair_of_spina_bifida"
@@ -218,7 +224,9 @@ class ProcedureType(str, Enum):
OPEN_INGUINAL_HERNIA_REPAIR = "open_inguinal_hernia_repair"
LAPAROSCOPIC_INGUINAL_HERNIA_REPAIR = "laparoscopic_inguinal_hernia_repair"
TOTALLY_EXTRAPERITONEAL_HERNIA_REPAIR = "totally_extraperitoneal_hernia_repair"
- TRANSABDOMINAL_PREPERITONEAL_HERNIA_REPAIR = "transabdominal_preperitoneal_hernia_repair"
+ TRANSABDOMINAL_PREPERITONEAL_HERNIA_REPAIR = (
+ "transabdominal_preperitoneal_hernia_repair"
+ )
FEMORAL_HERNIA_REPAIR = "femoral_hernia_repair"
UMBILICAL_HERNIA_REPAIR = "umbilical_hernia_repair"
INCISIONAL_HERNIA_REPAIR = "incisional_hernia_repair"
@@ -234,7 +242,9 @@ class ProcedureType(str, Enum):
BILE_DUCT_EXPLORATION = "bile_duct_exploration"
CHOLEDOCHOTOMY = "choledochotomy"
HEPATICOJEJUNOSTOMY = "hepaticojejunostomy"
- ENDOSCOPIC_RETROGRADE_CHOLANGIOPANCREATOGRAPHY = "endoscopic_retrograde_cholangiopancreatography"
+ ENDOSCOPIC_RETROGRADE_CHOLANGIOPANCREATOGRAPHY = (
+ "endoscopic_retrograde_cholangiopancreatography"
+ )
ERCP_WITH_SPHINCTEROTOMY = "ercp_with_sphincterotomy"
ERCP_WITH_STENT_INSERTION = "ercp_with_stent_insertion"
ERCP_WITH_STONE_EXTRACTION = "ercp_with_stone_extraction"
@@ -273,8 +283,12 @@ class ProcedureType(str, Enum):
DOUBLE_VALVE_REPLACEMENT = "double_valve_replacement"
PERMANENT_PACEMAKER_INSERTION = "permanent_pacemaker_insertion"
PACEMAKER_BOX_CHANGE = "pacemaker_box_change"
- IMPLANTABLE_CARDIOVERTER_DEFIBRILLATOR_INSERTION = "implantable_cardioverter_defibrillator_insertion"
- CARDIAC_RESYNCHRONISATION_THERAPY_DEVICE_INSERTION = "cardiac_resynchronisation_therapy_device_insertion"
+ IMPLANTABLE_CARDIOVERTER_DEFIBRILLATOR_INSERTION = (
+ "implantable_cardioverter_defibrillator_insertion"
+ )
+ CARDIAC_RESYNCHRONISATION_THERAPY_DEVICE_INSERTION = (
+ "cardiac_resynchronisation_therapy_device_insertion"
+ )
LEAD_EXTRACTION = "lead_extraction"
ELECTROPHYSIOLOGICAL_STUDY = "electrophysiological_study"
CATHETER_ABLATION_FOR_ARRHYTHMIA = "catheter_ablation_for_arrhythmia"
@@ -284,12 +298,18 @@ class ProcedureType(str, Enum):
PERICARDIOCENTESIS = "pericardiocentesis"
PERICARDIAL_WINDOW = "pericardial_window"
CARDIAC_TRANSPLANTATION = "cardiac_transplantation"
- LEFT_VENTRICULAR_ASSIST_DEVICE_INSERTION = "left_ventricular_assist_device_insertion"
+ LEFT_VENTRICULAR_ASSIST_DEVICE_INSERTION = (
+ "left_ventricular_assist_device_insertion"
+ )
INTRA_AORTIC_BALLOON_PUMP_INSERTION = "intra_aortic_balloon_pump_insertion"
- EXTRACORPOREAL_MEMBRANE_OXYGENATION_CANNULATION = "extracorporeal_membrane_oxygenation_cannulation"
+ EXTRACORPOREAL_MEMBRANE_OXYGENATION_CANNULATION = (
+ "extracorporeal_membrane_oxygenation_cannulation"
+ )
ATRIAL_SEPTAL_DEFECT_REPAIR = "atrial_septal_defect_repair"
VENTRICULAR_SEPTAL_DEFECT_REPAIR = "ventricular_septal_defect_repair"
- SURGICAL_CLOSURE_OF_PATENT_DUCTUS_ARTERIOSUS = "surgical_closure_of_patent_ductus_arteriosus"
+ SURGICAL_CLOSURE_OF_PATENT_DUCTUS_ARTERIOSUS = (
+ "surgical_closure_of_patent_ductus_arteriosus"
+ )
# L Arteries and Veins
OPEN_ABDOMINAL_AORTIC_ANEURYSM_REPAIR = "open_abdominal_aortic_aneurysm_repair"
@@ -311,8 +331,12 @@ class ProcedureType(str, Enum):
CREATION_OF_ARTERIOVENOUS_FISTULA = "creation_of_arteriovenous_fistula"
INSERTION_OF_ARTERIOVENOUS_GRAFT = "insertion_of_arteriovenous_graft"
VARICOSE_VEIN_STRIPPING = "varicose_vein_stripping"
- ENDOVENOUS_LASER_ABLATION_OF_VARICOSE_VEINS = "endovenous_laser_ablation_of_varicose_veins"
- RADIOFREQUENCY_ABLATION_OF_VARICOSE_VEINS = "radiofrequency_ablation_of_varicose_veins"
+ ENDOVENOUS_LASER_ABLATION_OF_VARICOSE_VEINS = (
+ "endovenous_laser_ablation_of_varicose_veins"
+ )
+ RADIOFREQUENCY_ABLATION_OF_VARICOSE_VEINS = (
+ "radiofrequency_ablation_of_varicose_veins"
+ )
FOAM_SCLEROTHERAPY_OF_VARICOSE_VEINS = "foam_sclerotherapy_of_varicose_veins"
PHLEBECTOMY = "phlebectomy"
LIGATION_OF_VARICOSE_VEINS = "ligation_of_varicose_veins"
@@ -339,7 +363,9 @@ class ProcedureType(str, Enum):
RADICAL_CYSTECTOMY = "radical_cystectomy"
PARTIAL_CYSTECTOMY = "partial_cystectomy"
ILEAL_CONDUIT_FORMATION = "ileal_conduit_formation"
- TRANSURETHRAL_RESECTION_OF_BLADDER_TUMOUR = "transurethral_resection_of_bladder_tumour"
+ TRANSURETHRAL_RESECTION_OF_BLADDER_TUMOUR = (
+ "transurethral_resection_of_bladder_tumour"
+ )
CYSTOSCOPY = "cystoscopy"
CYSTODIATHERMY = "cystodiathermy"
INSERTION_OF_SUPRAPUBIC_CATHETER = "insertion_of_suprapubic_catheter"
@@ -347,12 +373,16 @@ class ProcedureType(str, Enum):
LASER_ENUCLEATION_OF_PROSTATE = "laser_enucleation_of_prostate"
RADICAL_PROSTATECTOMY = "radical_prostatectomy"
ROBOTIC_ASSISTED_RADICAL_PROSTATECTOMY = "robotic_assisted_radical_prostatectomy"
- TRANSRECTAL_ULTRASOUND_GUIDED_PROSTATE_BIOPSY = "transrectal_ultrasound_guided_prostate_biopsy"
+ TRANSRECTAL_ULTRASOUND_GUIDED_PROSTATE_BIOPSY = (
+ "transrectal_ultrasound_guided_prostate_biopsy"
+ )
TRANSPERINEAL_PROSTATE_BIOPSY = "transperineal_prostate_biopsy"
URETHROPLASTY = "urethroplasty"
OPTICAL_URETHROTOMY = "optical_urethrotomy"
URETHRAL_DILATATION = "urethral_dilatation"
- INSERTION_OF_ARTIFICIAL_URINARY_SPHINCTER = "insertion_of_artificial_urinary_sphincter"
+ INSERTION_OF_ARTIFICIAL_URINARY_SPHINCTER = (
+ "insertion_of_artificial_urinary_sphincter"
+ )
MID_URETHRAL_SLING_PROCEDURE = "mid_urethral_sling_procedure"
COLPOSUSPENSION = "colposuspension"
@@ -411,7 +441,9 @@ class ProcedureType(str, Enum):
EPISIOTOMY = "episiotomy"
REPAIR_OF_OBSTETRIC_PERINEAL_TEAR = "repair_of_obstetric_perineal_tear"
MANUAL_REMOVAL_OF_PLACENTA = "manual_removal_of_placenta"
- EVACUATION_OF_RETAINED_PRODUCTS_OF_CONCEPTION = "evacuation_of_retained_products_of_conception"
+ EVACUATION_OF_RETAINED_PRODUCTS_OF_CONCEPTION = (
+ "evacuation_of_retained_products_of_conception"
+ )
CERVICAL_CERCLAGE = "cervical_cerclage"
EXTERNAL_CEPHALIC_VERSION = "external_cephalic_version"
FETAL_BLOOD_SAMPLING = "fetal_blood_sampling"
@@ -422,7 +454,9 @@ class ProcedureType(str, Enum):
EXCISION_OF_MALIGNANT_MELANOMA = "excision_of_malignant_melanoma"
SHAVE_EXCISION_OF_SKIN_LESION = "shave_excision_of_skin_lesion"
CURETTAGE_AND_CAUTERY_OF_SKIN_LESION = "curettage_and_cautery_of_skin_lesion"
- SURGICAL_DEBRIDEMENT_OF_SKIN_AND_SUBCUTANEOUS_TISSUE = "surgical_debridement_of_skin_and_subcutaneous_tissue"
+ SURGICAL_DEBRIDEMENT_OF_SKIN_AND_SUBCUTANEOUS_TISSUE = (
+ "surgical_debridement_of_skin_and_subcutaneous_tissue"
+ )
SPLIT_SKIN_GRAFT = "split_skin_graft"
FULL_THICKNESS_SKIN_GRAFT = "full_thickness_skin_graft"
LOCAL_SKIN_FLAP_RECONSTRUCTION = "local_skin_flap_reconstruction"
@@ -490,12 +524,16 @@ class ProcedureType(str, Enum):
POSTERIOR_LUMBAR_INTERBODY_FUSION = "posterior_lumbar_interbody_fusion"
REVISION_SPINAL_FUSION = "revision_spinal_fusion"
POSTERIOR_INSTRUMENTED_FUSION_SPINE = "posterior_instrumented_fusion_spine"
- INSTRUMENTED_CORRECTION_SPINAL_DEFORMITY = "instrumented_correction_spinal_deformity"
+ INSTRUMENTED_CORRECTION_SPINAL_DEFORMITY = (
+ "instrumented_correction_spinal_deformity"
+ )
SCOLIOSIS_CORRECTION_SURGERY = "scoliosis_correction_surgery"
EXCISION_OF_SPINAL_LESION = "excision_of_spinal_lesion"
VERTEBROPLASTY = "vertebroplasty"
KYPHOPLASTY = "kyphoplasty"
- REDUCTION_AND_FIXATION_OF_SPINAL_FRACTURE = "reduction_and_fixation_of_spinal_fracture"
+ REDUCTION_AND_FIXATION_OF_SPINAL_FRACTURE = (
+ "reduction_and_fixation_of_spinal_fracture"
+ )
SPINAL_CORD_STIMULATOR_INSERTION = "spinal_cord_stimulator_insertion"
BIOPSY_OF_SPINE = "biopsy_of_spine"
FACET_JOINT_DENERVATION = "facet_joint_denervation"
@@ -522,10 +560,18 @@ class ProcedureType(str, Enum):
BONE_GRAFTING = "bone_grafting"
BONE_MARROW_ASPIRATION_OR_BIOPSY = "bone_marrow_aspiration_or_biopsy"
DRAINAGE_OF_BONE_ABSCESS = "drainage_of_bone_abscess"
- OPEN_REDUCTION_INTERNAL_FIXATION_LONG_BONE_FRACTURE = "open_reduction_internal_fixation_long_bone_fracture"
- OPEN_REDUCTION_INTERNAL_FIXATION_INTRAARTICULAR_FRACTURE = "open_reduction_internal_fixation_intraarticular_fracture"
- CLOSED_REDUCTION_INTERNAL_FIXATION_FRACTURE = "closed_reduction_internal_fixation_fracture"
- CLOSED_REDUCTION_EXTERNAL_FIXATION_FRACTURE = "closed_reduction_external_fixation_fracture"
+ OPEN_REDUCTION_INTERNAL_FIXATION_LONG_BONE_FRACTURE = (
+ "open_reduction_internal_fixation_long_bone_fracture"
+ )
+ OPEN_REDUCTION_INTERNAL_FIXATION_INTRAARTICULAR_FRACTURE = (
+ "open_reduction_internal_fixation_intraarticular_fracture"
+ )
+ CLOSED_REDUCTION_INTERNAL_FIXATION_FRACTURE = (
+ "closed_reduction_internal_fixation_fracture"
+ )
+ CLOSED_REDUCTION_EXTERNAL_FIXATION_FRACTURE = (
+ "closed_reduction_external_fixation_fracture"
+ )
CLOSED_REDUCTION_OF_FRACTURE = "closed_reduction_of_fracture"
DYNAMIC_HIP_SCREW_FIXATION = "dynamic_hip_screw_fixation"
INTRAMEDULLARY_NAILING_FEMUR = "intramedullary_nailing_femur"
@@ -583,8 +629,12 @@ class ProcedureType(str, Enum):
KNEE_ARTHROSCOPY_CARTILAGE_PROCEDURE = "knee_arthroscopy_cartilage_procedure"
DIAGNOSTIC_KNEE_ARTHROSCOPY = "diagnostic_knee_arthroscopy"
HIP_ARTHROSCOPY = "hip_arthroscopy"
- SHOULDER_ARTHROSCOPY_SUBACROMIAL_DECOMPRESSION = "shoulder_arthroscopy_subacromial_decompression"
- SHOULDER_ARTHROSCOPY_ROTATOR_CUFF_REPAIR = "shoulder_arthroscopy_rotator_cuff_repair"
+ SHOULDER_ARTHROSCOPY_SUBACROMIAL_DECOMPRESSION = (
+ "shoulder_arthroscopy_subacromial_decompression"
+ )
+ SHOULDER_ARTHROSCOPY_ROTATOR_CUFF_REPAIR = (
+ "shoulder_arthroscopy_rotator_cuff_repair"
+ )
SHOULDER_ARTHROSCOPY_LABRAL_REPAIR = "shoulder_arthroscopy_labral_repair"
ANKLE_ARTHROSCOPY = "ankle_arthroscopy"
WRIST_ARTHROSCOPY = "wrist_arthroscopy"
@@ -616,7 +666,9 @@ class ProcedureType(str, Enum):
INCISION_AND_DRAINAGE_OF_ABSCESS = "incision_and_drainage_of_abscess"
INSERTION_OF_CENTRAL_VENOUS_CATHETER = "insertion_of_central_venous_catheter"
REMOVAL_OF_CENTRAL_VENOUS_CATHETER = "removal_of_central_venous_catheter"
- INSERTION_OF_PERIPHERALLY_INSERTED_CENTRAL_CATHETER = "insertion_of_peripherally_inserted_central_catheter"
+ INSERTION_OF_PERIPHERALLY_INSERTED_CENTRAL_CATHETER = (
+ "insertion_of_peripherally_inserted_central_catheter"
+ )
REMOVAL_OF_FOREIGN_BODY = "removal_of_foreign_body"
EXAMINATION_UNDER_ANAESTHESIA = "examination_under_anaesthesia"
@@ -663,7 +715,9 @@ class ComplicationType(str, Enum):
PERIPROSTHETIC_FRACTURE = "periprosthetic_fracture"
IMPLANT_MALPOSITION_OR_MALALIGNMENT = "implant_malposition_or_malalignment"
LEG_LENGTH_DISCREPANCY = "leg_length_discrepancy"
- INTRAOPERATIVE_DISLOCATION_OR_INSTABILITY = "intraoperative_dislocation_or_instability"
+ INTRAOPERATIVE_DISLOCATION_OR_INSTABILITY = (
+ "intraoperative_dislocation_or_instability"
+ )
SCREW_OR_GUIDEWIRE_MALPOSITION = "screw_or_guidewire_malposition"
IMPLANT_MALFUNCTION_OR_BREAKAGE = "implant_malfunction_or_breakage"
@@ -674,7 +728,9 @@ class ComplicationType(str, Enum):
BONE_CEMENT_IMPLANTATION_SYNDROME = "bone_cement_implantation_syndrome"
FAT_EMBOLISM_SYNDROME = "fat_embolism_syndrome"
TOURNIQUET_RELATED_COMPLICATION = "tourniquet_related_complication"
- EXCESSIVE_BLOOD_LOSS_REQUIRING_TRANSFUSION = "excessive_blood_loss_requiring_transfusion"
+ EXCESSIVE_BLOOD_LOSS_REQUIRING_TRANSFUSION = (
+ "excessive_blood_loss_requiring_transfusion"
+ )
# Orthopaedic / MSK - other
COMPARTMENT_SYNDROME = "compartment_syndrome"
@@ -725,7 +781,8 @@ class Implant(BaseModel):
None, description="Manufacturer of the implant, if stated"
)
size_or_specification: str | None = Field(
- None, description="Size, offset, or other specification of the implant, if stated"
+ None,
+ description="Size, offset, or other specification of the implant, if stated",
)
serial_or_lot_number: str | None = Field(
None, description="Serial or batch/lot number of the implant, if stated"
@@ -742,7 +799,8 @@ class ProcedureComplication(BaseModel):
description="Direct extract or close paraphrase describing the complication as documented"
)
management: str | None = Field(
- None, description="Direct extract of how the complication was managed, if stated"
+ None,
+ description="Direct extract of how the complication was managed, if stated",
)
@@ -770,7 +828,8 @@ class ProcedureMetadata(BaseModel):
do not infer or estimate."""
urgency: ProcedureUrgency | None = Field(
- None, description="Elective, urgent, or emergency, if stated or clearly inferable from context (e.g. 'trauma list')"
+ None,
+ description="Elective, urgent, or emergency, if stated or clearly inferable from context (e.g. 'trauma list')",
)
anaesthetic_type: AnaestheticType | None = Field(
None, description="Type of anaesthetic used, if stated"
@@ -789,7 +848,7 @@ class ProcedureMetadata(BaseModel):
)
surgeon_grade: str | None = Field(
None,
- description="Grade or seniority of the operating surgeon as documented (e.g. 'consultant', 'registrar'), with names/identifiers redacted",
+ description="Grade of the most senior operating surgeon as documented (e.g. 'consultant', 'registrar'), with names/identifiers redacted",
)
@@ -815,7 +874,8 @@ class OperationNote(BaseModel):
description="Complications occurring during, or noted in immediate relation to, the procedure; empty if none reported",
)
metadata: ProcedureMetadata | None = Field(
- None, description="Operative metadata reported for the case; None if nothing stated"
+ None,
+ description="Operative metadata reported for the case; None if nothing stated",
)
operation_summary: str | None = Field(
None,
diff --git a/schemas/opnoteschema/tests/test_example.py b/schemas/opnoteschema/tests/test_example.py
index 26923ab..8ba7fa3 100644
--- a/schemas/opnoteschema/tests/test_example.py
+++ b/schemas/opnoteschema/tests/test_example.py
@@ -1,5 +1,19 @@
"""Test that the canonical example validates."""
+from mesa_types import TrainingExample
+
+from opnoteschema.prompt_builder import PromptBuilder
+from opnoteschema.schema import OperationNote
+
def test_example_validates() -> None:
- pass
+ """Test that the canonical example validates against the schema."""
+ builder = PromptBuilder()
+
+ example_str = builder._load("examples", "example.json")
+ example_data = TrainingExample.model_validate_json(example_str)
+
+ validated = OperationNote.model_validate(example_data.output)
+
+ assert validated is not None
+ assert validated.is_operation_note is True
diff --git a/schemas/opnoteschema/tests/test_opnoteschema.py b/schemas/opnoteschema/tests/test_opnoteschema.py
index f2254db..9153adf 100644
--- a/schemas/opnoteschema/tests/test_opnoteschema.py
+++ b/schemas/opnoteschema/tests/test_opnoteschema.py
@@ -1,13 +1,45 @@
"""Tests for opnoteschema package."""
+from opnoteschema.prompt_builder import PromptBuilder
+from opnoteschema.schema import OperationNote
+
def test_validate_schema() -> None:
- pass
+ """Test that we can instantiate and validate the schema."""
+ OperationNote.model_json_schema()
def test_build_datagen_prompt() -> None:
- pass
+ """Test building data generation prompt."""
+ builder = PromptBuilder()
+ prompt = builder.build_datagen_prompt()
+
+ # Placeholders replaced
+ assert "{SCHEMA}" not in prompt, "Schema placeholder should be replaced"
+ assert "{EXAMPLE}" not in prompt, "Example placeholder should be replaced"
+
+ # Schema source present
+ assert "OperationNote" in prompt, "Schema should contain the root model"
+ assert "procedure_type" in prompt, "Schema should contain key field"
+
+ # Example should be present
+ assert '"content"' in prompt or "'content'" in prompt, (
+ "Example should contain 'content' field"
+ )
+ assert '"output"' in prompt or "'output'" in prompt, (
+ "Example should contain 'output' field"
+ )
def test_build_main_prompt() -> None:
- pass
\ No newline at end of file
+ """Test building main prompt."""
+ builder = PromptBuilder()
+ prompt = builder.build_main_prompt()
+
+ # Placeholders replaced
+ assert "{SCHEMA}" not in prompt, "Schema placeholder should be replaced"
+ assert "{EXAMPLE}" not in prompt, "Main prompt should not have example placeholder"
+
+ # Schema source present
+ assert "OperationNote" in prompt, "Schema should contain the root model"
+ assert "procedure_type" in prompt, "Schema should contain key field"
diff --git a/schemas/opnoteschema/uv.lock b/schemas/opnoteschema/uv.lock
index bc0e322..c377fdc 100644
--- a/schemas/opnoteschema/uv.lock
+++ b/schemas/opnoteschema/uv.lock
@@ -751,14 +751,18 @@ wheels = [
[[package]]
name = "londonaicentre-mesa-types"
-version = "2.3.0"
+version = "2.5.0"
source = { editable = "../../lib/types" }
dependencies = [
{ name = "pydantic" },
+ { name = "pyyaml" },
]
[package.metadata]
-requires-dist = [{ name = "pydantic", specifier = ">=2.12.5" }]
+requires-dist = [
+ { name = "pydantic", specifier = ">=2.12.5" },
+ { name = "pyyaml", specifier = ">=6.0.2" },
+]
[package.metadata.requires-dev]
dev = [
@@ -769,11 +773,12 @@ dev = [
[[package]]
name = "londonaicentre-mesa-utils"
-version = "1.1.1"
+version = "1.4.0"
source = { editable = "../../lib/utils" }
dependencies = [
{ name = "boto3" },
{ name = "bs4" },
+ { name = "httpx" },
{ name = "litellm" },
{ name = "londonaicentre-mesa-types" },
{ name = "markdown" },
@@ -784,6 +789,7 @@ dependencies = [
requires-dist = [
{ name = "boto3", specifier = ">=1.41.1" },
{ name = "bs4", specifier = ">=0.0.2" },
+ { name = "httpx", specifier = ">=0.28.1" },
{ name = "litellm", specifier = ">=1.80.0" },
{ name = "londonaicentre-mesa-types", editable = "../../lib/types" },
{ name = "markdown", specifier = ">=3.10" },
@@ -795,6 +801,7 @@ dev = [
{ name = "boto3-stubs", specifier = ">=1.41.4" },
{ name = "mypy", specifier = ">=1.18.2" },
{ name = "pytest", specifier = ">=9.0.2" },
+ { name = "pytest-mock", specifier = ">=3.15.1" },
{ name = "ruff", specifier = ">=0.14.6" },
{ name = "types-markdown", specifier = ">=3.10.0.20251106" },
]