From d64b4bddd963ca04550d87119f7f3549ac81fdfc Mon Sep 17 00:00:00 2001 From: apgupta3303 Date: Tue, 12 Mar 2024 00:40:21 -0700 Subject: [PATCH] Consolidating UI + tuning LLM Assistnat prompt with few shot prompting --- Intake/Allergy Records/AddAllergy.swift | 36 +++---- Intake/Allergy Records/AllergyRecords.swift | 77 ++++++--------- Intake/Home.swift | 2 + Intake/LLMFiltering.swift | 2 + .../Medical History/MedicalHistoryView.swift | 43 ++++++-- Intake/Resources/Localizable.xcstrings | 12 +-- Intake/ScrollablePDF.swift | 97 +++++++++++++------ Intake/Surgery/SurgeryView.swift | 3 - 8 files changed, 159 insertions(+), 113 deletions(-) diff --git a/Intake/Allergy Records/AddAllergy.swift b/Intake/Allergy Records/AddAllergy.swift index ba6efdb..339b95a 100644 --- a/Intake/Allergy Records/AddAllergy.swift +++ b/Intake/Allergy Records/AddAllergy.swift @@ -16,27 +16,28 @@ import SpeziFHIR import SwiftUI struct EditAllergyView: View { - @State private var index: Int + @Binding var item: AllergyItem @Environment(DataStore.self) private var data - @Binding private var showingReaction: Bool + @Environment(NavigationPathWrapper.self) private var navigationPath + @Environment(\.presentationMode) var presentationMode + var body: some View { - NavigationView { - VStack(alignment: .leading, spacing: 10) { - @Bindable var data = data - TextField("Allergy Name", text: $data.allergyData[index].allergy) - .textFieldStyle(RoundedBorderTextFieldStyle()) - .padding([.horizontal, .top]) - ReactionSectionView(index: index) - Spacer() - saveButton - } - .navigationBarTitle("Allergy") - } + VStack(alignment: .leading, spacing: 10) { + let index = data.allergyData.firstIndex(of: item) ?? 0 + @Bindable var data = data + TextField("Allergy Name", text: $data.allergyData[index].allergy) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .padding([.horizontal, .top]) + ReactionSectionView(index: index) + Spacer() + saveButton + } + .navigationBarTitle("Allergy Name") } private var saveButton: some View { Button(action: { - showingReaction = false + presentationMode.wrappedValue.dismiss() }) { Text("Save") .foregroundColor(.white) @@ -47,11 +48,6 @@ struct EditAllergyView: View { } .padding() } - - init(index: Int, showingReaction: Binding) { - self._index = State(initialValue: index) - self._showingReaction = showingReaction - } } diff --git a/Intake/Allergy Records/AllergyRecords.swift b/Intake/Allergy Records/AllergyRecords.swift index e6c14b1..61a053a 100644 --- a/Intake/Allergy Records/AllergyRecords.swift +++ b/Intake/Allergy Records/AllergyRecords.swift @@ -58,6 +58,7 @@ struct AllergyList: View { @State private var selectedIndex = 0 @State private var showingChat = false @State private var presentingAccount = false + @State private var newAllergy = AllergyItem(allergy: "", reaction: []) @LLMSessionProvider var session: LLMOpenAISession @@ -69,7 +70,6 @@ struct AllergyList: View { .padding() } .sheet(isPresented: $showingChat, content: chatSheetView) - .sheet(isPresented: $showingReaction, content: editAllergySheetView) } else { ProgressView() .task { @@ -86,48 +86,61 @@ struct AllergyList: View { Form { Section(header: Text("What are your current allergies?")) { allergyEntries - addAllergyButton Text("*Click the details to view/edit the reactions") .font(.caption) .foregroundColor(.gray) } } - .navigationBarItems(trailing: EditButton()) .navigationTitle("Allergies") + .navigationBarItems(trailing: addAllergyButton) .navigationBarItems(trailing: NavigationLink(destination: AllergyLLMAssistant(presentingAccount: $presentingAccount)) { Text("Chat") }) } private var allergyEntries: some View { - ForEach(0.. some View { - HStack { - Text(data.allergyData[index].allergy) - .foregroundColor(.black) - Spacer() - Image(systemName: "chevron.right") - .foregroundColor(.gray) - .accessibilityLabel(Text("DETAILS")) - } - } - - private func allergyButton(index: Int) -> some View { - Button(action: { - self.selectedIndex = index - self.showingReaction = true - }) { - allergyEntryRow(index: index) - } - } - private func submitAction() { navigationPath.path.append(NavigationViews.menstrual) } - - private func addAllergyAction() { - data.allergyData.append(AllergyItem(allergy: "", reaction: [])) - self.selectedIndex = data.allergyData.count - 1 - showingReaction = true - } private func chatSheetView() -> some View { LLMAssistantView( @@ -184,10 +171,6 @@ struct AllergyList: View { prompt: .constant("Pretend you are a nurse. Your job is to help the patient understand what allergies they have.") ) } - - private func editAllergySheetView() -> some View { - EditAllergyView(index: selectedIndex, showingReaction: $showingReaction) - } private func removeTextWithinParentheses(from string: String) -> String { let pattern = "\\s*\\([^)]+\\)" diff --git a/Intake/Home.swift b/Intake/Home.swift index 1d0410e..3f967aa 100644 --- a/Intake/Home.swift +++ b/Intake/Home.swift @@ -24,6 +24,7 @@ enum NavigationViews: String { case pdfs case inspect case general + case newAllergy } struct StartButton: View { @@ -127,6 +128,7 @@ struct HomeView: View { case .patient: EditPatientView() case .pdfs: ScrollablePDF() case .inspect: InspectSurgeryView(surgery: $data.surgeries[data.surgeries.count - 1], isNew: true) + case .newAllergy: EditAllergyView(item: $data.allergyData[data.allergyData.count - 1]) case .general: PatientInfo() } } diff --git a/Intake/LLMFiltering.swift b/Intake/LLMFiltering.swift index 95191a8..262351d 100644 --- a/Intake/LLMFiltering.swift +++ b/Intake/LLMFiltering.swift @@ -106,6 +106,7 @@ class LLMFiltering { func filterConditions() async throws { let conditions = data.conditionData.map { $0.condition } + print(conditions) let filteredNames = try await self.LLMFilter(names: conditions) let filteredConditions = data.conditionData.filter { self.containsAnyWords(item: $0.condition, words: filteredNames) } var cleaned = filteredConditions @@ -122,6 +123,7 @@ class LLMFiltering { func filterAllergies() async throws { let allergies = data.allergyData.map { $0.allergy } + print(allergies) let filteredNames = try await self.LLMFilter(names: allergies) let filteredAllergies = data.allergyData.filter { self.containsAnyWords(item: $0.allergy, words: filteredNames) } var cleaned = filteredAllergies diff --git a/Intake/Medical History/MedicalHistoryView.swift b/Intake/Medical History/MedicalHistoryView.swift index e1a0bb6..c4d095d 100644 --- a/Intake/Medical History/MedicalHistoryView.swift +++ b/Intake/Medical History/MedicalHistoryView.swift @@ -59,15 +59,14 @@ struct MedicalHistoryView: View { Form { Section(header: Text("Please list conditions you have had")) { conditionEntries - addConditionButton instructionText } } .navigationTitle("Medical History") + .navigationBarItems(trailing: addConditionButton) .navigationBarItems(trailing: NavigationLink(destination: MedicalHistoryLLMAssistant(presentingAccount: .constant(false))) { Text("Chat") }) - .navigationBarItems(trailing: EditButton()) } private var conditionEntries: some View { @@ -92,9 +91,8 @@ struct MedicalHistoryView: View { private var addConditionButton: some View { Button(action: addConditionAction) { HStack { - Image(systemName: "plus.circle.fill") + Image(systemName: "plus") .accessibilityHidden(true) - Text("Add Field") } } } @@ -108,18 +106,43 @@ struct MedicalHistoryView: View { .foregroundColor(.gray) } - init() { + init() { // swiftlint:disable:this function_body_length let systemPrompt = """ You are a helpful assistant that filters lists of conditions. You will be given\ - an array of strings. Each string will be the name of a condition. + an array of strings. Each string will be the name of a condition, but we only want\ + to keep the names of relevant conditions. By relevant, we do not want to add conditions\ + that are not severe and super common such as colds and ear infections For example, if you are given the following list: - Mammography (procedure), Certification procedure (procedure), Cytopathology\ - procedure, preparation of smear, genital source (procedure), Transplant of kidney\ - (procedure), + Atopic dermatitis, Acute viral pharyngitis (disorder), Otitis media, Perennial allergic rhinitis,\ + Aortic valve stenosis (disorder), Streptococcal sore throat (disorder) you should return something like this: - Transplant of kidney, Mammography. + Atopic dermatitis, Perennial allergic rhinitis, Aortic valve stenosis + + Another example would be if you are given the following list: + Received higher education (finding), Body mass index 30+ - obesity (finding), Gout, Essential\ + hypertension (disorder), Chronic kidney disease stage 1 (disorder), Disorder of kidney due to\ + diabetes mellitus (disorder), Chronic kidney disease stage 2 (disorder), Microalbuminuria due\ + to type 2 diabetes mellitus (disorder), Has a criminal record (finding), Refugee (person),\ + Chronic kidney disease stage 3 (disorder), Proteinuria due to type 2 diabetes mellitus\ + (disorder), Metabolic syndrome X (disorder), Prediabetes, Limited social contact (finding),\ + Reports of violence in the environment (finding), Victim of intimate partner abuse (finding),\ + Not in labor force (finding), Social isolation (finding), Acute viral pharyngitis (disorder),\ + Unhealthy alcohol drinking behavior (finding), Anemia (disorder), Awaiting transplantation of\ + kidney (situation), Chronic kidney disease stage 4 (disorder), Unemployed (finding), Ischemic\ + heart disease (disorder), Abnormal findings diagnostic imaging heart+coronary circulat (finding),\ + History of renal transplant (situation), Viral sinusitis (disorder), Malignant neoplasm of breast\ + (disorder), Acute bronchitis (disorder) + + you should return something like this: + Obesity, Gout, hypertension, Chronic kidney disease stage 1, Disorder of kidney due to\ + diabetes mellitus, Chronic kidney disease stage 2, Microalbuminuria due to type 2 diabetes mellitus,\ + Chronic kidney disease stage 3, Proteinuria due to type 2 diabetes mellitus, Metabolic syndrome X,\ + Prediabetes, Victim of intimate partner abuse, Unhealthy alcohol drinking behavior, Anemi, Awaiting\ + transplantation of kidney, Chronic kidney disease stage 4,Ischemic heart disease, \ + Malignant neoplasm of breast + In your response, return only the name of the condition. Remove words in parenthesis like (disorder), so "Aortic valve stenosis (disorder)" would turn to "Aortic valve stenosis". diff --git a/Intake/Resources/Localizable.xcstrings b/Intake/Resources/Localizable.xcstrings index 13e1594..653a328 100644 --- a/Intake/Resources/Localizable.xcstrings +++ b/Intake/Resources/Localizable.xcstrings @@ -88,6 +88,9 @@ }, "Add Field" : { + }, + "Add_allergy" : { + }, "ADD_REACTION" : { @@ -118,9 +121,6 @@ }, "Allergies:" : { - }, - "Allergy" : { - }, "Allergy Assistant" : { @@ -260,9 +260,6 @@ }, "DELETE_SURGERY" : { - }, - "DETAILS" : { - }, "Do you currently smoke or have you smoked in the past?" : { @@ -518,6 +515,9 @@ }, "No past surgeries" : { + }, + "No reactions" : { + }, "No Reactions" : { diff --git a/Intake/ScrollablePDF.swift b/Intake/ScrollablePDF.swift index 04e2d93..c067ec4 100644 --- a/Intake/ScrollablePDF.swift +++ b/Intake/ScrollablePDF.swift @@ -159,39 +159,82 @@ struct ScrollablePDF: View { } } - private struct Allergy: View { +// private struct Allergy: View { +// @Environment(DataStore.self) private var data +// @State private var showingReaction = false +// @State private var selectedIndex = 0 +// var body: some View { +// Section(header: HeaderTitle(title: "Allergy", nextView: NavigationViews.allergies)) { +// List { +// ForEach(0.. some View { +// ReactionPDF(index: selectedIndex, showingReaction: $showingReaction) +// } +// +// private func allergyButton(index: Int) -> some View { +// Button(action: { +// self.selectedIndex = index +// self.showingReaction = true +// }) { +// HStack { +// Text(data.allergyData[index].allergy) +// .foregroundColor(.black) +// Spacer() +// Image(systemName: "chevron.right") +// .foregroundColor(.gray) +// .accessibilityLabel(Text("DETAILS")) +// } +// } +// } +// +// func concatenate(strings: [ReactionItem]) -> String { +// let names = strings.map { $0.reaction } +// return names.joined(separator: ", ") +// } +// } + + private struct AllergySection: View { @Environment(DataStore.self) private var data - @State private var showingReaction = false - @State private var selectedIndex = 0 + @Environment(NavigationPathWrapper.self) private var navigationPath + var body: some View { - Section(header: HeaderTitle(title: "Allergy", nextView: NavigationViews.allergies)) { - List { - ForEach(0.. some View { - ReactionPDF(index: selectedIndex, showingReaction: $showingReaction) - } - - private func allergyButton(index: Int) -> some View { - Button(action: { - self.selectedIndex = index - self.showingReaction = true - }) { - HStack { - Text(data.allergyData[index].allergy) - .foregroundColor(.black) - Spacer() - Image(systemName: "chevron.right") - .foregroundColor(.gray) - .accessibilityLabel(Text("DETAILS")) - } - } + func concatenate(strings: [ReactionItem]) -> String { + let names = strings.map { $0.reaction } + return names.joined(separator: ", ") } } @@ -266,7 +309,7 @@ struct ScrollablePDF: View { ConditionSection() SurgerySection() MedicationSection() - Allergy() + AllergySection() MenstrualSection() SmokingSection() } diff --git a/Intake/Surgery/SurgeryView.swift b/Intake/Surgery/SurgeryView.swift index db22b61..2a41147 100644 --- a/Intake/Surgery/SurgeryView.swift +++ b/Intake/Surgery/SurgeryView.swift @@ -60,15 +60,12 @@ struct InspectSurgeryView: View { Section(header: Text("Procedure")) { TextField("", text: $surgery.surgeryName) } - // Date Section(header: Text("Performed")) { TextField("YYYY-MM-DD", text: $surgery.date) } - // Status Section(header: Text("Status")) { TextField("", text: $surgery.status) } - // Location Section(header: Text("Location")) { TextField("", text: $surgery.location) }