#include "strata/core/conversation_cache.hpp" #include #include 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 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 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 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 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({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{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{1, 2}, {}, true).tokens == 0, "equal-length checkpoint cannot consume last token"); check(cache.best(std::vector{1}, {}, true).tokens == 0, "short prompt cannot match"); check(cache.best(std::vector{}, {}, 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 ids) { ConversationCheckpoint c; c.ids = std::move(ids); return c; }; auto with = [](std::vector prefix, std::initializer_list more) { prefix.insert(prefix.end(), more); return prefix; }; const std::vector 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 history = with(root, {20}); // SUB's conversation so far, re-rendered std::vector 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 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); }