// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® module Crafter.Review:Plan_impl; import :Plan; import :Json; import :Text; import :Crypto; import std; namespace Crafter::Review::Plan { namespace { // The bytes of `p\d+:` at the start of a unit id, where \d is any // Unicode decimal digit, as re.match reads it. std::optional PkPrefix(std::string_view id) { if (!id.starts_with('p')) return std::nullopt; const std::size_t digits = Dyn::DecimalRun(id, 1); if (digits == 0 || 1 + digits >= id.size() || id[1 + digits] != ':') return std::nullopt; return 2 + digits; } // group(1) of ^p\d+:(.+)#[^#]+$: the path between the prefix and the // last '#', which must have something after it. `.` stops at a // newline; [^#] does not. std::optional FileOfUnit(std::string_view id) { const std::optional prefix = PkPrefix(id); if (!prefix) return std::nullopt; const std::string_view rest = id.substr(*prefix); const std::size_t hash = rest.rfind('#'); if (hash == std::string_view::npos || hash == 0 || hash + 1 >= rest.size()) return std::nullopt; const std::string_view path = rest.substr(0, hash); if (path.contains('\n')) return std::nullopt; return path; } bool IsHex(std::string_view text) { return !text.empty() && std::ranges::all_of(text, [](char c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'); }); } // ^\d+\.\d+ bool StartsWithVersion(std::string_view line) { const std::size_t major = Dyn::DecimalRun(line, 0); if (major == 0 || major >= line.size() || line[major] != '.') return false; return Dyn::DecimalRun(line, major + 1) > 0; } // ^(base-commit|prerequisite-patch-id): [0-9a-f]+$ bool IsTailLine(std::string_view line) { for (const std::string_view head : {std::string_view("base-commit: "), std::string_view("prerequisite-patch-id: ")}) { if (line.starts_with(head) && IsHex(line.substr(head.size()))) return true; } return false; } // ^index [0-9a-f]+\.\.[0-9a-f]+( \d+)?$ -> group(1) ("" when absent) std::optional IndexMode(std::string_view line) { if (!line.starts_with("index ")) return std::nullopt; const std::string_view rest = line.substr(6); const std::size_t dots = rest.find(".."); if (dots == std::string_view::npos || !IsHex(rest.substr(0, dots))) return std::nullopt; const std::string_view after = rest.substr(dots + 2); const std::size_t space = after.find(' '); if (space == std::string_view::npos) { if (!IsHex(after)) return std::nullopt; return std::string_view(); } if (!IsHex(after.substr(0, space))) return std::nullopt; const std::string_view mode = after.substr(space); if (mode.size() < 2 || Dyn::DecimalRun(mode, 1) != mode.size() - 1) return std::nullopt; return mode; } // \d+(?:,\d+)? at `at`: the bytes it takes, 0 for no match. std::size_t LineRange(std::string_view text, std::size_t at) { const std::size_t start = Dyn::DecimalRun(text, at); if (start == 0) return 0; std::size_t end = at + start; if (end < text.size() && text[end] == ',') { const std::size_t count = Dyn::DecimalRun(text, end + 1); if (count > 0) end += 1 + count; } return end - at; } // ^@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@(.*)$ -> group(1) std::optional HunkTail(std::string_view line) { if (!line.starts_with("@@ -")) return std::nullopt; std::size_t at = 4; const std::size_t old = LineRange(line, at); if (old == 0) return std::nullopt; at += old; if (!line.substr(at).starts_with(" +")) return std::nullopt; at += 2; const std::size_t now = LineRange(line, at); if (now == 0) return std::nullopt; at += now; if (!line.substr(at).starts_with(" @@")) return std::nullopt; return line.substr(at + 3); } // Every line start that `head` follows: where the text is cut. // Position 0 is one when the text starts with it, which makes the // first piece empty. std::vector SplitBeforeLines(std::string_view text, std::string_view head) { std::vector cuts; for (std::size_t at = 0; at <= text.size();) { if (text.substr(at).starts_with(head)) cuts.push_back(at); const std::size_t newline = text.find('\n', at); if (newline == std::string_view::npos) break; at = newline + 1; } std::vector pieces; std::size_t from = 0; for (const std::size_t cut : cuts) { pieces.emplace_back(text.substr(from, cut - from)); from = cut; } pieces.emplace_back(text.substr(from)); return pieces; } // re.sub(r'^Subject: (\[[^\]]*\]\s*)*', 'Subject: ', line): the // `[PATCH n/m]` tags after `Subject: ` dropped, and the space after // each. std::string StripSubjectTags(std::string_view line) { if (!line.starts_with("Subject: ")) return std::string(line); std::size_t at = 9; while (at < line.size() && line[at] == '[') { const std::size_t close = line.find(']', at + 1); if (close == std::string_view::npos) break; at = close + 1; at += Dyn::SpaceRun(line, at); } return std::format("Subject: {}", line.substr(at)); } // ^base-commit: [0-9a-f]{40}$ bool IsBaseCommit(std::string_view line) { constexpr std::string_view Head = "base-commit: "; return line.size() == Head.size() + 40 && line.starts_with(Head) && IsHex(line.substr(Head.size())); } std::int64_t IntItem(const Json& range, std::size_t index) { const Json& value = range.at(index); if (value.is_boolean()) return value.get() ? 1 : 0; return value.get(); } Json Mechanical(std::string_view name, std::string_view lensFile, const Json& unit, std::string_view text, std::string_view basis) { const std::string id = Dyn::At(unit, "id").get(); return { {"label", std::format("{}:na:{}", name, id)}, {"lens", name}, {"lensFile", lensFile}, {"kind", "auto-na"}, {"unit", id}, {"expectedIds", {id}}, {"unitSha", Sha(text)}, {"basis", basis}, }; } bool Contains(std::span list, std::string_view item) { return std::ranges::contains(list, item); } } std::vector& Models() { static std::vector models = {{"opus", "claude-opus-5-5"}, {"fable", "claude-opus-5-5"}}; return models; } const std::string& ModelFor(std::string_view tier) { for (const ModelTier& model : Models()) { if (model.tier == tier) return model.id; } throw Dyn::Error("KeyError", Dyn::Repr(tier)); } bool IsLens(std::string_view file) { return std::ranges::any_of(Lenses, [&](const Lens& lens) { return lens.file == file; }); } const Lens& LensOf(std::string_view file) { for (const Lens& lens : Lenses) { if (lens.file == file) return lens; } throw Dyn::Error("KeyError", Dyn::Repr(file)); } const std::vector& Gates() { static const std::vector gates = { {"Gate 1 — Approach", {"repo-better-solution.md"}}, {"Gate 2 — Functional", {FunctionalLens}}, {"Gate 3 — Proof", {"repo-bug-prover.md", "repo-security.md"}}, }; return gates; } const Environment* FindEnvironment(std::string_view name) { for (const Environment& environment : Environments) { if (environment.name == name) return &environment; } return nullptr; } std::vector EnvDeps(std::string_view lensFile, std::string_view environment) { const std::string_view fallback = LensOf(lensFile).env; const std::string_view used = environment.empty() ? fallback : environment; if (used == fallback) return {}; return {std::format("env={}", used)}; } Budget BudgetFor(std::string_view kind, std::string_view unitKind, std::string_view lensFile) { if (kind == "whole") return lensFile == FunctionalLens ? Budget{220, 120} : Budget{180, 105}; if (unitKind != "code") throw Dyn::Error("KeyError", Dyn::Repr(std::format("block:{}", unitKind))); return lensFile == FunctionalLens ? Budget{90, 60} : Budget{60, 40}; } std::string_view DefaultEffort() { return Effort; } std::string_view UnitKindOf(std::string_view unitId) { if (unitId.starts_with("whole:")) return "slot"; const std::optional prefix = PkPrefix(unitId); if (!prefix) return "text"; // `$` also matches before a final newline. std::string_view name = unitId.substr(*prefix); if (name.ends_with('\n')) name.remove_suffix(1); if (name == "subject" || name == "msg" || name == "trailers" || name == "diffstat") return "text"; return "code"; } std::string_view UnitKind(const Json& unit) { return UnitKindOf(Dyn::At(unit, "id").get_ref()); } bool IsDocPath(std::string_view path) { auto endsWithMd = [](std::string_view text) { const std::u32string decoded = Dyn::Decode(text); if (decoded.size() < 3) return false; const std::size_t n = decoded.size(); return decoded[n - 3] == '.' && Dyn::IgnoreCaseLetter(decoded[n - 2]) == 'm' && Dyn::IgnoreCaseLetter(decoded[n - 1]) == 'd'; }; return endsWithMd(path) || (path.ends_with('\n') && endsWithMd(path.substr(0, path.size() - 1))); } bool IsDocUnit(std::string_view unitId) { const std::optional path = FileOfUnit(unitId); return path && IsDocPath(*path); } // The lens is not consulted (yet): no lens of this pipeline reads a // documentation file since the facts lens was removed. std::string_view KindFor(std::string_view, const Json& unit) { const std::string_view kind = UnitKind(unit); return kind == "code" && IsDocUnit(Dyn::At(unit, "id").get_ref()) ? "doc" : kind; } std::optional UnitPath(std::string_view unitId) { const std::optional path = FileOfUnit(unitId); if (!path) return std::nullopt; return std::string(*path); } std::vector NotReviewed(const Json& units, std::span lines, std::span lensFiles, std::span exclude) { std::set covered; for (const std::string& lensFile : lensFiles) { const LensPlan plan = PlanLens(lensFile, units, lines, {}, exclude); if (Dyn::Truthy(plan.whole)) { for (const Json& id : Dyn::At(plan.whole, "expectedIds")) covered.insert(id.get()); } for (const Json& block : plan.blocks) { if (Dyn::At(block, "kind") != "auto-na") covered.insert(Dyn::At(block, "unit").get()); } } std::vector out; for (const Json& unit : units) { const std::string& id = Dyn::At(unit, "id").get_ref(); if (!covered.contains(id)) out.push_back(id); } return out; } std::optional ExcludedBy(std::string_view path, std::span globs) { for (const std::string& glob : globs) { if (Dyn::FnMatch(path, glob) || (glob.starts_with("**/") && Dyn::FnMatch(path, std::string_view(glob).substr(3)))) return glob; } return std::nullopt; } std::string UnitText(std::span lines, const Json& unit) { const Json& range = Dyn::At(unit, "lines"); const std::int64_t a = IntItem(range, 0); const std::int64_t b = IntItem(range, 1); if (a == 0) return ""; const auto [from, to] = Dyn::SliceBounds(lines.size(), a - 1, b); return Dyn::Join(lines.subspan(from, to - from), "\n"); } std::string Sha(std::string_view text) { return Sha256Hex(text); } std::string LensName(std::string_view lensFile) { return Dyn::Replace(lensFile, ".md", ""); } std::vector ModeLenses() { std::vector out; for (const Gate& gate : Gates()) { for (const std::string_view lens : gate.lenses) out.emplace_back(lens); } return out; } std::vector GatesFor(std::span only, std::span without, std::span off) { std::vector out; for (const Gate& gate : Gates()) { Gate kept{gate.title, {}}; for (const std::string_view lens : gate.lenses) { if (!only.empty() && !Contains(only, lens)) continue; if (Contains(without, lens) || Contains(off, lens)) continue; kept.lenses.push_back(lens); } if (!kept.lenses.empty()) out.push_back(std::move(kept)); } return out; } LensPlan PlanLens(std::string_view lensFile, const Json& units, std::span lines, std::span unitsFilter, std::span exclude) { const Lens& lens = LensOf(lensFile); const std::string name = LensName(lensFile); LensPlan plan; if (!lens.whole.empty()) { const Budget budget = BudgetFor("whole", "", lensFile); Json slots = Json::array(); for (const Json& unit : units) { const std::string& id = Dyn::At(unit, "id").get_ref(); if (UnitKind(unit) == "slot" && std::ranges::contains(lens.slots, std::string_view(id))) slots.push_back(id); } plan.whole = { {"label", std::format("{}:whole", name)}, {"lens", name}, {"lensFile", lensFile}, {"kind", "whole"}, {"unit", nullptr}, {"expectedIds", slots}, {"model", ModelFor(lens.model)}, {"maxTurns", budget.maxTurns}, {"timeoutMin", budget.timeoutMin}, }; } for (const Json& unit : units) { const std::string_view kind = KindFor(lensFile, unit); if (!std::ranges::contains(lens.block, kind)) continue; const std::string& id = Dyn::At(unit, "id").get_ref(); if (!unitsFilter.empty() && !Contains(unitsFilter, id)) continue; const std::string text = UnitText(lines, unit); const std::optional path = UnitPath(id); if (!lens.perCommitSlots && (id.ends_with(":outside") || id.ends_with(":absent"))) { // The functional lens judges adjacent flows and what is missing // in its whole pass, not per commit. continue; } if (lensFile == FunctionalLens && path && id.ends_with("#hdr")) { plan.blocks.push_back(Mechanical(name, lensFile, unit, text, "mechanical: a diff header (mode, rename, new or deleted file) carries no behaviour of its own; the hunks of the file do")); continue; } if (lensFile == FunctionalLens && path) { if (const std::optional glob = ExcludedBy(*path, exclude)) { plan.blocks.push_back(Mechanical(name, lensFile, unit, text, std::format("mechanical: no runtime behaviour - the file matches functional.exclude ({})", *glob))); continue; } } const Budget budget = BudgetFor("block", kind, lensFile); plan.blocks.push_back({ {"label", std::format("{}:block:{}", name, id)}, {"lens", name}, {"lensFile", lensFile}, {"kind", "block"}, {"unit", id}, {"unitKind", kind}, {"expectedIds", {id}}, {"unitSha", Sha(text)}, {"model", ModelFor(lens.model)}, {"maxTurns", budget.maxTurns}, {"timeoutMin", budget.timeoutMin}, }); } return plan; } std::optional ReuseKey(const Json& job, std::string_view lensFile, std::string_view unitText, std::vector deps, std::vector inForceIds, std::string_view charterSha, std::int32_t promptVersion, std::string_view ident) { if (!LensOf(lensFile).reuse || Dyn::At(job, "kind") != "block") return std::nullopt; std::ranges::sort(deps); std::ranges::sort(inForceIds); const Json& model = Dyn::Get(job, "model"); const std::vector parts = { std::string(lensFile), ident.empty() ? Dyn::At(job, "unit").get() : std::string(ident), Sha(unitText), Sha(Dyn::Join(deps, "\n")), Dyn::Join(inForceIds, ","), std::string(charterSha), std::to_string(promptVersion), Dyn::Has(job, "model") ? model.get() : std::string(), std::string(ReuseKeyEffortSlot), }; return Sha(Dyn::Join(parts, "|")).substr(0, 16); } std::string CodeNorm(std::string_view text) { std::vector lines = Dyn::Split(text, "\n"); while (!lines.empty() && lines.back().empty()) lines.pop_back(); if (lines.size() >= 2 && lines[lines.size() - 2] == "-- " && StartsWithVersion(lines.back())) { lines.resize(lines.size() - 2); while (!lines.empty() && (lines.back().empty() || IsTailLine(lines.back()))) lines.pop_back(); } for (std::string& line : lines) { if (const std::optional mode = IndexMode(line)) { line = std::format("index{}", *mode); } else if (const std::optional tail = HunkTail(line)) { line = std::format("@@{}", *tail); } } return Dyn::Join(lines, "\n"); } PatchParts PartsOf(std::string_view patchText) { const std::vector lines = Dyn::Split(patchText, "\n"); std::optional subject; for (std::size_t i = 0; i < lines.size(); ++i) { if (lines[i].starts_with("Subject:")) { subject = i; break; } } std::size_t body0 = 1; if (subject) { for (std::size_t i = *subject + 1; i < lines.size(); ++i) { if (Dyn::Strip(lines[i]).empty()) { body0 = i + 1; break; } } } std::size_t diff0 = lines.size(); for (std::size_t i = body0; i < lines.size(); ++i) { if (lines[i].starts_with("---")) { diff0 = i; break; } } std::size_t first = lines.size(); for (std::size_t i = diff0 + 1; i < lines.size(); ++i) { if (lines[i].starts_with("diff --git")) { first = i; break; } } PatchParts parts; for (const std::string& line : lines) { if (IsBaseCommit(line)) { parts.base = Dyn::Split(line)[1]; break; } } const std::span code(lines.begin() + static_cast(std::min(first, lines.size())), lines.end()); for (const std::string& section : SplitBeforeLines(CodeNorm(Dyn::Join(code, "\n")), "diff --git ")) { const std::string firstLine = Dyn::Split(section, "\n", 1)[0]; const std::string path = Dyn::Strip(Dyn::Split(firstLine, " b/").back()); if (section.starts_with("diff --git ") && IsDocPath(path)) { // re.split(r'(?m)^@@', section, maxsplit=1)[0].rstrip('\n') std::size_t header = section.size(); for (std::size_t at = 0; at < section.size();) { if (std::string_view(section).substr(at).starts_with("@@")) { header = at; break; } const std::size_t newline = section.find('\n', at); if (newline == std::string::npos) break; at = newline + 1; } parts.docs.push_back(Dyn::RStrip(std::string_view(section).substr(0, header), "\n")); } else { parts.code += section; } } std::ranges::sort(parts.docs); const auto [messageFrom, messageTo] = Dyn::SliceBounds(lines.size(), static_cast(subject ? *subject : body0), static_cast(diff0)); std::vector message; for (std::size_t i = messageFrom; i < messageTo; ++i) message.push_back(StripSubjectTags(lines[i])); parts.message = Dyn::Join(message, "\n"); const auto [bodyFrom, bodyTo] = Dyn::SliceBounds(lines.size(), static_cast(body0), static_cast(diff0)); for (std::size_t i = bodyFrom; i < bodyTo; ++i) { if (!Dyn::Strip(lines[i]).empty()) { parts.hasBody = true; break; } } return parts; } CommitCode CommitCodeOf(const std::map& patches) { CommitCode cc; std::set bases; std::vector codes; for (const auto& [k, text] : patches) { PatchParts parts = PartsOf(text); cc.body[k] = parts.hasBody; cc.code[k] = Sha(parts.code); cc.docs[k] = std::move(parts.docs); cc.message[k] = Sha(parts.message); if (!parts.base.empty()) bases.insert(parts.base); codes.push_back(cc.code[k]); cc.upto[k] = Sha(Dyn::Join(codes, "\n")); } cc.range = Sha(Dyn::Join(codes, "\n")); cc.base = Dyn::Join(std::vector(bases.begin(), bases.end()), ","); return cc; } std::pair> CodeKeyParts(std::string_view unitId, const CommitCode& cc) { const std::size_t colon = unitId.find(':'); if (colon == std::string_view::npos) throw Dyn::Error("ValueError", "not enough values to unpack (expected 2, got 1)"); const std::string_view pk = unitId.substr(0, colon); const std::string_view rest = unitId.substr(colon + 1); const std::string_view number = pk.empty() ? pk : pk.substr(1); const std::optional k = Dyn::ParseInt(number); if (!k) throw Dyn::Error("ValueError", Dyn::IntError(number)); std::vector deps = {std::format("base={}", cc.base)}; if (rest == "outside" || rest == "absent") { const auto code = cc.code.find(*k); if (code == cc.code.end()) throw Dyn::Error("KeyError", std::to_string(*k)); deps.push_back(std::format("commit={}:{}", *k, code->second)); deps.push_back(std::format("range={}", cc.range)); deps.push_back(std::format("docs={}", Dyn::Join(cc.docs.at(*k), "|"))); if (rest == "absent") deps.push_back(std::format("body={}", cc.body.at(*k) ? "yes" : "no")); return {std::format("code:{}", rest), deps}; } const std::size_t hash = rest.rfind('#'); if (hash == std::string_view::npos) throw Dyn::Error("ValueError", "not enough values to unpack (expected 2, got 1)"); const std::string_view tag = rest.substr(hash + 1); return {std::format("code:{}#{}", rest.substr(0, hash), tag == "hdr" ? "hdr" : "hunk"), deps}; } std::optional CodeReuseKey(const Json& job, std::string_view lensFile, std::string_view unitText, const CommitCode& cc, std::span inForceIds, std::string_view charterSha, std::int32_t promptVersion, std::string_view environment) { auto [ident, deps] = CodeKeyParts(Dyn::At(job, "unit").get_ref(), cc); std::ranges::move(EnvDeps(lensFile, environment), std::back_inserter(deps)); return ReuseKey(job, lensFile, CodeNorm(unitText), deps, {inForceIds.begin(), inForceIds.end()}, charterSha, promptVersion, ident); } std::optional FunctionalReuseKey(const Json& job, std::string_view lensFile, std::string_view unitText, const CommitCode& cc, std::span inForceIds, std::string_view charterSha, std::int32_t promptVersion, std::string_view environment) { auto [ident, deps] = CodeKeyParts(Dyn::At(job, "unit").get_ref(), cc); deps.push_back(std::format("range={}", cc.range)); std::ranges::move(EnvDeps(lensFile, environment), std::back_inserter(deps)); return ReuseKey(job, lensFile, CodeNorm(unitText), deps, {inForceIds.begin(), inForceIds.end()}, charterSha, promptVersion, std::format("functional:{}", ident)); } std::optional BlockReuseKey(const Json& job, std::string_view lensFile, std::string_view unitText, const CommitCode& cc, std::span inForceIds, std::string_view charterSha, std::int32_t promptVersion, std::string_view environment) { if (LensOf(lensFile).key == "functional") { return FunctionalReuseKey(job, lensFile, unitText, cc, inForceIds, charterSha, promptVersion, environment); } return CodeReuseKey(job, lensFile, unitText, cc, inForceIds, charterSha, promptVersion, environment); } std::optional WholeCodeKey(const Json& job, std::string_view lensFile, const CommitCode& cc, std::span inForceIds, std::string_view charterSha, std::int32_t promptVersion, std::string_view environment) { if (!LensOf(lensFile).wholeReuse || Dyn::At(job, "kind") != "whole") return std::nullopt; std::string body; for (const auto& [k, has] : cc.body) { if (!body.empty()) body.push_back(','); body.push_back(has ? 'y' : 'n'); } std::vector docs; for (const auto& [k, files] : cc.docs) docs.push_back(Dyn::Join(files, "|")); std::vector deps = { std::format("base={}", cc.base), std::format("range={}", cc.range), std::format("body={}", body), std::format("docs={}", Dyn::Join(docs, ";")), }; std::ranges::move(EnvDeps(lensFile, environment), std::back_inserter(deps)); std::vector ids(inForceIds.begin(), inForceIds.end()); std::ranges::sort(ids); const std::vector parts = { std::string(lensFile), "code:whole", Sha(Dyn::Join(deps, "\n")), Dyn::Join(ids, ","), std::string(charterSha), std::to_string(promptVersion), Dyn::Has(job, "model") ? Dyn::Get(job, "model").get() : std::string(), std::string(ReuseKeyEffortSlot), }; return Sha(Dyn::Join(parts, "|")).substr(0, 16); } std::int64_t ReadsOf(const Json& entry) { if (!Dyn::Truthy(entry)) return 0; const Json& reads = Dyn::Get(entry, "reads"); if (!Dyn::Truthy(reads)) return 1; if (reads.is_string()) return static_cast(Dyn::Length(reads.get_ref())); return static_cast(reads.size()); } bool ReadIn(const Json& entry, std::string_view hash8, std::int64_t round) { const Json& reads = Dyn::Or(Dyn::Get(entry, "reads"), Dyn::EmptyList()); return std::ranges::any_of(reads, [&](const Json& read) { return Dyn::Equal(Dyn::Get(read, "hash"), Json(hash8)) && Dyn::Equal(Dyn::Get(read, "round"), Json(round)); }); } Json Rebind(const Json& entry, std::string_view unitId) { Json out = entry; out["unit"] = unitId; Json coverage = Dyn::At(out, "coverage"); if (!coverage.is_object()) throw Dyn::Error("TypeError", std::format("'{}' object is not iterable", Dyn::TypeName(coverage))); coverage["item"] = unitId; out["coverage"] = std::move(coverage); for (const std::string_view field : {std::string_view("findings"), std::string_view("notes")}) { if (!Dyn::Has(out, field)) continue; for (Json& item : out[std::string(field)]) { if (Dyn::Get(item, "items").is_array()) item["items"] = Json::array({unitId}); } } return out; } Json Estimate(std::span jobs, std::int64_t prefixBytes, const std::map& lensLayerBytes, std::int64_t tailBytes, double tpb, std::int64_t sysTokens, std::int64_t outPerTurn) { const double prefix = static_cast(prefixBytes) / tpb + static_cast(sysTokens); double cacheRead = 0.0; double cacheWrite = 0.0; double output = 0.0; std::int64_t processes = 0; std::set> seen; for (const Json& job : jobs) { const std::string kind = Dyn::At(job, "kind").get(); if (kind == "auto-na") continue; const std::string lens = Dyn::At(job, "lens").get(); const auto layer = lensLayerBytes.find(lens); const double lensTokens = static_cast(layer == lensLayerBytes.end() ? 3000 : layer->second) / tpb; const double tail = static_cast(tailBytes) / tpb; const std::int64_t turns = kind == "whole" ? 40 : (Dyn::At(job, "lensFile") == FunctionalLens ? 25 : 10); if (seen.insert({lens, kind}).second) cacheWrite += prefix + lensTokens; cacheWrite += tail + static_cast(turns * 3000); std::vector reads; for (std::int64_t i = 0; i < turns; ++i) reads.push_back(prefix + lensTokens + tail + static_cast(3000 * i)); cacheRead += Dyn::Sum(reads); output += static_cast(turns * outPerTurn); ++processes; } const double costUnits = 1.25 * cacheWrite + 0.1 * cacheRead + 5.0 * output; const double listUsd = Dyn::Round((6.25 * cacheWrite + 0.5 * cacheRead + 25.0 * output) / 1e6, 2); return { {"cacheRead", cacheRead}, {"cacheWrite", cacheWrite}, {"output", output}, {"processes", processes}, {"costUnits", costUnits}, {"listUsd", listUsd}, }; } }