diff --git a/common/common.cpp b/common/common.cpp index ac2de2343e..8fe4ec2bdb 100644 --- a/common/common.cpp +++ b/common/common.cpp @@ -3770,6 +3770,15 @@ bool string_is_found(const std::string& window, const std::string& str, size_t& return pos != std::string::npos; } +void string_assign_append(std::string& dst, const std::string_view& sv, const std::string& str, const int32_t pos) { + const int32_t margin = SSIZE(sv) - pos; + if (margin > 0) { + dst.assign(sv, pos).append(str); + } else { + dst.assign(str, -margin); + } +} + // // Filesystem utils // @@ -5473,7 +5482,7 @@ void argparse_expiring_logit_bias(const std::string& content, common_params_samp static const std::vector names = { X_COMMON_PARAMS_SAMPLING }; std::vector addsubs(names.size(), 0.0f); - bool is_sb = false; + bool is_epb = false; // (... : SPARAM ...) const auto window = line.substr(last_qq_pos + 1); @@ -5489,7 +5498,7 @@ void argparse_expiring_logit_bias(const std::string& content, common_params_samp auto sub = string_strip(window.substr(pos, next_pos - pos)); if (sub[0] == '~') { addsubs[j] += std::stof(sub.substr(1)); - is_sb = true; + is_epb = true; LLAMA_LOG_DEBUG("%s: line %zu: bias = %f\n", __func__, i, addsubs[j]); } } @@ -5497,18 +5506,19 @@ void argparse_expiring_logit_bias(const std::string& content, common_params_samp auto& phrases = extracts; if (phrases.empty()) { - if (is_sb) { + if (is_epb) { phrases.push_back(""); } else { continue; // next line } } + int32_t max_keyword_len = 0; const auto n_phrase = phrases.size(); std::vector biases; bool is_range = false; - if (!is_sb) { + if (!is_epb) { // (... : BIAS ...) const auto cln_rpos = line.rfind(':'); auto sub = line.substr(cln_rpos + 1, n_char - cln_rpos - 2); @@ -5532,20 +5542,19 @@ void argparse_expiring_logit_bias(const std::string& content, common_params_samp if (biases.empty()) { continue; // next line } + } else { + for (const auto& keyword: phrases) { + LLAMA_LOG_DEBUG("%s: line %zu: keyword = \"%s\"\n", __func__, i, keyword.c_str()); + max_keyword_len = std::max(SSIZE(keyword), max_keyword_len); + } + LLAMA_LOG_DEBUG("%s: line %zu: max_keyword_len = %d\n", __func__, i, max_keyword_len); } - size_t max_phrase_len = 0; - for (const auto& phrase: phrases) { - LLAMA_LOG_DEBUG("%s: line %zu: phrase = \"%s\"\n", __func__, i, phrase.c_str()); - max_phrase_len = std::max(phrase.length(), max_phrase_len); - } - LLAMA_LOG_DEBUG("%s: line %zu: max_phrase_len = %zu\n", __func__, i, max_phrase_len); - common_params_sampling::elb_param::elb_entry entry = { - std::vector(n_phrase, 0), + max_keyword_len, + std::vector(n_phrase, 0), std::move(addsubs), std::vector(n_phrase, false), - max_phrase_len, std::move(phrases), std::move(biases), duration, diff --git a/common/common.h b/common/common.h index 1eaf664440..b13781c6ff 100644 --- a/common/common.h +++ b/common/common.h @@ -682,6 +682,8 @@ std::vector string_extract(const std::string& str, const char c, st bool string_is_found(const std::string& window, const std::string& str, size_t& pos); +void string_assign_append(std::string& dst, const std::string_view& sv, const std::string& str, const int32_t pos); + // // Filesystem utils // diff --git a/common/sampling.cpp b/common/sampling.cpp index 5a7a9b6953..8e7c9f456c 100644 --- a/common/sampling.cpp +++ b/common/sampling.cpp @@ -175,6 +175,10 @@ struct common_sampler * common_sampler_init(const struct llama_model * model, co } } + result->is_decoding = false; + + result->n_rewind = 0; + result->elb_idx = 0; result->elb_search_pos = 0; @@ -217,6 +221,7 @@ static void llama_grammar_reset(common_sampler * ctx) { void common_sampler_reset(common_sampler * ctx) { // llama_grammar_reset(ctx); + ctx->is_decoding = false; ctx->prev.clear(); llama_sampler_dry_reset(ctx->smpl); } @@ -226,6 +231,10 @@ void common_sampler_review(common_sampler * ctx, const size_t n_unsent, const bo if (ctx->adapt_p_ctx != nullptr) { llama_review_adaptive_p(ctx->adapt_p_ctx, n_unsent, rewind_status); } + + if (ctx->n_rewind > 0) { + common_expiring_logit_bias_rewind(ctx); + } } void llama_sampling_set_rng_seed(struct common_sampler * ctx, uint32_t seed) { @@ -730,7 +739,9 @@ void common_sampler_accept( llama_sampler_dry_accept(ctx_sampling->smpl, token); } - if (ctx_sampling->elb_states.size() > ctx_sampling->elb_idx) { + if (ctx_sampling->is_decoding) { + ctx_sampling->playing_text += common_token_to_piece(ctx_main, token, true); + common_expiring_logit_bias_accept(ctx_sampling, ctx_main); } } @@ -779,8 +790,6 @@ std::vector common_sampler_sample_and_accept_n(struct common_sample for (; i < draft.size(); i++) { const llama_token id = common_sampler_sample(gsmpl, ctx, idxs[i], grammar_first); - gsmpl->drafted_text += common_token_to_piece(ctx, id, true); - common_sampler_accept(gsmpl, ctx, id, true); result.push_back(id); @@ -793,8 +802,6 @@ std::vector common_sampler_sample_and_accept_n(struct common_sample if (i == draft.size()) { const llama_token id = common_sampler_sample(gsmpl, ctx, idxs[i], grammar_first); - gsmpl->drafted_text += common_token_to_piece(ctx, id, true); - common_sampler_accept(gsmpl, ctx, id, true); result.push_back(id); @@ -827,63 +834,63 @@ static void elb_sub(common_params_sampling& sparams, const common_params_samplin } void common_expiring_logit_bias_apply(struct common_sampler* ctx_sampling, float* logits) { - auto index_first_inactive = [](auto countup, auto& tokens) { + const auto& elb = ctx_sampling->elb_states[ctx_sampling->elb_idx]; + + auto index_first_inactive = [&elb](auto& tokens) { return std::distance( tokens.begin(), - std::upper_bound(tokens.begin(), tokens.end(), countup, [](const auto& countup, const auto& token) { - return countup > token.duration; + std::upper_bound(tokens.begin(), tokens.end(), NULL, [&elb](const auto& _, const auto& token) { + return elb.countup > token.duration; }) ); }; - const auto& elb = ctx_sampling->elb_states[ctx_sampling->elb_idx]; - - std::string combined_text; - const std::string* search_window = &combined_text; - if (!ctx_sampling->drafted_text.empty()) { - // add speculated tokens - combined_text = ctx_sampling->to_generated_text != nullptr ? ( - ctx_sampling->to_generated_text->substr(std::max(0, int32_t(ctx_sampling->to_generated_text->length()) - elb.max_cond_len)) - ) : "" + ctx_sampling->drafted_text; - } else if (ctx_sampling->to_generated_text != nullptr) { - search_window = ctx_sampling->to_generated_text; - } + auto& search_window = ctx_sampling->scratch; + string_assign_append(search_window, + ctx_sampling->generated_text, + ctx_sampling->playing_text, + std::max(0, SSIZE(ctx_sampling->generated_text) + SSIZE(ctx_sampling->playing_text) - elb.max_cond_len)); - if (!search_window->empty() && !elb.other_tokens.empty() && (elb.other_tokens.front().duration > elb.countup)) { - const auto ifi = index_first_inactive(elb.countup, elb.other_tokens); + if (!search_window.empty() && !elb.other_tokens.empty() && (elb.other_tokens.front().duration > elb.countup)) { + const auto ifi = index_first_inactive(elb.other_tokens); for (size_t j = 0; j < ifi; ++j) { const auto& [id, bias, _, cond] = elb.other_tokens[j]; - if (string_ends_with(*search_window, cond)) { + if (string_ends_with(search_window, cond)) { + // LLAMA_LOG_DEBUG("%s[%d]: %d\n", __func__, __LINE__, id); logits[id] += bias; } } } if (!elb.first_tokens.empty() && (elb.first_tokens.front().duration > elb.countup)) { - const auto ifi = index_first_inactive(elb.countup, elb.first_tokens); - if (search_window->empty()) { + const auto ifi = index_first_inactive(elb.first_tokens); + if (search_window.empty()) { // empty case here for (size_t j = 0; j < ifi; ++j) { + // LLAMA_LOG_DEBUG("%s[%d]: %d\n", __func__, __LINE__, elb.first_tokens[j].id); logits[elb.first_tokens[j].id] += elb.first_tokens[j].bias; } } else { for (size_t j = 0; j < ifi; ++j) { const auto& [id, bias, _, cond] = elb.first_tokens[j]; // no bias if seen (probably too late) - if (!string_ends_with(*search_window, cond)) { + if (!string_ends_with(search_window, cond)) { + // LLAMA_LOG_DEBUG("%s[%d]: %d\n", __func__, __LINE__, id); logits[id] += bias; } } } } - // expiring sampler bias + // expiring sparam bias for (auto& entry: ctx_sampling->params.elb_params[ctx_sampling->elb_idx].entries) { if (!entry.biases.empty()) { continue; // next entry } - for (size_t j = 0; j < entry.phrases.size(); ++j) { - const auto& phrase = entry.phrases[j]; + + const auto& keywords = entry.phrases; + for (size_t j = 0; j < keywords.size(); ++j) { + const auto& phrase = keywords[j]; if (phrase.empty()) { // duration bound only if (elb.countup == 0) { @@ -907,14 +914,17 @@ void common_expiring_logit_bias_apply(struct common_sampler* ctx_sampling, float } continue; // next entry } + + string_assign_append(search_window, ctx_sampling->generated_text, ctx_sampling->playing_text, entry.search_posi[j]); + size_t count = 0; - auto pos = ctx_sampling->to_generated_text->find(phrase, entry.posi[j]); + auto pos = search_window.find(phrase); while (pos != std::string::npos) { LLAMA_LOG_DEBUG("%s: found %s @ %zu\n", __func__, phrase.c_str(), pos); ++count; - pos = ctx_sampling->to_generated_text->find(phrase, pos + phrase.length()); + pos = search_window.find(phrase, pos + phrase.length()); } - entry.posi[j] = std::max(0, int32_t(ctx_sampling->to_generated_text->length()) - int32_t(phrase.length()) + 1); + entry.search_posi[j] = std::max(0, SSIZE(ctx_sampling->generated_text) + SSIZE(ctx_sampling->playing_text) - SSIZE(phrase) + 1); if (count % 2 == 1) { // even = no match or cancelled LLAMA_LOG_DEBUG("%s: before\n", __func__); @@ -931,40 +941,36 @@ void common_expiring_logit_bias_apply(struct common_sampler* ctx_sampling, float } void common_expiring_logit_bias_accept(struct common_sampler* ctx_sampling, struct llama_context * ctx_main) { - if (ctx_sampling->to_generated_text == nullptr) { - // prompt processing + const auto idx = ctx_sampling->elb_idx; + if (idx >= ctx_sampling->elb_states.size()) { + ctx_sampling->elb_idx++; return; } - auto idx = ctx_sampling->elb_idx; auto& elb = ctx_sampling->elb_states[idx]; - if ((elb.delay > ++elb.countup) || (elb.search_word_len == 0)) { + if ((elb.delay > ++(elb.countup)) || (elb.search_word_len == 0)) { return; } - const std::string window = ctx_sampling->to_generated_text->substr(std::min( - ctx_sampling->to_generated_text->length(), - ctx_sampling->elb_search_pos)) + common_token_to_piece(ctx_main, ctx_sampling->prev.back(), true); + auto& search_pos = ctx_sampling->elb_search_pos; + auto& search_window = ctx_sampling->scratch; + string_assign_append(search_window, ctx_sampling->generated_text, ctx_sampling->playing_text, search_pos); + size_t pos = 0; - if (string_is_found(window, elb.jumpword, pos)) { - LLAMA_LOG_DEBUG("%s: found %s in %s @ %zu\n", __func__, string_unescape(elb.jumpword).c_str(), string_unescape(window).c_str(), pos); - pos += ctx_sampling->elb_search_pos + elb.jumpword.length(); + if (string_is_found(search_window, elb.jumpword, pos)) { + LLAMA_LOG_DEBUG("%s: found %s in %s @ %zu\n", __func__, string_unescape(elb.jumpword).c_str(), string_unescape(search_window).c_str(), search_pos + pos); ctx_sampling->elb_idx = elb.jump_idx; - } else if (string_is_found(window, elb.exitword, pos)) { - LLAMA_LOG_DEBUG("%s: found %s in %s @ %zu\n", __func__, string_unescape(elb.exitword).c_str(), string_unescape(window).c_str(), pos); - pos += ctx_sampling->elb_search_pos + elb.exitword.length(); - ++ctx_sampling->elb_idx; + search_pos += pos + elb.jumpword.length(); + } else if (string_is_found(search_window, elb.exitword, pos)) { + LLAMA_LOG_DEBUG("%s: found %s in %s @ %zu\n", __func__, string_unescape(elb.exitword).c_str(), string_unescape(search_window).c_str(), search_pos + pos); + ctx_sampling->elb_idx++; + search_pos += pos + elb.exitword.length(); } else { - // not found. move search position to include next token - ctx_sampling->elb_search_pos += std::max(0, int32_t(window.length()) - int32_t(elb.search_word_len) + 1); + search_pos += std::max(0, SSIZE(search_window) - elb.search_word_len + 1); return; } - // single character clearance - // e.g. stop \n\n from expiring two \n immediately - ctx_sampling->elb_search_pos = pos + 1; - - // undo current sampler bias + // undo current sparam bias for (auto& entry: ctx_sampling->params.elb_params[idx].entries) { for (const auto addflag: entry.addflags) { if (addflag) { @@ -979,10 +985,108 @@ void common_expiring_logit_bias_accept(struct common_sampler* ctx_sampling, stru } } - // prepare next sampler bias + // initialize next stage + ctx_sampling->elb_states[ctx_sampling->elb_idx].init_pos = search_pos; for (auto& entry: ctx_sampling->params.elb_params[ctx_sampling->elb_idx].entries) { - // no clearance for sampler bias - std::fill(entry.posi.begin(), entry.posi.end(), pos); + entry.search_posi.assign(entry.search_posi.size(), search_pos); + } +} + +void common_expiring_logit_bias_rewind(struct common_sampler* ctx_sampling) { + auto& idx = ctx_sampling->elb_idx; + auto n_rewind = ctx_sampling->n_rewind; + + // LLAMA_LOG_DEBUG("%s[%d]: idx = %d, n_rewind = %d\n", __func__, __LINE__, idx, n_rewind); + + if (idx >= ctx_sampling->elb_states.size()) { + // entirely expired + n_rewind -= 1 + idx - ctx_sampling->elb_states.size(); + idx = ctx_sampling->elb_states.size() - 1; + if (n_rewind <= 0) { + // not enough rewind to reanimate + idx -= n_rewind; + return; + } + } + + // LLAMA_LOG_DEBUG("%s[%d]: idx = %d, n_rewind = %d\n", __func__, __LINE__, idx, n_rewind); + + if (n_rewind > ctx_sampling->elb_states[idx].countup) { + // leaving current state so cleanup + for (auto& entry: ctx_sampling->params.elb_params[idx].entries) { + for (const auto addflag: entry.addflags) { + if (addflag) { + LLAMA_LOG_DEBUG("%s: before\n", __func__); + elb_print(ctx_sampling->params, entry); + + elb_sub(ctx_sampling->params, entry); + + LLAMA_LOG_DEBUG("%s: after\n", __func__); + elb_print(ctx_sampling->params, entry); + } + } + } + } + + // consume n_rewind + while (n_rewind > 0) { + while (ctx_sampling->elb_states[idx].countup == 0) { + --idx; + } + auto& countup = ctx_sampling->elb_states[idx].countup; + LLAMA_LOG_DEBUG("%s[%d]: idx = %d, countup = %zu, n_rewind = %d\n", __func__, __LINE__, idx, countup, n_rewind); + if (countup > n_rewind) { + countup -= n_rewind; + n_rewind = 0; + } else { + n_rewind -= countup; + countup = 0; + } + LLAMA_LOG_DEBUG("%s[%d]: idx = %d, countup = %zu, n_rewind = %d\n", __func__, __LINE__, idx, countup, n_rewind); + } + LLAMA_LOG_DEBUG("%s[%d]: idx = %d, n_rewind = %d\n", __func__, __LINE__, idx, n_rewind); + + const int32_t net_text_len = SSIZE(ctx_sampling->generated_text) + SSIZE(ctx_sampling->playing_text) - SSIZE(ctx_sampling->rewinded_text); + + auto& elb = ctx_sampling->elb_states[idx]; + auto& window = ctx_sampling->scratch; + for (auto& entry: ctx_sampling->params.elb_params[idx].entries) { + if (!entry.biases.empty()) { + // no need to rewind logit biases + continue; + } + const auto& keywords = entry.phrases; + + const int32_t min_window_pos = net_text_len - entry.max_keyword_len + 1; + string_assign_append(window, ctx_sampling->generated_text, ctx_sampling->playing_text, min_window_pos); + + for (size_t j = 0; j < keywords.size(); ++j) { + const auto& keyword = keywords[j]; + if (keyword.empty() && (elb.countup < entry.duration) && !entry.addflags[j]) { + // redo duration-based bias + elb_add(ctx_sampling->params, entry); + entry.addflags[j] = true; + continue; + } + + // keyword match within rewinded text? + size_t count = 0; + auto pos = window.find(keyword, entry.max_keyword_len - SSIZE(keyword) + std::max(0, elb.init_pos - min_window_pos)); + while (pos != std::string::npos) { + ++count; + pos = window.find(keyword, pos + 1); + } + if (count % 2 == 1) { + (entry.addflags[j] ? elb_sub : elb_add)(ctx_sampling->params, entry); + entry.addflags[j] = !entry.addflags[j]; + } + } + } + + // reinitialize + ctx_sampling->elb_search_pos = std::max(elb.init_pos, net_text_len - elb.search_word_len + 1); + for (auto& entry: ctx_sampling->params.elb_params[idx].entries) { + entry.search_posi.assign(entry.search_posi.size(), elb.init_pos); } } diff --git a/common/sampling.h b/common/sampling.h index c36aff4f40..7f52e5873e 100644 --- a/common/sampling.h +++ b/common/sampling.h @@ -11,6 +11,8 @@ #define A_DOT_B(a, b) a.b +#define SSIZE(container) static_cast(container.size()) + // sampler types enum class llama_sampler_type : char { DRY = 'd', @@ -174,25 +176,26 @@ typedef struct common_params_sampling { std::vector penalty_prompt_tokens; bool use_penalty_prompt_tokens = false; - // expiring logit bias + // expiring logit bias (elb) + expiring sparam bias (epb) struct elb_param { struct elb_entry { - std::vector posi; // positions of phrases in generated text - std::vector addsubs; // add/modify then subtract/restore sampling parameters - std::vector addflags; // true if added - size_t max_phrase_len; - std::vector phrases; - std::vector biases; // for each phrase, nth bias for nth token, extrapolate - int32_t duration; // bias duration, unless exitword matches - bool is_range; // has lower and upper biases + int32_t max_keyword_len; + std::vector search_posi; // epb: starting search positions for phrases + std::vector addsubs; // epb: bias for sparams + std::vector addflags; // epb: true if added + std::vector phrases; // exitwords for elb (below) OR keywords for epb (above) + std::vector biases; // elb: for each phrase, nth bias for nth token, extrapolate + int32_t duration; // elb: bias duration, unless exitword matches + bool is_range; // elb: has lower and upper biases bool operator == (const struct elb_entry& other) const { return (is_range == other.is_range) && (duration == other.duration) - && (biases == other.biases) - && (phrases == other.phrases) + && (max_keyword_len == other.max_keyword_len) && (addflags == other.addflags) + && (biases == other.biases) && (addsubs == other.addsubs) - && (posi == other.posi); + && (search_posi == other.search_posi) + && (phrases == other.phrases); } }; std::vector entries; @@ -214,6 +217,10 @@ struct common_sampler { // parameters that will be used for sampling common_params_sampling params; + bool is_decoding; // set true near the end of pp + + std::string scratch; + // mirostat sampler state float mirostat_mu; @@ -233,22 +240,25 @@ struct common_sampler { size_t n_valid; // Number of correct top tokens with correct probabilities. + int32_t n_rewind; + std::string rewinded_text; + + std::string_view generated_text; + std::string playing_text; // token_buffer + draft + llama_token_data_array cur_p; // current candidates std::mt19937 rng; std::vector* server_biases; - std::string drafted_text; - std::string* to_generated_text = nullptr; - // expiring logit bias struct elb_state { struct elb_token { int32_t id; float bias; size_t duration; - std::string cond; // bias activation condition + std::string cond; // bias activation condition }; std::vector first_tokens; // first token of each phrase std::vector other_tokens; @@ -257,12 +267,13 @@ struct common_sampler { size_t delay; // to avoid early termination of positively biased phrases int32_t max_cond_len; std::string jumpword; - size_t jump_idx; - size_t search_word_len; + int32_t jump_idx; + int32_t search_word_len; + int32_t init_pos; }; std::vector elb_states; - size_t elb_idx; // for elb_states - size_t elb_search_pos; + int32_t elb_idx; // for elb_states and elb_params + int32_t elb_search_pos; // for exitwords }; @@ -364,5 +375,7 @@ void common_expiring_logit_bias_apply(struct common_sampler* ctx_sampling, float void common_expiring_logit_bias_accept(struct common_sampler* ctx_sampling, struct llama_context * ctx_main); +void common_expiring_logit_bias_rewind(struct common_sampler* ctx_sampling); + llama_grammar* llama_sampler_init_llg(const llama_vocab* vocab, const char* grammar_kind, const char* grammar_data); diff --git a/examples/server/server-context.cpp b/examples/server/server-context.cpp index 5ed5a39523..f39e2410bb 100644 --- a/examples/server/server-context.cpp +++ b/examples/server/server-context.cpp @@ -466,7 +466,7 @@ void server_slot::prompt_load(server_prompt_cache& prompt_cache, const server_to void server_slot::reset() { n_prompt_tokens = 0; - last_gentxt_size = 0; + last_gentxt_len = 0; generated_text = ""; truncated = false; stopped_eos = false; @@ -1914,7 +1914,7 @@ bool server_context::launch_slot_with_task(server_slot& slot, server_task& task) } } - slot.ctx_sampling->elb_states.push_back({ { }, { }, exitword, 0, 0, 0, "", 0, exitword.length() }); + slot.ctx_sampling->elb_states.push_back({ { }, { }, exitword, 0, 0, 0, "", 0, SSIZE(exitword), 0 }); auto& first_tokens = slot.ctx_sampling->elb_states.back().first_tokens; auto& other_tokens = slot.ctx_sampling->elb_states.back().other_tokens; @@ -1974,7 +1974,7 @@ bool server_context::launch_slot_with_task(server_slot& slot, server_task& task) } other_tokens.push_back({ ids[j], biases[j], size_t(duration + m * j), cond }); } - max_cond_len = std::max(int32_t(cond.length()), max_cond_len); + max_cond_len = std::max(SSIZE(cond), max_cond_len); } } } @@ -2133,7 +2133,7 @@ bool server_context::process_token(completion_token_output& result, server_slot& slot.sampled = result.tok; // search stop word and delete it - slot.last_gentxt_size = slot.generated_text.size(); + slot.last_gentxt_len = slot.generated_text.size(); slot.generated_text += token_str; slot.has_next_token = true; @@ -4096,6 +4096,7 @@ void server_context::batch_pending_prompt(const int32_t n_ubatch, const int32_t common_sampler_accept(slot.ctx_sampling, ctx, id, false); } } + slot.ctx_sampling->is_decoding = true; // extract the logits only for the last token batch.logits[batch.n_tokens - 1] = true; @@ -4159,15 +4160,14 @@ void server_context::speculative_decoding_accept() { const llama_token sampled_before = slot.sampled; size_t n_draft = slot.drafted.size(); - slot.ctx_sampling->to_generated_text = &slot.generated_text; - if (n_draft > 0) { - (void) populate_vocab_pieces(); // max_piece_len - slot.ctx_sampling->drafted_text.reserve(max_piece_len * n_draft); - slot.ctx_sampling->drafted_text.clear(); - } - apply_server_biases(slot); + slot.ctx_sampling->generated_text = slot.generated_text; + slot.ctx_sampling->playing_text.clear(); + for (const auto& token: slot.token_buffer) { + slot.ctx_sampling->playing_text.append(token.text_to_send); + } + // the accepted tokens from the speculation std::vector ids; try { @@ -4245,6 +4245,9 @@ void server_context::speculative_decoding_accept() { populate_token_probs(slot, result, slot.params.post_sampling_probs, params_base.special, i); } + slot.ctx_sampling->n_rewind = 0; + slot.ctx_sampling->rewinded_text.clear(); + if (slot.n_buffer == 0 || !params_base.can_ban_phrases) { if (!process_token(result, slot)) { // release slot because of stop condition @@ -4259,6 +4262,21 @@ void server_context::speculative_decoding_accept() { } } + if (slot.ctx_sampling->n_rewind > 0) { + // consume out-of-context tokens + auto banned_n = slot.banned_n; + slot.banned_n = 0; + for (++i; i < ids.size(); ++i) { + slot.token_buffer.push_back({ + ids[i], + common_token_to_piece(ctx, result.tok, accept_special_token(slot, result.tok)), + 0.0f, + { } }); + rewind_context(slot, slot.n_past); + } + slot.banned_n = banned_n; + } + common_sampler_review(slot.ctx_sampling, slot.token_buffer.size(), slot.rewind_status); update_allowlist_state(slot); @@ -4403,13 +4421,19 @@ inline int32_t check_ban_phrase(server_slot& slot) { return -1; } -inline void rewind_context(server_slot& slot, int32_t ban_pos) { +void server_context::rewind_context(server_slot& slot, int32_t ban_pos) { slot.rewind_count++; int32_t buffer_start_pos = slot.n_past - (int32_t)slot.token_buffer.size() + 1; int32_t n_keep_buffer = ban_pos - buffer_start_pos; if (n_keep_buffer < 0) n_keep_buffer = 0; + slot.ctx_sampling->n_rewind += slot.token_buffer.size() - n_keep_buffer; + LLAMA_LOG_DEBUG("%s[%d]: n_rewind = %d\n", __func__, __LINE__, slot.ctx_sampling->n_rewind); + for (int32_t j = n_keep_buffer; j < slot.token_buffer.size(); ++j) { + slot.ctx_sampling->rewinded_text.append(slot.token_buffer[j].text_to_send); + } + if (slot.banned_n != 0) { int32_t n = 0; for (auto result = slot.token_buffer.begin() + n_keep_buffer; result != slot.token_buffer.end(); result++) { @@ -4534,7 +4558,7 @@ void server_context::update_allowlist_state(server_slot& slot) { // search for keyword auto kw = kws[idx]; - auto pos = slot.generated_text.find(kw, std::max(0, slot.last_gentxt_size - (int32_t)kw.length() + 1)); + auto pos = slot.generated_text.find(kw, std::max(0, slot.last_gentxt_len - (int32_t)kw.length() + 1)); while (pos != std::string::npos) { if (++idx >= kws.size()) { break; @@ -4675,8 +4699,6 @@ void server_context::process_batch_tokens(int32_t & n_batch) { } } - slot.ctx_sampling->to_generated_text = &slot.generated_text; - completion_token_output result; const int tok_idx = slot.i_batch - i; @@ -4686,6 +4708,12 @@ void server_context::process_batch_tokens(int32_t & n_batch) { apply_server_biases(slot); + slot.ctx_sampling->generated_text = slot.generated_text; + slot.ctx_sampling->playing_text.clear(); + for (const auto& token: slot.token_buffer) { + slot.ctx_sampling->playing_text.append(token.text_to_send); + } + llama_token id; try { id = common_sampler_sample(slot.ctx_sampling, ctx, tok_idx); @@ -4729,6 +4757,9 @@ void server_context::process_batch_tokens(int32_t & n_batch) { populate_token_probs(slot, result, slot.params.post_sampling_probs, params_base.special, tok_idx); } + slot.ctx_sampling->n_rewind = 0; + slot.ctx_sampling->rewinded_text.clear(); + // no ban string for recurrent/hybrid model if (slot.n_buffer == 0 || !params_base.can_ban_phrases) { slot.token_buffer = { result }; diff --git a/examples/server/server-context.h b/examples/server/server-context.h index c2936a41a4..4695d9be74 100644 --- a/examples/server/server-context.h +++ b/examples/server/server-context.h @@ -65,7 +65,7 @@ struct server_slot { server_tokens prompt_tokens; server_tokens cache_tokens; - int32_t last_gentxt_size = 0; + int32_t last_gentxt_len = 0; std::string generated_text; // idx of draft tokens in the main batch @@ -380,6 +380,8 @@ struct server_context { void send_token_results(completion_token_outputs& results, server_slot& slot, int32_t n = 0); + void rewind_context(server_slot& slot, int32_t ban_pos); + void buffer_and_check_string_ban(server_slot& slot, completion_token_output& result); void update_allowlist_state(server_slot& slot);