Skip to content

Add starter query suggestions for empty chat screen - #59

Open
ehanrsha wants to merge 2 commits into
StanfordBDHG:mainfrom
ehanrsha:feature/starter-queries
Open

ehanrsha wants to merge 2 commits into
StanfordBDHG:mainfrom
ehanrsha:feature/starter-queries

Conversation

@ehanrsha

Copy link
Copy Markdown

✧ When the chat is empty, display tappable example prompts so first-time users see what kinds of questions HealthGPT can answer. Suggestions disappear once the conversation has any messages.

✧ Closes #24

✧ Signed-off-by: Ehan Shah ehanrsha@gmail.com

✧ Created A Video Demonstrating The Addition -> https://drive.google.com/file/d/1QnNQ4cw0hT9HTUWk4DZHG_FmUZxRZUuQ/view?usp=sharing

✧ When the chat is empty, display tappable example prompts so first-time users see what kinds of questions HealthGPT can answer. Suggestions disappear once the conversation has any messages.

✧ Closes StanfordBDHG#24

✧ Signed-off-by: Ehan Shah <ehanrsha@gmail.com>
@coderabbitai

coderabbitai Bot commented May 22, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR implements starter query suggestions for empty chat sessions. A new SuggestionBannerView displays rotating sample queries with fade animations. HealthGPTView is refactored to detect empty chat via isChatEmpty, overlay the suggestion banner in a ZStack, and move LLM query logic into a new chatViewContent view builder. Unresolved merge-conflict markers are present.

Changes

Empty Chat Suggestion Flow

Layer / File(s) Summary
Merge conflict marker
HealthGPT/HealthGPT/HealthGPTView.swift
Unresolved merge-conflict marker (<<<<<<< Updated upstream) at line 32 must be resolved before the file compiles.
SuggestionBannerView component
HealthGPT/HealthGPT/SuggestionBannerView.swift
New SuggestionBannerView displays a "Try asking" prompt with a rotating suggestion pill. The view cycles suggestions asynchronously with fade-in/fade-out animations, seeds the initial suggestion from SampleQueries.random(), and invokes onSuggestionTapped when the suggestion is tapped. The cycleSuggestions() async method runs for the view's lifetime and exits early on task cancellation.
HealthGPTView refactor and integration
HealthGPT/HealthGPT/HealthGPTView.swift
isChatEmpty computed property checks whether the LLM context contains any messages. body is refactored to delegate to chatViewContent, a new view builder that conditionally renders either the suggestion banner in a ZStack overlay (when chat is empty) or the normal chat UI. The .onChange handler for messageTaskId and the async .task(id:) that calls healthDataInterpreter.queryLLM() are moved into chatViewContent.

Sequence Diagrams

sequenceDiagram
  participant User
  participant SuggestionBannerView
  participant cycleSuggestions
  participant SampleQueries
  
  User->>SuggestionBannerView: View appears
  activate cycleSuggestions
  SuggestionBannerView->>cycleSuggestions: task starts
  loop Rotate suggestions
    cycleSuggestions->>SuggestionBannerView: fade out (opacity = 0)
    SuggestionBannerView->>SampleQueries: random(excluding previous)
    SampleQueries-->>cycleSuggestions: new suggestion
    cycleSuggestions->>SuggestionBannerView: update currentSuggestion
    cycleSuggestions->>SuggestionBannerView: fade in (opacity = 1)
    cycleSuggestions->>cycleSuggestions: wait for interval
  end
  User->>SuggestionBannerView: tap suggestion pill
  SuggestionBannerView->>User: onSuggestionTapped(currentSuggestion)
  deactivate cycleSuggestions
