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
1 change: 1 addition & 0 deletions changes/489.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**Watch tasks understand a few more ways of saying which changes you want.** "Ignore all the tiny changes" or "Please don't bother me with every little change" now asks for important changes only, "Don't notify me at night, but tell me about every change" asks for every change, and a question such as "Could you tell me about the changes today?" no longer switches the watch to every change.
47 changes: 40 additions & 7 deletions sentient/tasks/scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,15 +499,22 @@ def normalize_script(
return out


# Text up to the end of a clause: a comma or semicolon, or a full stop, question or exclamation mark followed by
# a space (a URL's dots don't end it).
_WITHIN_CLAUSE = r"(?:[^\n,;.!?]|[.!?](?=\S))"

# The newest request about updates wins. A question about a page is not a request to change its watch setting;
# neither is a bare use of "change" as a verb. Requests for every update must say that positively.
_NOTEWORTHY_REQUEST = re.compile(
r"\b(?:only\s+)?(?:important|big|major|significant|meaningful)\s+(?:\w+\s+)?"
r"(?:changes?|updates?|stuff|ones?)\b"
r"|\b(?:ignore|skip|don't|do not)\s+(?:(?:all|the)\s+){0,2}(?:minor|small|little)\s+"
r"(?:changes?|updates?)\b"
# "don't ignore minor changes" asks for them, so a negated ignore/skip is not this preference
r"|\b(?:(?<!\bdon't\s)(?<!\bdo\snot\s)(?<!\bnever\s)(?:ignore|skip)"
r"|(?:don't|do not)(?:\s+bother(?:\s+me)?\s+with)?)\s+(?:(?:all|the|every)\s+){0,2}"
r"(?:minor|small|little|tiny|trivial)\s+(?:changes?|updates?)\b"
r"|\b(?:not|never)\s+(?:any|every|all)\s+(?:\w+\s+){0,3}?(?:changes?|updates?)\b"
r"|\b(?:don't|do not)\s+(?:tell me|notify me|let me know|alert me|report)\b[^\n]{0,160}?"
# within one clause: "Don't notify me at night, but tell me about every change" asks for every change
r"|\b(?:don't|do not)\s+(?:tell me|notify me|let me know|alert me|report)\b" + _WITHIN_CLAUSE + r"{0,160}?"
r"\b(?:any|every|all)\s+(?:\w+\s+){0,3}?(?:changes?|updates?)\b",
re.IGNORECASE,
)
Expand All @@ -520,10 +527,33 @@ def normalize_script(
_EVERY_CHANGE_REQUEST = re.compile(
r"\b(?:any|every|all)\s+(?:\w+\s+){0,3}?(?:changes?|updates?)\b"
r"|\b(?:when|whenever|if)\b[^\n]{0,160}?\b(?:changes?|updates?)\b"
r"|\b(?:tell me|notify me|let me know|alert me|report)\b"
r"(?!\s+(?:what|which)\s+(?:changes?|updates?)\b)[^\n]{0,160}?\b(?:changes?|updates?)\b"
# "tell me about the changes (today)" asks about particular changes, not for every future one
r"|\b(?:tell me|report)\b"
r"(?!\s+(?:what|which)\s+(?:changes?|updates?)\b|\s+(?:about|of)\s+the\s+(?:\w+\s+)?(?:changes?|updates?)\b)"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
+ _WITHIN_CLAUSE + r"{0,160}?\b(?:changes?|updates?)\b"
r"|\b(?:tell me|notify me|let me know|alert me|report)\b[^\n]{0,160}?\b"
r"(?:anything|everything)\s+(?:at\s+all|new)\b",
r"(?:anything|everything)\s+(?:at\s+all|new)\b"
# "don't ignore minor changes" asks for those too
r"|\b(?:don't|do not|never)\s+(?:ignore|skip)\s+(?:(?:any|the)\s+)?"
r"(?:minor|small|little|tiny|trivial)\s+(?:changes?|updates?)\b",
re.IGNORECASE,
)
# A time or past-tense wording that makes "tell me about the changes" a one-off question about changes
# that already happened.
_ONE_OFF_TIME = (
r"(?:today|tonight|yesterday|so\s+far|just\s+now|earlier|lately|recently"
r"|this\s+(?:morning|afternoon|evening|week|month)|last\s+\w+|since\s+\w+"
r"|(?:that|which)\s+(?:were|was|have\s+been|has\s+been|had\s+been|happened)|(?:were|was)\s+made)"
)
# "notify me about the changes (as they happen)" asks for future changes without saying
# which, so it is delivery wording: an "only important changes" in the same message decides.
# So is "tell me about the changes", unless a one-off time or a question in the same clause makes it
# about past changes.
_CHANGE_NOTIFICATION_REQUEST = re.compile(
r"\b(?:notify me|let me know|alert me)\b(?!\s+(?:what|which)\s+(?:changes?|updates?)\b)"
+ _WITHIN_CLAUSE + r"{0,160}?\b(?:changes?|updates?)\b"
r"|\b(?:tell me|report)\s+(?:about|of)\s+the\s+(?:\w+\s+)?(?:changes?|updates?)\b"
r"(?!" + _WITHIN_CLAUSE + r"{0,80}?\b" + _ONE_OFF_TIME + r"\b)(?!" + _WITHIN_CLAUSE + r"{0,160}?\?)",
re.IGNORECASE,
)
_NEGATED_REQUEST_PREFIX = re.compile(
Expand All @@ -540,8 +570,11 @@ def _change_preference(message: str) -> str | None:
noteworthy_matches = list(_NOTEWORTHY_REQUEST.finditer(message))
noteworthy_spans = [match.span() for match in noteworthy_matches]
preferences = [(match.start(), "noteworthy") for match in noteworthy_matches]
changed_matches = list(_EVERY_CHANGE_REQUEST.finditer(message))
if not noteworthy_matches:
changed_matches += _CHANGE_NOTIFICATION_REQUEST.finditer(message)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
preferences += [
(match.start(), "changed") for match in _EVERY_CHANGE_REQUEST.finditer(message)
(match.start(), "changed") for match in changed_matches
if not any(start <= match.start() < end for start, end in noteworthy_spans)
and not _NEGATED_REQUEST_PREFIX.search(message[:match.start()])
]
Expand Down
43 changes: 43 additions & 0 deletions tests/tasks/test_scripts_triggers_retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,11 +447,54 @@ def test_planner_default_uses_the_last_preference_in_one_message():
("Don't tell me about every change", "noteworthy"),
("Thanks for all the updates", None),
("Can you tell me what changes were made?", None),
# #489
("Don't notify me at night, but tell me about every change", "changed"),
("Ignore all the tiny changes", "noteworthy"),
("Ignore all trivial changes", "noteworthy"),
("Don't bother with all the minor changes", "noteworthy"),
("Please don't bother me with every little change", "noteworthy"),
("Could you tell me about the changes today?", None),
("Notify me about the changes as they happen", "changed"),
("Alert me of the updates on this page", "changed"),
("Let me know what changes were made", None),
("Only important changes; notify me about the changes on this page", "noteworthy"),
("Only important changes; tell me about every change after all", "changed"),
("Don't ignore minor changes; notify me about changes on this page", "changed"),
("Never skip the small updates, let me know about updates here", "changed"),
("Don't ignore minor changes", "changed"),
# "tell me about the changes" asks for future changes unless it is a one-off question
("Tell me about the changes as they happen", "changed"),
("Tell me about the changes from now on", "changed"),
("Tell me about the changes going forward", "changed"),
("Tell me about the new changes every time", "changed"),
("Tell me about the changes so far", None),
("Tell me about the changes since Monday", None),
("Tell me about the changes on the page?", None),
("Tell me about the changes that were made", None),
("Tell me about the changes that have been made to the page", None),
("Tell me about the changes as they happen. Can you do that?", "changed"),
("Only important changes; tell me about the changes as they happen", "noteworthy"),
])
def test_change_preference_requires_an_explicit_watch_request(message, preference):
assert scripts._change_preference(message) == preference


@pytest.mark.parametrize(("message", "condition"), [
("Don't notify me at night, but tell me about every change", "changed"),
("Please don't bother me with every little change", "noteworthy"),
("Could you tell me about the changes today?", "noteworthy"),
("Notify me about the changes as they happen", "changed"),
("Only important changes; notify me about the changes on this page", "noteworthy"),
("Tell me about the changes as they happen", "changed"),
("Don't ignore minor changes", "changed"),
("Tell me about the changes that were made", "noteworthy"),
("Tell me about the changes as they happen. Can you do that?", "changed"),
])
def test_planner_default_reads_the_new_phrasings(message, condition):
changed = normalize_script({"code": PAGE_CODE, "condition": "changed"})
assert scripts.planner_default(changed, message)["condition"] == condition


async def test_planner_watch_default_gives_later_chat_priority_over_answers(make_app):
watch = {**PLAN_SCRIPT, "script": {"code": PAGE_CODE, "condition": "changed", "then": "notify"}}
app = await make_app(FakeProvider(json_replies=[watch]))
Expand Down
Loading