| 734 | } |
| 735 | |
| 736 | std::string Tokenizer::token_text(int32_t id) const { |
| 737 | if (id < 0 || id >= (int32_t)id_to_token_.size()) return ""; |
| 738 | const std::string & tok = id_to_token_[id]; |
| 739 | |
| 740 | // Handle byte-fallback tokens like <0xNN>. |
| 741 | if (tok.size() == 6 && tok[0] == '<' && tok[1] == '0' && |
| 742 | tok[2] == 'x' && tok[5] == '>') { |
| 743 | unsigned val = 0; |
| 744 | if (std::sscanf(tok.c_str(), "<0x%02X>", &val) == 1) { |
| 745 | return std::string(1, (char)(uint8_t)val); |
| 746 | } |
| 747 | } |
| 748 | |
| 749 | // Special tokens (e.g. <|im_start|>, <turn|>) — return as-is. |
| 750 | if (!tok.empty() && tok[0] == '<' && tok.back() == '>') { |
| 751 | return tok; |
| 752 | } |
| 753 | |
| 754 | if (is_sentencepiece_) { |
| 755 | // SentencePiece: tokens are raw UTF-8 with ▁ (U+2581) for space. |
| 756 | std::string out; |
| 757 | out.reserve(tok.size()); |
| 758 | const char * p = tok.c_str(); |
| 759 | const char * end = p + tok.size(); |
| 760 | while (p < end) { |
| 761 | // ▁ is 3 bytes: 0xE2 0x96 0x81 |
| 762 | if (end - p >= 3 && |
| 763 | (uint8_t)p[0] == 0xE2 && |
| 764 | (uint8_t)p[1] == 0x96 && |
| 765 | (uint8_t)p[2] == 0x81) { |
| 766 | out.push_back(' '); |
| 767 | p += 3; |
| 768 | } else { |
| 769 | out.push_back(*p); |
| 770 | p++; |
| 771 | } |
| 772 | } |
| 773 | return out; |
| 774 | } |
| 775 | |
| 776 | // Decode GPT-2 byte-level BPE encoding → raw bytes. |
| 777 | return decode_gpt2_bpe(tok); |
| 778 | } |
| 779 | |
| 780 | std::string Tokenizer::decode(const std::vector<int32_t> & ids) const { |
| 781 | std::string result; |
no test coverage detected