├── .github └── workflows │ ├── beta-deployment.yml │ └── pull-request.yml ├── .gitignore ├── .swiftlint.yml ├── CONTRIBUTORS.md ├── Figures ├── Chat-dark.png ├── Chat-dark.png.license ├── Chat.png ├── Chat.png.license ├── Example.png ├── Example.png.license ├── Export-dark.png ├── Export-dark.png.license ├── Export.png ├── Export.png.license ├── Settings-dark.png ├── Settings-dark.png.license ├── Settings.png └── Settings.png.license ├── HealthGPT.xcodeproj ├── project.pbxproj ├── project.pbxproj.license ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── contents.xcworkspacedata.license │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ ├── IDEWorkspaceChecks.plist.license │ │ ├── WorkspaceSettings.xcsettings │ │ ├── WorkspaceSettings.xcsettings.license │ │ └── swiftpm │ │ ├── Package.resolved │ │ └── Package.resolved.license └── xcshareddata │ └── xcschemes │ ├── HealthGPT.xcscheme │ └── HealthGPT.xcscheme.license ├── HealthGPT ├── HealthGPT │ ├── HealthData.swift │ ├── HealthDataFetcher+Process.swift │ ├── HealthDataFetcher.swift │ ├── HealthDataFetcherError.swift │ ├── HealthDataInterpreter.swift │ ├── HealthGPTView.swift │ ├── PromptGenerator.swift │ ├── SettingsView.swift │ └── UIApplication+Keyboard.swift ├── HealthGPTAppDelegate.swift ├── HealthGPTAppTestingSetup.swift ├── HealthGPTApplication.swift ├── HealthGPTStandard.swift ├── Helper │ ├── Binding+Negate.swift │ ├── CodableArray+RawRepresentable.swift │ └── Date+Extensions.swift ├── Onboarding │ ├── Disclaimer.swift │ ├── HealthKitPermissions.swift │ ├── LLMLocalDownload.swift │ ├── LLMSource.swift │ ├── LLMSourceSelection.swift │ ├── OnboardingFlow.swift │ ├── OpenAIAPIKey.swift │ ├── OpenAIModelSelection.swift │ ├── String+ModuleLocalized.swift │ └── Welcome.swift ├── SharedContext │ ├── FeatureFlags.swift │ └── StorageKeys.swift └── Supporting Files │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ ├── Contents.json │ │ └── Contents.json.license │ ├── AppIcon.appiconset │ │ ├── AppIcon.png │ │ ├── AppIcon.png.license │ │ ├── Contents.json │ │ └── Contents.json.license │ ├── Contents.json │ └── Contents.json.license │ ├── HealthGPT.entitlements │ ├── HealthGPT.entitlements.license │ ├── Info.plist │ ├── Info.plist.license │ ├── Localizable.xcstrings │ └── Localizable.xcstrings.license ├── HealthGPTTests └── PromptGeneratorTests.swift ├── HealthGPTUITests ├── HealthGPTViewUITests.swift └── OnboardingUITests.swift ├── LICENSE.md ├── LICENSES └── MIT.txt ├── README.md └── fastlane ├── .gitignore ├── Appfile ├── Fastfile └── README.md /.github/workflows/beta-deployment.yml: -------------------------------------------------------------------------------- 1 | # 2 | # This source file is part of the Stanford HealthGPT project 3 | # 4 | # SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | # 6 | # SPDX-License-Identifier: MIT 7 | # 8 | 9 | name: Beta Deployment 10 | 11 | on: 12 | push: 13 | branches: 14 | - main 15 | workflow_dispatch: 16 | 17 | jobs: 18 | buildandtest: 19 | name: Build and Test 20 | uses: ./.github/workflows/pull-request.yml 21 | iosapptestflightdeployment: 22 | name: iOS App TestFlight Deployment 23 | needs: buildandtest 24 | uses: StanfordBDHG/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2 25 | secrets: inherit 26 | with: 27 | runsonlabels: '["macOS", "self-hosted"]' 28 | artifactname: HealthGPT.xcresult 29 | fastlanelane: beta 30 | setupsigning: true 31 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | # 2 | # This source file is part of the Stanford HealthGPT project 3 | # 4 | # SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | # 6 | # SPDX-License-Identifier: MIT 7 | # 8 | 9 | name: Pull Request 10 | 11 | on: 12 | pull_request: 13 | workflow_dispatch: 14 | workflow_call: 15 | 16 | jobs: 17 | buildandtest: 18 | name: Build and Test Application 19 | uses: StanfordBDHG/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2 20 | with: 21 | artifactname: HealthGPT.xcresult 22 | fastlanelane: test 23 | runsonlabels: '["macOS", "self-hosted"]' 24 | reuse_action: 25 | name: REUSE Compliance Check 26 | uses: StanfordBDHG/.github/.github/workflows/reuse.yml@v2 27 | swiftlint: 28 | name: SwiftLint 29 | uses: StanfordBDHG/.github/.github/workflows/swiftlint.yml@v2 30 | uploadcoveragereport: 31 | name: Upload Coverage Report 32 | needs: buildandtest 33 | uses: StanfordBDHG/.github/.github/workflows/create-and-upload-coverage-report.yml@v2 34 | with: 35 | coveragereports: HealthGPT.xcresult 36 | secrets: 37 | token: ${{ secrets.CODECOV_TOKEN }} 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # This source file is part of the Stanford HealthGPT project 3 | # 4 | # SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | # 6 | # SPDX-License-Identifier: MIT 7 | # 8 | 9 | # Swift Package Manager 10 | *.xcodeproj 11 | .swiftpm 12 | .derivedData 13 | .build/ 14 | !HealthGPT.xcodeproj 15 | 16 | # IDE related folders 17 | .idea 18 | 19 | # Xcode 20 | xcuserdata/ 21 | *.ipa 22 | *.dSYM 23 | *.dSYM.zip 24 | 25 | # Other files 26 | .DS_Store 27 | .env 28 | 29 | # Tests 30 | report.junit 31 | report.html 32 | HealthGPT.xcresult 33 | 34 | # Logs 35 | logs 36 | *.log 37 | npm-debug.log* 38 | yarn-debug.log* 39 | yarn-error.log* 40 | firebase-debug.log* 41 | firebase-debug.*.log* 42 | 43 | # Firebase cache 44 | .firebase/ 45 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | # 2 | # This source file is part of the Stanford HealthGPT open-source project 3 | # 4 | # SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md) 5 | # 6 | # SPDX-License-Identifier: MIT 7 | # 8 | 9 | # The whitelist_rules configuration also includes rules that are enabled by default to provide a good overview of all rules. 10 | only_rules: 11 | # All Images that provide context should have an accessibility label. Purely decorative images can be hidden from accessibility. 12 | - accessibility_label_for_image 13 | # Attributes should be on their own lines in functions and types, but on the same line as variables and imports. 14 | - attributes 15 | # Prefer using Array(seq) over seq.map { $0 } to convert a sequence into an Array. 16 | - array_init 17 | # Prefer the new block based KVO API with keypaths when using Swift 3.2 or later. 18 | - block_based_kvo 19 | # Non-constant variables should not be listed in a closure’s capture list to avoid confusion about closures capturing variables at creation time. 20 | - capture_variable 21 | # Delegate protocols should be class-only so they can be weakly referenced. 22 | - class_delegate_protocol 23 | # Closing brace with closing parenthesis should not have any whitespaces in the middle. 24 | - closing_brace 25 | # Closure bodies should not span too many lines. 26 | - closure_body_length 27 | # Closure end should have the same indentation as the line that started it. 28 | - closure_end_indentation 29 | # Closure parameters should be on the same line as opening brace. 30 | - closure_parameter_position 31 | # Closure expressions should have a single space inside each brace. 32 | - closure_spacing 33 | # Use commas to separate types in inheritance lists 34 | - comma_inheritance 35 | # Prefer at least one space after slashes for comments. 36 | - comment_spacing 37 | # All elements in a collection literal should be vertically aligned 38 | - collection_alignment 39 | # Colons should be next to the identifier when specifying a type and next to the key in dictionary literals. 40 | - colon 41 | # There should be no space before and one after any comma. 42 | - comma 43 | # The initializers declared in compiler protocols such as ExpressibleByArrayLiteral shouldn't be called directly. 44 | - compiler_protocol_init 45 | # Getter and setters in computed properties and subscripts should be in a consistent order. 46 | - computed_accessors_order 47 | # Conditional statements should always return on the next line 48 | - conditional_returns_on_newline 49 | # Prefer contains over comparing filter(where:).count to 0. 50 | - contains_over_filter_count 51 | # Prefer contains over using filter(where:).isEmpty 52 | - contains_over_filter_is_empty 53 | # Prefer `contains` over `first(where:) != nil` 54 | - contains_over_first_not_nil 55 | # Prefer contains over range(of:) != nil and range(of:) == nil 56 | - contains_over_range_nil_comparison 57 | # if, for, guard, switch, while, and catch statements shouldn't unnecessarily wrap their conditionals or arguments in parentheses. 58 | - control_statement 59 | # Types used for hosting only static members should be implemented as a caseless enum to avoid instantiation. 60 | - convenience_type 61 | # Complexity of function bodies should be limited. 62 | - cyclomatic_complexity 63 | # Availability checks or attributes shouldn’t be using older versions that are satisfied by the deployment target. 64 | - deployment_target 65 | # When registering for a notification using a block, the opaque observer that is returned should be stored so it can be removed later. 66 | - discarded_notification_center_observer 67 | # Discouraged direct initialization of types that can be harmful. e.g. UIDevice(), Bundle() 68 | - discouraged_direct_init 69 | # Prefer initializers over object literals. 70 | - discouraged_object_literal 71 | # Prefer non-optional booleans over optional booleans. 72 | - discouraged_optional_boolean 73 | # Prefer empty collection over optional collection. 74 | - discouraged_optional_collection 75 | # Dictionary literals with duplicated keys will crash in runtime. 76 | - duplicated_key_in_dictionary_literal 77 | # Duplicate Imports 78 | - duplicate_imports 79 | # Avoid using 'dynamic' and '@inline(__always)' together. 80 | - dynamic_inline 81 | # Prefer checking isEmpty over comparing collection to an empty array or dictionary literal. 82 | - empty_collection_literal 83 | # Prefer checking `isEmpty` over comparing `count` to zero. 84 | - empty_count 85 | # Arguments can be omitted when matching enums with associated types if they are not used. 86 | - empty_enum_arguments 87 | # Prefer () -> over Void ->. 88 | - empty_parameters 89 | # When using trailing closures, empty parentheses should be avoided after the method call. 90 | - empty_parentheses_with_trailing_closure 91 | # Prefer checking `isEmpty` over comparing string to an empty string literal. 92 | - empty_string 93 | # Empty XCTest method should be avoided. 94 | - empty_xctest_method 95 | # Number of associated values in an enum case should be low 96 | - enum_case_associated_values_count 97 | # Explicitly calling .init() should be avoided. 98 | - explicit_init 99 | # A fatalError call should have a message. 100 | - fatal_error_message 101 | # Files should not span too many lines. 102 | # See file_length below for the exact configuration. 103 | - file_length 104 | # File name should not contain any whitespace. 105 | - file_name_no_space 106 | # Specifies how the types within a file should be ordered. 107 | - file_types_order 108 | # Prefer using ``.first(where:)`` over ``.filter { }.first` in collections. 109 | - first_where 110 | # Prefer flatMap over map followed by reduce([], +). 111 | - flatmap_over_map_reduce 112 | # where clauses are preferred over a single if inside a for. 113 | - for_where 114 | # Force casts should be avoided. 115 | - force_cast 116 | # Force tries should be avoided. 117 | - force_try 118 | # Force unwrapping should be avoided. 119 | - force_unwrapping 120 | # Prefer to locate parameters with defaults toward the end of the parameter list. 121 | - function_default_parameter_at_end 122 | # Functions bodies should not span too many lines. 123 | # See function_body_length below for the exact configuration. 124 | - function_body_length 125 | # Number of function parameters should be low. 126 | # See function_parameter_count below for the exact configuration. 127 | - function_parameter_count 128 | # Generic type name should only contain alphanumeric characters, start with an uppercase character and span between 1 and 20 characters in length. 129 | - generic_type_name 130 | # Comparing two identical operands is likely a mistake. 131 | - identical_operands 132 | # Identifier names should only contain alphanumeric characters and start with a lowercase character or should only contain capital letters. 133 | # In an exception to the above, variable names may start with a capital letter when they are declared static and immutable. 134 | # Variable names should not be too long or too short. Excluded names are listed below. 135 | - identifier_name 136 | # Computed read-only properties and subscripts should avoid using the get keyword. 137 | - implicit_getter 138 | # Prefer implicit returns in closures. 139 | - implicit_return 140 | # Implicitly unwrapped optionals should be avoided when possible. 141 | - implicitly_unwrapped_optional 142 | # Identifiers should use inclusive language that avoids discrimination against groups of people based on race, gender, or socioeconomic status 143 | - inclusive_language 144 | # Prefer using Set.isDisjoint(with:) over Set.intersection(_:).isEmpty. 145 | - is_disjoint 146 | # Discouraged explicit usage of the default separator. 147 | - joined_default_parameter 148 | # Tuples shouldn't have too many members. Create a custom type instead. 149 | # See large_tuple below for the exact configuration. 150 | - large_tuple 151 | # Prefer using .last(where:) over .filter { }.last in collections. 152 | - last_where 153 | # Files should not contain leading whitespace. 154 | - leading_whitespace 155 | # CGGeometry: Struct extension properties and methods are preferred over legacy functions 156 | - legacy_cggeometry_functions 157 | # Struct-scoped constants are preferred over legacy global constants (CGSize, CGRect, NSPoint, ...). 158 | - legacy_constant 159 | # Swift constructors are preferred over legacy convenience functions (CGPointMake, CGSizeMake, UIOffsetMake, ...). 160 | - legacy_constructor 161 | # Prefer using the hash(into:) function instead of overriding hashValue 162 | - legacy_hashing 163 | # Prefer using the isMultiple(of:) function instead of using the remainder operator (%). 164 | - legacy_multiple 165 | # Struct extension properties and methods are preferred over legacy functions 166 | - legacy_nsgeometry_functions 167 | # Prefer using type.random(in:) over legacy functions. 168 | - legacy_random 169 | # Lines should not span too many characters. 170 | # See line_length below for the exact configuration. 171 | - line_length 172 | # Array and dictionary literal end should have the same indentation as the line that started it. 173 | - literal_expression_end_indentation 174 | # Ensure definitions have a lower access control level than their enclosing parent 175 | - lower_acl_than_parent 176 | # MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...' 177 | - mark 178 | # Declarations should be documented. 179 | - missing_docs 180 | # Modifier order should be consistent. 181 | - modifier_order 182 | # Arguments should be either on the same line, or one per line. 183 | - multiline_arguments 184 | # Multiline arguments should have their surrounding brackets in a new line. 185 | - multiline_arguments_brackets 186 | # Chained function calls should be either on the same line, or one per line. 187 | - multiline_function_chains 188 | # Multiline literals should have their surrounding brackets in a new line. 189 | - multiline_literal_brackets 190 | # Functions and methods parameters should be either on the same line, or one per line. 191 | - multiline_parameters 192 | # Multiline parameters should have their surrounding brackets in a new line. 193 | - multiline_parameters_brackets 194 | # Types and functions should only be nested to a certain level deep. 195 | # See nesting below for the exact configuration. 196 | - nesting 197 | # Prefer Nimble operator overloads over free matcher functions. 198 | - nimble_operator 199 | # Prefer not to use extension access modifiers 200 | - no_extension_access_modifier 201 | # Fallthroughs can only be used if the case contains at least one other statement. 202 | - no_fallthrough_only 203 | # Don’t add a space between the method name and the parentheses. 204 | - no_space_in_method_call 205 | # An object should only remove itself as an observer in deinit. 206 | - notification_center_detachment 207 | # Static strings should be used as key in NSLocalizedString in order to genstrings work. 208 | - nslocalizedstring_key 209 | # NSObject subclasses should implement isEqual instead of ==. 210 | - nsobject_prefer_isequal 211 | # Prefer object literals over image and color inits. 212 | - object_literal 213 | # Opening braces should be preceded by a single space and on the same line as the declaration. 214 | - opening_brace 215 | # Operators should be surrounded by a single whitespace when they are being used. 216 | - operator_usage_whitespace 217 | # Operators should be surrounded by a single whitespace when defining them. 218 | - operator_whitespace 219 | # Matching an enum case against an optional enum without ‘?’ is supported on Swift 5.1 and above. 220 | - optional_enum_case_matching 221 | # A doc comment should be attached to a declaration. 222 | - orphaned_doc_comment 223 | # Extensions shouldn’t override declarations. 224 | - override_in_extension 225 | # Some overridden methods should always call super 226 | - overridden_super_call 227 | # Combine multiple pattern matching bindings by moving keywords out of tuples. 228 | - pattern_matching_keywords 229 | # Prefer Self over type(of: self) when accessing properties or calling methods. 230 | - prefer_self_type_over_type_of_self 231 | # Prefer .zero over explicit init with zero parameters (e.g. CGPoint(x: 0, y: 0)) 232 | - prefer_zero_over_explicit_init 233 | # Prefer private over fileprivate declarations. 234 | - private_over_fileprivate 235 | # Combine Subject should be private. 236 | - private_subject 237 | # Unit tests marked private are silently skipped. 238 | - private_unit_test 239 | # Creating views using Interface Builder should be avoided. 240 | - prohibited_interface_builder 241 | # Some methods should not call super ( 242 | # NSFileProviderExtension: providePlaceholder(at:completionHandler:) 243 | # NSTextInput doCommand(by:) 244 | # NSView updateLayer() 245 | # UIViewController loadView()) 246 | - prohibited_super_call 247 | # When declaring properties in protocols, the order of accessors should be get set. 248 | - protocol_property_accessors_order 249 | # Prefer using .allSatisfy() or .contains() over reduce(true) or reduce(false) 250 | - reduce_boolean 251 | # Prefer reduce(into:_:) over reduce(_:_:) for copy-on-write types 252 | - reduce_into 253 | # Prefer _ = foo() over let _ = foo() when discarding a result from a function. 254 | - redundant_discardable_let 255 | # nil coalescing operator is only evaluated if the lhs is nil, coalescing operator with nil as rhs is redundant 256 | - redundant_nil_coalescing 257 | # Objective-C attribute (@objc) is redundant in declaration. 258 | - redundant_objc_attribute 259 | # Initializing an optional variable with nil is redundant. 260 | - redundant_optional_initialization 261 | # Property setter access level shouldn't be explicit if it's the same as the variable access level. 262 | - redundant_set_access_control 263 | # String enum values can be omitted when they are equal to the enumcase name. 264 | - redundant_string_enum_value 265 | # Variables should not have redundant type annotation 266 | - redundant_type_annotation 267 | # Returning Void in a function declaration is redundant. 268 | - redundant_void_return 269 | # Return arrow and return type should be separated by a single space or on a separate line. 270 | - return_arrow_whitespace 271 | # Returning values from Void functions should be avoided. 272 | - return_value_from_void_function 273 | # Re-bind self to a consistent identifier name. 274 | - self_binding 275 | # Prefer shorthand operators (+=, -=, *=, /=) over doing the operation and assigning. 276 | - shorthand_operator 277 | # Test files should contain a single QuickSpec or XCTestCase class. 278 | - single_test_class 279 | # Prefer using `min()`` or `max()`` over `sorted().first` or `sorted().last` 280 | - sorted_first_last 281 | # Imports should be sorted. 282 | - sorted_imports 283 | # Else and catch should be on the same line, one space after the previous declaration. 284 | - statement_position 285 | # Operators should be declared as static functions, not free functions. 286 | - static_operator 287 | # SwiftLint ‘disable’ commands are superfluous when the disabled rule would not have triggered a violation in the disabled region. Use “ - ” if you wish to document a command. 288 | - superfluous_disable_command 289 | # Case statements should vertically align with their enclosing switch statement, or indented if configured otherwise. 290 | - switch_case_alignment 291 | # Shorthand syntactic sugar should be used, i.e. [Int] instead of Array. 292 | - syntactic_sugar 293 | # TODOs and FIXMEs should be resolved. 294 | - todo 295 | # Prefer someBool.toggle() over someBool = !someBool. 296 | - toggle_bool 297 | # Trailing closure syntax should be used whenever possible. 298 | - trailing_closure 299 | # Trailing commas in arrays and dictionaries should be avoided/enforced. 300 | - trailing_comma 301 | # Files should have a single trailing newline. 302 | - trailing_newline 303 | # Lines should not have trailing semicolons. 304 | - trailing_semicolon 305 | # Lines should not have trailing whitespace. 306 | # Ignored lines are specified below. 307 | - trailing_whitespace 308 | # Type bodies should not span too many lines. 309 | # See large_tuple below for the exact configuration. 310 | - type_body_length 311 | # Specifies the order of subtypes, properties, methods & more within a type. 312 | - type_contents_order 313 | # Type name should only contain alphanumeric characters, start with an uppercase character and span between 3 and 40 characters in length. 314 | # Excluded types are listed below. 315 | - type_name 316 | # Prefer using Array(seq) over seq.map { $0 } to convert a sequence into an Array. 317 | - typesafe_array_init 318 | # Use #unavailable/#available instead of #available/#unavailable with an empty body. 319 | - unavailable_condition 320 | # Unimplemented functions should be marked as unavailable. 321 | - unavailable_function 322 | # Avoid using unneeded break statements. 323 | - unneeded_break_in_switch 324 | # Parentheses are not needed when declaring closure arguments. 325 | - unneeded_parentheses_in_closure_argument 326 | # Prefer capturing references as weak to avoid potential crashes. 327 | - unowned_variable_capture 328 | # Catch statements should not declare error variables without type casting. 329 | - untyped_error_in_catch 330 | # Unused parameter in a closure should be replaced with _. 331 | - unused_closure_parameter 332 | # Unused control flow label should be removed. 333 | - unused_control_flow_label 334 | # Declarations should be referenced at least once within all files linted. 335 | - unused_declaration 336 | # When the index or the item is not used, .enumerated() can be removed. 337 | - unused_enumerated 338 | # All imported modules should be required to make the file compile. 339 | - unused_import 340 | # Prefer != nil over let _ = 341 | - unused_optional_binding 342 | # Setter value is not used. 343 | - unused_setter_value 344 | # @IBInspectable should be applied to variables only, have its type explicit and be of a supported type 345 | - valid_ibinspectable 346 | # Function parameters should be aligned vertically if they're in multiple lines in a declaration. 347 | - vertical_parameter_alignment 348 | # Function parameters should be aligned vertically if they're in multiple lines in a method call. 349 | - vertical_parameter_alignment_on_call 350 | # Limit vertical whitespace to a single empty line. 351 | # See vertical_whitespace below for the exact configuration. 352 | - vertical_whitespace 353 | # Don’t include vertical whitespace (empty line) before closing braces. 354 | - vertical_whitespace_closing_braces 355 | # Don’t include vertical whitespace (empty line) after opening braces. 356 | - vertical_whitespace_opening_braces 357 | # Using ternary to call Void functions should be avoided. 358 | - void_function_in_ternary 359 | # Prefer -> Void over -> (). 360 | - void_return 361 | # Delegates should be weak to avoid reference cycles. 362 | - weak_delegate 363 | # Prefer specific XCTest matchers over XCTAssertEqual and XCTAssertNotEqual 364 | - xct_specific_matcher 365 | # An XCTFail call should include a description of the assertion. 366 | - xctfail_message 367 | # The variable should be placed on the left, the constant on the right of a comparison operator. 368 | - yoda_condition 369 | 370 | attributes: 371 | attributes_with_arguments_always_on_line_above: false 372 | 373 | deployment_target: # Availability checks or attributes shouldn’t be using older versions that are satisfied by the deployment target. 374 | iOSApplicationExtension_deployment_target: 16.0 375 | iOS_deployment_target: 16.0 376 | 377 | excluded: # paths to ignore during linting. Takes precedence over `included`. 378 | - .build 379 | - .swiftpm 380 | - .derivedData 381 | 382 | closure_body_length: # Closure bodies should not span too many lines. 383 | - 35 # warning - default: 20 384 | - 35 # error - default: 100 385 | 386 | enum_case_associated_values_count: # Number of associated values in an enum case should be low 387 | - 5 # warning - default: 5 388 | - 5 # error - default: 6 389 | 390 | file_length: # Files should not span too many lines. 391 | - 500 # warning - default: 400 392 | - 500 # error - default: 1000 393 | 394 | function_body_length: # Functions bodies should not span too many lines. 395 | - 50 # warning - default: 40 396 | - 50 # error - default: 100 397 | 398 | function_parameter_count: # Number of function parameters should be low. 399 | - 5 # warning - default: 5 400 | - 5 # error - default: 8 401 | 402 | identifier_name: 403 | excluded: # excluded names 404 | - id 405 | - ok 406 | - or 407 | - p8 408 | - of 409 | - s3 410 | - at 411 | - to 412 | - in 413 | 414 | large_tuple: # Tuples shouldn't have too many members. Create a custom type instead. 415 | - 2 # warning - default: 2 416 | - 2 # error - default: 3 417 | 418 | line_length: # Lines should not span too many characters. 419 | warning: 150 # default: 120 420 | error: 150 # default: 200 421 | ignores_comments: true # default: false 422 | ignores_urls: true # default: false 423 | ignores_function_declarations: false # default: false 424 | ignores_interpolated_strings: true # default: false 425 | 426 | nesting: # Types should be nested at most 2 level deep, and functions should be nested at most 5 levels deep. 427 | type_level: 428 | warning: 2 # warning - default: 1 429 | function_level: 430 | warning: 5 # warning - default: 5 431 | 432 | trailing_closure: 433 | only_single_muted_parameter: true 434 | 435 | type_body_length: # Type bodies should not span too many lines. 436 | - 250 # warning - default: 200 437 | - 250 # error - default: 200 438 | 439 | type_name: 440 | excluded: # excluded names 441 | - ID 442 | 443 | trailing_whitespace: 444 | ignores_empty_lines: true # default: false 445 | ignores_comments: true # default: false 446 | 447 | unused_optional_binding: 448 | ignore_optional_try: true 449 | 450 | vertical_whitespace: # Limit vertical whitespace to a single empty line. 451 | max_empty_lines: 2 # warning - default: 1 452 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | HealthGPT 12 | ==================================================== 13 | 14 | The initial prototype based on [Spezi](https://github.com/StanfordSpezi/Spezi) and the [SpeziTemplateApplication](https://github.com/StanfordSpezi/SpeziTemplateApplication/) was built by [Varun Shenoy](https://varunshenoy.com) in April 2023 in collaboration with the Stanford Biodesign Digital Health team. 15 | 16 | Contributors to HealthGPT include: 17 | 18 | * [Paul Schmiedmayer](https://github.com/PSchmiedmayer) 19 | * [Varun Shenoy](https://github.com/varunshenoy) 20 | * [Vishnu Ravi](https://github.com/vishnuravi) -------------------------------------------------------------------------------- /Figures/Chat-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StanfordBDHG/HealthGPT/8ad37f59c7fe984bcd62f4ffb771fb8b549633d9/Figures/Chat-dark.png -------------------------------------------------------------------------------- /Figures/Chat-dark.png.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /Figures/Chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StanfordBDHG/HealthGPT/8ad37f59c7fe984bcd62f4ffb771fb8b549633d9/Figures/Chat.png -------------------------------------------------------------------------------- /Figures/Chat.png.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /Figures/Example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StanfordBDHG/HealthGPT/8ad37f59c7fe984bcd62f4ffb771fb8b549633d9/Figures/Example.png -------------------------------------------------------------------------------- /Figures/Example.png.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /Figures/Export-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StanfordBDHG/HealthGPT/8ad37f59c7fe984bcd62f4ffb771fb8b549633d9/Figures/Export-dark.png -------------------------------------------------------------------------------- /Figures/Export-dark.png.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /Figures/Export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StanfordBDHG/HealthGPT/8ad37f59c7fe984bcd62f4ffb771fb8b549633d9/Figures/Export.png -------------------------------------------------------------------------------- /Figures/Export.png.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /Figures/Settings-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StanfordBDHG/HealthGPT/8ad37f59c7fe984bcd62f4ffb771fb8b549633d9/Figures/Settings-dark.png -------------------------------------------------------------------------------- /Figures/Settings-dark.png.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /Figures/Settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StanfordBDHG/HealthGPT/8ad37f59c7fe984bcd62f4ffb771fb8b549633d9/Figures/Settings.png -------------------------------------------------------------------------------- /Figures/Settings.png.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /HealthGPT.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2719BC7829F0A31500995C31 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2719BC7729F0A31500995C31 /* SettingsView.swift */; }; 11 | 2719BC8029F0A5DD00995C31 /* UIApplication+Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2719BC7F29F0A5DD00995C31 /* UIApplication+Keyboard.swift */; }; 12 | 272FB34E2A03CDFF0010B98D /* HealthDataFetcher+Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272FB34D2A03CDFF0010B98D /* HealthDataFetcher+Process.swift */; }; 13 | 272FB3502A03D1000010B98D /* HealthDataFetcherError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272FB34F2A03D1000010B98D /* HealthDataFetcherError.swift */; }; 14 | 272FB3522A03D14C0010B98D /* HealthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272FB3512A03D14C0010B98D /* HealthData.swift */; }; 15 | 272FB3542A03F1860010B98D /* PromptGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272FB3532A03F1860010B98D /* PromptGenerator.swift */; }; 16 | 272FB3562A0401130010B98D /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272FB3552A0401130010B98D /* Date+Extensions.swift */; }; 17 | 275DEFCE29EEC5AD0079D453 /* StorageKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275DEFCD29EEC5AD0079D453 /* StorageKeys.swift */; }; 18 | 275DEFD029EEC5D20079D453 /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275DEFCF29EEC5D20079D453 /* FeatureFlags.swift */; }; 19 | 275DEFD529EEC6870079D453 /* HealthKitPermissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275DEFD429EEC6870079D453 /* HealthKitPermissions.swift */; }; 20 | 275DEFD729EEC6A60079D453 /* Disclaimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275DEFD629EEC6A60079D453 /* Disclaimer.swift */; }; 21 | 275DEFD929EEC6B80079D453 /* OnboardingFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275DEFD829EEC6B80079D453 /* OnboardingFlow.swift */; }; 22 | 275DEFDB29EEC6CA0079D453 /* String+ModuleLocalized.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275DEFDA29EEC6CA0079D453 /* String+ModuleLocalized.swift */; }; 23 | 275DEFDD29EEC6DC0079D453 /* Welcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275DEFDC29EEC6DC0079D453 /* Welcome.swift */; }; 24 | 275DEFF229EECA030079D453 /* Binding+Negate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275DEFF129EECA030079D453 /* Binding+Negate.swift */; }; 25 | 275DEFF429EECA180079D453 /* CodableArray+RawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275DEFF329EECA170079D453 /* CodableArray+RawRepresentable.swift */; }; 26 | 27859BFF2A34F15E00397C85 /* Spezi in Frameworks */ = {isa = PBXBuildFile; productRef = 27859BFE2A34F15E00397C85 /* Spezi */; }; 27 | 27859C012A34F15E00397C85 /* XCTSpezi in Frameworks */ = {isa = PBXBuildFile; productRef = 27859C002A34F15E00397C85 /* XCTSpezi */; }; 28 | 27859C042A34F16B00397C85 /* SpeziHealthKit in Frameworks */ = {isa = PBXBuildFile; productRef = 27859C032A34F16B00397C85 /* SpeziHealthKit */; }; 29 | 27859C072A34F17800397C85 /* SpeziOnboarding in Frameworks */ = {isa = PBXBuildFile; productRef = 27859C062A34F17800397C85 /* SpeziOnboarding */; }; 30 | 27859C102A34F1A900397C85 /* SpeziLocalStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 27859C0F2A34F1A900397C85 /* SpeziLocalStorage */; }; 31 | 27859C202A34F2BF00397C85 /* XCTestApp in Frameworks */ = {isa = PBXBuildFile; productRef = 27859C1F2A34F2BF00397C85 /* XCTestApp */; }; 32 | 27859C222A34F2BF00397C85 /* XCTestExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = 27859C212A34F2BF00397C85 /* XCTestExtensions */; }; 33 | 27859C252A34F2D000397C85 /* XCTHealthKit in Frameworks */ = {isa = PBXBuildFile; productRef = 27859C242A34F2D000397C85 /* XCTHealthKit */; }; 34 | 27859C282A34F2DE00397C85 /* XCTRuntimeAssertions in Frameworks */ = {isa = PBXBuildFile; productRef = 27859C272A34F2DE00397C85 /* XCTRuntimeAssertions */; }; 35 | 27B249672A065D360091E52C /* PromptGeneratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27B249662A065D360091E52C /* PromptGeneratorTests.swift */; }; 36 | 27E2CD172AEF564000998FCA /* HealthGPTViewUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E2CD162AEF564000998FCA /* HealthGPTViewUITests.swift */; }; 37 | 2F4242952A8B0393006E2B01 /* OpenAIAPIKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F4242942A8B0393006E2B01 /* OpenAIAPIKey.swift */; }; 38 | 2F4242972A8B03A5006E2B01 /* OpenAIModelSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F4242962A8B03A5006E2B01 /* OpenAIModelSelection.swift */; }; 39 | 2F4242992A8B0432006E2B01 /* HealthGPTStandard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F4242982A8B0432006E2B01 /* HealthGPTStandard.swift */; }; 40 | 2F4E21D829F0518B0067EE98 /* OnboardingUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F4E21D729F0518B0067EE98 /* OnboardingUITests.swift */; }; 41 | 2F4E23832989D51F0013F3D9 /* HealthGPTAppTestingSetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F4E23822989D51F0013F3D9 /* HealthGPTAppTestingSetup.swift */; }; 42 | 2F5E32BD297E05EA003432F8 /* HealthGPTAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F5E32BC297E05EA003432F8 /* HealthGPTAppDelegate.swift */; }; 43 | 63439A4E2BA6069E008BDBFD /* SpeziLLM in Frameworks */ = {isa = PBXBuildFile; productRef = 63439A4D2BA6069E008BDBFD /* SpeziLLM */; }; 44 | 63439A502BA6069E008BDBFD /* SpeziLLMOpenAI in Frameworks */ = {isa = PBXBuildFile; productRef = 63439A4F2BA6069E008BDBFD /* SpeziLLMOpenAI */; }; 45 | 634523F12BAEF62E00A6E2A1 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 634523F02BAEF62D00A6E2A1 /* Localizable.xcstrings */; }; 46 | 63497B6C2BBF095A001F8419 /* LLMSourceSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63497B6B2BBF095A001F8419 /* LLMSourceSelection.swift */; }; 47 | 63497B6E2BBF0C0B001F8419 /* LLMSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63497B6D2BBF0C0B001F8419 /* LLMSource.swift */; }; 48 | 63DAAF712BBEB14A009E5E19 /* SpeziLLMLocal in Frameworks */ = {isa = PBXBuildFile; productRef = 63DAAF702BBEB14A009E5E19 /* SpeziLLMLocal */; }; 49 | 63DAAF732BBEB14A009E5E19 /* SpeziLLMLocalDownload in Frameworks */ = {isa = PBXBuildFile; productRef = 63DAAF722BBEB14A009E5E19 /* SpeziLLMLocalDownload */; }; 50 | 63DAAF752BBEB24D009E5E19 /* LLMLocalDownload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63DAAF742BBEB24D009E5E19 /* LLMLocalDownload.swift */; }; 51 | 63E247642DC9B77C0044A0BB /* SpeziKeychainStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 63E247632DC9B77C0044A0BB /* SpeziKeychainStorage */; }; 52 | 653A2551283387FE005D4D48 /* HealthGPTApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 653A2550283387FE005D4D48 /* HealthGPTApplication.swift */; }; 53 | 653A255528338800005D4D48 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 653A255428338800005D4D48 /* Assets.xcassets */; }; 54 | E21D86A029EDB17500490C26 /* HealthDataFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = E21D869F29EDB17500490C26 /* HealthDataFetcher.swift */; }; 55 | E22B62EF29E8CE1E008F3484 /* HealthGPTView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22B62EE29E8CE1E008F3484 /* HealthGPTView.swift */; }; 56 | F381BD3C2A4DE41900BCEB69 /* HealthDataInterpreter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F381BD3B2A4DE41900BCEB69 /* HealthDataInterpreter.swift */; }; 57 | /* End PBXBuildFile section */ 58 | 59 | /* Begin PBXContainerItemProxy section */ 60 | 27B2495E2A06590E0091E52C /* PBXContainerItemProxy */ = { 61 | isa = PBXContainerItemProxy; 62 | containerPortal = 653A2545283387FE005D4D48 /* Project object */; 63 | proxyType = 1; 64 | remoteGlobalIDString = 653A254C283387FE005D4D48; 65 | remoteInfo = HealthGPT; 66 | }; 67 | 2F4E21DB29F0518B0067EE98 /* PBXContainerItemProxy */ = { 68 | isa = PBXContainerItemProxy; 69 | containerPortal = 653A2545283387FE005D4D48 /* Project object */; 70 | proxyType = 1; 71 | remoteGlobalIDString = 653A254C283387FE005D4D48; 72 | remoteInfo = HealthGPT; 73 | }; 74 | /* End PBXContainerItemProxy section */ 75 | 76 | /* Begin PBXFileReference section */ 77 | 2719BC7729F0A31500995C31 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; 78 | 2719BC7F29F0A5DD00995C31 /* UIApplication+Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Keyboard.swift"; sourceTree = ""; }; 79 | 272FB34D2A03CDFF0010B98D /* HealthDataFetcher+Process.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HealthDataFetcher+Process.swift"; sourceTree = ""; }; 80 | 272FB34F2A03D1000010B98D /* HealthDataFetcherError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthDataFetcherError.swift; sourceTree = ""; }; 81 | 272FB3512A03D14C0010B98D /* HealthData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthData.swift; sourceTree = ""; }; 82 | 272FB3532A03F1860010B98D /* PromptGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PromptGenerator.swift; sourceTree = ""; }; 83 | 272FB3552A0401130010B98D /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = ""; }; 84 | 275DEFCD29EEC5AD0079D453 /* StorageKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageKeys.swift; sourceTree = ""; }; 85 | 275DEFCF29EEC5D20079D453 /* FeatureFlags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlags.swift; sourceTree = ""; }; 86 | 275DEFD429EEC6870079D453 /* HealthKitPermissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthKitPermissions.swift; sourceTree = ""; }; 87 | 275DEFD629EEC6A60079D453 /* Disclaimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Disclaimer.swift; sourceTree = ""; }; 88 | 275DEFD829EEC6B80079D453 /* OnboardingFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingFlow.swift; sourceTree = ""; }; 89 | 275DEFDA29EEC6CA0079D453 /* String+ModuleLocalized.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+ModuleLocalized.swift"; sourceTree = ""; }; 90 | 275DEFDC29EEC6DC0079D453 /* Welcome.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Welcome.swift; sourceTree = ""; }; 91 | 275DEFF129EECA030079D453 /* Binding+Negate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Negate.swift"; sourceTree = ""; }; 92 | 275DEFF329EECA170079D453 /* CodableArray+RawRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CodableArray+RawRepresentable.swift"; sourceTree = ""; }; 93 | 27B2495A2A06590E0091E52C /* HealthGPTTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HealthGPTTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 94 | 27B249662A065D360091E52C /* PromptGeneratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PromptGeneratorTests.swift; sourceTree = ""; }; 95 | 27E2CD162AEF564000998FCA /* HealthGPTViewUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthGPTViewUITests.swift; sourceTree = ""; }; 96 | 2F4242942A8B0393006E2B01 /* OpenAIAPIKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenAIAPIKey.swift; sourceTree = ""; }; 97 | 2F4242962A8B03A5006E2B01 /* OpenAIModelSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenAIModelSelection.swift; sourceTree = ""; }; 98 | 2F4242982A8B0432006E2B01 /* HealthGPTStandard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthGPTStandard.swift; sourceTree = ""; }; 99 | 2F4E21D529F0518B0067EE98 /* HealthGPTUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HealthGPTUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 100 | 2F4E21D729F0518B0067EE98 /* OnboardingUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingUITests.swift; sourceTree = ""; }; 101 | 2F4E23822989D51F0013F3D9 /* HealthGPTAppTestingSetup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthGPTAppTestingSetup.swift; sourceTree = ""; }; 102 | 2F5E32BC297E05EA003432F8 /* HealthGPTAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthGPTAppDelegate.swift; sourceTree = ""; }; 103 | 2FAEC07F297F583900C11C42 /* HealthGPT.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = HealthGPT.entitlements; sourceTree = ""; }; 104 | 634523F02BAEF62D00A6E2A1 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; 105 | 63497B6B2BBF095A001F8419 /* LLMSourceSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LLMSourceSelection.swift; sourceTree = ""; }; 106 | 63497B6D2BBF0C0B001F8419 /* LLMSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LLMSource.swift; sourceTree = ""; }; 107 | 63DAAF742BBEB24D009E5E19 /* LLMLocalDownload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LLMLocalDownload.swift; sourceTree = ""; }; 108 | 653A254D283387FE005D4D48 /* HealthGPT.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HealthGPT.app; sourceTree = BUILT_PRODUCTS_DIR; }; 109 | 653A2550283387FE005D4D48 /* HealthGPTApplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthGPTApplication.swift; sourceTree = ""; }; 110 | 653A255428338800005D4D48 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 111 | 653A258928339462005D4D48 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 112 | E21D869F29EDB17500490C26 /* HealthDataFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthDataFetcher.swift; sourceTree = ""; }; 113 | E22B62EE29E8CE1E008F3484 /* HealthGPTView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthGPTView.swift; sourceTree = ""; }; 114 | E26542A229E8D70100094BAD /* OpenAI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = OpenAI; path = "/Users/varunshenoy/Library/Developer/Xcode/DerivedData/TemplateApplication-fhjipkzkgfsoxiezldfjbwynfudi/SourcePackages/checkouts/OpenAI"; sourceTree = ""; }; 115 | F381BD3B2A4DE41900BCEB69 /* HealthDataInterpreter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthDataInterpreter.swift; sourceTree = ""; }; 116 | /* End PBXFileReference section */ 117 | 118 | /* Begin PBXFrameworksBuildPhase section */ 119 | 27B249572A06590E0091E52C /* Frameworks */ = { 120 | isa = PBXFrameworksBuildPhase; 121 | buildActionMask = 2147483647; 122 | files = ( 123 | ); 124 | runOnlyForDeploymentPostprocessing = 0; 125 | }; 126 | 2F4E21D229F0518B0067EE98 /* Frameworks */ = { 127 | isa = PBXFrameworksBuildPhase; 128 | buildActionMask = 2147483647; 129 | files = ( 130 | 27859C202A34F2BF00397C85 /* XCTestApp in Frameworks */, 131 | 27859C222A34F2BF00397C85 /* XCTestExtensions in Frameworks */, 132 | 27859C282A34F2DE00397C85 /* XCTRuntimeAssertions in Frameworks */, 133 | 27859C252A34F2D000397C85 /* XCTHealthKit in Frameworks */, 134 | ); 135 | runOnlyForDeploymentPostprocessing = 0; 136 | }; 137 | 653A254A283387FE005D4D48 /* Frameworks */ = { 138 | isa = PBXFrameworksBuildPhase; 139 | buildActionMask = 2147483647; 140 | files = ( 141 | 63DAAF712BBEB14A009E5E19 /* SpeziLLMLocal in Frameworks */, 142 | 63439A4E2BA6069E008BDBFD /* SpeziLLM in Frameworks */, 143 | 27859C072A34F17800397C85 /* SpeziOnboarding in Frameworks */, 144 | 27859C012A34F15E00397C85 /* XCTSpezi in Frameworks */, 145 | 63DAAF732BBEB14A009E5E19 /* SpeziLLMLocalDownload in Frameworks */, 146 | 27859C042A34F16B00397C85 /* SpeziHealthKit in Frameworks */, 147 | 63E247642DC9B77C0044A0BB /* SpeziKeychainStorage in Frameworks */, 148 | 27859BFF2A34F15E00397C85 /* Spezi in Frameworks */, 149 | 27859C102A34F1A900397C85 /* SpeziLocalStorage in Frameworks */, 150 | 63439A502BA6069E008BDBFD /* SpeziLLMOpenAI in Frameworks */, 151 | ); 152 | runOnlyForDeploymentPostprocessing = 0; 153 | }; 154 | /* End PBXFrameworksBuildPhase section */ 155 | 156 | /* Begin PBXGroup section */ 157 | 275DEFCC29EEC58A0079D453 /* SharedContext */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | 275DEFCF29EEC5D20079D453 /* FeatureFlags.swift */, 161 | 275DEFCD29EEC5AD0079D453 /* StorageKeys.swift */, 162 | ); 163 | path = SharedContext; 164 | sourceTree = ""; 165 | }; 166 | 275DEFD129EEC6620079D453 /* Onboarding */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 275DEFD629EEC6A60079D453 /* Disclaimer.swift */, 170 | 275DEFD429EEC6870079D453 /* HealthKitPermissions.swift */, 171 | 2F4242942A8B0393006E2B01 /* OpenAIAPIKey.swift */, 172 | 2F4242962A8B03A5006E2B01 /* OpenAIModelSelection.swift */, 173 | 275DEFD829EEC6B80079D453 /* OnboardingFlow.swift */, 174 | 275DEFDA29EEC6CA0079D453 /* String+ModuleLocalized.swift */, 175 | 275DEFDC29EEC6DC0079D453 /* Welcome.swift */, 176 | 63DAAF742BBEB24D009E5E19 /* LLMLocalDownload.swift */, 177 | 63497B6B2BBF095A001F8419 /* LLMSourceSelection.swift */, 178 | 63497B6D2BBF0C0B001F8419 /* LLMSource.swift */, 179 | ); 180 | path = Onboarding; 181 | sourceTree = ""; 182 | }; 183 | 275DEFEE29EEC9E30079D453 /* Helper */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | 275DEFF129EECA030079D453 /* Binding+Negate.swift */, 187 | 275DEFF329EECA170079D453 /* CodableArray+RawRepresentable.swift */, 188 | 272FB3552A0401130010B98D /* Date+Extensions.swift */, 189 | ); 190 | path = Helper; 191 | sourceTree = ""; 192 | }; 193 | 27B2495B2A06590E0091E52C /* HealthGPTTests */ = { 194 | isa = PBXGroup; 195 | children = ( 196 | 27B249662A065D360091E52C /* PromptGeneratorTests.swift */, 197 | ); 198 | path = HealthGPTTests; 199 | sourceTree = ""; 200 | }; 201 | 2F4E21D629F0518B0067EE98 /* HealthGPTUITests */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | 2F4E21D729F0518B0067EE98 /* OnboardingUITests.swift */, 205 | 27E2CD162AEF564000998FCA /* HealthGPTViewUITests.swift */, 206 | ); 207 | path = HealthGPTUITests; 208 | sourceTree = ""; 209 | }; 210 | 2FC9759D2978E30800BA99FE /* Supporting Files */ = { 211 | isa = PBXGroup; 212 | children = ( 213 | 653A255428338800005D4D48 /* Assets.xcassets */, 214 | 2FAEC07F297F583900C11C42 /* HealthGPT.entitlements */, 215 | 653A258928339462005D4D48 /* Info.plist */, 216 | 634523F02BAEF62D00A6E2A1 /* Localizable.xcstrings */, 217 | ); 218 | path = "Supporting Files"; 219 | sourceTree = ""; 220 | }; 221 | 2FF8DBF129F0414E00239E1A /* HealthGPT */ = { 222 | isa = PBXGroup; 223 | children = ( 224 | 272FB3512A03D14C0010B98D /* HealthData.swift */, 225 | E21D869F29EDB17500490C26 /* HealthDataFetcher.swift */, 226 | 272FB34D2A03CDFF0010B98D /* HealthDataFetcher+Process.swift */, 227 | 272FB34F2A03D1000010B98D /* HealthDataFetcherError.swift */, 228 | E22B62EE29E8CE1E008F3484 /* HealthGPTView.swift */, 229 | 2719BC7729F0A31500995C31 /* SettingsView.swift */, 230 | 2719BC7F29F0A5DD00995C31 /* UIApplication+Keyboard.swift */, 231 | 272FB3532A03F1860010B98D /* PromptGenerator.swift */, 232 | F381BD3B2A4DE41900BCEB69 /* HealthDataInterpreter.swift */, 233 | ); 234 | path = HealthGPT; 235 | sourceTree = ""; 236 | }; 237 | 653A2544283387FE005D4D48 = { 238 | isa = PBXGroup; 239 | children = ( 240 | 653A254F283387FE005D4D48 /* HealthGPT */, 241 | 2F4E21D629F0518B0067EE98 /* HealthGPTUITests */, 242 | 27B2495B2A06590E0091E52C /* HealthGPTTests */, 243 | 653A254E283387FE005D4D48 /* Products */, 244 | 653A258B283395A7005D4D48 /* Frameworks */, 245 | ); 246 | sourceTree = ""; 247 | }; 248 | 653A254E283387FE005D4D48 /* Products */ = { 249 | isa = PBXGroup; 250 | children = ( 251 | 653A254D283387FE005D4D48 /* HealthGPT.app */, 252 | 2F4E21D529F0518B0067EE98 /* HealthGPTUITests.xctest */, 253 | 27B2495A2A06590E0091E52C /* HealthGPTTests.xctest */, 254 | ); 255 | name = Products; 256 | sourceTree = ""; 257 | }; 258 | 653A254F283387FE005D4D48 /* HealthGPT */ = { 259 | isa = PBXGroup; 260 | children = ( 261 | 2FF8DBF129F0414E00239E1A /* HealthGPT */, 262 | 275DEFEE29EEC9E30079D453 /* Helper */, 263 | 275DEFD129EEC6620079D453 /* Onboarding */, 264 | 275DEFCC29EEC58A0079D453 /* SharedContext */, 265 | 2FC9759D2978E30800BA99FE /* Supporting Files */, 266 | 2F5E32BC297E05EA003432F8 /* HealthGPTAppDelegate.swift */, 267 | 653A2550283387FE005D4D48 /* HealthGPTApplication.swift */, 268 | 2F4242982A8B0432006E2B01 /* HealthGPTStandard.swift */, 269 | 2F4E23822989D51F0013F3D9 /* HealthGPTAppTestingSetup.swift */, 270 | ); 271 | path = HealthGPT; 272 | sourceTree = ""; 273 | }; 274 | 653A258B283395A7005D4D48 /* Frameworks */ = { 275 | isa = PBXGroup; 276 | children = ( 277 | E26542A229E8D70100094BAD /* OpenAI */, 278 | ); 279 | name = Frameworks; 280 | sourceTree = ""; 281 | }; 282 | /* End PBXGroup section */ 283 | 284 | /* Begin PBXNativeTarget section */ 285 | 27B249592A06590E0091E52C /* HealthGPTTests */ = { 286 | isa = PBXNativeTarget; 287 | buildConfigurationList = 27B249632A06590E0091E52C /* Build configuration list for PBXNativeTarget "HealthGPTTests" */; 288 | buildPhases = ( 289 | 27B249562A06590E0091E52C /* Sources */, 290 | 27B249572A06590E0091E52C /* Frameworks */, 291 | 27B249582A06590E0091E52C /* Resources */, 292 | ); 293 | buildRules = ( 294 | ); 295 | dependencies = ( 296 | 27B2495F2A06590E0091E52C /* PBXTargetDependency */, 297 | ); 298 | name = HealthGPTTests; 299 | productName = HealthGPTTests; 300 | productReference = 27B2495A2A06590E0091E52C /* HealthGPTTests.xctest */; 301 | productType = "com.apple.product-type.bundle.unit-test"; 302 | }; 303 | 2F4E21D429F0518B0067EE98 /* HealthGPTUITests */ = { 304 | isa = PBXNativeTarget; 305 | buildConfigurationList = 2F4E21E029F0518B0067EE98 /* Build configuration list for PBXNativeTarget "HealthGPTUITests" */; 306 | buildPhases = ( 307 | 2F4E21D129F0518B0067EE98 /* Sources */, 308 | 2F4E21D229F0518B0067EE98 /* Frameworks */, 309 | 2F4E21D329F0518B0067EE98 /* Resources */, 310 | ); 311 | buildRules = ( 312 | ); 313 | dependencies = ( 314 | 2F4E21DC29F0518B0067EE98 /* PBXTargetDependency */, 315 | ); 316 | name = HealthGPTUITests; 317 | packageProductDependencies = ( 318 | 27859C1F2A34F2BF00397C85 /* XCTestApp */, 319 | 27859C212A34F2BF00397C85 /* XCTestExtensions */, 320 | 27859C242A34F2D000397C85 /* XCTHealthKit */, 321 | 27859C272A34F2DE00397C85 /* XCTRuntimeAssertions */, 322 | ); 323 | productName = HealthGPTUITests; 324 | productReference = 2F4E21D529F0518B0067EE98 /* HealthGPTUITests.xctest */; 325 | productType = "com.apple.product-type.bundle.ui-testing"; 326 | }; 327 | 653A254C283387FE005D4D48 /* HealthGPT */ = { 328 | isa = PBXNativeTarget; 329 | buildConfigurationList = 653A257128338800005D4D48 /* Build configuration list for PBXNativeTarget "HealthGPT" */; 330 | buildPhases = ( 331 | 653A2549283387FE005D4D48 /* Sources */, 332 | 653A254A283387FE005D4D48 /* Frameworks */, 333 | 653A254B283387FE005D4D48 /* Resources */, 334 | 2F5B528D29BD237B002020B7 /* ShellScript */, 335 | ); 336 | buildRules = ( 337 | ); 338 | dependencies = ( 339 | ); 340 | name = HealthGPT; 341 | packageProductDependencies = ( 342 | 27859BFE2A34F15E00397C85 /* Spezi */, 343 | 27859C002A34F15E00397C85 /* XCTSpezi */, 344 | 27859C032A34F16B00397C85 /* SpeziHealthKit */, 345 | 27859C062A34F17800397C85 /* SpeziOnboarding */, 346 | 27859C0F2A34F1A900397C85 /* SpeziLocalStorage */, 347 | 63439A4D2BA6069E008BDBFD /* SpeziLLM */, 348 | 63439A4F2BA6069E008BDBFD /* SpeziLLMOpenAI */, 349 | 63DAAF702BBEB14A009E5E19 /* SpeziLLMLocal */, 350 | 63DAAF722BBEB14A009E5E19 /* SpeziLLMLocalDownload */, 351 | 63E247632DC9B77C0044A0BB /* SpeziKeychainStorage */, 352 | ); 353 | productName = TemplateApplication; 354 | productReference = 653A254D283387FE005D4D48 /* HealthGPT.app */; 355 | productType = "com.apple.product-type.application"; 356 | }; 357 | /* End PBXNativeTarget section */ 358 | 359 | /* Begin PBXProject section */ 360 | 653A2545283387FE005D4D48 /* Project object */ = { 361 | isa = PBXProject; 362 | attributes = { 363 | BuildIndependentTargetsInParallel = 1; 364 | LastSwiftUpdateCheck = 1430; 365 | LastUpgradeCheck = 1340; 366 | TargetAttributes = { 367 | 27B249592A06590E0091E52C = { 368 | CreatedOnToolsVersion = 14.3; 369 | TestTargetID = 653A254C283387FE005D4D48; 370 | }; 371 | 2F4E21D429F0518B0067EE98 = { 372 | CreatedOnToolsVersion = 14.3; 373 | TestTargetID = 653A254C283387FE005D4D48; 374 | }; 375 | 653A254C283387FE005D4D48 = { 376 | CreatedOnToolsVersion = 13.4; 377 | }; 378 | }; 379 | }; 380 | buildConfigurationList = 653A2548283387FE005D4D48 /* Build configuration list for PBXProject "HealthGPT" */; 381 | compatibilityVersion = "Xcode 13.0"; 382 | developmentRegion = en; 383 | hasScannedForEncodings = 0; 384 | knownRegions = ( 385 | en, 386 | Base, 387 | ); 388 | mainGroup = 653A2544283387FE005D4D48; 389 | packageReferences = ( 390 | 27859BFD2A34F15E00397C85 /* XCRemoteSwiftPackageReference "Spezi" */, 391 | 27859C022A34F16A00397C85 /* XCRemoteSwiftPackageReference "SpeziHealthKit" */, 392 | 27859C052A34F17800397C85 /* XCRemoteSwiftPackageReference "SpeziOnboarding" */, 393 | 27859C0E2A34F1A900397C85 /* XCRemoteSwiftPackageReference "SpeziStorage" */, 394 | 27859C1E2A34F2BF00397C85 /* XCRemoteSwiftPackageReference "XCTestExtensions" */, 395 | 27859C232A34F2D000397C85 /* XCRemoteSwiftPackageReference "XCTHealthKit" */, 396 | 27859C262A34F2DE00397C85 /* XCRemoteSwiftPackageReference "XCTRuntimeAssertions" */, 397 | 63439A4C2BA6069E008BDBFD /* XCRemoteSwiftPackageReference "SpeziLLM" */, 398 | ); 399 | productRefGroup = 653A254E283387FE005D4D48 /* Products */; 400 | projectDirPath = ""; 401 | projectRoot = ""; 402 | targets = ( 403 | 653A254C283387FE005D4D48 /* HealthGPT */, 404 | 2F4E21D429F0518B0067EE98 /* HealthGPTUITests */, 405 | 27B249592A06590E0091E52C /* HealthGPTTests */, 406 | ); 407 | }; 408 | /* End PBXProject section */ 409 | 410 | /* Begin PBXResourcesBuildPhase section */ 411 | 27B249582A06590E0091E52C /* Resources */ = { 412 | isa = PBXResourcesBuildPhase; 413 | buildActionMask = 2147483647; 414 | files = ( 415 | ); 416 | runOnlyForDeploymentPostprocessing = 0; 417 | }; 418 | 2F4E21D329F0518B0067EE98 /* Resources */ = { 419 | isa = PBXResourcesBuildPhase; 420 | buildActionMask = 2147483647; 421 | files = ( 422 | ); 423 | runOnlyForDeploymentPostprocessing = 0; 424 | }; 425 | 653A254B283387FE005D4D48 /* Resources */ = { 426 | isa = PBXResourcesBuildPhase; 427 | buildActionMask = 2147483647; 428 | files = ( 429 | 653A255528338800005D4D48 /* Assets.xcassets in Resources */, 430 | 634523F12BAEF62E00A6E2A1 /* Localizable.xcstrings in Resources */, 431 | ); 432 | runOnlyForDeploymentPostprocessing = 0; 433 | }; 434 | /* End PBXResourcesBuildPhase section */ 435 | 436 | /* Begin PBXShellScriptBuildPhase section */ 437 | 2F5B528D29BD237B002020B7 /* ShellScript */ = { 438 | isa = PBXShellScriptBuildPhase; 439 | alwaysOutOfDate = 1; 440 | buildActionMask = 2147483647; 441 | files = ( 442 | ); 443 | inputFileListPaths = ( 444 | ); 445 | inputPaths = ( 446 | ); 447 | outputFileListPaths = ( 448 | ); 449 | outputPaths = ( 450 | ); 451 | runOnlyForDeploymentPostprocessing = 0; 452 | shellPath = /bin/sh; 453 | shellScript = "if [ \"${CONFIGURATION}\" = \"Debug\" ]; then\n export PATH=\"$PATH:/opt/homebrew/bin\"\n if which swiftlint > /dev/null; then\n swiftlint\n else\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\n fi\nfi\n"; 454 | }; 455 | /* End PBXShellScriptBuildPhase section */ 456 | 457 | /* Begin PBXSourcesBuildPhase section */ 458 | 27B249562A06590E0091E52C /* Sources */ = { 459 | isa = PBXSourcesBuildPhase; 460 | buildActionMask = 2147483647; 461 | files = ( 462 | 27B249672A065D360091E52C /* PromptGeneratorTests.swift in Sources */, 463 | ); 464 | runOnlyForDeploymentPostprocessing = 0; 465 | }; 466 | 2F4E21D129F0518B0067EE98 /* Sources */ = { 467 | isa = PBXSourcesBuildPhase; 468 | buildActionMask = 2147483647; 469 | files = ( 470 | 27E2CD172AEF564000998FCA /* HealthGPTViewUITests.swift in Sources */, 471 | 2F4E21D829F0518B0067EE98 /* OnboardingUITests.swift in Sources */, 472 | ); 473 | runOnlyForDeploymentPostprocessing = 0; 474 | }; 475 | 653A2549283387FE005D4D48 /* Sources */ = { 476 | isa = PBXSourcesBuildPhase; 477 | buildActionMask = 2147483647; 478 | files = ( 479 | 275DEFD929EEC6B80079D453 /* OnboardingFlow.swift in Sources */, 480 | 275DEFDD29EEC6DC0079D453 /* Welcome.swift in Sources */, 481 | 275DEFD729EEC6A60079D453 /* Disclaimer.swift in Sources */, 482 | 275DEFF429EECA180079D453 /* CodableArray+RawRepresentable.swift in Sources */, 483 | 2719BC7829F0A31500995C31 /* SettingsView.swift in Sources */, 484 | 63DAAF752BBEB24D009E5E19 /* LLMLocalDownload.swift in Sources */, 485 | 2F4242992A8B0432006E2B01 /* HealthGPTStandard.swift in Sources */, 486 | 2F4242972A8B03A5006E2B01 /* OpenAIModelSelection.swift in Sources */, 487 | 2F4E23832989D51F0013F3D9 /* HealthGPTAppTestingSetup.swift in Sources */, 488 | 272FB34E2A03CDFF0010B98D /* HealthDataFetcher+Process.swift in Sources */, 489 | 275DEFD029EEC5D20079D453 /* FeatureFlags.swift in Sources */, 490 | 272FB3522A03D14C0010B98D /* HealthData.swift in Sources */, 491 | 63497B6E2BBF0C0B001F8419 /* LLMSource.swift in Sources */, 492 | F381BD3C2A4DE41900BCEB69 /* HealthDataInterpreter.swift in Sources */, 493 | 275DEFF229EECA030079D453 /* Binding+Negate.swift in Sources */, 494 | 2F5E32BD297E05EA003432F8 /* HealthGPTAppDelegate.swift in Sources */, 495 | 275DEFD529EEC6870079D453 /* HealthKitPermissions.swift in Sources */, 496 | 272FB3542A03F1860010B98D /* PromptGenerator.swift in Sources */, 497 | 63497B6C2BBF095A001F8419 /* LLMSourceSelection.swift in Sources */, 498 | 275DEFDB29EEC6CA0079D453 /* String+ModuleLocalized.swift in Sources */, 499 | 2719BC8029F0A5DD00995C31 /* UIApplication+Keyboard.swift in Sources */, 500 | E22B62EF29E8CE1E008F3484 /* HealthGPTView.swift in Sources */, 501 | 275DEFCE29EEC5AD0079D453 /* StorageKeys.swift in Sources */, 502 | 272FB3502A03D1000010B98D /* HealthDataFetcherError.swift in Sources */, 503 | 2F4242952A8B0393006E2B01 /* OpenAIAPIKey.swift in Sources */, 504 | 272FB3562A0401130010B98D /* Date+Extensions.swift in Sources */, 505 | 653A2551283387FE005D4D48 /* HealthGPTApplication.swift in Sources */, 506 | E21D86A029EDB17500490C26 /* HealthDataFetcher.swift in Sources */, 507 | ); 508 | runOnlyForDeploymentPostprocessing = 0; 509 | }; 510 | /* End PBXSourcesBuildPhase section */ 511 | 512 | /* Begin PBXTargetDependency section */ 513 | 27B2495F2A06590E0091E52C /* PBXTargetDependency */ = { 514 | isa = PBXTargetDependency; 515 | target = 653A254C283387FE005D4D48 /* HealthGPT */; 516 | targetProxy = 27B2495E2A06590E0091E52C /* PBXContainerItemProxy */; 517 | }; 518 | 2F4E21DC29F0518B0067EE98 /* PBXTargetDependency */ = { 519 | isa = PBXTargetDependency; 520 | target = 653A254C283387FE005D4D48 /* HealthGPT */; 521 | targetProxy = 2F4E21DB29F0518B0067EE98 /* PBXContainerItemProxy */; 522 | }; 523 | /* End PBXTargetDependency section */ 524 | 525 | /* Begin XCBuildConfiguration section */ 526 | 27B249602A06590E0091E52C /* Debug */ = { 527 | isa = XCBuildConfiguration; 528 | buildSettings = { 529 | BUNDLE_LOADER = "$(TEST_HOST)"; 530 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 531 | CODE_SIGN_STYLE = Automatic; 532 | CURRENT_PROJECT_VERSION = 1; 533 | GENERATE_INFOPLIST_FILE = YES; 534 | IPHONEOS_DEPLOYMENT_TARGET = 17.0; 535 | MARKETING_VERSION = 1.0; 536 | PRODUCT_BUNDLE_IDENTIFIER = com.stanfordBDHG.HealthGPTTests; 537 | PRODUCT_NAME = "$(TARGET_NAME)"; 538 | SWIFT_EMIT_LOC_STRINGS = NO; 539 | SWIFT_VERSION = 5.0; 540 | TARGETED_DEVICE_FAMILY = "1,2"; 541 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HealthGPT.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/HealthGPT"; 542 | }; 543 | name = Debug; 544 | }; 545 | 27B249612A06590E0091E52C /* Test */ = { 546 | isa = XCBuildConfiguration; 547 | buildSettings = { 548 | BUNDLE_LOADER = "$(TEST_HOST)"; 549 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 550 | CODE_SIGN_STYLE = Automatic; 551 | CURRENT_PROJECT_VERSION = 1; 552 | GENERATE_INFOPLIST_FILE = YES; 553 | IPHONEOS_DEPLOYMENT_TARGET = 17.0; 554 | MARKETING_VERSION = 1.0; 555 | PRODUCT_BUNDLE_IDENTIFIER = com.stanfordBDHG.HealthGPTTests; 556 | PRODUCT_NAME = "$(TARGET_NAME)"; 557 | SWIFT_EMIT_LOC_STRINGS = NO; 558 | SWIFT_VERSION = 5.0; 559 | TARGETED_DEVICE_FAMILY = "1,2"; 560 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HealthGPT.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/HealthGPT"; 561 | }; 562 | name = Test; 563 | }; 564 | 27B249622A06590E0091E52C /* Release */ = { 565 | isa = XCBuildConfiguration; 566 | buildSettings = { 567 | BUNDLE_LOADER = "$(TEST_HOST)"; 568 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 569 | CODE_SIGN_STYLE = Automatic; 570 | CURRENT_PROJECT_VERSION = 1; 571 | GENERATE_INFOPLIST_FILE = YES; 572 | IPHONEOS_DEPLOYMENT_TARGET = 17.0; 573 | MARKETING_VERSION = 1.0; 574 | PRODUCT_BUNDLE_IDENTIFIER = com.stanfordBDHG.HealthGPTTests; 575 | PRODUCT_NAME = "$(TARGET_NAME)"; 576 | SWIFT_EMIT_LOC_STRINGS = NO; 577 | SWIFT_VERSION = 5.0; 578 | TARGETED_DEVICE_FAMILY = "1,2"; 579 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HealthGPT.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/HealthGPT"; 580 | }; 581 | name = Release; 582 | }; 583 | 2F4E21DD29F0518B0067EE98 /* Debug */ = { 584 | isa = XCBuildConfiguration; 585 | buildSettings = { 586 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 587 | CODE_SIGN_STYLE = Automatic; 588 | CURRENT_PROJECT_VERSION = 1; 589 | DEVELOPMENT_TEAM = ""; 590 | GENERATE_INFOPLIST_FILE = YES; 591 | IPHONEOS_DEPLOYMENT_TARGET = 17.0; 592 | MARKETING_VERSION = 1.0; 593 | PRODUCT_BUNDLE_IDENTIFIER = edu.stanford.HealthGPTUITests; 594 | PRODUCT_NAME = "$(TARGET_NAME)"; 595 | SWIFT_EMIT_LOC_STRINGS = NO; 596 | SWIFT_VERSION = 5.0; 597 | TARGETED_DEVICE_FAMILY = "1,2"; 598 | TEST_TARGET_NAME = HealthGPT; 599 | }; 600 | name = Debug; 601 | }; 602 | 2F4E21DE29F0518B0067EE98 /* Test */ = { 603 | isa = XCBuildConfiguration; 604 | buildSettings = { 605 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 606 | CODE_SIGN_STYLE = Automatic; 607 | CURRENT_PROJECT_VERSION = 1; 608 | DEVELOPMENT_TEAM = ""; 609 | GENERATE_INFOPLIST_FILE = YES; 610 | IPHONEOS_DEPLOYMENT_TARGET = 17.0; 611 | MARKETING_VERSION = 1.0; 612 | PRODUCT_BUNDLE_IDENTIFIER = edu.stanford.HealthGPTUITests; 613 | PRODUCT_NAME = "$(TARGET_NAME)"; 614 | SWIFT_EMIT_LOC_STRINGS = NO; 615 | SWIFT_VERSION = 5.0; 616 | TARGETED_DEVICE_FAMILY = "1,2"; 617 | TEST_TARGET_NAME = HealthGPT; 618 | }; 619 | name = Test; 620 | }; 621 | 2F4E21DF29F0518B0067EE98 /* Release */ = { 622 | isa = XCBuildConfiguration; 623 | buildSettings = { 624 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 625 | CODE_SIGN_STYLE = Automatic; 626 | CURRENT_PROJECT_VERSION = 1; 627 | DEVELOPMENT_TEAM = ""; 628 | GENERATE_INFOPLIST_FILE = YES; 629 | IPHONEOS_DEPLOYMENT_TARGET = 17.0; 630 | MARKETING_VERSION = 1.0; 631 | PRODUCT_BUNDLE_IDENTIFIER = edu.stanford.HealthGPTUITests; 632 | PRODUCT_NAME = "$(TARGET_NAME)"; 633 | SWIFT_EMIT_LOC_STRINGS = NO; 634 | SWIFT_VERSION = 5.0; 635 | TARGETED_DEVICE_FAMILY = "1,2"; 636 | TEST_TARGET_NAME = HealthGPT; 637 | }; 638 | name = Release; 639 | }; 640 | 2FEE10302998C89C000822E1 /* Test */ = { 641 | isa = XCBuildConfiguration; 642 | buildSettings = { 643 | ALWAYS_SEARCH_USER_PATHS = NO; 644 | CLANG_ANALYZER_NONNULL = YES; 645 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 646 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 647 | CLANG_ENABLE_MODULES = YES; 648 | CLANG_ENABLE_OBJC_ARC = YES; 649 | CLANG_ENABLE_OBJC_WEAK = YES; 650 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 651 | CLANG_WARN_BOOL_CONVERSION = YES; 652 | CLANG_WARN_COMMA = YES; 653 | CLANG_WARN_CONSTANT_CONVERSION = YES; 654 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 655 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 656 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 657 | CLANG_WARN_EMPTY_BODY = YES; 658 | CLANG_WARN_ENUM_CONVERSION = YES; 659 | CLANG_WARN_INFINITE_RECURSION = YES; 660 | CLANG_WARN_INT_CONVERSION = YES; 661 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 662 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 663 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 664 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 665 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 666 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 667 | CLANG_WARN_STRICT_PROTOTYPES = YES; 668 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 669 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 670 | CLANG_WARN_UNREACHABLE_CODE = YES; 671 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 672 | COPY_PHASE_STRIP = NO; 673 | DEBUG_INFORMATION_FORMAT = dwarf; 674 | ENABLE_STRICT_OBJC_MSGSEND = YES; 675 | ENABLE_TESTABILITY = YES; 676 | GCC_C_LANGUAGE_STANDARD = gnu11; 677 | GCC_DYNAMIC_NO_PIC = NO; 678 | GCC_NO_COMMON_BLOCKS = YES; 679 | GCC_OPTIMIZATION_LEVEL = 0; 680 | GCC_PREPROCESSOR_DEFINITIONS = ( 681 | "DEBUG=1", 682 | "$(inherited)", 683 | ); 684 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 685 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 686 | GCC_WARN_UNDECLARED_SELECTOR = YES; 687 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 688 | GCC_WARN_UNUSED_FUNCTION = YES; 689 | GCC_WARN_UNUSED_VARIABLE = YES; 690 | IPHONEOS_DEPLOYMENT_TARGET = 16.0; 691 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 692 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 693 | MTL_FAST_MATH = YES; 694 | ONLY_ACTIVE_ARCH = YES; 695 | SDKROOT = iphoneos; 696 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = TEST; 697 | SWIFT_OBJC_INTEROP_MODE = objcxx; 698 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 699 | }; 700 | name = Test; 701 | }; 702 | 2FEE10312998C89C000822E1 /* Test */ = { 703 | isa = XCBuildConfiguration; 704 | buildSettings = { 705 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 706 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 707 | CODE_SIGN_ENTITLEMENTS = "HealthGPT/Supporting Files/HealthGPT.entitlements"; 708 | CODE_SIGN_IDENTITY = "Apple Development"; 709 | CODE_SIGN_STYLE = Automatic; 710 | CURRENT_PROJECT_VERSION = 1; 711 | DEVELOPMENT_ASSET_PATHS = ""; 712 | DEVELOPMENT_TEAM = ""; 713 | ENABLE_PREVIEWS = YES; 714 | GENERATE_INFOPLIST_FILE = YES; 715 | INFOPLIST_FILE = "HealthGPT/Supporting Files/Info.plist"; 716 | INFOPLIST_KEY_NSCameraUsageDescription = "This message should never appear. Please adjust this when you start using camera information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 717 | INFOPLIST_KEY_NSHealthShareUsageDescription = "HealthGPT allows users to interact with their health data using natural language, including querying sleep, step count, active energy, exercise minutes, heart rate, and body mass."; 718 | INFOPLIST_KEY_NSHealthUpdateUsageDescription = "HealthGPT allows users to interact with their health data using natural language, including querying sleep, step count, active energy, exercise minutes, heart rate, and body mass."; 719 | INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "This message should never appear. Please adjust this when you start using location information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 720 | INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "This message should never appear. Please adjust this when you start using location information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 721 | INFOPLIST_KEY_NSMicrophoneUsageDescription = "This message should never appear. Please adjust this when you start using microphone information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 722 | INFOPLIST_KEY_NSMotionUsageDescription = "This message should never appear. Please adjust this when you start using motion information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 723 | INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "This message should never appear. Please adjust this when you start using speecg information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 724 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 725 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; 726 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; 727 | INFOPLIST_KEY_UITemplateApplicationlicationSceneManifest_Generation = YES; 728 | INFOPLIST_KEY_UITemplateApplicationlicationSupportsIndirectInputEvents = YES; 729 | IPHONEOS_DEPLOYMENT_TARGET = 17.0; 730 | LD_RUNPATH_SEARCH_PATHS = ( 731 | "$(inherited)", 732 | "@executable_path/Frameworks", 733 | ); 734 | MARKETING_VERSION = 1.0; 735 | PRODUCT_BUNDLE_IDENTIFIER = edu.stanford.bdhg.healthgpt; 736 | PRODUCT_NAME = "$(TARGET_NAME)"; 737 | PROVISIONING_PROFILE_SPECIFIER = ""; 738 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; 739 | SUPPORTS_MACCATALYST = NO; 740 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; 741 | "SWIFT_ELicenseRef-TemplateApplication_LOC_STRINGS" = YES; 742 | SWIFT_EMIT_LOC_STRINGS = YES; 743 | SWIFT_OBJC_INTEROP_MODE = objcxx; 744 | SWIFT_VERSION = 5.0; 745 | TARGETED_DEVICE_FAMILY = 1; 746 | }; 747 | name = Test; 748 | }; 749 | 653A256F28338800005D4D48 /* Debug */ = { 750 | isa = XCBuildConfiguration; 751 | buildSettings = { 752 | ALWAYS_SEARCH_USER_PATHS = NO; 753 | CLANG_ANALYZER_NONNULL = YES; 754 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 755 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 756 | CLANG_ENABLE_MODULES = YES; 757 | CLANG_ENABLE_OBJC_ARC = YES; 758 | CLANG_ENABLE_OBJC_WEAK = YES; 759 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 760 | CLANG_WARN_BOOL_CONVERSION = YES; 761 | CLANG_WARN_COMMA = YES; 762 | CLANG_WARN_CONSTANT_CONVERSION = YES; 763 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 764 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 765 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 766 | CLANG_WARN_EMPTY_BODY = YES; 767 | CLANG_WARN_ENUM_CONVERSION = YES; 768 | CLANG_WARN_INFINITE_RECURSION = YES; 769 | CLANG_WARN_INT_CONVERSION = YES; 770 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 771 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 772 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 773 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 774 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 775 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 776 | CLANG_WARN_STRICT_PROTOTYPES = YES; 777 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 778 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 779 | CLANG_WARN_UNREACHABLE_CODE = YES; 780 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 781 | COPY_PHASE_STRIP = NO; 782 | DEBUG_INFORMATION_FORMAT = dwarf; 783 | ENABLE_STRICT_OBJC_MSGSEND = YES; 784 | ENABLE_TESTABILITY = YES; 785 | GCC_C_LANGUAGE_STANDARD = gnu11; 786 | GCC_DYNAMIC_NO_PIC = NO; 787 | GCC_NO_COMMON_BLOCKS = YES; 788 | GCC_OPTIMIZATION_LEVEL = 0; 789 | GCC_PREPROCESSOR_DEFINITIONS = ( 790 | "DEBUG=1", 791 | "$(inherited)", 792 | ); 793 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 794 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 795 | GCC_WARN_UNDECLARED_SELECTOR = YES; 796 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 797 | GCC_WARN_UNUSED_FUNCTION = YES; 798 | GCC_WARN_UNUSED_VARIABLE = YES; 799 | IPHONEOS_DEPLOYMENT_TARGET = 16.0; 800 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 801 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 802 | MTL_FAST_MATH = YES; 803 | ONLY_ACTIVE_ARCH = YES; 804 | SDKROOT = iphoneos; 805 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 806 | SWIFT_OBJC_INTEROP_MODE = objcxx; 807 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 808 | }; 809 | name = Debug; 810 | }; 811 | 653A257028338800005D4D48 /* Release */ = { 812 | isa = XCBuildConfiguration; 813 | buildSettings = { 814 | ALWAYS_SEARCH_USER_PATHS = NO; 815 | CLANG_ANALYZER_NONNULL = YES; 816 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 817 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 818 | CLANG_ENABLE_MODULES = YES; 819 | CLANG_ENABLE_OBJC_ARC = YES; 820 | CLANG_ENABLE_OBJC_WEAK = YES; 821 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 822 | CLANG_WARN_BOOL_CONVERSION = YES; 823 | CLANG_WARN_COMMA = YES; 824 | CLANG_WARN_CONSTANT_CONVERSION = YES; 825 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 826 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 827 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 828 | CLANG_WARN_EMPTY_BODY = YES; 829 | CLANG_WARN_ENUM_CONVERSION = YES; 830 | CLANG_WARN_INFINITE_RECURSION = YES; 831 | CLANG_WARN_INT_CONVERSION = YES; 832 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 833 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 834 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 835 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 836 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 837 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 838 | CLANG_WARN_STRICT_PROTOTYPES = YES; 839 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 840 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 841 | CLANG_WARN_UNREACHABLE_CODE = YES; 842 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 843 | COPY_PHASE_STRIP = NO; 844 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 845 | ENABLE_NS_ASSERTIONS = NO; 846 | ENABLE_STRICT_OBJC_MSGSEND = YES; 847 | GCC_C_LANGUAGE_STANDARD = gnu11; 848 | GCC_NO_COMMON_BLOCKS = YES; 849 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 850 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 851 | GCC_WARN_UNDECLARED_SELECTOR = YES; 852 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 853 | GCC_WARN_UNUSED_FUNCTION = YES; 854 | GCC_WARN_UNUSED_VARIABLE = YES; 855 | IPHONEOS_DEPLOYMENT_TARGET = 16.0; 856 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 857 | MTL_ENABLE_DEBUG_INFO = NO; 858 | MTL_FAST_MATH = YES; 859 | SDKROOT = iphoneos; 860 | SWIFT_COMPILATION_MODE = wholemodule; 861 | SWIFT_OBJC_INTEROP_MODE = objcxx; 862 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 863 | VALIDATE_PRODUCT = YES; 864 | }; 865 | name = Release; 866 | }; 867 | 653A257228338800005D4D48 /* Debug */ = { 868 | isa = XCBuildConfiguration; 869 | buildSettings = { 870 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 871 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 872 | CODE_SIGN_ENTITLEMENTS = "HealthGPT/Supporting Files/HealthGPT.entitlements"; 873 | CODE_SIGN_IDENTITY = "Apple Development"; 874 | CODE_SIGN_STYLE = Automatic; 875 | CURRENT_PROJECT_VERSION = 1; 876 | DEVELOPMENT_ASSET_PATHS = ""; 877 | DEVELOPMENT_TEAM = ""; 878 | ENABLE_PREVIEWS = YES; 879 | GENERATE_INFOPLIST_FILE = YES; 880 | INFOPLIST_FILE = "HealthGPT/Supporting Files/Info.plist"; 881 | INFOPLIST_KEY_NSCameraUsageDescription = "This message should never appear. Please adjust this when you start using camera information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 882 | INFOPLIST_KEY_NSHealthShareUsageDescription = "HealthGPT allows users to interact with their health data using natural language, including querying sleep, step count, active energy, exercise minutes, heart rate, and body mass."; 883 | INFOPLIST_KEY_NSHealthUpdateUsageDescription = "HealthGPT allows users to interact with their health data using natural language, including querying sleep, step count, active energy, exercise minutes, heart rate, and body mass."; 884 | INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "This message should never appear. Please adjust this when you start using location information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 885 | INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "This message should never appear. Please adjust this when you start using location information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 886 | INFOPLIST_KEY_NSMicrophoneUsageDescription = "This message should never appear. Please adjust this when you start using microphone information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 887 | INFOPLIST_KEY_NSMotionUsageDescription = "This message should never appear. Please adjust this when you start using motion information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 888 | INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "This message should never appear. Please adjust this when you start using speecg information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 889 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 890 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; 891 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; 892 | INFOPLIST_KEY_UITemplateApplicationlicationSceneManifest_Generation = YES; 893 | INFOPLIST_KEY_UITemplateApplicationlicationSupportsIndirectInputEvents = YES; 894 | IPHONEOS_DEPLOYMENT_TARGET = 17.0; 895 | LD_RUNPATH_SEARCH_PATHS = ( 896 | "$(inherited)", 897 | "@executable_path/Frameworks", 898 | ); 899 | MARKETING_VERSION = 1.0; 900 | PRODUCT_BUNDLE_IDENTIFIER = edu.stanford.bdhg.healthgpt; 901 | PRODUCT_NAME = "$(TARGET_NAME)"; 902 | PROVISIONING_PROFILE_SPECIFIER = ""; 903 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; 904 | SUPPORTS_MACCATALYST = NO; 905 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; 906 | "SWIFT_ELicenseRef-TemplateApplication_LOC_STRINGS" = YES; 907 | SWIFT_EMIT_LOC_STRINGS = YES; 908 | SWIFT_OBJC_INTEROP_MODE = objcxx; 909 | SWIFT_VERSION = 5.0; 910 | TARGETED_DEVICE_FAMILY = 1; 911 | }; 912 | name = Debug; 913 | }; 914 | 653A257328338800005D4D48 /* Release */ = { 915 | isa = XCBuildConfiguration; 916 | buildSettings = { 917 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 918 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 919 | CODE_SIGN_ENTITLEMENTS = "HealthGPT/Supporting Files/HealthGPT.entitlements"; 920 | CODE_SIGN_IDENTITY = "Apple Development"; 921 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; 922 | CODE_SIGN_STYLE = Manual; 923 | CURRENT_PROJECT_VERSION = 1; 924 | DEVELOPMENT_ASSET_PATHS = ""; 925 | DEVELOPMENT_TEAM = ""; 926 | "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 637867499T; 927 | ENABLE_PREVIEWS = YES; 928 | GENERATE_INFOPLIST_FILE = YES; 929 | INFOPLIST_FILE = "HealthGPT/Supporting Files/Info.plist"; 930 | INFOPLIST_KEY_NSCameraUsageDescription = "This message should never appear. Please adjust this when you start using camera information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 931 | INFOPLIST_KEY_NSHealthShareUsageDescription = "HealthGPT allows users to interact with their health data using natural language, including querying sleep, step count, active energy, exercise minutes, heart rate, and body mass."; 932 | INFOPLIST_KEY_NSHealthUpdateUsageDescription = "HealthGPT allows users to interact with their health data using natural language, including querying sleep, step count, active energy, exercise minutes, heart rate, and body mass."; 933 | INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "This message should never appear. Please adjust this when you start using location information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 934 | INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "This message should never appear. Please adjust this when you start using location information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 935 | INFOPLIST_KEY_NSMicrophoneUsageDescription = "This message should never appear. Please adjust this when you start using microphone information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 936 | INFOPLIST_KEY_NSMotionUsageDescription = "This message should never appear. Please adjust this when you start using motion information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 937 | INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "This message should never appear. Please adjust this when you start using speecg information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; 938 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 939 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; 940 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; 941 | INFOPLIST_KEY_UITemplateApplicationlicationSceneManifest_Generation = YES; 942 | INFOPLIST_KEY_UITemplateApplicationlicationSupportsIndirectInputEvents = YES; 943 | IPHONEOS_DEPLOYMENT_TARGET = 17.0; 944 | LD_RUNPATH_SEARCH_PATHS = ( 945 | "$(inherited)", 946 | "@executable_path/Frameworks", 947 | ); 948 | MARKETING_VERSION = 1.0; 949 | PRODUCT_BUNDLE_IDENTIFIER = edu.stanford.bdhg.healthgpt; 950 | PRODUCT_NAME = "$(TARGET_NAME)"; 951 | PROVISIONING_PROFILE_SPECIFIER = ""; 952 | "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = HealthGPT; 953 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; 954 | SUPPORTS_MACCATALYST = NO; 955 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; 956 | "SWIFT_ELicenseRef-TemplateApplication_LOC_STRINGS" = YES; 957 | SWIFT_EMIT_LOC_STRINGS = YES; 958 | SWIFT_OBJC_INTEROP_MODE = objcxx; 959 | SWIFT_VERSION = 5.0; 960 | TARGETED_DEVICE_FAMILY = 1; 961 | }; 962 | name = Release; 963 | }; 964 | /* End XCBuildConfiguration section */ 965 | 966 | /* Begin XCConfigurationList section */ 967 | 27B249632A06590E0091E52C /* Build configuration list for PBXNativeTarget "HealthGPTTests" */ = { 968 | isa = XCConfigurationList; 969 | buildConfigurations = ( 970 | 27B249602A06590E0091E52C /* Debug */, 971 | 27B249612A06590E0091E52C /* Test */, 972 | 27B249622A06590E0091E52C /* Release */, 973 | ); 974 | defaultConfigurationIsVisible = 0; 975 | defaultConfigurationName = Release; 976 | }; 977 | 2F4E21E029F0518B0067EE98 /* Build configuration list for PBXNativeTarget "HealthGPTUITests" */ = { 978 | isa = XCConfigurationList; 979 | buildConfigurations = ( 980 | 2F4E21DD29F0518B0067EE98 /* Debug */, 981 | 2F4E21DE29F0518B0067EE98 /* Test */, 982 | 2F4E21DF29F0518B0067EE98 /* Release */, 983 | ); 984 | defaultConfigurationIsVisible = 0; 985 | defaultConfigurationName = Release; 986 | }; 987 | 653A2548283387FE005D4D48 /* Build configuration list for PBXProject "HealthGPT" */ = { 988 | isa = XCConfigurationList; 989 | buildConfigurations = ( 990 | 653A256F28338800005D4D48 /* Debug */, 991 | 2FEE10302998C89C000822E1 /* Test */, 992 | 653A257028338800005D4D48 /* Release */, 993 | ); 994 | defaultConfigurationIsVisible = 0; 995 | defaultConfigurationName = Release; 996 | }; 997 | 653A257128338800005D4D48 /* Build configuration list for PBXNativeTarget "HealthGPT" */ = { 998 | isa = XCConfigurationList; 999 | buildConfigurations = ( 1000 | 653A257228338800005D4D48 /* Debug */, 1001 | 2FEE10312998C89C000822E1 /* Test */, 1002 | 653A257328338800005D4D48 /* Release */, 1003 | ); 1004 | defaultConfigurationIsVisible = 0; 1005 | defaultConfigurationName = Release; 1006 | }; 1007 | /* End XCConfigurationList section */ 1008 | 1009 | /* Begin XCRemoteSwiftPackageReference section */ 1010 | 27859BFD2A34F15E00397C85 /* XCRemoteSwiftPackageReference "Spezi" */ = { 1011 | isa = XCRemoteSwiftPackageReference; 1012 | repositoryURL = "https://github.com/StanfordSpezi/Spezi.git"; 1013 | requirement = { 1014 | kind = upToNextMajorVersion; 1015 | minimumVersion = 1.8.0; 1016 | }; 1017 | }; 1018 | 27859C022A34F16A00397C85 /* XCRemoteSwiftPackageReference "SpeziHealthKit" */ = { 1019 | isa = XCRemoteSwiftPackageReference; 1020 | repositoryURL = "https://github.com/StanfordSpezi/SpeziHealthKit.git"; 1021 | requirement = { 1022 | kind = upToNextMajorVersion; 1023 | minimumVersion = 1.1.3; 1024 | }; 1025 | }; 1026 | 27859C052A34F17800397C85 /* XCRemoteSwiftPackageReference "SpeziOnboarding" */ = { 1027 | isa = XCRemoteSwiftPackageReference; 1028 | repositoryURL = "https://github.com/StanfordSpezi/SpeziOnboarding.git"; 1029 | requirement = { 1030 | kind = upToNextMajorVersion; 1031 | minimumVersion = 1.2.2; 1032 | }; 1033 | }; 1034 | 27859C0E2A34F1A900397C85 /* XCRemoteSwiftPackageReference "SpeziStorage" */ = { 1035 | isa = XCRemoteSwiftPackageReference; 1036 | repositoryURL = "https://github.com/StanfordSpezi/SpeziStorage.git"; 1037 | requirement = { 1038 | kind = upToNextMajorVersion; 1039 | minimumVersion = 2.1.0; 1040 | }; 1041 | }; 1042 | 27859C1E2A34F2BF00397C85 /* XCRemoteSwiftPackageReference "XCTestExtensions" */ = { 1043 | isa = XCRemoteSwiftPackageReference; 1044 | repositoryURL = "https://github.com/StanfordBDHG/XCTestExtensions.git"; 1045 | requirement = { 1046 | kind = upToNextMajorVersion; 1047 | minimumVersion = 1.1.0; 1048 | }; 1049 | }; 1050 | 27859C232A34F2D000397C85 /* XCRemoteSwiftPackageReference "XCTHealthKit" */ = { 1051 | isa = XCRemoteSwiftPackageReference; 1052 | repositoryURL = "https://github.com/StanfordBDHG/XCTHealthKit.git"; 1053 | requirement = { 1054 | kind = upToNextMajorVersion; 1055 | minimumVersion = 0.3.5; 1056 | }; 1057 | }; 1058 | 27859C262A34F2DE00397C85 /* XCRemoteSwiftPackageReference "XCTRuntimeAssertions" */ = { 1059 | isa = XCRemoteSwiftPackageReference; 1060 | repositoryURL = "https://github.com/StanfordBDHG/XCTRuntimeAssertions.git"; 1061 | requirement = { 1062 | kind = upToNextMajorVersion; 1063 | minimumVersion = 1.1.3; 1064 | }; 1065 | }; 1066 | 63439A4C2BA6069E008BDBFD /* XCRemoteSwiftPackageReference "SpeziLLM" */ = { 1067 | isa = XCRemoteSwiftPackageReference; 1068 | repositoryURL = "https://github.com/StanfordSpezi/SpeziLLM"; 1069 | requirement = { 1070 | kind = upToNextMajorVersion; 1071 | minimumVersion = 0.9.1; 1072 | }; 1073 | }; 1074 | /* End XCRemoteSwiftPackageReference section */ 1075 | 1076 | /* Begin XCSwiftPackageProductDependency section */ 1077 | 27859BFE2A34F15E00397C85 /* Spezi */ = { 1078 | isa = XCSwiftPackageProductDependency; 1079 | package = 27859BFD2A34F15E00397C85 /* XCRemoteSwiftPackageReference "Spezi" */; 1080 | productName = Spezi; 1081 | }; 1082 | 27859C002A34F15E00397C85 /* XCTSpezi */ = { 1083 | isa = XCSwiftPackageProductDependency; 1084 | package = 27859BFD2A34F15E00397C85 /* XCRemoteSwiftPackageReference "Spezi" */; 1085 | productName = XCTSpezi; 1086 | }; 1087 | 27859C032A34F16B00397C85 /* SpeziHealthKit */ = { 1088 | isa = XCSwiftPackageProductDependency; 1089 | package = 27859C022A34F16A00397C85 /* XCRemoteSwiftPackageReference "SpeziHealthKit" */; 1090 | productName = SpeziHealthKit; 1091 | }; 1092 | 27859C062A34F17800397C85 /* SpeziOnboarding */ = { 1093 | isa = XCSwiftPackageProductDependency; 1094 | package = 27859C052A34F17800397C85 /* XCRemoteSwiftPackageReference "SpeziOnboarding" */; 1095 | productName = SpeziOnboarding; 1096 | }; 1097 | 27859C0F2A34F1A900397C85 /* SpeziLocalStorage */ = { 1098 | isa = XCSwiftPackageProductDependency; 1099 | package = 27859C0E2A34F1A900397C85 /* XCRemoteSwiftPackageReference "SpeziStorage" */; 1100 | productName = SpeziLocalStorage; 1101 | }; 1102 | 27859C1F2A34F2BF00397C85 /* XCTestApp */ = { 1103 | isa = XCSwiftPackageProductDependency; 1104 | package = 27859C1E2A34F2BF00397C85 /* XCRemoteSwiftPackageReference "XCTestExtensions" */; 1105 | productName = XCTestApp; 1106 | }; 1107 | 27859C212A34F2BF00397C85 /* XCTestExtensions */ = { 1108 | isa = XCSwiftPackageProductDependency; 1109 | package = 27859C1E2A34F2BF00397C85 /* XCRemoteSwiftPackageReference "XCTestExtensions" */; 1110 | productName = XCTestExtensions; 1111 | }; 1112 | 27859C242A34F2D000397C85 /* XCTHealthKit */ = { 1113 | isa = XCSwiftPackageProductDependency; 1114 | package = 27859C232A34F2D000397C85 /* XCRemoteSwiftPackageReference "XCTHealthKit" */; 1115 | productName = XCTHealthKit; 1116 | }; 1117 | 27859C272A34F2DE00397C85 /* XCTRuntimeAssertions */ = { 1118 | isa = XCSwiftPackageProductDependency; 1119 | package = 27859C262A34F2DE00397C85 /* XCRemoteSwiftPackageReference "XCTRuntimeAssertions" */; 1120 | productName = XCTRuntimeAssertions; 1121 | }; 1122 | 63439A4D2BA6069E008BDBFD /* SpeziLLM */ = { 1123 | isa = XCSwiftPackageProductDependency; 1124 | package = 63439A4C2BA6069E008BDBFD /* XCRemoteSwiftPackageReference "SpeziLLM" */; 1125 | productName = SpeziLLM; 1126 | }; 1127 | 63439A4F2BA6069E008BDBFD /* SpeziLLMOpenAI */ = { 1128 | isa = XCSwiftPackageProductDependency; 1129 | package = 63439A4C2BA6069E008BDBFD /* XCRemoteSwiftPackageReference "SpeziLLM" */; 1130 | productName = SpeziLLMOpenAI; 1131 | }; 1132 | 63DAAF702BBEB14A009E5E19 /* SpeziLLMLocal */ = { 1133 | isa = XCSwiftPackageProductDependency; 1134 | package = 63439A4C2BA6069E008BDBFD /* XCRemoteSwiftPackageReference "SpeziLLM" */; 1135 | productName = SpeziLLMLocal; 1136 | }; 1137 | 63DAAF722BBEB14A009E5E19 /* SpeziLLMLocalDownload */ = { 1138 | isa = XCSwiftPackageProductDependency; 1139 | package = 63439A4C2BA6069E008BDBFD /* XCRemoteSwiftPackageReference "SpeziLLM" */; 1140 | productName = SpeziLLMLocalDownload; 1141 | }; 1142 | 63E247632DC9B77C0044A0BB /* SpeziKeychainStorage */ = { 1143 | isa = XCSwiftPackageProductDependency; 1144 | package = 27859C0E2A34F1A900397C85 /* XCRemoteSwiftPackageReference "SpeziStorage" */; 1145 | productName = SpeziKeychainStorage; 1146 | }; 1147 | /* End XCSwiftPackageProductDependency section */ 1148 | }; 1149 | rootObject = 653A2545283387FE005D4D48 /* Project object */; 1150 | } 1151 | -------------------------------------------------------------------------------- /HealthGPT.xcodeproj/project.pbxproj.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /HealthGPT.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /HealthGPT.xcodeproj/project.xcworkspace/contents.xcworkspacedata.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /HealthGPT.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /HealthGPT.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist.license: -------------------------------------------------------------------------------- 1 | 2 | This source file is part of the Stanford HealthGPT Template Application project 3 | 4 | SPDX-FileCopyrightText: 2023 Stanford University 5 | 6 | SPDX-License-Identifier: MIT 7 | -------------------------------------------------------------------------------- /HealthGPT.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /HealthGPT.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /HealthGPT.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "763dd6c3fb283cd30c2edd3e18fad0c0f9ef9fb226c4b08bdd490e0a7de07181", 3 | "pins" : [ 4 | { 5 | "identity" : "gzipswift", 6 | "kind" : "remoteSourceControl", 7 | "location" : "https://github.com/1024jp/GzipSwift", 8 | "state" : { 9 | "revision" : "731037f6cc2be2ec01562f6597c1d0aa3fe6fd05", 10 | "version" : "6.0.1" 11 | } 12 | }, 13 | { 14 | "identity" : "jinja", 15 | "kind" : "remoteSourceControl", 16 | "location" : "https://github.com/maiqingqiang/Jinja", 17 | "state" : { 18 | "revision" : "6dbe4c449469fb586d0f7339f900f0dd4d78b167", 19 | "version" : "1.0.6" 20 | } 21 | }, 22 | { 23 | "identity" : "mlx-swift", 24 | "kind" : "remoteSourceControl", 25 | "location" : "https://github.com/ml-explore/mlx-swift", 26 | "state" : { 27 | "revision" : "70dbb62128a5a1471a5ab80363430adb33470cab", 28 | "version" : "0.21.2" 29 | } 30 | }, 31 | { 32 | "identity" : "mlx-swift-examples", 33 | "kind" : "remoteSourceControl", 34 | "location" : "https://github.com/ml-explore/mlx-swift-examples", 35 | "state" : { 36 | "revision" : "6ef303b84ec69e2e1e1da68f2059e99ee1dcef4d", 37 | "version" : "2.21.2" 38 | } 39 | }, 40 | { 41 | "identity" : "openapikit", 42 | "kind" : "remoteSourceControl", 43 | "location" : "https://github.com/mattpolzin/OpenAPIKit", 44 | "state" : { 45 | "revision" : "da7d3a5ddff0dd8e5e9fdb5585d186d645bcc21b", 46 | "version" : "3.5.0" 47 | } 48 | }, 49 | { 50 | "identity" : "spezi", 51 | "kind" : "remoteSourceControl", 52 | "location" : "https://github.com/StanfordSpezi/Spezi.git", 53 | "state" : { 54 | "revision" : "49ed1ddcfb7d0f753990b55578c33a887513fb39", 55 | "version" : "1.8.1" 56 | } 57 | }, 58 | { 59 | "identity" : "spezichat", 60 | "kind" : "remoteSourceControl", 61 | "location" : "https://github.com/StanfordSpezi/SpeziChat", 62 | "state" : { 63 | "revision" : "021ec65df13c02176d43737b8587a41cd520a510", 64 | "version" : "0.2.4" 65 | } 66 | }, 67 | { 68 | "identity" : "spezifoundation", 69 | "kind" : "remoteSourceControl", 70 | "location" : "https://github.com/StanfordSpezi/SpeziFoundation", 71 | "state" : { 72 | "revision" : "6e66cc6a843ef3e1273c6370c04183f0c5325e68", 73 | "version" : "2.1.5" 74 | } 75 | }, 76 | { 77 | "identity" : "spezihealthkit", 78 | "kind" : "remoteSourceControl", 79 | "location" : "https://github.com/StanfordSpezi/SpeziHealthKit.git", 80 | "state" : { 81 | "revision" : "9d7f54852d40da9376a5189b124ad30c76d9d78d", 82 | "version" : "1.1.3" 83 | } 84 | }, 85 | { 86 | "identity" : "spezillm", 87 | "kind" : "remoteSourceControl", 88 | "location" : "https://github.com/StanfordSpezi/SpeziLLM", 89 | "state" : { 90 | "revision" : "61b664bf019032634a6eeacff89afa1ea7d545fd", 91 | "version" : "0.10.1" 92 | } 93 | }, 94 | { 95 | "identity" : "spezionboarding", 96 | "kind" : "remoteSourceControl", 97 | "location" : "https://github.com/StanfordSpezi/SpeziOnboarding.git", 98 | "state" : { 99 | "revision" : "a3d7bc15e6803b2205eb8dca010a48b1a40215be", 100 | "version" : "1.2.2" 101 | } 102 | }, 103 | { 104 | "identity" : "spezispeech", 105 | "kind" : "remoteSourceControl", 106 | "location" : "https://github.com/StanfordSpezi/SpeziSpeech", 107 | "state" : { 108 | "revision" : "6c51d4165d2cabb6102aa61233c480fb75c167f6", 109 | "version" : "1.1.2" 110 | } 111 | }, 112 | { 113 | "identity" : "spezistorage", 114 | "kind" : "remoteSourceControl", 115 | "location" : "https://github.com/StanfordSpezi/SpeziStorage.git", 116 | "state" : { 117 | "revision" : "b5dfb4a551d3f4243e2798133d6da2414a70e274", 118 | "version" : "2.1.0" 119 | } 120 | }, 121 | { 122 | "identity" : "speziviews", 123 | "kind" : "remoteSourceControl", 124 | "location" : "https://github.com/StanfordSpezi/SpeziViews", 125 | "state" : { 126 | "revision" : "10b125763d8dd83c3fc9f9d6cd3137c8529212ce", 127 | "version" : "1.10.1" 128 | } 129 | }, 130 | { 131 | "identity" : "swift-algorithms", 132 | "kind" : "remoteSourceControl", 133 | "location" : "https://github.com/apple/swift-algorithms", 134 | "state" : { 135 | "revision" : "87e50f483c54e6efd60e885f7f5aa946cee68023", 136 | "version" : "1.2.1" 137 | } 138 | }, 139 | { 140 | "identity" : "swift-argument-parser", 141 | "kind" : "remoteSourceControl", 142 | "location" : "https://github.com/apple/swift-argument-parser.git", 143 | "state" : { 144 | "revision" : "41982a3656a71c768319979febd796c6fd111d5c", 145 | "version" : "1.5.0" 146 | } 147 | }, 148 | { 149 | "identity" : "swift-atomics", 150 | "kind" : "remoteSourceControl", 151 | "location" : "https://github.com/apple/swift-atomics.git", 152 | "state" : { 153 | "revision" : "cd142fd2f64be2100422d658e7411e39489da985", 154 | "version" : "1.2.0" 155 | } 156 | }, 157 | { 158 | "identity" : "swift-collections", 159 | "kind" : "remoteSourceControl", 160 | "location" : "https://github.com/apple/swift-collections.git", 161 | "state" : { 162 | "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7", 163 | "version" : "1.1.4" 164 | } 165 | }, 166 | { 167 | "identity" : "swift-http-types", 168 | "kind" : "remoteSourceControl", 169 | "location" : "https://github.com/apple/swift-http-types", 170 | "state" : { 171 | "revision" : "a0a57e949a8903563aba4615869310c0ebf14c03", 172 | "version" : "1.4.0" 173 | } 174 | }, 175 | { 176 | "identity" : "swift-numerics", 177 | "kind" : "remoteSourceControl", 178 | "location" : "https://github.com/apple/swift-numerics", 179 | "state" : { 180 | "revision" : "0a5bc04095a675662cf24757cc0640aa2204253b", 181 | "version" : "1.0.2" 182 | } 183 | }, 184 | { 185 | "identity" : "swift-openapi-generator", 186 | "kind" : "remoteSourceControl", 187 | "location" : "https://github.com/apple/swift-openapi-generator", 188 | "state" : { 189 | "revision" : "02cab48b66b4b412013827c2938adaf0eb67dc8d", 190 | "version" : "1.7.2" 191 | } 192 | }, 193 | { 194 | "identity" : "swift-openapi-runtime", 195 | "kind" : "remoteSourceControl", 196 | "location" : "https://github.com/apple/swift-openapi-runtime", 197 | "state" : { 198 | "revision" : "8f33cc5dfe81169fb167da73584b9c72c3e8bc23", 199 | "version" : "1.8.2" 200 | } 201 | }, 202 | { 203 | "identity" : "swift-openapi-urlsession", 204 | "kind" : "remoteSourceControl", 205 | "location" : "https://github.com/apple/swift-openapi-urlsession", 206 | "state" : { 207 | "revision" : "6fac6f7c428d5feea2639b5f5c8b06ddfb79434b", 208 | "version" : "1.1.0" 209 | } 210 | }, 211 | { 212 | "identity" : "swift-transformers", 213 | "kind" : "remoteSourceControl", 214 | "location" : "https://github.com/huggingface/swift-transformers", 215 | "state" : { 216 | "revision" : "d42fdae473c49ea216671da8caae58e102d28709", 217 | "version" : "0.1.14" 218 | } 219 | }, 220 | { 221 | "identity" : "xctestextensions", 222 | "kind" : "remoteSourceControl", 223 | "location" : "https://github.com/StanfordBDHG/XCTestExtensions.git", 224 | "state" : { 225 | "revision" : "5379d70249cae926927105bfb6686770f03ee5b9", 226 | "version" : "1.1.0" 227 | } 228 | }, 229 | { 230 | "identity" : "xcthealthkit", 231 | "kind" : "remoteSourceControl", 232 | "location" : "https://github.com/StanfordBDHG/XCTHealthKit.git", 233 | "state" : { 234 | "revision" : "6e9344a2d632b801d94fe3bbd1d891817e032103", 235 | "version" : "0.3.5" 236 | } 237 | }, 238 | { 239 | "identity" : "xctruntimeassertions", 240 | "kind" : "remoteSourceControl", 241 | "location" : "https://github.com/StanfordBDHG/XCTRuntimeAssertions.git", 242 | "state" : { 243 | "revision" : "f560ec8410af032dd485ca9386e8c2b5d3e1a1f8", 244 | "version" : "1.1.3" 245 | } 246 | }, 247 | { 248 | "identity" : "yams", 249 | "kind" : "remoteSourceControl", 250 | "location" : "https://github.com/jpsim/Yams", 251 | "state" : { 252 | "revision" : "b4b8042411dc7bbb696300a34a4bf3ba1b7ad19b", 253 | "version" : "5.3.1" 254 | } 255 | } 256 | ], 257 | "version" : 3 258 | } 259 | -------------------------------------------------------------------------------- /HealthGPT.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved.license: -------------------------------------------------------------------------------- 1 | 2 | This source file is part of the Stanford HealthGPT Template Application project 3 | 4 | SPDX-FileCopyrightText: 2023 Stanford University 5 | 6 | SPDX-License-Identifier: MIT 7 | -------------------------------------------------------------------------------- /HealthGPT.xcodeproj/xcshareddata/xcschemes/HealthGPT.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 35 | 41 | 42 | 43 | 46 | 52 | 53 | 54 | 55 | 56 | 66 | 68 | 74 | 75 | 76 | 77 | 80 | 81 | 84 | 85 | 88 | 89 | 92 | 93 | 94 | 95 | 101 | 103 | 109 | 110 | 111 | 112 | 114 | 115 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /HealthGPT.xcodeproj/xcshareddata/xcschemes/HealthGPT.xcscheme.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /HealthGPT/HealthGPT/HealthData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | 10 | import Foundation 11 | 12 | 13 | struct HealthData: Codable { 14 | var date: String 15 | var steps: Double? 16 | var activeEnergy: Double? 17 | var exerciseMinutes: Double? 18 | var bodyWeight: Double? 19 | var sleepHours: Double? 20 | var heartRate: Double? 21 | } 22 | -------------------------------------------------------------------------------- /HealthGPT/HealthGPT/HealthDataFetcher+Process.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | 10 | import Foundation 11 | 12 | 13 | extension HealthDataFetcher { 14 | /// Fetches and processes health data for the last 14 days. 15 | /// 16 | /// - Returns: An array of `HealthData` objects, one for each day in the last 14 days. 17 | func fetchAndProcessHealthData() async -> [HealthData] { 18 | let calendar = Calendar.current 19 | let today = Date() 20 | var healthData: [HealthData] = [] 21 | 22 | // Create an array of HealthData objects for the last 14 days 23 | for day in 1...14 { 24 | guard let endDate = calendar.date(byAdding: .day, value: -day, to: today) else { continue } 25 | healthData.append( 26 | HealthData( 27 | date: DateFormatter.localizedString(from: endDate, dateStyle: .short, timeStyle: .none) 28 | ) 29 | ) 30 | } 31 | 32 | healthData = healthData.reversed() 33 | 34 | async let stepCounts = fetchLastTwoWeeksStepCount() 35 | async let sleepHours = fetchLastTwoWeeksSleep() 36 | async let caloriesBurned = fetchLastTwoWeeksActiveEnergy() 37 | async let exerciseTime = fetchLastTwoWeeksExerciseTime() 38 | async let bodyMass = fetchLastTwoWeeksBodyWeight() 39 | 40 | let fetchedStepCounts = try? await stepCounts 41 | let fetchedSleepHours = try? await sleepHours 42 | let fetchedCaloriesBurned = try? await caloriesBurned 43 | let fetchedExerciseTime = try? await exerciseTime 44 | let fetchedBodyMass = try? await bodyMass 45 | 46 | for day in 0...13 { 47 | healthData[day].steps = fetchedStepCounts?[day] 48 | healthData[day].sleepHours = fetchedSleepHours?[day] 49 | healthData[day].activeEnergy = fetchedCaloriesBurned?[day] 50 | healthData[day].exerciseMinutes = fetchedExerciseTime?[day] 51 | healthData[day].bodyWeight = fetchedBodyMass?[day] 52 | } 53 | 54 | return healthData 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /HealthGPT/HealthGPT/HealthDataFetcher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import HealthKit 10 | import Spezi 11 | 12 | 13 | @Observable 14 | class HealthDataFetcher: DefaultInitializable, Module, EnvironmentAccessible { 15 | @ObservationIgnored private let healthStore = HKHealthStore() 16 | 17 | required init() { } 18 | 19 | 20 | /// Fetches the user's health data for the specified quantity type identifier for the last two weeks. 21 | /// 22 | /// - Parameters: 23 | /// - identifier: The `HKQuantityTypeIdentifier` representing the type of health data to fetch. 24 | /// - unit: The `HKUnit` to use for the fetched health data values. 25 | /// - options: The `HKStatisticsOptions` to use when fetching the health data. 26 | /// - Returns: An array of `Double` values representing the daily health data for the specified identifier. 27 | /// - Throws: `HealthDataFetcherError` if the data cannot be fetched. 28 | func fetchLastTwoWeeksQuantityData( 29 | for identifier: HKQuantityTypeIdentifier, 30 | unit: HKUnit, 31 | options: HKStatisticsOptions 32 | ) async throws -> [Double] { 33 | guard let quantityType = HKObjectType.quantityType(forIdentifier: identifier) else { 34 | throw HealthDataFetcherError.invalidObjectType 35 | } 36 | 37 | let predicate = createLastTwoWeeksPredicate() 38 | 39 | let quantityLastTwoWeeks = HKSamplePredicate.quantitySample( 40 | type: quantityType, 41 | predicate: predicate 42 | ) 43 | 44 | let query = HKStatisticsCollectionQueryDescriptor( 45 | predicate: quantityLastTwoWeeks, 46 | options: options, 47 | anchorDate: Date.startOfDay(), 48 | intervalComponents: DateComponents(day: 1) 49 | ) 50 | 51 | let quantityCounts = try await query.result(for: healthStore) 52 | 53 | var dailyData = [Double]() 54 | 55 | quantityCounts.enumerateStatistics( 56 | from: Date().twoWeeksAgoStartOfDay(), 57 | to: Date.startOfDay() 58 | ) { statistics, _ in 59 | if let quantity = statistics.sumQuantity() { 60 | dailyData.append(quantity.doubleValue(for: unit)) 61 | } else { 62 | dailyData.append(0) 63 | } 64 | } 65 | 66 | return dailyData 67 | } 68 | 69 | /// Fetches the user's step count data for the last two weeks. 70 | /// 71 | /// - Returns: An array of `Double` values representing daily step counts. 72 | /// - Throws: `HealthDataFetcherError` if the data cannot be fetched. 73 | func fetchLastTwoWeeksStepCount() async throws -> [Double] { 74 | try await fetchLastTwoWeeksQuantityData( 75 | for: .stepCount, 76 | unit: HKUnit.count(), 77 | options: [.cumulativeSum] 78 | ) 79 | } 80 | 81 | /// Fetches the user's active energy burned data for the last two weeks. 82 | /// 83 | /// - Returns: An array of `Double` values representing daily active energy burned. 84 | /// - Throws: `HealthDataFetcherError` if the data cannot be fetched. 85 | func fetchLastTwoWeeksActiveEnergy() async throws -> [Double] { 86 | try await fetchLastTwoWeeksQuantityData( 87 | for: .activeEnergyBurned, 88 | unit: HKUnit.largeCalorie(), 89 | options: [.cumulativeSum] 90 | ) 91 | } 92 | 93 | /// Fetches the user's exercise time data for the last two weeks. 94 | /// 95 | /// - Returns: An array of `Double` values representing daily exercise times in minutes. 96 | /// - Throws: `HealthDataFetcherError` if the data cannot be fetched. 97 | func fetchLastTwoWeeksExerciseTime() async throws -> [Double] { 98 | try await fetchLastTwoWeeksQuantityData( 99 | for: .appleExerciseTime, 100 | unit: .minute(), 101 | options: [.cumulativeSum] 102 | ) 103 | } 104 | 105 | /// Fetches the user's body weight data for the last two weeks. 106 | /// 107 | /// - Returns: An array of `Double` values representing daily body weights in pounds. 108 | /// - Throws: `HealthDataFetcherError` if the data cannot be fetched. 109 | func fetchLastTwoWeeksBodyWeight() async throws -> [Double] { 110 | try await fetchLastTwoWeeksQuantityData( 111 | for: .bodyMass, 112 | unit: .pound(), 113 | options: [.discreteAverage] 114 | ) 115 | } 116 | 117 | /// Fetches the user's heart rate data for the last two weeks. 118 | /// 119 | /// - Returns: An array of `Double` values representing daily average heart rates. 120 | /// - Throws: `HealthDataFetcherError` if the data cannot be fetched. 121 | func fetchLastTwoWeeksHeartRate() async throws -> [Double] { 122 | try await fetchLastTwoWeeksQuantityData( 123 | for: .heartRate, 124 | unit: .count(), 125 | options: [.discreteAverage] 126 | ) 127 | } 128 | 129 | /// Fetches the user's sleep data for the last two weeks. 130 | /// 131 | /// - Returns: An array of `Double` values representing daily sleep duration in hours. 132 | /// - Throws: `HealthDataFetcherError` if the data cannot be fetched. 133 | func fetchLastTwoWeeksSleep() async throws -> [Double] { 134 | var dailySleepData: [Double] = [] 135 | 136 | // We go through all possible days in the last two weeks. 137 | for day in -14..<0 { 138 | // We start the calculation at 3 PM the previous day to 3 PM on the day in question. 139 | guard let startOfSleepDay = Calendar.current.date(byAdding: DateComponents(day: day - 1), to: Date.startOfDay()), 140 | let startOfSleep = Calendar.current.date(bySettingHour: 15, minute: 0, second: 0, of: startOfSleepDay), 141 | let endOfSleepDay = Calendar.current.date(byAdding: DateComponents(day: day), to: Date.startOfDay()), 142 | let endOfSleep = Calendar.current.date(bySettingHour: 15, minute: 0, second: 0, of: endOfSleepDay) else { 143 | dailySleepData.append(0) 144 | continue 145 | } 146 | 147 | 148 | let sleepType = HKCategoryType(.sleepAnalysis) 149 | 150 | let dateRangePredicate = HKQuery.predicateForSamples(withStart: startOfSleep, end: endOfSleep, options: .strictEndDate) 151 | let allAsleepValuesPredicate = HKCategoryValueSleepAnalysis.predicateForSamples(equalTo: HKCategoryValueSleepAnalysis.allAsleepValues) 152 | let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [dateRangePredicate, allAsleepValuesPredicate]) 153 | 154 | let descriptor = HKSampleQueryDescriptor( 155 | predicates: [.categorySample(type: sleepType, predicate: compoundPredicate)], 156 | sortDescriptors: [] 157 | ) 158 | 159 | let results = try await descriptor.result(for: healthStore) 160 | 161 | var secondsAsleep = 0.0 162 | for result in results { 163 | secondsAsleep += result.endDate.timeIntervalSince(result.startDate) 164 | } 165 | 166 | // Append the hours of sleep for that date 167 | dailySleepData.append(secondsAsleep / (60 * 60)) 168 | } 169 | 170 | return dailySleepData 171 | } 172 | 173 | private func createLastTwoWeeksPredicate() -> NSPredicate { 174 | let now = Date() 175 | let startDate = Calendar.current.date(byAdding: DateComponents(day: -14), to: now) ?? Date() 176 | return HKQuery.predicateForSamples(withStart: startDate, end: now, options: .strictStartDate) 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /HealthGPT/HealthGPT/HealthDataFetcherError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | enum HealthDataFetcherError: Error { 13 | case healthDataNotAvailable 14 | case invalidObjectType 15 | case resultsNotFound 16 | case authorizationFailed 17 | } 18 | -------------------------------------------------------------------------------- /HealthGPT/HealthGPT/HealthDataInterpreter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import Foundation 10 | import Spezi 11 | import SpeziChat 12 | import SpeziLLM 13 | import SpeziLLMLocal 14 | import SpeziLLMOpenAI 15 | import SpeziSpeechSynthesizer 16 | 17 | 18 | @Observable 19 | class HealthDataInterpreter: DefaultInitializable, Module, EnvironmentAccessible { 20 | @ObservationIgnored @Dependency(LLMRunner.self) private var llmRunner 21 | @ObservationIgnored @Dependency(HealthDataFetcher.self) private var healthDataFetcher 22 | 23 | var llm: (any LLMSession)? 24 | @ObservationIgnored private var systemPrompt = "" 25 | 26 | required init() { } 27 | 28 | 29 | /// Creates an `LLMRunner`, from an `LLMSchema` and injects the system prompt 30 | /// into the context, and assigns the resulting `LLMSession` to the `llm` property. For more 31 | /// information, please refer to the [`SpeziLLM`](https://swiftpackageindex.com/StanfordSpezi/SpeziLLM/documentation/spezillm) documentation. 32 | /// 33 | /// - Parameter schema: the LLMSchema to use 34 | @MainActor 35 | func prepareLLM(with schema: any LLMSchema) async throws { 36 | let llm = llmRunner(with: schema) 37 | systemPrompt = await generateSystemPrompt() 38 | llm.context.append(systemMessage: systemPrompt) 39 | if let localLLM = llm as? LLMLocalSession { 40 | try await localLLM.setup() 41 | } 42 | self.llm = llm 43 | } 44 | 45 | /// Queries the LLM using the current session in the `llm` property and adds the output to the context. 46 | @MainActor 47 | func queryLLM() async throws { 48 | guard let llm, 49 | llm.context.last?.role == .user || !(llm.context.contains(where: { $0.role == .assistant() }) ) else { 50 | return 51 | } 52 | 53 | let stream = try await llm.generate() 54 | 55 | for try await token in stream { 56 | llm.context.append(assistantOutput: token) 57 | } 58 | } 59 | 60 | /// Resets the LLM context and re-injects the system prompt. 61 | @MainActor 62 | func resetChat() async { 63 | systemPrompt = await generateSystemPrompt() 64 | llm?.context.reset() 65 | llm?.context.append(systemMessage: systemPrompt) 66 | } 67 | 68 | /// Fetches updated health data using the `HealthDataFetcher` 69 | /// and passes it to the `PromptGenerator` to create the system prompt. 70 | private func generateSystemPrompt() async -> String { 71 | let healthData = await healthDataFetcher.fetchAndProcessHealthData() 72 | return PromptGenerator(with: healthData).buildMainPrompt() 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /HealthGPT/HealthGPT/HealthGPTView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import SpeziChat 10 | import SpeziLLM 11 | import SpeziLLMLocal 12 | import SpeziLLMOpenAI 13 | import SpeziSpeechSynthesizer 14 | import SwiftUI 15 | 16 | 17 | struct HealthGPTView: View { 18 | @AppStorage(StorageKeys.onboardingFlowComplete) var completedOnboardingFlow = false 19 | @AppStorage(StorageKeys.enableTextToSpeech) private var textToSpeech = StorageKeys.Defaults.enableTextToSpeech 20 | @AppStorage(StorageKeys.llmSource) private var llmSource = StorageKeys.Defaults.llmSource 21 | @AppStorage(StorageKeys.openAIModel) private var openAIModel = LLMOpenAIParameters.ModelType.gpt4o 22 | 23 | @Environment(HealthDataInterpreter.self) private var healthDataInterpreter 24 | @State private var showSettings = false 25 | @State private var showErrorAlert = false 26 | @State private var errorMessage = "" 27 | 28 | var body: some View { 29 | NavigationStack { 30 | if let llm = healthDataInterpreter.llm { 31 | let contextBinding = Binding { llm.context.chat } set: { llm.context.chat = $0 } 32 | 33 | ChatView(contextBinding, exportFormat: .text) 34 | .speak(llm.context.chat, muted: !textToSpeech) 35 | .speechToolbarButton(muted: !$textToSpeech) 36 | .viewStateAlert(state: llm.state) 37 | .navigationTitle("WELCOME_TITLE") 38 | .toolbar { 39 | ToolbarItem(placement: .primaryAction) { 40 | settingsButton 41 | } 42 | ToolbarItem(placement: .primaryAction) { 43 | resetChatButton 44 | } 45 | } 46 | .onChange(of: llm.context, initial: true) { _, _ in 47 | Task { 48 | if !llm.context.isEmpty && llm.state != .generating && llm.context.last?.role != .system { 49 | do { 50 | try await healthDataInterpreter.queryLLM() 51 | } catch { 52 | showErrorAlert = true 53 | errorMessage = "Error querying LLM: \(error.localizedDescription)" 54 | } 55 | } 56 | } 57 | } 58 | } else { 59 | loadingChatView 60 | } 61 | } 62 | .sheet(isPresented: $showSettings) { 63 | SettingsView() 64 | } 65 | .alert("ERROR_ALERT_TITLE", isPresented: $showErrorAlert) { 66 | Button("ERROR_ALERT_CANCEL", role: .cancel) {} 67 | } message: { 68 | Text(errorMessage) 69 | } 70 | .task { 71 | do { 72 | if FeatureFlags.mockMode { 73 | try await healthDataInterpreter.prepareLLM(with: LLMMockSchema()) 74 | } else if FeatureFlags.localLLM || llmSource == .local { 75 | try await healthDataInterpreter.prepareLLM(with: LLMLocalSchema(model: .llama3_8B_4bit)) 76 | } else { 77 | try await healthDataInterpreter.prepareLLM(with: LLMOpenAISchema(parameters: .init(modelType: openAIModel))) 78 | } 79 | } catch { 80 | showErrorAlert = true 81 | errorMessage = "Error querying LLM: \(error.localizedDescription)" 82 | } 83 | } 84 | } 85 | 86 | private var settingsButton: some View { 87 | Button( 88 | action: { 89 | showSettings = true 90 | }, 91 | label: { 92 | Image(systemName: "gearshape") 93 | .accessibilityLabel(Text("OPEN_SETTINGS")) 94 | } 95 | ) 96 | .accessibilityIdentifier("settingsButton") 97 | } 98 | 99 | private var resetChatButton: some View { 100 | Button( 101 | action: { 102 | Task { 103 | await healthDataInterpreter.resetChat() 104 | } 105 | }, 106 | label: { 107 | Image(systemName: "arrow.counterclockwise") 108 | .accessibilityLabel(Text("RESET")) 109 | } 110 | ) 111 | .accessibilityIdentifier("resetChatButton") 112 | } 113 | 114 | private var loadingChatView: some View { 115 | VStack { 116 | Text("LOADING_CHAT_VIEW") 117 | ProgressView() 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /HealthGPT/HealthGPT/PromptGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | 10 | import Foundation 11 | 12 | 13 | class PromptGenerator { 14 | var healthData: [HealthData] 15 | 16 | init(with healthData: [HealthData]) { 17 | self.healthData = healthData 18 | } 19 | 20 | func buildMainPrompt() -> String { 21 | let today = DateFormatter.localizedString(from: Date(), dateStyle: .full, timeStyle: .none) 22 | var mainPrompt = "You are HealthGPT, an enthusiastic, expert caretaker with a deep understanding in personal health. Given the context, provide a short response that could answer the user's question. Do NOT provide statistics. If numbers seem low, provide advice on how they can improve.\n\nSome health metrics over the past two weeks (14 days) to incorporate is given below. If a value is zero, the user has not inputted anything for that day. Today is \(today). Note that you do not have data about the current day. \n\n" 23 | mainPrompt += buildFourteenDaysHealthDataPrompt() 24 | return mainPrompt 25 | } 26 | 27 | private func buildFourteenDaysHealthDataPrompt() -> String { 28 | var healthDataPrompt = "" 29 | for day in 0...13 { 30 | let dayData = healthData[day] 31 | let dayPrompt = buildOneDayHealthDataPrompt(with: dayData) 32 | healthDataPrompt += "\(dayData.date): \(dayPrompt) \n" 33 | } 34 | return healthDataPrompt 35 | } 36 | 37 | private func buildOneDayHealthDataPrompt(with dayData: HealthData) -> String { 38 | var dayPrompt = "" 39 | if let steps = dayData.steps { 40 | dayPrompt += "\(Int(steps)) steps," 41 | } 42 | if let sleepHours = dayData.sleepHours { 43 | dayPrompt += " \(Int(sleepHours)) hours of sleep," 44 | } 45 | if let activeEnergy = dayData.activeEnergy { 46 | dayPrompt += " \(Int(activeEnergy)) calories burned," 47 | } 48 | if let exerciseMinutes = dayData.exerciseMinutes { 49 | dayPrompt += " \(Int(exerciseMinutes)) minutes of exercise," 50 | } 51 | if let bodyWeight = dayData.bodyWeight { 52 | dayPrompt += " \(bodyWeight) lbs of body weight," 53 | } 54 | return dayPrompt 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /HealthGPT/HealthGPT/SettingsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import OSLog 10 | import SpeziChat 11 | import SpeziLLMOpenAI 12 | import SwiftUI 13 | 14 | struct SettingsView: View { 15 | private enum SettingsDestinations { 16 | case openAIKey 17 | case openAIModelSelection 18 | } 19 | 20 | @State private var path = NavigationPath() 21 | @Environment(\.dismiss) private var dismiss 22 | @Environment(HealthDataInterpreter.self) private var healthDataInterpreter 23 | @AppStorage(StorageKeys.enableTextToSpeech) private var enableTextToSpeech = StorageKeys.Defaults.enableTextToSpeech 24 | @AppStorage(StorageKeys.llmSource) private var llmSource = StorageKeys.Defaults.llmSource 25 | @AppStorage(StorageKeys.openAIModel) private var openAIModel = LLMOpenAIParameters.ModelType.gpt4o 26 | let logger = Logger(subsystem: "HealthGPT", category: "Settings") 27 | 28 | 29 | var body: some View { 30 | NavigationStack(path: $path) { 31 | List { 32 | if !FeatureFlags.localLLM && !(llmSource == .local) { 33 | openAISettings 34 | } 35 | 36 | chatSettings 37 | speechSettings 38 | disclaimer 39 | } 40 | .navigationTitle("SETTINGS_TITLE") 41 | .navigationDestination(for: SettingsDestinations.self) { destination in 42 | navigate(to: destination) 43 | } 44 | .toolbar { 45 | ToolbarItem(placement: .confirmationAction) { 46 | Button("SETTINGS_DONE") { 47 | dismiss() 48 | } 49 | } 50 | } 51 | .accessibilityIdentifier("settingsList") 52 | } 53 | } 54 | 55 | private var openAISettings: some View { 56 | Section("SETTINGS_OPENAI") { 57 | NavigationLink(value: SettingsDestinations.openAIKey) { 58 | Text("SETTINGS_OPENAI_KEY") 59 | } 60 | .accessibilityIdentifier("openAIKey") 61 | NavigationLink(value: SettingsDestinations.openAIModelSelection) { 62 | Text("SETTINGS_OPENAI_MODEL") 63 | } 64 | .accessibilityIdentifier("openAIModel") 65 | } 66 | } 67 | 68 | private var chatSettings: some View { 69 | Section("SETTINGS_CHAT") { 70 | Button("SETTINGS_CHAT_RESET") { 71 | Task { 72 | await healthDataInterpreter.resetChat() 73 | dismiss() 74 | } 75 | } 76 | .buttonStyle(PlainButtonStyle()) 77 | .accessibilityIdentifier("resetButton") 78 | } 79 | } 80 | 81 | private var speechSettings: some View { 82 | Section("SETTINGS_SPEECH") { 83 | Toggle(isOn: $enableTextToSpeech) { 84 | Text("SETTINGS_SPEECH_TEXT_TO_SPEECH") 85 | } 86 | } 87 | } 88 | 89 | private var disclaimer: some View { 90 | Section("SETTINGS_DISCLAIMER_TITLE") { 91 | Text("SETTINGS_DISCLAIMER_TEXT") 92 | } 93 | } 94 | 95 | private func navigate(to destination: SettingsDestinations) -> some View { 96 | Group { 97 | switch destination { 98 | case .openAIKey: 99 | LLMOpenAIAPITokenOnboardingStep(actionText: "OPEN_AI_KEY_SAVE_ACTION") { 100 | path.removeLast() 101 | } 102 | case .openAIModelSelection: 103 | LLMOpenAIModelOnboardingStep( 104 | actionText: "OPEN_AI_MODEL_SAVE_ACTION", 105 | models: [ 106 | .gpt3_5_turbo, 107 | .gpt4_turbo, 108 | .gpt4o, 109 | .o1, 110 | .o1_mini, 111 | .o3_mini, 112 | .o3_mini_high 113 | ] 114 | ) { model in 115 | Task { 116 | openAIModel = model 117 | try? await healthDataInterpreter.prepareLLM(with: LLMOpenAISchema(parameters: .init(modelType: model))) 118 | path.removeLast() 119 | } 120 | } 121 | } 122 | } 123 | } 124 | } 125 | 126 | #Preview { 127 | SettingsView() 128 | } 129 | -------------------------------------------------------------------------------- /HealthGPT/HealthGPT/UIApplication+Keyboard.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import SwiftUI 10 | 11 | 12 | extension UIApplication { 13 | func hideKeyboard() { 14 | sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /HealthGPT/HealthGPTAppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import HealthKit 10 | import Spezi 11 | import SpeziHealthKit 12 | import SpeziKeychainStorage 13 | import SpeziLLM 14 | import SpeziLLMLocal 15 | import SpeziLLMOpenAI 16 | import SpeziSpeechSynthesizer 17 | import SwiftUI 18 | 19 | 20 | class HealthGPTAppDelegate: SpeziAppDelegate { 21 | override var configuration: Configuration { 22 | Configuration(standard: HealthGPTStandard()) { 23 | if HKHealthStore.isHealthDataAvailable() { 24 | healthKit 25 | } 26 | LLMRunner { 27 | LLMOpenAIPlatform() 28 | LLMLocalPlatform() 29 | LLMMockPlatform() 30 | } 31 | HealthDataInterpreter() 32 | HealthDataFetcher() 33 | KeychainStorage() 34 | } 35 | } 36 | 37 | 38 | private var healthKit: HealthKit { 39 | HealthKit { 40 | RequestReadAccess( 41 | quantity: 42 | [ 43 | .activeEnergyBurned, 44 | .appleExerciseTime, 45 | .bodyMass, 46 | .heartRate, 47 | .stepCount 48 | ] 49 | ) 50 | RequestReadAccess(category: [.sleepAnalysis]) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /HealthGPT/HealthGPTAppTestingSetup.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import OSLog 10 | @_spi(Internal) import SpeziKeychainStorage 11 | import SwiftUI 12 | 13 | 14 | private struct HealthGPTAppTestingSetup: ViewModifier { 15 | @AppStorage(StorageKeys.onboardingFlowComplete) var completedOnboardingFlow = false 16 | @Environment(KeychainStorage.self) var keychainStorage 17 | 18 | let logger = Logger(subsystem: "HealthGPT", category: "Testing") 19 | 20 | func body(content: Content) -> some View { 21 | content 22 | .task { 23 | if FeatureFlags.skipOnboarding { 24 | completedOnboardingFlow = true 25 | } 26 | if FeatureFlags.showOnboarding { 27 | completedOnboardingFlow = false 28 | } 29 | if FeatureFlags.resetKeychainStorage { 30 | do { 31 | try keychainStorage.deleteAllCredentials(accessGroup: .any) 32 | } catch { 33 | logger.error("Could not clear secure storage: \(error.localizedDescription)") 34 | } 35 | } 36 | } 37 | } 38 | } 39 | 40 | 41 | extension View { 42 | func testingSetup() -> some View { 43 | self.modifier(HealthGPTAppTestingSetup()) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /HealthGPT/HealthGPTApplication.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import Spezi 10 | import SwiftUI 11 | 12 | 13 | @main 14 | struct HealthGPTApplication: App { 15 | @UIApplicationDelegateAdaptor(HealthGPTAppDelegate.self) var appDelegate 16 | @AppStorage(StorageKeys.onboardingFlowComplete) var completedOnboardingFlow = false 17 | 18 | 19 | var body: some Scene { 20 | WindowGroup { 21 | Group { 22 | if completedOnboardingFlow { 23 | HealthGPTView() 24 | } else { 25 | EmptyView() 26 | } 27 | } 28 | .sheet(isPresented: !$completedOnboardingFlow) { 29 | OnboardingFlow() 30 | } 31 | .testingSetup() 32 | .spezi(appDelegate) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /HealthGPT/HealthGPTStandard.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import Spezi 10 | import SpeziHealthKit 11 | import SwiftUI 12 | 13 | 14 | actor HealthGPTStandard: Standard, HealthKitConstraint { 15 | func handleNewSamples( 16 | _ addedSamples: some Collection, 17 | ofType sampleType: SampleType 18 | ) async { } 19 | 20 | func handleDeletedObjects( 21 | _ deletedObjects: some Collection, 22 | ofType sampleType: SampleType 23 | ) async { } 24 | } 25 | -------------------------------------------------------------------------------- /HealthGPT/Helper/Binding+Negate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import SwiftUI 10 | 11 | 12 | extension Binding where Value == Bool { 13 | /// Negates a `Binding`. 14 | prefix static func ! (value: Binding) -> Binding { 15 | Binding( 16 | get: { !value.wrappedValue }, 17 | set: { value.wrappedValue = !$0 } 18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /HealthGPT/Helper/CodableArray+RawRepresentable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | extension Array: @retroactive RawRepresentable where Element: Codable { 13 | public var rawValue: String { 14 | guard let data = try? JSONEncoder().encode(self), 15 | let rawValue = String(data: data, encoding: .utf8) else { 16 | return "[]" 17 | } 18 | return rawValue 19 | } 20 | 21 | public init?(rawValue: String) { 22 | guard let data = rawValue.data(using: .utf8), 23 | let result = try? JSONDecoder().decode([Element].self, from: data) else { 24 | return nil 25 | } 26 | self = result 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /HealthGPT/Helper/Date+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | 10 | import Foundation 11 | 12 | 13 | extension Date { 14 | /// - Returns: A `Date` object representing the start of the current day. 15 | static func startOfDay() -> Date { 16 | Calendar.current.startOfDay(for: Date()) 17 | } 18 | 19 | /// - Returns: A `Date` object representing the start of the day exactly two weeks ago. 20 | func twoWeeksAgoStartOfDay() -> Date { 21 | Calendar.current.date(byAdding: DateComponents(day: -14), to: Date.startOfDay()) ?? Date() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /HealthGPT/Onboarding/Disclaimer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors & Project Contributors 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import SpeziOnboarding 10 | import SwiftUI 11 | 12 | 13 | struct Disclaimer: View { 14 | @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath 15 | 16 | 17 | var body: some View { 18 | SequentialOnboardingView( 19 | title: "INTERESTING_MODULES_TITLE".moduleLocalized, 20 | subtitle: "INTERESTING_MODULES_SUBTITLE".moduleLocalized, 21 | content: [ 22 | .init( 23 | title: "INTERESTING_MODULES_AREA1_TITLE".moduleLocalized, 24 | description: "INTERESTING_MODULES_AREA1_DESCRIPTION".moduleLocalized 25 | ), 26 | .init( 27 | title: "INTERESTING_MODULES_AREA2_TITLE".moduleLocalized, 28 | description: "INTERESTING_MODULES_AREA2_DESCRIPTION".moduleLocalized 29 | ), 30 | .init( 31 | title: "INTERESTING_MODULES_AREA3_TITLE".moduleLocalized, 32 | description: "INTERESTING_MODULES_AREA3_DESCRIPTION".moduleLocalized 33 | ), 34 | .init( 35 | title: "INTERESTING_MODULES_AREA4_TITLE".moduleLocalized, 36 | description: "INTERESTING_MODULES_AREA4_DESCRIPTION".moduleLocalized 37 | ) 38 | ], 39 | actionText: "INTERESTING_MODULES_BUTTON".moduleLocalized, 40 | action: { 41 | onboardingNavigationPath.nextStep() 42 | } 43 | ) 44 | } 45 | } 46 | 47 | 48 | #if DEBUG 49 | struct Disclaimer_Previews: PreviewProvider { 50 | static var previews: some View { 51 | Disclaimer() 52 | } 53 | } 54 | #endif 55 | -------------------------------------------------------------------------------- /HealthGPT/Onboarding/HealthKitPermissions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import OSLog 10 | import SpeziHealthKit 11 | import SpeziOnboarding 12 | import SwiftUI 13 | 14 | 15 | struct HealthKitPermissions: View { 16 | @Environment(HealthKit.self) var healthKitDataSource 17 | @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath 18 | @State var healthKitProcessing = false 19 | let logger = Logger(subsystem: "HealthGPT", category: "Onboarding") 20 | 21 | 22 | var body: some View { 23 | OnboardingView( 24 | contentView: { 25 | VStack { 26 | OnboardingTitleView( 27 | title: "HEALTHKIT_PERMISSIONS_TITLE".moduleLocalized, 28 | subtitle: "HEALTHKIT_PERMISSIONS_SUBTITLE".moduleLocalized 29 | ) 30 | Spacer() 31 | Image(systemName: "heart.text.square.fill") 32 | .accessibilityHidden(true) 33 | .font(.system(size: 150)) 34 | .foregroundColor(.accentColor) 35 | Text("HEALTHKIT_PERMISSIONS_DESCRIPTION") 36 | .multilineTextAlignment(.center) 37 | .padding(.vertical, 16) 38 | Spacer() 39 | } 40 | }, actionView: { 41 | OnboardingActionsView( 42 | "HEALTHKIT_PERMISSIONS_BUTTON", 43 | action: { 44 | do { 45 | healthKitProcessing = true 46 | // HealthKit is not available in the preview simulator. 47 | if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" { 48 | try await _Concurrency.Task.sleep(for: .seconds(5)) 49 | } else { 50 | try await healthKitDataSource.askForAuthorization() 51 | } 52 | } catch { 53 | logger.error("Could not request HealthKit permissions: \(error.localizedDescription)") 54 | } 55 | onboardingNavigationPath.nextStep() 56 | healthKitProcessing = false 57 | } 58 | ) 59 | } 60 | ) 61 | .navigationBarBackButtonHidden(healthKitProcessing) 62 | } 63 | } 64 | 65 | 66 | #if DEBUG 67 | struct HealthKitPermissions_Previews: PreviewProvider { 68 | static var previews: some View { 69 | HealthKitPermissions() 70 | } 71 | } 72 | #endif 73 | -------------------------------------------------------------------------------- /HealthGPT/Onboarding/LLMLocalDownload.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2024 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import SpeziLLMLocalDownload 10 | import SpeziOnboarding 11 | import SwiftUI 12 | 13 | struct LLMLocalDownload: View { 14 | @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath 15 | 16 | 17 | var body: some View { 18 | LLMLocalDownloadView( 19 | model: .llama3_8B_4bit, 20 | downloadDescription: "LLAMA3_DOWNLOAD_DESCRIPTION" 21 | ) { 22 | onboardingNavigationPath.nextStep() 23 | } 24 | } 25 | } 26 | 27 | #Preview { 28 | LLMLocalDownload() 29 | } 30 | -------------------------------------------------------------------------------- /HealthGPT/Onboarding/LLMSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2024 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | enum LLMSource: String, CaseIterable, Identifiable, Codable { 13 | case openai 14 | case local 15 | 16 | var id: String { 17 | self.rawValue 18 | } 19 | 20 | var localizedDescription: LocalizedStringResource { 21 | switch self { 22 | case .local: 23 | LocalizedStringResource("LOCAL_LLM_LABEL") 24 | case .openai: 25 | LocalizedStringResource("OPENAI_LLM_LABEL") 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /HealthGPT/Onboarding/LLMSourceSelection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2024 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import SpeziOnboarding 10 | import SwiftUI 11 | 12 | struct LLMSourceSelection: View { 13 | @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath 14 | @AppStorage(StorageKeys.llmSource) private var llmSource = StorageKeys.Defaults.llmSource 15 | 16 | var body: some View { 17 | OnboardingView( 18 | contentView: { 19 | VStack { 20 | OnboardingTitleView( 21 | title: "LLM_SOURCE_SELECTION_TITLE", 22 | subtitle: "LLM_SOURCE_SELECTION_SUBTITLE" 23 | ) 24 | Spacer() 25 | sourceSelector 26 | Spacer() 27 | } 28 | }, 29 | actionView: { 30 | OnboardingActionsView( 31 | "LLM_SOURCE_SELECTION_BUTTON" 32 | ) { 33 | if llmSource == .local { 34 | onboardingNavigationPath.append(customView: LLMLocalDownload()) 35 | } else { 36 | onboardingNavigationPath.append(customView: OpenAIAPIKey()) 37 | } 38 | } 39 | } 40 | ) 41 | } 42 | 43 | private var sourceSelector: some View { 44 | Picker("LLM_SOURCE_PICKER_LABEL", selection: $llmSource) { 45 | ForEach(LLMSource.allCases) { source in 46 | Text(source.localizedDescription) 47 | .tag(source) 48 | } 49 | } 50 | .pickerStyle(.inline) 51 | .accessibilityIdentifier("llmSourcePicker") 52 | } 53 | } 54 | 55 | #Preview { 56 | LLMSourceSelection() 57 | } 58 | -------------------------------------------------------------------------------- /HealthGPT/Onboarding/OnboardingFlow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import HealthKit 10 | import SpeziLLMOpenAI 11 | import SpeziOnboarding 12 | import SwiftUI 13 | 14 | 15 | /// Displays an multi-step onboarding flow for the HealthGPT Application. 16 | struct OnboardingFlow: View { 17 | @AppStorage(StorageKeys.onboardingFlowComplete) var completedOnboardingFlow = false 18 | @AppStorage(StorageKeys.llmSource) var llmSource = StorageKeys.Defaults.llmSource 19 | 20 | 21 | var body: some View { 22 | OnboardingStack(onboardingFlowComplete: $completedOnboardingFlow) { 23 | Welcome() 24 | Disclaimer() 25 | 26 | if FeatureFlags.localLLM { 27 | LLMLocalDownload() 28 | } else { 29 | LLMSourceSelection() 30 | } 31 | 32 | if HKHealthStore.isHealthDataAvailable() { 33 | HealthKitPermissions() 34 | } 35 | } 36 | .navigationBarTitleDisplayMode(.inline) 37 | .interactiveDismissDisabled(!completedOnboardingFlow) 38 | } 39 | } 40 | 41 | 42 | #if DEBUG 43 | struct OnboardingFlow_Previews: PreviewProvider { 44 | static var previews: some View { 45 | OnboardingFlow() 46 | } 47 | } 48 | #endif 49 | -------------------------------------------------------------------------------- /HealthGPT/Onboarding/OpenAIAPIKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import SpeziLLMOpenAI 10 | import SpeziOnboarding 11 | import SwiftUI 12 | 13 | 14 | struct OpenAIAPIKey: View { 15 | @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath 16 | 17 | 18 | var body: some View { 19 | LLMOpenAIAPITokenOnboardingStep { 20 | onboardingNavigationPath.append(customView: OpenAIModelSelection()) 21 | } 22 | } 23 | } 24 | 25 | #Preview { 26 | OpenAIAPIKey() 27 | } 28 | -------------------------------------------------------------------------------- /HealthGPT/Onboarding/OpenAIModelSelection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import SpeziLLMOpenAI 10 | import SpeziOnboarding 11 | import SwiftUI 12 | 13 | 14 | struct OpenAIModelSelection: View { 15 | @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath 16 | @AppStorage(StorageKeys.openAIModel) private var openAIModel = LLMOpenAIParameters.ModelType.gpt4o 17 | 18 | 19 | var body: some View { 20 | LLMOpenAIModelOnboardingStep( 21 | actionText: "OPEN_AI_MODEL_SAVE_ACTION", 22 | models: [ 23 | .gpt3_5_turbo, 24 | .gpt4_turbo, 25 | .gpt4o, 26 | .o1, 27 | .o1_mini, 28 | .o3_mini, 29 | .o3_mini_high 30 | ] 31 | ) { model in 32 | openAIModel = model 33 | onboardingNavigationPath.nextStep() 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /HealthGPT/Onboarding/String+ModuleLocalized.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | extension String { 10 | var moduleLocalized: String { 11 | String(localized: LocalizationValue(self)) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /HealthGPT/Onboarding/Welcome.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import SpeziOnboarding 10 | import SwiftUI 11 | 12 | 13 | struct Welcome: View { 14 | @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath 15 | 16 | 17 | var body: some View { 18 | OnboardingView( 19 | title: "WELCOME_TITLE".moduleLocalized, 20 | subtitle: "WELCOME_SUBTITLE".moduleLocalized, 21 | areas: [ 22 | .init( 23 | icon: Image(systemName: "shippingbox.fill"), // swiftlint:disable:this accessibility_label_for_image 24 | title: "WELCOME_AREA1_TITLE".moduleLocalized, 25 | description: "WELCOME_AREA1_DESCRIPTION".moduleLocalized 26 | ), 27 | .init( 28 | icon: Image(systemName: "applewatch.side.right"), // swiftlint:disable:this accessibility_label_for_image 29 | title: "WELCOME_AREA2_TITLE".moduleLocalized, 30 | description: "WELCOME_AREA2_DESCRIPTION".moduleLocalized 31 | ), 32 | .init( 33 | icon: Image(systemName: "list.bullet.clipboard.fill"), // swiftlint:disable:this accessibility_label_for_image 34 | title: "WELCOME_AREA3_TITLE".moduleLocalized, 35 | description: "WELCOME_AREA3_DESCRIPTION".moduleLocalized 36 | ) 37 | ], 38 | actionText: "WELCOME_BUTTON".moduleLocalized, 39 | action: { 40 | onboardingNavigationPath.nextStep() 41 | } 42 | ) 43 | } 44 | } 45 | 46 | 47 | #if DEBUG 48 | struct Welcome_Previews: PreviewProvider { 49 | static var previews: some View { 50 | Welcome() 51 | } 52 | } 53 | #endif 54 | -------------------------------------------------------------------------------- /HealthGPT/SharedContext/FeatureFlags.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | 10 | /// A collection of feature flags for the HealthGPT app. 11 | enum FeatureFlags { 12 | /// Skips the onboarding flow to enable easier development of features in the application 13 | /// and to allow UI tests to skip the onboarding flow. 14 | static let skipOnboarding = CommandLine.arguments.contains("--skipOnboarding") 15 | /// Always show the onboarding when the application is launched. Makes it easy to modify 16 | /// and test the onboarding flow without the need to manually remove the application or reset the simulator. 17 | static let showOnboarding = CommandLine.arguments.contains("--showOnboarding") 18 | /// Resets all credentials in Spezi Keychain Storage when the application is launched in order to facilitate testing of OpenAI API keys. 19 | static let resetKeychainStorage = CommandLine.arguments.contains("--resetKeychainStorage") 20 | /// Configures SpeziLLM to use a local model stored on the device downloaded during onboarding 21 | static let localLLM = CommandLine.arguments.contains("--localLLM") 22 | /// Configures SpeziLLM to mock all generated responses for development and UI tests 23 | static let mockMode = CommandLine.arguments.contains("--mockMode") 24 | } 25 | -------------------------------------------------------------------------------- /HealthGPT/SharedContext/StorageKeys.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | 10 | /// Constants shared across the HealthGPT Application to access 11 | /// storage information including the `AppStorage` and `SceneStorage` 12 | enum StorageKeys { 13 | enum Defaults { 14 | static let enableTextToSpeech = false 15 | static let llmSource = LLMSource.openai 16 | } 17 | 18 | // MARK: - Onboarding 19 | /// A `Bool` flag indicating of the onboarding was completed. 20 | static let onboardingFlowComplete = "onboardingFlow.complete" 21 | /// A `Step` flag indicating the current step in the onboarding process. 22 | static let onboardingFlowStep = "onboardingFlow.step" 23 | /// An `LLMSource` flag indicating the source of the model (local vs. OpenAI) 24 | static let llmSource = "llmsource" 25 | /// An `LLMOpenAIModelType` flag indicating the OpenAI model to use 26 | static let openAIModel = "openAI.model" 27 | /// A `Bool` flag indicating if messages should be spoken. 28 | static let enableTextToSpeech = "settings.enableTextToSpeech" 29 | } 30 | -------------------------------------------------------------------------------- /HealthGPT/Supporting Files/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /HealthGPT/Supporting Files/Assets.xcassets/AccentColor.colorset/Contents.json.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /HealthGPT/Supporting Files/Assets.xcassets/AppIcon.appiconset/AppIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StanfordBDHG/HealthGPT/8ad37f59c7fe984bcd62f4ffb771fb8b549633d9/HealthGPT/Supporting Files/Assets.xcassets/AppIcon.appiconset/AppIcon.png -------------------------------------------------------------------------------- /HealthGPT/Supporting Files/Assets.xcassets/AppIcon.appiconset/AppIcon.png.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /HealthGPT/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "AppIcon.png", 5 | "idiom" : "universal", 6 | "platform" : "ios", 7 | "size" : "1024x1024" 8 | } 9 | ], 10 | "info" : { 11 | "author" : "xcode", 12 | "version" : 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /HealthGPT/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /HealthGPT/Supporting Files/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /HealthGPT/Supporting Files/Assets.xcassets/Contents.json.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /HealthGPT/Supporting Files/HealthGPT.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.healthkit 6 | 7 | com.apple.developer.healthkit.access 8 | 9 | com.apple.developer.healthkit.background-delivery 10 | 11 | com.apple.developer.kernel.increased-memory-limit 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /HealthGPT/Supporting Files/HealthGPT.entitlements.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /HealthGPT/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ITSAppUsesNonExemptEncryption 6 | 7 | UIApplicationSceneManifest 8 | 9 | UIApplicationSupportsMultipleScenes 10 | 11 | UISceneConfigurations 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /HealthGPT/Supporting Files/Info.plist.license: -------------------------------------------------------------------------------- 1 | This source file is part of the HealthGPT open-source project 2 | 3 | SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /HealthGPT/Supporting Files/Localizable.xcstrings: -------------------------------------------------------------------------------- 1 | { 2 | "sourceLanguage" : "en", 3 | "strings" : { 4 | "API_KEY_SUBTITLE" : { 5 | "extractionState" : "manual", 6 | "localizations" : { 7 | "en" : { 8 | "stringUnit" : { 9 | "state" : "translated", 10 | "value" : "Please copy and paste your API key into the box below." 11 | } 12 | } 13 | } 14 | }, 15 | "API_KEY_TITLE" : { 16 | "comment" : "MARK: API Key", 17 | "extractionState" : "manual", 18 | "localizations" : { 19 | "en" : { 20 | "stringUnit" : { 21 | "state" : "translated", 22 | "value" : "OpenAI API Key" 23 | } 24 | } 25 | } 26 | }, 27 | "ERROR_ALERT_CANCEL" : { 28 | "localizations" : { 29 | "en" : { 30 | "stringUnit" : { 31 | "state" : "translated", 32 | "value" : "OK" 33 | } 34 | } 35 | } 36 | }, 37 | "ERROR_ALERT_TITLE" : { 38 | "localizations" : { 39 | "en" : { 40 | "stringUnit" : { 41 | "state" : "translated", 42 | "value" : "Error" 43 | } 44 | } 45 | } 46 | }, 47 | "HEALTHKIT_PERMISSIONS_BUTTON" : { 48 | "localizations" : { 49 | "en" : { 50 | "stringUnit" : { 51 | "state" : "translated", 52 | "value" : "Grant Access" 53 | } 54 | } 55 | } 56 | }, 57 | "HEALTHKIT_PERMISSIONS_DESCRIPTION" : { 58 | "localizations" : { 59 | "en" : { 60 | "stringUnit" : { 61 | "state" : "translated", 62 | "value" : "Supported data types include sleep, step count, active energy, exercise minutes, heart rate, and body mass. \n\n Aggregated HealthKit data for the past 14 days will be uploaded to OpenAI. \n\n Please refer to the OpenAI privacy policy for more information." 63 | } 64 | } 65 | } 66 | }, 67 | "HEALTHKIT_PERMISSIONS_SUBTITLE" : { 68 | "extractionState" : "manual", 69 | "localizations" : { 70 | "en" : { 71 | "stringUnit" : { 72 | "state" : "translated", 73 | "value" : "HealthGPT can access data from HealthKit to use in conversations with ChatGPT." 74 | } 75 | } 76 | } 77 | }, 78 | "HEALTHKIT_PERMISSIONS_TITLE" : { 79 | "comment" : "MARK: HealthKit", 80 | "extractionState" : "manual", 81 | "localizations" : { 82 | "en" : { 83 | "stringUnit" : { 84 | "state" : "translated", 85 | "value" : "HealthKit Access" 86 | } 87 | } 88 | } 89 | }, 90 | "INTERESTING_MODULES_AREA1_DESCRIPTION" : { 91 | "extractionState" : "manual", 92 | "localizations" : { 93 | "en" : { 94 | "stringUnit" : { 95 | "state" : "translated", 96 | "value" : "HealthGPT is provided for general informational purposes only and is not intended as a substitute for professional medical advice, diagnosis, or treatment." 97 | } 98 | } 99 | } 100 | }, 101 | "INTERESTING_MODULES_AREA1_TITLE" : { 102 | "extractionState" : "manual", 103 | "localizations" : { 104 | "en" : { 105 | "stringUnit" : { 106 | "state" : "translated", 107 | "value" : "Warning" 108 | } 109 | } 110 | } 111 | }, 112 | "INTERESTING_MODULES_AREA2_DESCRIPTION" : { 113 | "extractionState" : "manual", 114 | "localizations" : { 115 | "en" : { 116 | "stringUnit" : { 117 | "state" : "translated", 118 | "value" : "Large language models, such as those provided by OpenAI, are known to hallucinate and at times return false information." 119 | } 120 | } 121 | } 122 | }, 123 | "INTERESTING_MODULES_AREA2_TITLE" : { 124 | "extractionState" : "manual", 125 | "localizations" : { 126 | "en" : { 127 | "stringUnit" : { 128 | "state" : "translated", 129 | "value" : "Warning" 130 | } 131 | } 132 | } 133 | }, 134 | "INTERESTING_MODULES_AREA3_DESCRIPTION" : { 135 | "extractionState" : "manual", 136 | "localizations" : { 137 | "en" : { 138 | "stringUnit" : { 139 | "state" : "translated", 140 | "value" : "The use of HealthGPT is at your own risk. Always consult a qualified healthcare provider for personalized advice regarding your health and well-being." 141 | } 142 | } 143 | } 144 | }, 145 | "INTERESTING_MODULES_AREA3_TITLE" : { 146 | "extractionState" : "manual", 147 | "localizations" : { 148 | "en" : { 149 | "stringUnit" : { 150 | "state" : "translated", 151 | "value" : "Warning" 152 | } 153 | } 154 | } 155 | }, 156 | "INTERESTING_MODULES_AREA4_DESCRIPTION" : { 157 | "extractionState" : "manual", 158 | "localizations" : { 159 | "en" : { 160 | "stringUnit" : { 161 | "state" : "translated", 162 | "value" : "This app is for demonstration only and should not be used to process any personal health information." 163 | } 164 | } 165 | } 166 | }, 167 | "INTERESTING_MODULES_AREA4_TITLE" : { 168 | "extractionState" : "manual", 169 | "localizations" : { 170 | "en" : { 171 | "stringUnit" : { 172 | "state" : "translated", 173 | "value" : "Warning" 174 | } 175 | } 176 | } 177 | }, 178 | "INTERESTING_MODULES_BUTTON" : { 179 | "extractionState" : "manual", 180 | "localizations" : { 181 | "en" : { 182 | "stringUnit" : { 183 | "state" : "translated", 184 | "value" : "I Agree" 185 | } 186 | } 187 | } 188 | }, 189 | "INTERESTING_MODULES_SUBTITLE" : { 190 | "extractionState" : "manual", 191 | "localizations" : { 192 | "en" : { 193 | "stringUnit" : { 194 | "state" : "translated", 195 | "value" : "HealthGPT is an experimental iOS app that allows users to interact with health data stored in Apple Health using ChatGPT." 196 | } 197 | } 198 | } 199 | }, 200 | "INTERESTING_MODULES_TITLE" : { 201 | "comment" : "MARK: Disclaimer", 202 | "extractionState" : "manual", 203 | "localizations" : { 204 | "en" : { 205 | "stringUnit" : { 206 | "state" : "translated", 207 | "value" : "Disclaimer" 208 | } 209 | } 210 | } 211 | }, 212 | "LLAMA3_DOWNLOAD_DESCRIPTION" : { 213 | "localizations" : { 214 | "en" : { 215 | "stringUnit" : { 216 | "state" : "translated", 217 | "value" : "The Llama3 8B model will be downloaded." 218 | } 219 | } 220 | } 221 | }, 222 | "LLM_SOURCE_PICKER_LABEL" : { 223 | "localizations" : { 224 | "en" : { 225 | "stringUnit" : { 226 | "state" : "translated", 227 | "value" : "Choose an LLM source" 228 | } 229 | } 230 | } 231 | }, 232 | "LLM_SOURCE_SELECTION_BUTTON" : { 233 | "localizations" : { 234 | "en" : { 235 | "stringUnit" : { 236 | "state" : "translated", 237 | "value" : "Save Choice" 238 | } 239 | } 240 | } 241 | }, 242 | "LLM_SOURCE_SELECTION_SUBTITLE" : { 243 | "localizations" : { 244 | "en" : { 245 | "stringUnit" : { 246 | "state" : "translated", 247 | "value" : "Choose an on-device or OpenAI LLM" 248 | } 249 | } 250 | } 251 | }, 252 | "LLM_SOURCE_SELECTION_TITLE" : { 253 | "localizations" : { 254 | "en" : { 255 | "stringUnit" : { 256 | "state" : "translated", 257 | "value" : "LLM Source Selection" 258 | } 259 | } 260 | } 261 | }, 262 | "LOADING_CHAT_VIEW" : { 263 | "localizations" : { 264 | "en" : { 265 | "stringUnit" : { 266 | "state" : "translated", 267 | "value" : "Loading the chat window..." 268 | } 269 | } 270 | } 271 | }, 272 | "LOCAL_LLM_LABEL" : { 273 | "localizations" : { 274 | "en" : { 275 | "stringUnit" : { 276 | "state" : "translated", 277 | "value" : "On-device LLM" 278 | } 279 | } 280 | } 281 | }, 282 | "MODEL_SELECTION_SUBTITLE" : { 283 | "extractionState" : "manual", 284 | "localizations" : { 285 | "en" : { 286 | "stringUnit" : { 287 | "state" : "translated", 288 | "value" : "If you have access to GPT4, you may select it below. Otherwise select GPT 3.5 Turbo." 289 | } 290 | } 291 | } 292 | }, 293 | "MODEL_SELECTION_TITLE" : { 294 | "comment" : "MARK: Model Selection", 295 | "extractionState" : "manual", 296 | "localizations" : { 297 | "en" : { 298 | "stringUnit" : { 299 | "state" : "translated", 300 | "value" : "Select an OpenAI Model" 301 | } 302 | } 303 | } 304 | }, 305 | "OPEN_AI_KEY_SAVE_ACTION" : { 306 | "localizations" : { 307 | "en" : { 308 | "stringUnit" : { 309 | "state" : "translated", 310 | "value" : "Save API Key" 311 | } 312 | } 313 | } 314 | }, 315 | "OPEN_AI_MODEL_SAVE_ACTION" : { 316 | "localizations" : { 317 | "en" : { 318 | "stringUnit" : { 319 | "state" : "translated", 320 | "value" : "Save OpenAI Model" 321 | } 322 | } 323 | } 324 | }, 325 | "OPEN_SETTINGS" : { 326 | "localizations" : { 327 | "en" : { 328 | "stringUnit" : { 329 | "state" : "translated", 330 | "value" : "Press to open the settings editor." 331 | } 332 | } 333 | } 334 | }, 335 | "OPENAI_LLM_LABEL" : { 336 | "localizations" : { 337 | "en" : { 338 | "stringUnit" : { 339 | "state" : "translated", 340 | "value" : "Open AI LLM" 341 | } 342 | } 343 | } 344 | }, 345 | "RESET" : { 346 | "localizations" : { 347 | "en" : { 348 | "stringUnit" : { 349 | "state" : "translated", 350 | "value" : "Reset" 351 | } 352 | } 353 | } 354 | }, 355 | "SETTINGS_CHAT" : { 356 | "localizations" : { 357 | "en" : { 358 | "stringUnit" : { 359 | "state" : "translated", 360 | "value" : "Chat Settings" 361 | } 362 | } 363 | } 364 | }, 365 | "SETTINGS_CHAT_RESET" : { 366 | "localizations" : { 367 | "en" : { 368 | "stringUnit" : { 369 | "state" : "translated", 370 | "value" : "Reset Chat" 371 | } 372 | } 373 | } 374 | }, 375 | "SETTINGS_DISCLAIMER_TEXT" : { 376 | "localizations" : { 377 | "en" : { 378 | "stringUnit" : { 379 | "state" : "translated", 380 | "value" : "HealthGPT is powered by the OpenAI API. Data submitted here is not used for training OpenAI's models according to their terms and conditions. Currently, HealthGPT is accessing your step count, sleep analysis, exercise minutes, active calories burned, body weight, and heart rate, all from data stored in the Health app. Remember to log your data and wear your Apple Watch throughout the day for the most accurate results." 381 | } 382 | } 383 | } 384 | }, 385 | "SETTINGS_DISCLAIMER_TITLE" : { 386 | "localizations" : { 387 | "en" : { 388 | "stringUnit" : { 389 | "state" : "translated", 390 | "value" : "Disclaimer" 391 | } 392 | } 393 | } 394 | }, 395 | "SETTINGS_DONE" : { 396 | "localizations" : { 397 | "en" : { 398 | "stringUnit" : { 399 | "state" : "translated", 400 | "value" : "Done" 401 | } 402 | } 403 | } 404 | }, 405 | "SETTINGS_OPENAI" : { 406 | "localizations" : { 407 | "en" : { 408 | "stringUnit" : { 409 | "state" : "translated", 410 | "value" : "Open AI Settings" 411 | } 412 | } 413 | } 414 | }, 415 | "SETTINGS_OPENAI_KEY" : { 416 | "localizations" : { 417 | "en" : { 418 | "stringUnit" : { 419 | "state" : "translated", 420 | "value" : "Open AI API Key" 421 | } 422 | } 423 | } 424 | }, 425 | "SETTINGS_OPENAI_MODEL" : { 426 | "localizations" : { 427 | "en" : { 428 | "stringUnit" : { 429 | "state" : "translated", 430 | "value" : "Open AI Model" 431 | } 432 | } 433 | } 434 | }, 435 | "SETTINGS_SPEECH" : { 436 | "localizations" : { 437 | "en" : { 438 | "stringUnit" : { 439 | "state" : "translated", 440 | "value" : "Speech Settings" 441 | } 442 | } 443 | } 444 | }, 445 | "SETTINGS_SPEECH_TEXT_TO_SPEECH" : { 446 | "localizations" : { 447 | "en" : { 448 | "stringUnit" : { 449 | "state" : "translated", 450 | "value" : "Enable Text to Speech" 451 | } 452 | } 453 | } 454 | }, 455 | "SETTINGS_TITLE" : { 456 | "comment" : "MARK: Settings View", 457 | "localizations" : { 458 | "en" : { 459 | "stringUnit" : { 460 | "state" : "translated", 461 | "value" : "Settings" 462 | } 463 | } 464 | } 465 | }, 466 | "SPEAKER_DISABLED" : { 467 | "comment" : "MARK: HealthGPT View", 468 | "extractionState" : "manual", 469 | "localizations" : { 470 | "en" : { 471 | "stringUnit" : { 472 | "state" : "translated", 473 | "value" : "Text to speech is disabled, press to enable text to speech." 474 | } 475 | } 476 | } 477 | }, 478 | "SPEAKER_ENABLED" : { 479 | "extractionState" : "manual", 480 | "localizations" : { 481 | "en" : { 482 | "stringUnit" : { 483 | "state" : "translated", 484 | "value" : "Text to speech is enabled, press to disable text to speech." 485 | } 486 | } 487 | } 488 | }, 489 | "WELCOME_AREA1_DESCRIPTION" : { 490 | "extractionState" : "manual", 491 | "localizations" : { 492 | "en" : { 493 | "stringUnit" : { 494 | "state" : "translated", 495 | "value" : "HealthGPT is an experimental iOS app that allows users to query health data stored in Apple Health using ChatGPT." 496 | } 497 | } 498 | } 499 | }, 500 | "WELCOME_AREA1_TITLE" : { 501 | "extractionState" : "manual", 502 | "localizations" : { 503 | "en" : { 504 | "stringUnit" : { 505 | "state" : "translated", 506 | "value" : "ChatGPT" 507 | } 508 | } 509 | } 510 | }, 511 | "WELCOME_AREA2_DESCRIPTION" : { 512 | "extractionState" : "manual", 513 | "localizations" : { 514 | "en" : { 515 | "stringUnit" : { 516 | "state" : "translated", 517 | "value" : "HealthGPT can surface data from Apple Health to use in conversations with ChatGPT." 518 | } 519 | } 520 | } 521 | }, 522 | "WELCOME_AREA2_TITLE" : { 523 | "extractionState" : "manual", 524 | "localizations" : { 525 | "en" : { 526 | "stringUnit" : { 527 | "state" : "translated", 528 | "value" : "Apple Health" 529 | } 530 | } 531 | } 532 | }, 533 | "WELCOME_AREA3_DESCRIPTION" : { 534 | "extractionState" : "manual", 535 | "localizations" : { 536 | "en" : { 537 | "stringUnit" : { 538 | "state" : "translated", 539 | "value" : "HealthGPT was built with the Spezi open-source digital health framework at Stanford University." 540 | } 541 | } 542 | } 543 | }, 544 | "WELCOME_AREA3_TITLE" : { 545 | "extractionState" : "manual", 546 | "localizations" : { 547 | "en" : { 548 | "stringUnit" : { 549 | "state" : "translated", 550 | "value" : "The Spezi Framework" 551 | } 552 | } 553 | } 554 | }, 555 | "WELCOME_BUTTON" : { 556 | "extractionState" : "manual", 557 | "localizations" : { 558 | "en" : { 559 | "stringUnit" : { 560 | "state" : "translated", 561 | "value" : "Continue" 562 | } 563 | } 564 | } 565 | }, 566 | "WELCOME_SUBTITLE" : { 567 | "extractionState" : "manual", 568 | "localizations" : { 569 | "en" : { 570 | "stringUnit" : { 571 | "state" : "translated", 572 | "value" : "HealthGPT demonstrates how LLMs can change how we interact with digital health data." 573 | } 574 | } 575 | } 576 | }, 577 | "WELCOME_TITLE" : { 578 | "comment" : "MARK: Welcome", 579 | "localizations" : { 580 | "en" : { 581 | "stringUnit" : { 582 | "state" : "translated", 583 | "value" : "HealthGPT" 584 | } 585 | } 586 | } 587 | } 588 | }, 589 | "version" : "1.0" 590 | } -------------------------------------------------------------------------------- /HealthGPT/Supporting Files/Localizable.xcstrings.license: -------------------------------------------------------------------------------- 1 | This source file is part of the Stanford HealthGPT project 2 | 3 | SPDX-FileCopyrightText: 2024 Stanford University 4 | 5 | SPDX-License-Identifier: MIT 6 | -------------------------------------------------------------------------------- /HealthGPTTests/PromptGeneratorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | @testable import HealthGPT 10 | import XCTest 11 | 12 | class PromptGeneratorTests: XCTestCase { 13 | var sampleHealthData: [HealthData] = createSampleHealthData() 14 | 15 | private static func createSampleHealthData() -> [HealthData] { 16 | var healthData: [HealthData] = [] 17 | for day in 0...13 { 18 | guard let date = Calendar.current.date(byAdding: .day, value: -(13 - day), to: Date()) else { 19 | continue 20 | } 21 | let dateFormatter = DateFormatter() 22 | dateFormatter.dateFormat = "yyyy-MM-dd" 23 | let dateString = dateFormatter.string(from: date) 24 | 25 | let steps = Double.random(in: 5000..<10000) 26 | let activeEnergy = Double.random(in: 100..<500) 27 | let exerciseMinutes = Double.random(in: 10..<100) 28 | let bodyWeight = Double.random(in: 100..<120) 29 | let sleepHours = Double.random(in: 4..<9) 30 | 31 | let healthDataItem = HealthData( 32 | date: dateString, 33 | steps: steps, 34 | activeEnergy: activeEnergy, 35 | exerciseMinutes: exerciseMinutes, 36 | bodyWeight: bodyWeight, 37 | sleepHours: sleepHours 38 | ) 39 | 40 | healthData.append(healthDataItem) 41 | } 42 | return healthData 43 | } 44 | 45 | func testBuildMainPrompt() { 46 | let promptGenerator = PromptGenerator(with: sampleHealthData) 47 | let mainPrompt = promptGenerator.buildMainPrompt() 48 | let today = DateFormatter.localizedString(from: Date(), dateStyle: .full, timeStyle: .none) 49 | 50 | XCTAssertNotNil(mainPrompt) 51 | 52 | // swiftlint:disable:next line_length 53 | XCTAssertTrue(mainPrompt.contains("You are HealthGPT, an enthusiastic, expert caretaker with a deep understanding in personal health. Given the context, provide a short response that could answer the user's question. Do NOT provide statistics. If numbers seem low, provide advice on how they can improve.\n\nSome health metrics over the past two weeks (14 days) to incorporate is given below. If a value is zero, the user has not inputted anything for that day.")) 54 | 55 | XCTAssertTrue(mainPrompt.contains("Today is \(today)")) 56 | 57 | for healthDataItem in sampleHealthData { 58 | XCTAssertTrue(mainPrompt.contains(healthDataItem.date)) 59 | if let steps = healthDataItem.steps { 60 | XCTAssertTrue(mainPrompt.contains("\(Int(steps)) steps")) 61 | } 62 | if let sleepHours = healthDataItem.sleepHours { 63 | XCTAssertTrue(mainPrompt.contains("\(Int(sleepHours)) hours of sleep")) 64 | } 65 | if let activeEnergy = healthDataItem.activeEnergy { 66 | XCTAssertTrue(mainPrompt.contains("\(Int(activeEnergy)) calories burned")) 67 | } 68 | if let exerciseMinutes = healthDataItem.exerciseMinutes { 69 | XCTAssertTrue(mainPrompt.contains("\(Int(exerciseMinutes)) minutes of exercise")) 70 | } 71 | if let bodyWeight = healthDataItem.bodyWeight { 72 | XCTAssertTrue(mainPrompt.contains("\(bodyWeight) lbs of body weight")) 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /HealthGPTUITests/HealthGPTViewUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import XCTest 10 | import XCTestExtensions 11 | 12 | 13 | final class HealthGPTViewUITests: XCTestCase { 14 | override func setUpWithError() throws { 15 | try super.setUpWithError() 16 | continueAfterFailure = false 17 | 18 | let app = XCUIApplication() 19 | app.launchArguments = ["--showOnboarding", "--resetKeychainStorage", "--mockMode"] 20 | app.deleteAndLaunch(withSpringboardAppName: "HealthGPT") 21 | } 22 | 23 | override func tearDownWithError() throws { 24 | try super.tearDownWithError() 25 | } 26 | 27 | func testChatView() throws { 28 | let app = XCUIApplication() 29 | try app.conductOnboardingIfNeeded() 30 | 31 | XCTAssert(app.buttons["Record Message"].waitForExistence(timeout: 2)) 32 | 33 | try app.textFields["Message Input Textfield"].enter(value: "New Message!") 34 | 35 | XCTAssert(app.buttons["Send Message"].waitForExistence(timeout: 2)) 36 | app.buttons["Send Message"].tap() 37 | 38 | sleep(3) 39 | 40 | XCTAssert(app.staticTexts["Mock Message from SpeziLLM!"].waitForExistence(timeout: 5)) 41 | } 42 | 43 | func testSettingsView() throws { 44 | let app = XCUIApplication() 45 | try app.conductOnboardingIfNeeded() 46 | 47 | let settingsButton = app.buttons["settingsButton"] 48 | XCTAssertTrue(settingsButton.waitForExistence(timeout: 5)) 49 | settingsButton.tap() 50 | 51 | XCTAssert(app.staticTexts["Settings"].waitForExistence(timeout: 2)) 52 | 53 | XCTAssertTrue(app.buttons["Open AI API Key"].exists) 54 | app.buttons["Open AI API Key"].tap() 55 | app.navigationBars.buttons["Settings"].tap() 56 | 57 | XCTAssertTrue(app.buttons["Open AI Model"].exists) 58 | app.buttons["Open AI Model"].tap() 59 | 60 | let picker = app.pickers["modelPicker"] 61 | let optionToSelect = picker.pickerWheels.element(boundBy: 0) 62 | optionToSelect.adjust(toPickerWheelValue: "gpt-4o") 63 | 64 | app.buttons["Save OpenAI Model"].tap() 65 | 66 | XCTAssertTrue(app.staticTexts["Enable Text to Speech"].exists) 67 | 68 | XCTAssert(app.buttons["Done"].exists) 69 | app.buttons["Done"].tap() 70 | 71 | settingsButton.tap() 72 | XCTAssertTrue(app.buttons["Reset Chat"].exists) 73 | app.buttons["Reset Chat"].tap() 74 | 75 | XCTAssert(app.staticTexts["HealthGPT"].waitForExistence(timeout: 2)) 76 | } 77 | 78 | func testResetChat() throws { 79 | let app = XCUIApplication() 80 | try app.conductOnboardingIfNeeded() 81 | 82 | let resetChatButton = app.buttons["resetChatButton"] 83 | XCTAssertTrue(resetChatButton.waitForExistence(timeout: 5)) 84 | resetChatButton.tap() 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /HealthGPTUITests/OnboardingUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This source file is part of the Stanford HealthGPT project 3 | // 4 | // SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | // 6 | // SPDX-License-Identifier: MIT 7 | // 8 | 9 | import XCTest 10 | import XCTestExtensions 11 | import XCTHealthKit 12 | 13 | 14 | final class OnboardingUITests: XCTestCase { 15 | override func setUpWithError() throws { 16 | try super.setUpWithError() 17 | 18 | continueAfterFailure = false 19 | 20 | let app = XCUIApplication() 21 | app.launchArguments = ["--showOnboarding", "--resetKeychainStorage"] 22 | app.deleteAndLaunch(withSpringboardAppName: "HealthGPT") 23 | } 24 | 25 | override func tearDownWithError() throws { 26 | try super.tearDownWithError() 27 | } 28 | 29 | func testOnboardingFlow() throws { 30 | let app = XCUIApplication() 31 | 32 | try app.navigateOnboardingFlow(assertThatHealthKitConsentIsShown: true) 33 | } 34 | } 35 | 36 | extension XCUIApplication { 37 | func conductOnboardingIfNeeded() throws { 38 | if self.staticTexts["HealthGPT"].waitForExistence(timeout: 10) { 39 | try navigateOnboardingFlow(assertThatHealthKitConsentIsShown: false) 40 | } 41 | } 42 | 43 | func navigateOnboardingFlow(assertThatHealthKitConsentIsShown: Bool = true) throws { 44 | try navigateOnboardingFlowWelcome() 45 | try navigateOnboardingFlowDisclaimer() 46 | try navigateOnboardingFlowLLMSourceSelection() 47 | try navigateOnboardingFlowApiKey() 48 | try navigateOnboardingFlowModelSelection() 49 | try navigateOnboardingFlowHealthKitAccess(assertThatHealthKitConsentIsShown: assertThatHealthKitConsentIsShown) 50 | } 51 | 52 | private func navigateOnboardingFlowWelcome() throws { 53 | XCTAssertTrue(staticTexts["HealthGPT"].waitForExistence(timeout: 10)) 54 | 55 | XCTAssertTrue(buttons["Continue"].waitForExistence(timeout: 10)) 56 | buttons["Continue"].tap() 57 | } 58 | 59 | private func navigateOnboardingFlowDisclaimer() throws { 60 | XCTAssertTrue(staticTexts["Disclaimer"].waitForExistence(timeout: 10)) 61 | 62 | for _ in 1..<4 { 63 | XCTAssertTrue(buttons["Next"].waitForExistence(timeout: 10)) 64 | buttons["Next"].tap() 65 | } 66 | 67 | XCTAssertTrue(buttons["I Agree"].waitForExistence(timeout: 10)) 68 | buttons["I Agree"].tap() 69 | } 70 | 71 | private func navigateOnboardingFlowLLMSourceSelection() throws { 72 | XCTAssertTrue(staticTexts["LLM Source Selection"].waitForExistence(timeout: 5)) 73 | 74 | let picker = pickers["llmSourcePicker"] 75 | let optionToSelect = picker.pickerWheels.element(boundBy: 0) 76 | optionToSelect.adjust(toPickerWheelValue: "On-device LLM") 77 | 78 | XCTAssertTrue(buttons["Save Choice"].waitForExistence(timeout: 5)) 79 | buttons["Save Choice"].tap() 80 | 81 | XCTAssertTrue(staticTexts["LLM Download"].waitForExistence(timeout: 5)) 82 | XCTAssertTrue(buttons["Back"].waitForExistence(timeout: 2)) 83 | buttons["Back"].tap() 84 | 85 | optionToSelect.adjust(toPickerWheelValue: "Open AI LLM") 86 | XCTAssertTrue(buttons["Save Choice"].waitForExistence(timeout: 5)) 87 | buttons["Save Choice"].tap() 88 | } 89 | 90 | private func navigateOnboardingFlowApiKey() throws { 91 | try textFields["OpenAI API Key"].enter(value: "sk-123456789") 92 | 93 | XCTAssertTrue(buttons["Next"].waitForExistence(timeout: 2)) 94 | buttons["Next"].tap() 95 | } 96 | 97 | private func navigateOnboardingFlowModelSelection() throws { 98 | XCTAssertTrue(staticTexts["Select an OpenAI Model"].waitForExistence(timeout: 10)) 99 | XCTAssertTrue(buttons["Save OpenAI Model"].waitForExistence(timeout: 10)) 100 | 101 | let picker = pickers["modelPicker"] 102 | let optionToSelect = picker.pickerWheels.element(boundBy: 0) 103 | optionToSelect.adjust(toPickerWheelValue: "gpt-4o") 104 | 105 | buttons["Save OpenAI Model"].tap() 106 | } 107 | 108 | private func navigateOnboardingFlowHealthKitAccess(assertThatHealthKitConsentIsShown: Bool = true) throws { 109 | XCTAssertTrue(staticTexts["HealthKit Access"].waitForExistence(timeout: 10)) 110 | 111 | XCTAssertTrue(buttons["Grant Access"].waitForExistence(timeout: 10)) 112 | buttons["Grant Access"].tap() 113 | 114 | try handleHealthKitAuthorization() 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Stanford University and the project authors (see CONTRIBUTORS.md) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | # HealthGPT 12 | 13 | [![Beta Deployment](https://github.com/StanfordBDHG/HealthGPT/actions/workflows/beta-deployment.yml/badge.svg)](https://github.com/StanfordBDHG/HealthGPT/actions/workflows/beta-deployment.yml) 14 | [![codecov](https://codecov.io/gh/StanfordBDHG/HealthGPT/branch/main/graph/badge.svg?token=5BEldGX6G1)](https://codecov.io/gh/StanfordBDHG/HealthGPT) 15 | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7850785.svg)](https://doi.org/10.5281/zenodo.7850785) 16 | 17 | |Screenshot showing an example conversation with HealthGPT|Screenshot showing settings for HealthGPT.|Screenshot showing chat export for HealthGPT.| 18 | |:--:|:--:|:--:| 19 | |Example Conversation|Settings|Export Chat| 20 | 21 | HealthGPT is an experimental iOS app based on [Stanford Spezi](https://github.com/StanfordSpezi/Spezi) that allows users to interact with their health data stored in the Apple Health app using natural language. The application offers an easy-to-extend solution for those looking to make large language model (LLM) powered apps within the Apple Health ecosystem. 22 | 23 | HealthGPT is an open-source project of the [Stanford Biodesign Digital Health](https://bdh.stanford.edu/) team. The initial prototype based on [Spezi](https://github.com/StanfordSpezi/Spezi) and the [SpeziTemplateApplication](https://github.com/StanfordSpezi/SpeziTemplateApplication/) was built by [Varun Shenoy](https://varunshenoy.com). 24 | 25 | > [!NOTE] 26 | > Do you want to try HealthGPT? You can download it to your iOS device using [TestFlight](https://testflight.apple.com/join/1wYMt3em)! 27 | 28 | ## Features 29 | 30 | - Extensible architecture built on the [Stanford Spezi](https://github.com/StanfordSpezi/Spezi) open-source digital health development framework for easy customization. 31 | - Chat-style interface for user-friendly health data interaction using the [SpeziChat](https://github.com/StanfordSpezi/SpeziChat) module with speech-to-text (recognition) as well as text-to-speech (synthesize) accessibility capabilities and chat export functionality. 32 | - Integration with the Apple Health app via [SpeziHealthKit](https://github.com/StanfordSpezi/SpeziHealthKit). 33 | - GPT-3.5 + GPT-4 queries through the [SpeziLLM](https://github.com/StanfordSpezi/SpeziLLM) module. 34 | - Option for privacy preserving local execution of LLM queries using [SpeziLLMLocal](https://swiftpackageindex.com/stanfordspezi/spezillm/documentation/spezillmlocal) with [Llama3 8B](https://llama.meta.com/llama3/), including automated download and storage of model files during onboarding. 35 | - Out of the box support for querying sleep, step count, active energy, exercise minutes, heart rate, and body mass. 36 | 37 | 38 | ## Disclaimer 39 | 40 | HealthGPT is provided for general informational purposes only and is not intended as a substitute for professional medical advice, diagnosis, or treatment. Large language models, such as those provided by OpenAI, are known to hallucinate and at times return false information. The use of HealthGPT is at your own risk. Always consult a qualified healthcare provider for personalized advice regarding your health and well-being. Aggregated HealthKit data for the past 14 days will be uploaded to OpenAI. Please refer to the [OpenAI privacy policy](https://openai.com/policies/privacy-policy) for more information. 41 | 42 | ## Set Up 43 | 44 | Building and running HealthGPT requires a Mac with [Xcode 16.2](https://developer.apple.com/xcode/) or newer installed. 45 | 46 | 1. Clone this repository to your local computer. 47 | 2. Open `HealthGPT.xcodeproj` in Xcode. Wait for all dependencies to install and indexing to finish. 48 | 3. Run the app (on an iOS device or in the iOS simulator) and play with HealthGPT on your own data 🚀 49 | 50 | > [!IMPORTANT] 51 | > If you are running HealthGPT using the simulator, you will need to manually add data in the Apple Health app. Otherwise, all of your results will read zero. 52 | 53 | > [!IMPORTANT] 54 | > Running HealthGPT with a local LLM requires installing the app on a physical device, as [SpeziLLMLocal](https://swiftpackageindex.com/stanfordspezi/spezillm/documentation/spezillmlocal) requires a modern Metal MTLGPUFamily and simulators do not provide that. 55 | 56 | You can add queries for additional [HealthKit](https://developer.apple.com/documentation/healthkit) quantities and categories as follows: 57 | 58 | 1. Update the SpeziHealthKit configuration in [`HealthGPT/HealthGPT/AppDelegate.swift`](https://github.com/StanfordBDHG/HealthGPT/blob/main/HealthGPT/HealthGPTAppDelegate.swift) to include the additional data type(s). For more information about configuring SpeziHealthKit, please refer to the [official documentation](https://swiftpackageindex.com/StanfordSpezi/SpeziHealthKit/0.5.3/documentation/spezihealthkit). 59 | 2. Edit [`HealthGPT/HealthGPT/HealthDataFetcher.swift`](https://github.com/StanfordBDHG/HealthGPT/blob/main/HealthGPT/HealthGPT/HealthDataFetcher.swift) to create appropriate query for your data type(s). 60 | 3. Update the prompt in [`HealthGPT/HealthGPT/PromptGenerator.swift`](https://github.com/StanfordBDHG/HealthGPT/blob/main/HealthGPT/HealthGPT/PromptGenerator.swift) to pass the newly acquired data to the OpenAI API. 61 | 62 | 63 | ## Contributing 64 | 65 | Contributions to this project are welcome. Please make sure to read the [contribution guidelines](https://github.com/StanfordBDHG/.github/blob/main/CONTRIBUTING.md) and the [contributor covenant code of conduct](https://github.com/StanfordBDHG/.github/blob/main/CODE_OF_CONDUCT.md) first. 66 | You can find a list of contributors in the [`CONTRIBUTORS.md`](https://github.com/StanfordBDHG/HealthGPT/blob/main/CONTRIBUTORS.md) file. 67 | 68 | 69 | ## License 70 | 71 | This project is licensed under the MIT License. See [Licenses](https://github.com/StanfordBDHG/HealthGPT/blob/main/LICENSES) for more information. 72 | 73 | 74 | ![Stanford Byers Center for Biodesign Logo](https://raw.githubusercontent.com/StanfordBDHG/.github/main/assets/biodesign-footer-light.png#gh-light-mode-only) 75 | ![Stanford Byers Center for Biodesign Logo](https://raw.githubusercontent.com/StanfordBDHG/.github/main/assets/biodesign-footer-dark.png#gh-dark-mode-only) 76 | -------------------------------------------------------------------------------- /fastlane/.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # This source file is part of the Stanford HealthGPT project 3 | # 4 | # SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | # 6 | # SPDX-License-Identifier: MIT 7 | # 8 | 9 | test_output 10 | report.xml 11 | screenshots -------------------------------------------------------------------------------- /fastlane/Appfile: -------------------------------------------------------------------------------- 1 | # 2 | # This source file is part of the Stanford HealthGPT project 3 | # 4 | # SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | # 6 | # SPDX-License-Identifier: MIT 7 | # 8 | 9 | # For more information about the Appfile, see: 10 | # https://docs.fastlane.tools/advanced/#appfile 11 | 12 | app_identifier "edu.stanford.bdhg.healthgpt" # The bundle identifier of your app 13 | apple_id ENV["APPLE_ID"] # Your Apple email address -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | # 2 | # This source file is part of the Stanford HealthGPT project 3 | # 4 | # SPDX-FileCopyrightText: 2023 Stanford University & Project Contributors (see CONTRIBUTORS.md) 5 | # 6 | # SPDX-License-Identifier: MIT 7 | # 8 | 9 | default_platform(:ios) 10 | 11 | platform :ios do 12 | before_all do 13 | ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "5" 14 | ENV["FASTLANE_XCODEBUILD_SETTINGS_RETRIES"] = "6" 15 | end 16 | 17 | desc "Build and test" 18 | lane :test do 19 | run_tests( 20 | skip_build: true, 21 | derived_data_path: ".derivedData", 22 | code_coverage: true, 23 | devices: ["iPhone 15 Pro"], 24 | disable_slide_to_type: false, 25 | concurrent_workers: 1, 26 | max_concurrent_simulators: 1, 27 | result_bundle: true, 28 | output_directory: ".", 29 | xcargs: "-skipPackagePluginValidation" 30 | ) 31 | end 32 | 33 | desc "CodeQL" 34 | lane :codeql do 35 | build_app( 36 | skip_archive: true, 37 | skip_codesigning: true, 38 | derived_data_path: ".derivedData", 39 | xcargs: "-skipPackagePluginValidation" 40 | ) 41 | end 42 | 43 | desc "Build app" 44 | lane :build do 45 | build_app( 46 | derived_data_path: ".derivedData", 47 | xcargs: "-skipPackagePluginValidation", 48 | export_options: { 49 | provisioningProfiles: { 50 | "edu.stanford.bdhg.healthgpt" => "HealthGPT" 51 | } 52 | } 53 | ) 54 | end 55 | 56 | desc "Sign in to the App Store Connect API" 57 | lane :signin do 58 | app_store_connect_api_key( 59 | key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"], 60 | issuer_id: ENV["APP_STORE_CONNECT_ISSUER_ID"], 61 | key_content: ENV["APP_STORE_CONNECT_API_KEY_BASE64"], 62 | is_key_content_base64: true 63 | ) 64 | end 65 | 66 | desc "Publish a beta release to internal TestFlight testers" 67 | lane :beta do 68 | signin 69 | increment_build_number( 70 | { 71 | build_number: latest_testflight_build_number + 1 72 | } 73 | ) 74 | build 75 | commit = last_git_commit 76 | upload_to_testflight( 77 | distribute_external: true, 78 | groups: [ 79 | "External Testers" 80 | ], 81 | submit_beta_review: true, 82 | notify_external_testers: true, 83 | expire_previous_builds: true, 84 | changelog: commit[:message] 85 | ) 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | fastlane documentation 12 | ---- 13 | 14 | # Installation 15 | 16 | Make sure you have the latest version of the Xcode command line tools installed: 17 | 18 | ```sh 19 | xcode-select --install 20 | ``` 21 | 22 | For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane) 23 | 24 | # Available Actions 25 | 26 | ## iOS 27 | 28 | ### ios test 29 | 30 | ```sh 31 | [bundle exec] fastlane ios test 32 | ``` 33 | 34 | Build and test 35 | 36 | ### ios codeql 37 | 38 | ```sh 39 | [bundle exec] fastlane ios codeql 40 | ``` 41 | 42 | CodeQL 43 | 44 | ### ios build 45 | 46 | ```sh 47 | [bundle exec] fastlane ios build 48 | ``` 49 | 50 | Build app 51 | 52 | ### ios signin 53 | 54 | ```sh 55 | [bundle exec] fastlane ios signin 56 | ``` 57 | 58 | Sign in to the App Store Connect API 59 | 60 | ### ios beta 61 | 62 | ```sh 63 | [bundle exec] fastlane ios beta 64 | ``` 65 | 66 | Publish a beta release to internal TestFlight testers 67 | 68 | ---- 69 | 70 | This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. 71 | 72 | More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools). 73 | 74 | The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 75 | --------------------------------------------------------------------------------