Report a declined phone number as not-reserved, not a generic tool error - #6801
Report a declined phone number as not-reserved, not a generic tool error#6801u9g wants to merge 5 commits into
Conversation
…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.
| 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 |
There was a problem hiding this comment.
🟡 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.)
| 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 |
Was this helpful? React with 👍 or 👎 to provide feedback.
…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.
Summary
PhoneNumberCaptureDeclinedErrorso a caller who refuses to give a phone number is distinguishable from any otherGetPhoneNumberTaskfailure —decline_phone_number_capturecompleted with a bareToolErrorbefore, which carried no way to branch on the refusalopen_phone_dialogcompletes the task withRestaurantReservationNotCreatedError, andstart_restaurant_bookingreports the outcome as not-reserved instead of letting a generic tool error through that the model could still narrate as a booked tableSplit out of #6567.
Testing
PYTHONPATH=livekit-agents .venv/bin/python -m pytest --unit(1944 passed, 5 skipped)ruff format --check/ruff checkclean;scripts/check_types.pyreports only the pre-existing cv2/loguru/boto3 missing-stub errors in bithuman and aws