70#include <llama/llama.h>
84#include <unordered_map>
143 return static_cast<uint16_t
>(h >>
GEN_SHIFT);
153 return (
static_cast<uint32_t
>(generation) <<
GEN_SHIFT) | index;
181 :
chain(o.chain),
has_dist(o.has_dist) { o.chain =
nullptr; }
208 sampler = o.sampler; o.sampler =
nullptr;
249template <SamplingParamsLike P>
251 using ::lloyal::detail::as_value;
253 as_value(p.temperature, 0.8f),
254 as_value(p.top_k,
static_cast<int32_t
>(40)),
255 as_value(p.top_p, 0.95f),
256 as_value(p.typical_p, 1.0f),
257 as_value(p.min_p, 0.05f),
258 as_value(p.penalty_repeat, 1.0f),
259 as_value(p.penalty_freq, 0.0f),
260 as_value(p.penalty_present, 0.0f),
261 as_value(p.penalty_last_n,
static_cast<int32_t
>(64)),
262 as_value(p.seed, absent_seed),
267inline uint32_t
clock_seed() {
return static_cast<uint32_t
>(std::time(
nullptr)); }
294 llama_context*
ctx =
nullptr;
338 std::function<void(llama_token_data_array&)>
steer_fn;
357 throw std::runtime_error(
"branch: cannot capture logits, branch has no vocab (n_vocab=" +
358 std::to_string(
n_vocab) +
")");
437 std::unordered_map<BranchHandle, size_t> first;
438 first.reserve(handles.size());
439 for (
size_t i = 0; i < handles.size(); ++i) {
441 const auto [it, inserted] = first.emplace(handles[i], i);
443 throw std::runtime_error(std::string(who) +
" - duplicate handle at indices " +
444 std::to_string(it->second) +
" and " + std::to_string(i) +
445 " (sequential calls required for multiple runs per branch)");
498 if (initial_capacity < 2) {
499 initial_capacity = 2;
501 slots_.resize(initial_capacity);
503 slots_[0].in_use =
true;
504 slots_[0].generation = 0xFFFF;
508 for (
size_t i = initial_capacity; i-- > 1; ) {
509 freelist_.push_back(
static_cast<uint16_t
>(i));
516 if (tenancy_.
ctx !=
nullptr) {
519 for (
size_t i = 1; i < slots_.size(); ++i) {
520 if (slots_[i].in_use) {
521 free_branch_resources(slots_[i]);
550 return {handle, seq};
570 c.erase(std::remove(c.begin(), c.end(), handle), c.end());
583 cells_used_ = (unique <= cells_used_) ? cells_used_ - unique : 0;
589 free_branch_resources(*st);
594 if (freelist_.size() == slots_.size() - 1) {
619 if (tenancy_.
ctx ==
nullptr)
return;
621 for (
size_t i = 1; i < slots_.size(); ++i) {
622 if (slots_[i].in_use) {
623 free_branch_resources(slots_[i]);
624 reset_slot(slots_[i]);
627 tenancy_.
ctx =
nullptr;
642 if (!w)
throw std::runtime_error(
"retainOnly: invalid winner handle");
643 if (w->
seq_id ==
NO_LEASE)
throw std::runtime_error(
"retainOnly: winner has no lease");
646 std::vector<BranchHandle> losers;
647 for (
size_t i = 1; i < slots_.size(); ++i) {
648 if (!slots_[i].in_use)
continue;
650 if (h == winner)
continue;
653 for (
auto h : losers)
654 release_slot_only(h);
699 if (!a)
throw std::runtime_error(
"fast_forward: invalid parent handle");
701 if (!p)
throw std::runtime_error(
"fast_forward: invalid child handle");
703 throw std::runtime_error(
"fast_forward: the child was not forked from this parent");
706 throw std::runtime_error(
"fast_forward: a branch has no lease");
709 throw std::runtime_error(
"fast_forward: the child has children of its own");
712 throw std::runtime_error(
"fast_forward: the child is poisoned by a failed decode; prune it");
715 throw std::runtime_error(
"fast_forward: the parent has decoded since the fork");
718 throw std::runtime_error(
"fast_forward: the parent has changed since the fork");
760 if (!tenancy_.
ctx)
return {0, 0, 0};
761 uint32_t n_ctx =
static_cast<uint32_t
>(llama_n_ctx(tenancy_.
ctx));
762 uint32_t remaining = (n_ctx > cells_used_) ? n_ctx - cells_used_ : 0;
763 return { n_ctx, cells_used_, remaining };
795 static const std::vector<BranchHandle> empty;
807 return st ? st->
children.empty() :
true;
836 if (index == 0)
return nullptr;
838 if (index >= slots_.size())
return nullptr;
860 template <SamplingParamsLike P>
866 entry.
has_dist = (temperature > 0.0f);
867 sampler_chains_.emplace(h, std::move(entry));
877 if (h == 0)
return 0;
878 auto it = sampler_chains_.find(h);
879 if (it == sampler_chains_.end())
return 0;
883 entry.
has_dist = it->second.has_dist;
884 sampler_chains_.emplace(nh, std::move(entry));
893 if (h != 0) sampler_chains_.erase(h);
902 if (h == 0)
return nullptr;
903 auto it = sampler_chains_.find(h);
904 return it != sampler_chains_.end() ? it->second.chain :
nullptr;
913 if (h == 0)
return false;
914 auto it = sampler_chains_.find(h);
915 return it != sampler_chains_.end() ? it->second.has_dist :
false;
928 const char* grammar_str,
929 const char* root =
"root") {
933 grammars_.emplace(h, std::move(entry));
947 const llama_model* model,
948 const char* grammar_str,
949 const std::vector<std::string>& trigger_patterns,
950 const std::vector<llama_token>& trigger_tokens,
951 const char* root =
"root") {
955 model, grammar_str, trigger_patterns, trigger_tokens, root);
957 grammars_.emplace(h, std::move(entry));
967 if (h == 0)
return 0;
968 auto it = grammars_.find(h);
969 if (it == grammars_.end())
return 0;
973 grammars_.emplace(nh, std::move(entry));
982 if (h != 0) grammars_.erase(h);
991 if (h == 0)
return nullptr;
992 auto it = grammars_.find(h);
993 return it != grammars_.end() ? it->second.sampler :
nullptr;
1014 if (h == 0)
return 0;
1015 auto it = metrics_registry_.find(h);
1016 if (it == metrics_registry_.end())
return 0;
1018 metrics_registry_[nh] = it->second;
1027 if (h != 0) metrics_registry_.erase(h);
1037 auto it = metrics_registry_.find(h);
1038 if (it == metrics_registry_.end())
return;
1039 if (!std::isfinite(surprisal))
return;
1040 it->second.model.nll_sum_nats += std::max(0.0f, surprisal);
1041 it->second.model.count++;
1051 auto it = metrics_registry_.find(h);
1052 if (it == metrics_registry_.end())
return;
1053 if (!std::isfinite(surprisal))
return;
1054 it->second.sampling.nll_sum_nats += std::max(0.0f, surprisal);
1055 it->second.sampling.count++;
1064 if (h == 0)
return std::numeric_limits<float>::infinity();
1065 auto it = metrics_registry_.find(h);
1066 if (it == metrics_registry_.end() || it->second.model.count == 0)
1067 return std::numeric_limits<float>::infinity();
1068 return std::exp(it->second.model.nll_sum_nats /
1069 static_cast<float>(it->second.model.count));
1078 if (h == 0)
return std::numeric_limits<float>::infinity();
1079 auto it = metrics_registry_.find(h);
1080 if (it == metrics_registry_.end() || it->second.sampling.count == 0)
1081 return std::numeric_limits<float>::infinity();
1082 return std::exp(it->second.sampling.nll_sum_nats /
1083 static_cast<float>(it->second.sampling.count));
1111 if (items.empty())
return;
1113 const int32_t n =
static_cast<int32_t
>(items.size());
1116 std::vector<BranchState*> states(n);
1117 for (int32_t i = 0; i < n; ++i) {
1118 states[i] =
get(items[i].handle);
1120 throw std::runtime_error(
"BranchStore::decode_each - invalid handle at index " + std::to_string(i));
1122 if (i > 0 && states[i]->ctx != states[0]->ctx) {
1123 throw std::runtime_error(
"BranchStore::decode_each - all branches must share the same context");
1128 std::vector<BranchHandle> handles(n);
1129 for (int32_t i = 0; i < n; ++i) handles[i] = items[i].handle;
1134 std::vector<decode::EachItem> decode_items(n);
1135 for (int32_t i = 0; i < n; ++i) {
1136 decode_items[i].token = items[i].token;
1137 decode_items[i].pos = states[i]->position;
1138 decode_items[i].seq_id = states[i]->seq_id;
1139 decode_items[i].output_logits =
true;
1143 if (
const int32_t rc =
decode::each(states[0]->ctx, decode_items.data(), n, scratch_); rc != 0) {
1145 for (
auto* st : states) st->poison();
1151 for (int32_t i = 0; i < n; ++i) {
1152 states[i]->capture_logits(i);
1153 states[i]->position += 1;
1154 ++states[i]->revision;
1156 cells_used_ +=
static_cast<uint32_t
>(items.size());
1190 if (items.empty())
return;
1192 const int32_t n =
static_cast<int32_t
>(items.size());
1195 std::vector<BranchState*> states(n);
1196 for (int32_t i = 0; i < n; ++i) {
1197 states[i] =
get(items[i].handle);
1199 throw std::runtime_error(
"BranchStore::decode_scatter - invalid handle at index " + std::to_string(i));
1201 if (i > 0 && states[i]->ctx != states[0]->ctx) {
1202 throw std::runtime_error(
"BranchStore::decode_scatter - all branches must share the same context");
1208 std::vector<BranchHandle> material(n);
1209 for (int32_t i = 0; i < n; ++i) {
1210 material[i] = items[i].tokens.empty() ?
INVALID_HANDLE : items[i].handle;
1215 llama_context* ctx = states[0]->ctx;
1216 const int32_t batch_limit =
static_cast<int32_t
>(llama_n_batch(ctx));
1219 std::vector<std::span<const llama_token>> spans(n);
1220 for (int32_t i = 0; i < n; ++i) {
1221 spans[i] = items[i].tokens;
1227 for (
const auto& chunk : chunks) {
1228 if (chunk.oversized) {
1229 int32_t idx = chunk.indices[0];
1230 int32_t tc =
static_cast<int32_t
>(items[idx].tokens.size());
1232 int32_t committed = 0;
1233 if (
const int32_t rc =
decode::many(ctx, items[idx].tokens.data(), tc,
1234 states[idx]->position, batch_limit,
1235 states[idx]->seq_id, &committed); rc != 0) {
1238 "BranchStore::decode_scatter - decode::many failed for oversized item " + std::to_string(idx));
1241 land_tokens(*states[idx], tc, -1);
1247 std::vector<decode::ScatterItem> scatter_items(chunk.indices.size());
1248 for (
size_t k = 0; k < chunk.indices.size(); ++k) {
1249 int32_t idx = chunk.indices[k];
1250 scatter_items[k].tokens = items[idx].tokens;
1251 scatter_items[k].start_pos = states[idx]->position;
1252 scatter_items[k].seq_id = states[idx]->seq_id;
1253 scatter_items[k].output_logits =
true;
1257 static_cast<int32_t
>(scatter_items.size()),
1258 scratch_); rc != 0) {
1261 for (
const int32_t idx : chunk.indices) states[idx]->poison();
1263 throw decode::DecodeError(rc, landed > 0,
"BranchStore::decode_scatter - decode::scatter failed");
1268 for (
size_t k = 0; k < scatter_items.size(); ++k) {
1269 int32_t idx = chunk.indices[k];
1270 int32_t item_n =
static_cast<int32_t
>(scatter_items[k].tokens.size());
1272 land_tokens(*states[idx], item_n, cursor + item_n - 1);
1341 const float* rows, int32_t n_tokens, int32_t n_embd_inp,
1343 const llama_pos* pos, int32_t n_pos_per_embd,
1344 bool non_causal,
bool want_logits) {
1347 throw std::runtime_error(
"BranchStore::decode_embd - invalid handle");
1349 if (n_pos <= 0 || n_pos > n_tokens) {
1350 throw std::runtime_error(
"BranchStore::decode_embd - invalid n_pos");
1380 int32_t committed = 0;
1384 "BranchStore::decode_embd - llama_decode failed; this branch is "
1385 "poisoned, prune it and replay onto a fresh one");
1388 land_rows(*state, n_tokens, n_pos, want_logits);
1434 throw std::runtime_error(
"BranchStore::decode_segments - invalid handle");
1437 const llama_pos start_pos = state->
position;
1438 DecodeSegmentsResult result;
1440 std::vector<size_t> landed_by_input;
1442 const int32_t input = source.
input_of(seg.index);
1443 if (!on_progress || input < 0)
return;
1444 if (landed_by_input.size() <=
static_cast<size_t>(input)) landed_by_input.resize(input + 1, 0);
1445 landed_by_input[input] +=
static_cast<size_t>(seg.cells);
1451 scratch_, segment_pos_, [&](
const decode::LandedSegment& seg) {
1452 if (seg.kind == decode::Segment::Kind::Text) {
1453 land_tokens(*state, seg.cells, -1);
1455 land_rows(*state, seg.cells, seg.n_pos, seg.has_logits);
1457 result.cells += seg.cells;
1460 }
catch (
const decode::DecodeError& e) {
1466 const bool moved = state->
position != start_pos;
1468 if (e.partial || !moved)
throw;
1469 throw e.as_partial();
1476 result.advance = state->
position - start_pos;
1506 std::span<const BranchHandle> expert_handles,
1510 throw std::runtime_error(
"BranchStore::merge_logits - invalid dst handle");
1513 throw std::runtime_error(
"BranchStore::merge_logits - dst has no captured logits");
1516 throw std::runtime_error(
"BranchStore::merge_logits - dst n_vocab invalid");
1519 const int32_t n_vocab = dst->
n_vocab;
1522 std::vector<const float*> sources;
1523 sources.reserve(expert_handles.size());
1524 for (
size_t i = 0; i < expert_handles.size(); ++i) {
1527 throw std::runtime_error(
1528 "BranchStore::merge_logits - invalid expert handle at index " +
1532 throw std::runtime_error(
1533 "BranchStore::merge_logits - expert has no captured logits at index " +
1536 if (src->
n_vocab != n_vocab) {
1537 throw std::runtime_error(
1538 "BranchStore::merge_logits - n_vocab mismatch at index " +
1543 if (sources.empty())
return;
1546 for (
const float* src_logits : sources) {
1547 for (int32_t t = 0; t < n_vocab; ++t) {
1548 dst_logits[t] += alpha * src_logits[t];
1557 void land_tokens(
BranchState& state, int32_t n_tokens, int32_t logits_index) {
1561 cells_used_ +=
static_cast<uint32_t
>(n_tokens);
1566 void land_rows(BranchState& state, int32_t n_rows, llama_pos n_pos,
bool with_logits) {
1567 state.position += n_pos;
1568 cells_used_ +=
static_cast<uint32_t
>(n_rows);
1569 const uint32_t slack =
static_cast<uint32_t
>(n_rows - n_pos);
1570 state.img_slack_own += slack;
1571 state.img_slack_total += slack;
1575 state.capture_logits(-1);
1581 state.has_logits =
false;
1586 void free_branch_resources(BranchState& slot) {
1587 if (slot.sampler_chain != 0) {
1589 slot.sampler_chain = 0;
1591 if (slot.grammar != 0) {
1592 free_grammar(slot.grammar);
1595 if (slot.boundary_tracker) {
1596 delete slot.boundary_tracker;
1597 slot.boundary_tracker =
nullptr;
1599 if (slot.metrics != 0) {
1600 free_metrics(slot.metrics);
1606 void reset_slot(BranchState& slot) {
1607 slot.in_use =
false;
1608 slot.generation =
static_cast<uint16_t
>(slot.generation + 1);
1610 slot.model =
nullptr;
1615 slot.fork_revision = 0;
1616 slot.poisoned =
false;
1617 slot.img_slack_own = 0;
1618 slot.img_slack_total = 0;
1619 slot.sampler_chain = 0;
1621 slot.grammar_sample_first =
false;
1623 slot.cached_params = CachedSamplingParams{};
1624 slot.last_token = -1;
1625 slot.last_candidates.clear();
1626 slot.logits_snapshot.clear();
1627 slot.has_logits =
false;
1628 slot.logit_bias.clear();
1629 slot.steer_fn =
nullptr;
1630 slot.candidates_buffer.clear();
1634 slot.children.clear();
1639 if (freelist_.empty()) {
1640 size_t old_size = slots_.size();
1641 size_t new_size = old_size * 2;
1645 if (old_size >= new_size) {
1649 slots_.resize(new_size);
1650 for (
size_t i = new_size; i-- > old_size; ) {
1651 freelist_.push_back(
static_cast<uint16_t
>(i));
1654 uint16_t index = freelist_.back();
1655 freelist_.pop_back();
1656 BranchState& slot = slots_[index];
1665 BranchState* st =
get(handle);
1669 BranchState* p =
get(st->parent);
1671 auto& c = p->children;
1672 c.erase(std::remove(c.begin(), c.end(), handle), c.end());
1675 free_branch_resources(*st);
1682 std::deque<BranchState> slots_;
1683 std::vector<uint16_t> freelist_;
1689 kv::tenancy::State tenancy_;
1694 uint32_t cells_used_ = 0;
1698 decode::Scratch scratch_;
1702 std::vector<llama_pos> segment_pos_;
1706 std::unordered_map<SamplerChainHandle, SamplerChainEntry> sampler_chains_;
1709 std::unordered_map<GrammarHandle, GrammarEntry> grammars_;
1712 std::unordered_map<MetricsHandle, metrics::BranchMetricsState> metrics_registry_;
1739template <SamplingParamsLike P>
1742 const llama_model* model,
1744 llama_pos start_pos,
1746 int n_batch = DEFAULT_N_BATCH,
1747 const char* grammar_str =
nullptr,
1749 if (!ctx || !model) {
1758 const llama_vocab* vocab = llama_model_get_vocab(model);
1759 const int n_vocab = llama_vocab_n_tokens(vocab);
1761 throw std::runtime_error(
"branch::create - model has no vocab (n_vocab=" +
1762 std::to_string(n_vocab) +
"); nothing to sample");
1765 auto [handle, seq_id] = s.
allocate();
1777 state->
model = model;
1792 if (grammar_str && grammar_str[0] !=
'\0') {
1799 LLOYAL_LOG_DEBUG(
"[branch::create] Created branch handle=%u seq=%d pos=%d",
1800 handle, seq_id, start_pos);
1820 bool clone_logits =
true;
1846 BranchState* src = s.
get(source);
1849 return INVALID_HANDLE;
1852 auto [new_handle, new_seq_id] = s.
allocate();
1853 if (new_handle == INVALID_HANDLE) {
1854 return INVALID_HANDLE;
1857 BranchState* dst = s.
get(new_handle);
1864 dst->ctx = src->ctx;
1865 dst->model = src->model;
1866 dst->seq_id = new_seq_id;
1867 dst->position = src->position;
1868 dst->fork_head = src->position;
1869 dst->fork_revision = src->revision;
1870 dst->poisoned = src->poisoned;
1872 dst->img_slack_total = src->img_slack_total;
1873 dst->img_slack_own = 0;
1874 dst->n_batch = src->n_batch;
1875 dst->n_vocab = src->n_vocab;
1878 assert(kv::pos_max(src->ctx, new_seq_id) < 0 &&
"tenancy: acquired seq must be clean");
1879 assert(dst->parent == INVALID_HANDLE && dst->children.empty() &&
"fresh slot must have no topology");
1883 kv::seq_cp(src->ctx, src->seq_id, new_seq_id);
1886 dst->parent = source;
1887 src->children.push_back(new_handle);
1890 if (src->sampler_chain != 0) {
1893 dst->cached_params = src->cached_params;
1895 if (src->grammar != 0) {
1898 dst->grammar_sample_first = src->grammar_sample_first;
1900 if (src->boundary_tracker) {
1901 dst->boundary_tracker = src->boundary_tracker->clone().release();
1904 if (src->metrics != 0) {
1908 dst->last_token = src->last_token;
1909 dst->last_candidates = src->last_candidates;
1915 if (opts.clone_logits) {
1916 dst->logits_snapshot = src->logits_snapshot;
1917 dst->has_logits = src->has_logits;
1919 if (
static_cast<int32_t
>(dst->logits_snapshot.size()) != dst->n_vocab) {
1920 dst->logits_snapshot.resize(dst->n_vocab);
1922 dst->has_logits =
false;
1924 dst->logit_bias = src->logit_bias;
1926 dst->candidates_buffer.resize(dst->n_vocab);
1928 LLOYAL_LOG_DEBUG(
"[branch::fork] Forked handle=%u -> handle=%u seq=%d->%d",
1929 source, new_handle, src->seq_id, new_seq_id);
1956 const llama_logit_bias* biases,
1963 throw std::runtime_error(
"set_logit_bias: invalid branch handle");
1967 state->
logit_bias.assign(biases, biases + n_biases);
1988 throw std::runtime_error(
"clear_logit_bias: invalid branch handle");
1994 LLOYAL_LOG_DEBUG(
"[branch::clear_logit_bias] Cleared biases on handle=%u", handle);
2025 std::function<
void(llama_token_data_array&)> steer_fn,
2031 throw std::runtime_error(
"set_steer: invalid branch handle");
2034 state->
steer_fn = std::move(steer_fn);
2036 LLOYAL_LOG_DEBUG(
"[branch::set_steer] Set steer callback on handle=%u", handle);
2053 throw std::runtime_error(
"clear_steer: invalid branch handle");
2058 LLOYAL_LOG_DEBUG(
"[branch::clear_steer] Cleared steer callback on handle=%u", handle);
2079template <SamplingParamsLike P>
2083 throw std::runtime_error(
"set_sampler_params: invalid branch handle");
2101 LLOYAL_LOG_DEBUG(
"[branch::set_sampler_params] Rebuilt chain on handle=%u temp=%.3f",
2119 const llama_model* model,
2120 const char* grammar_str,
2124 throw std::runtime_error(
"set_grammar: invalid branch handle");
2135 if (grammar_str && grammar_str[0] !=
'\0') {
2141 state->
grammar != 0 ?
"Set" :
"Cleared", handle);
2160 const llama_model* model,
2161 const char* grammar_str,
2162 const std::vector<std::string>& trigger_patterns,
2163 const std::vector<llama_token>& trigger_tokens,
2167 throw std::runtime_error(
"set_grammar_lazy: invalid branch handle");
2176 if (grammar_str && grammar_str[0] !=
'\0') {
2178 model, grammar_str, trigger_patterns, trigger_tokens);
2183 state->
grammar != 0 ?
"Set" :
"Cleared", handle);
2203 throw std::runtime_error(
"set_grammar_order: invalid branch handle");
2225 throw std::runtime_error(
"prune: RESTRICT — branch has children. Use pruneSubtree() for CASCADE.");
2239 std::vector<BranchHandle> stack{h}, post_order;
2240 while (!stack.empty()) {
2242 post_order.push_back(cur);
2244 if (st)
for (
auto child : st->
children) stack.push_back(child);
2246 for (
auto it = post_order.rbegin(); it != post_order.rend(); ++it)
2265 throw std::runtime_error(
"reseed_sampler: invalid branch handle");
2313 throw std::runtime_error(
"force_snapshot_logits: invalid branch handle");
2338 const llama_token* tokens,
2345 throw std::runtime_error(
"prefill: invalid branch handle");
2349 int32_t committed = 0;
2350 if (
const int32_t rc = decode::many(state->
ctx, tokens,
static_cast<int32_t
>(n_tokens),
2352 &committed); rc != 0) {
2353 if (!decode::leaves_branch_intact(rc, committed > 0)) state->
poison();
2357 state->
position +=
static_cast<llama_pos
>(n_tokens);
2383 throw std::runtime_error(
"step: invalid branch handle");
2386 if (
const int32_t rc = decode::one(state->
ctx, token, state->
position, state->
seq_id,
true); rc != 0) {
2387 if (!decode::leaves_branch_intact(rc,
false)) state->
poison();
2439 throw std::runtime_error(
"set_logits: invalid handle");
2442 throw std::runtime_error(
"set_logits: invalid vocab size");
2444 if (
static_cast<int32_t
>(data.size()) != state->
n_vocab) {
2445 throw std::runtime_error(
2446 "set_logits: data length (" + std::to_string(data.size()) +
2447 ") does not match branch n_vocab (" + std::to_string(state->
n_vocab) +
")");
2451 state->
n_vocab *
sizeof(
float));
2477 std::span<const int32_t> indices,
2478 std::span<float> out,
2480 if (indices.size() != out.size()) {
2481 throw std::runtime_error(
"get_logits_at: indices/out size mismatch");
2485 throw std::runtime_error(
"get_logits_at: invalid handle");
2488 throw std::runtime_error(
"get_logits_at: no captured logits for handle");
2490 const int32_t n_vocab = state->
n_vocab;
2492 for (
size_t i = 0; i < indices.size(); ++i) {
2493 const int32_t idx = indices[i];
2496 if (idx < 0 || idx >= n_vocab) {
2497 throw std::runtime_error(
"get_logits_at: index out of range");
2499 out[i] = logits[idx];
2524 if (!state || !chain) {
2530 LLOYAL_LOG_DEBUG(
"[branch::sample] No logits captured - call prefill()/step() first");
2538 auto fill = [&]() -> llama_token_data_array {
2539 for (
int i = 0; i < state->
n_vocab; i++) {
2541 static_cast<llama_token
>(i),
2545 return llama_token_data_array{
2547 static_cast<size_t>(state->
n_vocab),
2553 auto shape = [&](llama_token_data_array& cur_p,
bool mask_with_grammar) -> llama_token {
2554 if (mask_with_grammar && gram) {
2555 grammar::apply(gram, &cur_p);
2562 if (bias.token >= 0 && bias.token < state->
n_vocab) {
2563 cur_p.data[bias.token].logit += bias.bias;
2571 }
catch (
const std::exception& e) {
2577 sampler::apply(chain, &cur_p);
2578 return cur_p.selected == -1 ? -1 : cur_p.data[cur_p.selected].id;
2583 auto admits = [&](llama_token token) ->
bool {
2584 llama_token_data single{token, 0.0f, 0.0f};
2585 llama_token_data_array one{&single, 1, -1,
false};
2586 grammar::apply(gram, &one);
2587 return one.data[0].logit != -INFINITY;
2590 llama_token_data_array cur_p = fill();
2592 llama_token token = shape(cur_p, !sample_first);
2594 if (sample_first && token != -1 && !admits(token)) {
2597 token = shape(cur_p,
true);
2610 for (
size_t i = 0; i < cur_p.size; i++) {
2645 grammar::accept(gram, token);
2651 sampler::accept(chain, token);
2657 float ms = metrics::model_surprisal(
2666 std::vector<float> candidate_logits;
2667 std::vector<int32_t> candidate_ids;
2672 candidate_logits.push_back(cand.logit);
2673 candidate_ids.push_back(cand.id);
2677 float ss = metrics::sampling_surprisal(
2678 candidate_logits.data(),
2679 candidate_ids.data(),
2680 static_cast<int>(candidate_logits.size()),
2710 if (!state || !gram)
return;
2713 std::vector<llama_token_data>* candidates_ptr;
2714 std::vector<llama_token_data> temp_buffer;
2719 temp_buffer.resize(n_vocab);
2720 candidates_ptr = &temp_buffer;
2723 auto& candidates = *candidates_ptr;
2724 for (
int i = 0; i < n_vocab; i++) {
2725 candidates[i] = llama_token_data{
2726 static_cast<llama_token
>(i), logits[i], 0.0f};
2729 llama_token_data_array cur_p = {
2731 static_cast<size_t>(n_vocab),
2735 grammar::apply(gram, &cur_p);
2738 for (
int i = 0; i < n_vocab; i++) {
2739 logits[i] = candidates[i].logit;
2768 for (
int i = 0; i < state->
n_vocab; i++) {
2770 static_cast<llama_token
>(i),
2775 llama_token_data_array cur_p = {
2777 static_cast<size_t>(state->
n_vocab),
2784 grammar::apply(gram, &cur_p);
2789 std::vector<std::pair<llama_token, float>> legal_priors;
2790 float max_logit = -std::numeric_limits<float>::infinity();
2792 for (
size_t i = 0; i < cur_p.size; i++) {
2793 if (std::isfinite(cur_p.data[i].logit)) {
2794 legal_priors.emplace_back(cur_p.data[i].id, cur_p.data[i].logit);
2795 if (cur_p.data[i].logit > max_logit) {
2796 max_logit = cur_p.data[i].logit;
2801 if (legal_priors.empty()) {
2806 float sum_exp = 0.0f;
2807 for (
auto& [token, logit] : legal_priors) {
2808 float exp_val = std::exp(logit - max_logit);
2814 for (
auto& [token, prob] : legal_priors) {
2818 return legal_priors;
2841 return -std::numeric_limits<float>::infinity();
2845 for (
int i = 0; i < state->
n_vocab; i++) {
2847 static_cast<llama_token
>(i),
2852 llama_token_data_array cur_p = {
2854 static_cast<size_t>(state->
n_vocab),
2861 grammar::apply(gram, &cur_p);
2865 float max_logit = -std::numeric_limits<float>::infinity();
2866 for (
size_t i = 0; i < cur_p.size; i++) {
2867 if (std::isfinite(cur_p.data[i].logit) && cur_p.data[i].logit > max_logit) {
2868 max_logit = cur_p.data[i].logit;
2872 if (!std::isfinite(max_logit)) {
2873 return -std::numeric_limits<float>::infinity();
2876 float sum_exp = 0.0f;
2877 for (
size_t i = 0; i < cur_p.size; i++) {
2878 if (std::isfinite(cur_p.data[i].logit)) {
2879 sum_exp += std::exp(cur_p.data[i].logit - max_logit);
2883 return max_logit + std::log(sum_exp);
2904 if (!state || token < 0 || token >= state->
n_vocab) {
2915 llama_token_data single_candidate = {
2921 llama_token_data_array cur_p = {
2929 grammar::apply(gram, &cur_p);
2931 return std::isfinite(single_candidate.logit);
2961 return std::exp(logit - logsumexp);
3002 return state ? state->
position : -1;
3030 if (!state || state->
metrics == 0) {
3031 return std::numeric_limits<float>::infinity();
3050 if (!state || state->
metrics == 0) {
3051 return std::numeric_limits<float>::infinity();
3075 std::vector<float> candidate_logits;
3076 std::vector<int32_t> candidate_ids;
3081 candidate_logits.push_back(cand.logit);
3082 candidate_ids.push_back(cand.id);
3086 float surprisal = metrics::sampling_surprisal(
3087 candidate_logits.data(),
3088 candidate_ids.data(),
3089 static_cast<int>(candidate_logits.size()),
3094 return std::exp(-surprisal);
3106 return state ? state->
n_vocab : 0;
3134 : store_(store), handle_(handle) {}
3139 branch::pruneSubtree(handle_, *store_);
3144 : store_(other.store_), handle_(other.handle_) {
3149 if (
this != &other) {
3151 branch::pruneSubtree(handle_, *store_);
3153 store_ = other.store_;
3154 handle_ = other.handle_;
3164 template <SamplingParamsLike P>
3167 const llama_model* model,
3169 llama_pos start_pos,
3171 int n_batch = DEFAULT_N_BATCH,
3172 const char* grammar_str =
nullptr,
3174 BranchHandle h = branch::create(ctx, model, store, start_pos, params, n_batch, grammar_str, boundary_tracker);
3175 return Branch(&store, h);
3181 return Branch(store_, h);
3186 branch::prune(handle_, *store_);
3192 branch::pruneSubtree(handle_, *store_);
3199 branch::force_snapshot_logits(handle_, *store_);
3205 void prefill(
const llama_token* tokens,
size_t n) {
3206 branch::prefill(handle_, tokens, n, *store_);
3212 branch::step(handle_, token, *store_);
3218 return branch::get_logits(handle_, *store_);
3225 return branch::sample(handle_, *store_);
3231 branch::accept_token(handle_, token, *store_);
3236 const BranchState* st = store_ ? store_->get(handle_) :
nullptr;
3237 return st && st->
model ? tokenizer::is_eog(st->
model, token) :
false;
3242 template <SamplingParamsLike P>
3244 branch::set_sampler_params(handle_, params, *store_);
3250 const BranchState* st = store_ ? store_->get(handle_) :
nullptr;
3251 branch::set_grammar(handle_, st ? st->
model :
nullptr, grammar_str, *store_);
3257 llama_pos
position()
const {
return branch::get_position(handle_, *store_); }
3259 llama_pos
forkHead()
const {
return branch::get_fork_head(handle_, *store_); }
3261 float perplexity()
const {
return branch::get_perplexity(handle_, *store_); }
3263 int n_vocab()
const {
return branch::get_n_vocab(handle_, *store_); }
3275 static const std::vector<BranchHandle> empty;
3276 return store_ ? store_->children(handle_) : empty;
3279 bool isLeaf()
const {
return store_ ? store_->isLeaf(handle_) :
true; }
3281 bool isActive()
const {
return store_ ? store_->isActive(handle_) :
false; }
Stub BoundaryTracker - does nothing.
Handle table and batched decode orchestrator for branch management.
DecodeSegmentsResult decode_segments(BranchHandle handle, decode::SegmentSource &source, const std::function< void(const decode::SegmentProgress &)> &on_progress={})
Prefill a heterogeneous segment sequence into ONE branch.
GrammarHandle create_grammar_lazy(const llama_model *model, const char *grammar_str, const std::vector< std::string > &trigger_patterns, const std::vector< llama_token > &trigger_tokens, const char *root="root")
Create a lazy grammar (unconstrained until trigger fires)
float get_sampling_ppl(MetricsHandle h) const
Get sampling-level perplexity from a metrics tracker.
bool isActive(BranchHandle h) const
Test whether a branch holds a KV lease.
void free_grammar(GrammarHandle h)
Free a grammar.
GrammarHandle create_grammar(const llama_model *model, const char *grammar_str, const char *root="root")
Create a grammar sampler and register it.
MetricsHandle clone_metrics(MetricsHandle h)
Clone a metrics tracker (for fork)
size_t available() const
Number of vacant seq_ids available for acquisition.
void release(BranchHandle handle)
Release a branch slot + evict its KV lease.
void drain()
Explicit teardown — evict all leases while context is alive.
float get_model_ppl(MetricsHandle h) const
Get model-level perplexity from a metrics tracker.
void decode_embd(BranchHandle handle, const float *rows, int32_t n_tokens, int32_t n_embd_inp, llama_pos n_pos, const llama_pos *pos, int32_t n_pos_per_embd, bool non_causal, bool want_logits)
Decode embedding rows into ONE branch's KV (multimodal ingress)
bool isLeaf(BranchHandle h) const
Test whether a branch is a leaf (no children)
void decode_each(std::span< const DecodeEachItem > items)
Decode one token per branch in a single GPU dispatch.
void fast_forward(BranchHandle parent, BranchHandle child)
Fast-forward parent to child: the child's state becomes the parent's.
bool sampler_has_dist(SamplerChainHandle h) const
Check if a sampler chain ends with dist (stochastic) or greedy.
MetricsHandle create_metrics()
Create a metrics tracker and register it.
~BranchStore()
Destructor — frees CPU resources.
SamplerChainHandle clone_sampler(SamplerChainHandle h)
Clone a sampler chain (for fork)
llama_pos fork_head(BranchHandle h) const
Get a branch's fork head (parent position at fork time)
Allocation allocate()
Allocate a branch slot + KV lease atomically.
void add_cells_used(uint32_t n)
Increment cells_used counter (for standalone prefill/step outside BranchStore methods)
const BranchState * get(BranchHandle handle) const
Look up branch state by handle.
void free_sampler(SamplerChainHandle h)
Free a sampler chain.
void retainOnly(BranchHandle winner)
Keep only the winner — nuclear KV + CPU cleanup.
KvPressure kv_pressure() const
KV cache pressure snapshot — O(1), no tree walking.
SamplerChainHandle create_sampler(const P ¶ms, int32_t n_vocab)
Create a sampler chain and register it.
llama_sampler * get_grammar_sampler(GrammarHandle h) const
Dereference a grammar handle (non-owning)
void add_sampling_surprisal(MetricsHandle h, float surprisal)
Add sampling-level surprisal to a metrics tracker.
void init_tenancy(llama_context *ctx)
Initialize KV tenancy after context creation.
void merge_logits(BranchHandle dst_handle, std::span< const BranchHandle > expert_handles, float alpha)
Merge experts' logits_snapshot into dst's logits_snapshot.
GrammarHandle clone_grammar(GrammarHandle h)
Clone a grammar (for fork)
void decode_scatter(std::span< const DecodeScatterItem > items)
Decode variable token counts per branch with auto-chunking.
BranchStore(size_t initial_capacity=16)
Construct a branch store with initial slot capacity.
void free_metrics(MetricsHandle h)
Free a metrics tracker.
const std::vector< BranchHandle > & children(BranchHandle h) const
Get a branch's child handles.
BranchState * get(BranchHandle handle)
Look up branch state by handle.
BranchHandle parent(BranchHandle h) const
Get a branch's parent handle.
void add_model_surprisal(MetricsHandle h, float surprisal)
Add model-level surprisal to a metrics tracker.
llama_sampler * get_sampler_chain(SamplerChainHandle h) const
Dereference a sampler chain handle (non-owning)
~Branch()
Destructor — CASCADE prunes entire subtree.
void accept(llama_token token)
Accept a token — advance grammar, penalty window, and metrics.
Branch & operator=(const Branch &)=delete
int n_vocab() const
Vocabulary size.
void setGrammar(const char *grammar_str)
Replace grammar constraint (nullptr/empty to remove)
const std::vector< BranchHandle > & childHandles() const
Child branch handles (empty if leaf)
void setSamplerParams(const P ¶ms)
Replace sampler chain with new parameters (memoized)
BranchHandle handle() const
Underlying opaque handle (for interop with free functions)
const float * logits() const
Get the branch's captured logits snapshot.
bool is_eog(llama_token token) const
Check if a token is end-of-generation for this branch's model.
BranchHandle parentHandle() const
Parent branch handle, or INVALID_HANDLE if root.
Branch(BranchStore *store, BranchHandle handle)
llama_pos position() const
Current decode position (token count)
llama_token sample()
Sample a token from captured logits.
void step(llama_token token)
Decode one token and capture logits (generation step)
void pruneSubtree()
CASCADE prune — removes entire subtree.
float perplexity() const
Model-level perplexity (from raw logits, pre-filter)
Branch(const Branch &)=delete
bool isActive() const
True if this branch holds a KV lease.
llama_pos forkHead() const
Parent's position at fork time (0 for root branches)
void force_snapshot_logits()
Force-copy shared logits buffer into this branch's snapshot.
bool isLeaf() const
True if this branch has no children.
void prefill(const llama_token *tokens, size_t n)
Decode multiple tokens and capture logits atomically (prompt injection)
bool valid() const
True if this Branch holds a valid handle.
void prune()
RESTRICT prune (throws if children exist)
Branch fork()
Fork: allocates slot + lease, records topology edge.
Branch & operator=(Branch &&other) noexcept
static Branch create(llama_context *ctx, const llama_model *model, BranchStore &store, llama_pos start_pos, const P ¶ms, int n_batch=DEFAULT_N_BATCH, const char *grammar_str=nullptr, boundaries::BoundaryTracker *boundary_tracker=nullptr)
Factory: allocates slot + lease from store.
Branch(Branch &&other) noexcept
#define LLOYAL_LOG_DEBUG(...)
liblloyal - Common definitions and logging
Batch Decoding Operations.
Grammar-Constrained Sampling.
constexpr llama_seq_id NO_LEASE
Sentinel value indicating a branch has no KV residency.
Zero-copy logits access with clear lifetime semantics.
Distribution Metrics for Test-Time Alignment.
BranchHandle fork(BranchHandle source, BranchStore &s, ForkOpts opts={})
Fork a branch into a new independent sequence.
void prefill(BranchHandle handle, const llama_token *tokens, size_t n_tokens, BranchStore &s)
Decode multiple tokens and capture logits atomically (prompt prefill)
void set_logit_bias(BranchHandle handle, const llama_logit_bias *biases, size_t n_biases, BranchStore &s)
float get_perplexity(BranchHandle handle, BranchStore &s)
Get model-level perplexity (from raw logits)
void apply_grammar(BranchHandle handle, float *logits, int n_vocab, BranchStore &s)
Apply grammar constraints to an external logits buffer.
int32_t GrammarHandle
Handle to a grammar sampler in BranchStore's registry (0 = invalid/none)
uint32_t BranchHandle
Opaque handle to a branch slot.
void step(BranchHandle handle, llama_token token, BranchStore &s)
Decode a single token and capture logits (generation step)
constexpr int DEFAULT_N_BATCH
Default batch size for decode operations.
void set_grammar_lazy(BranchHandle handle, const llama_model *model, const char *grammar_str, const std::vector< std::string > &trigger_patterns, const std::vector< llama_token > &trigger_tokens, BranchStore &s)
Set lazy grammar on a branch (unconstrained until trigger fires)
void set_logits(BranchHandle handle, std::span< const float > data, BranchStore &s)
Overwrite a branch's logits_snapshot with caller-provided values.
void set_steer(BranchHandle handle, std::function< void(llama_token_data_array &)> steer_fn, BranchStore &s)
void fast_forward(BranchHandle parent, BranchHandle child, BranchStore &s)
Fast-forward parent to child; see BranchStore::fast_forward()
constexpr uint32_t GEN_SHIFT
Bit shift for generation field.
void accept_token(BranchHandle handle, llama_token token, BranchStore &s)
Accept a sampled token, advancing grammar and sampler state.
void force_snapshot_logits(BranchHandle handle, BranchStore &s)
Force-copy the shared llama.cpp logits buffer into this branch's private snapshot.
llama_pos get_fork_head(BranchHandle handle, BranchStore &s)
Get the branch's fork head (parent position at fork time)
float get_sampling_perplexity(BranchHandle handle, BranchStore &s)
Get sampling-level perplexity (from filtered distribution)
float get_legal_logsumexp(BranchHandle handle, BranchStore &s)
Compute log-sum-exp over grammar-legal logits.
void clear_logit_bias(BranchHandle handle, BranchStore &s)
Clear all logit biases from a branch.
uint16_t handle_generation(BranchHandle h)
Extract generation counter from a branch handle.
uint32_t clock_seed()
A fresh seed for a chain created without one.
const float * get_logits(BranchHandle handle, BranchStore &s)
Get the branch's captured logits snapshot.
void set_sampler_params(BranchHandle handle, const P ¶ms, BranchStore &s)
Replace a branch's sampler chain with new parameters.
void prune(BranchHandle handle, BranchStore &s)
Prune a leaf branch (RESTRICT — throws if children exist)
BranchHandle create(llama_context *ctx, const llama_model *model, BranchStore &s, llama_pos start_pos, const P ¶ms, int n_batch=DEFAULT_N_BATCH, const char *grammar_str=nullptr, boundaries::BoundaryTracker *boundary_tracker=nullptr)
Create a new branch with sampler chain, optional grammar, and metrics.
void require_distinct_handles(std::span< const BranchHandle > handles, const char *who)
A handle may appear at most once per batched call: each item's start position is read from its branch...
constexpr uint32_t INDEX_MASK
Mask for slot index field.
int get_n_vocab(BranchHandle handle, BranchStore &s)
Get the branch's vocabulary size.
bool is_poisoned(BranchHandle handle, BranchStore &s)
Whether a failed decode left this branch's KV ahead of its books.
CachedSamplingParams snapshot_params(const P &p, uint32_t absent_seed)
Resolve sampling params into the record a chain is built from.
llama_token sample(BranchHandle handle, BranchStore &s)
Sample a token from the branch's captured logits.
void pruneSubtree(BranchHandle h, BranchStore &s)
Prune a branch and all descendants (CASCADE — iterative post-order)
constexpr BranchHandle INVALID_HANDLE
Null handle sentinel.
float get_token_prior(BranchHandle handle, llama_token token, float logsumexp, BranchStore &s)
Compute prior probability for a token, checking grammar legality first.
constexpr llama_seq_id NO_LEASE
Branch has no KV residency.
int32_t MetricsHandle
Handle to a metrics tracker in BranchStore's registry (0 = invalid/none)
llama_pos get_position(BranchHandle handle, BranchStore &s)
Get the branch's current decode position.
void set_grammar(BranchHandle handle, const llama_model *model, const char *grammar_str, BranchStore &s)
Replace a branch's grammar constraint.
void clear_steer(BranchHandle handle, BranchStore &s)
Clear the steer callback from a branch.
float get_token_prior_assume_legal(BranchHandle handle, llama_token token, float logsumexp, BranchStore &s)
Compute prior probability for a token known to be grammar-legal.
void set_grammar_order(BranchHandle handle, bool sample_first, BranchStore &s)
Choose how sample() consults the attached grammar.
BranchHandle make_handle(uint16_t index, uint16_t generation)
Construct a branch handle from index and generation.
uint16_t handle_index(BranchHandle h)
Extract slot index from a branch handle.
int32_t SamplerChainHandle
Handle to a sampler chain in BranchStore's registry (0 = invalid/none)
void reseed_sampler(BranchHandle handle, uint32_t seed, BranchStore &s)
Reseed a branch's sampling PRNG, the way forks are made to diverge.
std::vector< std::pair< llama_token, float > > get_legal_priors(BranchHandle handle, BranchStore &s)
Get grammar-legal tokens with renormalized probabilities.
bool is_token_legal(BranchHandle handle, llama_token token, BranchStore &s)
Check if a token is legal under grammar constraints.
float get_last_sampling_prior(BranchHandle handle, BranchStore &s)
Get the last sampled token's prior from the filtered distribution.
void get_logits_at(BranchHandle handle, std::span< const int32_t > indices, std::span< float > out, BranchStore &s)
Read selected logit values from a branch's snapshot.
void segments(llama_context *ctx, llama_seq_id seq, llama_pos base, SegmentSource &source, int32_t rows_n_batch, Scratch &scratch, std::vector< llama_pos > &positions, OnLanded &&landed)
Place a source's segments into one sequence, in order.
std::vector< PackedChunk > bin_pack(const std::span< const llama_token > *items, int32_t n, int32_t n_batch)
Greedy first-fit bin-packing of token spans into n_batch-sized chunks.
int embd(llama_context *ctx, const EmbdItem &item, int32_t n_batch, Scratch &scratch, int32_t *n_committed=nullptr)
Decode pre-computed embedding rows into one sequence's KV cache.
bool leaves_branch_intact(int32_t rc, bool partial)
llama_decode failure carrying the raw return code
int scatter(llama_context *ctx, const ScatterItem *items, int32_t n, Scratch &scratch)
Decode multiple tokens per sequence in a single llama_decode() call.
int each(llama_context *ctx, const EachItem *items, int32_t n, Scratch &scratch)
Decode one token per sequence in a single llama_decode() call.
int many(llama_context *ctx, const llama_token *tokens, int32_t n_tokens, int32_t n_past, int32_t n_batch, llama_seq_id seq_id=0, int32_t *n_committed=nullptr)
Decode multiple tokens into the KV cache with auto-chunking.
constexpr T as_value(const X &x, T def)
Extract value from either T or std::optional<T> with fallback.
std::vector< float > get(llama_context *ctx, Normalize normalize=Normalize::L2)
Get embeddings for last decoded batch.
void free_sampler(llama_sampler *smpl)
Free a grammar sampler.
llama_sampler * clone_sampler(llama_sampler *smpl)
Clone a grammar sampler (for fork/branching).
llama_sampler * init_sampler(const llama_model *model, const std::string &grammar_str, const std::string &root_rule="root")
Initialize a grammar sampler from GBNF grammar string.
llama_sampler * init_lazy_sampler(const llama_model *model, const std::string &grammar_str, const std::vector< std::string > &trigger_patterns, const std::vector< llama_token > &trigger_tokens, const std::string &root_rule="root")
Initialize a lazy grammar sampler from GBNF grammar string.
void evict_all(State &s)
Evict every leased seq_id.
llama_seq_id acquire(State &s)
Acquire a seq_id from the vacant pool.
size_t available(const State &s)
Number of vacant seq_ids available for acquisition.
void evict(State &s, llama_seq_id seq)
Evict a seq_id — strip all KV tags then release.
void retain(State &s, llama_seq_id keep)
Nuclear retain — keep one seq, rebuild vacancy from scratch.
State init(llama_context *ctx, llama_seq_id n_seq_max)
Initialize tenancy with all seq_ids vacant.
void release(State &s, llama_seq_id seq)
Release a seq_id back to vacant — bookkeeping only, no KV calls.
float * get(llama_context *ctx, int32_t index=-1)
llama_sampler * clone_chain(llama_sampler *chain)
Clone a sampler chain.
llama_sampler * create_chain(const P ¶ms, int32_t n_vocab)
Create a persistent sampler chain from parameters.
void free_chain(llama_sampler *chain)
Free a sampler chain.
Token Sampling Operations.
Consolidated mutable state for a single branch.
bool in_use
True when slot is allocated to an active branch.
const llama_model * model
Llama model (not owned, must outlive branch)
uint64_t fork_revision
The parent's revision at fork time (0 for root branches).
bool has_logits
True once logits_snapshot holds a live distribution: capture_logits(), set_logits(),...
std::function< void(llama_token_data_array &)> steer_fn
Dynamic logit callback, NOT cloned on fork.
int n_batch
Batch size for decode operations.
bool grammar_sample_first
Grammar order for sample(): false masks the whole vocabulary before the chain (exact under truncation...
llama_seq_id seq_id
KV cache sequence identifier (NO_LEASE when inactive)
llama_pos position
Current decode position in the sequence.
std::vector< llama_logit_bias > logit_bias
Static token biases, cloned on fork.
std::vector< llama_token_data > last_candidates
Filtered candidates from last sample()
std::vector< float > logits_snapshot
Captured logit distribution (n_vocab floats)
std::vector< llama_token_data > candidates_buffer
Reusable scratch buffer for sampling (avoids O(n_vocab) allocs per sample call).
void capture_logits(int32_t index)
Copy the logits at batch row index into this branch's private snapshot.
uint32_t img_slack_own
Embedding-row slack: cells beyond position-delta from decode_embd() (an M-RoPE image adds nx*ny cells...
boundaries::BoundaryTracker * boundary_tracker
Token boundary detector (owned, optional)
SamplerChainHandle sampler_chain
Handle into BranchStore's sampler registry.
llama_context * ctx
Llama context (not owned, must outlive branch)
uint64_t revision
Counts every change to the state fast_forward() moves: decodes, logits, sampling, grammar and bias.
BranchHandle parent
Parent branch (INVALID_HANDLE if root)
int n_vocab
Vocabulary size (cached for buffer pre-allocation)
GrammarHandle grammar
Handle into BranchStore's grammar registry.
std::vector< BranchHandle > children
Child branches forked from this one.
llama_token last_token
Last token returned by sample()
bool poisoned
A decode failed and left the KV ahead of these books (see decode::leaves_branch_intact).
MetricsHandle metrics
Handle into BranchStore's metrics registry.
llama_pos fork_head
Parent's position at fork time (0 for root branches)
CachedSamplingParams cached_params
Params used to create current chain (for memoization)
uint16_t generation
Slot generation counter (for ABA prevention)
Result of allocate(): a slot handle + its leased seq_id.
The resolved sampling params a branch's chain was built from.
bool operator==(const CachedSamplingParams &) const =default
Item for decode_each: one token per branch.
Item for decode_scatter: variable tokens per branch.
std::span< const llama_token > tokens
What a heterogeneous prefill consumed.
Options controlling what state fork() clones from source to child.
RAII entry for a grammar sampler in the registry.
GrammarEntry(GrammarEntry &&o) noexcept
GrammarEntry & operator=(const GrammarEntry &)=delete
GrammarEntry & operator=(GrammarEntry &&o) noexcept
GrammarEntry(const GrammarEntry &)=delete
Snapshot of KV cache pressure from BranchStore.
uint32_t remaining
n_ctx - cells_used (clamped to 0)
uint32_t n_ctx
Total KV capacity.
uint32_t cells_used
Cells allocated since last reset.
RAII entry for a sampler chain in the registry.
SamplerChainEntry()=default
SamplerChainEntry(const SamplerChainEntry &)=delete
SamplerChainEntry & operator=(SamplerChainEntry &&o) noexcept
SamplerChainEntry(SamplerChainEntry &&o) noexcept
bool has_dist
True if chain ends with dist (temp > 0), false if greedy.
SamplerChainEntry & operator=(const SamplerChainEntry &)=delete
Input item for decode::embd — embedding rows for one sequence.
int32_t n_embd_inp
Row width — llama_model_n_embd_inp(model), the INPUT dim (not n_embd)
const float * rows
n_rows x n_embd_inp floats, caller-owned, valid for the call's duration
bool output_logits
When true, compute logits for the LAST row of the last sub-chunk.
const llama_pos * pos
Section-major positions: n_rows * n_pos_per_embd entries, laid out [s0...][s1...]....
bool non_causal
Bracket the whole decode in non-causal attention (Gemma-class projectors; requires n_ubatch >= n_rows...
int32_t n_pos_per_embd
1 (plain positions) or 4 (M-RoPE)
A segment of a stream once it is in the KV: what moved, for the caller's books.
How far one input of a stream has landed: a reading of progress.
Supplies the segment sequence for one branch's prefill.
virtual size_t total_of(int32_t) const
KV cells input input takes, summed over its segments: what progress on it counts towards.
virtual int32_t input_of(size_t) const
The input segment i belongs to (an image, a video, a recording), or -1 for prompt text that belongs t...
llama_context * ctx
Context for KV operations (nullptr after drain)
Unified model + sampling perplexity tracker.