okami.mom/albedo

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

nekomimi.petfix(daemon): heal an unreadable transcript row instead of refusing to start87a6f95

main
7.1 KiB200 linesraw
1//// Fit composition and legacy backfill faults require packed database fixtures
2//// and exact source bounds that the HTTP E2E API does not expose.
3
4import albedo/daemon/conversation
5import albedo/daemon/family
6import albedo/daemon/history
7import albedo/daemon/image_fit
8import albedo/daemon/migrations/transcript_classes
9import albedo/daemon/store
10import albedo/daemon/transcript
11import albedo/harness/runtime
12import albedo/openai_api/types
13import gleam/bit_array
14import gleam/crypto
15import gleam/dynamic/decode
16import gleam/list
17import gleam/option.{None, Some}
18import gleam/string
19import gleeunit/should
20
21@external(erlang, "albedo_runtime_test_support", "temporary_database")
22fn temporary_database() -> String
23
24@external(erlang, "albedo_runtime_test_support", "cleanup")
25fn cleanup(path: String) -> Nil
26
27fn picture(hash: String) -> types.Image {
28  let assert Ok(image) =
29    types.stored_image("image/png", hash, 32, fn() { Error(Nil) }, 2, 3, 24)
30  image
31}
32
33fn fit(source: String, replacement: types.Image) -> transcript.ImageFit {
34  transcript.ImageFit("fit", source, replacement)
35}
36
37pub fn reverse_fits_preserve_chains_repeated_sources_and_reuse_test() -> Nil {
38  let original = picture("A")
39  let second = picture("B")
40  let third = picture("C")
41  let input = types.UserImage("look", [original])
42  let later = image_fit.add(image_fit.replacements(), fit("A", third))
43  image_fit.apply_replacements(input, later)
44  |> should.equal(types.UserImage("look", [third]))
45  let earlier = image_fit.add(later, fit("A", second))
46  image_fit.apply_replacements(input, earlier)
47  |> should.equal(types.UserImage("look", [second]))
48  let chain =
49    image_fit.add(
50      image_fit.add(image_fit.replacements(), fit("B", third)),
51      fit("A", second),
52    )
53  image_fit.apply_replacements(input, chain)
54  |> should.equal(types.UserImage("look", [third]))
55}
56
57fn session() -> conversation.Info {
58  conversation.Info(
59    "session",
60    "new session",
61    "/tmp",
62    "provider",
63    "model",
64    types.Responses,
65    conversation.Idle,
66    None,
67    None,
68  )
69}
70
71pub fn metadata_range_and_backfill_resume_test() -> Nil {
72  let path = temporary_database()
73  let assert Ok(host) = runtime.start(path)
74  let ledger = runtime.ledger(host)
75  let assert Ok(_) = conversation.initialise(ledger)
76  let assert Ok(_) = conversation.create(ledger, session())
77  let inputs = list.repeat(types.User("[note] daemon boundary"), 260)
78  let assert Ok(_) =
79    conversation.commit(ledger, "session", inputs, conversation.Idle)
80  let assert Ok(_) =
81    store.write(ledger, "UPDATE transcript SET row_class=NULL", [])
82  // A corrupt row in the third batch is healed into a user-role note and
83  // classified with it; its bytes are kept, and every batch completes.
84  let assert Ok(_) =
85    store.write(ledger, "UPDATE transcript SET payload=X'00' WHERE seq=260", [])
86  let assert Ok(_) = transcript_classes.run(ledger)
87  let assert Ok([pending]) =
88    store.read(
89      ledger,
90      "SELECT COUNT(*) FROM transcript WHERE row_class IS NULL",
91      [],
92      decode.field(0, decode.int, decode.success),
93    )
94  pending |> should.equal(0)
95  let assert Ok(quarantined) =
96    store.read(ledger, "SELECT seq,payload FROM transcript_quarantine", [], {
97      use seq <- decode.field(0, decode.int)
98      use payload <- decode.field(1, decode.bit_array)
99      decode.success(#(seq, payload))
100    })
101  quarantined |> should.equal([#(260, <<0>>)])
102  let assert Ok(snapshot) = conversation.snapshot(ledger, "session")
103  let assert Ok(stats) = conversation.source_stats(ledger, snapshot, 128)
104  stats |> should.equal(conversation.SourceStats(Some(260), True, 132))
105  let assert Ok(selected) =
106    conversation.fold_sources(ledger, snapshot, 128, 132, [], fn(acc, row) {
107      conversation.Continue([row.source.seq, ..acc])
108    })
109  selected |> should.equal([132, 131, 130, 129, 128])
110  let assert Ok(first) =
111    conversation.fold_sources(ledger, snapshot, 1, 260, 0, fn(_, row) {
112      conversation.Stop(row.source.seq)
113    })
114  first |> should.equal(1)
115  runtime.stop(host)
116  cleanup(path)
117}
118
119pub fn ranged_images_equal_full_history_with_later_fits_test() -> Nil {
120  let path = temporary_database()
121  let assert Ok(host) = runtime.start(path)
122  let ledger = runtime.ledger(host)
123  let assert Ok(_) = conversation.initialise(ledger)
124  let assert Ok(_) = family.initialise(ledger)
125  let assert Ok(_) = conversation.create(ledger, session())
126  let data = "iVBORw0KGgoAAAANSUhEUgAAAAIAAAAD"
127  let assert Ok(original) = types.image("image/png", data, 2, 3, 24)
128  let assert Ok(second) = types.image("image/png", data <> "AAAA", 2, 3, 27)
129  let assert Ok(third) = types.image("image/png", data <> "BBBB", 2, 3, 27)
130  let source =
131    crypto.hash(crypto.Sha256, bit_array.from_string(data))
132    |> bit_array.base16_encode
133    |> string.lowercase
134  let assert Ok(_) =
135    conversation.commit(
136      ledger,
137      "session",
138      [types.UserImage("before", [original])],
139      conversation.Idle,
140    )
141  let assert Ok(_) =
142    conversation.commit_fits(
143      ledger,
144      "session",
145      [fit(source, second)],
146      "provider",
147    )
148  let assert Ok(_) =
149    conversation.commit(
150      ledger,
151      "session",
152      [types.UserImage("reuse", [original])],
153      conversation.Idle,
154    )
155  let assert Ok(before_last_fit) = conversation.snapshot(ledger, "session")
156  let assert Ok(_) =
157    conversation.commit_fits(
158      ledger,
159      "session",
160      [fit(source, third)],
161      "provider",
162    )
163  let assert Ok(snapshot) = conversation.snapshot(ledger, "session")
164  let assert Ok(stats) = conversation.source_stats(ledger, snapshot, 0)
165  stats.uncovered_users |> should.equal(4)
166  let assert Ok(full) = conversation.load_sources(ledger, "session")
167  let assert Ok(range) =
168    conversation.fold_sources(ledger, snapshot, 1, 3, [], fn(acc, row) {
169      conversation.Continue([row, ..acc])
170    })
171  // Image reader functions are fresh closures, so compare content fingerprints.
172  list.reverse(range)
173  |> list.map(fn(row) { fingerprint(row.entry.input) })
174  |> should.equal(
175    list.take(full, 3) |> list.map(fn(row) { fingerprint(row.entry.input) }),
176  )
177  let assert Ok(reused) =
178    conversation.fold_sources(ledger, snapshot, 3, 3, [], fn(acc, row) {
179      conversation.Continue([fingerprint(row.entry.input), ..acc])
180    })
181  reused |> should.equal([fingerprint(types.UserImage("reuse", [third]))])
182  let assert Ok(before) =
183    conversation.fold_sources(ledger, before_last_fit, 3, 3, [], fn(acc, row) {
184      conversation.Continue([fingerprint(row.entry.input), ..acc])
185    })
186  before |> should.equal([fingerprint(types.UserImage("reuse", [original]))])
187  let assert Ok(_) = history.fork(ledger, "session", "branch", 2)
188  let assert Ok(branch) = conversation.snapshot(ledger, "branch")
189  let assert Ok(branch_stats) = conversation.source_stats(ledger, branch, 0)
190  branch_stats.uncovered_users |> should.equal(2)
191  let assert Ok(branch_rows) = conversation.load_sources(ledger, "branch")
192  let assert [first, ..] = branch_rows
193  fingerprint(first.entry.input)
194  |> should.equal(fingerprint(types.UserImage("before", [second])))
195  runtime.stop(host)
196  cleanup(path)
197}
198
199@external(erlang, "albedo_compaction", "fingerprint")
200fn fingerprint(value: a) -> String