CoolFace
Apppublic

qminh369/Compression_v1

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
utils_llmlingua2_test.py2463 linesDownload Raw Back to root
1# Copyright (c) 2023 Microsoft2# Licensed under The MIT License [see LICENSE for details]3 4import bisect5import copy6import re7import string8from collections import defaultdict9from typing import List10 11import nltk12import numpy as np13import tiktoken14import torch15import torch.nn.functional as F16from torch.utils.data import DataLoader17from transformers import (18    AutoConfig,19    AutoModelForCausalLM,20    AutoModelForTokenClassification,21    AutoTokenizer,22)23 24from core_utils_llmlingua2 import (TokenClfDataset, get_pure_token, is_begin_of_new_word, replace_added_token, seed_everything,)25#from core_utils_llmlingua2_phobert import (TokenClfDataset, get_pure_token, is_begin_of_new_word, replace_added_token, seed_everything,)26 27 28class PromptCompressor:29    """30    PromptCompressor is designed for compressing prompts based on a given language model.31 32    This class initializes with the language model and its configuration, preparing it for prompt compression tasks.33    The PromptCompressor class is versatile and can be adapted for various models and specific requirements in prompt processing.34    Users can specify different model names and configurations as needed for their particular use case.The architecture is35    based on the paper "LLMLingua: Compressing Prompts for Accelerated Inference of Large Language Models". Jiang, Huiqiang, Qianhui Wu,36    Chin-Yew Lin, Yuqing Yang, and Lili Qiu. "Llmlingua: Compressing prompts for accelerated inference of large language models."37    arXiv preprint arXiv:2310.05736 (2023).38 39    Args:40        model_name (str, optional): The name of the language model to be loaded. Default is "NousResearch/Llama-2-7b-hf".41        device_map (str, optional): The device to load the model onto, e.g., "cuda" for GPU. Default is "cuda".42        model_config (dict, optional): A dictionary containing the configuration parameters for the model. Default is an empty dictionary.43        open_api_config (dict, optional): A dictionary containing configuration for openai APIs that may be used in conjunction with the model. Default is an empty dictionary.44        use_llmlingua2 (bool, optional): Whether to use llmlingua-2 compressor based on the paper45            "LLMLingua-2: Context-Aware Data Distillation for Efficient and Faithful Task-Agnostic Prompt Compression".46            Zhuoshi Pan, Qianhui Wu, Huiqiang Jiang, Menglin Xia, Xufang Luo, Jue Zhang, Qingwei Lin, Victor Ruhle, Yuqing Yang, Chin-Yew Lin, H. Vicky Zhao, Lili Qiu, Dongmei Zhang.47            "LLMLingua-2: Context-Aware Data Distillation for Efficient and Faithful Task-Agnostic Prompt Compression". arXiv preprint arXiv:,48            Default is True.49        llmlingua2_config (dict, optional): A dictionary containing the configuration parameters for llmlingua-2. Default is50            {51                "max_batch_size": 50,52                "max_force_token": 100, # max number of the tokens which will be forcely preserved53            }54    Example:55        >>> compress_method = PromptCompressor(model_name="microsoft/llmlingua-2-xlm-roberta-large-meetingbank", use_llmlingua2=True, )56        >>> context = ["This is the first context sentence.", "Here is another context sentence."]57        >>> result = compress_method.compress_prompt(context, use_context_level_filter=True, target_token=5)58        >>> print(result["compressed_prompt"])59        # This will print the compressed version of the context.60 61    Note:62        The `PromptCompressor` class requires the Hugging Face Transformers library and an appropriate environment to load and run the models.63    """64 65    def __init__(66        self,67        model_name: str = "NousResearch/Llama-2-7b-hf",68        device_map: str = "cuda",69        model_config: dict = {},70        open_api_config: dict = {},71        use_llmlingua2: bool = False,72        llmlingua2_config: dict = {},73    ):74        self.model_name = model_name75        self.use_llmlingua2 = use_llmlingua276        self.retrieval_model = None77        self.retrieval_model_name = None78        self.open_api_config = open_api_config79        self.cache_bos_num = 1080        self.prefix_bos_num = 10081        self.oai_tokenizer = tiktoken.encoding_for_model("gpt-3.5-turbo")82 83        self.load_model(model_name, device_map, model_config)84        if use_llmlingua2:85            self.init_llmlingua2(**llmlingua2_config)86 87    def init_llmlingua2(88        self,89        max_batch_size: int = 50,90        max_force_token: int = 100,91    ):92        seed_everything(42)93        self.max_batch_size = max_batch_size94        self.max_seq_len = 512 # 512 (xlm-roberta)  256 (phobert)95        self.max_force_token = max_force_token96        self.special_tokens = set(      # trả ra special tokens97            [98                v99                for k, v in self.tokenizer.special_tokens_map.items()100                if k != "additional_special_tokens"101            ]102        )103 104        self.added_tokens = [f"[NEW{i}]" for i in range(max_force_token)]105        self.tokenizer.add_special_tokens(      # Add special token in force token106            {"additional_special_tokens": self.added_tokens}107        )108        self.model.resize_token_embeddings(len(self.tokenizer))     # Resize embedding dim109 110    def load_model(111        self, model_name: str, device_map: str = "cuda", model_config: dict = {}112    ):113        trust_remote_code = model_config.get("trust_remote_code", True)114        if "trust_remote_code" not in model_config:115            model_config["trust_remote_code"] = trust_remote_code116        config = AutoConfig.from_pretrained(model_name, **model_config)117        tokenizer = AutoTokenizer.from_pretrained(model_name, **model_config)118        if model_config.get("pad_to_left", True):119            tokenizer.padding_side = "left"120            tokenizer.pad_token_id = (121                config.pad_token_id if config.pad_token_id else tokenizer.eos_token_id122            )123        MODEL_CLASS = (124            AutoModelForTokenClassification # Use llmlingua2125            if any("ForTokenClassification" in ar for ar in config.architectures)126            else AutoModelForCausalLM127        )128        self.device = (129            device_map130            if any(key in device_map for key in ["cuda", "cpu", "mps"])131            else "cuda"132        )133        if "cuda" in device_map or "cpu" in device_map:134            model = MODEL_CLASS.from_pretrained(135                model_name,136                torch_dtype=model_config.get(137                    "torch_dtype", "auto" if device_map == "cuda" else torch.float32138                ),139                device_map=device_map,140                config=config,141                ignore_mismatched_sizes=True,142                **model_config,143            )144        else:145            model = MODEL_CLASS.from_pretrained(146                model_name,147                device_map=device_map,148                torch_dtype=model_config.get("torch_dtype", "auto"),149                pad_token_id=tokenizer.pad_token_id,150                **model_config,151            )152        self.tokenizer = tokenizer153        self.model = model154        self.context_idxs = []155        self.max_position_embeddings = config.max_position_embeddings156 157    def get_ppl(158        self,159        text: str,160        granularity: str = "sentence",161        input_ids=None,162        attention_mask=None,163        past_key_values=None,164        return_kv=False,165        end=None,166        condition_mode: str = "none",167        condition_pos_id: int = 0,168    ):169        if input_ids is None:170            tokenized_text = self.tokenizer(text, return_tensors="pt")171            input_ids = tokenized_text["input_ids"].to(self.device)172            attention_mask = tokenized_text["attention_mask"].to(self.device)173        if past_key_values is not None:174            past_length = past_key_values[0][0].shape[2]175        else:176            past_length = 0177        if end is None:178            end = input_ids.shape[1]179        end = min(end, past_length + self.max_position_embeddings)180        with torch.no_grad():181            response = self.model(182                input_ids[:, past_length:end],183                attention_mask=attention_mask[:, :end],184                past_key_values=past_key_values,185                use_cache=True,186            )187            past_key_values = response.past_key_values188 189        shift_logits = response.logits[..., :-1, :].contiguous()190        shift_labels = input_ids[..., past_length + 1 : end].contiguous()191        # Flatten the tokens192        active = (attention_mask[:, past_length:end] == 1)[..., :-1].view(-1)193        active_logits = shift_logits.view(-1, shift_logits.size(-1))[active]194        active_labels = shift_labels.view(-1)[active]195        loss_fct = torch.nn.CrossEntropyLoss(reduction="none")196        loss = loss_fct(active_logits, active_labels)197        if condition_mode == "before":198            loss = loss[:condition_pos_id]199        elif condition_mode == "after":200            loss = loss[condition_pos_id:]201        res = loss.mean() if granularity == "sentence" else loss202        return (res, past_key_values) if return_kv else res203 204    def __call__(self, *args, **kwargs):205        return self.compress_prompt(*args, **kwargs)206 207    def structured_compress_prompt(208        self,209        context: List[str],210        instruction: str = "",211        question: str = "",212        rate: float = 0.5,213        target_token: float = -1,214        iterative_size: int = 200,215        force_context_ids: List[int] = None,216        force_context_number: int = None,217        use_sentence_level_filter: bool = False,218        use_context_level_filter: bool = True,219        use_token_level_filter: bool = True,220        keep_split: bool = False,221        keep_first_sentence: int = 0,222        keep_last_sentence: int = 0,223        keep_sentence_number: int = 0,224        high_priority_bonus: int = 100,225        context_budget: str = "+100",226        token_budget_ratio: float = 1.4,227        condition_in_question: str = "none",228        reorder_context: str = "original",229        dynamic_context_compression_ratio: float = 0.0,230        condition_compare: bool = False,231        add_instruction: bool = False,232        rank_method: str = "llmlingua",233        concate_question: bool = True,234    ):235        """236        Compresses the given prompt context based on a specified structure.237 238        Each element of context should be segmented using one or more non-nested '<llmlingua></llmlingua>' tags.239        Each '<llmlingua>' tag can include optional parameters 'rate' and 'compress' (e.g., '<llmlingua, rate=0.3, compress=True>'),240        indicating the compression rate for that segment. Default values are 'rate=rate' and 'compress=True'.241        When 'compress' is set to False, it overrides the 'rate' parameter, resulting in no compression for that segment.242 243        Args:244            context (List[str]): List of context strings divided by '<llmlingua></llmlingua>' tags with optional compression settings.245            instruction (str, optional): Additional instruction text to be included in the prompt. Default is an empty string.246            question (str, optional): A specific question that the prompt is addressing. Default is an empty string.247            rate (float, optional): The compression rate is defined the same as in paper "Language Modeling Is Compression".248                Delétang, Grégoire, Anian Ruoss, Paul-Ambroise Duquenne, Elliot Catt, Tim Genewein, Christopher Mattern,249                Jordi Grau-Moya et al. "Language modeling is compression." arXiv preprint arXiv:2309.10668 (2023):250                .. math::\text{Compression Rate} = \frac{\text{Compressed Size}}{\text{Raw Size}}251                Default is 0.5. The actual compression rate is generally lower than the specified target, but there can be252                fluctuations due to differences in tokenizers. If specified, it should be a float less than or equal253                to 1.0, representing the target compression rate. ``rate``, is applicable only within the context-level filter254                and the sentence-level filter. In the token-level filter, the rate for each segment overrides the global rate.255                However, for segments where no specific rate is defined, the global rate serves as the default value. The final256                compression rate of the entire text is a composite result of multiple compression rates applied across different sections.257            target_token (float, optional): The global maximum number of tokens to be achieved. Default is -1, indicating no258                specific target. The actual number of tokens after compression should generally be less than the specified target_token,259                but there can be fluctuations due to differences in tokenizers. If specified, compression will be based on the target_token as260                the sole criterion, overriding the ``rate``. ``target_token``, is applicable only within the context-level261                filter and the sentence-level filter. In the token-level filter, the rate for each segment overrides the global target token.262                However, for segments where no specific rate is defined, the global rate calculated from global target token serves263                as the default value. The final target token of the entire text is a composite result of multiple compression rates264                applied across different sections.265            iterative_size (int, optional): The number of tokens to consider in each iteration of compression. Default is 200.266            force_context_ids (List[int], optional): List of specific context IDs to always include in the compressed result. Default is None.267            force_context_number (int, optional): The number of context sections to forcibly include. Default is None.268            use_sentence_level_filter (bool, optional): Whether to apply sentence-level filtering in compression. Default is False.269            use_context_level_filter (bool, optional): Whether to apply context-level filtering in compression. Default is True.270            use_token_level_filter (bool, optional): Whether to apply token-level filtering in compression. Default is True.271            keep_split (bool, optional): Whether to preserve the original separators without compression. Default is False.272            keep_first_sentence (int, optional): Number of sentences to forcibly preserve from the start of the context. Default is 0.273            keep_last_sentence (int, optional): Number of sentences to forcibly preserve from the end of the context. Default is 0.274            keep_sentence_number (int, optional): Total number of sentences to forcibly preserve in the compression. Default is 0.275            high_priority_bonus (int, optional): Bonus score for high-priority sentences to influence their likelihood of being retained. Default is 100.276            context_budget (str, optional): Token budget for the context-level filtering, expressed as a string to indicate flexibility. Default is "+100".277            token_budget_ratio (float, optional): Ratio to adjust token budget during sentence-level filtering. Default is 1.4.278            condition_in_question (str, optional): Specific condition to apply to question in the context. Default is "none".279            reorder_context (str, optional): Strategy for reordering context in the compressed result. Default is "original".280            dynamic_context_compression_ratio (float, optional): Ratio for dynamically adjusting context compression. Default is 0.0.281            condition_compare (bool, optional): Whether to enable condition comparison during token-level compression. Default is False.282            add_instruction (bool, optional): Whether to add the instruction to the prompt prefix. Default is False.283            rank_method (str, optional): Method used for ranking elements during compression. Default is "llmlingua".284            concate_question (bool, optional): Whether to concatenate the question to the compressed prompt. Default is True.285 286        Returns:287            dict: A dictionary containing:288                - "compressed_prompt" (str): The resulting compressed prompt.289                - "origin_tokens" (int): The original number of tokens in the input.290                - "compressed_tokens" (int): The number of tokens in the compressed output.291                - "ratio" (str): The compression ratio achieved, calculated as the original token number divided by the token number after compression.292                - "rate" (str): The compression rate achieved, in a human-readable format.293                - "saving" (str): Estimated savings in GPT-4 token usage.294        """295        if not context:296            context = [" "]297        if isinstance(context, str):298            context = [context]299        context = [300            self.tokenizer.decode(self.tokenizer(c, add_special_tokens=False).input_ids)301            for c in context302        ]303        context_tokens_length = [self.get_token_length(c) for c in context]304        instruction_tokens_length, question_tokens_length = self.get_token_length(305            instruction306        ), self.get_token_length(question)307        if target_token == -1:308            target_token = (309                (310                    instruction_tokens_length311                    + question_tokens_length312                    + sum(context_tokens_length)313                )314                * rate315                - instruction_tokens_length316                - (question_tokens_length if concate_question else 0)317            )318        else:319            rate = target_token / sum(context_tokens_length)320        (321            context,322            context_segs,323            context_segs_rate,324            context_segs_compress,325        ) = self.segment_structured_context(context, rate)326        return self.compress_prompt(327            context,328            instruction,329            question,330            rate,331            target_token,332            iterative_size,333            force_context_ids,334            force_context_number,335            use_sentence_level_filter,336            use_context_level_filter,337            use_token_level_filter,338            keep_split,339            keep_first_sentence,340            keep_last_sentence,341            keep_sentence_number,342            high_priority_bonus,343            context_budget,344            token_budget_ratio,345            condition_in_question,346            reorder_context,347            dynamic_context_compression_ratio,348            condition_compare,349            add_instruction,350            rank_method,351            concate_question,352            context_segs=context_segs,353            context_segs_rate=context_segs_rate,354            context_segs_compress=context_segs_compress,355        )356 357    def compress_prompt(358        self,359        context: List[str],360        instruction: str = "",361        question: str = "",362        # llmlingua1363        rate: float = 0.5,364        target_token: float = -1,365        iterative_size: int = 200,366        force_context_ids: List[int] = None,367        force_context_number: int = None,368        use_sentence_level_filter: bool = False,    # hầu như ko dùng369        use_context_level_filter: bool = True,370        use_token_level_filter: bool = True,371        keep_split: bool = False,372        keep_first_sentence: int = 0,373        keep_last_sentence: int = 0,374        keep_sentence_number: int = 0,375        high_priority_bonus: int = 100,376        context_budget: str = "+100",377        token_budget_ratio: float = 1.4,378        condition_in_question: str = "none",379        reorder_context: str = "original",380        dynamic_context_compression_ratio: float = 0.0,381        condition_compare: bool = False,382        add_instruction: bool = False,383        rank_method: str = "llmlingua",384        concate_question: bool = True,385        context_segs: List[str] = None,386        context_segs_rate: List[float] = None,387        context_segs_compress: List[bool] = None,388        # llmlingua2389        target_context: int = -1,       # config số lượng context trả về390        context_level_rate: float = 1.0,        # config tỉ lệ nén nhỏ nhất khi sử dụng context-level391        context_level_target_token: int = -1,    # config số token tối đa khi sử dụng context-level392        return_word_label: bool = False,    # config liệu có trả về word trong label393        word_sep: str = "\t\t|\t\t",394        label_sep: str = " ",395        token_to_word: str = "mean",    # Config phương pháp sử dụng chuyển từ xác suất token sang xác suất word396        force_tokens: List[str] = [],   # Config các tokens luôn được giữ lại trong compressed prompt397        force_reserve_digit: bool = False,  # Config liệu có bắt buộc giữ các token là chữ số398        drop_consecutive: bool = False,     # Config liệu có loại bỏ các tokens trong force token khi mà các từ này xuất hiện trong compressed prompt399        chunk_end_tokens: List[str] = [".", "\n"],  # Config các stop token để segment chunk400    ):401        """402        Compresses the given context.403 404        Args:405            context (List[str]): List of context strings that form the basis of the prompt.406            instruction (str, optional): Additional instruction text to be included in the prompt. Default is an empty string.407            question (str, optional): A specific question that the prompt is addressing. Default is an empty string.408            rate (float, optional): The maximum compression rate target to be achieved. The compression rate is defined409                the same as in paper "Language Modeling Is Compression". Delétang, Grégoire, Anian Ruoss, Paul-Ambroise Duquenne,410                Elliot Catt, Tim Genewein, Christopher Mattern, Jordi Grau-Moya et al. "Language modeling is compression."411                arXiv preprint arXiv:2309.10668 (2023):412                .. math::\text{Compression Rate} = \frac{\text{Compressed Size}}{\text{Raw Size}}413                Default is 0.5. The actual compression rate is generally lower than the specified target, but there can be414                fluctuations due to differences in tokenizers. If specified, it should be a float less than or equal415                to 1.0, representing the target compression rate.416            target_token (float, optional): The maximum number of tokens to be achieved. Default is -1, indicating no specific target.417                The actual number of tokens after compression should generally be less than the specified target_token, but there can418                be fluctuations due to differences in tokenizers. If specified, compression will be based on the target_token as419                the sole criterion, overriding the ``rate``.420            iterative_size (int, optional): The number of tokens to consider in each iteration of compression. Default is 200.421            force_context_ids (List[int], optional): List of specific context IDs to always include in the compressed result. Default is None.422            force_context_number (int, optional): The number of context sections to forcibly include. Default is None.423            use_sentence_level_filter (bool, optional): Whether to apply sentence-level filtering in compression. Default is False.424            use_context_level_filter (bool, optional): Whether to apply context-level filtering in compression. Default is True.425            use_token_level_filter (bool, optional): Whether to apply token-level filtering in compression. Default is True.426            keep_split (bool, optional): Whether to preserve the original separators without compression. Default is False.427            keep_first_sentence (int, optional): Number of sentences to forcibly preserve from the start of the context. Default is 0.428            keep_last_sentence (int, optional): Number of sentences to forcibly preserve from the end of the context. Default is 0.429            keep_sentence_number (int, optional): Total number of sentences to forcibly preserve in the compression. Default is 0.430            high_priority_bonus (int, optional): Bonus score for high-priority sentences to influence their likelihood of being retained. Default is 100.431            context_budget (str, optional): Token budget for the context-level filtering, expressed as a string to indicate flexibility. Default is "+100".432            token_budget_ratio (float, optional): Ratio to adjust token budget during sentence-level filtering. Default is 1.4.433            condition_in_question (str, optional): Specific condition to apply to question in the context. Default is "none".434            reorder_context (str, optional): Strategy for reordering context in the compressed result. Default is "original".435            dynamic_context_compression_ratio (float, optional): Ratio for dynamically adjusting context compression. Default is 0.0.436            condition_compare (bool, optional): Whether to enable condition comparison during token-level compression. Default is False.437            add_instruction (bool, optional): Whether to add the instruction to the prompt prefix. Default is False.438            rank_method (str, optional): Method used for ranking elements during compression. Default is "llmlingua".439            concate_question (bool, optional): Whether to concatenate the question to the compressed prompt. Default is True.440 441            target_context (int, optional): The maximum number of contexts to be achieved. Default is -1, indicating no specific target.442            context_level_rate (float, optional): The minimum compression rate target to be achieved in context level. Default is 1.0.443            context_level_target_token (float, optional): The maximum number of tokens to be achieved in context level compression.444                Default is -1, indicating no specific target. Only used in the coarse-to-fine compression senario.445            force_context_ids (List[int], optional): List of specific context IDs to always include in the compressed result. Default is None.446            return_word_label (bool, optional): Whether to return word with corresponding label. Default is False.447            word_sep (str, optional): The sep token used in fn_labeled_original_prompt to partition words. Default is "\t\t|\t\t".448            label_sep (str, optional): The sep token used in fn_labeled_original_prompt to partition word and label.  Default is " ".449            token_to_word (str, optional): How to convert token probability to word probability. Default is "mean".450            force_tokens (List[str], optional): List of specific tokens to always include in the compressed result. Default is [].451            force_reserve_digit  (bool, optional): Whether to forcibly reserve tokens that containing digit (0,...,9). Default is False.452            drop_consecutive (bool, optinal): Whether to drop tokens which are in 'force_tokens' but appears consecutively in compressed prompt.453                Default is False.454            chunk_end_tokens (List[str], optinal): The early stop tokens for segmenting chunk. Default is [".", "\n"],455        Returns:456            dict: A dictionary containing:457                - "compressed_prompt" (str): The resulting compressed prompt.458                - "compressed_prompt_list" (List[str]): List of the resulting compressed prompt. Only used in llmlingua2.459                - "fn_labeled_original_prompt" (str): original words along with their labels460                    indicating whether to reserve in compressed prompt, in the format (word label_sep label)461                    Only used in llmlingua2 when return_word_label = True.462                - "origin_tokens" (int): The original number of tokens in the input.463                - "compressed_tokens" (int): The number of tokens in the compressed output.464                - "ratio" (str): The compression ratio achieved, calculated as the original token number divided by the token number after compression.465                - "rate" (str): The compression rate achieved, in a human-readable format.466                - "saving" (str): Estimated savings in GPT-4 token usage.467        """468        if self.use_llmlingua2: # dùng cả llmlingua2 và llmlingua1469            return self.compress_prompt_llmlingua2(470                context,471                rate=rate,472                target_token=target_token,473                use_context_level_filter=use_context_level_filter,      # True474                use_token_level_filter=use_token_level_filter,475                target_context=target_context,476                context_level_rate=context_level_rate,477                context_level_target_token=context_level_target_token,478                force_context_ids=force_context_ids,479                return_word_label=return_word_label,480                word_sep=word_sep,481                label_sep=label_sep,482                token_to_word=token_to_word,483                force_tokens=force_tokens,484                force_reserve_digit=force_reserve_digit,485                drop_consecutive=drop_consecutive,486                chunk_end_tokens=chunk_end_tokens,487            )488        489        # return luôn một hàm là ko chạy tiếp phần sau nữa490        assert (491            rate <= 1.0492        ), "Error: 'rate' must not exceed 1.0. The value of 'rate' indicates compression rate and must be within the range [0, 1]."493 494        if not context:495            context = [" "]496        if isinstance(context, str):497            context = [context]498        assert not (499            rank_method == "longllmlingua" and not question500        ), "In the LongLLMLingua, it is necessary to set a question."501        if condition_compare and "_condition" not in condition_in_question:502            condition_in_question += "_condition"503        if rank_method == "longllmlingua":504            if condition_in_question == "none":505                condition_in_question = "after"506        elif rank_method == "llmlingua":507            condition_in_question = (508                "none"509                if "_condition" not in condition_in_question510                else "none_condition"511            )512        origin_tokens = len(513            self.oai_tokenizer.encode(514                "\n\n".join([instruction] + context + [question]).strip()515            )516        )517        context_tokens_length = [self.get_token_length(c) for c in context]518        instruction_tokens_length, question_tokens_length = self.get_token_length(519            instruction520        ), self.get_token_length(question)521        if target_token == -1:522            target_token = (523                (524                    instruction_tokens_length525                    + question_tokens_length526                    + sum(context_tokens_length)527                )528                * rate529                - instruction_tokens_length530                - (question_tokens_length if concate_question else 0)531            )532        condition_flag = "_condition" in condition_in_question533        condition_in_question = condition_in_question.replace("_condition", "")534 535        if len(context) > 1 and use_context_level_filter:536            context, dynamic_ratio, context_used = self.control_context_budget(537                context,538                context_tokens_length,539                target_token,540                force_context_ids,541                force_context_number,542                question,543                condition_in_question,544                reorder_context=reorder_context,545                dynamic_context_compression_ratio=dynamic_context_compression_ratio,546                rank_method=rank_method,547                context_budget=context_budget,548                context_segs=context_segs,549                context_segs_rate=context_segs_rate,550                context_segs_compress=context_segs_compress,551            )552            #print('Context used: ', context_used)553            if context_segs is not None:554                context_segs = [context_segs[idx] for idx in context_used]555                context_segs_rate = [context_segs_rate[idx] for idx in context_used]556                context_segs_compress = [557                    context_segs_compress[idx] for idx in context_used558                ]559        else:560            dynamic_ratio = [0.0] * len(context)561 562        segments_info = []563        if use_sentence_level_filter:564            context, segments_info = self.control_sentence_budget(565                context,566                target_token,567                keep_first_sentence=keep_first_sentence,568                keep_last_sentence=keep_last_sentence,569                keep_sentence_number=keep_sentence_number,570                high_priority_bonus=high_priority_bonus,571                token_budget_ratio=token_budget_ratio,572                question=question,573                condition_in_question=condition_in_question,574                rank_method=rank_method,575                context_segs=context_segs,576                context_segs_rate=context_segs_rate,577                context_segs_compress=context_segs_compress,578            )579        elif context_segs is not None:580            for context_idx in range(len(context)):581                segments_info.append(582                    [583                        (len(seg_text), seg_rate, seg_compress)584                        for seg_text, seg_rate, seg_compress in zip(585                            context_segs[context_idx],586                            context_segs_rate[context_idx],587                            context_segs_compress[context_idx],588                        )589                    ]590                )591        segments_info = [592            self.concate_segment_info(segment_info) for segment_info in segments_info593        ]594 595        if condition_flag:596            prefix = question + "\n\n" + instruction if add_instruction else question597            if (598                self.get_token_length(prefix + "\n\n") + iterative_size * 2599                > self.max_position_embeddings600            ):601                tokens = self.tokenizer(prefix, add_special_tokens=False).input_ids602                prefix = self.tokenizer.decode(603                    tokens[: self.prefix_bos_num]604                    + tokens[605                        len(tokens)606                        - self.max_position_embeddings607                        + 2608                        + self.prefix_bos_num609                        + 2 * iterative_size :610                    ]611                )612            start = self.get_prefix_length(prefix + "\n\n", context[0])613            context = [prefix] + context614        else:615            start = 0616 617        #print('Context level: ', context)618 619        if use_token_level_filter:620            context = self.iterative_compress_prompt(621                context,622                target_token,623                iterative_size=iterative_size,624                keep_split=keep_split,625                start=start,626                dynamic_ratio=dynamic_ratio,627                condition_compare=condition_compare,628                segments_info=segments_info,629            )630            compressed_prompt = (631                self.tokenizer.batch_decode(context[0])[0]632                .replace("<s> ", "")633                .replace("<s>", "")634            )635        else:636            if condition_flag:637                context = context[1:]638            compressed_prompt = "\n\n".join(context)639            #compressed_prompt = " ".join(context)640 641 642        compressed_prompt = "\n\n".join(context)        # gồm cả context của 2 loại level643        #compressed_prompt = " ".join(context)644        res = []645        if instruction:646            res.append(instruction)647        if compressed_prompt.strip():648            res.append(compressed_prompt)649        if question and concate_question:650            res.append(question)651 652        compressed_prompt = "\n\n".join(res)653        #compressed_prompt = " ".join(res)654 655        compressed_tokens = len(self.oai_tokenizer.encode(compressed_prompt))656        saving = (origin_tokens - compressed_tokens) * 0.06 / 1000657        ratio = 1 if compressed_tokens == 0 else origin_tokens / compressed_tokens658        rate = 1 / ratio659        return {660            "compressed_prompt": compressed_prompt,661            "origin_tokens": origin_tokens,662            "compressed_tokens": compressed_tokens,663            "ratio": f"{ratio:.1f}x",664            "rate": f"{rate * 100:.1f}%",665            "saving": f", Saving ${saving:.1f} in GPT-4.",666        }667 668    def compress_prompt_llmlingua2(669        self,670        context: List[str],671        rate: float = 0.5,672        target_token: int = -1,673        use_context_level_filter: bool = False,     # True674        use_token_level_filter: bool = True,675        target_context: int = -1,676        context_level_rate: float = 1.0,677        context_level_target_token: int = -1,678        force_context_ids: List[int] = [],679        return_word_label: bool = False,680        word_sep: str = "\t\t|\t\t",681        label_sep: str = " ",682        token_to_word: str = "mean",683        force_tokens: List[str] = [],684        force_reserve_digit: bool = False,685        drop_consecutive: bool = False,686        chunk_end_tokens: List[str] = [".", "\n"],687    ):688        """689        Compresses the given context, instruction and question.690 691        Args:692            context (List[str]): List of context strings that form the basis of the prompt.693            rate (float, optional): The minimum compression rate target to be achieved. Default is 0.5. The actual compression rate694                generally exceeds the specified target, but there can be fluctuations due to differences in tokenizers. If specified,695                it should be a float greater than or equal to 1.0, representing the target compression rate.696            target_token (int, optional): The maximum number of tokens to be achieved. Default is -1, indicating no specific target.697                The actual number of tokens after compression should generally be less than the specified target_token, but there can698                be fluctuations due to differences in tokenizers. If specified, compression will be based on the target_token as699                the sole criterion, overriding the rate.700            target_context (int, optional): The maximum number of contexts to be achieved. Default is -1, indicating no specific target.701                Only used in the coarse-to-fine compression.702            context_level_rate (float, optional): The minimum compression rate target to be achieved in context level. Default is 1.0.703                Only used in the coarse-to-fine compression.704            context_level_target_token (float, optional): The maximum number of tokens to be achieved in context level compression.705                Default is -1, indicating no specific target. Only used in the coarse-to-fine compression senario.706            force_context_ids (List[int], optional): List of specific context IDs to always include in the compressed result. Default is None.707            return_word_label (bool, optional): Whether to return word with corresponding label. Default is False.708            word_sep (str, optional): The sep token used in fn_labeled_original_prompt to partition words. Default is "\t\t|\t\t".709            label_sep (str, optional): The sep token used in fn_labeled_original_prompt to partition word and label.  Default is " ".710            token_to_word (str, optional): How to convert token probability to word probability. Default is "mean".711            force_tokens (List[str], optional): List of specific tokens to always include in the compressed result. Default is [].712            force_reserve_digit  (bool, optional): Whether to forcibly reserve tokens that containing digit (0,...,9). Default is False.713            drop_consecutive (bool, optinal): Whether to drop tokens which are in 'force_tokens' but appears consecutively in compressed prompt.714                Default is False.715            chunk_end_tokens (List[str], optional): The early stop tokens for segmenting chunk. Default is [".", "\n"].716        Returns:717            dict: A dictionary containing:718                - "compressed_prompt" (str): The resulting compressed prompt.719                - "compressed_prompt_list" (List[str]): List of the resulting compressed prompt. (compress cho từng chụnk)720                - "fn_labeled_original_prompt" (str): original words along with their labels     (các từ được giữ lại)721                    indicating whether to reserve in compressed prompt, in the format (word label_sep label)722                - "origin_tokens" (int): The original number of tokens in the input.723                - "compressed_tokens" (int): The number of tokens in the compressed output.724                - "ratio" (str): The compression ratio achieved, in a human-readable format.725                - "rate" (str): The compression rate achieved, in a human-readable format.726                - "saving" (str): Estimated savings in GPT-4 token usage.727 728        """729        assert len(force_tokens) <= self.max_force_token    # báo hiệu force token730        token_map = {}731        for i, t in enumerate(force_tokens):732            if len(self.tokenizer.tokenize(t)) != 1:733                token_map[t] = self.added_tokens[i]     # add token (là các force token) + các kí tự [NEW]734        #print('token map:', token_map)735        chunk_end_tokens = copy.deepcopy(chunk_end_tokens)736        for c in chunk_end_tokens:737            if c in token_map:738                chunk_end_tokens.append(token_map[c])       # Thêm các force token739        chunk_end_tokens = set(chunk_end_tokens)740        #print('chunk_end_tokens: ', chunk_end_tokens)741 742        if type(context) == str:743            context = [context]744        context = copy.deepcopy(context)745 746        #print('original context: ', context)747 748        if len(context) == 1 and use_context_level_filter:  # Sử dụng context-level     # len context > 1749            use_context_level_filter = False750        # Bắt buộc ko dùng context level751            752        n_original_token = 0753        context_chunked = []754        for i in range(len(context)):755            n_original_token += self.get_token_length(756                context[i], use_oai_tokenizer=True757            )758            for ori_token, new_token in token_map.items():759                context[i] = context[i].replace(ori_token, new_token)760            context_chunked.append(761                self.__chunk_context(context[i], chunk_end_tokens=chunk_end_tokens)     # Hàm chia chunk trong llmlingua2762            )       # list chunk763        #print('context chunked:', context_chunked) (vẫn còn 5 context ban đầu)764 765        #========================================================================================766        # tinh chỉnh hyperparameter767        if use_context_level_filter: # mặc định là dùng context level trong llmlingua2 do trong hàm compress prompt ban đầu default True   768            # want use_context_level_filter but do not specify any parameters in context level?769            # Sử dụng context-level nhưng không config cụ thể các tham số trong context-level770            # we will set context_level_rate = (rate + 1.0) / 2 if specify rate or target_token * 2 if specify target_token771            if (772                target_context <= 0773                and context_level_rate >= 1.0774                and context_level_target_token <= 0775            ):776                if target_token < 0 and rate < 1.0:777                    context_level_rate = (778                        (rate + 1.0) / 2 if use_token_level_filter else rate779                    )780                if target_token >= 0:781                    context_level_target_token = (782                        target_token * 2 if use_token_level_filter else target_token783                    )784 785            if target_context >= 0:     # Config target_context786                context_level_rate = min(target_context / len(context), 1.0)787            if context_level_target_token >= 0: # Config target_token (context_level)788                context_level_rate = min(789                    context_level_target_token / n_original_token, 1.0790                )791        #========================================================================================792            context_probs, context_words = self.__get_context_prob(793                context_chunked,        # list các context chunk794                token_to_word=token_to_word,795                force_tokens=force_tokens,796                token_map=token_map,797                force_reserve_digit=force_reserve_digit,798            )799            #print('context_probs: ', context_probs)    # prob của tưng context800            #print('context words: ', context_words)801            #print('context level rate: ', context_level_rate)802 803            threshold = np.percentile(      # filtering theo probs804                context_probs, int(100 * (1 - context_level_rate))      # chỉnh context_level_rate cho threshold, lọc context-level805            )806            #print('threshold: ', threshold)807 808            reserved_context = []       # các context được giữ lại theo threshold (từ 5 ban đầu có thể giảm đi (<5))809            context_label = [False] * len(context_probs)810            for i, p in enumerate(context_probs):811                if p >= threshold or (812                    force_context_ids is not None and i in force_context_ids813                ):814                    reserved_context.append(context_chunked[i])815                    context_label[i] = True816            #print('reserved_context: ', reserved_context)      # các context được giữ lại theo threshold817            #print('context_label: ', context_label)818 819            n_reserved_token = 0820            for chunks in reserved_context:821                for c in chunks:822                    n_reserved_token += self.get_token_length(c, use_oai_tokenizer=True)    # số lượng token được giữ lại823            if target_token >= 0:824                rate = min(target_token / n_reserved_token, 1.0)825 826            # có/ko sử dụng token-level vẫn trả về prompt compress 827            if use_token_level_filter:  # lọc theo context-level rồi lọc theo token-level ()828                compressed_context, word_list, word_label_list = self.__compress(829                    reserved_context,       # compress từng context reserved được giữ lại830                    reduce_rate=max(0, 1 - rate),831                    token_to_word=token_to_word,832                    force_tokens=force_tokens,833                    token_map=token_map,834                    force_reserve_digit=force_reserve_digit,835                    drop_consecutive=drop_consecutive,836                )837            else:838                compressed_context, word_list, word_label_list = self.__compress(839                    reserved_context,840                    reduce_rate=0,841                    token_to_word=token_to_word,842                    force_tokens=force_tokens,843                    token_map=token_map,844                    force_reserve_digit=force_reserve_digit,845                    drop_consecutive=drop_consecutive,846                )847 848            #print('compressed_context 1: ', compressed_context)  # list        # Final compressed849            #print('word_list: ', word_list)850            #print('word_label_list: ', word_label_list)    # labels list của từng chunk851 852            n_compressed_token = 0853            for c in compressed_context:854                n_compressed_token += self.get_token_length(c, use_oai_tokenizer=True)855            saving = (n_original_token - n_compressed_token) * 0.06 / 1000856            ratio = (857                1 if n_compressed_token == 0 else n_original_token / n_compressed_token858            )859            res = {860                "compressed_prompt": "\n\n".join(compressed_context),861                #"compressed_prompt": " ".join(compressed_context),862                #"compressed_prompt_list": compressed_context,863                "origin_tokens": n_original_token,864                "compressed_tokens": n_compressed_token,865                "ratio": f"{ratio:.1f}x",866                "rate": f"{1 / ratio * 100:.1f}%",867                "saving": f", Saving ${saving:.1f} in GPT-4.",868            }869            #print('res: ', res)870 871            if return_word_label:       # Nếu trả về label word (default=False)872                words = []873                labels = []874                j = 0875                for i in range(len(context)):876                    if context_label[i]:877                        words.extend(word_list[j])878                        labels.extend(word_label_list[j])879                        j += 1880                    else:881                        words.extend(context_words[i])882                        labels.extend([0] * len(context_words[i]))883                word_label_lines = word_sep.join(       # join theo word_sep884                    [f"{word}{label_sep}{label}" for word, label in zip(words, labels)]885                )886                res["fn_labeled_original_prompt"] = word_label_lines    # đánh labels từng từ887                #print('res: ', res)888            return res889        # tinh chỉnh hyperparameter 890        if target_token > 0:891            rate = min(target_token / n_original_token, 1.0)892 893        if use_token_level_filter:  # Chỉ Sử dụng token-level trong llmlingua2894            compressed_context, word_list, word_label_list = self.__compress(   # compress theo llmlingua2895                context_chunked,896                reduce_rate=max(0, 1 - rate),897                token_to_word=token_to_word,898                force_tokens=force_tokens,899                token_map=token_map,900                force_reserve_digit=force_reserve_digit,901                drop_consecutive=drop_consecutive,  # Whether to drop tokens which are in 'force_tokens' but appears consecutively in compressed prompt.902            )903        else:904            compressed_context, word_list, word_label_list = self.__compress(905                context_chunked,906                reduce_rate=0,907                token_to_word=token_to_word,908                force_tokens=force_tokens,909                token_map=token_map,910                force_reserve_digit=force_reserve_digit,911                drop_consecutive=drop_consecutive,912            )913        # giống phần trên914        #print('compressed_context 2: ', compressed_context)    # compress theo token-level915 916        n_compressed_token = 0917        for c in compressed_context:918            n_compressed_token += self.get_token_length(c, use_oai_tokenizer=True)919        saving = (n_original_token - n_compressed_token) * 0.06 / 1000920        ratio = 1 if n_compressed_token == 0 else n_original_token / n_compressed_token921        res = {922            "compressed_prompt": "\n\n".join(compressed_context),  923            #"compressed_prompt": " ".join(compressed_context),      # phân tách các context bằng "\n\n"924            #"compressed_prompt_list": compressed_context,925            "origin_tokens": n_original_token,926            "compressed_tokens": n_compressed_token,927            "ratio": f"{ratio:.1f}x",928            "rate": f"{1 / ratio * 100:.1f}%",929            "saving": f", Saving ${saving:.1f} in GPT-4.",930        }931        if return_word_label:932            words = []933            labels = []934            for w_list, l_list in zip(word_list, word_label_list):935                words.extend(w_list)936                labels.extend(l_list)937 938            word_label_lines = word_sep.join(939                [f"{word}{label_sep}{label}" for word, label in zip(words, labels)]940            )941            res["fn_labeled_original_prompt"] = word_label_lines942        return res943 944    def get_token_length(945        self,946        text: str,947        add_special_tokens: bool = True,948        use_oai_tokenizer: bool = False,949    ):950        if use_oai_tokenizer:951            return len(self.oai_tokenizer.encode(text))952        else:953            return len(954                self.tokenizer(text, add_special_tokens=add_special_tokens).input_ids955            )956 957    def get_prefix_length(self, prefix: str, text: str):958        possible_prefix_token = max(self.get_token_length(prefix, False) - 3, 1)959        full_input_ids = self.tokenizer(960            prefix + text[:100], add_special_tokens=False961        ).input_ids962        for i in range(possible_prefix_token, len(full_input_ids)):963            cur_prefix = self.tokenizer.decode(full_input_ids[:i])964            if cur_prefix == prefix:965                break966        assert self.tokenizer.decode(full_input_ids[i:]) == text[:100]967        return i968 969    def get_condition_ppl(970        self,971        text: str,972        question: str,973        condition_in_question: str = "none",974        granularity: str = "sentence",975    ):976        if condition_in_question == "none":977            return self.get_ppl(text, granularity=granularity)978        elif condition_in_question == "before":979            return self.get_ppl(980                question + text,981                granularity=granularity,982                condition_mode="after",983                condition_pos_id=self.get_token_length(question) - 1,984            )985        elif condition_in_question == "after":986            return self.get_ppl(987                text + question,988                granularity=granularity,989                condition_mode="after",990                condition_pos_id=self.get_token_length(text) - 1,991            )992 993    def get_dynamic_compression_ratio(994        self,995        context: list,996        target_token: float,997        iterative_size: int,998        dynamic_ratio: list,999        start: int,1000        seg_info: List[List[tuple]] = None,1001    ):1002        def get_ratio(base: float, delta: float):1003            return max(min(1, base + delta), 0)1004 1005        context_length = [self.get_token_length(ii, False) + 2 for ii in context]1006        if start:1007            context_length = context_length[1:]1008        tau = target_token / (sum(context_length) + 1)1009        res, idx, last, last_target = [], 0, 1, []1010        while idx < len(context_length):1011            if last + context_length[idx] >= iterative_size:1012                last_target.append(1013                    (iterative_size - last, get_ratio(tau, dynamic_ratio[idx]))1014                )1015                res.append(last_target)1016                last = last + context_length[idx] - iterative_size1017                if last > iterative_size:1018                    k = last // iterative_size1019                    res.extend(1020                        [[(iterative_size, get_ratio(tau, dynamic_ratio[idx]))]] * k1021                    )1022                    last -= k * iterative_size1023 1024                last_target = (1025                    [(last, get_ratio(tau, dynamic_ratio[idx]))] if last else []1026                )1027            else:1028                last += context_length[idx]1029                last_target.append(1030                    (context_length[idx], get_ratio(tau, dynamic_ratio[idx]))1031                )1032            idx += 11033        if last_target:1034            res.append(last_target)1035        return res1036 1037    def get_structured_dynamic_compression_ratio(1038        self,1039        context: list,1040        iterative_size: int,1041        dynamic_ratio: list,1042        start: int,1043        seg_info: List[List[tuple]] = None,1044    ):1045        if start:1046            pure_context = context[1:]1047        else:1048            pure_context = context1049        global_dynamic_rate, global_dynamic_compress, segments = [], [], []1050        for context_idx, text in enumerate(pure_context):1051            text_seen = 01052            for seg_idx, (seg_len, seg_rate, seg_compress) in enumerate(1053                seg_info[context_idx]1054            ):1055                seg_text = text[text_seen : text_seen + seg_len]1056                if (1057                    seg_idx == len(seg_info[context_idx]) - 11058                    and context_idx != len(pure_context) - 11059                ):1060                    seg_text += "\n\n"1061                segments.append(seg_text)1062                if seg_compress:1063                    global_dynamic_rate.append(seg_rate)1064                else:1065                    global_dynamic_rate.append(1.0)1066                global_dynamic_compress.append(seg_compress)1067                text_seen += seg_len1068        origin_text = "\n\n".join(pure_context)1069        assert len("".join(segments)) == len(origin_text)1070        assert len(segments) == len(global_dynamic_rate) == len(global_dynamic_compress)1071 1072        text_input_ids = self.tokenizer(1073            "\n\n".join(context), add_special_tokens=False1074        ).input_ids[start:]1075        assert self.tokenizer.decode(text_input_ids) == origin_text1076        dynamic_compression_ratio = self.token_segment(1077            text_input_ids,1078            iterative_size,1079            segments,1080            global_dynamic_rate,1081            global_dynamic_compress,1082        )1083        return dynamic_compression_ratio1084 1085    def token_segment(1086        self,1087        text_input_ids: List[int],1088        iterative_size: int,1089        segments: List[str],1090        global_dynamic_rate: List[float],1091        global_dynamic_compress: List[bool],1092    ):1093        decode_window = 31094        seg_idx, seg_seen, token_seen_num, last_rate = 0, 0, 0, -11095        dynamic_compression_rate, local_compresssion_rate = [], []1096        for i in range(len(text_input_ids)):1097            if i < decode_window:1098                id_pre, id_cur = text_input_ids[:i], text_input_ids[: i + 1]1099            else:1100                id_pre, id_cur = (1101                    text_input_ids[i - decode_window + 1 : i],1102                    text_input_ids[i - decode_window + 1 : i + 1],1103                )1104            cur_word = self.tokenizer.decode(id_cur)[1105                len(self.tokenizer.decode(id_pre)) :1106            ]1107            cur_word_len = len(cur_word)1108            if cur_word_len and cur_word_len >= len(segments[seg_idx]) - seg_seen:1109                possible_rate, possible_compress = [], []1110                while (1111                    cur_word_len and cur_word_len >= len(segments[seg_idx]) - seg_seen1112                ):1113                    possible_rate.append(global_dynamic_rate[seg_idx])1114                    possible_compress.append(global_dynamic_compress[seg_idx])1115                    cur_word_len -= len(segments[seg_idx]) - seg_seen1116                    seg_idx += 11117                    seg_seen = 01118                if cur_word_len:1119                    possible_rate.append(global_dynamic_rate[seg_idx])1120                    possible_compress.append(global_dynamic_compress[seg_idx])1121                new_rate = 1.0 if False in possible_compress else min(possible_rate)1122            else:1123                new_rate = global_dynamic_rate[seg_idx]1124            if new_rate != last_rate and i - token_seen_num:1125                local_compresssion_rate.append((i - token_seen_num, last_rate))1126                token_seen_num = i1127            last_rate = new_rate1128            seg_seen += cur_word_len1129            if (i + 1) % iterative_size == 0:1130                if token_seen_num != i + 1:1131                    local_compresssion_rate.append((i + 1 - token_seen_num, last_rate))1132                    token_seen_num = i + 11133                dynamic_compression_rate.append(local_compresssion_rate[:])1134                local_compresssion_rate = []1135        if token_seen_num != len(text_input_ids):1136            local_compresssion_rate.append(1137                (len(text_input_ids) - token_seen_num, last_rate)1138            )1139        if local_compresssion_rate != []:1140            dynamic_compression_rate.append(local_compresssion_rate[:])1141        return dynamic_compression_rate1142 1143    def control_context_budget(1144        self,1145        context: List[str],1146        context_tokens_length: List[int],1147        target_token: float,1148        force_context_ids: List[int] = None,1149        force_context_number: int = None,1150        question: str = "",1151        condition_in_question: str = "none",1152        reorder_context: str = "original",1153        dynamic_context_compression_ratio: float = 0.0,1154        rank_method: str = "longllmlingua",1155        context_budget: str = "+100",1156        context_segs: List[List[str]] = None,1157        context_segs_rate: List[List[float]] = None,1158        context_segs_compress: List[List[bool]] = None,1159    ):1160        demostrations_sort = self.get_rank_results(1161            context,1162            question,1163            rank_method,1164            condition_in_question,1165            context_tokens_length,1166        )1167 1168        if target_token < 0:1169            target_token = 1001170        target_token = eval("target_token" + context_budget)1171        res = []1172        used = force_context_ids if force_context_ids is not None else []1173        if context_segs is not None:1174            for idx, _ in enumerate(context):1175                if False in context_segs_compress[idx]:1176                    used.append(idx)1177 1178        self.context_idxs.append([x for idx, (x, _) in enumerate(demostrations_sort)])1179        for idx, _ in demostrations_sort:1180            if idx >= len(context_tokens_length):1181                continue1182            target_token -= context_tokens_length[idx]1183            if idx not in used:1184                used.append(idx)1185            if target_token < 0 or (1186                force_context_number is not None and len(res) >= force_context_number1187            ):1188                break1189        original_used = used1190        if reorder_context == "original":1191            used = sorted(used)1192        elif reorder_context == "two_stage":1193            l, r = [_ for idx, _ in enumerate(used) if idx % 2 == 0], [1194                _ for idx, _ in enumerate(used) if idx % 2 == 11195            ]1196            used = l + r[::-1]1197 1198        if dynamic_context_compression_ratio > 0:1199            N = len(used)1200            dynamic_ratio = [

Showing the first 1,200 of 2463 lines. Download the file for the rest.