Current section
Files
Jump to
Current section
Files
src/memory/adk_memory_service.erl
%% @doc Versioned contract for cross-session long-term memory.
%%
%% Version 2 makes the application/user authority explicit. Implementations
%% must never infer a principal from caller supplied metadata. The old
%% add/search/delete/session callbacks remain as compatibility entry points;
%% new runtime code should feature-detect `contract_version => 2' and use the
%% scoped operations.
-module(adk_memory_service).
-type handle() :: term().
-type scope() :: {user, AppName :: binary(), UserId :: binary()}.
-type query() :: binary().
-type filter() :: map().
-type provenance() :: #{
session_id => binary(),
event_ids => [binary()],
author => binary(),
timestamp => integer()
}.
-type entry_input() :: #{
content := binary(),
metadata => map(),
provenance => provenance()
}.
-type entry() :: #{
schema_version := pos_integer(),
id := binary(),
scope := scope(),
content := binary(),
metadata := map(),
provenance := map(),
digest := binary(),
timestamp := integer()
}.
-type hit() :: entry() | #{
id := binary(),
scope := scope(),
content := binary(),
metadata := map(),
provenance := map(),
score := float(),
score_type := lexical_overlap,
timestamp := integer()
}.
-type ingest_result() :: #{added := non_neg_integer(),
duplicates := non_neg_integer(),
skipped := non_neg_integer()}.
-type call_options() :: #{timeout_ms => pos_integer()}.
-export_type([handle/0, scope/0, query/0, filter/0, provenance/0,
entry_input/0, entry/0, hit/0, ingest_result/0,
call_options/0]).
%% Version 2 API. `Opts' carries bounded filter/limit/idempotency options;
%% adapters reject unknown options rather than silently changing semantics.
-callback capabilities(Handle :: handle()) -> map().
-callback add_entry(Handle :: handle(), Scope :: scope(),
Input :: entry_input(), Opts :: map()) ->
{ok, entry()} | {error, term()}.
-callback add_events(Handle :: handle(), Scope :: scope(),
SessionId :: binary(), Events :: [adk_event:event()],
Opts :: map()) ->
{ok, ingest_result()} | {error, term()}.
-callback add_session_to_memory(Handle :: handle(), Scope :: scope(),
SessionId :: binary(),
Events :: [adk_event:event()], Opts :: map()) ->
{ok, ingest_result()} | {error, term()}.
-callback search(Handle :: handle(), ScopeOrQuery :: scope() | query(),
QueryOrFilter :: query() | filter(), OptsOrLimit :: map() | pos_integer()) ->
{ok, [hit()]} | {error, term()}.
-callback delete_entry(Handle :: handle(), Scope :: scope(), Id :: binary()) ->
ok | {error, not_found | term()}.
-callback delete_session(Handle :: handle(), Scope :: scope(),
SessionId :: binary()) ->
ok | {error, not_found | term()}.
-callback delete_user(Handle :: handle(), Scope :: scope()) ->
ok | {error, not_found | term()}.
%% Deadline-aware variants are used by least-authority contexts. The adapter
%% must embed the deadline in queued work so a timed-out caller cannot observe
%% a later invisible mutation.
-callback add_entry(Handle :: handle(), Scope :: scope(),
Input :: entry_input(), Opts :: map(),
CallOptions :: call_options()) ->
{ok, entry()} | {error, term()}.
-callback add_events(Handle :: handle(), Scope :: scope(),
SessionId :: binary(), Events :: [adk_event:event()],
Opts :: map(), CallOptions :: call_options()) ->
{ok, ingest_result()} | {error, term()}.
-callback search(Handle :: handle(), Scope :: scope(), Query :: query(),
Opts :: map(), CallOptions :: call_options()) ->
{ok, [hit()]} | {error, term()}.
-callback delete_entry(Handle :: handle(), Scope :: scope(), Id :: binary(),
CallOptions :: call_options()) ->
ok | {error, not_found | term()}.
-callback delete_session(Handle :: handle(), Scope :: scope(),
SessionId :: binary(),
CallOptions :: call_options()) ->
ok | {error, not_found | term()}.
-callback delete_user(Handle :: handle(), Scope :: scope(),
CallOptions :: call_options()) ->
ok | {error, not_found | term()}.
%% Version 1 compatibility API. Constructors are deliberately not callbacks:
%% OTP adapters use `init/1' for their process callback while older adapters
%% may still expose an `init/1' convenience function.
-callback add(Handle :: handle(), Content :: binary(), Metadata :: map()) ->
{ok, binary()} | {error, term()}.
-callback delete(Handle :: handle(), Id :: binary()) ->
ok | {error, term()}.
-callback add_session_to_memory(Handle :: handle(), SessionId :: binary(),
Events :: [adk_event:event()]) ->
ok | {error, term()}.
%% Erlang behaviours cannot express "implement either the v2 set or the v1
%% set". Keep the callbacks that distinguish each contract optional and let
%% `adk_service_ref:validate/2' enforce one complete set at the integration
%% boundary. `search/4' is shared by both contracts and remains mandatory.
-optional_callbacks([capabilities/1, add_entry/4, add_events/5,
add_session_to_memory/5, delete_entry/3,
delete_session/3, delete_user/2,
add/3, delete/2, add_session_to_memory/3,
add_entry/5, add_events/6, search/5,
delete_entry/4, delete_session/4, delete_user/3]).