okami.mom/albedo

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

dawncompaction: split strategies into a read-only project and a separate compactdeef7f4

main
3.6 KiB123 linesraw
1//// Building a request must not change what the next one builds: every
2//// strategy's projection reads its saved state and writes nothing, even when
3//// the history no longer matches that state. Only a compaction writes. The
4//// E2E harness cannot run a projection apart from the compaction that may
5//// follow it, so it cannot tell a read from a write.
6
7import albedo/daemon/conversation
8import albedo/daemon/store
9import albedo/harness/extension
10import albedo/harness/extensions
11import albedo/harness/extensions/lcm/extension as lcm
12import albedo/harness/extensions/lcm/memory
13import albedo/harness/extensions/rolling/extension as rolling
14import albedo/harness/extensions/snapcompact/extension as snapcompact
15import albedo/harness/runtime
16import albedo/openai_api/types
17import gleam/dynamic/decode
18import gleam/list
19import gleam/option.{None, Some}
20import gleam/string
21import gleeunit/should
22import harness/session_fixture
23import sqlight
24
25pub fn rolling_projection_writes_nothing_test() -> Nil {
26  projections_write_nothing(
27    [rolling.configured_extension(rolling.Config(Some(100_000), 90, 25))],
28    ["rolling"],
29  )
30}
31
32pub fn lcm_projection_writes_nothing_test() -> Nil {
33  projections_write_nothing(
34    [
35      memory.extension(),
36      lcm.configured_extension(lcm.Config(Some(100_000), 90, 25)),
37    ],
38    ["lcm-memory", "lcm"],
39  )
40}
41
42pub fn snapcompact_projection_writes_nothing_test() -> Nil {
43  projections_write_nothing([snapcompact.memory(), snapcompact.extension()], [
44    "snapcompact-memory",
45    "snapcompact",
46  ])
47}
48
49/// Compacts once, then prepares requests below the trigger: from the history
50/// the compaction saw, and from a rewritten one its saved state no longer
51/// matches. Neither may write.
52fn projections_write_nothing(
53  installed: List(extension.Extension),
54  enabled: List(String),
55) -> Nil {
56  let assert Ok(host) =
57    runtime.start_with_config(":memory:", extensions.Config(installed, enabled))
58  session_fixture.initialise(host)
59  session_fixture.create(host, "projection", "/tmp")
60  let ledger = runtime.ledger(host)
61  let history = conversation_of(["first", "second", "third", "fourth"])
62  let assert Ok(_) =
63    conversation.commit_from(
64      ledger,
65      "projection",
66      history,
67      conversation.Idle,
68      Some("provider"),
69    )
70  let assert Ok(session) = runtime.open_session(host, "projection", "/tmp")
71  let assert Ok(_) =
72    runtime.compact_history_scoped(
73      host,
74      session,
75      "model",
76      "model",
77      None,
78      "",
79      fn(_) { Ok("summary of earlier work") },
80      history,
81    )
82  let prepare = fn(history) {
83    let before = changes(ledger)
84    let assert Ok(inputs) =
85      runtime.prepare_history_with(
86        host,
87        session,
88        "model",
89        "",
90        fn(_) { Error("a projection must not summarize") },
91        history,
92      )
93    changes(ledger) |> should.equal(before)
94    inputs
95  }
96  // The saved compaction stands in for the start of the history it covers.
97  prepare(history) |> should.not_equal(history)
98  prepare(conversation_of(["rewritten", "second", "third", "fourth"]))
99  runtime.stop(host)
100}
101
102fn conversation_of(topics: List(String)) -> List(types.Input) {
103  list.flat_map(topics, fn(topic) {
104    [
105      types.User(topic <> " " <> string.repeat("q", 4000)),
106      types.Assistant(topic <> " answer " <> string.repeat("a", 4000)),
107    ]
108  })
109}
110
111/// Rows the store's one connection has changed since it opened.
112fn changes(ledger: store.Store) -> Int {
113  let assert Ok([count]) =
114    store.query(ledger, fn(db) {
115      sqlight.query(
116        "SELECT total_changes()",
117        db,
118        [],
119        decode.field(0, decode.int, decode.success),
120      )
121    })
122  count
123}