Skip to content

Commit

Permalink
Llmchat/finalize (#40)
Browse files Browse the repository at this point in the history
Final LLM Chat UI with Greeting and Function Calling.

## ♻️ Current situation & Problem
The basic LLM chat infrastructure was done, but it did not include a
welcome greeting and did not have function calling capabilities to
summarize the chat into a concise chief complaint.


## ⚙️ Release Notes 
Added function calling to the LLMInteraction struct's LLM model
(currently commented out, as the function is not yet complete), and
enabled a greeting message upon opening the app. Fine tuned the system
prompt.


## 📚 Documentation
NA


## ✅ Testing
NA


## 📝 Code of Conduct & Contributing Guidelines 

By submitting creating this pull request, you agree to follow our [Code
of
Conduct](https://github.com/CS342/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/CS342/.github/blob/main/CONTRIBUTING.md):
- [X] I agree to follow the [Code of
Conduct](https://github.com/CS342/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/CS342/.github/blob/main/CONTRIBUTING.md).

---------

Co-authored-by: Nina Boord <[email protected]>
  • Loading branch information
nriedman and ninaboord authored Feb 3, 2024
1 parent 974a1e0 commit 4c61cbe
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 21 deletions.
4 changes: 4 additions & 0 deletions Intake.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
F42AB1DC2B637C8C002E13A6 /* LLMOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42AB1DB2B637C8C002E13A6 /* LLMOnboardingView.swift */; };
F42AB1DF2B637C9D002E13A6 /* LLMInteraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42AB1DE2B637C9C002E13A6 /* LLMInteraction.swift */; };
F42AB1E52B6383F9002E13A6 /* LLMOpenAITokenOnboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42AB1E42B6383F9002E13A6 /* LLMOpenAITokenOnboarding.swift */; };
F42AB1EC2B6DBF21002E13A6 /* SummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42AB1EB2B6DBF20002E13A6 /* SummaryView.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -152,6 +153,7 @@
F42AB1DB2B637C8C002E13A6 /* LLMOnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LLMOnboardingView.swift; sourceTree = "<group>"; };
F42AB1DE2B637C9C002E13A6 /* LLMInteraction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LLMInteraction.swift; sourceTree = "<group>"; };
F42AB1E42B6383F9002E13A6 /* LLMOpenAITokenOnboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LLMOpenAITokenOnboarding.swift; sourceTree = "<group>"; };
F42AB1EB2B6DBF20002E13A6 /* SummaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SummaryView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -389,6 +391,7 @@
F42AB1E12B637CAC002E13A6 /* OpenAI */,
F42AB1DB2B637C8C002E13A6 /* LLMOnboardingView.swift */,
F42AB1DE2B637C9C002E13A6 /* LLMInteraction.swift */,
F42AB1EB2B6DBF20002E13A6 /* SummaryView.swift */,
);
path = ChiefComplaint;
sourceTree = "<group>";
Expand Down Expand Up @@ -638,6 +641,7 @@
56F6F2A02AB441930022FE5A /* ContributionsList.swift in Sources */,
566155292AB8447C00209B80 /* Package+LicenseType.swift in Sources */,
5680DD392AB8983D004E6D4A /* PackageCell.swift in Sources */,
F42AB1EC2B6DBF21002E13A6 /* SummaryView.swift in Sources */,
2F5E32BD297E05EA003432F8 /* IntakeDelegate.swift in Sources */,
2FE5DC5229EDD7FA004B9AB4 /* IntakeScheduler.swift in Sources */,
F42AB1E52B6383F9002E13A6 /* LLMOpenAITokenOnboarding.swift in Sources */,
Expand Down
84 changes: 64 additions & 20 deletions Intake/ChiefComplaint/LLMInteraction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,77 @@
// SPDX-License-Identifier: MIT
//

import SpeziChat
import SpeziLLM
import SpeziLLMLocal
import SpeziLLMOpenAI
import SwiftUI

