#pragma once #include #include #include #include #include #include #include #include #include #include namespace curriculum { /// Lossless byte-level BPE tokenizer for curriculum-trained vocabularies. /// /// The Rust trainer and this C++ inference implementation share: /// - the same deterministic pre-tokenization rules, /// - numeric merge rules (left ID, right ID, result ID), /// - byte-exact decoding. class Tokenizer { public: Tokenizer() = default; bool load(const std::string& vocab_path, const std::string& merges_path); std::vector encode(const std::string& text) const; std::string decode(const std::vector& ids) const; std::vector> encode_batch( const std::vector& texts, size_t num_threads = 0) const; size_t vocab_size() const; void set_cache_capacity(size_t capacity); void clear_cache(); std::unordered_map cache_info() const; /// Safe, printable token representation used by vocab.json. std::string id_to_token(int32_t id) const; /// Find an ID from literal token text/bytes. Returns -1 when absent. int32_t token_to_id(const std::string& token) const; void set_special_tokens(const std::vector& tokens); void configure_special_tokens( const std::unordered_map& token_ids, const std::unordered_map& aliases, size_t expected_vocab_size); // Optional decode-only aliases explicitly declared by legacy artifacts. void set_special_token_aliases(const std::unordered_map& aliases); private: struct EncodingScratch; struct CachedPiece { std::string bytes; std::vector ids; }; struct CacheShard { mutable std::mutex mutex; std::list lru; // Views refer to owned, stable strings in the LRU list. std::unordered_map::iterator> index; size_t payload_bytes = 0; size_t hits = 0; size_t misses = 0; }; static constexpr size_t kCacheShards = 16; static constexpr size_t kCachePayloadBytes = 4 * 1024 * 1024; static constexpr size_t kMaxCachedPieceBytes = 256; mutable std::shared_mutex model_mutex_; mutable std::array piece_cache_; size_t cache_capacity_ = 16384; struct MergeInfo { size_t rank; int32_t result; }; std::vector id_to_token_; // raw token bytes std::unordered_map token_to_id_; // raw bytes -> ID // Ordered merge rules: left, right, result. std::vector> merges_; // Earliest rule for each pair. Results are unique in trained artifacts, // so later duplicate rules can never match after the first rule fires. std::unordered_map merge_lookup_; bool fast_merge_order_ = true; std::unordered_map special_tokens_; std::unordered_map special_token_aliases_; std::vector encode_impl(const std::string& text) const; void append_regular(std::string_view text, std::vector& output, EncodingScratch& scratch) const; void append_word(std::string_view bytes, std::vector& output, EncodingScratch& scratch) const; void encode_word_into(std::string_view bytes, EncodingScratch& scratch) const; void invalidate_cache(); // Caller holds the exclusive model lock. bool load_vocab(const std::string& path); bool load_merges(const std::string& path); }; } // namespace curriculum