Packages

fnord

0.8.92

AI code archaeology

Current section

Files

Jump to
fnord lib memory presentation.ex
Raw

lib/memory/presentation.ex

defmodule Memory.Presentation do
@moduledoc """
Helpers for presenting `%Memory{}` metadata to humans.
This module is intentionally small and pure (no I/O) so it can be reused by:
- `AI.Tools.Memory` (tool output)
- `Cmd.Memory` (CLI output)
- `AI.Agent.Coordinator` (auto-injected recall section)
Timestamps are stored on `%Memory{}` as ISO8601 strings (`DateTime.to_iso8601/1`).
"""
@type iso8601 :: binary()
@doc """
Parse an ISO8601 timestamp string into a `DateTime`.
Returns `{:ok, dt}` or `:error`.
"""
@spec parse_ts(nil | iso8601()) :: {:ok, DateTime.t()} | :error
def parse_ts(nil), do: :error
def parse_ts(ts) when is_binary(ts) do
case DateTime.from_iso8601(ts) do
{:ok, dt, _offset} -> {:ok, dt}
_ -> :error
end
end
@doc """
Returns the age in whole days since `dt` relative to `now`.
If `dt` is in the future, returns `0`.
"""
@spec age_days(DateTime.t(), DateTime.t()) :: non_neg_integer()
def age_days(%DateTime{} = dt, %DateTime{} = now) do
seconds = DateTime.diff(now, dt, :second)
days = div(max(seconds, 0), 86_400)
days
end
@doc """
Returns an "Age" line for a memory.
Examples:
- `"Age: unknown (missing timestamps)"`
- `"Age: 312 days (updated 12 days ago)"`
Notes:
- Uses `updated_at` if present; otherwise uses `inserted_at`.
- Uses `now` for deterministic testing.
"""
@spec age_line(Memory.t(), DateTime.t()) :: binary()
def age_line(%Memory{} = mem, %DateTime{} = now) do
inserted_days = ts_days(mem.inserted_at, now)
updated_days = ts_days(mem.updated_at, now)
cond do
inserted_days == :unknown and updated_days == :unknown ->
"Age: unknown (missing timestamps)"
inserted_days != :unknown and updated_days == :unknown ->
"Age: #{inserted_days} days"
inserted_days == :unknown and updated_days != :unknown ->
"Age: #{updated_days} days (updated #{updated_days} days ago)"
true ->
"Age: #{inserted_days} days (updated #{updated_days} days ago)"
end
end
@doc """
Returns an optional warning line based on how long ago the memory was updated.
- If `updated_at` is missing/unparseable, returns `nil`.
- If `updated_days >= strong_days`, returns a strong warning.
- Else if `updated_days >= mild_days`, returns a mild warning.
- Else returns `nil`.
This is intended as a gentle prompt to check for cobwebs.
"""
@spec warning_line(Memory.t(), DateTime.t(), keyword()) :: binary() | nil
def warning_line(%Memory{} = mem, %DateTime{} = now, opts \\ []) do
mild_days = Keyword.get(opts, :mild_days, 180)
strong_days = Keyword.get(opts, :strong_days, 365)
case ts_days(mem.updated_at, now) do
:unknown ->
nil
days when is_integer(days) and days >= strong_days ->
"Warning: last updated #{days} days ago. Check for cobwebs and verify/update/forget if stale."
days when is_integer(days) and days >= mild_days ->
"Note: last updated #{days} days ago. Consider checking for cobwebs."
_ ->
nil
end
end
defp ts_days(ts, now) do
case parse_ts(ts) do
{:ok, dt} -> age_days(dt, now)
:error -> :unknown
end
end
end