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

471 lines
16 KiB
C++

/*!
* Copyright (c) 2023-2025 by Contributors
* \file serve/config.h
*/
#ifndef MLC_LLM_SERVE_CONFIG_H_
#define MLC_LLM_SERVE_CONFIG_H_
#include <tvm/ffi/container/array.h>
#include <tvm/ffi/container/shape.h>
#include <tvm/ffi/extra/json.h>
#include <tvm/ffi/object.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/ffi/string.h>
#include <tvm/runtime/device_api.h>
#include <optional>
#include "../metadata/model.h"
#include "../support/result.h"
namespace mlc {
namespace llm {
namespace serve {
using namespace tvm;
using namespace tvm::runtime;
using tvm::ffi::Array;
using tvm::ffi::Object;
using tvm::ffi::ObjectPtr;
using tvm::ffi::ObjectRef;
using tvm::ffi::Optional;
using tvm::ffi::Shape;
using tvm::ffi::String;
/****************** GenerationConfig ******************/
/*! \brief The response format of a request. */
struct ResponseFormat {
String type = "text";
Optional<String> schema = std::nullopt;
/*!
* \brief Create debug config from JSON.
* \param config_json The json string for generation config
* \returns The converted result.
*/
static Result<ResponseFormat> FromJSON(const tvm::ffi::json::Object& config_json);
/**
* \return serialized json value of the config.
*/
tvm::ffi::json::Object AsJSON() const;
};
enum class SpecialRequestKind : int {
kNone = 0,
kQueryEngineMetrics = 1,
};
enum class DisaggRequestKind : int {
kNone = 0,
kPrepareReceive = 1,
kRemoteSend = 2,
kStartGeneration = 3,
};
/*! \brief Controls the behavior of inference with grammar constraint. */
enum class GrammarExecutionMode : int {
/*! \brief If grammar is provided for a request, use the grammar to constrain the output token. */
kConstraint = 0,
/*! \brief If grammar is provided for a request, not only constrain the output, but also use the
* jump-forward decoding to predict the next tokens. This is the default option. */
kJumpForward = 1,
};
/*! \brief The config for disaggregation requests. */
class DisaggConfig {
public:
DisaggRequestKind kind = DisaggRequestKind::kNone;
std::vector<Shape> kv_append_metadata;
// "kv_window_begin" and "kv_window_end" denote the KV interval of interests.
// "kv_window_end" supports Python style negative indexing.
// The concrete meaning varies for different special request kind:
// - For "prepare_receive", the begin is always 0, and "[0:end]" denotes
// the KV range to prefill on a prefill instance.
// - For "remote_send", "[begin:end]" means the KV range to compute prefill
// and send to the decode instance.
// - For "start_generation", the end is always nullopt, and "[begin:]" denotes
// the KV range to prefill locally on the decode instance.
std::optional<int> kv_window_begin = std::nullopt;
std::optional<int> kv_window_end = std::nullopt;
std::optional<int> dst_group_offset = std::nullopt;
static Result<DisaggConfig> FromJSON(const tvm::ffi::json::Object& config_json);
tvm::ffi::json::Object AsJSON() const;
};
/*! \brief The debug configuration of a request. */
class DebugConfig {
public:
bool ignore_eos = false;
bool pinned_system_prompt = false;
SpecialRequestKind special_request = SpecialRequestKind::kNone;
/*! \brief The grammar execution mode. */
GrammarExecutionMode grammar_execution_mode = GrammarExecutionMode::kJumpForward;
DisaggConfig disagg_config;
/*!
* \brief Create debug config from JSON.
* \param config_json The json string for generation config
* \returns The converted result.
*/
static Result<DebugConfig> FromJSON(const tvm::ffi::json::Object& config_json);
/**
* \return serialized json value of the config.
*/
tvm::ffi::json::Object AsJSON() const;
};
/*! \brief The generation configuration of a request. */
class GenerationConfigNode : public Object {
public:
int n = 1;
double temperature = 1.0;
double top_p = 1.0;
double frequency_penalty = 0.0;
double presence_penalty = 0.0;
double repetition_penalty = 1.0;
bool logprobs = false;
int top_logprobs = 0;
std::vector<std::pair<int, float>> logit_bias;
int seed;
// -1 means infinite
int max_tokens = -1;
Array<String> stop_strs;
std::vector<int> stop_token_ids;
ResponseFormat response_format;
DebugConfig debug_config;
tvm::ffi::json::Object AsJSON() const;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<GenerationConfigNode>();
}
static constexpr const bool _type_has_method_sequal_reduce = false;
static constexpr const bool _type_has_method_shash_reduce = false;
TVM_FFI_DECLARE_OBJECT_INFO("mlc.serve.GenerationConfig", GenerationConfigNode, Object);
};
class GenerationConfig : public ObjectRef {
public:
/*!
* \brief Run validation of generation config and ensure values are in bound.
* \return The validtaed Generation config or error.
*/
static Result<GenerationConfig> Validate(GenerationConfig cfg);
/*!
* \brief Create generation config from JSON.
* \param config_json The json string for generation config
* \param default_config The default config
*/
static Result<GenerationConfig> FromJSON(const tvm::ffi::json::Object& config_json,
const GenerationConfig& default_config);
/*! \brief Get the default generation config from the model config. */
static GenerationConfig GetDefaultFromModelConfig(const tvm::ffi::json::Object& json);
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(GenerationConfig, ObjectRef, GenerationConfigNode);
};
/****************** Engine config ******************/
/*!
* \brief The engine mode in MLC LLM.
* We provide three preset modes: "local", "interactive" and "server".
* The default mode is "local".
* The choice of mode decides the values of "max_batch_size", "max_total_sequence_length"
* and "prefill_chunk_size" when they are not explicitly specified.
* 1. Mode "local" refers to the local server deployment which has low
* request concurrency. So the max batch size will be set to 4, and max
* total sequence length and prefill chunk size are set to the context
* window size (or sliding window size) of the model.
* 2. Mode "interactive" refers to the interactive use of server, which
* has at most 1 concurrent request. So the max batch size will be set to 1,
* and max total sequence length and prefill chunk size are set to the context
* window size (or sliding window size) of the model.
* 3. Mode "server" refers to the large server use case which may handle
* many concurrent request and want to use GPU memory as much as possible.
* In this mode, we will automatically infer the largest possible max batch
* size and max total sequence length.
*/
enum class EngineMode : int {
kLocal = 0,
kInteractive = 1,
kServer = 2,
};
/*! \brief The prefix cache mode. */
enum class PrefixCacheMode : int {
/*! \brief Disable prefix cache. */
kDisable = 0,
/*! \brief The paged radix tree based prefix cache mode. */
kRadix = 1,
};
/*! \brief The speculative mode. */
enum class SpeculativeMode : int {
/*! \brief Disable speculative decoding. */
kDisable = 0,
/*! \brief The normal speculative decoding (small draft) mode. */
kSmallDraft = 1,
/*! \brief The eagle-style speculative decoding. */
kEagle = 2,
/*! \brief The Medusa-style speculative decoding. */
kMedusa = 3,
};
/*! \brief The prefill mode. */
enum class PrefillMode : int {
/*! \brief Only chunked prefill is enabled. */
kChunked = 0,
/*!
* \brief The hybrid prefill or split-fuse prefill is enabled, some decode steps will be fused
* to prefill
*/
kHybrid = 1,
};
class InferrableEngineConfig;
/*! \brief The configuration of engine execution config. */
class EngineConfigNode : public Object {
public:
/*************** Models ***************/
/*! \brief The path to the model directory. */
String model;
/*! \brief The path or identifier to the model library. */
String model_lib;
/*! \brief The path to the additional models' directories. */
Array<String> additional_models;
/*! \brief The path to the additional models' libraries. */
Array<String> additional_model_libs;
/*************** KV cache config and engine capacities ***************/
/*!
* \brief The engine mode in MLC LLM.
* \sa EngineMode
*/
EngineMode mode = EngineMode::kLocal;
/*!
* \brief A number in (0, 1) denoting the fraction of GPU memory used by the server in total.
* It is used to infer to maximum possible KV cache capacity.
* When it is unspecified, it defaults to 0.85.
* Under mode "local" or "interactive", the actual memory usage may be
* significantly smaller than this number. Under mode "server", the actual
* memory usage may be slightly larger than this number.
*/
float gpu_memory_utilization = 0.85;
/*! \brief The number of consecutive tokens handled in each page in paged KV cache. */
int kv_cache_page_size = 16;
/*!
* \brief The maximum number of sequences that are allowed to be
* processed by the KV cache at any time.
*/
int max_num_sequence = 4;
/*! \brief The maximum length allowed for a single sequence in the engine. */
int64_t max_total_sequence_length = 4096;
/*!
* \brief The maximum total number of tokens whose KV data are allowed
* to exist in the KV cache at any time.
*/
int64_t max_single_sequence_length = 4096;
/*! \brief The maximum total sequence length in a prefill. */
int64_t prefill_chunk_size = 1024;
/*! \brief The maximum history size for RNN state. KV cache does not need this. */
int max_history_size = 0;
/*************** Prefix cache ***************/
/*! \brief The prefix cache mode. */
PrefixCacheMode prefix_cache_mode = PrefixCacheMode::kRadix;
/*! \brief The maximum number of recycling sequences in prefix cache, default as max_num_sequence.
* And set 0 to disable prefix cache, set -1 to have infinite capacity prefix cache. */
int prefix_cache_max_num_recycling_seqs = -1;
/*************** Speculative decoding ***************/
/*! \brief The speculative mode. */
SpeculativeMode speculative_mode = SpeculativeMode::kDisable;
/*!
* \brief The number of tokens to generate in speculative proposal (draft).
* Being 0 means to enable adaptive speculative mode, where the draft length
* will be automatically adjusted based on engine state.
*/
int spec_draft_length = 0;
/*! \brief The number of tokens to generate in speculative tree decoding */
int spec_tree_width = 1;
/*************** Prefill mode ***************/
/*! \brief The prefill mode. */
PrefillMode prefill_mode = PrefillMode::kHybrid;
/*************** Debug ***************/
bool verbose = false;
String AsJSONString() const;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<EngineConfigNode>();
}
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.EngineConfig", EngineConfigNode, Object);
};
class EngineConfig : public ObjectRef {
public:
/*! \brief Create EngineConfig from JSON object and inferred config. */
static EngineConfig FromJSONAndInferredConfig(const tvm::ffi::json::Object& json,
const InferrableEngineConfig& inferred_config);
/*!
* \brief Get all the models and model libs from the JSON string for engine initialization.
* \return The parsed models/model libs from config or error message.
*/
static Result<std::vector<std::pair<std::string, std::string>>>
GetModelsAndModelLibsFromJSONString(const std::string& json_str);
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(EngineConfig, ObjectRef, EngineConfigNode);
};
/*! \brief A subset of engine config that is inferrable. */
struct InferrableEngineConfig {
std::optional<int64_t> max_num_sequence;
std::optional<int64_t> max_total_sequence_length;
std::optional<int64_t> max_single_sequence_length;
std::optional<int64_t> prefill_chunk_size;
std::optional<int64_t> max_history_size;
/*! \brief Infer the config for KV cache from a given initial config. */
static Result<InferrableEngineConfig> InferForKVCache(
EngineMode mode, Device device, double gpu_memory_utilization,
const std::vector<tvm::ffi::json::Object>& model_configs,
const std::vector<ModelMetadata>& model_metadata, InferrableEngineConfig init_config,
bool verbose);
/*! \brief Infer the config for RNN state from a given initial config. */
static Result<InferrableEngineConfig> InferForRNNState(
EngineMode mode, Device device, double gpu_memory_utilization,
const std::vector<tvm::ffi::json::Object>& model_configs,
const std::vector<ModelMetadata>& model_metadata, InferrableEngineConfig init_config,
bool verbose);
};
/****************** Config utils ******************/
/*! \brief Check if the models use KV cache or RNN state. */
Result<bool> ModelsUseKVCache(const std::vector<tvm::ffi::json::Object>& model_configs);
inline std::string EngineModeToString(EngineMode mode) {
if (mode == EngineMode::kLocal) {
return "local";
} else if (mode == EngineMode::kInteractive) {
return "interactive";
} else if (mode == EngineMode::kServer) {
return "server";
} else {
LOG(FATAL) << "Invalid engine mode: " << static_cast<int>(mode);
throw;
}
}
inline EngineMode EngineModeFromString(const std::string& mode) {
if (mode == "local") {
return EngineMode::kLocal;
} else if (mode == "interactive") {
return EngineMode::kInteractive;
} else if (mode == "server") {
return EngineMode::kServer;
} else {
LOG(FATAL) << "Invalid engine mode string: " << mode;
throw;
}
}
inline std::string PrefixCacheModeToString(PrefixCacheMode prefix_cache_mode) {
if (prefix_cache_mode == PrefixCacheMode::kDisable) {
return "disable";
} else if (prefix_cache_mode == PrefixCacheMode::kRadix) {
return "radix";
} else {
LOG(FATAL) << "Invalid prefix cache mode: " << static_cast<int>(prefix_cache_mode);
}
}
inline PrefixCacheMode PrefixCacheModeFromString(const std::string& prefix_cache_mode) {
if (prefix_cache_mode == "disable") {
return PrefixCacheMode::kDisable;
} else if (prefix_cache_mode == "radix") {
return PrefixCacheMode::kRadix;
} else {
LOG(FATAL) << "Invalid prefix cache mode string: " << prefix_cache_mode;
throw;
}
}
inline std::string SpeculativeModeToString(SpeculativeMode speculative_mode) {
if (speculative_mode == SpeculativeMode::kDisable) {
return "disable";
} else if (speculative_mode == SpeculativeMode::kSmallDraft) {
return "small_draft";
} else if (speculative_mode == SpeculativeMode::kEagle) {
return "eagle";
} else if (speculative_mode == SpeculativeMode::kMedusa) {
return "medusa";
} else {
LOG(FATAL) << "Invalid speculative mode: " << static_cast<int>(speculative_mode);
}
}
inline SpeculativeMode SpeculativeModeFromString(const std::string& speculative_mode) {
if (speculative_mode == "disable") {
return SpeculativeMode::kDisable;
} else if (speculative_mode == "small_draft") {
return SpeculativeMode::kSmallDraft;
} else if (speculative_mode == "eagle") {
return SpeculativeMode::kEagle;
} else if (speculative_mode == "medusa") {
return SpeculativeMode::kMedusa;
} else {
LOG(FATAL) << "Invalid speculative mode string: " << speculative_mode;
throw;
}
}
inline std::string PrefillModeToString(PrefillMode prefill_mode) {
if (prefill_mode == PrefillMode::kChunked) {
return "chunked";
} else if (prefill_mode == PrefillMode::kHybrid) {
return "hybrid";
} else {
LOG(FATAL) << "Invalid prefill mode: " << static_cast<int>(prefill_mode);
}
}
inline PrefillMode PrefillModeFromString(const std::string& prefill_mode) {
if (prefill_mode == "chunked") {
return PrefillMode::kChunked;
} else if (prefill_mode == "hybrid") {
return PrefillMode::kHybrid;
} else {
LOG(FATAL) << "Invalid prefill mode string: " << prefill_mode;
throw;
}
}
} // namespace serve
} // namespace llm
} // namespace mlc
#endif // MLC_LLM_SERVE_CONFIG_H_