feat: time awareness — Lyra perceives 'now' and how long it's been
She had no clock: current date/time and the gap since Brian last spoke were
invisible between turns, and reflection was timeless. Now:
- lyra/clock.py: wall-clock stamp + coarse human gaps ("3 days")
- chat: inject a 'now' note (date/time + gap since last turn) after her
self-state — when she is, before the world
- reflect(): feed current time + silence gap into reflection, neutrally —
prompt invites her to weigh elapsed time "to whatever degree it genuinely
affects you" (no prescribed feeling; whether silence means anything is left
to emerge)
- memory.last_exchange_at(): timestamp of the most recent exchange
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
"""Time-awareness: gap humanizing + the 'now' note injected into chat context."""
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
from datetime import timedelta
|
||||
|
||||
import pytest
|
||||
|
||||
from lyra import clock
|
||||
|
||||
|
||||
def test_humanize_gap_scales():
|
||||
ref = clock.now()
|
||||
assert clock.humanize_gap(None) is None
|
||||
assert clock.humanize_gap((ref - timedelta(seconds=10)).isoformat(), ref) == "moments"
|
||||
assert clock.humanize_gap((ref - timedelta(minutes=5)).isoformat(), ref) == "5 minutes"
|
||||
assert clock.humanize_gap((ref - timedelta(hours=3)).isoformat(), ref) == "3 hours"
|
||||
assert clock.humanize_gap((ref - timedelta(days=3)).isoformat(), ref) == "3 days"
|
||||
assert clock.humanize_gap((ref - timedelta(days=21)).isoformat(), ref) == "3 weeks"
|
||||
assert clock.humanize_gap((ref - timedelta(days=90)).isoformat(), ref) == "3 months"
|
||||
|
||||
|
||||
def test_humanize_gap_handles_future_and_naive():
|
||||
ref = clock.now()
|
||||
# future timestamp clamps to "moments", never negative
|
||||
assert clock.humanize_gap((ref + timedelta(hours=1)).isoformat(), ref) == "moments"
|
||||
# naive ISO (no tz) is treated as UTC, doesn't crash
|
||||
assert clock.humanize_gap("2026-06-01T00:00:00") is not None
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def lyra(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("LYRA_DB_PATH", str(tmp_path / "test.db"))
|
||||
from lyra import llm
|
||||
monkeypatch.setattr(llm, "embed", lambda texts: [[0.1, 0.2, 0.3] for _ in texts])
|
||||
import lyra.memory as memory
|
||||
importlib.reload(memory)
|
||||
return memory
|
||||
|
||||
|
||||
def test_now_note_first_contact(lyra):
|
||||
from lyra import chat
|
||||
note = chat._now_note()["content"]
|
||||
assert "current date and time is" in note
|
||||
assert "first thing Brian has ever said" in note
|
||||
|
||||
|
||||
def test_now_note_reports_gap(lyra):
|
||||
memory = lyra
|
||||
memory.remember("s1", "user", "hey")
|
||||
from lyra import chat
|
||||
note = chat._now_note()["content"]
|
||||
assert "since Brian last spoke with you" in note
|
||||
Reference in New Issue
Block a user