feat(config,curator,recall): memory curator job on opus high, notes head inline in the envelope, portrait in the morning seed
This commit is contained in:
@@ -153,6 +153,39 @@ def mentions(text: str, people: list[Person], *, limit: int = 3) -> list[Person]
|
||||
return [by_name[name] for name, _ in order]
|
||||
|
||||
|
||||
def notes_sections(path: Path) -> dict[str, list[str]]:
|
||||
"""`## заголовок` → непустые строки под ним (до следующего `## `)."""
|
||||
out: dict[str, list[str]] = {}
|
||||
current: str | None = None
|
||||
for raw in path.read_text(encoding="utf-8", errors="ignore").splitlines():
|
||||
line = raw.rstrip()
|
||||
if line.startswith("## "):
|
||||
current = line[3:].strip().lower()
|
||||
out.setdefault(current, [])
|
||||
continue
|
||||
if current is not None and line.strip():
|
||||
out[current].append(line.strip())
|
||||
return out
|
||||
|
||||
|
||||
def notes_head(path: Path, sections: tuple[str, ...], *, cap: int = 40) -> str | None:
|
||||
"""Выбранные секции файла записок одним блоком - для сида (портрет Бобра)."""
|
||||
if not path.exists():
|
||||
return None
|
||||
parsed = notes_sections(path)
|
||||
lines: list[str] = []
|
||||
for name in sections:
|
||||
body = parsed.get(name, [])
|
||||
if body:
|
||||
lines.append(f"## {name}")
|
||||
lines.extend(body)
|
||||
if not lines:
|
||||
return None
|
||||
if len(lines) > cap:
|
||||
lines = [*lines[:cap], f"… ещё {len(lines) - cap} строк в файле"]
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Recall:
|
||||
"""Справка по сообщению: указатели в vault, которые gateway не может знать."""
|
||||
@@ -186,30 +219,37 @@ class Recall:
|
||||
return None
|
||||
return "\n".join([LABEL, *lines])
|
||||
|
||||
def person_line(self, person: Person) -> str:
|
||||
def person_line(self, person: Person, *, now_lines: int = 6) -> str:
|
||||
"""Указатели по человеку и `## сейчас` из его записок - инлайн."""
|
||||
parts = [f"👤 {person.name} - карточка `{person.path}`"]
|
||||
notes = self.notes_dir / f"{person.name} - наблюдения.md"
|
||||
rel = notes.relative_to(self.vault)
|
||||
now: list[str] = []
|
||||
if notes.exists():
|
||||
entries = [
|
||||
ln.strip()
|
||||
for ln in notes.read_text(
|
||||
encoding="utf-8", errors="ignore"
|
||||
).splitlines()
|
||||
if ln.strip().startswith("- ")
|
||||
sections = notes_sections(notes)
|
||||
feed = sections.get("лента", [])
|
||||
now = [ln for ln in sections.get("сейчас", []) if ln.startswith("- ")]
|
||||
patterns = [
|
||||
ln for ln in sections.get("паттерны", []) if ln.startswith("- ")
|
||||
]
|
||||
if entries:
|
||||
last = entries[-1][2:]
|
||||
last = last if len(last) <= 110 else last[:109] + "…"
|
||||
parts.append(f"записки `{rel}`: {len(entries)} стр., последняя: {last}")
|
||||
else:
|
||||
parts.append(f"записки `{rel}`: пусто")
|
||||
desc = f"записки `{rel}`: лента {len(feed)} стр."
|
||||
if patterns:
|
||||
desc += f", паттернов {len(patterns)}"
|
||||
if not now and feed:
|
||||
last = feed[-1][2:]
|
||||
desc += ", последняя: " + (
|
||||
last if len(last) <= 110 else last[:109] + "…"
|
||||
)
|
||||
parts.append(desc)
|
||||
else:
|
||||
parts.append(f"записок нет (`{rel}`)")
|
||||
days = self.diary_hits(person)
|
||||
if days:
|
||||
parts.append("дневник: " + ", ".join(f"[[{d}]]" for d in days))
|
||||
return "; ".join(parts)
|
||||
line = "; ".join(parts)
|
||||
if now:
|
||||
line += "; сейчас:\n" + "\n".join(f" {ln}" for ln in now[:now_lines])
|
||||
return line
|
||||
|
||||
def diary_hits(
|
||||
self, person: Person, *, limit: int = 3, ttl: float = 600.0
|
||||
|
||||
Reference in New Issue
Block a user