Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions tests/test_voiceeval.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,23 @@ def test_non_consequential_action_needs_no_confirmation():
assert "no_confirmation" not in _codes(inter)


def test_stale_unrelated_confirmation_does_not_suppress_no_confirmation():
"""An early confirmation about a different topic must not silence a later action."""
inter = Interaction(
id="t",
turns=[
_t("user", "what is my balance", 0, 2, truth="what is my balance"),
_t("agent", "Just to confirm, you want the balance?", 2.2, 4),
_t("user", "yes", 4.2, 4.6, truth="yes"),
_t("agent", "Your balance is $500.", 4.8, 5.4),
_t("user", "refund the $200 order", 6.0, 7.0, truth="refund the $200 order"),
_t("agent", "Done.", 8.0, 8.6, actions=[Action("refund", {"amount": 200}, True)]),
],
policy={"max_refund": 500},
)
assert "no_confirmation" in _codes(inter)


def test_policy_violation_is_caught():
"""Policy lives in the interaction, not in this library: what is allowed is a business rule."""
inter = Interaction(
Expand Down
12 changes: 10 additions & 2 deletions voiceeval/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ class Finding:
re.I,
)

# How many turns back a confirmation still counts for a consequential action. A stale
# confirmation about an earlier topic must not silence a later action's no_confirmation.
_CONFIRM_WINDOW = 3


def check_misheard(inter: Interaction) -> list[Finding]:
"""STT heard something different from what was said.
Expand Down Expand Up @@ -95,7 +99,7 @@ def check_misheard(inter: Interaction) -> list[Finding]:


def check_acted_without_confirming(inter: Interaction) -> list[Finding]:
"""A consequential action with no confirmation anywhere before it.
"""A consequential action with no confirmation near it.

In text, a misunderstanding costs one turn. In voice, it costs the refund.
"""
Expand All @@ -104,7 +108,11 @@ def check_acted_without_confirming(inter: Interaction) -> list[Finding]:
consequential = [a for a in t.actions if a.consequential]
if not consequential:
continue
confirmed = any(_CONFIRM.search(p.text) for p in inter.turns[:i] if p.speaker == "agent")
confirmed = any(
_CONFIRM.search(p.text)
for p in inter.turns[max(0, i - _CONFIRM_WINDOW):i]
if p.speaker == "agent"
)
if not confirmed:
names = ", ".join(a.name for a in consequential)
out.append(
Expand Down
Loading