Skip to content

Report a declined phone number as not-reserved, not a generic tool error - #6801

Open
u9g wants to merge 5 commits into
mainfrom
fix/phone-decline-not-reserved
Open

Report a declined phone number as not-reserved, not a generic tool error#6801
u9g wants to merge 5 commits into
mainfrom
fix/phone-decline-not-reserved

Conversation

@u9g

@u9g u9g commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add PhoneNumberCaptureDeclinedError so a caller who refuses to give a phone number is distinguishable from any other GetPhoneNumberTask failure — decline_phone_number_capture completed with a bare ToolError before, which carried no way to branch on the refusal
  • catch it in the hotel receptionist's restaurant flow: open_phone_dialog completes the task with RestaurantReservationNotCreatedError, and start_restaurant_booking reports the outcome as not-reserved instead of letting a generic tool error through that the model could still narrate as a booked table

Split out of #6567.

Testing

  • PYTHONPATH=livekit-agents .venv/bin/python -m pytest --unit (1944 passed, 5 skipped)
  • ruff format --check / ruff check clean; scripts/check_types.py reports only the pre-existing cv2/loguru/boto3 missing-stub errors in bithuman and aws

u9g added 2 commits August 11, 2026 16:09
…lure

GetPhoneNumberTask completed with a bare ToolError when the caller
refused, leaving callers no way to tell a refusal from any other
capture failure. Complete with a typed PhoneNumberCaptureDeclinedError
instead, so a caller can branch on the refusal.
…oked table

The restaurant flow let PhoneNumberCaptureDeclinedError surface as a
generic tool error, from which the model could still narrate a
successful reservation. Catch it, complete the task with
RestaurantReservationNotCreatedError, and have start_restaurant_booking
report the outcome as not-reserved.
@u9g
u9g requested a review from a team as a code owner August 11, 2026 20:13

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View 2 additional findings in Devin Review.

Open in Devin Review

Comment on lines +62 to +69
class PhoneNumberCaptureDeclinedError(ToolError):
def __init__(self, reason: str) -> None:
super().__init__(f"couldn't get the phone number: {reason}")
self._reason = reason

@property
def reason(self) -> str:
return self._reason

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 New public error type ships without documentation

The newly exported error class for a declined phone number is added to the public workflows API without any docstring (PhoneNumberCaptureDeclinedError at livekit-agents/livekit/agents/beta/workflows/phone_number.py:62-69), so the generated API docs describe it with nothing.
Impact: Users of the public API see an undocumented error type in the reference docs and can't tell when it is raised.

Repository rule requiring docs for new public classes/methods

CONTRIBUTING.md states: "If writing new methods/enums/classes, document them. This project uses pdoc3 for automatic API documentation generation, and every new addition has to be properly documented." The class is exported in livekit-agents/livekit/agents/beta/workflows/__init__.py:7-11 and listed in __all__, so it is part of the public surface; neither the class nor its reason property carries a docstring. (RestaurantReservationNotCreatedError in the example does carry one.)

Suggested change
class PhoneNumberCaptureDeclinedError(ToolError):
def __init__(self, reason: str) -> None:
super().__init__(f"couldn't get the phone number: {reason}")
self._reason = reason
@property
def reason(self) -> str:
return self._reason
class PhoneNumberCaptureDeclinedError(ToolError):
"""Raised when the user explicitly declines to provide a phone number.
`GetPhoneNumberTask` completes with this error (instead of a generic
`ToolError`) so callers can branch on an explicit refusal.
"""
def __init__(self, reason: str) -> None:
super().__init__(f"couldn't get the phone number: {reason}")
self._reason = reason
@property
def reason(self) -> str:
"""The short explanation of why the user declined."""
return self._reason
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

u9g added 3 commits August 11, 2026 16:27
…returns

Both paths return a string - the declined branch returns the not-reserved
wording, the success branch the phone-recorded line. The Optional was
widened without a caller that can return None.
…cline rule

The 'do not say the table is booked' sentence scored 1/20 on its own
against a caller who can't produce a number; the situation-to-tool
sentence carries the whole effect at 20/20. The not-reserved framing is
already carried by the tool return and start_restaurant_booking.
… phone decline

RestaurantReservationNotCreatedError and the start_restaurant_booking
catch existed to relabel an error whose message already states the
outcome. A ToolError carrying that message reaches the model the same
way with two fewer surfaces.
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.

1 participant