chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
/*!
|
||||
* Copyright (c) 2023-2025 by Contributors
|
||||
* \file support/debug_utils.h
|
||||
* \brief Tools for debug purposes.
|
||||
*/
|
||||
#ifndef MLC_LLM_SUPPORT_DEBUG_UTILS_H_
|
||||
#define MLC_LLM_SUPPORT_DEBUG_UTILS_H_
|
||||
|
||||
#include "../tokenizers/tokenizers.h"
|
||||
|
||||
namespace mlc {
|
||||
namespace llm {
|
||||
|
||||
/*! \brief A registry for debug information. */
|
||||
class DebugRegistry {
|
||||
public:
|
||||
static DebugRegistry* Global() {
|
||||
static DebugRegistry reg;
|
||||
return ®
|
||||
}
|
||||
|
||||
// Tokenizer information, helpful for converting token id to token string in debugging
|
||||
Tokenizer tokenizer;
|
||||
};
|
||||
|
||||
/*! \brief Register the tokenizer to the global tokenizer registry. */
|
||||
inline void DebugRegisterTokenizer(const Tokenizer& tokenizer) {
|
||||
DebugRegistry::Global()->tokenizer = tokenizer;
|
||||
}
|
||||
|
||||
/*! \brief Get the registered tokenizer from the global tokenizer registry. */
|
||||
inline Tokenizer DebugGetTokenizer() { return DebugRegistry::Global()->tokenizer; }
|
||||
|
||||
} // namespace llm
|
||||
} // namespace mlc
|
||||
|
||||
#endif // MLC_LLM_SUPPORT_DEBUG_UTILS_H_
|
||||
@@ -0,0 +1,147 @@
|
||||
/*!
|
||||
* Copyright (c) 2023-2025 by Contributors
|
||||
* \file support/dynamic_bitset.h
|
||||
* \brief The header for utilities used in grammar-guided generation.
|
||||
*/
|
||||
#ifndef MLC_LLM_SUPPORT_DYNAMIC_BITSET_H_
|
||||
#define MLC_LLM_SUPPORT_DYNAMIC_BITSET_H_
|
||||
|
||||
#include <tvm/runtime/logging.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
namespace mlc {
|
||||
namespace llm {
|
||||
|
||||
/*!
|
||||
* \brief A bitset whose length is specified at runtime. Note the size cannot be changed after
|
||||
* construction.
|
||||
* \details The buffer of the bitset is a uint32_t array. There are two uses for this class:
|
||||
* - When passing nullptr to data, it maintains an internal buffer for the bitset.
|
||||
* - When passing a pointer to a buffer with enough size, it uses the external buffer for the
|
||||
* bitset.
|
||||
*/
|
||||
class DynamicBitset {
|
||||
public:
|
||||
/*!
|
||||
* \brief Calculate the minimal size of the uint32_t buffer for the bitset with the given size.
|
||||
* \param element_size The size of the bitset.
|
||||
* \return The minimal buffer size.
|
||||
*/
|
||||
static int CalculateBufferSize(int element_size) { return (element_size + 31) / 32; }
|
||||
|
||||
/*!
|
||||
* \brief Construct a empty bitset. This object should be assigned to a valid bitset before using.
|
||||
*/
|
||||
DynamicBitset() : size_(0), buffer_size_(0), data_(nullptr), is_internal_(true) {}
|
||||
|
||||
/*!
|
||||
* \brief Construct a bitset with the given size.
|
||||
* \param size The size of the bitset.
|
||||
* \param data The buffer for the bitset. If nullptr, the bitset will maintain an internal buffer.
|
||||
*/
|
||||
DynamicBitset(int size, uint32_t* data = nullptr)
|
||||
: size_(size), buffer_size_(CalculateBufferSize(size)) {
|
||||
if (data == nullptr) {
|
||||
internal_buffer_.resize(buffer_size_, 0);
|
||||
data_ = internal_buffer_.data();
|
||||
is_internal_ = true;
|
||||
} else {
|
||||
data_ = data;
|
||||
is_internal_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Copy assignment. */
|
||||
DynamicBitset& operator=(const DynamicBitset& other) {
|
||||
TVM_FFI_DCHECK(is_internal_ || size_ >= other.size_)
|
||||
<< "Expanding bitset size is not allowed when the "
|
||||
"memory of the bitset is externally managed";
|
||||
size_ = other.size_;
|
||||
buffer_size_ = other.buffer_size_;
|
||||
if (is_internal_) {
|
||||
internal_buffer_.reserve(buffer_size_);
|
||||
data_ = internal_buffer_.data();
|
||||
}
|
||||
if (data_ != other.data_) {
|
||||
std::memcpy(data_, other.data_, buffer_size_ * sizeof(uint32_t));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*! \brief Move assignment. */
|
||||
DynamicBitset& operator=(DynamicBitset&& other) {
|
||||
size_ = other.size_;
|
||||
buffer_size_ = other.buffer_size_;
|
||||
is_internal_ = other.is_internal_;
|
||||
if (is_internal_) {
|
||||
internal_buffer_ = std::move(other.internal_buffer_);
|
||||
data_ = internal_buffer_.data();
|
||||
} else {
|
||||
data_ = other.data_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*! \brief Get the value of the bit at the given index. */
|
||||
bool operator[](int index) const {
|
||||
TVM_FFI_DCHECK(data_ && index >= 0 && index < size_);
|
||||
return (data_[index / 32] >> (index % 32)) & 1;
|
||||
}
|
||||
|
||||
/*! \brief Get the size of the bitset. */
|
||||
int Size() const { return size_; }
|
||||
|
||||
/*! \brief Set the whole bitset to true. */
|
||||
void Set() {
|
||||
TVM_FFI_DCHECK(data_);
|
||||
std::memset(data_, 0xFF, buffer_size_ * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
/*! \brief Set the bit at the given index to the given value. */
|
||||
void Set(int index, bool value = true) {
|
||||
TVM_FFI_DCHECK(data_ && index >= 0 && index < size_);
|
||||
if (value) {
|
||||
data_[index / 32] |= 1 << (index % 32);
|
||||
} else {
|
||||
data_[index / 32] &= ~(1 << (index % 32));
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Set the whole bitset to false. */
|
||||
void Reset() {
|
||||
TVM_FFI_DCHECK(data_);
|
||||
std::memset(data_, 0, buffer_size_ * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
/*! \brief Set the bit at the given index to false. */
|
||||
void Reset(int index) { Set(index, false); }
|
||||
|
||||
/*! \brief Perform a bitwise OR operation between the current bitset and another bitset. */
|
||||
DynamicBitset& operator|=(const DynamicBitset& other) {
|
||||
TVM_FFI_DCHECK(buffer_size_ <= other.buffer_size_);
|
||||
for (int i = 0; i < buffer_size_; ++i) {
|
||||
data_[i] |= other.data_[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
// The size of the bitset.
|
||||
int size_;
|
||||
// The size of the buffer.
|
||||
int buffer_size_;
|
||||
// The buffer for the bitset.
|
||||
uint32_t* data_;
|
||||
// The internal buffer. It is empty if not needed.
|
||||
std::vector<uint32_t> internal_buffer_;
|
||||
// Whether the buffer is internally managed.
|
||||
bool is_internal_;
|
||||
};
|
||||
|
||||
} // namespace llm
|
||||
} // namespace mlc
|
||||
|
||||
#endif // MLC_LLM_SUPPORT_DYNAMIC_BITSET_H_
|
||||
@@ -0,0 +1,211 @@
|
||||
/*!
|
||||
* Copyright (c) 2023-2025 by Contributors
|
||||
* \file support/encoding.cc
|
||||
*/
|
||||
#include "encoding.h"
|
||||
|
||||
#include <tvm/runtime/logging.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace mlc {
|
||||
namespace llm {
|
||||
|
||||
std::string PrintAsUTF8(TCodepoint codepoint) {
|
||||
TVM_FFI_ICHECK(codepoint <= 0x10FFFF) << "Invalid codepoint: " << codepoint;
|
||||
std::string utf8;
|
||||
if (codepoint <= 0x7F) {
|
||||
// 1-byte sequence
|
||||
utf8 += static_cast<char>(codepoint);
|
||||
} else if (codepoint <= 0x7FF) {
|
||||
// 2-byte sequence
|
||||
utf8 += static_cast<char>(0xC0 | ((codepoint >> 6) & 0x1F));
|
||||
utf8 += static_cast<char>(0x80 | (codepoint & 0x3F));
|
||||
} else if (codepoint <= 0xFFFF) {
|
||||
// 3-byte sequence
|
||||
utf8 += static_cast<char>(0xE0 | ((codepoint >> 12) & 0x0F));
|
||||
utf8 += static_cast<char>(0x80 | ((codepoint >> 6) & 0x3F));
|
||||
utf8 += static_cast<char>(0x80 | (codepoint & 0x3F));
|
||||
} else {
|
||||
// 4-byte sequence
|
||||
utf8 += static_cast<char>(0xF0 | ((codepoint >> 18) & 0x07));
|
||||
utf8 += static_cast<char>(0x80 | ((codepoint >> 12) & 0x3F));
|
||||
utf8 += static_cast<char>(0x80 | ((codepoint >> 6) & 0x3F));
|
||||
utf8 += static_cast<char>(0x80 | (codepoint & 0x3F));
|
||||
}
|
||||
return utf8;
|
||||
}
|
||||
|
||||
std::string PrintAsEscaped(
|
||||
TCodepoint codepoint,
|
||||
const std::unordered_map<TCodepoint, std::string>& additional_escape_map) {
|
||||
static const std::unordered_map<TCodepoint, std::string> kCodepointToEscape = {
|
||||
{'\'', "\\\'"}, {'\"', "\\\""}, {'\?', "\\\?"}, {'\\', "\\\\"}, {'\a', "\\a"},
|
||||
{'\b', "\\b"}, {'\f', "\\f"}, {'\n', "\\n"}, {'\r', "\\r"}, {'\t', "\\t"},
|
||||
{'\v', "\\v"}, {'\0', "\\0"}, {'\x1B', "\\e"}};
|
||||
|
||||
if (auto it = additional_escape_map.find(codepoint); it != additional_escape_map.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
if (auto it = kCodepointToEscape.find(codepoint); it != kCodepointToEscape.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
if (codepoint >= 0x20 && codepoint <= 0x7E) {
|
||||
return std::string({static_cast<char>(codepoint)});
|
||||
}
|
||||
|
||||
// convert codepoint to hex
|
||||
char prefix = codepoint <= 0xFF ? 'x' : codepoint <= 0xFFFF ? 'u' : 'U';
|
||||
int width = codepoint <= 0xFF ? 2 : codepoint <= 0xFFFF ? 4 : 8;
|
||||
std::stringstream ss;
|
||||
ss << std::setfill('0') << std::setw(width) << std::hex << codepoint;
|
||||
auto hex = ss.str();
|
||||
return std::string("\\") + prefix + hex;
|
||||
}
|
||||
|
||||
std::string PrintAsEscaped(uint8_t raw_char) { return PrintAsEscaped(raw_char); }
|
||||
|
||||
std::string PrintAsEscaped(std::string raw_str) {
|
||||
std::string res;
|
||||
auto codepoints = ParseUTF8(raw_str.c_str(), UTF8ErrorPolicy::kReturnByte);
|
||||
for (auto c : codepoints) {
|
||||
res += PrintAsEscaped(c);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::tuple<bool, int, TCodepoint> HandleUTF8FirstByte(uint8_t byte) {
|
||||
static const std::array<int8_t, 5> kFirstByteMask = {0x00, 0x7F, 0x1F, 0x0F, 0x07};
|
||||
// clang-format off
|
||||
static const std::array<int, 256> kUtf8Bytes = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
};
|
||||
// clang-format on
|
||||
auto num_bytes = kUtf8Bytes[static_cast<uint8_t>(byte)];
|
||||
if (num_bytes == -1) {
|
||||
return {false, 0, 0};
|
||||
}
|
||||
return {true, num_bytes, byte & kFirstByteMask[num_bytes]};
|
||||
}
|
||||
|
||||
std::pair<TCodepoint, const char*> ParseNextUTF8(const char* utf8, UTF8ErrorPolicy error_policy) {
|
||||
auto [accepted, num_bytes, res] = HandleUTF8FirstByte(utf8[0]);
|
||||
if (accepted) {
|
||||
for (int i = 1; i < num_bytes; ++i) {
|
||||
if (utf8[i] == 0 || (static_cast<uint8_t>(utf8[i]) & 0xC0) != 0x80) {
|
||||
// invalid utf8
|
||||
accepted = false;
|
||||
break;
|
||||
}
|
||||
res = (res << 6) | (static_cast<uint8_t>(utf8[i]) & 0x3F);
|
||||
}
|
||||
}
|
||||
|
||||
if (!accepted) {
|
||||
// invalid utf8
|
||||
if (error_policy == UTF8ErrorPolicy::kReturnInvalid) {
|
||||
return {CharHandlingError::kInvalidUTF8, utf8};
|
||||
} else {
|
||||
return {static_cast<unsigned char>(utf8[0]), utf8 + 1};
|
||||
}
|
||||
}
|
||||
|
||||
return {res, utf8 + num_bytes};
|
||||
}
|
||||
|
||||
std::vector<TCodepoint> ParseUTF8(const char* utf8, UTF8ErrorPolicy error_policy) {
|
||||
std::vector<TCodepoint> codepoints;
|
||||
while (*utf8 != 0) {
|
||||
TCodepoint codepoint;
|
||||
std::tie(codepoint, utf8) = ParseNextUTF8(utf8, error_policy);
|
||||
if (codepoint == CharHandlingError::kInvalidUTF8) {
|
||||
return {codepoint};
|
||||
}
|
||||
codepoints.push_back(codepoint);
|
||||
}
|
||||
return codepoints;
|
||||
}
|
||||
|
||||
inline int HexCharToInt(char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
return c - 'a' + 10;
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
return c - 'A' + 10;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<TCodepoint, const char*> ParseNextUTF8OrEscaped(
|
||||
const char* utf8, const std::unordered_map<std::string, TCodepoint>& additional_escape_map) {
|
||||
static const std::unordered_map<std::string, TCodepoint> kEscapeToCodepoint = {
|
||||
{"\\\'", '\''}, {"\\\"", '\"'}, {"\\\?", '\?'}, {"\\\\", '\\'}, {"\\a", '\a'},
|
||||
{"\\b", '\b'}, {"\\f", '\f'}, {"\\n", '\n'}, {"\\r", '\r'}, {"\\t", '\t'},
|
||||
{"\\v", '\v'}, {"\\0", '\0'}, {"\\e", '\x1B'}};
|
||||
if (utf8[0] != '\\') {
|
||||
return ParseNextUTF8(utf8, UTF8ErrorPolicy::kReturnInvalid);
|
||||
}
|
||||
|
||||
auto escape_sequence = std::string(utf8, 2);
|
||||
if (auto it = additional_escape_map.find(escape_sequence); it != additional_escape_map.end()) {
|
||||
return {it->second, utf8 + 2};
|
||||
}
|
||||
if (auto it = kEscapeToCodepoint.find(escape_sequence); it != kEscapeToCodepoint.end()) {
|
||||
return {it->second, utf8 + 2};
|
||||
}
|
||||
|
||||
if (utf8[1] == 'x') {
|
||||
// arbitrary length hex
|
||||
int len = 0;
|
||||
int32_t codepoint = 0;
|
||||
while (true) {
|
||||
auto digit = HexCharToInt(utf8[2 + len]);
|
||||
if (digit == -1) {
|
||||
break;
|
||||
}
|
||||
codepoint = codepoint * 16 + digit;
|
||||
++len;
|
||||
}
|
||||
if (len == 0) {
|
||||
return {CharHandlingError::kInvalidEscape, utf8};
|
||||
}
|
||||
return {codepoint, utf8 + len + 2};
|
||||
} else if (utf8[1] == 'u' || utf8[1] == 'U') {
|
||||
// 4- or 8-digit hex
|
||||
int len = utf8[1] == 'u' ? 4 : 8;
|
||||
int32_t codepoint = 0;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
auto digit = HexCharToInt(utf8[i + 2]);
|
||||
if (digit == -1) {
|
||||
return {CharHandlingError::kInvalidEscape, utf8};
|
||||
}
|
||||
codepoint = codepoint * 16 + digit;
|
||||
}
|
||||
return {codepoint, utf8 + len + 2};
|
||||
} else {
|
||||
return {CharHandlingError::kInvalidEscape, utf8};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace llm
|
||||
} // namespace mlc
|
||||
@@ -0,0 +1,114 @@
|
||||
/*!
|
||||
* Copyright (c) 2023-2025 by Contributors
|
||||
* \file support/encoding.h
|
||||
* \brief Encoding and decoding from/to UTF-8 and escape sequence to/from codepoints.
|
||||
*/
|
||||
#ifndef MLC_LLM_SUPPORT_ENCODING_H_
|
||||
#define MLC_LLM_SUPPORT_ENCODING_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace mlc {
|
||||
namespace llm {
|
||||
|
||||
/*! \brief Represents a unicode codepoint. */
|
||||
using TCodepoint = int32_t;
|
||||
|
||||
/*!
|
||||
* \brief Handle the utf-8 first byte.
|
||||
* \returns (is_valid, total_number_of_bytes, initial_codepoint).
|
||||
*/
|
||||
std::tuple<bool, int, TCodepoint> HandleUTF8FirstByte(uint8_t byte);
|
||||
|
||||
/*!
|
||||
* \brief Print a codepoint to a UTF-8 string.
|
||||
* \param codepoint The codepoint.
|
||||
* \return The UTF-8 string.
|
||||
*/
|
||||
std::string PrintAsUTF8(TCodepoint codepoint);
|
||||
|
||||
/*!
|
||||
* \brief Print a codepoint to a escaped string. If the codepoint is not printable, it will be
|
||||
* escaped. By default the function support escape sequences in C ("\n", "\t", "\u0123"). User can
|
||||
* specify more escape sequences using additional_escape_map.
|
||||
* \param codepoint The codepoint.
|
||||
* \param additional_escape_map A map from codepoint to escape sequence. If the codepoint is in the
|
||||
* map, it will be escaped using the corresponding escape sequence. e.g. {{'-', "\\-"}}. \return The
|
||||
* printable string.
|
||||
*/
|
||||
std::string PrintAsEscaped(
|
||||
TCodepoint codepoint,
|
||||
const std::unordered_map<TCodepoint, std::string>& additional_escape_map = {});
|
||||
|
||||
/*!
|
||||
* \brief Print the given char to a escaped string that can be printed.
|
||||
* \return The escaped string.
|
||||
*/
|
||||
std::string PrintAsEscaped(uint8_t raw_char);
|
||||
|
||||
/*!
|
||||
* \brief Print the given string to a escaped string that can be printed.
|
||||
* \return The escaped string.
|
||||
*/
|
||||
std::string PrintAsEscaped(std::string raw_str);
|
||||
|
||||
/*!
|
||||
* \brief Represents an error when handling characters. Will be returned as a special TCodepoint
|
||||
* value.
|
||||
*/
|
||||
enum CharHandlingError : TCodepoint {
|
||||
/*! \brief The UTF-8 string is invalid. */
|
||||
kInvalidUTF8 = -10,
|
||||
/*! \brief The escape sequence is invalid. */
|
||||
kInvalidEscape = -11,
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief The method to handle invalid UTF-8 sequence.
|
||||
*/
|
||||
enum class UTF8ErrorPolicy {
|
||||
/*! \brief Return an error codepoint when an error is encountered. */
|
||||
kReturnInvalid,
|
||||
/*! \brief Skip the error and continue parsing. */
|
||||
kReturnByte,
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Parse the first codepoint in a UTF-8 string.
|
||||
* \param utf8 The UTF-8 string.
|
||||
* \return The codepoint and new pointer. If the UTF-8 string is invalid, and the error policy is
|
||||
* kReturnInvalid, the function returns (CharHandlingError::kInvalidUTF8, input char pointer).
|
||||
*/
|
||||
std::pair<TCodepoint, const char*> ParseNextUTF8(
|
||||
const char* utf8, UTF8ErrorPolicy error_policy = UTF8ErrorPolicy::kReturnInvalid);
|
||||
|
||||
/*!
|
||||
* \brief Parse all codepoints in a UTF-8 string.
|
||||
* \param utf8 The UTF-8 string.
|
||||
* \return All codepoints. If the UTF-8 string is invalid, and the error policy is
|
||||
* kReturnInvalid, the function returns {CharHandlingError::kInvalidUTF8}.
|
||||
*/
|
||||
std::vector<TCodepoint> ParseUTF8(const char* utf8,
|
||||
UTF8ErrorPolicy error_policy = UTF8ErrorPolicy::kReturnInvalid);
|
||||
|
||||
/*!
|
||||
* \brief Parse the first codepoint from a UTF-8 string. Also checks escape sequences and converts
|
||||
* the escaped char to its original value.
|
||||
* \param utf8 The UTF-8 string or the escape sequence.
|
||||
* \param additional_escape_map A map from escape sequence to codepoint. If the escape sequence is
|
||||
* in the map, it will be converted to the corresponding codepoint. e.g. {{"\\-", '-'}}.
|
||||
* \return The codepoint and the new pointer. If the UTF-8 string or the escape sequence is
|
||||
* invalid, and the error policy is kReturnInvalid, the function returns
|
||||
* (CharHandlingError::kInvalidUTF8, input char pointer).
|
||||
*/
|
||||
std::pair<TCodepoint, const char*> ParseNextUTF8OrEscaped(
|
||||
const char* utf8,
|
||||
const std::unordered_map<std::string, TCodepoint>& additional_escape_map = {});
|
||||
|
||||
} // namespace llm
|
||||
} // namespace mlc
|
||||
|
||||
#endif // MLC_LLM_SUPPORT_ENCODING_H_
|
||||
@@ -0,0 +1,284 @@
|
||||
/*!
|
||||
* \file support/json_parser.h
|
||||
* \brief Helps to parse JSON strings and objects.
|
||||
*/
|
||||
#ifndef MLC_LLM_SUPPORT_JSON_PARSER_H_
|
||||
#define MLC_LLM_SUPPORT_JSON_PARSER_H_
|
||||
|
||||
#include <tvm/ffi/container/shape.h>
|
||||
#include <tvm/ffi/dtype.h>
|
||||
#include <tvm/ffi/extra/json.h>
|
||||
#include <tvm/ffi/string.h>
|
||||
#include <tvm/runtime/logging.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "result.h"
|
||||
|
||||
namespace mlc {
|
||||
namespace llm {
|
||||
namespace json {
|
||||
|
||||
using ::tvm::ffi::json::Array;
|
||||
using ::tvm::ffi::json::Object;
|
||||
using ::tvm::ffi::json::Value;
|
||||
|
||||
/*!
|
||||
* \brief Parse a JSON string to a JSON object.
|
||||
* \param json_str The JSON string to parse.
|
||||
* \return The parsed JSON object.
|
||||
*/
|
||||
inline Object ParseToJSONObject(const std::string& json_str) {
|
||||
tvm::ffi::String err;
|
||||
Value result = ::tvm::ffi::json::Parse(json_str, &err);
|
||||
TVM_FFI_CHECK(err.empty(), ValueError)
|
||||
<< "Failed to parse JSON: err. The JSON string is:" << json_str;
|
||||
auto opt = result.try_cast<Object>();
|
||||
TVM_FFI_CHECK(opt.has_value(), ValueError)
|
||||
<< "The given string is not a JSON object: " << json_str;
|
||||
return *opt;
|
||||
}
|
||||
/*!
|
||||
* \brief Parse a JSON string to a JSON object.
|
||||
* \param json_str The JSON string to parse.
|
||||
* \return The parsed JSON object, or the error message.
|
||||
*/
|
||||
inline Result<Object> ParseToJSONObjectWithResultReturn(const std::string& json_str) {
|
||||
using TResult = Result<Object>;
|
||||
tvm::ffi::String err;
|
||||
Value result = ::tvm::ffi::json::Parse(json_str, &err);
|
||||
if (!err.empty()) {
|
||||
return TResult::Error("Failed to parse JSON: err. The JSON string is: " + json_str +
|
||||
". The error is " + std::string(err));
|
||||
}
|
||||
auto opt = result.try_cast<Object>();
|
||||
if (!opt.has_value()) {
|
||||
return TResult::Error("ValueError: The given string is not a JSON object: " + json_str);
|
||||
}
|
||||
return TResult::Ok(*opt);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Lookup a JSON object by a key, and convert it to a given type.
|
||||
* \param json The JSON object to look up.
|
||||
* \param key The key to look up.
|
||||
* \tparam ValueType The type to be converted to.
|
||||
* \return The converted value.
|
||||
*/
|
||||
template <typename ValueType>
|
||||
ValueType Lookup(const Object& json, const std::string& key);
|
||||
/*!
|
||||
* \brief Lookup a JSON array by an index, and convert it to a given type.
|
||||
* \param json The JSON array to look up.
|
||||
* \param index The index to look up.
|
||||
* \tparam ValueType The type to be converted to.
|
||||
* \return The converted value.
|
||||
*/
|
||||
template <typename ValueType>
|
||||
ValueType Lookup(const Array& json, int index);
|
||||
/*!
|
||||
* \brief Lookup a JSON object by a key, and convert it to a given type.
|
||||
* If the key doesn't exist or has null value, the default value is returned.
|
||||
* \param json The JSON object to look up.
|
||||
* \param key The key to look up.
|
||||
* \tparam ValueType The type to be converted to.
|
||||
* \return The converted value, or the default value if the key doesn't exist or has null value.
|
||||
*/
|
||||
template <typename ValueType>
|
||||
inline ValueType LookupOrDefault(const Object& json, const std::string& key,
|
||||
const ValueType& default_value) {
|
||||
if (json.count(key) == 0 || json.at(key) == nullptr) {
|
||||
return default_value;
|
||||
}
|
||||
auto opt = json.at(key).try_cast<ValueType>();
|
||||
TVM_FFI_CHECK(opt.has_value(), ValueError) << "key `" << key << "` has unexpected type";
|
||||
return *opt;
|
||||
}
|
||||
/*!
|
||||
* \brief Lookup a JSON object by a key, and convert it to a given type.
|
||||
* If the key doesn't exist or has null value, return std::nullopt.
|
||||
* \param json The JSON object to look up.
|
||||
* \param key The key to look up.
|
||||
* \tparam ValueType The type to be converted to.
|
||||
* \return The converted value, or std::nullopt if the value doesn't exist or has null value.
|
||||
*/
|
||||
template <typename ValueType>
|
||||
inline std::optional<ValueType> LookupOptional(const Object& json, const std::string& key) {
|
||||
if (json.count(key) == 0 || json.at(key) == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto opt = json.at(key).try_cast<ValueType>();
|
||||
TVM_FFI_CHECK(opt.has_value(), ValueError) << "key `" << key << "` has unexpected type";
|
||||
return *opt;
|
||||
}
|
||||
/*!
|
||||
* \brief Lookup a JSON object by a key, and convert it to a given type.
|
||||
* \param json The JSON object to look up.
|
||||
* \param key The key to look up.
|
||||
* \tparam ValueType The type to be converted to.
|
||||
* \return The converted value, or the error message.
|
||||
*/
|
||||
template <typename ValueType>
|
||||
inline Result<ValueType> LookupWithResultReturn(const Object& json, const std::string& key) {
|
||||
using TResult = Result<ValueType>;
|
||||
if (json.count(key) == 0) {
|
||||
return TResult::Error("ValueError: key \"" + key + "\" not found in the JSON object");
|
||||
}
|
||||
auto opt = json.at(key).try_cast<ValueType>();
|
||||
if (!opt.has_value()) {
|
||||
return TResult::Error("ValueError: key \"" + key + "\" has unexpected value type.");
|
||||
}
|
||||
return TResult::Ok(*opt);
|
||||
}
|
||||
/*!
|
||||
* \brief Lookup a JSON object by a key, and convert it to a given type.
|
||||
* If the key doesn't exist or has null value, the default value is returned.
|
||||
* \param json The JSON object to look up.
|
||||
* \param key The key to look up.
|
||||
* \tparam ValueType The type to be converted to.
|
||||
* \return The converted value, or the default value if the key doesn't exist or has null value
|
||||
* , or the error message.
|
||||
*/
|
||||
template <typename ValueType>
|
||||
inline Result<ValueType> LookupOrDefaultWithResultReturn(const Object& json, const std::string& key,
|
||||
const ValueType& default_value) {
|
||||
using TResult = Result<ValueType>;
|
||||
if (json.count(key) == 0 || json.at(key) == nullptr) {
|
||||
return TResult::Ok(default_value);
|
||||
}
|
||||
auto opt = json.at(key).try_cast<ValueType>();
|
||||
if (!opt.has_value()) {
|
||||
return TResult::Error("ValueError: key \"" + key + "\" has unexpected value type.");
|
||||
}
|
||||
return TResult::Ok(*opt);
|
||||
}
|
||||
/*!
|
||||
* \brief Lookup a JSON object by a key, and convert it to a given type.
|
||||
* If the key doesn't exist or has null value, return std::nullopt.
|
||||
* \param json The JSON object to look up.
|
||||
* \param key The key to look up.
|
||||
* \tparam ValueType The type to be converted to.
|
||||
* \return The converted value, or std::nullopt if the value doesn't exist or has null value,
|
||||
* , or the error message.
|
||||
*/
|
||||
template <typename ValueType>
|
||||
inline Result<std::optional<ValueType>> LookupOptionalWithResultReturn(const Object& json,
|
||||
const std::string& key) {
|
||||
using TResult = Result<std::optional<ValueType>>;
|
||||
if (json.count(key) == 0 || json.at(key) == nullptr) {
|
||||
return TResult::Ok(std::nullopt);
|
||||
}
|
||||
auto opt = json.at(key).try_cast<ValueType>();
|
||||
if (!opt.has_value()) {
|
||||
return TResult::Error("ValueError: key \"" + key + "\" has unexpected value type.");
|
||||
}
|
||||
return TResult::Ok(*opt);
|
||||
}
|
||||
|
||||
// Implementation details
|
||||
|
||||
/*! \brief Shape extension to incorporate symbolic shapes. */
|
||||
struct SymShapeTuple {
|
||||
tvm::ffi::Shape shape_values;
|
||||
std::vector<std::string> sym_names;
|
||||
|
||||
/*! \brief Convert symbolic shape tuple to static shape tuple with model config. */
|
||||
tvm::ffi::Shape ToStatic(const Object& model_config) {
|
||||
std::vector<int64_t> shape;
|
||||
shape.reserve(shape_values.size());
|
||||
for (int i = 0; i < static_cast<int>(shape_values.size()); ++i) {
|
||||
if (shape_values[i] != -1) {
|
||||
shape.push_back(shape_values[i]);
|
||||
} else {
|
||||
auto opt = model_config.at(sym_names[i]).try_cast<int64_t>();
|
||||
TVM_FFI_CHECK(opt.has_value(), ValueError)
|
||||
<< "model config is expected to contain \"" << sym_names[i]
|
||||
<< "\" as an integer. However, the given config has unexpected type for \""
|
||||
<< sym_names[i] << "\".";
|
||||
shape.push_back(*opt);
|
||||
}
|
||||
}
|
||||
return tvm::ffi::Shape(std::move(shape));
|
||||
}
|
||||
};
|
||||
|
||||
namespace details {
|
||||
|
||||
inline DLDataType DTypeFromString(const std::string& s) { return tvm::ffi::StringToDLDataType(s); }
|
||||
|
||||
inline SymShapeTuple SymShapeTupleFromArray(const Array& shape) {
|
||||
std::vector<int64_t> result;
|
||||
std::vector<std::string> sym_names;
|
||||
result.reserve(shape.size());
|
||||
sym_names.reserve(shape.size());
|
||||
for (int i = 0; i < static_cast<int>(shape.size()); ++i) {
|
||||
const auto& dim = shape[i];
|
||||
auto str_opt = dim.try_cast<std::string>();
|
||||
if (str_opt.has_value()) {
|
||||
result.push_back(-1);
|
||||
sym_names.push_back(*str_opt);
|
||||
} else {
|
||||
auto int_opt = dim.try_cast<int64_t>();
|
||||
TVM_FFI_CHECK(int_opt.has_value(), ValueError) << "shape has unexpected type";
|
||||
result.push_back(*int_opt);
|
||||
sym_names.push_back("");
|
||||
}
|
||||
}
|
||||
return SymShapeTuple{tvm::ffi::Shape(std::move(result)), sym_names};
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename ValueType>
|
||||
inline ValueType Lookup(const Object& json, const std::string& key) {
|
||||
TVM_FFI_CHECK(json.count(key) != 0, ValueError)
|
||||
<< "key `" << key << "` not found in the JSON object";
|
||||
auto opt = json.at(key).try_cast<ValueType>();
|
||||
TVM_FFI_CHECK(opt.has_value(), ValueError) << "key `" << key << "` has unexpected type";
|
||||
return *opt;
|
||||
}
|
||||
|
||||
template <typename ValueType>
|
||||
inline ValueType Lookup(const Array& json, int index) {
|
||||
TVM_FFI_ICHECK(index < static_cast<int>(json.size()))
|
||||
<< "IndexError: json::array index out of range";
|
||||
auto opt = json[index].try_cast<ValueType>();
|
||||
TVM_FFI_ICHECK(opt.has_value()) << "ValueError: value at index `" << index
|
||||
<< "` has unexpected type";
|
||||
return *opt;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline DLDataType Lookup(const Object& json, const std::string& key) {
|
||||
return details::DTypeFromString(Lookup<std::string>(json, key));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline DLDataType Lookup(const Array& json, int index) {
|
||||
return details::DTypeFromString(Lookup<std::string>(json, index));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline SymShapeTuple Lookup(const Object& json, const std::string& key) {
|
||||
return details::SymShapeTupleFromArray(Lookup<Array>(json, key));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline SymShapeTuple LookupOrDefault(const Object& json, const std::string& key,
|
||||
const SymShapeTuple& default_value) {
|
||||
if (json.count(key) == 0 || json.at(key) == nullptr) {
|
||||
return default_value;
|
||||
}
|
||||
return details::SymShapeTupleFromArray(Lookup<Array>(json, key));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline SymShapeTuple Lookup(const Array& json, int index) {
|
||||
return details::SymShapeTupleFromArray(Lookup<Array>(json, index));
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace llm
|
||||
} // namespace mlc
|
||||
|
||||
#endif // MLC_LLM_SUPPORT_JSON_PARSER_H_
|
||||
@@ -0,0 +1,32 @@
|
||||
/*!
|
||||
* Copyright (c) 2023-2025 by Contributors
|
||||
* \file support/load_bytes_from_file.h
|
||||
* \brief Utility methods to load from files.
|
||||
*/
|
||||
#ifndef MLC_LLM_SUPPORT_LOAD_BYTES_FROM_FILE_H_
|
||||
#define MLC_LLM_SUPPORT_LOAD_BYTES_FROM_FILE_H_
|
||||
|
||||
#include <tvm/runtime/logging.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
namespace mlc {
|
||||
namespace llm {
|
||||
|
||||
inline std::string LoadBytesFromFile(const std::string& path) {
|
||||
std::ifstream fs(path, std::ios::in | std::ios::binary);
|
||||
TVM_FFI_ICHECK(!fs.fail()) << "Cannot open " << path;
|
||||
std::string data;
|
||||
fs.seekg(0, std::ios::end);
|
||||
size_t size = static_cast<size_t>(fs.tellg());
|
||||
fs.seekg(0, std::ios::beg);
|
||||
data.resize(size);
|
||||
fs.read(data.data(), size);
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace llm
|
||||
} // namespace mlc
|
||||
|
||||
#endif // MLC_LLM_SUPPORT_LOAD_BYTES_FROM_FILE_H_
|
||||
@@ -0,0 +1,107 @@
|
||||
/*!
|
||||
* \file support/module_vtable.h
|
||||
* \brief Compatibility shim providing the TVM_MODULE_VTABLE_* macros that
|
||||
* previously lived in <tvm/runtime/module.h>. After the TVM runtime
|
||||
* refactor the macros were moved into TVM's private
|
||||
* src/runtime/vm/module_utils.h, so we vendor the surface mlc-llm uses
|
||||
* here to keep the existing call sites unchanged.
|
||||
*/
|
||||
#ifndef MLC_LLM_SUPPORT_MODULE_VTABLE_H_
|
||||
#define MLC_LLM_SUPPORT_MODULE_VTABLE_H_
|
||||
|
||||
#include <tvm/ffi/cast.h>
|
||||
#include <tvm/ffi/extra/module.h>
|
||||
#include <tvm/ffi/function.h>
|
||||
#include <tvm/ffi/object.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace mlc {
|
||||
namespace llm {
|
||||
namespace module_vtable_detail {
|
||||
|
||||
template <typename T>
|
||||
struct EntryHelper {};
|
||||
|
||||
template <typename T, typename R, typename... Args>
|
||||
struct EntryHelper<R (T::*)(Args...) const> {
|
||||
using MemFnType = R (T::*)(Args...) const;
|
||||
TVM_FFI_INLINE static void Call(::tvm::ffi::Any* rv, T* self, MemFnType f,
|
||||
::tvm::ffi::PackedArgs args) {
|
||||
auto wrapped = [self, f](Args... args) -> R { return (self->*f)(std::forward<Args>(args)...); };
|
||||
::tvm::ffi::details::unpack_call<R>(std::make_index_sequence<sizeof...(Args)>{}, nullptr,
|
||||
wrapped, args.data(), args.size(), rv);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename R, typename... Args>
|
||||
struct EntryHelper<R (T::*)(Args...)> {
|
||||
using MemFnType = R (T::*)(Args...);
|
||||
TVM_FFI_INLINE static void Call(::tvm::ffi::Any* rv, T* self, MemFnType f,
|
||||
::tvm::ffi::PackedArgs args) {
|
||||
auto wrapped = [self, f](Args... args) -> R { return (self->*f)(std::forward<Args>(args)...); };
|
||||
::tvm::ffi::details::unpack_call<R>(std::make_index_sequence<sizeof...(Args)>{}, nullptr,
|
||||
wrapped, args.data(), args.size(), rv);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
struct EntryHelper<void (T::*)(Args...) const> {
|
||||
using MemFnType = void (T::*)(Args...) const;
|
||||
TVM_FFI_INLINE static void Call(::tvm::ffi::Any* rv, T* self, MemFnType f,
|
||||
::tvm::ffi::PackedArgs args) {
|
||||
auto wrapped = [self, f](Args... args) -> void { (self->*f)(std::forward<Args>(args)...); };
|
||||
::tvm::ffi::details::unpack_call<void>(std::make_index_sequence<sizeof...(Args)>{}, nullptr,
|
||||
wrapped, args.data(), args.size(), rv);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
struct EntryHelper<void (T::*)(Args...)> {
|
||||
using MemFnType = void (T::*)(Args...);
|
||||
TVM_FFI_INLINE static void Call(::tvm::ffi::Any* rv, T* self, MemFnType f,
|
||||
::tvm::ffi::PackedArgs args) {
|
||||
auto wrapped = [self, f](Args... args) -> void { (self->*f)(std::forward<Args>(args)...); };
|
||||
::tvm::ffi::details::unpack_call<void>(std::make_index_sequence<sizeof...(Args)>{}, nullptr,
|
||||
wrapped, args.data(), args.size(), rv);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace module_vtable_detail
|
||||
} // namespace llm
|
||||
} // namespace mlc
|
||||
|
||||
#define TVM_MODULE_VTABLE_BEGIN(TypeKey) \
|
||||
const char* kind() const final { return TypeKey; } \
|
||||
::tvm::ffi::Optional<::tvm::ffi::Function> GetFunction(const ::tvm::ffi::String& _name) \
|
||||
override { \
|
||||
using SelfPtr = std::remove_cv_t<decltype(this)>; \
|
||||
::tvm::ffi::ObjectPtr<::tvm::ffi::Object> _self = \
|
||||
::tvm::ffi::GetObjectPtr<::tvm::ffi::Object>(this);
|
||||
#define TVM_MODULE_VTABLE_END() \
|
||||
return std::nullopt; \
|
||||
}
|
||||
#define TVM_MODULE_VTABLE_END_WITH_DEFAULT(MemFunc) \
|
||||
{ \
|
||||
auto f = (MemFunc); \
|
||||
return (this->*f)(_name); \
|
||||
} \
|
||||
}
|
||||
#define TVM_MODULE_VTABLE_ENTRY(Name, MemFunc) \
|
||||
if (_name == Name) { \
|
||||
return ::tvm::ffi::Function::FromPacked( \
|
||||
[_self](::tvm::ffi::PackedArgs args, ::tvm::ffi::Any* rv) -> void { \
|
||||
using Helper = ::mlc::llm::module_vtable_detail::EntryHelper<decltype(MemFunc)>; \
|
||||
SelfPtr self = static_cast<SelfPtr>(_self.get()); \
|
||||
Helper::Call(rv, self, MemFunc, args); \
|
||||
}); \
|
||||
}
|
||||
#define TVM_MODULE_VTABLE_ENTRY_PACKED(Name, MemFunc) \
|
||||
if (_name == Name) { \
|
||||
return ::tvm::ffi::Function( \
|
||||
[_self](::tvm::ffi::PackedArgs args, ::tvm::ffi::Any* rv) -> void { \
|
||||
(static_cast<SelfPtr>(_self.get())->*(MemFunc))(args, rv); \
|
||||
}); \
|
||||
}
|
||||
|
||||
#endif // MLC_LLM_SUPPORT_MODULE_VTABLE_H_
|
||||
@@ -0,0 +1,49 @@
|
||||
/*!
|
||||
* Copyright (c) 2023-2025 by Contributors
|
||||
* \file support/progress_bar.h
|
||||
* \brief A simple progress bar in C++.
|
||||
*/
|
||||
#ifndef MLC_LLM_SUPPORT_PROGRESS_BAR_H_
|
||||
#define MLC_LLM_SUPPORT_PROGRESS_BAR_H_
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace mlc {
|
||||
namespace llm {
|
||||
|
||||
class ProgressBar {
|
||||
public:
|
||||
explicit ProgressBar(int total, int width = 100) : total(total), width(width), cur(0) {}
|
||||
|
||||
void Progress() {
|
||||
if (cur < total) {
|
||||
++cur;
|
||||
}
|
||||
int bar_width = width - 2; // Adjust for borders
|
||||
int completed = static_cast<int>(static_cast<float>(cur) / total * bar_width);
|
||||
int remaining = bar_width - completed;
|
||||
std::cout << "[" //
|
||||
<< std::string(completed, '=') //
|
||||
<< ">" //
|
||||
<< std::string(remaining, ' ') //
|
||||
<< "] " //
|
||||
<< " [" << cur << "/" << total << "]";
|
||||
if (cur < total) {
|
||||
std::cout << "\r";
|
||||
std::cout.flush();
|
||||
} else {
|
||||
std::cout << std::endl; // Move to the next line after the progress bar is complete
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int total;
|
||||
int width;
|
||||
int cur;
|
||||
};
|
||||
|
||||
} // namespace llm
|
||||
} // namespace mlc
|
||||
|
||||
#endif // MLC_LLM_SUPPORT_PROGRESS_BAR_H_
|
||||
@@ -0,0 +1,37 @@
|
||||
/*!
|
||||
* Copyright (c) 2023-2025 by Contributors
|
||||
* \file support/random.h
|
||||
* \brief Header of random number generator.
|
||||
*/
|
||||
|
||||
#ifndef MLC_LLM_SUPPORT_RANDOM_H_
|
||||
#define MLC_LLM_SUPPORT_RANDOM_H_
|
||||
|
||||
#include <random>
|
||||
|
||||
namespace mlc {
|
||||
namespace llm {
|
||||
|
||||
// Random number generator
|
||||
class RandomGenerator {
|
||||
private:
|
||||
std::mt19937 gen;
|
||||
std::uniform_real_distribution<> dis;
|
||||
|
||||
public:
|
||||
RandomGenerator(int seed = std::random_device{}()) : gen(seed), dis(0.0, 1.0) {}
|
||||
|
||||
static RandomGenerator& GetInstance(int seed = std::random_device{}()) {
|
||||
static RandomGenerator instance(seed);
|
||||
return instance;
|
||||
}
|
||||
|
||||
double GetRandomNumber() { return dis(gen); }
|
||||
|
||||
void SetSeed(int seed) { gen.seed(seed); }
|
||||
};
|
||||
|
||||
} // namespace llm
|
||||
} // namespace mlc
|
||||
|
||||
#endif // MLC_LLM_SUPPORT_RANDOM_H_
|
||||
@@ -0,0 +1,77 @@
|
||||
/*!
|
||||
* Copyright (c) 2023-2025 by Contributors
|
||||
* \file support/result.h
|
||||
* \brief The header for the Result class in MLC LLM.
|
||||
*/
|
||||
#ifndef MLC_LLM_SUPPORT_RESULT_H_
|
||||
#define MLC_LLM_SUPPORT_RESULT_H_
|
||||
|
||||
#include <tvm/runtime/logging.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace mlc {
|
||||
namespace llm {
|
||||
|
||||
/*!
|
||||
* \brief The result class in MLC LLM.
|
||||
* Each instance is either an okay value or an error.
|
||||
* \tparam T The okay value type of the result.
|
||||
* \tparam E The error type of the result.
|
||||
*/
|
||||
template <typename T, typename E = std::string>
|
||||
class Result {
|
||||
public:
|
||||
/*! \brief Create a result with an okay value. */
|
||||
static Result Ok(T value) {
|
||||
Result result;
|
||||
result.ok_value_ = std::move(value);
|
||||
return result;
|
||||
}
|
||||
/*! \brief Create a result with an error value. */
|
||||
static Result Error(E error) {
|
||||
Result result;
|
||||
result.err_value_ = std::move(error);
|
||||
return result;
|
||||
}
|
||||
/*! \brief Check if the result is okay or not. */
|
||||
bool IsOk() const { return ok_value_.has_value(); }
|
||||
/*! \brief Check if the result is an error or not. */
|
||||
bool IsErr() const { return err_value_.has_value(); }
|
||||
/*!
|
||||
* \brief Unwrap the result and return the okay value.
|
||||
* Throwing exception if it is an error.
|
||||
* \note This function returns the ok value by moving, so a Result can be unwrapped only once.
|
||||
*/
|
||||
T Unwrap() {
|
||||
TVM_FFI_ICHECK(ok_value_.has_value()) << "Cannot unwrap result on an error value.";
|
||||
TVM_FFI_ICHECK(!unwrapped_) << "Cannot unwrap a Result instance twice.";
|
||||
unwrapped_ = true;
|
||||
return std::move(ok_value_.value());
|
||||
}
|
||||
/*!
|
||||
* \brief Unwrap the result and return the error value.
|
||||
* Throwing exception if it is an okay value.
|
||||
* \note This function returns the error value by moving, so a Result can be unwrapped only once.
|
||||
*/
|
||||
E UnwrapErr() {
|
||||
TVM_FFI_ICHECK(err_value_.has_value()) << "Cannot unwrap result on an okay value.";
|
||||
TVM_FFI_ICHECK(!unwrapped_) << "Cannot unwrap a Result instance twice.";
|
||||
unwrapped_ = true;
|
||||
return std::move(err_value_.value());
|
||||
}
|
||||
|
||||
private:
|
||||
/*! \brief A boolean flag indicating if the result is okay or error. */
|
||||
bool unwrapped_ = false;
|
||||
/*! \brief The internal optional okay value. */
|
||||
std::optional<T> ok_value_;
|
||||
/*! \brief The internal optional error value. */
|
||||
std::optional<E> err_value_;
|
||||
};
|
||||
|
||||
} // namespace llm
|
||||
} // namespace mlc
|
||||
|
||||
#endif // MLC_LLM_SUPPORT_RESULT_H_
|
||||
@@ -0,0 +1,73 @@
|
||||
/*!
|
||||
* \file support/threading_backend.h
|
||||
* \brief Compatibility shim providing the threading helpers that used to live
|
||||
* at <tvm/runtime/threading_backend.h>. The public TVM header was
|
||||
* removed in a runtime refactor, but the underlying symbols
|
||||
* (TVMBackendParallelLaunch, threading::MaxConcurrency,
|
||||
* threading::SetMaxConcurrency) are still exported from libtvm_runtime.
|
||||
* We re-declare the inline parallel-for template here so existing
|
||||
* mlc-llm call sites keep compiling.
|
||||
*/
|
||||
#ifndef MLC_LLM_SUPPORT_THREADING_BACKEND_H_
|
||||
#define MLC_LLM_SUPPORT_THREADING_BACKEND_H_
|
||||
|
||||
#include <tvm/runtime/base.h>
|
||||
#include <tvm/runtime/c_backend_api.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
namespace tvm {
|
||||
namespace runtime {
|
||||
namespace threading {
|
||||
|
||||
/*! \return the maximum number of effective workers for this system. */
|
||||
int MaxConcurrency();
|
||||
/*! \brief Setting the maximum number of available cores. */
|
||||
void SetMaxConcurrency(int value);
|
||||
|
||||
} // namespace threading
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
struct ParallelForWithThreadingBackendLambdaInvoker {
|
||||
static int TVMParallelLambdaInvoke(int task_id, TVMParallelGroupEnv* penv, void* cdata) {
|
||||
int num_task = penv->num_task;
|
||||
T* lambda_ptr = static_cast<T*>(cdata);
|
||||
(*lambda_ptr)(task_id, num_task);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline void parallel_launch_with_threading_backend(T flambda) {
|
||||
void* cdata = &flambda;
|
||||
TVMBackendParallelLaunch(ParallelForWithThreadingBackendLambdaInvoker<T>::TVMParallelLambdaInvoke,
|
||||
cdata, /*num_task=*/0);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
inline void parallel_for_with_threading_backend(T flambda, int64_t begin, int64_t end) {
|
||||
if (end - begin == 1) {
|
||||
flambda(begin);
|
||||
return;
|
||||
}
|
||||
auto flaunch = [begin, end, flambda](int task_id, int num_task) {
|
||||
int64_t total_len = end - begin;
|
||||
int64_t step = (total_len + num_task - 1) / num_task;
|
||||
int64_t local_begin = std::min(begin + step * task_id, end);
|
||||
int64_t local_end = std::min(local_begin + step, end);
|
||||
for (int64_t i = local_begin; i < local_end; ++i) {
|
||||
flambda(i);
|
||||
}
|
||||
};
|
||||
detail::parallel_launch_with_threading_backend(flaunch);
|
||||
}
|
||||
|
||||
} // namespace runtime
|
||||
} // namespace tvm
|
||||
|
||||
#endif // MLC_LLM_SUPPORT_THREADING_BACKEND_H_
|
||||
@@ -0,0 +1,79 @@
|
||||
/*!
|
||||
* Copyright (c) 2023-2025 by Contributors
|
||||
* \file support/utils.h
|
||||
* \brief Utility functions.
|
||||
*/
|
||||
#ifndef MLC_LLM_SUPPORT_UTILS_H_
|
||||
#define MLC_LLM_SUPPORT_UTILS_H_
|
||||
|
||||
#include <tvm/support/io.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../../3rdparty/tvm/src/support/base64.h"
|
||||
#include "../../3rdparty/tvm/src/support/bytes_io.h"
|
||||
|
||||
namespace mlc {
|
||||
namespace llm {
|
||||
|
||||
/*! \brief Split the input string by the given delimiter character. */
|
||||
inline std::vector<std::string> Split(const std::string& str, char delim) {
|
||||
std::string item;
|
||||
std::istringstream is(str);
|
||||
std::vector<std::string> ret;
|
||||
while (std::getline(is, item, delim)) {
|
||||
ret.push_back(item);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Check whether the string starts with a given prefix.
|
||||
* \param str The given string.
|
||||
* \param prefix The given prefix.
|
||||
* \return Whether the prefix matched.
|
||||
*/
|
||||
inline bool StartsWith(const std::string& str, const char* prefix) {
|
||||
size_t n = str.length();
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (prefix[i] == '\0') return true;
|
||||
if (str.data()[i] != prefix[i]) return false;
|
||||
}
|
||||
// return true if the str is equal to the prefix
|
||||
return prefix[n] == '\0';
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Get the base64 encoded result of a string.
|
||||
* \param str The string to encode.
|
||||
* \return The base64 encoded string.
|
||||
*/
|
||||
inline std::string Base64Encode(std::string str) {
|
||||
std::string result;
|
||||
tvm::support::BytesOutStream m_stream(&result);
|
||||
tvm::support::Base64OutStream b64stream(&m_stream);
|
||||
static_cast<tvm::support::Stream*>(&b64stream)->Write(str);
|
||||
b64stream.Finish();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Get the base64 decoded result of a string.
|
||||
* \param str The string to decode.
|
||||
* \return The base64 decoded string.
|
||||
*/
|
||||
inline std::string Base64Decode(std::string str) {
|
||||
std::string result;
|
||||
tvm::support::BytesInStream m_stream(str);
|
||||
tvm::support::Base64InStream b64stream(&m_stream);
|
||||
b64stream.InitPosition();
|
||||
static_cast<tvm::support::Stream*>(&b64stream)->Read(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace llm
|
||||
} // namespace mlc
|
||||
|
||||
#endif // MLC_LLM_SUPPORT_UTILS_H_
|
||||
@@ -0,0 +1,59 @@
|
||||
/*!
|
||||
* Copyright (c) 2023-2025 by Contributors
|
||||
* \file support/image_utils.cc
|
||||
*/
|
||||
#include "vlm_utils.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace mlc {
|
||||
namespace llm {
|
||||
|
||||
void CalculateResizeShape(tvm::runtime::Tensor image_data, std::string model_type,
|
||||
int* p_target_height, int* p_target_width) {
|
||||
TVM_FFI_ICHECK_EQ(image_data->shape[3], 3) << "Image format must be NHWC";
|
||||
int height = image_data->shape[1];
|
||||
int width = image_data->shape[2];
|
||||
|
||||
if ("phi3_v" == model_type) {
|
||||
const int hd_num = 4;
|
||||
double ratio = static_cast<double>(width) / height;
|
||||
int scale = 1;
|
||||
while (scale * std::ceil(scale / ratio) <= hd_num) {
|
||||
scale += 1;
|
||||
}
|
||||
scale -= 1;
|
||||
*p_target_width = static_cast<int>(scale * 336);
|
||||
*p_target_height = static_cast<int>(*p_target_width / ratio);
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatePadShape(tvm::runtime::Tensor image_data, std::string model_type, int* p_pad_height,
|
||||
int* p_pad_width) {
|
||||
TVM_FFI_ICHECK_EQ(image_data->shape[3], 3) << "Image format must be NHWC";
|
||||
if ("phi3_v" == model_type) {
|
||||
int resized_height = 0, resized_width = 0;
|
||||
CalculateResizeShape(image_data, model_type, &resized_height, &resized_width);
|
||||
int tar = (int)(ceil(resized_height / 336.0) * 336);
|
||||
int top_padding = (int)((tar - resized_height) / 2);
|
||||
int bottom_padding = tar - resized_height - top_padding;
|
||||
TVM_FFI_ICHECK_EQ(tar, resized_height + top_padding + bottom_padding)
|
||||
<< "Padding size not equal!";
|
||||
*p_pad_height = tar;
|
||||
*p_pad_width = resized_width;
|
||||
}
|
||||
}
|
||||
|
||||
void CalculateCropShape(tvm::runtime::Tensor image_data, std::string model_type, int* p_crop_height,
|
||||
int* p_crop_width) {
|
||||
TVM_FFI_ICHECK_EQ(image_data->shape[3], 3) << "Image format must be NHWC";
|
||||
if ("phi3_v" == model_type) {
|
||||
int pad_h = 0, pad_w = 0;
|
||||
CalculatePadShape(image_data, model_type, &pad_h, &pad_w);
|
||||
*p_crop_height = pad_h / 336;
|
||||
*p_crop_width = pad_w / 336;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace llm
|
||||
} // namespace mlc
|
||||
@@ -0,0 +1,48 @@
|
||||
/*!
|
||||
* Copyright (c) 2023-2025 by Contributors
|
||||
* \file support/vlm_utils.h
|
||||
* \brief Tools for debug purposes.
|
||||
*/
|
||||
#ifndef MLC_LLM_SUPPORT_VLM_UTILS_H_
|
||||
#define MLC_LLM_SUPPORT_VLM_UTILS_H_
|
||||
|
||||
#include <tvm/runtime/tensor.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace mlc {
|
||||
namespace llm {
|
||||
|
||||
/*!
|
||||
* \brief Calculate the target height and width for resizing an image based on the input data and
|
||||
* model type. \param image_data The input image data as a TVM Tensor. \param model_type The type
|
||||
* of the model influencing the resizing parameters (e.g., phi3v). \param target_height Reference to
|
||||
* the variable where the calculated target height will be stored. \param target_width Reference to
|
||||
* the variable where the calculated target width will be stored.
|
||||
*/
|
||||
void CalculateResizeShape(tvm::runtime::Tensor image_data, std::string model_type,
|
||||
int* p_target_height, int* p_target_width);
|
||||
/*!
|
||||
* \brief Calculate the padding height and width for an image based on the input data and model
|
||||
* type. \param image_data The input image data as a TVM Tensor. \param model_type The type of the
|
||||
* model influencing the padding parameters (e.g., phi3v). \param pad_height Reference to the
|
||||
* variable where the calculated padding height will be stored. \param pad_width Reference to the
|
||||
* variable where the calculated padding width will be stored.
|
||||
*/
|
||||
void CalculatePadShape(tvm::runtime::Tensor image_data, std::string model_type, int* p_pad_height,
|
||||
int* p_pad_width);
|
||||
|
||||
/*!
|
||||
* \brief Calculate the cropping height and width for an image based on the input data and model
|
||||
* type. \param image_data The input image data as a TVM Tensor. \param model_type The type of the
|
||||
* model influencing the cropping parameters (e.g., phi3v). \param crop_height Reference to the
|
||||
* variable where the calculated cropping height will be stored. \param crop_width Reference to the
|
||||
* variable where the calculated cropping width will be stored.
|
||||
*/
|
||||
void CalculateCropShape(tvm::runtime::Tensor image_data, std::string model_type, int* p_crop_height,
|
||||
int* p_crop_width);
|
||||
|
||||
} // namespace llm
|
||||
} // namespace mlc
|
||||
|
||||
#endif // MLC_LLM_SUPPORT_IMAGE_UTILS_H_
|
||||
Reference in New Issue
Block a user