Files
wehub-resource-sync 770d92cb1f
Lint / lint (push) Has been cancelled
Build Docs / Deploy Docs (push) Has been cancelled
Windows CI / Windows (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:23:58 +08:00

419 lines
18 KiB
C++

/*!
* Copyright (c) 2023-2025 by Contributors
* \file serve/model.h
* \brief The header for runtime module of LLM functions (prefill/decode/etc.)
*/
#ifndef MLC_LLM_SERVE_MODEL_H_
#define MLC_LLM_SERVE_MODEL_H_
#include <tvm/ffi/extra/json.h>
#include <tvm/ffi/string.h>
#include <tvm/runtime/tensor.h>
#include "../base.h"
#include "../support/result.h"
#include "config.h"
#include "draft_token_workspace_manager.h"
#include "event_trace_recorder.h"
#include "function_table.h"
#include "logit_processor.h"
#include "sampler/sampler.h"
namespace mlc {
namespace llm {
namespace serve {
using tvm::Device;
using namespace tvm::runtime;
using tvm::ffi::Object;
using tvm::ffi::ObjectRef;
using tvm::ffi::Shape;
// Declare the sampler class for `Model::CreateSampler`.
class Sampler;
/*!
* \brief The workspace tensors that may be shared across different
* calls to Model. For example, the prefill action use the `embeddings`
* workspace for the concatenated embeddings of different sequences.
* The workspace tensor is created by Model but owned by engine.
*/
struct ModelWorkspace {
/*!
* \brief The embedding tensor. It can be either an Tensor when tensor
* model parallelism is not enabled, or a DRef when using tensor model parallelism.
*/
ObjectRef embeddings{nullptr};
/*!
* \brief The hidden_states tensor for the current batch. It can be either an Tensor when tensor
* model parallelism is not enabled, or a DRef when using tensor model parallelism.
*/
ObjectRef hidden_states{nullptr};
/*!
* \brief The draft token probabilities tensor for the current batch.
*/
Tensor draft_probs{nullptr};
/*!
* \brief The hidden_states tensor storing the hidden_states of draft tokens of all requests.
*/
ObjectRef draft_hidden_states_storage{nullptr};
/*!
* \brief The draft token probabilities tensor storing the probabilities of draft tokens of all
* requests.
*/
Tensor draft_probs_storage{nullptr};
};
/*!
* \brief The model module for LLM functions.
* It runs an LLM, and has an internal KV cache that maintains
* the history KV values of all processed tokens.
*
* It contains the following functions:
*
* Model related:
* - "token_embed": take token ids as input and return the embeddings,
* - "batch_prefill": take embedding of a single sequence
* as input, forward the embedding through LLM and return the logits,
* - "decode": take the embeddings of the last-committed token of an
* entire batch as input, forward through LLM and return the logits
* for all sequences in the batch,
* - "softmax_with_temperature": take logits and temperatures, return
* probabilities.
*
* KV cache related:
* - "create_kv_cache": create the KV cache for this module,
* - "add_new_sequence": add (declare) a new sequence in the KV cache,
* - "remove_sequence": remove a sequence from KV cache.
*
* ... and some other auxiliary functions.
*/
class ModelObj : public Object {
public:
/*********************** Model Computation ***********************/
/*!
* \brief Compute embeddings for the input token ids.
* When the input destination pointer is defined, it in-place writes the
* embedding into the input destination array at the given offset.
* Otherwise, the embeddings will be directly returned back.
* \param token_ids The token ids to compute embedding for.
* \param dst The destination array of the embedding lookup.
* \param offset The token offset where the computed embeddings will be written
* into the destination array.
* \return The updated destination embedding array or the computed embeddings.
* \note When `dst` is undefined, we require `offset` to be 0.
*/
virtual ObjectRef TokenEmbed(Shape batch_token_ids, ObjectRef* dst = nullptr, int offset = 0) = 0;
/*!
* \brief Compute embeddings for the input image.
* \param image The image to compute embedding for.
* \return The computed embeddings.
*/
virtual ObjectRef ImageEmbed(const Tensor& image, ObjectRef* dst = nullptr, int offset = 0) = 0;
/*!
* \brief Fuse the embeddings and hidden_states.
* \param embeddings The embedding of the input to be prefilled.
* \param previous_hidden_states The hidden_states from previous base model.
* \param batch_size Batch size.
* \param seq_len Sequence length.
* \return The fused hidden_states.
*/
virtual ObjectRef FuseEmbedHidden(const ObjectRef& embeddings,
const ObjectRef& previous_hidden_states, int batch_size,
int seq_len) = 0;
/*!
* \brief Return if the model has lm_head so that we can get logits.
*/
virtual bool CanGetLogits() = 0;
/*!
* \brief Compute logits for last hidden_states.
* \param last_hidden_states The last hidden_states to compute logits for.
* \return The computed logits.
*/
virtual Tensor GetLogits(const ObjectRef& last_hidden_states) = 0;
virtual Array<Tensor> GetMultiStepLogits(const ObjectRef& last_hidden_states) = 0;
/*!
* \brief Batch prefill function. Embedding in, logits out.
* The embedding order of sequences in `embedding_arr` follows
* the order of `seq_ids`.
* \param embeddings The embedding of the input to be prefilled.
* \param seq_id The id of the sequence in the KV cache.
* \param lengths The length of each sequence to prefill.
* \return The logits for the next token.
*/
virtual Tensor BatchPrefill(const ObjectRef& embeddings, const std::vector<int64_t>& seq_ids,
const std::vector<int>& lengths) = 0;
/*!
* \brief Batch prefill function. Input hidden_states are computed from
* input embeddings and previous hidden_states, output last hidden_states.
* \param hidden_states The hidden_states of the input to be prefilled.
* \param seq_id The id of the sequence in the KV cache.
* \param lengths The length of each sequence to prefill.
* \return The hidden_states for the next token.
*/
virtual ObjectRef BatchPrefillToLastHidden(const ObjectRef& hidden_states,
const std::vector<int64_t>& seq_ids,
const std::vector<int>& lengths) = 0;
/*!
* \brief Batch decode function. Embedding in, logits out.
* The embedding order of sequences in `embeddings` follows
* the order of `seq_ids`.
* \param embeddings The embedding of last generated token in the entire batch.
* \param seq_id The id of the sequence in the KV cache.
* \return The logits for the next token for each sequence in the batch.
*/
virtual Tensor BatchDecode(const ObjectRef& embeddings, const std::vector<int64_t>& seq_ids) = 0;
virtual Tensor BatchTreeDecode(const ObjectRef& embeddings, const std::vector<int64_t>& seq_ids,
const std::vector<int>& lengths,
const std::vector<int64_t>& token_tree_parent_ptr) = 0;
/*!
* \brief Batch decode function. Input hidden_states are computed from
* input embeddings and previous hidden_states, output last hidden_states.
* \param hidden_states The hidden_states of last generated token in the entire batch.
* \param seq_id The id of the sequence in the KV cache.
* \return The hidden_states for the next token for each sequence in the batch.
*/
virtual ObjectRef BatchDecodeToLastHidden(const ObjectRef& hidden_states,
const std::vector<int64_t>& seq_ids) = 0;
/*!
* \brief Batch verify function. Embedding in, logits out.
* \param embeddings The embedding of the input to be verified.
* \param seq_id The id of the sequence in the KV cache.
* \param lengths The length of each sequence to verify.
* \param token_tree_parent_ptr The parent pointers of the token tree.
* It's size is the sum of "lengths". It contains a batch of independent trees,
* one for each sequence. Parent being "-1" means the node is a root.
* \return The logits for the draft token for each sequence in the batch.
* \note The function runs for **every** sequence in the batch.
* That is to say, it does not accept "running a verify step for a subset
* of the full batch".
*/
virtual Tensor BatchVerify(const ObjectRef& embeddings, const std::vector<int64_t>& seq_ids,
const std::vector<int>& lengths,
const std::vector<int64_t>& token_tree_parent_ptr) = 0;
/*!
* \brief Batch verify function. Input hidden_states are computed from
* input embeddings and previous hidden_states, output last hidden_states.
* \param hidden_states The hidden_states of the input to be verified.
* \param seq_id The id of the sequence in the KV cache.
* \param lengths The length of each sequence to verify.
* \param token_tree_parent_ptr The parent pointers of the token tree.
* It's size is the sum of "lengths". It contains a batch of independent trees,
* one for each sequence. Parent being "-1" means the node is a root.
* \return The hidden_states for the draft token for each sequence in the batch.
* \note The function runs for **every** sequence in the batch.
* That is to say, it does not accept "running a verify step for a subset
* of the full batch".
*/
virtual ObjectRef BatchVerifyToLastHidden(const ObjectRef& hidden_states,
const std::vector<int64_t>& seq_ids,
const std::vector<int>& lengths,
const std::vector<int64_t>& token_tree_parent_ptr) = 0;
/*********************** KV Cache Management ***********************/
/*!
* \brief Create the KV cache inside the model with regard to the input config.
* \param page_size The number of consecutive tokens handled in each page in paged KV cache.
* \param max_num_sequence The maximum number of sequences that are allowed to be
* processed by the KV cache at any time.
* \param max_total_sequence_length The maximum length allowed for a single sequence
* in the engine.
* \param prefill_chunk_size The maximum total number of tokens whose KV data
* are allowed to exist in the KV cache at any time.
* \param max_history_size The maximum history size for RNN state to roll back.
* The KV cache does not need this.
* \param prefix_cache_max_num_recycling_seqs The maximum number of recycling
* sequences kept by prefix cache. For hybrid models, the RNN state needs
* extra slots beyond max_num_sequence to hold these recycling sequences
* simultaneously with active sequences.
*/
virtual void CreateKVCache(int page_size, int max_num_sequence, int64_t max_total_sequence_length,
int64_t prefill_chunk_size, int max_history_size,
int prefix_cache_max_num_recycling_seqs = 0) = 0;
/*! \brief Add a new sequence with the given sequence id to the KV cache. */
virtual void AddNewSequence(int64_t seq_id) = 0;
/*! \brief Fork a sequence from a given parent sequence. */
virtual void ForkSequence(int64_t parent_seq_id, int64_t child_seq_id, int64_t fork_pos = -1) = 0;
/*! \brief Remove the given sequence from the KV cache in the model. */
virtual void RemoveSequence(int64_t seq_id) = 0;
/*! \brief Pop out N pages from KV cache. */
virtual void PopNFromKVCache(int64_t seq_id, int num_tokens) = 0;
/*!
* \brief Commit the accepted token tree nodes to KV cache.
* The unaccepted token tree node will be removed from KV cache.
* This is usually used in the verification stage of speculative decoding.
*/
virtual void CommitAcceptedTokenTreeNodesToKVCache(
const std::vector<int64_t>& seq_ids, const std::vector<int64_t>& accepted_leaf_indices) = 0;
/*!
* \brief Enabling sliding window for the given sequence.
* It is a no-op if the model does not support sliding window.
* \note Given this operation is tied with the underlying KV cache,
* we add the function in Model interface to expose this for Engine.
* This may be optimized with decoupling KV cache and Model in the future.
*/
virtual void EnableSlidingWindowForSeq(int64_t seq_id) = 0;
/*! \brief Prepare for the disaggregation KV data receive for the specified sequence and length.*/
virtual Shape DisaggPrepareKVRecv(int64_t seq_id, int length) = 0;
/*! \brief Prepare for the disaggregation KV data send for the specified sequence and length.*/
virtual void DisaggMarkKVSend(int64_t seq_id, int begin_pos, Shape compressed_kv_append_metadata,
int dst_group_offset) = 0;
/************** Raw Info Query **************/
/*! \brief Return the metadata JSON object of the model. */
virtual ModelMetadata GetMetadata() const = 0;
/*! \brief Get the number of available pages in KV cache. */
virtual int GetNumAvailablePages() const = 0;
/*! \brief Get the current total sequence length in the KV cache. */
virtual int GetCurrentTotalSequenceLength() const = 0;
/*********************** Utilities ***********************/
/*! \brief Load the model's weight parameters, which is not loaded at construction time. */
virtual void LoadParams() = 0;
/*!
* \brief Set the maximum number of sequences to be processed for the model,
* which is not initialized at construction time.
*/
virtual void SetMaxNumSequence(int max_num_sequence) = 0;
/*!
* \brief Set the prefill chunk size for the model,
* which is not initialized at construction time.
*/
virtual void SetPrefillChunkSize(int prefill_chunk_size) = 0;
/*! \brief Create a logit processor from this model. */
virtual LogitProcessor CreateLogitProcessor(int max_num_token,
Optional<EventTraceRecorder> trace_recorder) = 0;
/*! \brief Create a sampler from this model. */
virtual Sampler CreateSampler(int max_num_sample, int num_models,
Optional<EventTraceRecorder> trace_recorder) = 0;
/*!
* \brief Estimate number of CPU units required to drive the model
* executing during TP.
* \note This normally equals to the number of TP shards (or 0 if
* the model does not use TP) and can be used to hint runtime to
* avoid overuse cores in other places.
*/
virtual int EstimateHostCPURequirement() const = 0;
/*! \brief Get the sliding window size of the model. "-1" means sliding window is not enabled. */
virtual int GetSlidingWindowSize() const = 0;
/*! \brief Get the attention sink size of the model. */
virtual int GetAttentionSinkSize() const = 0;
/*! \brief Allocate an embedding tensor with the prefill chunk size. */
virtual ObjectRef AllocEmbeddingTensor() = 0;
/*! \brief Allocate an hidden_states tensor with the prefill chunk size. */
virtual ObjectRef AllocHiddenStatesTensor() = 0;
/*! \brief Reset the model KV cache and other metrics. */
virtual void Reset() = 0;
/*********************** Utilities for speculative decoding. ***********************/
virtual DraftTokenWorkspaceManager CreateDraftTokenWorkspaceManager(int max_num_token) = 0;
/*! \brief Gather the hidden_states of the given indices and in-place update the dst tensor. */
virtual ObjectRef GatherHiddenStates(const ObjectRef& input, const std::vector<int>& indices,
ObjectRef* dst) = 0;
/*! \brief Scatter the hidden_states of the given indices to the dst tensor. */
virtual void ScatterHiddenStates(const ObjectRef& input, const std::vector<int>& indices,
ObjectRef* dst) = 0;
/*! \brief Gather the draft token probabilities of the given indices and in-place update the dst
* tensor. */
virtual Tensor GatherDraftProbs(const Tensor& input, const std::vector<int>& indices,
Tensor* dst) = 0;
/*! \brief Scatter the draft token probabilities of the given indices to the dst tensor. */
virtual void ScatterDraftProbs(const Tensor& input, const std::vector<int>& indices,
Tensor* dst) = 0;
/************** Debug/Profile **************/
/*! \brief Call the given global function on all workers. Only for debug purpose. */
virtual void DebugCallFuncOnAllAllWorker(const String& func_name, Optional<String> func_args) = 0;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<ModelObj>();
}
static constexpr const bool _type_has_method_sequal_reduce = false;
static constexpr const bool _type_has_method_shash_reduce = false;
static constexpr const bool _type_mutable = true;
TVM_FFI_DECLARE_OBJECT_INFO("mlc.serve.Model", ModelObj, Object);
};
class Model : public ObjectRef {
public:
/*!
* \brief Create the runtime module for LLM functions.
* \param reload_lib_path The model library path.
* \param model_path The path to the model weight parameters.
* \param model_config The model config json object.
* \param device The device to run the model on.
* \param session The session to run the model on.
* \param num_shards The number of tensor parallel shards of the model.
* \param num_stages The number of pipeline parallel stages of the model.
* \param trace_enabled A boolean indicating whether tracing is enabled.
* \return The created runtime module.
*/
static Model Create(String reload_lib_path, String model_path,
const tvm::ffi::json::Object& model_config, DLDevice device,
const Optional<Session>& session, int num_shards, int num_stages,
bool trace_enabled);
/*!
* Load the model config from the given model path.
* \param model_path The path to the model weight parameters.
* \return The model config json object.
*/
static Result<tvm::ffi::json::Object> LoadModelConfig(const String& model_path);
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(Model, ObjectRef, ModelObj);
};
} // namespace serve
} // namespace llm
} // namespace mlc
#endif // MLC_LLM_SERVE_MODEL_H_