Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the
📝 WalkthroughWalkthroughA new healthcare agent workflow module is introduced featuring data structures for insurance and appointment results, task implementations for collecting insurance and scheduling appointments, and a core HealthcareAgent with information gathering, appointment scheduling, and server integration capabilities. Changes
Sequence DiagramsequenceDiagram
participant Client as RTCClient
participant Agent as HealthcareAgent
participant InfoTG as TaskGroup<br/>(Information)
participant GetInsTask as GetInsuranceTask
participant SchAppTask as ScheduleAppointmentTask
Client->>Agent: Start Session
Agent->>Agent: on_enter() - gather reason for call
Client->>Agent: schedule_appointment()
Agent->>InfoTG: build information TaskGroup
InfoTG->>GetInsTask: collect insurance info
GetInsTask->>Client: prompt for insurance
Client->>GetInsTask: record_health_insurance()
GetInsTask->>InfoTG: complete with GetInsuranceResult
InfoTG->>SchAppTask: begin appointment scheduling
SchAppTask->>Client: confirm doctor selection
Client->>SchAppTask: confirm_doctor_selection()
SchAppTask->>Client: confirm visit reason
Client->>SchAppTask: confirm_visit_reason()
SchAppTask->>Client: schedule appointment
Client->>SchAppTask: schedule_appointment(time)
SchAppTask->>SchAppTask: validate required fields
SchAppTask->>InfoTG: complete with ScheduleAppointmentResult
InfoTG->>Agent: information gathering complete
Agent->>Client: return completion message
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In `@examples/healthcare/healthcare_agent.py`:
- Around line 145-146: The variable results assigned from awaiting task_group is
unused and triggers ruff F841; either remove the binding or use the
value—replace "results = await task_group" with just "await task_group" to avoid
the unused variable, or if you intend to persist task outputs, iterate the
returned results and load them into the DB (e.g., process/insert each item) so
the results from task_group are consumed; apply the same change for the other
occurrence where results is assigned.
- Around line 72-76: The tool handler stubs never store the user's choices, so
implement the handlers (e.g., the `@function_tool` methods
confirm_doctor_selection and the corresponding confirm_visit_reason handler) to
set the agent's state fields _selected_doctor and _visit_reason respectively
from their arguments, persist any needed normalized values, and return a short
confirmation string; update any downstream call in on_enter or schedule flow to
read these fields so scheduling can proceed.
- Around line 78-88: schedule_appointment currently passes the appointment_time
string into ScheduleAppointmentResult which expects a datetime; parse and
validate the incoming appointment_time string into a datetime before calling
self.complete. In schedule_appointment, use a safe parser (e.g.,
datetime.fromisoformat or a robust parser) to convert appointment_time to a
datetime, catch parsing exceptions and call self.session.generate_reply with a
clear error message if parsing fails, and only construct
ScheduleAppointmentResult with the parsed datetime
(doctor_name=self._selected_doctor, appointment_time=<parsed_datetime>,
visit_reason=self._visit_reason) when validation succeeds.
- Around line 96-101: The instructions string in HealthcareAgent tells the model
to call EndCallTool but EndCallTool is not imported or registered; either remove
the "call EndCallTool" phrase from the instructions or register the tool by
importing EndCallTool and passing it in the Agent's tools parameter (uncomment
and wire the existing tools=[EndCallTool(...)] line), ensuring the tools list
includes the EndCallTool instance and any required end_instructions so the model
can actually invoke it; update HealthcareAgent.__init__ accordingly and keep the
"schedule_appointment" instruction if that tool is handled separately.
- Around line 69-70: Replace the Python 3.10-only union annotations with
typing.Optional for compatibility: import Optional from typing at the top of the
module, and change the attributes self._selected_doctor: str | None and
self._visit_reason: str | None to use Optional[str] (i.e.,
self._selected_doctor: Optional[str] and self._visit_reason: Optional[str]);
ensure the import is added only if not already present.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
examples/healthcare/healthcare_agent.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings
Files:
examples/healthcare/healthcare_agent.py
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: livekit/agents PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-16T07:44:49.987Z
Learning: Follow the guidelines and standards defined in AGENTS.md
🧬 Code graph analysis (1)
examples/healthcare/healthcare_agent.py (3)
livekit-agents/livekit/agents/voice/agent.py (4)
Agent(34-642)AgentTask(648-813)instructions(99-104)complete(687-706)livekit-agents/livekit/agents/beta/workflows/email_address.py (1)
GetEmailTask(27-130)livekit-agents/livekit/agents/beta/workflows/task_group.py (1)
TaskGroup(39-155)
🪛 GitHub Check: ruff
examples/healthcare/healthcare_agent.py
[failure] 159-159: Ruff (F841)
examples/healthcare/healthcare_agent.py:159:9: F841 Local variable results is assigned to but never used
[failure] 145-145: Ruff (F841)
examples/healthcare/healthcare_agent.py:145:9: F841 Local variable results is assigned to but never used
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: type-check (3.13)
- GitHub Check: type-check (3.9)
- GitHub Check: livekit-plugins-cartesia
- GitHub Check: livekit-plugins-deepgram
- GitHub Check: unit-tests
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| if len(str(card_number)) < 13 or len(str(card_number)) > 19: | ||
| self.session.generate_reply( | ||
| instructions="The length of the card number is invalid, ask the user to repeat their card number." | ||
| ) | ||
| return None |
There was a problem hiding this comment.
🟡 Negative card number passes length check and crashes Luhn validation
In GetCardNumberTask.update_card_number, the length validation at line 127 uses len(str(card_number)). For a negative integer (e.g., -1234567890123), str() includes the minus sign in the length ("-1234567890123" has length 14), so the check < 13 or > 19 can pass. The negative number is then stored and passed to validate_card_number, where str(card_number)[::-1] reverses to "3210987654321-". When iterating and calling int(digit) on the '-' character at credit_card.py:198, an unhandled ValueError is raised.
Root Cause
The length check len(str(card_number)) at line 127 counts the minus sign as part of the length, so a negative integer with 13+ digits (plus sign) passes validation. The Luhn algorithm at credit_card.py:185-200 then crashes because int('-') raises ValueError.
Impact: An unhandled ValueError exception propagates from the tool function, causing a tool execution failure rather than a graceful error message to the user.
| if len(str(card_number)) < 13 or len(str(card_number)) > 19: | |
| self.session.generate_reply( | |
| instructions="The length of the card number is invalid, ask the user to repeat their card number." | |
| ) | |
| return None | |
| if card_number < 0 or len(str(card_number)) < 13 or len(str(card_number)) > 19: | |
| self.session.generate_reply( | |
| instructions="The length of the card number is invalid, ask the user to repeat their card number." | |
| ) | |
| return None | |
Was this helpful? React with 👍 or 👎 to provide feedback.
| if self._found_profile: | ||
| self._database.update_patient_record(patient_name, **profile) | ||
| self.session.userdata.profile = self._database.get_patient_by_name(patient_name) |
There was a problem hiding this comment.
🔴 NOT_GIVEN sentinel is truthy, causing if self._found_profile: to always take the update path for new patients
At line 491, the code uses if self._found_profile: to decide whether to update an existing patient record or add a new one. However, self._found_profile is initialized as NOT_GIVEN (a NotGiven instance) at healthcare_agent.py:400, and is only ever set to True (at line 418) when an existing record is found. It is never set to False.
For new patients (no existing record), _found_profile remains NOT_GIVEN. Since NotGiven is a plain class without a __bool__ override, Python treats all its instances as truthy by default. This means if self._found_profile: evaluates to True even for new patients.
Root Cause and Impact
When a new patient goes through profile_authenticator, the code incorrectly enters the update branch:
if self._found_profile: # NOT_GIVEN is truthy → True!
self._database.update_patient_record(patient_name, **profile) # returns False, patient doesn't exist
self.session.userdata.profile = self._database.get_patient_by_name(patient_name) # returns Noneupdate_patient_record silently fails (returns False, not checked), and get_patient_by_name returns None for the non-existent patient. This sets self.session.userdata.profile = None, causing subsequent operations (e.g., self.session.userdata.profile["name"]) to crash with a TypeError.
Notably, the rest of the code correctly uses is_given(self._found_profile) for the same variable (at lines 463 and 470), making this inconsistency a clear oversight.
Impact: New patients can never be registered. The profile is set to None, and all downstream operations that access profile fields will crash.
| if self._found_profile: | |
| self._database.update_patient_record(patient_name, **profile) | |
| self.session.userdata.profile = self._database.get_patient_by_name(patient_name) | |
| if is_given(self._found_profile) and self._found_profile: | |
| self._database.update_patient_record(patient_name, **profile) | |
| self.session.userdata.profile = self._database.get_patient_by_name(patient_name) |
Was this helpful? React with 👍 or 👎 to provide feedback.
|
Can we use a different TTS? Deepgram doesn’t sound very good |
|
nit: small prompt tweaks: I don't think the agent should be that verbose EDIT: Same for phone numbers |
Can we improve the prompt engineering there? |
|
The way the agent is saying available time slots is very unnatural IMO |
| await self.session.generate_reply( | ||
| instructions=f"The selected doctor has availabilities at {available_times}. Ask the user which time slot they prefer." | ||
| ) |
There was a problem hiding this comment.
We need a more reliable way to communicate critical information like the available times.
If the user interrupts or if the agent gets interrupted by background noise—the agent can lose context and may no longer be able to retrieve the available times.
There was a problem hiding this comment.
how about injecting the context with the available times before generating speech?
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.