Skip to content

fix(hotel_receptionist): room and dispute tool-flow fixes - #6807

Open
u9g wants to merge 9 commits into
mainfrom
fix/hotel-room-tool-flows
Open

fix(hotel_receptionist): room and dispute tool-flow fixes#6807
u9g wants to merge 9 commits into
mainfrom
fix/hotel-room-tool-flows

Conversation

@u9g

@u9g u9g commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Nine independent room- and dispute-tool fixes from #6567, split one per commit. Everything here is scoped to examples/hotel_receptionist/tools_rooms.py plus the Userdata fields two of the guards need.

Dispute flow

  • dispute_charge took a bool for whether the caller had accepted the offer, but the agent has to call the tool to learn what the policy offer even is — so the first call filed the dispute and applied the waiver or credit before the caller had been told anything, and a declined second call could not undo it. The argument is now pending/accepted/declined: a pending call resolves the policy and hands back the offer to present, persisting nothing. Policies whose accepted and declined outcomes are identical are automatic and still resolve on the first call.
  • An escalation reply that opened with "I've escalated this to the manager" skipped the stance the caller is owed from the record — a card-guaranteed no-show charge that stands, say — and read as a brush-off. The policy explanation comes first, and a callback number gets recorded rather than an unasked-for live transfer offered.
  • lookup_invoice rendered a line as Room (2 nights) 560 dollars, so the model copied the merged label-plus-amount string into dispute_charge, the exact-label match failed, and the error told it to read the lines back — producing the same merged string again, looping until the caller gave up. Labels are quoted at the source, and the no-match error lists the exact labels and says what to do when the charge genuinely isn't on the invoice.
  • "I thought I cancelled" reads as a cancellation, so the model picked cancellation_fee; that category is only for a fee from an actual recorded cancellation. The no_show line is now drawn in the arg docstring, where the choice is made.

Room tools

  • A caller booking two rooms can get the model to call start_room_booking twice in one turn. Inline AgentTasks cannot run in parallel — the second activation supplants the first, whose function call then never resolves, and the session wedges. A second call now raises instead of running.
  • When card capture ends with no usable card, the bare ToolError reached the model as a dead end and it left the caller thinking the reservation was at risk. Nothing was lost: the existing card stays on file and a working one isn't needed until check-in. The callback offer becomes the turn's question.
  • The model sometimes re-invoked start_booking_modification with no new caller input instead of relaying the result, re-opening the flow on the booking it just updated while the confirmation and any refund never got spoken. Same guard the cancel path already has.
  • The progressive-disclosure rule bled into lookup_booking, so a caller confirming their reservation heard only the field they named and never got the chance to catch a wrong room type or smoking status before arrival. That rule is now scoped to option lists.
  • The walk plan told the agent to give it "in short pieces", which let it spread the four parts across turns — an angry guest hangs up before the later ones land, so they never hear that the taxi is covered or that their own room is theirs again. All four parts go out in one reply, and the walk is stated as the resolution so the agent stops offering a manager on top of it.

Not carried over from #6567

Testing

  • ruff format --check and ruff check clean across examples/hotel_receptionist/.
  • Smoke-checked the three-state resolution across all six DISPUTE_POLICIES actions: the two automatic actions (sub-threshold minibar, duplicate-charge ticket) still resolve on the first call; every other action returns an unfiled offer while pending, then the matching outcome on accepted/declined.
  • mypy on the two changed files goes 13 -> 15 errors, all RoomToolsMixin has no attribute "session" — the pre-existing mixin pattern the cancel guard already trips, and examples/ is outside scripts/check_types.py's scope.

u9g added 9 commits August 11, 2026 16:52
…sition

An escalation reply that opens with "I have escalated this to the manager"
skips the stance the caller is owed from the record - a card-guaranteed
no-show charge that stands, say - and reads as a brush-off. Put the policy
explanation first, and record a callback number so the manager can reach
them instead of offering an unasked-for live transfer.
Telling the agent to give the walk "in short pieces" let it spread the four
parts across turns, and an angry guest hangs up before the later ones land -
so the guest never hears that the taxi is covered or that their own room is
theirs again. Require all four parts in one reply, and say the walk itself is
the resolution so the agent stops offering a manager on top of it.
A caller booking two rooms can get the model to call start_room_booking twice
in one turn. Inline AgentTasks cannot run in parallel - the second activation
supplants the first, whose function call then never resolves, and the session
wedges. Track the in-flight flow and raise a ToolError on re-entry so the
model finishes one room before starting the next.
When card capture ends with no usable card, GetCardTask raises and the bare
ToolError reaches the model as a dead end - it apologizes and leaves the
caller thinking the reservation is at risk. Nothing was lost: the existing
card stays on file and a working one is not needed until check-in. Say that,
and make the callback offer the turn's question.
The model sometimes re-invokes start_booking_modification with no new caller
input instead of relaying the result, re-opening the flow on the booking it
just updated and walking the caller through the same changes again while the
confirmation and any refund never get spoken. Mirror the cancel guard: with no
caller turn since the last modification, re-surface that outcome.
…field

The progressive-disclosure rule ("surface results progressively, don't recite
the whole list") bleeds into lookup_booking, so a caller who asks to confirm
their reservation hears only the field they named and never gets the chance to
catch a wrong room type or smoking status before arrival. Scope that rule to
option lists explicitly.
…unambiguous

lookup_invoice rendered a line as "Room (2 nights) 560 dollars", so the model
copied the merged label-plus-amount string into dispute_charge, the exact-label
match failed, and the error told it to read the lines back - which produced the
same merged string again, looping until the caller gave up. Quote the labels at
the source, and have the no-match error list the exact labels and say what to
do when the charge is genuinely not on the invoice.
dispute_charge took a bool for whether the caller had accepted the offer, but
the agent has to call the tool to learn what the policy offer even is - so the
first call filed the dispute and applied the waiver or credit before the caller
had been told anything, and a "declined" second call could not undo it.

Split the argument into pending/accepted/declined. A pending call resolves the
policy and hands back the offer to present, persisting nothing; only an explicit
accepted or declined call files. Policies whose two outcomes are identical are
automatic and still resolve on the first call.
"I thought I cancelled" reads as a cancellation to the model, so it picked
cancellation_fee and the caller got the wrong policy - that category is only for
a fee from an actual recorded cancellation. Draw the line at the call site; the
system prompt already draws it, but the choice is made here.
@u9g
u9g requested a review from a team as a code owner August 11, 2026 20:59

@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: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

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