Loading
sequenceDiagram
  participant HealthGPTView
  participant chatViewContent
  participant isChatEmpty
  participant SuggestionBannerView
  participant healthDataInterpreter
  
  HealthGPTView->>isChatEmpty: check if chat has messages
  isChatEmpty-->>chatViewContent: boolean result
  chatViewContent->>chatViewContent: condition on isChatEmpty
  alt Chat is empty
    chatViewContent->>SuggestionBannerView: render with ZStack overlay
    SuggestionBannerView->>SuggestionBannerView: wait for user tap
    SuggestionBannerView->>chatViewContent: onSuggestionTapped(query)
    chatViewContent->>chatViewContent: append ChatEntity to context
  else Chat has messages
    chatViewContent->>chatViewContent: render chat messages
  end
  chatViewContent->>chatViewContent: onChange messageTaskId incremented
  chatViewContent->>healthDataInterpreter: queryLLM() on new non-system message
  healthDataInterpreter-->>chatViewContent: LLM response or error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding starter query suggestions to the empty chat screen.
Description check ✅ Passed The description adequately explains the feature, its purpose for first-time users, and references the linked issue #24.
Linked Issues check ✅ Passed The code changes implement the core objective of issue #24: displaying sample queries on an empty chat screen so users understand possible interactions.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing starter query suggestions. The refactoring of HealthGPTView and addition of SuggestionBannerView are both necessary and scoped to the feature.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ehanrsha ehanrsha mentioned this pull request May 22, 2026
1 task done

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
HealthGPT/HealthGPT/HealthGPTView.swift (1)

32-83: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Unresolved merge conflict markers will break compilation.

Lines 32, 70, and 83 contain Git conflict markers (<<<<<<< Updated upstream, =======, >>>>>>> Stashed changes). These must be resolved before merging. Based on the PR objectives, you likely want to keep the "Stashed changes" section (the new isChatEmpty property and refactored body).

🐛 Proposed fix to resolve the merge conflict

Remove lines 32-70 and line 83 to keep the new implementation:

