fix(control-plane): coalesce dates for timelines - #3516
Conversation
The timeline only plotted rows with occurred_start, a content date the LLM sets on a fraction of memories, so most entity timelines rendered "No Timeline Data". Coalesce the unit's effective time like the backend does (COALESCE(occurred_start, mentioned_at, occurred_end)), extended with created_at and the legacy event_date, so every row with any usable date appears; reword the empty-state message across locales. Extract date partition and granularity grouping into pure helpers with unit tests. The graph endpoint's space-separated "YYYY-MM-DD HH:MM" date is parsed explicitly as UTC because it falls outside ECMA-262's date grammar.
|
Thanks for digging into this — the diagnosis is right ( Collapsing five fields into one hidden chain conflates two genuinely different questions: when did this happen ( What I'd rather have is an explicit date-basis selector on the timeline — occurred vs mentioned (and ingest, if useful) — so the user picks the axis and knows what they're looking at. Both views are legitimate and answer different questions; the user should choose, not the code. We already have this pattern: Concretely:
Most of this PR survives that change: extracting the partition/grouping logic into pure tested helpers in Two smaller things, in case some form of this lands anyway:
And I agree with your follow-up #2 in the description: computing this server-side is the better long-term home. If |
The entity timeline in the control plane plotted each memory only by
occurred_start, a content date the LLM sets on a small fraction of memories, so most entities showed "No Timeline Data" despite every memory carrying mention and ingest timestamps.This PR makes it so that timelines plot each row by an effective date computed in the UI as
occurred_start→mentioned_at→occurred_end→created_at→event_date, replicating the backend'sCOALESCEorder for a unit's effective time and extending it with system fields that are always populated. Only rows with no usable date at all are excluded. Filtering, sorting, grouping, and labels all use this date.Additionally:
YYYY-MM-DD HH:MMdate is parsed explicitly as UTC since it falls outside the ECMAScript date grammar.Note: The backend actually shows two different date policies (this PR follows the 2nd one):
COALESCE(occurred_start, mentioned_at, occurred_end), used by temporal retrieval (retrieval.py::_coalesce_date) and recency reranking (reranking.py:156, but inlined instead of calling the helper).created_at/event_date, used byget_memories_timeseriesand now by the timeline view.This PR adds more behaviour by handling the graph endpoint's non-ISO date format, which no backend helper currently does, but it does mean the display chain now exists in three places.
Two follow-ups worth considering:
_coalesce_dateto a shared module and havereranking.pyuse it, eliminating the inline duplicate.effective_dateserver-side on/listand/graphendpoints