Repository navigation
fix(memory): keep titles like Dr. in the sentence a memory claim is checked on #555
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| **Chat now catches a false "I saved it" about a name with a title, like "Dr. Mehta".** Before, the check cut the sentence at "Dr.", so a different saved dentist counted as the same memory and the reply got no note. Now it reads the whole sentence, and when nothing was saved it adds the short note. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,8 @@ | |
| import re | ||
| from typing import Any | ||
|
|
||
| from sentient.memory import origins | ||
|
|
||
| log = logging.getLogger(__name__) | ||
|
|
||
| VERIFY_TIMEOUT_S = 15.0 | ||
|
|
@@ -532,14 +534,38 @@ def summary(checks: list[dict]) -> str: | |
| ) | ||
|
|
||
|
|
||
| # A period that does not end the sentence: after a title and its initials ("Dr. M. Mehta"), after "a.m." or "p.m." | ||
| # before a lowercase word ("7 a.m. on weekdays") and between "No." and a number ("apartment No. 12"). "St." and "No." | ||
| # before a capital can end one ("Main St. I've saved that...", "No. I've saved that..."), so they never glue the next | ||
| # sentence on. | ||
| _CLOCK = re.compile(r"\b[ap]\.m\.$", re.I) | ||
| _NUMBER_SIGN = re.compile(r"\w\s+No\.$") | ||
| _TITLE = re.compile(r"\b(?:Dr|Mr|Mrs|Ms|Mx|Prof)\.(?:\s+[A-Z]\.)*$|\b(?:e\.g|i\.e)\.$") | ||
| # A name after a title is no keyword: "Dr. May" is not a hedge and "Dr. Page" is not a page. Only the first word keeps | ||
| # the place check, so "Dr. May Notes" is still about notes. | ||
| _TITLE_NAME = re.compile(r"\b(?:Dr|Mr|Mrs|Ms|Mx|Prof)\.(?:\s+[A-Z]\w*\.?)+") | ||
| _TITLE_FIRST = re.compile(r"\b(?:Dr|Mr|Mrs|Ms|Mx|Prof)\.(?:\s+[A-Z]\.)*\s+[A-Z]\w*") | ||
|
|
||
|
|
||
| def _continues(previous: str, following: str) -> bool: | ||
| return bool( | ||
| _TITLE.search(previous) | ||
| or (_CLOCK.search(previous) and following[:1].islower()) | ||
| or (_NUMBER_SIGN.search(previous) and following[:1].isdigit()) | ||
| ) | ||
|
|
||
|
|
||
| def memory_claim(text: str) -> str: | ||
| """The first sentence of ``text`` in which Sentient says it kept something in its memory, or "". Folds curly | ||
| apostrophes, and skips questions, offers, negations, earlier saves and saves to anything other than memory.""" | ||
| for sentence in re.split(r"(?<=[.!?])\s+|\n+", _straight_apostrophes(text)): | ||
| s = sentence.strip() | ||
| if not s or s.endswith("?") or _MEMORY_HEDGE.search(s) or _MEMORY_EARLIER.search(s): | ||
| continue | ||
| if _MEMORY_OTHER_PLACE.search(s): | ||
| for s in origins.sentences(_straight_apostrophes(text), _continues): | ||
| bare = _TITLE_NAME.sub("", s) | ||
| if ( | ||
| s.endswith("?") | ||
| or _MEMORY_HEDGE.search(bare) | ||
| or _MEMORY_EARLIER.search(bare) | ||
| or _MEMORY_OTHER_PLACE.search(_TITLE_FIRST.sub("", s)) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| ): | ||
|
Comment on lines
+561
to
+568
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| continue | ||
| cleaned = re.sub(r"\bno (?:problem|worries)\b", "", s, flags=re.I) # "No problem, I've saved that..." | ||
| for pattern in _MEMORY_CLAIMS: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ | |
| import json | ||
| import os | ||
| import re | ||
| from collections.abc import Callable | ||
| from typing import Any | ||
|
|
||
| CONVERSATION = "conversation" | ||
|
|
@@ -167,14 +168,25 @@ def sources_for(fact: str, first: str, read: list[Read], *, held: bool, user_nam | |
| _ABBREVIATION = re.compile(r"(?:\b(?:Dr|Mr|Mrs|Ms|Prof|St|Jr|Sr|vs|etc|No|e\.g|i\.e)|\b[A-Z])\.$") | ||
|
|
||
|
|
||
| def quote(text: str, fact: str) -> str: | ||
| """The sentence of ``text`` that says most of ``fact`` (its start when none does), short.""" | ||
| def _after_abbreviation(previous: str, following: str) -> bool: | ||
| return bool(_ABBREVIATION.search(previous)) | ||
|
|
||
|
|
||
| def sentences(text: str, continues: Callable[[str, str], bool] = _after_abbreviation) -> list[str]: | ||
| """``text`` split into sentences, without cutting after an abbreviation: "Dr. Mehta" stays in one. ``continues`` | ||
| says whether the piece ``following`` a piece ``previous`` belongs to it.""" | ||
| parts: list[str] = [] | ||
| for p in (p.strip() for p in _SENTENCE.split(str(text or ""))): | ||
| if p and parts and _ABBREVIATION.search(parts[-1]): # "Dr. Mehta" is one sentence | ||
| if p and parts and continues(parts[-1], p): | ||
| parts[-1] = f"{parts[-1]} {p}" | ||
| elif p: | ||
| parts.append(p) | ||
| return parts | ||
|
Comment on lines
+171
to
+184
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
|
|
||
| def quote(text: str, fact: str) -> str: | ||
| """The sentence of ``text`` that says most of ``fact`` (its start when none does), short.""" | ||
| parts = sentences(text) | ||
| if not parts: | ||
| return "" | ||
| want = _words(fact) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script executed:
Repository: existence-master/Sentient
Length of output: 42178
Script executed:
Repository: existence-master/Sentient
Length of output: 9678
Script executed:
rg -n -F -- '_memory_correction(' sentient/agent/loop.pyRepository: existence-master/Sentient
Length of output: 374
Script executed:
Repository: existence-master/Sentient
Length of output: 6338
Keep separate claim sentences separate after abbreviations.
When a reply says “You could ask Mr. Smith, e.g. I've saved that you like tea,” the splitter joins both sentences. The earlier
couldthen suppresses the save claim, so an unsupported claim receives no correction. Preserve sentence boundaries here while retaining title-name handling.