-<<<<<<< Updated upstream
-    var body: some View {
-        NavigationStack {
-            if let llm = self.healthDataInterpreter.llm {
-                let contextBinding = Binding { llm.context.chat } set: { llm.context.chat = $0 }
-                
-                ChatView(contextBinding, exportFormat: .text)
-                    .speak(llm.context.chat, muted: !self.textToSpeech)
-                    .speechToolbarButton(muted: !self.$textToSpeech)
-                    .viewStateAlert(state: llm.state)
-                    .navigationTitle("WELCOME_TITLE")
-                    .toolbar {
-                        ToolbarItem(placement: .primaryAction) {
-                            self.settingsButton
-                        }
-                        ToolbarItem(placement: .primaryAction) {
-                            self.resetChatButton
-                        }
-                    }
-                    .onChange(of: llm.context, initial: true) { _, _ in
-                        // Once the user enters a message in the chat, increase `messageTaskId` that triggers LLM inference
-                        if !llm.context.isEmpty && llm.state != .generating && llm.context.last?.role != .system {
-                            self.messageTaskId += 1
-                        }
-                    }
-                    // Triggered on every new user message in the chat via `messageTaskId`
-                    // Automatically cancels the LLM inference once view disappears
-                    .task(id: self.messageTaskId) {
-                        do {
-                            try await healthDataInterpreter.queryLLM()
-                        } catch {
-                            showErrorAlert = true
-                            errorMessage = "Error querying LLM: \(error.localizedDescription)"
-                        }
-                    }
-            } else {
-                self.loadingChatView
-            }
-=======
     /// Whether the chat has no user or assistant messages yet.
     /// Controls visibility of the suggestion banner overlay.
     private var isChatEmpty: Bool {
         ...
     }

     var body: some View {
         NavigationStack {
             chatViewContent
->>>>>>> Stashed changes
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@HealthGPT/HealthGPT/HealthGPTView.swift` around lines 32 - 83, Remove the Git
conflict markers and the duplicated "Updated upstream" block so the file
compiles; keep the "Stashed changes" implementation (the new isChatEmpty
property and the refactored var body that uses chatViewContent) and delete the
entire <<<<<<< Updated upstream ... ======= ... >>>>>>> Stashed changes section
(including the old ChatView-based body that references
messageTaskId/llm.context). Ensure only the single isChatEmpty property and the
single var body { NavigationStack { chatViewContent } } remain and no conflict
markers remain in HealthGPTView.swift.
🧹 Nitpick comments (1)
HealthGPT/HealthGPT/SuggestionBannerView.swift (1)

64-65: 💤 Low value

Comment/code timing mismatches.

Several comments don't match the actual timing values:

  • Line 64 says "10 seconds" but line 65 uses 7 seconds
  • Lines 71 and 86 say "0.4s" but the code uses duration: 0.6
📝 Proposed fix to align comments with code
-            // Wait 10 seconds before cycling
+            // Wait 7 seconds before cycling
             try? await Task.sleep(for: .seconds(7))
             ...
-            // Phase 1: Fade out (0.4s)
+            // Phase 1: Fade out (0.6s)
             withAnimation(.easeInOut(duration: 0.6)) {
             ...
-            // Phase 3: Fade in (0.4s)
+            // Phase 3: Fade in (0.6s)
             withAnimation(.easeInOut(duration: 0.6)) {

Also applies to: 71-72, 86-87

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@HealthGPT/HealthGPT/SuggestionBannerView.swift` around lines 64 - 65,
Comments in SuggestionBannerView.swift are out of sync with the actual timing
constants; update either the comments or the code so they match. Specifically,
reconcile the "Wait 10 seconds before cycling" comment with the Task.sleep call
(Task.sleep(for: .seconds(7))): either change the comment to "7 seconds" or
change Task.sleep to .seconds(10). Also update the two comments that say "0.4s"
to reflect the actual animation duration used (duration: 0.6) or change the
duration parameter to 0.4 to match the comments; the relevant symbols to inspect
are the Task.sleep(for: .seconds(7)) call and the animation blocks using
duration: 0.6.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@HealthGPT/HealthGPT/SuggestionBannerView.swift`:
- Line 19: SuggestionBannerView references SampleQueries.random() and
SampleQueries.random(excluding:) but that type is missing; add a small utility
type (e.g., struct SampleQueries) that exposes a static collection of sample
strings and two static helpers: random() -> String and random(excluding:
String?) -> String (or update the calls to whatever existing provider type you
already have). Implement random(excluding:) to return a random sample different
from the excluded value and call it from SuggestionBannerView where
currentSuggestion is initialized and updated so the view compiles and behavior
remains the same.

---

Outside diff comments:
In `@HealthGPT/HealthGPT/HealthGPTView.swift`:
- Around line 32-83: Remove the Git conflict markers and the duplicated "Updated
upstream" block so the file compiles; keep the "Stashed changes" implementation
(the new isChatEmpty property and the refactored var body that uses
chatViewContent) and delete the entire <<<<<<< Updated upstream ... ======= ...
>>>>>>> Stashed changes section (including the old ChatView-based body that
references messageTaskId/llm.context). Ensure only the single isChatEmpty
property and the single var body { NavigationStack { chatViewContent } } remain
and no conflict markers remain in HealthGPTView.swift.

---

Nitpick comments:
In `@HealthGPT/HealthGPT/SuggestionBannerView.swift`:
- Around line 64-65: Comments in SuggestionBannerView.swift are out of sync with
the actual timing constants; update either the comments or the code so they
match. Specifically, reconcile the "Wait 10 seconds before cycling" comment with
the Task.sleep call (Task.sleep(for: .seconds(7))): either change the comment to
"7 seconds" or change Task.sleep to .seconds(10). Also update the two comments
that say "0.4s" to reflect the actual animation duration used (duration: 0.6) or
change the duration parameter to 0.4 to match the comments; the relevant symbols
to inspect are the Task.sleep(for: .seconds(7)) call and the animation blocks
using duration: 0.6.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8adde90d-7c69-48cd-8188-564c09e5a60f

📥 Commits

Reviewing files that changed from the base of the PR and between 6e8b3db and a78886e.

📒 Files selected for processing (2)
  • HealthGPT/HealthGPT/HealthGPTView.swift
  • HealthGPT/HealthGPT/SuggestionBannerView.swift

let onSuggestionTapped: (String) -> Void

/// The currently displayed suggestion text.
@State private var currentSuggestion: String = SampleQueries.random()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for SampleQueries definition
ast-grep --pattern 'struct SampleQueries {
  $$$
}'

ast-grep --pattern 'enum SampleQueries {
  $$$
}'

# Also search with rg for any SampleQueries definition
rg -n 'SampleQueries' --type swift -C3

Repository: StanfordBDHG/HealthGPT

Length of output: 1306


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate any SampleQueries declarations/definitions and related APIs
rg -n --type swift '(^|\s)(struct|enum|class)\s+SampleQueries\b' .

# Find all references to `SampleQueries.` to inspect which APIs are expected/implemented
rg -n --type swift 'SampleQueries\.' .

# Find any occurrences of `random(` near SampleQueries usage/definitions
rg -n --type swift 'SampleQueries.*random' .

# List files that mention SampleQueries (helps locate where it's defined)
rg -l --type swift '\bSampleQueries\b' .

Repository: StanfordBDHG/HealthGPT

Length of output: 48


🏁 Script executed:

#!/bin/bash
set -uo pipefail

# Broader: find ANY mentions of SampleQueries
rg -n --type swift '\bSampleQueries\b' . || true

# Find possible declaration forms (struct/enum/class/static)
rg -n --type swift '((^|\s)(struct|enum|class)\s+SampleQueries\b)|((\bstatic\s+func\b|\bfunc\b)\s+random\b.*\bSampleQueries\b)|(\bSampleQueries\s*=)' . || true

# Find expected API usages explicitly
rg -n --type swift 'SampleQueries\s*\.\s*random\s*\(' . || true
rg -n --type swift 'SampleQueries\s*\.\s*random\s*\(\s*excluding\s*:' . || true

# Find any function named random(excluding:) across repo (to correlate with SampleQueries implementation)
rg -n --type swift '\brandom\s*\(\s*excluding\s*:' . || true

Repository: StanfordBDHG/HealthGPT

Length of output: 858


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect imports / context at top of the file under review
sed -n '1,60p' HealthGPT/HealthGPT/SuggestionBannerView.swift

# Search entire repo (all filetypes) for any SampleQueries declaration/definition hints
rg -n 'SampleQueries' . || true

# Specifically search for potential declaration forms across all filetypes
rg -n '(struct|enum|class)\s+SampleQueries\b|SampleQueries\s*=' . || true

Repository: StanfordBDHG/HealthGPT

Length of output: 2561


Fix missing SampleQueries type/API usage in SuggestionBannerView.

SuggestionBannerView.swift references SampleQueries.random() (line 19) and SampleQueries.random(excluding:) (line ~84), but SampleQueries is not defined anywhere in the repository (repo-wide search for SampleQueries only returns these usages). With only import SwiftUI, this will not compile unless SampleQueries is added or the calls are updated to the correct type/API.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@HealthGPT/HealthGPT/SuggestionBannerView.swift` at line 19,
SuggestionBannerView references SampleQueries.random() and
SampleQueries.random(excluding:) but that type is missing; add a small utility
type (e.g., struct SampleQueries) that exposes a static collection of sample
strings and two static helpers: random() -> String and random(excluding:
String?) -> String (or update the calls to whatever existing provider type you
already have). Implement random(excluding:) to return a random sample different
from the excluded value and call it from SuggestionBannerView where
currentSuggestion is initialized and updated so the view compiles and behavior
remains the same.

@ehanrsha

Copy link
Copy Markdown
Author

@vishnuravi Good afternoon! Were you able to review this by any change? Do you have any feedback, or do i need to make any changes? Thank you.

✧ Created A Video Demonstrating The Addition -> https://drive.google.com/file/d/1QnNQ4cw0hT9HTUWk4DZHG_FmUZxRZUuQ/view?usp=sharing

@vishnuravi

vishnuravi commented May 27, 2026 •

Copy link
Copy Markdown
Member

Hi @ehanrsha thanks so much for the code contribution. I'll try this out and get back to you shortly!

@ehanrsha

ehanrsha commented Jun 24, 2026 •

Copy link
Copy Markdown
Author

Hi @ehanrsha thanks so much for the code contribution. I'll try this out and get back to you shortly!

Good evening @vishnuravi Dr. Ravi,
I don't want to bother, and it’s no rush. I’m just following up to see if you were able to take a look at my code and see if we could merge it, or if any changes need to be made. Thank you so much!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Suggest starter queries

2 participants