// Copyright (c) OpenMMLab. All rights reserved. #pragma once #include #include #include #include #include #include #include "src/turbomind/core/core.h" #include "src/turbomind/core/interval.h" #include "src/turbomind/utils/metrics.h" namespace xgrammar { class GrammarMatcher; // forward declaration class CompiledGrammar; } // namespace xgrammar namespace turbomind { struct GenerationConfig { int max_new_tokens = 0; int min_new_tokens = 0; std::vector eos_ids; // only support single token id std::array, 2> stop_ids; // (token_id, offset) std::array, 2> bad_ids; int top_k = 1; float top_p = 0.f; float min_p = 0.f; float temperature = 1.f; float repetition_penalty = 1.f; uint64_t random_seed = 0; int output_logprobs = 0; enum OutType { kNone = 0, kAll = 1, kGeneration = 2 }; int output_last_hidden_state = 0; int output_logits = 0; }; std::ostream& operator<<(std::ostream& os, const GenerationConfig& c); struct SessionParam { uint64_t id; int step; bool start_flag; bool end_flag; bool kill_flag; }; struct RequestState { int status; int seq_len; }; struct AtomicRequestState { std::atomic data_; static_assert(std::atomic::is_always_lock_free); ~AtomicRequestState() { auto data = exchange(nullptr); } std::unique_ptr exchange(RequestState* data) { return std::unique_ptr{data_.exchange(data, std::memory_order_acq_rel)}; } }; struct Request { uint64_t id; // sequence id uint64_t unique_id; // monotonic increasing SessionParam session; GenerationConfig gen_cfg; bool stream_output; // reference to IO tensors TensorMap inputs; TensorMap outputs; // fast path for accessing common output buffers Tensor_ output_ids; Tensor_ sequence_length; std::function end_cb; std::atomic cancel_flag; std::function forward_cb; std::shared_ptr state; std::shared_ptr metrics; int ec = 0; // set when disabling conflicting requests enum { kOk = 0, kInvalid = 1, // Sequence not exist or both `start` & `stop` (instead of `end`) is set kConflict = 2, // Concurrent requests to the same sequence kBusy = 3, // Sequence is already running kInactive = 4, // Sequence to `stop` is not active kFail = 5, // Can't find sequence for `stop` request or internal error during inference kTooLong = 6, // history + prompt > session_len, kFinish = 7, kCancel = 8, kInconsistency = 9, // Inconsistent request parameters, e.g. prefix caching is not allowed in interactive mode kNoQueue = 10, // No queue available for submitting the request (in current process) }; std::shared_ptr grammar; std::shared_ptr matcher; }; void UpdateState(Request& r, int status, int seq_len); class Sequence; // Unlike `Request` which is shared by all local TP ranks, each rank has its own `RequestCache`. struct RequestCache { std::shared_ptr req; const Sequence* seq; // May be NULL in `Update` (seq get erased when req is done) const GenerationConfig& gen_cfg; RequestCache(std::shared_ptr r, const Sequence& s): req{std::move(r)}, seq{&s}, gen_cfg{req->gen_cfg} {} int status = Request::kOk; // These members may be opaque handles from individual modules (pointers to forward declared types), but we tend to // keep it simple as long as the complexity is manageable int* token_ids = nullptr; // currently the `output_ids` buf of request uint8_t* random_state = nullptr; int step0 = 0; // set at request init, constant, first prefill step int prompt_len = 0; // set at request init, constant, first decode step int max_seq_len = 0; // set at request init, constant int hidden_states_offset = 0; // set at request init, constant int logits_offset = 0; // set at request init, constant int seq_len = 0; // set at request init, updated per step int input_len = 0; // set at schedule (set to `seq.input_len`) int history_len = 0; // set at schedule (set to `seq.cache_len`) bool autoregres = false; // set at schedule, `seq_len` and `input_ids` taken from the engine bool generating = false; // set at schedule bool done = false; // set at cancel / update, is the request finished / canceled int alpha = 0; // pending growth of cache_len (draft_len + input_len) int beta = 0; // pending growth of seq_len (draft_len + {0,1}) float rope_base = 0.f; Interval output_hidden_states; Interval output_logits; }; template void serdes(Archive& ar, GenerationConfig& g) { // clang-format off ar & g.max_new_tokens; ar & g.min_new_tokens; ar & g.eos_ids; ar & g.stop_ids[0]; ar & g.stop_ids[1]; ar & g.bad_ids[0]; ar & g.bad_ids[1]; ar & g.top_k; ar & g.top_p; ar & g.min_p; ar & g.temperature; ar & g.repetition_penalty; ar & g.random_seed; ar & g.output_logprobs; ar & g.output_last_hidden_state; ar & g.output_logits; // clang-format on } template void save_req_output(Archive& ar, const TensorMap& map) { // clang-format off ar & map.size(); for (const auto& [k, t] : map) { TM_CHECK(t.device().type == kCPU); ar & k; ar & t.layout(); ar & t.dtype(); } // clang-format on } template void load_req_output(Archive& ar, TensorMap& map) { // clang-format off decltype(map.size()) size; ar & size; for (int i = 0; i < size; ++i) { std::string k; Layout layout; DataType dtype; ar & k; ar & layout; ar & dtype; map.emplace(std::move(k), Tensor{layout, dtype, kCPU}); } // clang-format on } template void serdes(Archive& ar, Request& r) { // clang-format off ar & r.id; ar & r.unique_id; ar & r.session; ar & r.gen_cfg; ar & r.stream_output; ar & r.inputs; if constexpr(Archive::is_loading) { load_req_output(ar, r.outputs); r.output_ids = r.outputs.at("output_ids"); r.sequence_length = r.outputs.at("sequence_length"); } else { save_req_output(ar, r.outputs); } ar & r.ec; // clang-format on } } // namespace turbomind