/*! * Copyright (c) 2023-2025 by Contributors * \file serve/data.cc */ #include "data.h" #include #include #include "model.h" namespace mlc { namespace llm { namespace serve { TVM_FFI_STATIC_INIT_BLOCK() { DataNode::RegisterReflection(); TextDataNode::RegisterReflection(); TokenDataNode::RegisterReflection(); ImageDataNode::RegisterReflection(); RequestStreamOutputObj::RegisterReflection(); } /****************** Data ******************/ std::pair, Array> SplitData(const Array& original_data, int total_length, int split_pos) { TVM_FFI_ICHECK_GE(split_pos, 0); TVM_FFI_ICHECK_GE(total_length, split_pos) << "Cannot truncate when the current length is already less than the target length"; std::vector lhs(original_data.begin(), original_data.end()); std::vector rhs; while (total_length > split_pos) { TVM_FFI_ICHECK(!lhs.empty()); Data last_data = lhs.back(); int last_data_length = last_data->GetLength(); TVM_FFI_ICHECK_GE(total_length - last_data_length, 0); if (total_length - last_data_length >= split_pos) { // Pop the entire last data. rhs.push_back(lhs.back()); lhs.pop_back(); total_length -= last_data_length; continue; } // Partially truncate the last data. const auto* token_data = last_data.as(); TVM_FFI_ICHECK(token_data != nullptr) << "Only TokenData supports partial truncation."; int length_to_truncate = total_length - split_pos; TVM_FFI_ICHECK_GT(length_to_truncate, 0); TVM_FFI_ICHECK_LT(length_to_truncate, last_data_length); TokenData lhs_token_data( Shape{token_data->token_ids.begin(), token_data->token_ids.end() - length_to_truncate}); TokenData rhs_token_data( Shape{token_data->token_ids.end() - length_to_truncate, token_data->token_ids.end()}); TVM_FFI_ICHECK_EQ(total_length - last_data_length + lhs_token_data->GetLength(), split_pos); lhs.pop_back(); lhs.push_back(lhs_token_data); rhs.push_back(rhs_token_data); std::reverse(rhs.begin(), rhs.end()); total_length = split_pos; } return {lhs, rhs}; } /****************** TextData ******************/ TextData::TextData(String text) { ObjectPtr n = tvm::ffi::make_object(); n->text = std::move(text); data_ = std::move(n); } int TextDataNode::GetLength() const { LOG(FATAL) << "\"GetLength\" for TextData is not supported. " "Please tokenize the text and construct a TokenData object."; } ObjectRef TextDataNode::GetEmbedding(Model model, ObjectRef* dst, int offset) const { LOG(FATAL) << "\"GetEmbedding\" for TextData is not supported. " "Please tokenize the text and construct a TokenData object."; } TVM_FFI_STATIC_INIT_BLOCK() { namespace refl = tvm::ffi::reflection; refl::GlobalDef() .def("mlc.serve.TextData", [](String text) { return TextData(std::move(text)); }) .def("mlc.serve.TextDataGetTextString", [](TextData data) { return data->text; }); } /****************** TokenData ******************/ TokenData::TokenData(Shape token_ids) { ObjectPtr n = tvm::ffi::make_object(); n->token_ids = std::move(token_ids); data_ = std::move(n); } TokenData::TokenData(std::vector token_ids) { ObjectPtr n = tvm::ffi::make_object(); n->token_ids = Shape(token_ids.begin(), token_ids.end()); data_ = std::move(n); } int TokenDataNode::GetLength() const { return token_ids.size(); } ObjectRef TokenDataNode::GetEmbedding(Model model, ObjectRef* dst, int offset) const { return model->TokenEmbed(token_ids, dst, offset); } TVM_FFI_STATIC_INIT_BLOCK() { namespace refl = tvm::ffi::reflection; refl::GlobalDef() .def_packed("mlc.serve.TokenData", [](ffi::PackedArgs args, ffi::Any* rv) { std::vector token_ids; token_ids.reserve(args.size()); for (int i = 0; i < args.size(); i++) { token_ids.push_back(args[i].cast()); } *rv = TokenData(std::move(token_ids)); }) .def("mlc.serve.TokenDataGetTokenIds", [](TokenData data) { return data->token_ids; }); } /****************** ImageData ******************/ ImageData::ImageData(Tensor image, int embed_size) { ObjectPtr n = tvm::ffi::make_object(); n->image = std::move(image); n->embed_size = embed_size; data_ = std::move(n); } int ImageDataNode::GetLength() const { return embed_size; } ObjectRef ImageDataNode::GetEmbedding(Model model, ObjectRef* dst, int offset) const { return model->ImageEmbed(image, dst, offset); } TVM_FFI_STATIC_INIT_BLOCK() { namespace refl = tvm::ffi::reflection; refl::GlobalDef() .def("mlc.serve.ImageData", [](Tensor image, int embed_size) { return ImageData(std::move(image), embed_size); }) .def("mlc.serve.ImageDataGetImage", [](ImageData data) { return data->image; }); } /****************** SampleResult ******************/ /*! \brief Convert a single token with probability to JSON string. */ inline void TokenToLogProbJSON(const Tokenizer& tokenizer, const TokenProbPair& token_prob, std::ostringstream* os) { const std::string& token = tokenizer->PostProcessedTokenTable()[token_prob.first]; (*os) << "\"token\": \""; for (char ch : token) { if (ch >= 33 && ch <= 126) { // The character is in ASCII visible range. // Handle escape characters in JSON. if (ch == '"') { (*os) << "\\\""; } else if (ch == '\\') { (*os) << "\\\\"; } else { (*os) << ch; } } } (*os) << "\", "; (*os) << "\"logprob\": " << std::log(std::max(token_prob.second, 1e-10f)) << ", "; (*os) << "\"bytes\": ["; int token_len = token.size(); for (int pos = 0; pos < token_len; ++pos) { (*os) << static_cast(static_cast(token[pos])); if (pos != token_len - 1) { (*os) << ", "; } } (*os) << "]"; } int32_t SampleResult::GetTokenId() const { return this->sampled_token_id.first; } std::string SampleResult::GetLogProbJSON(const Tokenizer& tokenizer, bool logprob) const { TVM_FFI_ICHECK(top_prob_tokens.empty() || logprob); if (!logprob) { // Logprob is not needed. return ""; } std::ostringstream os; os << "{"; // - Convert the sampled token to JSON. TokenToLogProbJSON(tokenizer, sampled_token_id, &os); // - Convert the tokens with top probabilities. os << ", \"top_logprobs\": ["; int num_top = top_prob_tokens.size(); for (int i = 0; i < num_top; ++i) { os << "{"; TokenToLogProbJSON(tokenizer, top_prob_tokens[i], &os); os << "}"; if (i != num_top - 1) { os << ", "; } } os << "]}"; return os.str(); } /****************** RequestStreamOutput ******************/ RequestStreamOutput::RequestStreamOutput( String request_id, std::vector> group_delta_token_ids, std::optional>> group_delta_logprob_json_strs, std::vector> group_finish_reason, std::vector group_extra_prefix_string) { ObjectPtr n = tvm::ffi::make_object(); n->request_id = std::move(request_id); n->group_delta_token_ids = std::move(group_delta_token_ids); n->group_delta_logprob_json_strs = std::move(group_delta_logprob_json_strs); n->group_finish_reason = std::move(group_finish_reason); n->group_extra_prefix_string = std::move(group_extra_prefix_string); data_ = std::move(n); } RequestStreamOutput RequestStreamOutput::Usage(String request_id, String request_final_usage_json_str) { ObjectPtr n = tvm::ffi::make_object(); n->request_id = std::move(request_id); n->request_final_usage_json_str = std::move(request_final_usage_json_str); return RequestStreamOutput(n); } TVM_FFI_STATIC_INIT_BLOCK() { namespace refl = tvm::ffi::reflection; refl::GlobalDef().def("mlc.serve.RequestStreamOutputUnpack", [](RequestStreamOutput output) { TVM_FFI_ICHECK(!output->unpacked) << "One RequestStreamOutput can be unpacked for at most once."; std::vector group_delta_token_ids; std::vector> group_delta_logprob_json_strs; group_delta_token_ids.reserve(output->group_delta_token_ids.size()); if (output->group_delta_logprob_json_strs.has_value()) { group_delta_logprob_json_strs.reserve(output->group_delta_token_ids.size()); } for (int i = 0; i < static_cast(output->group_delta_token_ids.size()); ++i) { group_delta_token_ids.push_back(output->group_delta_token_ids[i]); if (output->group_delta_logprob_json_strs.has_value()) { group_delta_logprob_json_strs.push_back(output->group_delta_logprob_json_strs.value()[i]); } } Array ret = {output->request_id, Array(std::move(group_delta_token_ids)), output->group_delta_logprob_json_strs.has_value() ? Array>(std::move(group_delta_logprob_json_strs)) : Optional>>(), Array>(output->group_finish_reason), output->request_final_usage_json_str, Array(output->group_extra_prefix_string)}; output->unpacked = true; return ret; }); } } // namespace serve } // namespace llm } // namespace mlc