struct LLMInteraction: View {
struct SummarizeFunction: LLMFunction {
static let name: String = "summarize_complaint"
static let description: String = """
When there is enough information to give to the doctor,\
summarize the conversation into a concise Chief Complaint.
"""
@Parameter(description: "The primary medical concern that the patient is experiencing.") var medicalConcern: String

@Parameter(description: "The severity of the primary medical concern.") var severity: String

@Parameter(description: "The duration of the primary medical concern.") var duration: String

static let desc: String = """
Extra important information relevant to the primary\
medical concern that the doctor should be aware of.
"""
@Parameter(description: desc) var supplementaryInfo: String

@Binding var chiefComplaint: String

func execute() async throws -> String? {
let summary = """
Here is the summary that will be provided to your doctor:\n
Primary concern: \(medicalConcern)\n
Severity: \(severity)\n
Duration: \(duration)\n
Extra Info: \(supplementaryInfo)\n
"""
chiefComplaint = summary
return summary
}
}

@State private var chiefComplaint: String? = "blah blah blah"
@State private var shouldNavigateToSummaryView = true
@Binding var presentingAccount: Bool
@Environment(LLMRunner.self) var runner: LLMRunner

@State var responseText: String
@State var showOnboarding = true

@State var model: LLM = LLMOpenAI(
parameters: .init(
modelType: .gpt3_5Turbo,
systemPrompt: """
You are acting as an intake person at a clinic and need to work with\
the patient to help clarify their chief complaint into a concise,\
specific complaint which includes elements of laterality if\
appropriate, as well as severity and duration.\
specific complaint.
You should always ask about severity and duration if the patient does not include this information.
Additionally, help guide the patient into providing information specific to the condition that the define.\
For example, if the patient is experiencing leg pain, you should prompt them to be more\
specific about laterality and location. You should also ask if the pain is dull or sharp,\
and encourage them to rate their pain on a scale of 1 to 10. For a cough, for example, you\
should inquire whether the cough is wet or dry, as well as any other characteristics of the\
cough that might allow a doctor to rule out diagnoses.
Please use everyday layman terms and avoid using complex medical terminology.\
Only ask one question or prompt at a time, and keep your responses brief (one to two short sentences).
"""
)
)
) {
// SummarizeFunction()
}

var body: some View {
NavigationStack {
Expand All @@ -51,29 +97,27 @@ struct LLMInteraction: View {
.sheet(isPresented: $showOnboarding) {
LLMOnboardingView(showOnboarding: $showOnboarding)
}
}
}

func executePrompt(prompt: String) async {
// Execute the query on the runner, returning a stream of outputs
let stream = try? await runner(with: model).generate(prompt: prompt)

if let unwrappedStream = stream {
do {
for try await token in unwrappedStream {
responseText.append(token)
.onAppear {
let assistantMessage = ChatEntity(role: .assistant, content: "Hello! What brings you to the doctor's office?")
model.context.insert(assistantMessage, at: 0)
}
.onChange(of: chiefComplaint) { _, newChiefComplaint in
if let newChiefComplaint = newChiefComplaint {
shouldNavigateToSummaryView = true
}
} catch {
// Handle any errors that occurred during the asynchronous operation
print("Error: \(error)")
}
NavigationLink(
destination: SummaryView(chiefComplaint: chiefComplaint ?? "No Chief Complaint"),
label: {
EmptyView()
}
)
}
}
}


#Preview {
LLMInteraction(presentingAccount: .constant(true), responseText: "Test")
LLMInteraction(presentingAccount: .constant(true))
.previewWith {
LLMRunner {
LLMOpenAIRunnerSetupTask()
Expand Down
28 changes: 28 additions & 0 deletions Intake/ChiefComplaint/SummaryView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// SummaryView.swift
// Intake
//
// Created by Nick Riedman on 2/2/24.
//
// This source file is part of the Intake based on the Stanford Spezi Template Application project
//
// SPDX-FileCopyrightText: 2023 Stanford University
//
// SPDX-License-Identifier: MIT
//

import Foundation
import SwiftUI

struct SummaryView: View {
let chiefComplaint: String

var body: some View {
VStack {
Text(chiefComplaint)
.padding()
.multilineTextAlignment(.center)
}
.navigationTitle("Summary")
}
}
2 changes: 1 addition & 1 deletion Intake/Home.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ struct HomeView: View {
Label("MOCK_WEB_SERVICE_TAB_TITLE", systemImage: "server.rack")
}
}
LLMInteraction(presentingAccount: $presentingAccount, responseText: "")
LLMInteraction(presentingAccount: $presentingAccount)
.tag(Tabs.form)
.tabItem {
Label("Create Form", systemImage: "captions.bubble.fill")
Expand Down
3 changes: 3 additions & 0 deletions Intake/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,9 @@
},
"Summarize your surgical history." : {

},
"Summary" : {

},
"Surgical History" : {

Expand Down

0 comments on commit 4c61cbe

Please sign in to comment.