From f4c8729cc3560e71e5346ee42e0575c56ba699ba Mon Sep 17 00:00:00 2001 From: Philipp Zagar Date: Sun, 25 Feb 2024 22:46:57 -0800 Subject: [PATCH] Last adjustments --- Package.swift | 13 +- .../Resources/Localizable.xcstrings | 190 ---- .../FHIRGetResourceLLMFunction.swift | 24 +- .../FHIRMultipleResourceInterpreter.swift | 43 +- .../MultipleResourcesChatView.swift | 22 +- .../FHIRResourceInterpreter.swift | 8 + .../FHIRInterpretationModule.swift | 60 +- .../FHIRProcessor/FHIRResourceProcessor.swift | 13 +- .../FHIRResourceProcessorError.swift | 0 .../FHIRSummary/FHIRResourceSummary.swift | 8 + .../FHIRSummary/FHIRResourceSummaryView.swift | 7 +- .../SpeziFHIRLLM/Helpers/Binding+Negate.swift | 20 + .../Helpers/FHIRResource+Identifier.swift | 2 +- .../Helpers/FHIRStore+Interpretation.swift | 18 +- .../Resources/Localizable.xcstrings | 876 ++++++++++++++++++ .../Resources/Localizable.xcstrings.license | 0 .../Settings/FHIRPrompt.swift | 2 +- .../Settings/FHIRPromptSettingsView.swift | 2 +- .../Views/FHIRResourcesView.swift | 30 +- .../Views/InspectResourceView.swift | 23 +- .../FHIRStore+TestingSupport.swift | 3 +- ...8-56a5-35fd-c37f-466ff119c625.json.license | 2 +- ...e-a2d0-d016-0839-bab3757c4c58.json.license | 2 +- ...a-d3b7-2198-3898-51f9153d023d.json.license | 2 +- ...f-30b2-acb7-658a-8b340dadd685.json.license | 2 +- ...a-22a7-d166-7bb1-63f6bbce1a32.json.license | 2 +- ...8-06cb-fc8a-8c13-85685b6ac939.json.license | 2 +- 27 files changed, 1062 insertions(+), 314 deletions(-) delete mode 100644 Sources/SpeziFHIRInterpretation/Resources/Localizable.xcstrings rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/FHIRInterpretation/MultipleResources/FHIRGetResourceLLMFunction.swift (80%) rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/FHIRInterpretation/MultipleResources/FHIRMultipleResourceInterpreter.swift (67%) rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/FHIRInterpretation/MultipleResources/MultipleResourcesChatView.swift (83%) rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/FHIRInterpretation/SingleResource/FHIRResourceInterpreter.swift (91%) rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/FHIRInterpretationModule.swift (72%) rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/FHIRProcessor/FHIRResourceProcessor.swift (81%) rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/FHIRProcessor/FHIRResourceProcessorError.swift (100%) rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/FHIRSummary/FHIRResourceSummary.swift (92%) rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/FHIRSummary/FHIRResourceSummaryView.swift (92%) create mode 100644 Sources/SpeziFHIRLLM/Helpers/Binding+Negate.swift rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/Helpers/FHIRResource+Identifier.swift (90%) rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/Helpers/FHIRStore+Interpretation.swift (87%) create mode 100644 Sources/SpeziFHIRLLM/Resources/Localizable.xcstrings rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/Resources/Localizable.xcstrings.license (100%) rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/Settings/FHIRPrompt.swift (97%) rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/Settings/FHIRPromptSettingsView.swift (97%) rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/Views/FHIRResourcesView.swift (96%) rename Sources/{SpeziFHIRInterpretation => SpeziFHIRLLM}/Views/InspectResourceView.swift (87%) diff --git a/Package.swift b/Package.swift index f29b0b4..65e6abe 100644 --- a/Package.swift +++ b/Package.swift @@ -20,18 +20,17 @@ let package = Package( products: [ .library(name: "SpeziFHIR", targets: ["SpeziFHIR"]), .library(name: "SpeziFHIRHealthKit", targets: ["SpeziFHIRHealthKit"]), - .library(name: "SpeziFHIRInterpretation", targets: ["SpeziFHIRInterpretation"]), + .library(name: "SpeziFHIRLLM", targets: ["SpeziFHIRLLM"]), .library(name: "SpeziFHIRMockPatients", targets: ["SpeziFHIRMockPatients"]) ], dependencies: [ .package(url: "https://github.com/apple/FHIRModels", .upToNextMinor(from: "0.5.0")), .package(url: "https://github.com/StanfordBDHG/HealthKitOnFHIR", .upToNextMinor(from: "0.2.4")), - .package(url: "https://github.com/StanfordSpezi/Spezi", from: "1.1.0"), - .package(url: "https://github.com/StanfordSpezi/SpeziHealthKit.git", .upToNextMinor(from: "0.5.0")), - //.package(url: "https://github.com/StanfordSpezi/SpeziLLM.git", branch: "feat/structural-improvments"), - .package(path: "../SpeziLLM"), + .package(url: "https://github.com/StanfordSpezi/Spezi", from: "1.2.1"), + .package(url: "https://github.com/StanfordSpezi/SpeziHealthKit.git", .upToNextMinor(from: "0.5.1")), + .package(url: "https://github.com/StanfordSpezi/SpeziLLM.git", .upToNextMinor(from: "0.7.0")), .package(url: "https://github.com/StanfordSpezi/SpeziStorage.git", from: "1.0.0"), - .package(url: "https://github.com/StanfordSpezi/SpeziChat.git", .upToNextMinor(from: "0.1.4")), + .package(url: "https://github.com/StanfordSpezi/SpeziChat.git", .upToNextMinor(from: "0.1.8")), .package(url: "https://github.com/StanfordSpezi/SpeziSpeech.git", from: "1.0.0") ], targets: [ @@ -53,7 +52,7 @@ let package = Package( ] ), .target( - name: "SpeziFHIRInterpretation", + name: "SpeziFHIRLLM", dependencies: [ .target(name: "SpeziFHIR"), .product(name: "Spezi", package: "Spezi"), diff --git a/Sources/SpeziFHIRInterpretation/Resources/Localizable.xcstrings b/Sources/SpeziFHIRInterpretation/Resources/Localizable.xcstrings deleted file mode 100644 index db76119..0000000 --- a/Sources/SpeziFHIRInterpretation/Resources/Localizable.xcstrings +++ /dev/null @@ -1,190 +0,0 @@ -{ - "sourceLanguage" : "en", - "strings" : { - "Close" : { - - }, - "Conditions" : { - - }, - "Could not create FHIR Summary" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Could not create FHIR Summary" - } - } - } - }, - "Create Resource Summary" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Create Resource Summary" - } - } - } - }, - "Customize the %@." : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Customize the %@." - } - } - } - }, - "Diagnostics" : { - - }, - "Encounters" : { - - }, - "FHIR_RESOURCES_EMPTY_SEARCH_MESSAGE" : { - - }, - "FHIR_RESOURCES_INTERPRETATION_BUTTON" : { - - }, - "FHIR_RESOURCES_INTERPRETATION_LOADING" : { - - }, - "FHIR_RESOURCES_INTERPRETATION_RESOURCE" : { - - }, - "FHIR_RESOURCES_INTERPRETATION_SECTION" : { - - }, - "FHIR_RESOURCES_SUMMARY_BUTTON" : { - - }, - "FHIR_RESOURCES_SUMMARY_SECTION" : { - - }, - "FUNCTION_DESCRIPTION" : { - - }, - "Immunizations" : { - - }, - "Interpretation Prompt" : { - "comment" : "Title of the interpretation prompt.\nTitle of the multiple resources interpretation prompt.", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Interpretation Prompt" - } - } - } - }, - "Interpretation Prompt Content" : { - "comment" : "Content of the interpretation prompt.\nContent of the multiple resources interpretation prompt.", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Your task is to interpret the following FHIR resource from the user's clinical record. You should provide the title and summary in the following locale: {{LOCALE}}.\n\nInterpret the resource by explaining its data relevant to the user's health.\nExplain the relevant medical context in a language understandable by a user who is not a medical professional.\nYou should provide factual and precise information in a compact summary in short responses.\n\nImmediately return an interpretation to the user, starting the conversation.\nDo not introduce yourself at the beginning, and start with your interpretation.\n\nThe following JSON representation defines the FHIR resource that you should provide an interpretation for:\n\n{{FHIR_RESOURCE}}" - } - } - } - }, - "Medications" : { - - }, - "Observations" : { - - }, - "Other Resources" : { - - }, - "PARAMETER_DESCRIPTION" : { - - }, - "Place %@ at the position in the prompt where the FHIR resource should be inserted. Optionally place %@ where you would like to insert the current locale." : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Place %1$@ at the position in the prompt where the FHIR resource should be inserted. Optionally place %2$@ where you would like to insert the current locale." - } - } - } - }, - "Procedures" : { - - }, - "Reset Chat" : { - - }, - "Save Prompt" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Save Prompt" - } - } - } - }, - "Summary Prompt" : { - "comment" : "Title of the summary prompt.", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Summary Prompt" - } - } - } - }, - "Summary Prompt Content" : { - "comment" : "Content of the summary prompt.", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Your task is to private a title and compact summary for an FHIR resource from the user's clinical record. You should provide the title and summary in the following locale: {{LOCALE}}.\n\nYour response should contain two lines without headings, markdown formatting, or any other structure beyond two lines. Directly provide the content without any additional structure or an introduction. Another computer program will parse the output.\n\n1. Line: A 1-5 Word summary of the FHIR resource that immediately identifies the resource and provides the essential information at a glance. Do NOT use a complete sentence; instead, use a formatting typically used for titles in computer systems.\n\n2. Line: Provide a short one-sentence summary of the resource in less than 100 words. Ensure that the summary only focuses on the essential information that a patient would need and, e.g., excludes the person who prescribed a medication or similar metadata. Ensure that all clinically relevant data is included, which allows us to use the summary in additional prompts where using the complete JSON might not be feasible.\n\nThe following JSON representation defines the FHIR resource that you should provide a title and summary for:\n\n{{FHIR_RESOURCE}}" - } - } - } - }, - "Text to speech is disabled, press to enable text to speech." : { - - }, - "Text to speech is enabled, press to disable text to speech." : { - - }, - "The medical record does not include any FHIR resources for the search term %@." : { - - }, - "This is the summary of the requested %@:\n\n%@" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "This is the summary of the requested %1$@:\n\n%2$@" - } - } - } - }, - "Total Number of Resources: %lld" : { - - }, - "Unable to parse result of the LLM prompt." : { - "comment" : "Error thrown if the result can not be parsed in the underlying type.", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Unable to parse result of the LLM prompt." - } - } - } - } - }, - "version" : "1.0" -} \ No newline at end of file diff --git a/Sources/SpeziFHIRInterpretation/FHIRInterpretation/MultipleResources/FHIRGetResourceLLMFunction.swift b/Sources/SpeziFHIRLLM/FHIRInterpretation/MultipleResources/FHIRGetResourceLLMFunction.swift similarity index 80% rename from Sources/SpeziFHIRInterpretation/FHIRInterpretation/MultipleResources/FHIRGetResourceLLMFunction.swift rename to Sources/SpeziFHIRLLM/FHIRInterpretation/MultipleResources/FHIRGetResourceLLMFunction.swift index ad0791e..012026b 100644 --- a/Sources/SpeziFHIRInterpretation/FHIRInterpretation/MultipleResources/FHIRGetResourceLLMFunction.swift +++ b/Sources/SpeziFHIRLLM/FHIRInterpretation/MultipleResources/FHIRGetResourceLLMFunction.swift @@ -1,5 +1,5 @@ // -// This source file is part of the Stanford LLM on FHIR project +// This source file is part of the Stanford Spezi project // // SPDX-FileCopyrightText: 2023 Stanford University // @@ -19,18 +19,32 @@ struct FHIRGetResourceLLMFunction: LLMFunction { private let fhirStore: FHIRStore private let resourceSummary: FHIRResourceSummary - private let allResourcesFunctionCallIdentifier: [String] @Parameter var resources: [String] - init(fhirStore: FHIRStore, resourceSummary: FHIRResourceSummary, allResourcesFunctionCallIdentifier: [String]) { + init( + fhirStore: FHIRStore, + resourceSummary: FHIRResourceSummary, + resourceCountLimit: Int, + allowedResourcesFunctionCallIdentifiers: Set? = nil // swiftlint:disable:this discouraged_optional_collection + ) { self.fhirStore = fhirStore self.resourceSummary = resourceSummary - self.allResourcesFunctionCallIdentifier = allResourcesFunctionCallIdentifier - _resources = Parameter(description: String(localized: "PARAMETER_DESCRIPTION"), enumValues: allResourcesFunctionCallIdentifier) + // Only take newest values of the health records + var allResourcesFunctionCallIdentifiers = Set(fhirStore.allResourcesFunctionCallIdentifier.suffix(resourceCountLimit)) + + // If identifiers are restricted, filter for only allowed function call identifiers of health records. + if let allowedResourcesFunctionCallIdentifiers { + allResourcesFunctionCallIdentifiers.formIntersection(allowedResourcesFunctionCallIdentifiers) + } + + _resources = Parameter( + description: String(localized: "PARAMETER_DESCRIPTION"), + enumValues: Array(allResourcesFunctionCallIdentifiers) + ) } diff --git a/Sources/SpeziFHIRInterpretation/FHIRInterpretation/MultipleResources/FHIRMultipleResourceInterpreter.swift b/Sources/SpeziFHIRLLM/FHIRInterpretation/MultipleResources/FHIRMultipleResourceInterpreter.swift similarity index 67% rename from Sources/SpeziFHIRInterpretation/FHIRInterpretation/MultipleResources/FHIRMultipleResourceInterpreter.swift rename to Sources/SpeziFHIRLLM/FHIRInterpretation/MultipleResources/FHIRMultipleResourceInterpreter.swift index 116646b..b637c16 100644 --- a/Sources/SpeziFHIRInterpretation/FHIRInterpretation/MultipleResources/FHIRMultipleResourceInterpreter.swift +++ b/Sources/SpeziFHIRLLM/FHIRInterpretation/MultipleResources/FHIRMultipleResourceInterpreter.swift @@ -1,5 +1,5 @@ // -// This source file is part of the Stanford LLM on FHIR project +// This source file is part of the Stanford Spezi project // // SPDX-FileCopyrightText: 2023 Stanford University // @@ -22,13 +22,14 @@ private enum FHIRMultipleResourceInterpreterConstants { } +/// Used to interpret multiple FHIR resources via a chat-based interface with an LLM. @Observable -class FHIRMultipleResourceInterpreter { +public class FHIRMultipleResourceInterpreter { static let logger = Logger(subsystem: "edu.stanford.spezi.fhir", category: "SpeziFHIRInterpretation") private let localStorage: LocalStorage private let llmRunner: LLMRunner - private let llmSchema: any LLMSchema + private var llmSchema: any LLMSchema private let fhirStore: FHIRStore var llm: (any LLMSession)? @@ -64,7 +65,7 @@ class FHIRMultipleResourceInterpreter { return } - var llm = await llmRunner(with: llmSchema) + let llm = llmRunner(with: llmSchema) // Read initial conversation from storage if let storedContext: Chat = try? localStorage.read(storageKey: FHIRMultipleResourceInterpreterConstants.chat) { llm.context = storedContext @@ -81,7 +82,7 @@ class FHIRMultipleResourceInterpreter { @MainActor func queryLLM() { guard let llm, - llm.context.last?.role == .user || !(llm.context.contains(where: { $0.role == .assistant }) ?? false) else { + llm.context.last?.role == .user || !(llm.context.contains(where: { $0.role == .assistant }) ) else { return } @@ -100,6 +101,34 @@ class FHIRMultipleResourceInterpreter { try localStorage.store(llm.context, storageKey: FHIRMultipleResourceInterpreterConstants.chat) } } + + /// Change the `LLMSchema` used by the ``FHIRMultipleResourceInterpreter``. + public func changeLLMSchema( + openAIModel model: LLMOpenAIModelType, + resourceCountLimit: Int, + resourceSummary: FHIRResourceSummary, + allowedResourcesFunctionCallIdentifiers: Set? = nil // swiftlint:disable:this discouraged_optional_collection + ) { + self.llmSchema = LLMOpenAISchema( + parameters: .init( + modelType: model, + systemPrompts: [] // No system prompt as this will be determined later by the resource interpreter + ) + ) { + // FHIR interpretation function + FHIRGetResourceLLMFunction( + fhirStore: self.fhirStore, + resourceSummary: resourceSummary, + resourceCountLimit: resourceCountLimit, + allowedResourcesFunctionCallIdentifiers: allowedResourcesFunctionCallIdentifiers + ) + } + self.llm = nil + + Task { @MainActor in + await prepareLLM() + } + } } @@ -112,10 +141,12 @@ extension FHIRPrompt { storageKey: "prompt.interpretMultipleResources", localizedDescription: String( localized: "Interpretation Prompt", + bundle: .module, comment: "Title of the multiple resources interpretation prompt." ), defaultPrompt: String( - localized: "Interpretation Prompt Content", + localized: "Multiple Resource Interpretation Prompt Content", + bundle: .module, comment: "Content of the multiple resources interpretation prompt." ) ) diff --git a/Sources/SpeziFHIRInterpretation/FHIRInterpretation/MultipleResources/MultipleResourcesChatView.swift b/Sources/SpeziFHIRLLM/FHIRInterpretation/MultipleResources/MultipleResourcesChatView.swift similarity index 83% rename from Sources/SpeziFHIRInterpretation/FHIRInterpretation/MultipleResources/MultipleResourcesChatView.swift rename to Sources/SpeziFHIRLLM/FHIRInterpretation/MultipleResources/MultipleResourcesChatView.swift index 610a254..114e86c 100644 --- a/Sources/SpeziFHIRInterpretation/FHIRInterpretation/MultipleResources/MultipleResourcesChatView.swift +++ b/Sources/SpeziFHIRLLM/FHIRInterpretation/MultipleResources/MultipleResourcesChatView.swift @@ -1,5 +1,5 @@ // -// This source file is part of the Stanford LLM on FHIR project +// This source file is part of the Stanford Spezi project // // SPDX-FileCopyrightText: 2023 Stanford University // @@ -33,8 +33,10 @@ public struct MultipleResourcesChatView: View { contextBinding, disableInput: llm.state.representation == .processing ) + .speak(llm.context, muted: !textToSpeech) + .speechToolbarButton(muted: !$textToSpeech) .viewStateAlert(state: llm.state) - .onChange(of: llm.context, initial: true) { old, new in + .onChange(of: llm.context, initial: true) { _, _ in if llm.state != .generating { multipleResourceInterpreter.queryLLM() } @@ -67,22 +69,6 @@ public struct MultipleResourcesChatView: View { } } } - ToolbarItem(placement: .primaryAction) { - Button( - action: { - textToSpeech.toggle() - }, - label: { - if textToSpeech { - Image(systemName: "speaker") - .accessibilityLabel(Text("Text to speech is enabled, press to disable text to speech.")) - } else { - Image(systemName: "speaker.slash") - .accessibilityLabel(Text("Text to speech is disabled, press to enable text to speech.")) - } - } - ) - } ToolbarItem(placement: .primaryAction) { Button( action: { diff --git a/Sources/SpeziFHIRInterpretation/FHIRInterpretation/SingleResource/FHIRResourceInterpreter.swift b/Sources/SpeziFHIRLLM/FHIRInterpretation/SingleResource/FHIRResourceInterpreter.swift similarity index 91% rename from Sources/SpeziFHIRInterpretation/FHIRInterpretation/SingleResource/FHIRResourceInterpreter.swift rename to Sources/SpeziFHIRLLM/FHIRInterpretation/SingleResource/FHIRResourceInterpreter.swift index 3d99f7a..0bbd1b9 100644 --- a/Sources/SpeziFHIRInterpretation/FHIRInterpretation/SingleResource/FHIRResourceInterpreter.swift +++ b/Sources/SpeziFHIRLLM/FHIRInterpretation/SingleResource/FHIRResourceInterpreter.swift @@ -50,6 +50,14 @@ public class FHIRResourceInterpreter { public func cachedInterpretation(forResource resource: FHIRResource) -> String? { resourceProcessor.results[resource.id] } + + /// Adjust the LLM schema used by the ``FHIRResourceInterpreter``. + /// + /// - Parameters: + /// - schema: The to-be-used `LLMSchema`. + public func changeLLMSchema(to schema: Schema) { + self.resourceProcessor.llmSchema = schema + } } diff --git a/Sources/SpeziFHIRInterpretation/FHIRInterpretationModule.swift b/Sources/SpeziFHIRLLM/FHIRInterpretationModule.swift similarity index 72% rename from Sources/SpeziFHIRInterpretation/FHIRInterpretationModule.swift rename to Sources/SpeziFHIRLLM/FHIRInterpretationModule.swift index 81d1a95..2784347 100644 --- a/Sources/SpeziFHIRInterpretation/FHIRInterpretationModule.swift +++ b/Sources/SpeziFHIRLLM/FHIRInterpretationModule.swift @@ -1,5 +1,5 @@ // -// This source file is part of the Stanford LLM on FHIR project +// This source file is part of the Stanford Spezi project // // SPDX-FileCopyrightText: 2023 Stanford University // @@ -20,7 +20,7 @@ public class FHIRInterpretationModule: Module, DefaultInitializable { .init( parameters: .init( modelType: .gpt4_turbo_preview, - systemPrompt: nil // No system prompt as this will be determined later by the resource interpreter + systemPrompts: [] // No system prompt as this will be determined later by the resource interpreter ) ) } @@ -35,11 +35,36 @@ public class FHIRInterpretationModule: Module, DefaultInitializable { @Model private var resourceInterpreter: FHIRResourceInterpreter @Model private var multipleResourceInterpreter: FHIRMultipleResourceInterpreter - // TODO: Adjust this to make it configurable after the fact - // UserDefaults.standard.string(forKey: StorageKeys.openAIModel) ?? StorageKeys.Defaults.openAIModel let summaryLLMSchema: any LLMSchema let interpretationLLMSchema: any LLMSchema let openAIModelType: LLMOpenAIModelType + let resourceCountLimit: Int + let allowedResourcesFunctionCallIdentifiers: Set? // swiftlint:disable:this discouraged_optional_collection + + + /// - Warning: Ensure that passed LLM schema's don't contain a system prompt! This will be configured by the ``FHIRInterpretationModule``. + public init( // swiftlint:disable:this function_default_parameter_at_end + summaryLLMSchema: SummaryLLM = Defaults.llmSchema, + interpretationLLMSchema: InterpretationLLM = Defaults.llmSchema, + multipleResourceInterpretationOpenAIModel: LLMOpenAIModelType, // swiftlint:disable:this identifier_name + resourceCountLimit: Int = 250, + allowedResourcesFunctionCallIdentifiers: Set? = nil // swiftlint:disable:this discouraged_optional_collection + ) { + self.summaryLLMSchema = summaryLLMSchema + self.interpretationLLMSchema = interpretationLLMSchema + self.openAIModelType = multipleResourceInterpretationOpenAIModel + self.resourceCountLimit = resourceCountLimit + self.allowedResourcesFunctionCallIdentifiers = allowedResourcesFunctionCallIdentifiers + } + + + public required convenience init() { + self.init( + summaryLLMSchema: Defaults.llmSchema, + interpretationLLMSchema: Defaults.llmSchema, + multipleResourceInterpretationOpenAIModel: .gpt4_turbo_preview + ) + } public func configure() { @@ -61,39 +86,18 @@ public class FHIRInterpretationModule: Module, DefaultInitializable { llmSchema: LLMOpenAISchema( parameters: .init( modelType: openAIModelType, - systemPrompt: nil // No system prompt as this will be determined later by the resource interpreter + systemPrompts: [] // No system prompt as this will be determined later by the resource interpreter ) ) { // FHIR interpretation function FHIRGetResourceLLMFunction( fhirStore: self.fhirStore, resourceSummary: self.resourceSummary, - allResourcesFunctionCallIdentifier: self.fhirStore.allResourcesFunctionCallIdentifier + resourceCountLimit: self.resourceCountLimit, + allowedResourcesFunctionCallIdentifiers: self.allowedResourcesFunctionCallIdentifiers ) }, fhirStore: fhirStore ) } - - - // TODO: The multipleResource LLM is always fixed to OpenAI itself because of the function calls - // Ensure that passed schema's don't contain a system prompt! - public init( - summaryLLMSchema: SummaryLLM = Defaults.llmSchema, - interpretationLLMSchema: InterpretationLLM = Defaults.llmSchema, - multipleResourceInterpretationOpenAIModel: LLMOpenAIModelType - ) { - self.summaryLLMSchema = summaryLLMSchema - self.interpretationLLMSchema = interpretationLLMSchema - self.openAIModelType = multipleResourceInterpretationOpenAIModel - } - - - public required convenience init() { - self.init( - summaryLLMSchema: Defaults.llmSchema, - interpretationLLMSchema: Defaults.llmSchema, - multipleResourceInterpretationOpenAIModel: .gpt4_turbo_preview - ) - } } diff --git a/Sources/SpeziFHIRInterpretation/FHIRProcessor/FHIRResourceProcessor.swift b/Sources/SpeziFHIRLLM/FHIRProcessor/FHIRResourceProcessor.swift similarity index 81% rename from Sources/SpeziFHIRInterpretation/FHIRProcessor/FHIRResourceProcessor.swift rename to Sources/SpeziFHIRLLM/FHIRProcessor/FHIRResourceProcessor.swift index 571349b..0fc419a 100644 --- a/Sources/SpeziFHIRInterpretation/FHIRProcessor/FHIRResourceProcessor.swift +++ b/Sources/SpeziFHIRLLM/FHIRProcessor/FHIRResourceProcessor.swift @@ -19,9 +19,9 @@ class FHIRResourceProcessor { private let localStorage: LocalStorage private let llmRunner: LLMRunner - private let llmSchema: any LLMSchema private let storageKey: String private let prompt: FHIRPrompt + var llmSchema: any LLMSchema var results: Results = [:] { @@ -57,17 +57,12 @@ class FHIRResourceProcessor { return result } - let chatStreamResults = try await llmRunner.oneShot( + let chatStreamResult: String = try await llmRunner.oneShot( with: llmSchema, - chat: .init(systemMessages: prompt.prompt(withFHIRResource: resource.jsonDescription)) + chat: .init(systemMessages: [prompt.prompt(withFHIRResource: resource.jsonDescription)]) ) - var result = "" - for try await chatStreamResult in chatStreamResults { - result.append(chatStreamResult) - } - - guard let content = Content(result) else { + guard let content = Content(chatStreamResult) else { throw FHIRResourceProcessorError.notParsableAsAString } diff --git a/Sources/SpeziFHIRInterpretation/FHIRProcessor/FHIRResourceProcessorError.swift b/Sources/SpeziFHIRLLM/FHIRProcessor/FHIRResourceProcessorError.swift similarity index 100% rename from Sources/SpeziFHIRInterpretation/FHIRProcessor/FHIRResourceProcessorError.swift rename to Sources/SpeziFHIRLLM/FHIRProcessor/FHIRResourceProcessorError.swift diff --git a/Sources/SpeziFHIRInterpretation/FHIRSummary/FHIRResourceSummary.swift b/Sources/SpeziFHIRLLM/FHIRSummary/FHIRResourceSummary.swift similarity index 92% rename from Sources/SpeziFHIRInterpretation/FHIRSummary/FHIRResourceSummary.swift rename to Sources/SpeziFHIRLLM/FHIRSummary/FHIRResourceSummary.swift index 4dcabe0..c5b3e13 100644 --- a/Sources/SpeziFHIRInterpretation/FHIRSummary/FHIRResourceSummary.swift +++ b/Sources/SpeziFHIRLLM/FHIRSummary/FHIRResourceSummary.swift @@ -75,6 +75,14 @@ public class FHIRResourceSummary { public func cachedSummary(forResource resource: FHIRResource) -> Summary? { resourceProcessor.results[resource.id] } + + /// Adjust the LLM schema used by the ``FHIRResourceSummary``. + /// + /// - Parameters: + /// - schema: The to-be-used `LLMSchema`. + public func changeLLMSchema(to schema: Schema) { + self.resourceProcessor.llmSchema = schema + } } diff --git a/Sources/SpeziFHIRInterpretation/FHIRSummary/FHIRResourceSummaryView.swift b/Sources/SpeziFHIRLLM/FHIRSummary/FHIRResourceSummaryView.swift similarity index 92% rename from Sources/SpeziFHIRInterpretation/FHIRSummary/FHIRResourceSummaryView.swift rename to Sources/SpeziFHIRLLM/FHIRSummary/FHIRResourceSummaryView.swift index 9111310..332904d 100644 --- a/Sources/SpeziFHIRInterpretation/FHIRSummary/FHIRResourceSummaryView.swift +++ b/Sources/SpeziFHIRLLM/FHIRSummary/FHIRResourceSummaryView.swift @@ -16,14 +16,12 @@ public struct FHIRResourceSummaryView: View { @Environment(FHIRResourceSummary.self) private var fhirResourceSummary @State private var viewState: ViewState = .idle - @State private var cachedSummary: FHIRResourceSummary.Summary? - private let resource: FHIRResource public var body: some View { Group { - if let summary = cachedSummary { + if let summary = fhirResourceSummary.cachedSummary(forResource: resource) { VStack(alignment: .leading, spacing: 0) { Text(summary.title) if let date = resource.date { @@ -59,9 +57,6 @@ public struct FHIRResourceSummaryView: View { } } .viewStateAlert(state: $viewState) - .task { - cachedSummary = fhirResourceSummary.cachedSummary(forResource: resource) - } } diff --git a/Sources/SpeziFHIRLLM/Helpers/Binding+Negate.swift b/Sources/SpeziFHIRLLM/Helpers/Binding+Negate.swift new file mode 100644 index 0000000..2708aa6 --- /dev/null +++ b/Sources/SpeziFHIRLLM/Helpers/Binding+Negate.swift @@ -0,0 +1,20 @@ +// +// This source file is part of the Stanford Spezi project +// +// SPDX-FileCopyrightText: 2023 Stanford University +// +// SPDX-License-Identifier: MIT +// + +import SwiftUI + + +extension Binding where Value == Bool { + /// Negates a `Binding`. + public prefix static func ! (value: Binding) -> Binding { + Binding( + get: { !value.wrappedValue }, + set: { value.wrappedValue = !$0 } + ) + } +} diff --git a/Sources/SpeziFHIRInterpretation/Helpers/FHIRResource+Identifier.swift b/Sources/SpeziFHIRLLM/Helpers/FHIRResource+Identifier.swift similarity index 90% rename from Sources/SpeziFHIRInterpretation/Helpers/FHIRResource+Identifier.swift rename to Sources/SpeziFHIRLLM/Helpers/FHIRResource+Identifier.swift index 80690aa..6ff2278 100644 --- a/Sources/SpeziFHIRInterpretation/Helpers/FHIRResource+Identifier.swift +++ b/Sources/SpeziFHIRLLM/Helpers/FHIRResource+Identifier.swift @@ -1,5 +1,5 @@ // -// This source file is part of the Stanford LLM on FHIR project +// This source file is part of the Stanford Spezi project // // SPDX-FileCopyrightText: 2023 Stanford University // diff --git a/Sources/SpeziFHIRInterpretation/Helpers/FHIRStore+Interpretation.swift b/Sources/SpeziFHIRLLM/Helpers/FHIRStore+Interpretation.swift similarity index 87% rename from Sources/SpeziFHIRInterpretation/Helpers/FHIRStore+Interpretation.swift rename to Sources/SpeziFHIRLLM/Helpers/FHIRStore+Interpretation.swift index a183a65..0ee78d1 100644 --- a/Sources/SpeziFHIRInterpretation/Helpers/FHIRStore+Interpretation.swift +++ b/Sources/SpeziFHIRLLM/Helpers/FHIRStore+Interpretation.swift @@ -1,5 +1,5 @@ // -// This source file is part of the Stanford LLM on FHIR project +// This source file is part of the Stanford Spezi project // // SPDX-FileCopyrightText: 2023 Stanford University // @@ -12,6 +12,7 @@ import SwiftUI extension FHIRStore { + /// All relevant `FHIRResource`s for the LLM interpretation. public var llmRelevantResources: [FHIRResource] { allergyIntolerances + llmConditions @@ -22,6 +23,7 @@ extension FHIRStore { + procedures.uniqueDisplayNames } + /// All `FHIRResource`s. public var allResources: [FHIRResource] { allergyIntolerances + conditions @@ -34,6 +36,7 @@ extension FHIRStore { + procedures } + /// The patient `FHIRResource` public var patient: FHIRResource? { otherResources .first { resource in @@ -104,11 +107,10 @@ extension FHIRStore { return outpatientMedications + activeMedications } - // TODO: No Storage Keys in here, where to get the config from? - // Move the filtering to the caller + /// Get the function call identifiers of all available health resources in the `FHIRStore`. + /// + /// - Tip: We use an array as the order indicates the sorting, oldest resources come first, newest one last public var allResourcesFunctionCallIdentifier: [String] { - //@AppStorage(StorageKeys.resourceLimit) var resourceLimit = StorageKeys.Defaults.resourceLimit - let relevantResources: [FHIRResource] if llmRelevantResources.count > 100 /*resourceLimit*/ { @@ -125,7 +127,7 @@ extension FHIRStore { relevantResources = llmRelevantResources } - return Array(Set(relevantResources.map { $0.functionCallIdentifier })) + return Array(Set(relevantResources.removingDuplicates().map { $0.functionCallIdentifier })) } } @@ -146,6 +148,10 @@ extension Array where Element == FHIRResource { return Array(reducedEncounters.values) } + fileprivate func removingDuplicates() -> [FHIRResource] { + var seen = Set() + return filter { seen.insert($0).inserted } + } fileprivate func dateSuffix(maxLength: Int) -> [FHIRResource] { self.lazy.sorted(by: { $0.date ?? .distantPast < $1.date ?? .distantPast }).suffix(maxLength) diff --git a/Sources/SpeziFHIRLLM/Resources/Localizable.xcstrings b/Sources/SpeziFHIRLLM/Resources/Localizable.xcstrings new file mode 100644 index 0000000..717ac79 --- /dev/null +++ b/Sources/SpeziFHIRLLM/Resources/Localizable.xcstrings @@ -0,0 +1,876 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "Close" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schließen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerrar" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fermer" + } + } + } + }, + "Conditions" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bedingungen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Condiciones" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conditions" + } + } + } + }, + "Could not create FHIR Summary" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "FHIR-Zusammenfassung konnte nicht erstellt werden" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Could not create FHIR Summary" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se ha podido crear el resumen FHIR" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible de créer un résumé FHIR" + } + } + } + }, + "Create Resource Summary" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ressourcenzusammenfassung erstellen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Create Resource Summary" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear resumen de recursos" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Créer un résumé des ressources" + } + } + } + }, + "Customize the %@." : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Customize the %@." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Customize the %@." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personaliza el %@." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personnalisez le %@." + } + } + } + }, + "Diagnostics" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diagnostik" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diagnóstico" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diagnostics" + } + } + } + }, + "Encounters" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Begegnungen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Encuentros" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rencontres" + } + } + } + }, + "FHIR_RESOURCES_EMPTY_SEARCH_MESSAGE" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Treffer gefunden" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No matches found" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se han encontrado coincidencias" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune correspondance trouvée" + } + } + } + }, + "FHIR_RESOURCES_INTERPRETATION_BUTTON" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lade Interpretation" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Load Interpretation" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interpretación de la carga" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interprétation de la charge" + } + } + } + }, + "FHIR_RESOURCES_INTERPRETATION_LOADING" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ergebnis laden ..." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loading result ..." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cargando resultado ..." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chargement du résultat ..." + } + } + } + }, + "FHIR_RESOURCES_INTERPRETATION_RESOURCE" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "FHIR-Ressource" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "FHIR Resource" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recurso FHIR" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "FHIR Resource" + } + } + } + }, + "FHIR_RESOURCES_INTERPRETATION_SECTION" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interpretation" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interpretation" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interpretación" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interprétation" + } + } + } + }, + "FHIR_RESOURCES_SUMMARY_BUTTON" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zusammenfassung der Ressourcen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Load Resource Summary" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resumen de recursos de carga" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Load Resource Summary" + } + } + } + }, + "FHIR_RESOURCES_SUMMARY_SECTION" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zusammenfassung" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Summary" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resumen" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Résumé" + } + } + } + }, + "FUNCTION_DESCRIPTION" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rufen Sie diese Funktion auf, um die relevanten FHIR-Gesundheitsdatentitel basierend auf der Frage des Benutzers zu ermitteln. Die Titel müssen den genauen Namen haben, der in der anfänglichen Aufforderung definiert wurde. \n\nÜbergeben Sie einen oder mehrere Titel, auf die Sie Zugriff benötigen, in der Eigenschaft \"resources\" als Array von Strings.\n\nVersuchen Sie immer, die geringste Anzahl von Ressourcentiteln auszugeben, die an das Modell gesendet werden, um ein Überschreiten des Token-Limits zu vermeiden." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Call this function to determine the relevant FHIR health record titles based on the user's question. The titles must have the exact name defined in the initial prompt. \n\nPass in one or more titles that you need access within the \"resources\" property as an array of Strings.\n\nAlways try to output the least amount of resource titles to be sent to the model to prevent exceeding the token limit." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Llame a esta función para determinar los títulos de registros sanitarios FHIR relevantes en función de la pregunta del usuario. Los títulos deben tener el nombre exacto definido en la pregunta inicial. \n\nPase uno o más títulos a los que necesite acceder dentro de la propiedad \"resources\" como un array de Strings.\n\nIntente siempre enviar la menor cantidad de títulos de recursos al modelo para evitar exceder el límite de tokens." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appeler cette fonction pour déterminer les titres d'enregistrements de santé FHIR pertinents en fonction de la question de l'utilisateur. Les titres doivent porter le nom exact défini dans l'invite initiale. \n\nTransmettez un ou plusieurs titres auxquels vous devez accéder dans la propriété \"resources\" sous la forme d'un tableau de chaînes de caractères.\n\nEssayez toujours de produire le moins de titres de ressources possible à envoyer au modèle pour éviter de dépasser la limite de jetons." + } + } + } + }, + "Immunizations" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impfungen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vacunas" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vaccinations" + } + } + } + }, + "Interpretation Prompt" : { + "comment" : "Title of the interpretation prompt.\nTitle of the multiple resources interpretation prompt.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interpretation Aufforderung" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interpretation Prompt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interpretación" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invitation à l'interprétation" + } + } + } + }, + "Interpretation Prompt Content" : { + "comment" : "Content of the interpretation prompt.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ihre Aufgabe ist es, die folgende FHIR-Ressource aus der Krankenakte des Benutzers zu interpretieren. Sie sollten den Titel und die Zusammenfassung im folgenden Gebietsschema angeben: {{LOCALE}}.\n\nInterpretieren Sie die Ressource, indem Sie die für die Gesundheit des Benutzers relevanten Daten erklären.\nErläutern Sie den relevanten medizinischen Kontext in einer Sprache, die für einen Benutzer, der kein medizinisches Fachpersonal ist, verständlich ist.\nSie sollten sachliche und präzise Informationen in einer kompakten Zusammenfassung in kurzen Antworten liefern.\n\nGeben Sie dem Nutzer sofort eine Interpretation zurück und beginnen Sie das Gespräch.\nStellen Sie sich nicht gleich zu Beginn vor, sondern beginnen Sie mit Ihrer Interpretation.\n\nDie folgende JSON-Darstellung definiert die FHIR-Ressource, für die Sie eine Interpretation liefern sollten:\n\n{{FHIR_RESOURCE}}" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Your task is to interpret the following FHIR resource from the user's clinical record. You should provide the title and summary in the following locale: {{LOCALE}}.\n\nInterpret the resource by explaining its data relevant to the user's health.\nExplain the relevant medical context in a language understandable by a user who is not a medical professional.\nYou should provide factual and precise information in a compact summary in short responses.\n\nImmediately return an interpretation to the user, starting the conversation.\nDo not introduce yourself at the beginning, and start with your interpretation.\n\nThe following JSON representation defines the FHIR resource that you should provide an interpretation for:\n\n{{FHIR_RESOURCE}}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Su tarea consiste en interpretar el siguiente recurso FHIR de la historia clínica del usuario. Debe proporcionar el título y el resumen en la siguiente configuración regional: {{LOCALE}}.\n\nInterprete el recurso explicando sus datos relevantes para la salud del usuario.\nExplique el contexto médico pertinente en un lenguaje comprensible para un usuario que no sea profesional de la medicina.\nDebe proporcionar información objetiva y precisa en un resumen compacto en respuestas breves.\n\nDevuelve inmediatamente una interpretación al usuario, iniciando la conversación.\nNo se presente al principio y comience con su interpretación.\n\nLa siguiente representación JSON define el recurso FHIR para el que debe proporcionar una interpretación:\n\n{{FHIR_RESOURCE}}" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Your task is to interpret the following FHIR resource from the user's clinical record. You should provide the title and summary in the following locale: {{LOCALE}}.\n\nInterpret the resource by explaining its data relevant to the user's health.\nExplain the relevant medical context in a language understandable by a user who is not a medical professional.\nYou should provide factual and precise information in a compact summary in short responses.\n\nImmediately return an interpretation to the user, starting the conversation.\nDo not introduce yourself at the beginning, and start with your interpretation.\n\nThe following JSON representation defines the FHIR resource that you should provide an interpretation for:\n\n{{FHIR_RESOURCE}}" + } + } + } + }, + "Medications" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medikamente" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medicamentos" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Médicaments" + } + } + } + }, + "Multiple Resource Interpretation Prompt Content" : { + "comment" : "Content of the multiple resources interpretation prompt.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sie sind eine Anwendung, die die Aufgabe hat, FHIR-Ressourcen aus den Krankenakten des Benutzers zu interpretieren.\n\nVerwenden Sie während des gesamten Gesprächs mit dem Benutzer die Funktion \"get_resources\", um die FHIR-Gesundheitsressourcen zu erhalten, die für die ordnungsgemäße Beantwortung der Frage des Benutzers erforderlich sind. Wenn der Benutzer beispielsweise nach seinen Allergien fragt, müssen Sie die Funktion \"get_resources\" verwenden, um die FHIR-Ressourcentitel für Allergiedatensätze auszugeben, damit Sie diese dann zur Beantwortung der Frage verwenden können. Ziel ist es, die Frage des Benutzers bestmöglich zu beantworten und dabei die mit \"get_resources\" erhaltenen FHIR-Ressourcen zu berücksichtigen. Lassen Sie alle technischen Details wie JSON, FHIR-Ressourcen und andere Implementierungsdetails der zugrunde liegenden Datenressourcen weg.\nFordern Sie nur relevante Ressourcen an und konzentrieren Sie sich auf aktuelle Ressourcen. Versuchen Sie, die Anzahl der angeforderten Ressourcen auf ein vernünftiges Maß zu reduzieren.\nWenn der Benutzer z. B. nach seinen Medikamenten fragt, wäre es empfehlenswert, alle FHIR-Ressourcentypen MedicationRequest anzufordern.\n\nInterpretieren Sie die Ressourcen, indem Sie die für die Gesundheit des Benutzers relevanten Daten erklären. Erläutern Sie den relevanten medizinischen Kontext in einer Sprache, die für einen Benutzer, der kein Mediziner ist, verständlich ist, idealerweise auf dem Niveau der fünften Klasse. Sie sollten sachliche und präzise Informationen in einer kompakten Zusammenfassung in kurzen Antworten bereitstellen.\n\nDie Patientenressource wird als anfänglicher Kontext bereitgestellt. Stellen Sie sicher, dass Sie alle sensiblen Nummern wie SSN, Passnummer und Telefonnummer weglassen. \nStellen Sie sich nicht zu Beginn vor und geben Sie sofort eine Zusammenfassung des Benutzers auf der Grundlage der FHIR-Patientenressourcen zurück. Fordern Sie für Ihre erste Nachricht keine FHIR-Ressourcen über die Funktion \"get_resources\" an. Die erste Zusammenfassung sollte kurz und einfach sein; bleiben Sie auf einem hohen Niveau. Beenden Sie sie mit einer Frage an den Benutzer, ob er noch Fragen hat. Achten Sie darauf, dass Ihre Antwort in derselben Sprache verfasst ist, in der der Benutzer Ihnen schreibt, und nicht in der Sprache, die in der Patientenressource angegeben ist. Die Zeitform sollte das Präsens sein." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "You are an application that is tasked with interpreting FHIR resources from the user's clinical records.\n\nThroughout the conversation with the user, use the \"get_resources\" function to obtain the FHIR health resources necessary to answer the user's question properly. For example, if the user asks about their allergies, you must use the \"get_resources\" function to output the FHIR resource titles for allergy records so you can then use them to answer the question. The end goal is to answer the user's question in the best way possible while taking the FHIR resources obtained using \"get_resources\" into consideration. Leave out any technical details like JSON, FHIR resources, and other implementation details of the underlying data resources.\nOnly request relevant resources and focus on recent resources. Try to reduce the number of requested resources to a reasonable scope.\nE.g. if the user asks about their Medication it would be recommended to request all MedicationRequest FHIR resource types.\n\nInterpret the resources by explaining the data relevant to the user's health. Explain the relevant medical context in a language understandable by a user who is not a medical professional, ideally at a 5th-grade reading level. You should provide factual and precise information in a compact summary in short responses.\n\nThe patient resource is provided as the initial context. Ensure to leave out any sensitive numbers like SSN, passport number, and telephone number. \nDo not introduce yourself at the beginning and immediately return a summary of the user based on the FHIR patient resources. No not request any FHIR resources using the \"get_resources\" function for your first message. The initial summary should be short and simple; stay at a high level. End with a question asking the user if they have any questions. Make sure your response is in the same language the user writes to you in, not the one stated in the patient resource. The tense should be present." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usted es una aplicación encargada de interpretar los recursos FHIR de la historia clínica del usuario.\n\nA lo largo de la conversación con el usuario, utilice la función \"get_resources\" para obtener los recursos sanitarios FHIR necesarios para responder correctamente a la pregunta del usuario. Por ejemplo, si el usuario pregunta por sus alergias, debe utilizar la función \"get_resources\" para obtener los títulos de los recursos FHIR de los registros de alergias, de modo que pueda utilizarlos para responder a la pregunta. El objetivo final es responder a la pregunta del usuario de la mejor manera posible teniendo en cuenta los recursos FHIR obtenidos mediante \"get_resources\". Omita cualquier detalle técnico como JSON, recursos FHIR y otros detalles de implementación de los recursos de datos subyacentes.\nSolicite únicamente recursos relevantes y céntrese en los recursos recientes. Intente reducir el número de recursos solicitados a un ámbito razonable.\nPor ejemplo, si el usuario pregunta por su medicación, se recomienda solicitar todos los tipos de recursos FHIR MedicationRequest.\n\nInterpretar los recursos explicando los datos relevantes para la salud del usuario. Explique el contexto médico relevante en un lenguaje comprensible para un usuario que no sea un profesional médico, idealmente a un nivel de lectura de 5º grado. Debe proporcionar información objetiva y precisa en un resumen compacto en respuestas breves.\n\nEl recurso del paciente se proporciona como contexto inicial. Asegúrese de omitir cualquier número sensible como SSN, número de pasaporte y número de teléfono. \nNo se presente al principio y devuelva inmediatamente un resumen del usuario basado en los recursos de paciente FHIR. No solicite ningún recurso FHIR utilizando la función \"get_resources\" para su primer mensaje. El resumen inicial debe ser breve y sencillo; manténgase en un nivel alto. Termine con una pregunta al usuario si tiene alguna duda. Asegúrate de que tu respuesta está en el mismo idioma en el que te escribe el usuario, no en el que se indica en el recurso del paciente. El tiempo verbal debe ser presente." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vous êtes une application chargée d'interpréter les ressources FHIR à partir des dossiers cliniques de l'utilisateur.\n\nTout au long de la conversation avec l'utilisateur, utilisez la fonction \"get_resources\" pour obtenir les ressources de santé FHIR nécessaires pour répondre correctement à la question de l'utilisateur. Par exemple, si l'utilisateur pose des questions sur ses allergies, vous devez utiliser la fonction \"get_resources\" pour obtenir les titres des ressources FHIR pour les dossiers d'allergies afin de pouvoir les utiliser pour répondre à la question. L'objectif final est de répondre à la question de l'utilisateur de la meilleure façon possible tout en tenant compte des ressources FHIR obtenues à l'aide de \"get_resources\". Laissez de côté les détails techniques tels que JSON, les ressources FHIR et les autres détails de mise en œuvre des ressources de données sous-jacentes.\nNe demandez que les ressources pertinentes et concentrez-vous sur les ressources récentes. Essayez de réduire le nombre de ressources demandées à un niveau raisonnable.\nPar exemple, si l'utilisateur demande des informations sur ses médicaments, il est recommandé de demander tous les types de ressources FHIR MedicationRequest.\n\nInterpréter les ressources en expliquant les données pertinentes pour la santé de l'utilisateur. Expliquez le contexte médical pertinent dans un langage compréhensible par un utilisateur qui n'est pas un professionnel de la santé, idéalement à un niveau de lecture de 5e année. Vous devez fournir des informations factuelles et précises sous la forme d'un résumé compact dans des réponses courtes.\n\nLa ressource du patient est fournie comme contexte initial. Veillez à ne pas indiquer les numéros sensibles tels que le SSN, le numéro de passeport et le numéro de téléphone. \nNe vous présentez pas au début et ne renvoyez pas immédiatement un résumé de l'utilisateur basé sur les ressources patient FHIR. Ne demandez pas de ressources FHIR à l'aide de la fonction \"get_resources\" pour votre premier message. Le résumé initial doit être court et simple ; restez à un niveau élevé. Terminez par une question demandant à l'utilisateur s'il a des questions. Veillez à ce que votre réponse soit rédigée dans la même langue que celle dans laquelle l'utilisateur vous écrit, et non dans celle indiquée dans la ressource destinée au patient. Le temps doit être au présent." + } + } + } + }, + "Observations" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beobachtungen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Observaciones" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Observations" + } + } + } + }, + "Other Resources" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Andere Ressourcen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otros recursos" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Autres ressources" + } + } + } + }, + "PARAMETER_DESCRIPTION" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ein Array aller FHIR-Gesundheitsdatentitel mit dem exakt gleichen Namen wie in der Liste angegeben, die für die Beantwortung der Fragen des Benutzers in Frage kommen. Es ist möglich, dass mehrere Titel auf die Frage des Benutzers zutreffen (z. B. für mehrere Medikamente). Sie können auch einen größeren Satz von FHIR-Ressourcen anfordern, indem Sie z. B. nur den Ressourcentyp angeben, was jedoch möglicherweise nicht alle relevanten Ressourcen umfasst, um eine Überschreitung des Token-Limits zu vermeiden.\n\nDie FHIR-Ressourcenbezeichner setzen sich aus drei Elementen zusammen:\n1. Der FHIR-Ressourcentyp, z.B. MedicationRequest, Condition, und weitere.\n2. Der Titel der FHIR-Ressource\n3. Das mit der FHIR-Ressource verbundene Datum.\n\nVerwenden Sie diese Informationen, um die bestmögliche FHIR-Ressource für jede Frage zu bestimmen. Versuchen Sie, alle erforderlichen Titel abzufragen, damit Sie die Frage umfassend beantworten können. \n\nWenn der Benutzer beispielsweise nach seiner Medikation fragt, wäre es empfehlenswert, alle FHIR-Ressourcentypen \"MedicationRequest\" sowie andere verwandte FHIR-Ressourcen anzufordern.\n\nEine Frage kann auf einer vorherigen Frage aufbauen und muss nicht explizit sein. Wenn ein Benutzer z. B. \"prescribe\" (verschreiben) sagt, ist dies mit \"medication\" (Medikamente) verbunden." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "An array of all the FHIR health record titles with the exact same name as given in the list that are applicable to answer the user's questions. It is possible that multiple titles apply to the users's question (e.g for multiple medications). You can also request a larger set of FHIR resources by, e.g., just stating the resource type but this might not include all relevant resources to avoid exceeding the token limit.\n\nThe FHIR resource identifiers are composed of three elements:\n1. The FHIR resource type, e.g., MedicationRequest, Condition, and more.\n2. The title of the FHIR resource\n3. The date associated with the FHIR resource.\n\nUse these informations to determine the best possible FHIR resource for each question. Try to request all the required titles to allow yourself to fully answer the question in a comprehensive manner. \n\nFor example, if the user asks about their Medication it would be recommended to request all MedicationRequest FHIR resource types as well as other related FHIR resources.\n\nA question can build upon a previous question and does not need to be explicit. e.g. if a user says prescribe, this is associated with medication." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Una matriz de todos los títulos de historiales médicos FHIR con el mismo nombre exacto que el indicado en la lista que son aplicables para responder a las preguntas del usuario. Es posible que se apliquen varios títulos a la pregunta del usuario (por ejemplo, para varios medicamentos). También puede solicitar un conjunto más amplio de recursos FHIR, por ejemplo, indicando sólo el tipo de recurso, pero es posible que no incluya todos los recursos relevantes para evitar superar el límite de tokens.\n\nLos identificadores de recursos FHIR se componen de tres elementos:\n1. 1. El tipo de recurso FHIR, por ejemplo, MedicationRequest, Condition, etc.\n2. El título del recurso FHIR.\n3. La fecha asociada al recurso FHIR.\n\nUtilice esta información para determinar el mejor recurso FHIR posible para cada pregunta. Intente solicitar todos los títulos necesarios para poder responder a la pregunta de forma completa. \n\nPor ejemplo, si el usuario pregunta sobre su medicación, sería recomendable solicitar todos los tipos de recursos FHIR MedicationRequest, así como otros recursos FHIR relacionados.\n\nUna pregunta puede basarse en una pregunta anterior y no necesita ser explícita. Por ejemplo, si un usuario dice prescribir, esto se asocia con la medicación." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Un tableau de tous les titres d'enregistrements de santé FHIR ayant exactement le même nom que celui donné dans la liste et qui sont applicables pour répondre aux questions de l'utilisateur. Il est possible que plusieurs titres s'appliquent à la question de l'utilisateur (par exemple pour plusieurs médicaments). Vous pouvez également demander un ensemble plus large de ressources FHIR, par exemple en indiquant simplement le type de ressource, mais il se peut que cela n'inclue pas toutes les ressources pertinentes pour éviter de dépasser la limite de jetons.\n\nLes identifiants de ressources FHIR sont composés de trois éléments :\n1. Le type de ressource FHIR, par exemple MedicationRequest, Condition, etc.\n2. Le titre de la ressource FHIR\n3. La date associée à la ressource FHIR.\n\nUtilisez ces informations pour déterminer la meilleure ressource FHIR possible pour chaque question. Essayez de demander tous les titres requis pour vous permettre de répondre à la question de manière complète. \n\nPar exemple, si l'utilisateur pose une question sur ses médicaments, il est recommandé de demander tous les types de ressources FHIR MedicationRequest ainsi que d'autres ressources FHIR connexes.\n\nUne question peut s'appuyer sur une question précédente et n'a pas besoin d'être explicite. Par exemple, si un utilisateur dit \"prescrire\", cela est associé aux médicaments." + } + } + } + }, + "Place %@ at the position in the prompt where the FHIR resource should be inserted. Optionally place %@ where you would like to insert the current locale." : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Setzen Sie %1$@ an die Stelle in der Eingabeaufforderung, an der die FHIR-Ressource eingefügt werden soll. Optional können Sie %2$@ an der Stelle einfügen, an der Sie das aktuelle Gebietsschema einfügen möchten." + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Place %1$@ at the position in the prompt where the FHIR resource should be inserted. Optionally place %2$@ where you would like to insert the current locale." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Coloque %1$@ en la posición de la solicitud en la que debe insertarse el recurso FHIR. Opcionalmente, coloque %2$@ donde desee insertar la configuración regional actual." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Placez %1$@ à l'endroit de l'invite où la ressource FHIR doit être insérée. Placez éventuellement %2$@ à l'endroit où vous souhaitez insérer la locale actuelle." + } + } + } + }, + "Procedures" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verfahren" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Procedimientos" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Procédures" + } + } + } + }, + "Reset Chat" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chat zurücksetzen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reiniciar chat" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réinitialiser le chat" + } + } + } + }, + "Save Prompt" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prompt speichern" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Save Prompt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guardar aviso" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sauvegarder l'invite" + } + } + } + }, + "Summary Prompt" : { + "comment" : "Title of the summary prompt.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zusammenfassungs Prompt" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Summary Prompt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resumen" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Résumé de l'énoncé" + } + } + } + }, + "Summary Prompt Content" : { + "comment" : "Content of the summary prompt.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ihre Aufgabe ist es, einen Titel und eine kompakte Zusammenfassung für eine FHIR-Ressource aus der klinischen Akte des Benutzers zu erstellen. Sie sollten den Titel und die Zusammenfassung in dem folgenden Gebietsschema bereitstellen: {{LOCALE}}.\n\nIhre Antwort sollte aus zwei Zeilen bestehen, ohne Überschriften, Markdown-Formatierung oder eine andere Struktur, die über zwei Zeilen hinausgeht. Geben Sie den Inhalt direkt und ohne zusätzliche Struktur oder eine Einleitung an. Ein anderes Computerprogramm wird die Ausgabe parsen.\n\n1. Zeile: Eine 1-5-Wort-Zusammenfassung der FHIR-Ressource, die die Ressource sofort identifiziert und die wesentlichen Informationen auf einen Blick liefert. Verwenden Sie KEINEN vollständigen Satz, sondern eine Formatierung, die typischerweise für Titel in Computersystemen verwendet wird.\n\n2. Zeile: Geben Sie eine kurze Zusammenfassung der Ressource in einem Satz mit weniger als 100 Wörtern. Achten Sie darauf, dass sich die Zusammenfassung nur auf die wesentlichen Informationen konzentriert, die ein Patient benötigen würde, und z. B. die Person, die ein Medikament verschrieben hat, oder ähnliche Metadaten ausschließt. Es muss sichergestellt werden, dass alle klinisch relevanten Daten enthalten sind, damit wir die Zusammenfassung in zusätzlichen Aufforderungen verwenden können, bei denen die Verwendung des vollständigen JSON nicht möglich ist.\n\nDie folgende JSON-Darstellung definiert die FHIR-Ressource, für die Sie einen Titel und eine Zusammenfassung angeben sollten:\n\n{{FHIR_RESOURCE}}" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Your task is to private a title and compact summary for an FHIR resource from the user's clinical record. You should provide the title and summary in the following locale: {{LOCALE}}.\n\nYour response should contain two lines without headings, markdown formatting, or any other structure beyond two lines. Directly provide the content without any additional structure or an introduction. Another computer program will parse the output.\n\n1. Line: A 1-5 Word summary of the FHIR resource that immediately identifies the resource and provides the essential information at a glance. Do NOT use a complete sentence; instead, use a formatting typically used for titles in computer systems.\n\n2. Line: Provide a short one-sentence summary of the resource in less than 100 words. Ensure that the summary only focuses on the essential information that a patient would need and, e.g., excludes the person who prescribed a medication or similar metadata. Ensure that all clinically relevant data is included, which allows us to use the summary in additional prompts where using the complete JSON might not be feasible.\n\nThe following JSON representation defines the FHIR resource that you should provide a title and summary for:\n\n{{FHIR_RESOURCE}}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Su tarea consiste en privado un título y un resumen compacto para un recurso FHIR de la historia clínica del usuario. Debe proporcionar el título y el resumen en la siguiente configuración regional: {{LOCALE}}.\n\nSu respuesta debe contener dos líneas sin encabezados, formato markdown o cualquier otra estructura más allá de dos líneas. Proporcione directamente el contenido sin ninguna estructura adicional ni introducción. Otro programa informático analizará la salida.\n\n1. Línea: Un resumen de 1-5 palabras del recurso FHIR que identifique inmediatamente el recurso y proporcione la información esencial de un vistazo. NO utilice una frase completa; en su lugar, utilice un formato típicamente utilizado para títulos en sistemas informáticos.\n\n2. Línea: Proporcione un breve resumen de una sola frase del recurso en menos de 100 palabras. Asegúrese de que el resumen sólo se centra en la información esencial que necesitaría un paciente y, por ejemplo, excluya la persona que prescribió un medicamento o metadatos similares. Garantizar que se incluyen todos los datos clínicamente relevantes, lo que nos permite utilizar el resumen en indicaciones adicionales en las que utilizar el JSON completo podría no ser factible.\n\nLa siguiente representación JSON define el recurso FHIR para el que debe proporcionar un título y un resumen:\n\n{{FHIR_RESOURCE}}" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Votre tâche consiste à fournir un titre et un résumé compact pour une ressource FHIR à partir du dossier clinique de l'utilisateur. Vous devez fournir le titre et le résumé dans la locale suivante : {{LOCALE}}.\n\nVotre réponse doit contenir deux lignes sans titres, formatage markdown ou toute autre structure au-delà de deux lignes. Fournissez directement le contenu sans structure supplémentaire ni introduction. Un autre programme informatique analysera le résultat.\n\n1. Ligne : Un résumé de 1 à 5 mots de la ressource FHIR qui identifie immédiatement la ressource et fournit les informations essentielles en un coup d'œil. N'utilisez PAS de phrase complète, mais plutôt un formatage généralement utilisé pour les titres dans les systèmes informatiques.\n\n2. Ligne : Fournir un résumé d'une phrase de la ressource en moins de 100 mots. Veillez à ce que le résumé se concentre uniquement sur les informations essentielles dont un patient aurait besoin et excluez, par exemple, la personne qui a prescrit un médicament ou d'autres métadonnées similaires. Veiller à ce que toutes les données cliniques pertinentes soient incluses, ce qui nous permet d'utiliser le résumé dans des invites supplémentaires lorsqu'il n'est pas possible d'utiliser le JSON complet.\n\nLa représentation JSON suivante définit la ressource FHIR pour laquelle vous devez fournir un titre et un résumé :\n\n{{FHIR_RESOURCE}}" + } + } + } + }, + "The medical record does not include any FHIR resources for the search term %@." : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Krankenakte enthält keine FHIR-Ressourcen für den Suchbegriff %@." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The medical record does not include any FHIR resources for the search term %@." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La historia clínica no incluye ningún recurso FHIR para el término de búsqueda %@." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Le dossier médical ne contient aucune ressource FHIR pour le terme de recherche %@." + } + } + } + }, + "This is the summary of the requested %@:\n\n%@" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dies ist die Zusammenfassung der angeforderten %1$@:\n\n%2$@" + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "This is the summary of the requested %1$@:\n\n%2$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Este es el resumen del %1$@ solicitado:\n\n%2$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voici le résumé de la demande %1$@ :\n\n%2$@" + } + } + } + }, + "Total Number of Resources: %lld" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gesamtzahl der Ressourcen: %lld" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total Number of Resources: %lld" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Número total de recursos: %lld" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre total de ressources : %lld" + } + } + } + }, + "Unable to parse result of the LLM prompt." : { + "comment" : "Error thrown if the result can not be parsed in the underlying type.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Das Ergebnis der LLM-Eingabeaufforderung kann nicht analysiert werden." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unable to parse result of the LLM prompt." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se ha podido analizar el resultado de la consulta LLM." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible d'analyser le résultat de l'invite LLM." + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Sources/SpeziFHIRInterpretation/Resources/Localizable.xcstrings.license b/Sources/SpeziFHIRLLM/Resources/Localizable.xcstrings.license similarity index 100% rename from Sources/SpeziFHIRInterpretation/Resources/Localizable.xcstrings.license rename to Sources/SpeziFHIRLLM/Resources/Localizable.xcstrings.license diff --git a/Sources/SpeziFHIRInterpretation/Settings/FHIRPrompt.swift b/Sources/SpeziFHIRLLM/Settings/FHIRPrompt.swift similarity index 97% rename from Sources/SpeziFHIRInterpretation/Settings/FHIRPrompt.swift rename to Sources/SpeziFHIRLLM/Settings/FHIRPrompt.swift index 0a13fc5..137967a 100644 --- a/Sources/SpeziFHIRInterpretation/Settings/FHIRPrompt.swift +++ b/Sources/SpeziFHIRLLM/Settings/FHIRPrompt.swift @@ -1,5 +1,5 @@ // -// This source file is part of the Stanford LLM on FHIR project +// This source file is part of the Stanford Spezi project // // SPDX-FileCopyrightText: 2023 Stanford University // diff --git a/Sources/SpeziFHIRInterpretation/Settings/FHIRPromptSettingsView.swift b/Sources/SpeziFHIRLLM/Settings/FHIRPromptSettingsView.swift similarity index 97% rename from Sources/SpeziFHIRInterpretation/Settings/FHIRPromptSettingsView.swift rename to Sources/SpeziFHIRLLM/Settings/FHIRPromptSettingsView.swift index bfd3597..36c4953 100644 --- a/Sources/SpeziFHIRInterpretation/Settings/FHIRPromptSettingsView.swift +++ b/Sources/SpeziFHIRLLM/Settings/FHIRPromptSettingsView.swift @@ -1,5 +1,5 @@ // -// This source file is part of the Stanford LLM on FHIR project +// This source file is part of the Stanford Spezi project // // SPDX-FileCopyrightText: 2023 Stanford University // diff --git a/Sources/SpeziFHIRInterpretation/Views/FHIRResourcesView.swift b/Sources/SpeziFHIRLLM/Views/FHIRResourcesView.swift similarity index 96% rename from Sources/SpeziFHIRInterpretation/Views/FHIRResourcesView.swift rename to Sources/SpeziFHIRLLM/Views/FHIRResourcesView.swift index 51ad5ad..41593bb 100644 --- a/Sources/SpeziFHIRInterpretation/Views/FHIRResourcesView.swift +++ b/Sources/SpeziFHIRLLM/Views/FHIRResourcesView.swift @@ -1,5 +1,5 @@ // -// This source file is part of the Stanford LLM on FHIR project +// This source file is part of the Stanford Spezi project // // SPDX-FileCopyrightText: 2023 Stanford University // @@ -59,14 +59,13 @@ public struct FHIRResourcesView: View { } if fhirStore.allResources.filterByDisplayName(with: searchText).isEmpty { - Text("FHIR_RESOURCES_EMPTY_SEARCH_MESSAGE") - .padding(.top, 8) + Text("FHIR_RESOURCES_EMPTY_SEARCH_MESSAGE", bundle: .module) } else { resourcesSection } Section { } footer: { - Text("Total Number of Resources: \(fhirStore.allResources.count)") + Text("Total Number of Resources: \(fhirStore.allResources.count)", bundle: .module) } } .searchable(text: $searchText) @@ -76,6 +75,17 @@ public struct FHIRResourcesView: View { .navigationTitle(navigationTitle) } + @ViewBuilder private var resourcesSection: some View { + section(for: \.conditions, sectionName: String(localized: "Conditions")) + section(for: \.diagnostics, sectionName: String(localized: "Diagnostics")) + section(for: \.encounters, sectionName: String(localized: "Encounters")) + section(for: \.immunizations, sectionName: String(localized: "Immunizations")) + section(for: \.medications, sectionName: String(localized: "Medications")) + section(for: \.observations, sectionName: String(localized: "Observations")) + section(for: \.procedures, sectionName: String(localized: "Procedures")) + section(for: \.otherResources, sectionName: String(localized: "Other Resources")) + } + /// Creates a ``FHIRResourcesView`` displaying a `List` of all available FHIR resources. /// @@ -109,18 +119,6 @@ public struct FHIRResourcesView: View { self.contentView = contentView() self.actionView = actionView() } - - - @ViewBuilder private var resourcesSection: some View { - section(for: \.conditions, sectionName: String(localized: "Conditions")) - section(for: \.diagnostics, sectionName: String(localized: "Diagnostics")) - section(for: \.encounters, sectionName: String(localized: "Encounters")) - section(for: \.immunizations, sectionName: String(localized: "Immunizations")) - section(for: \.medications, sectionName: String(localized: "Medications")) - section(for: \.observations, sectionName: String(localized: "Observations")) - section(for: \.procedures, sectionName: String(localized: "Procedures")) - section(for: \.otherResources, sectionName: String(localized: "Other Resources")) - } private func section(for keyPath: KeyPath, sectionName: String) -> some View { diff --git a/Sources/SpeziFHIRInterpretation/Views/InspectResourceView.swift b/Sources/SpeziFHIRLLM/Views/InspectResourceView.swift similarity index 87% rename from Sources/SpeziFHIRInterpretation/Views/InspectResourceView.swift rename to Sources/SpeziFHIRLLM/Views/InspectResourceView.swift index d0e93a4..1f6e286 100644 --- a/Sources/SpeziFHIRInterpretation/Views/InspectResourceView.swift +++ b/Sources/SpeziFHIRLLM/Views/InspectResourceView.swift @@ -1,5 +1,5 @@ // -// This source file is part of the Stanford LLM on FHIR project +// This source file is part of the Stanford Spezi project // // SPDX-FileCopyrightText: 2023 Stanford University // @@ -31,15 +31,14 @@ struct InspectResourceView: View { .viewStateAlert(state: $interpreting) .viewStateAlert(state: $loadingSummary) .task { - // TODO: No feature flag accessible in here - //if !FeatureFlags.testMode { + #if !TEST interpret() - //} + #endif } } @ViewBuilder private var summarySection: some View { - Section("FHIR_RESOURCES_SUMMARY_SECTION") { + Section("FHIR_RESOURCES_SUMMARY_SECTION".localized(.module).localizedString()) { if loadingSummary == .processing { HStack { Spacer() @@ -59,7 +58,7 @@ struct InspectResourceView: View { Text(summary.summary) .multilineTextAlignment(.leading) .contextMenu { - Button("FHIR_RESOURCES_SUMMARY_BUTTON") { + Button("FHIR_RESOURCES_SUMMARY_BUTTON".localized(.module).localizedString()) { loadSummary(forceReload: true) } } @@ -67,7 +66,7 @@ struct InspectResourceView: View { } } } else { - Button("FHIR_RESOURCES_SUMMARY_BUTTON") { + Button("FHIR_RESOURCES_SUMMARY_BUTTON".localized(.module).localizedString()) { loadSummary() } } @@ -75,25 +74,25 @@ struct InspectResourceView: View { } @ViewBuilder private var interpretationSection: some View { - Section("FHIR_RESOURCES_INTERPRETATION_SECTION") { + Section("FHIR_RESOURCES_INTERPRETATION_SECTION".localized(.module).localizedString()) { if let interpretation = fhirResourceInterpreter.cachedInterpretation(forResource: resource), !interpretation.isEmpty { Text(interpretation) .multilineTextAlignment(.leading) .contextMenu { - Button("FHIR_RESOURCES_INTERPRETATION_BUTTON") { + Button("FHIR_RESOURCES_INTERPRETATION_BUTTON".localized(.module).localizedString()) { interpret(forceReload: true) } } } else if interpreting == .processing { VStack(alignment: .center) { - Text("FHIR_RESOURCES_INTERPRETATION_LOADING") + Text("FHIR_RESOURCES_INTERPRETATION_LOADING", bundle: .module) .frame(maxWidth: .infinity) ProgressView() .progressViewStyle(.circular) } } else { VStack(alignment: .center) { - Button("FHIR_RESOURCES_INTERPRETATION_BUTTON") { + Button("FHIR_RESOURCES_INTERPRETATION_BUTTON".localized(.module).localizedString()) { interpret() } } @@ -102,7 +101,7 @@ struct InspectResourceView: View { } @ViewBuilder private var resourceSection: some View { - Section("FHIR_RESOURCES_INTERPRETATION_RESOURCE") { + Section("FHIR_RESOURCES_INTERPRETATION_RESOURCE".localized(.module).localizedString()) { LazyText(verbatim: resource.jsonDescription) .fontDesign(.monospaced) .lineLimit(1) diff --git a/Sources/SpeziFHIRMockPatients/FHIRStore+TestingSupport.swift b/Sources/SpeziFHIRMockPatients/FHIRStore+TestingSupport.swift index f5bc1d0..1458133 100644 --- a/Sources/SpeziFHIRMockPatients/FHIRStore+TestingSupport.swift +++ b/Sources/SpeziFHIRMockPatients/FHIRStore+TestingSupport.swift @@ -11,9 +11,8 @@ import SpeziFHIR extension FHIRStore { + /// Loads a mock resource into the `FHIRStore` for testing purposes. public func loadTestingResources() { - // TODO: Removed if FeatureFlags.testMode { ... } - let mockObservation = Observation( code: CodeableConcept(coding: [Coding(code: "1234".asFHIRStringPrimitive())]), id: FHIRPrimitive("1234"), diff --git a/Sources/SpeziFHIRMockPatients/Resources/Allen322_Ferry570_ad134528-56a5-35fd-c37f-466ff119c625.json.license b/Sources/SpeziFHIRMockPatients/Resources/Allen322_Ferry570_ad134528-56a5-35fd-c37f-466ff119c625.json.license index 750aeae..687b27d 100644 --- a/Sources/SpeziFHIRMockPatients/Resources/Allen322_Ferry570_ad134528-56a5-35fd-c37f-466ff119c625.json.license +++ b/Sources/SpeziFHIRMockPatients/Resources/Allen322_Ferry570_ad134528-56a5-35fd-c37f-466ff119c625.json.license @@ -1,5 +1,5 @@ -This source file is part of the Stanford LLM on FHIR project +This source file is part of the Stanford Spezi project SPDX-FileCopyrightText: 2023 Stanford University diff --git a/Sources/SpeziFHIRMockPatients/Resources/Beatris270_Bogan287_5b3645de-a2d0-d016-0839-bab3757c4c58.json.license b/Sources/SpeziFHIRMockPatients/Resources/Beatris270_Bogan287_5b3645de-a2d0-d016-0839-bab3757c4c58.json.license index 750aeae..687b27d 100644 --- a/Sources/SpeziFHIRMockPatients/Resources/Beatris270_Bogan287_5b3645de-a2d0-d016-0839-bab3757c4c58.json.license +++ b/Sources/SpeziFHIRMockPatients/Resources/Beatris270_Bogan287_5b3645de-a2d0-d016-0839-bab3757c4c58.json.license @@ -1,5 +1,5 @@ -This source file is part of the Stanford LLM on FHIR project +This source file is part of the Stanford Spezi project SPDX-FileCopyrightText: 2023 Stanford University diff --git a/Sources/SpeziFHIRMockPatients/Resources/Edythe31_Morar593_9c3df38a-d3b7-2198-3898-51f9153d023d.json.license b/Sources/SpeziFHIRMockPatients/Resources/Edythe31_Morar593_9c3df38a-d3b7-2198-3898-51f9153d023d.json.license index 750aeae..687b27d 100644 --- a/Sources/SpeziFHIRMockPatients/Resources/Edythe31_Morar593_9c3df38a-d3b7-2198-3898-51f9153d023d.json.license +++ b/Sources/SpeziFHIRMockPatients/Resources/Edythe31_Morar593_9c3df38a-d3b7-2198-3898-51f9153d023d.json.license @@ -1,5 +1,5 @@ -This source file is part of the Stanford LLM on FHIR project +This source file is part of the Stanford Spezi project SPDX-FileCopyrightText: 2023 Stanford University diff --git a/Sources/SpeziFHIRMockPatients/Resources/Gonzalo160_Duenas839_ed70a28f-30b2-acb7-658a-8b340dadd685.json.license b/Sources/SpeziFHIRMockPatients/Resources/Gonzalo160_Duenas839_ed70a28f-30b2-acb7-658a-8b340dadd685.json.license index 750aeae..687b27d 100644 --- a/Sources/SpeziFHIRMockPatients/Resources/Gonzalo160_Duenas839_ed70a28f-30b2-acb7-658a-8b340dadd685.json.license +++ b/Sources/SpeziFHIRMockPatients/Resources/Gonzalo160_Duenas839_ed70a28f-30b2-acb7-658a-8b340dadd685.json.license @@ -1,5 +1,5 @@ -This source file is part of the Stanford LLM on FHIR project +This source file is part of the Stanford Spezi project SPDX-FileCopyrightText: 2023 Stanford University diff --git a/Sources/SpeziFHIRMockPatients/Resources/Jacklyn830_Veum823_e0e1f21a-22a7-d166-7bb1-63f6bbce1a32.json.license b/Sources/SpeziFHIRMockPatients/Resources/Jacklyn830_Veum823_e0e1f21a-22a7-d166-7bb1-63f6bbce1a32.json.license index 750aeae..687b27d 100644 --- a/Sources/SpeziFHIRMockPatients/Resources/Jacklyn830_Veum823_e0e1f21a-22a7-d166-7bb1-63f6bbce1a32.json.license +++ b/Sources/SpeziFHIRMockPatients/Resources/Jacklyn830_Veum823_e0e1f21a-22a7-d166-7bb1-63f6bbce1a32.json.license @@ -1,5 +1,5 @@ -This source file is part of the Stanford LLM on FHIR project +This source file is part of the Stanford Spezi project SPDX-FileCopyrightText: 2023 Stanford University diff --git a/Sources/SpeziFHIRMockPatients/Resources/Milton509_Ortiz186_d66b5418-06cb-fc8a-8c13-85685b6ac939.json.license b/Sources/SpeziFHIRMockPatients/Resources/Milton509_Ortiz186_d66b5418-06cb-fc8a-8c13-85685b6ac939.json.license index 750aeae..687b27d 100644 --- a/Sources/SpeziFHIRMockPatients/Resources/Milton509_Ortiz186_d66b5418-06cb-fc8a-8c13-85685b6ac939.json.license +++ b/Sources/SpeziFHIRMockPatients/Resources/Milton509_Ortiz186_d66b5418-06cb-fc8a-8c13-85685b6ac939.json.license @@ -1,5 +1,5 @@ -This source file is part of the Stanford LLM on FHIR project +This source file is part of the Stanford Spezi project SPDX-FileCopyrightText: 2023 Stanford University