@@ -47,6 +47,8 @@ export class PatientService {
4747 public static readonly ICD10_SYSTEM = 'http://hl7.org/fhir/sid/icd-10' ;
4848 private static readonly EHR_LAB_LOCATION_SYSTEM = 'http://ehr-lab.demo/location' ;
4949 private static readonly PERSISTENCE_MODE_STORAGE_KEY = 'ehr_persistence_mode' ;
50+ private static readonly AI_PROVENANCE_TECHNICAL_AGENT_DISPLAY = 'AI/NLP pipeline' ;
51+ private static readonly AI_PROVENANCE_HUMAN_REVIEWER_DISPLAY = 'Human reviewer' ;
5052 private readonly FHIR_PATIENT_PAGE_SIZE = 20 ;
5153 private readonly ANATOMICAL_ANCHOR_POINTS : Array < { id : string ; ancestors : string [ ] } > = [
5254 {
@@ -2121,6 +2123,81 @@ export class PatientService {
21212123 } ;
21222124 }
21232125
2126+ createAiDerivedProvenance (
2127+ patientId : string ,
2128+ targetResource : { resourceType : string ; id : string ; code ?: { text ?: string } ; medicationCodeableConcept ?: { text ?: string } } ,
2129+ sourceReference : { reference : string ; display ?: string } ,
2130+ recorded : string = new Date ( ) . toISOString ( )
2131+ ) : Provenance {
2132+ const targetDisplay = targetResource . code ?. text
2133+ || targetResource . medicationCodeableConcept ?. text
2134+ || `${ targetResource . resourceType } ${ targetResource . id } ` ;
2135+
2136+ return {
2137+ resourceType : 'Provenance' ,
2138+ id : `provenance-${ Date . now ( ) } -${ Math . random ( ) . toString ( 36 ) . slice ( 2 , 11 ) } ` ,
2139+ recorded,
2140+ patient : {
2141+ reference : `Patient/${ patientId } ` ,
2142+ display : `Patient ${ patientId } `
2143+ } ,
2144+ activity : {
2145+ text : 'AI-derived clinical entity under active human review'
2146+ } ,
2147+ target : [
2148+ {
2149+ reference : `${ targetResource . resourceType } /${ targetResource . id } ` ,
2150+ display : targetDisplay
2151+ } ,
2152+ {
2153+ reference : `Patient/${ patientId } ` ,
2154+ display : `Patient ${ patientId } `
2155+ }
2156+ ] ,
2157+ agent : [
2158+ {
2159+ type : {
2160+ text : 'assembler'
2161+ } ,
2162+ role : [
2163+ {
2164+ text : 'technical agent'
2165+ }
2166+ ] ,
2167+ who : {
2168+ display : PatientService . AI_PROVENANCE_TECHNICAL_AGENT_DISPLAY
2169+ }
2170+ } ,
2171+ {
2172+ type : {
2173+ text : 'author'
2174+ } ,
2175+ role : [
2176+ {
2177+ text : 'human reviewer'
2178+ }
2179+ ] ,
2180+ who : {
2181+ display : PatientService . AI_PROVENANCE_HUMAN_REVIEWER_DISPLAY
2182+ }
2183+ }
2184+ ] ,
2185+ entity : [
2186+ {
2187+ role : 'derivation' ,
2188+ what : {
2189+ reference : sourceReference . reference ,
2190+ display : sourceReference . display
2191+ }
2192+ }
2193+ ] ,
2194+ text : {
2195+ status : 'generated' ,
2196+ div : '<div xmlns="http://www.w3.org/1999/xhtml"><p>Derived by AI from persisted source text and approved under active human supervision.</p></div>'
2197+ }
2198+ } ;
2199+ }
2200+
21242201 private normalizeAllergyForPersistence ( allergy : AllergyIntolerance ) : AllergyIntolerance {
21252202 const normalizedCategories = Array . isArray ( allergy . category )
21262203 ? allergy . category
@@ -2687,16 +2764,72 @@ export class PatientService {
26872764 patientId : string ,
26882765 payload : AiAssistedEntryTransactionPayload
26892766 ) : Promise < AiAssistedEntryTransactionResult > {
2690- if ( this . getCurrentPersistenceMode ( ) !== 'fhir' ) {
2691- throw new Error ( 'AI-assisted entry transactions are only available in FHIR mode.' ) ;
2692- }
2693-
26942767 await Promise . all ( [
26952768 ...payload . conditions . map ( ( condition ) => this . enrichCondition ( condition ) ) ,
26962769 ...payload . procedures . map ( ( procedure ) => this . enrichProcedure ( procedure ) ) ,
26972770 ...payload . medications . map ( ( medication ) => this . enrichMedication ( patientId , medication ) )
26982771 ] ) ;
26992772
2773+ if ( this . getCurrentPersistenceMode ( ) !== 'fhir' ) {
2774+ const savedEncounter = payload . encounter && this . addPatientEncounter ( patientId , payload . encounter )
2775+ ? payload . encounter
2776+ : null ;
2777+ const savedConditions : Condition [ ] = [ ] ;
2778+ const savedProcedures : Procedure [ ] = [ ] ;
2779+ const savedMedications : MedicationStatement [ ] = [ ] ;
2780+ const savedAllergies : AllergyIntolerance [ ] = [ ] ;
2781+
2782+ for ( const condition of payload . conditions ) {
2783+ if ( this . addPatientCondition ( patientId , condition ) ) {
2784+ savedConditions . push ( condition ) ;
2785+ }
2786+ }
2787+
2788+ for ( const procedure of payload . procedures ) {
2789+ if ( this . addPatientProcedure ( patientId , procedure ) ) {
2790+ savedProcedures . push ( procedure ) ;
2791+ }
2792+ }
2793+
2794+ for ( const medication of payload . medications ) {
2795+ if ( this . addPatientMedication ( patientId , medication ) ) {
2796+ savedMedications . push ( medication ) ;
2797+ }
2798+ }
2799+
2800+ for ( const allergy of payload . allergies ) {
2801+ if ( this . addPatientAllergy ( patientId , allergy ) ) {
2802+ savedAllergies . push ( allergy ) ;
2803+ }
2804+ }
2805+
2806+ const savedTargetReferences = new Set < string > ( [
2807+ ...savedConditions . map ( ( resource ) => `Condition/${ resource . id } ` ) ,
2808+ ...savedProcedures . map ( ( resource ) => `Procedure/${ resource . id } ` ) ,
2809+ ...savedMedications . map ( ( resource ) => `MedicationStatement/${ resource . id } ` ) ,
2810+ ...savedAllergies . map ( ( resource ) => `AllergyIntolerance/${ resource . id } ` )
2811+ ] ) ;
2812+
2813+ const savedProvenance : Provenance [ ] = [ ] ;
2814+ for ( const provenance of payload . provenance || [ ] ) {
2815+ const hasSavedTarget = ( provenance . target || [ ] ) . some ( ( target ) => savedTargetReferences . has ( target . reference ) ) ;
2816+ if ( ! hasSavedTarget ) {
2817+ continue ;
2818+ }
2819+ savedProvenance . push ( await this . createPatientProvenance ( patientId , provenance ) ) ;
2820+ }
2821+
2822+ this . notifyPatientDataChanged ( patientId ) ;
2823+ return {
2824+ encounter : savedEncounter ,
2825+ conditions : savedConditions ,
2826+ procedures : savedProcedures ,
2827+ medications : savedMedications ,
2828+ allergies : savedAllergies ,
2829+ provenance : savedProvenance
2830+ } ;
2831+ }
2832+
27002833 const result = await this . patientFhirStorageService . saveAiAssistedEntryTransaction ( patientId , payload ) ;
27012834
27022835 this . setResourceCache (
0 commit comments