/*! * Copyright (c) 2023-2025 by Contributors * \file serve/data.h */ #ifndef MLC_LLM_SERVE_DATA_H_ #define MLC_LLM_SERVE_DATA_H_ #include #include #include #include #include #include #include #include #include #include "../tokenizers/tokenizers.h" namespace mlc { namespace llm { namespace serve { using namespace tvm::runtime; using tvm::ffi::Object; using tvm::ffi::ObjectPtr; using tvm::ffi::ObjectRef; using tvm::ffi::Optional; using tvm::ffi::Shape; class Model; /****************** DataNode ******************/ /*! \brief The base class of multi-modality data (text, tokens, embedding, etc). */ class DataNode : public Object { public: /*! \brief Get the length (equivalent number of tokens) of the data. */ virtual int GetLength() const = 0; /*! * \brief Compute the embedding of this data with regard to the input model. * When the input destination pointer is not nullptr, it in-place writes the * embedding into the input destination array at the given offset. * Otherwise, the embeddings will be directly returned back. * \param model The model to take embeddings from. * \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 nullptr, we require `offset` to be 0. */ virtual ObjectRef GetEmbedding(Model model, ObjectRef* dst = nullptr, int offset = 0) const = 0; static void RegisterReflection() { namespace refl = tvm::ffi::reflection; refl::ObjectDef(); } static constexpr const bool _type_has_method_sequal_reduce = false; static constexpr const bool _type_has_method_shash_reduce = false; static constexpr const uint32_t _type_child_slots = 3; TVM_FFI_DECLARE_OBJECT_INFO("mlc.serve.Data", DataNode, Object); }; class Data : public ObjectRef { public: TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(Data, ObjectRef, DataNode); }; /*! \brief Split the given data array into two arrays at the "split_pos" position. */ std::pair, Array> SplitData(const Array& original_data, int total_length, int split_pos); /****************** TextDataNode ******************/ /*! \brief The class of text data, containing a text string. */ class TextDataNode : public DataNode { public: /*! \brief The text string. */ tvm::ffi::String text; int GetLength() const final; ObjectRef GetEmbedding(Model model, ObjectRef* dst = nullptr, int offset = 0) const final; static void RegisterReflection() { namespace refl = tvm::ffi::reflection; refl::ObjectDef(); } TVM_FFI_DECLARE_OBJECT_INFO_FINAL("mlc.serve.TextData", TextDataNode, DataNode); }; class TextData : public Data { public: explicit TextData(String text); TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(TextData, Data, TextDataNode); }; /****************** TokenDataNode ******************/ /*! \brief The class of token data, containing a list of token ids. */ class TokenDataNode : public DataNode { public: /*! \brief The token ids. */ Shape token_ids; int GetLength() const final; ObjectRef GetEmbedding(Model model, ObjectRef* dst = nullptr, int offset = 0) const final; static void RegisterReflection() { namespace refl = tvm::ffi::reflection; refl::ObjectDef(); } TVM_FFI_DECLARE_OBJECT_INFO_FINAL("mlc.serve.TokenData", TokenDataNode, DataNode); }; class TokenData : public Data { public: explicit TokenData(Shape token_ids); explicit TokenData(std::vector token_ids); TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(TokenData, Data, TokenDataNode); }; /****************** ImageDataNode ******************/ /*! \brief The class of image data, containing a 3D array of pixel values. */ class ImageDataNode : public DataNode { public: /*! \brief The pixel values. */ Tensor image; int embed_size; int GetLength() const final; ObjectRef GetEmbedding(Model model, ObjectRef* dst = nullptr, int offset = 0) const final; static void RegisterReflection() { namespace refl = tvm::ffi::reflection; refl::ObjectDef(); } TVM_FFI_DECLARE_OBJECT_INFO_FINAL("mlc.serve.ImageData", ImageDataNode, DataNode); }; class ImageData : public Data { public: explicit ImageData(Tensor image, int embed_size); TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(ImageData, Data, ImageDataNode); }; /****************** SampleResult ******************/ // The pair of a token id and its probability in sampling. using TokenProbPair = std::pair; /*! * \brief The class of sampler's sampling result. * It's not a TVM object since it will not be used directly on Python side. */ struct SampleResult { /*! \brief The token id and probability of the sampled token. */ TokenProbPair sampled_token_id; /*! \brief The token id and probability of the tokens with top probabilities. */ std::vector top_prob_tokens; /*! \brief Get the sampled token id. */ int32_t GetTokenId() const; /*! * \brief Get the logprob JSON string of this token with regard * to OpenAI API at https://platform.openai.com/docs/api-reference/chat/object. * \param tokenizer The tokenizer for token table lookup. * \param logprob A boolean indicating if need to return log probability. * \return A JSON string that conforms to the logprob spec in OpenAI API. */ std::string GetLogProbJSON(const Tokenizer& tokenizer, bool logprob) const; }; /****************** RequestStreamOutput ******************/ /*! * \brief The generated delta request output that is streamed back * through callback stream function. * * \note: This output object corresponds to parallel generated outputs when n != 1. * * For example, if n=2, then group_delta_token_ids[0] matches to the output stream 0 * and group_delta_token_ids[1] matches to the output stream 1 */ class RequestStreamOutputObj : public Object { public: /*! \brief The id of the request that the function is invoked for. */ String request_id; /*! * \brief The new generated token ids since the last callback invocation * for the input request. */ std::vector> group_delta_token_ids; /*! \brief The logprobs JSON strings of the new generated tokens since last invocation. */ std::optional>> group_delta_logprob_json_strs; /*! * \brief The finish reason of the request when it is finished, * of None if the request has not finished yet. */ std::vector> group_finish_reason; /*! * \brief The usage field of the response, this is global to all streams. */ Optional request_final_usage_json_str; /*! * \brief The extra prefix string of all requests. */ std::vector group_extra_prefix_string; std::atomic unpacked = false; static void RegisterReflection() { namespace refl = tvm::ffi::reflection; refl::ObjectDef(); } 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.RequestStreamOutput", RequestStreamOutputObj, Object); }; /*! * \brief Managed reference to RequestStreamOutputObj. * \sa RequestStreamOutputObj */ class RequestStreamOutput : public ObjectRef { public: explicit 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); static RequestStreamOutput Usage(String request_id, String request_final_usage_json_str); TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(RequestStreamOutput, ObjectRef, RequestStreamOutputObj); }; } // namespace serve } // namespace llm } // namespace mlc #endif // MLC_LLM_SERVE_DATA_H_