okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

nekomimi.petfeat: multiple images, collapsed pastes, and an attachment strip in the prompt6e82811

main
6.5 KiB132 linesraw
1%% Test-only structural inspection. Never returns inspected actor state.
2-module(albedo_context_snapshot_probe).
3-export([retention/0, actor/1, actor_json/1]).
4
5capture(Inputs) ->
6    Request = 'albedo@openai_api':request(<<"fixture">>, Inputs),
7    'albedo@daemon@context_snapshot':from_request(
8        {some, 1234}, <<"fixture">>, <<"responses">>, responses, Request, none).
9
10payload(Prefix, Index, Bytes) ->
11    Head = iolist_to_binary([Prefix, integer_to_binary(Index), <<":">>]),
12    <<Head/binary, (binary:copy(<<"x">>, Bytes - byte_size(Head)))/binary>>.
13replay(Index, Bytes) ->
14    Encoded = iolist_to_binary(json:encode(#{type => <<"reasoning">>,
15        opaque => payload(<<"REPLAY_SENTINEL:">>, Index, Bytes)})),
16    {ok, Item} = 'gleam@json':parse(Encoded,
17        'albedo@openai_api@types':replay_decoder(responses)),
18    {replay, Item}.
19
20inline() ->
21    {ok, Image} = 'albedo@openai_api@types':image(<<"image/png">>,
22        payload(<<"INLINE_SENTINEL:">>, 1, 1048576), 10, 10, 786432),
23    Image.
24stored() ->
25    Sentinel = payload(<<"READER_SENTINEL:">>, 1, 1048576),
26    {ok, Image} = 'albedo@openai_api@types':stored_image(<<"image/png">>,
27        <<"stored-hash">>, 1048576, fun() -> {ok, Sentinel} end, 10, 10, 786432),
28    Image.
29
30walk(Term, Test) when is_binary(Term) -> Test(Term);
31walk(Term, Test) when is_tuple(Term) -> walk(tuple_to_list(Term), Test);
32walk([Head | Tail], Test) -> walk(Head, Test) orelse walk(Tail, Test);
33walk(Term, Test) when is_map(Term) -> walk(maps:to_list(Term), Test);
34walk(Term, Test) when is_function(Term) ->
35    {env, Environment} = erlang:fun_info(Term, env), walk(Environment, Test);
36walk(_, _) -> false.
37contains(Term, Prefix) -> walk(Term, fun(Binary) ->
38    binary:match(Binary, Prefix) =/= nomatch end).
39
40retention() ->
41    Parent = self(),
42    {Producer, Monitor} = spawn_monitor(fun() ->
43        Parent ! {snapshot, capture([{user, <<"VISIBLE_SENTINEL:", (binary:copy(<<"v">>, 256))/binary>>},
44            replay(1, 1048576), {user_image, <<"image">>, [inline()]},
45            {tool_output, <<"call">>, <<"tool">>, [stored()]}])}
46    end),
47    Snapshot = receive {snapshot, Value} -> Value end,
48    receive {'DOWN', Monitor, process, Producer, normal} -> ok end,
49    erlang:garbage_collect(),
50    {contains(Snapshot, <<"VISIBLE_SENTINEL:">>),
51     contains(Snapshot, <<"REPLAY_SENTINEL:">>),
52     contains(Snapshot, <<"INLINE_SENTINEL:">>),
53     contains(Snapshot, <<"READER_SENTINEL:">>)}.
54
55binary_bytes(Pid) ->
56    {binary, Entries} = process_info(Pid, binary),
57    lists:sum(maps:values(maps:from_list([{Pointer, Bytes} || {Pointer, Bytes, _} <- Entries]))).
58actor(Id) ->
59    {some, Session} = 'albedo@daemon@session':live(Id),
60    {ok, Pid} = 'gleam@erlang@process':subject_owner(Session),
61    Workers = [Worker || Worker <- processes(), Label <- [proc_lib:get_label(Worker)],
62        is_tuple(Label), element(1, Label) =:= albedo_worker],
63    lists:foreach(fun(Worker) ->
64        Monitor = monitor(process, Worker),
65        receive {'DOWN', Monitor, process, Worker, _} -> ok after 10000 -> error(worker_still_alive) end
66    end, Workers),
67    erlang:garbage_collect(Pid),
68    {binary, BeforeEntries} = process_info(Pid, binary),
69    LargePointers = maps:from_list([{Pointer, Size} || {Pointer, Size, _} <- BeforeEntries,
70        Size >= 262144]),
71    {Evicted, EvictionMetric} = actor_measure(Pid, history_eviction_collection, fun() ->
72        DidEvict = 'albedo@daemon@session':evict_history(Session),
73        %% Two synchronous actor barriers pass the self-sent Collect message.
74        _ = 'albedo@daemon@session':report(Session),
75        _ = 'albedo@daemon@session':report(Session),
76        erlang:garbage_collect(Pid),
77        DidEvict
78    end),
79    Check = self(),
80    {Reader, ReaderMonitor} = spawn_monitor(fun() ->
81        State = sys:get_state(Pid),
82        Check ! {checked, 'albedo@daemon@session_diagnostics':history_unloaded(State),
83            contains(State, <<"REPLAY_SENTINEL:">>)}
84    end),
85    {Unloaded, Reachable} = receive
86        {checked, A, B} -> {A, B};
87        {'DOWN', ReaderMonitor, process, Reader, Reason} -> error({state_inspection_failed, Reason})
88    end,
89    receive {'DOWN', ReaderMonitor, process, Reader, normal} -> ok end,
90    erlang:garbage_collect(Pid),
91    Retained = binary_bytes(Pid),
92    {binary, AfterEntries} = process_info(Pid, binary),
93    Backing = lists:sum(maps:values(maps:from_list([{Pointer, Size}
94        || {Pointer, Size, _} <- AfterEntries, maps:is_key(Pointer, LargePointers)]))),
95    {ok, Snapshot} = 'albedo@daemon@session':prepared_context(Session, none),
96    HistoryPage = 'albedo@daemon@context_snapshot':page(Snapshot, <<"history">>, 0),
97    {_, IdleMetric} = actor_measure(Pid, idle_retention, fun() ->
98        _ = 'albedo@daemon@session':report(Session),
99        erlang:garbage_collect(Pid)
100    end),
101    {Released, ClearMetric} = actor_measure(Pid, context_clear, fun() ->
102        DidRelease = 'albedo@daemon@session':release(Session),
103        _ = 'albedo@daemon@session':report(Session),
104        _ = 'albedo@daemon@session':report(Session),
105        erlang:garbage_collect(Pid),
106        DidRelease
107    end),
108    {ok, Pending} = 'albedo@daemon@session':prepared_context(Session, none),
109    {pending, _} = Pending,
110    SessionMonitor = monitor(process, Pid),
111    _ = 'albedo@daemon@session':close(Session),
112    Stopped = receive {'DOWN', SessionMonitor, process, Pid, _} -> true after 5000 -> false end,
113    #{evicted => Evicted, history_unloaded => Unloaded, replay_reachable => Reachable,
114      history_readable => element(1, HistoryPage) =:= ok,
115      actor_binary_bytes => Retained, replay_backing_binary_bytes => Backing,
116      replay_backing_before_bytes => lists:sum(maps:values(LargePointers)),
117      kernel_released => Released, context_cleared => element(1, Pending) =:= pending,
118      actor_stopped => Stopped, workers_terminated => true,
119      phases => [EvictionMetric, IdleMetric, ClearMetric]}.
120actor_json(Id) -> iolist_to_binary(json:encode(actor(Id))).
121
122actor_measure(Pid, Name, Action) ->
123    {reductions, Before} = process_info(Pid, reductions),
124    Start = erlang:monotonic_time(microsecond),
125    Value = Action(),
126    Elapsed = erlang:monotonic_time(microsecond) - Start,
127    {reductions, After} = process_info(Pid, reductions),
128    {memory, Memory} = process_info(Pid, memory),
129    {message_queue_len, Mailbox} = process_info(Pid, message_queue_len),
130    {Value, #{phase => Name, wall_us => Elapsed, reductions => After - Before,
131        actor_memory_bytes => Memory, mailbox_messages => Mailbox,
132        actor_binary_bytes => binary_bytes(Pid)}}.