Winery-Strata / src /core /conversation_cache_test.cpp
Zandy-Wandy's picture
🍷 Winery Strata: fork of Niko1221/Strata (Winery Cuvée family, wine-red UI)
bbb6388 verified
Raw History Blame Contribute Delete
12.7 kB
#include "strata/core/conversation_cache.hpp"
#include <cstdio>
#include <cstdlib>
using namespace strata::core;
namespace {
int checks = 0;
void check(bool value, const char* description) {
++checks;
if (!value) { std::fprintf(stderr, "FAIL: %s\n", description); std::exit(1); }
}
SavedConversation image(std::initializer_list<int32_t> ids, bool cvec = true) {
SavedConversation s;
s.live.ids = ids;
s.live.gdn.resize(64, 7);
s.cvec = cvec;
return s;
}
}
int main() {
{
ConversationBuffer bytes;
const size_t first = ConversationBuffer::segment_bytes + 17;
const size_t peak = bytes.allocation_peak(first);
bytes.resize(first, 7);
check(bytes.bytes() <= peak, "segmented payload and directory fit admitted bytes");
uint8_t* original = nullptr;
bytes.visit(0, 1, [&](uint8_t* p, size_t, size_t) { original = p; return true; });
const size_t grown_peak = bytes.allocation_peak(first + 99);
bytes.resize(first + 99, 9);
bytes.visit(0, 1, [&](uint8_t* p, size_t, size_t) {
check(p == original, "appending preserves existing payload addresses"); return true;
});
check(bytes.bytes() <= grown_peak, "growth including transient directory fits admission");
std::vector<uint8_t> tail(116);
check(bytes.read(tail.data(), ConversationBuffer::segment_bytes, tail.size()), "read spans segment boundaries");
check(std::all_of(tail.begin(), tail.begin()+17, [](auto v){return v==7;}) &&
std::all_of(tail.begin()+17, tail.end(), [](auto v){return v==9;}), "growth preserves prefix and initializes only suffix");
auto copy = bytes;
bytes.resize(ConversationBuffer::segment_bytes + 10);
bytes.resize(first, 7); bytes.resize(first + 99, 9);
check(bytes == copy, "equality ignores differing segmentation after rewind and regrowth");
check(!bytes.read(tail.data(), bytes.size()-1, 2), "range check rejects a truncated payload");
check(bytes.allocation_peak(SIZE_MAX) == SIZE_MAX, "allocation estimate rejects overflow");
}
{
ConversationBuffer bytes;
for (size_t n=1;n<=4096;++n) {
const size_t peak = bytes.allocation_peak(n*17);
bytes.resize(n*17,7);
check(bytes.bytes() <= peak,"small append stays within the predicted allocation peak");
}
size_t segments = 0;
bytes.visit(0,bytes.size(),[&](const uint8_t*,size_t,size_t){++segments;return true;});
check(segments <= 4,"thousands of small turns do not create thousands of restore transfers");
}
{
ConversationKv layer;
layer.k.resize(400);
std::vector<ConversationKv> layers;
layers.push_back(std::move(layer));
const size_t retained = layers.capacity()*sizeof(ConversationKv) + layers[0].bytes();
const size_t parked = image({1,2,3}).bytes();
ConversationCache cache(retained + parked, 4);
check(cache.put(image({1,2,3})), "park before retaining active storage");
cache.retain(std::move(layers), 16);
check(cache.bytes() == retained + parked && cache.size() == 1, "active retained storage consumes bytes but no parked slot");
cache.limit_reuse(9); cache.limit_reuse(12);
auto reuse = cache.take_reuse();
check(reuse.unchanged_tokens == 9, "a later continuation cannot undo a rewind's dirty boundary");
check(cache.bytes() == parked, "taking retained buffers releases their budget accounting");
cache.retain(std::move(reuse.kv), 16);
check(!cache.can_fit(parked),"retained storage is counted when checking a capture without eviction");
check(cache.retained_bytes() == retained && cache.size() == 1 && cache.evictions() == 0,
"optional reuse admission does not evict or release anything");
check(!cache.can_fit(SIZE_MAX) && !cache.can_fit(1,SIZE_MAX),"non-mutating reservation rejects overflow");
check(cache.make_room(parked), "reservation can discard optional active buffers");
check(cache.retained_bytes() == 0 && cache.size() == 1 && cache.evictions() == 0,
"pressure drops retained storage before evicting parked conversations");
}
const std::vector<int64_t> a = {1, 2, 3, 4}, b = {9, 8, 7, 6};
{
ConversationCache cache(1024, 2);
check(cache.put(image({1, 2, 3})), "park A");
check(cache.put(image({9, 8, 7})), "park B");
auto match = cache.best(a, {}, true);
check(match.tokens == 3 && match.live, "A/B/A: recover A");
auto restored = cache.take(match.index);
check(restored.live.ids == std::vector<int32_t>({1, 2, 3}), "taking selected A preserves identity");
check(cache.size() == 1 && cache.best(b, {}, true).tokens == 3, "B remains parked");
check(cache.bytes() == image({9,8,7}).bytes(), "byte accounting after take");
check(cache.put(std::move(restored)), "park returned A as newest");
check(cache.put(image({5, 6})), "evict oldest by slot limit");
check(cache.best(b, {}, true).tokens == 0 && cache.best(a, {}, true).tokens == 3, "B evicted before A");
check(cache.evictions() == 1, "eviction counter");
}
{
auto s = image({1, 2, 3});
ConversationCheckpoint cp;
cp.ids = {1, 2};
s.checkpoints.push_back(cp);
ConversationCache cache(4096, 4);
cache.put(std::move(s));
auto match = cache.best(std::vector<int64_t>{1, 2, 9, 4}, {}, true);
check(match.tokens == 2 && !match.live, "edited suffix falls back to parked checkpoint");
check(cache.best(a, {}, true).tokens == 3, "live prefix beats shorter checkpoint");
check(cache.best(a, {}, false).tokens == 0, "steering mode is isolated");
check(cache.best(std::vector<int64_t>{1, 2}, {}, true).tokens == 0, "equal-length checkpoint cannot consume last token");
check(cache.best(std::vector<int64_t>{1}, {}, true).tokens == 0, "short prompt cannot match");
check(cache.best(std::vector<int64_t>{}, {}, true).tokens == 0, "empty prompt cannot match");
cache.put(image({1, 2, 3}));
check(cache.best(a, {}, true).index == 1, "ties prefer most recently parked");
}
{
auto s = image({1, 2, 3});
s.live.imgs = {{1, 123}};
ConversationCache cache(1024, 3);
cache.put(std::move(s));
check(cache.best(a, {{1,123}}, true).tokens == 3, "same image can resume");
check(cache.best(a, {{1,124}}, true).tokens == 0, "different image pixels invalidate same pad tokens");
check(cache.best(a, {}, true).tokens == 0, "missing image invalidates prefix");
check(cache.best(a, {{1,123},{2,45}}, true).tokens == 0, "additional image in cached prefix invalidates");
check(cache.best(a, {{1,123},{3,45}}, true).tokens == 3, "image after cached prefix does not invalidate");
}
{
const size_t one = image({1,2,3}).bytes();
ConversationCache cache(one*2, 8);
cache.put(image({1,2,3})); cache.put(image({9,8,7}));
check(cache.bytes() == one*2, "budget holds two exact-sized images");
auto held = cache.take(cache.best(a, {}, true).index);
check(cache.make_room(one, held.bytes()), "count in-flight image when reserving outgoing snapshot");
check(cache.size() == 0, "in-flight reservation evicts otherwise fitting B");
check(cache.put(image({5,6,7}), held.bytes()), "insert with in-flight accounting");
check(cache.bytes()+held.bytes() <= one*2, "exchange obeys byte budget");
check(!cache.make_room(one+1, one), "oversized exchange rejected");
check(cache.size() == 1, "oversized snapshot does not evict useful entries");
auto huge = image({1}); huge.live.gdn.resize(one*3);
check(!cache.put(std::move(huge)), "oversized image rejected");
check(cache.size() == 1, "oversized put leaves cache unchanged");
check(!cache.make_room(0, one*2+1), "held larger than budget cannot underflow");
}
{
// #342: MAIN -> SUB (9 turns) -> MAIN with 4 slots. Every SUB turn parks the previous turn's live state
// (its reply as generated, which the next request re-rendered) with a chain that holds that turn's
// boundary checkpoint; the copy a turn back adds only that stale tail and is dropped, so MAIN survives.
auto cp = [](std::vector<int32_t> ids) { ConversationCheckpoint c; c.ids = std::move(ids); return c; };
auto with = [](std::vector<int32_t> prefix, std::initializer_list<int32_t> more) {
prefix.insert(prefix.end(), more); return prefix;
};
const std::vector<int32_t> root = {1, 2, 3, 4};
SavedConversation main = image({});
main.live.ids = with(with(root, {10, 11, 12}), {13, 14});
main.checkpoints = {cp(root), cp(with(root, {10, 11, 12}))};
const size_t big = 1 << 20;
ConversationCache cache(big, 4);
check(cache.put(std::move(main)), "park MAIN");
std::vector<int32_t> history = with(root, {20}); // SUB's conversation so far, re-rendered
std::vector<ConversationCheckpoint> chain = {cp(root)};
for (int turn = 1; turn <= 9; ++turn) {
chain.push_back(cp(history)); // the turn boundary the next request resumes from
SavedConversation sub = image({});
sub.live.ids = with(history, {900, (int32_t) turn}); // + the reply with its thinking (stale tail)
sub.checkpoints = chain;
check(cache.put(std::move(sub)), "park SUB turn");
check(cache.size() <= 2, "one parked copy of SUB at a time");
history = with(history, {30, (int32_t) turn}); // the reply as the next request renders it
}
check(cache.evictions() == 0 && cache.superseded() == 8, "8 superseded copies dropped, nothing evicted");
const auto m = cache.best(with(with(root, {10, 11, 12}), {13, 14, 15}), {}, true);
check(m.tokens == 9 && m.live, "MAIN still restores in full");
const auto s = cache.best(with(history, {40}), {}, true);
check(s.tokens == (int64_t) history.size() - 2, "SUB resumes from its last turn boundary");
}
{
// what is NOT superseded: another conversation sharing only the root, an entry without checkpoints, the
// other steering mode, a branch whose deepest checkpoint the new chain does not hold
auto cp = [](std::vector<int32_t> ids) { ConversationCheckpoint c; c.ids = std::move(ids); return c; };
ConversationCache cache(1 << 20, 8);
SavedConversation other = image({1, 2, 3, 4, 50, 51, 52});
other.checkpoints = {cp({1, 2, 3, 4}), cp({1, 2, 3, 4, 50, 51})};
cache.put(std::move(other));
cache.put(image({1, 2, 3, 4, 7, 7})); // no checkpoints
SavedConversation steered = image({1, 2, 3, 4, 60, 61}, false);
steered.checkpoints = {cp({1, 2, 3, 4, 60})};
cache.put(std::move(steered));
SavedConversation branch = image({1, 2, 3, 4, 60, 70, 71});
branch.checkpoints = {cp({1, 2, 3, 4}), cp({1, 2, 3, 4, 60, 70})};
cache.put(std::move(branch));
SavedConversation incoming = image({1, 2, 3, 4, 60, 80, 81});
incoming.checkpoints = {cp({1, 2, 3, 4}), cp({1, 2, 3, 4, 60})};
check(cache.put(std::move(incoming)), "park a conversation sharing roots with all of them");
check(cache.size() == 5 && cache.superseded() == 0, "none of them is superseded");
SavedConversation same_state = image({1, 2, 3, 4, 60, 70}); // live equal to the branch's deepest point
same_state.checkpoints = {cp({1, 2, 3, 4})};
check(cache.put(std::move(same_state)) && cache.superseded() == 1 && cache.size() == 5,
"the live state equal to an entry's deepest checkpoint supersedes it");
SavedConversation huge = image({1, 2, 3, 4, 60, 80});
huge.checkpoints = {cp({1, 2, 3, 4}), cp({1, 2, 3, 4, 60})};
huge.live.gdn.resize(2 << 20);
check(!cache.put(std::move(huge)) && cache.size() == 5 && cache.superseded() == 1,
"an oversized put drops nothing");
}
{
ConversationCache disabled(0,4), no_slots(1024,0);
check(!disabled.enabled() && !no_slots.enabled(), "both disable switches");
check(!disabled.put(image({1,2,3})) && !no_slots.put(image({1,2,3})), "disabled cache stores nothing");
check(disabled.best(a,{},true).tokens == 0, "disabled cache has no matches");
}
std::printf("conversation_cache_test: %d checks passed\n", checks);
}