CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
processing_utils.py1783 linesDownload Raw Back to transformers
1# Copyright 2022 The HuggingFace Inc. team.2#3# Licensed under the Apache License, Version 2.0 (the "License");4# you may not use this file except in compliance with the License.5# You may obtain a copy of the License at6#7#     http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS,11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# See the License for the specific language governing permissions and13# limitations under the License.14"""15Processing saving/loading class for common processors.16"""17 18import bisect19import copy20import inspect21import json22import os23import sys24import typing25import warnings26from dataclasses import dataclass27from pathlib import Path28from typing import Any, Optional, TypedDict, TypeVar, Union29 30import numpy as np31import typing_extensions32from huggingface_hub.errors import EntryNotFoundError33 34from .audio_utils import AudioInput, load_audio35from .dynamic_module_utils import custom_object_save36from .feature_extraction_utils import BatchFeature37from .image_utils import ChannelDimension, ImageInput, is_vision_available38from .utils.chat_template_utils import render_jinja_template39from .video_utils import VideoInput, VideoMetadata40 41 42if is_vision_available():43    from .image_utils import PILImageResampling44 45 46from .tokenization_utils_base import (47    PaddingStrategy,48    PreTokenizedInput,49    PreTrainedTokenizerBase,50    TextInput,51    TruncationStrategy,52)53from .utils import (54    AUDIO_TOKENIZER_NAME,55    CHAT_TEMPLATE_DIR,56    CHAT_TEMPLATE_FILE,57    LEGACY_PROCESSOR_CHAT_TEMPLATE_FILE,58    PROCESSOR_NAME,59    PushToHubMixin,60    TensorType,61    cached_file,62    copy_func,63    direct_transformers_import,64    download_url,65    is_offline_mode,66    is_remote_url,67    is_torch_available,68    list_repo_templates,69    logging,70)71from .utils.deprecation import deprecate_kwarg72 73 74if is_torch_available():75    from .modeling_utils import PreTrainedAudioTokenizerBase76 77 78logger = logging.get_logger(__name__)79 80# type hinting: specifying the type of processor class that inherits from ProcessorMixin81SpecificProcessorType = TypeVar("SpecificProcessorType", bound="ProcessorMixin")82 83# Dynamically import the Transformers module to grab the attribute classes of the processor from their names.84transformers_module = direct_transformers_import(Path(__file__).parent)85 86 87AUTO_TO_BASE_CLASS_MAPPING = {88    "AutoTokenizer": "PreTrainedTokenizerBase",89    "AutoFeatureExtractor": "FeatureExtractionMixin",90    "AutoImageProcessor": "ImageProcessingMixin",91    "AutoVideoProcessor": "BaseVideoProcessor",92}93 94if sys.version_info >= (3, 11):95    Unpack = typing.Unpack96else:97    Unpack = typing_extensions.Unpack98 99 100class TextKwargs(TypedDict, total=False):101    """102    Keyword arguments for text processing. For extended documentation, check out tokenization_utils_base methods and103    docstrings associated.104 105    Attributes:106        add_special_tokens (`bool`, *optional*)107            Whether or not to add special tokens when encoding the sequences.108        padding (`bool`, `str` or [`~utils.PaddingStrategy`], *optional*)109            Activates and controls padding.110        truncation (`bool`, `str` or [`~tokenization_utils_base.TruncationStrategy`], *optional*):111            Activates and controls truncation.112        max_length (`int`, *optional*):113            Controls the maximum length to use by one of the truncation/padding parameters.114        stride (`int`, *optional*):115            If set, the overflowing tokens will contain some tokens from the end of the truncated sequence.116        is_split_into_words (`bool`, *optional*):117            Whether or not the input is already pre-tokenized.118        pad_to_multiple_of (`int`, *optional*):119            If set, will pad the sequence to a multiple of the provided value.120        return_token_type_ids (`bool`, *optional*):121            Whether to return token type IDs.122        return_attention_mask (`bool`, *optional*):123            Whether to return the attention mask.124        return_overflowing_tokens (`bool`, *optional*):125            Whether or not to return overflowing token sequences.126        return_special_tokens_mask (`bool`, *optional*):127            Whether or not to return special tokens mask information.128        return_offsets_mapping (`bool`, *optional*):129            Whether or not to return `(char_start, char_end)` for each token.130        return_length (`bool`, *optional*):131            Whether or not to return the lengths of the encoded inputs.132        verbose (`bool`, *optional*):133            Whether or not to print more information and warnings.134        padding_side (`str`, *optional*):135            The side on which padding will be applied.136        return_mm_token_type_ids (`bool`, *optional*):137            Whether to return multimodal token type ids indicating mm placeholder token positions.138    """139 140    text_pair: Optional[Union[TextInput, PreTokenizedInput, list[TextInput], list[PreTokenizedInput]]]141    text_target: Union[TextInput, PreTokenizedInput, list[TextInput], list[PreTokenizedInput]]142    text_pair_target: Optional[Union[TextInput, PreTokenizedInput, list[TextInput], list[PreTokenizedInput]]]143    add_special_tokens: Optional[bool]144    padding: Union[bool, str, PaddingStrategy]145    truncation: Union[bool, str, TruncationStrategy]146    max_length: Optional[int]147    stride: Optional[int]148    is_split_into_words: Optional[bool]149    pad_to_multiple_of: Optional[int]150    return_token_type_ids: Optional[bool]151    return_attention_mask: Optional[bool]152    return_overflowing_tokens: Optional[bool]153    return_special_tokens_mask: Optional[bool]154    return_offsets_mapping: Optional[bool]155    return_length: Optional[bool]156    verbose: Optional[bool]157    padding_side: Optional[str]158    return_mm_token_type_ids: Optional[bool]159 160 161class ImagesKwargs(TypedDict, total=False):162    """163    Keyword arguments for image processing. For extended documentation, check the appropriate ImageProcessor164    class methods and docstrings.165 166    Attributes:167        do_resize (`bool`, *optional*):168            Whether to resize the image.169        size (`dict[str, int]`, *optional*):170            Resize the shorter side of the input to `size["shortest_edge"]`.171        crop_size (`dict[str, int]`, *optional*):172            Desired output size when applying center-cropping.173        resample (`PILImageResampling`, *optional*):174            Resampling filter to use if resizing the image.175        do_rescale (`bool`, *optional*):176            Whether to rescale the image by the specified scale `rescale_factor`.177        rescale_factor (`int` or `float`, *optional*):178            Scale factor to use if rescaling the image.179        do_normalize (`bool`, *optional*):180            Whether to normalize the image.181        image_mean (`float` or `list[float]`, *optional*):182            Mean to use if normalizing the image.183        image_std (`float` or `list[float]`, *optional*):184            Standard deviation to use if normalizing the image.185        do_pad (`bool`, *optional*):186            Whether to pad the image to the `(max_height, max_width)` of the images in the batch.187        pad_size (`dict[str, int]`, *optional*):188            The size `{"height": int, "width" int}` to pad the images to.189        do_center_crop (`bool`, *optional*):190            Whether to center crop the image.191        data_format (`ChannelDimension` or `str`, *optional*):192            The channel dimension format for the output image.193        input_data_format (`ChannelDimension` or `str`, *optional*):194            The channel dimension format for the input image.195        device (`str`, *optional*):196            The device to use for processing (e.g. "cpu", "cuda"), only relevant for fast image processing.197    """198 199    do_resize: Optional[bool]200    size: Optional[dict[str, int]]201    crop_size: Optional[dict[str, int]]202    resample: Optional[Union["PILImageResampling", int]]203    do_rescale: Optional[bool]204    rescale_factor: Optional[float]205    do_normalize: Optional[bool]206    image_mean: Optional[Union[float, list[float]]]207    image_std: Optional[Union[float, list[float]]]208    do_pad: Optional[bool]209    pad_size: Optional[dict[str, int]]210    do_center_crop: Optional[bool]211    data_format: Optional[ChannelDimension]212    input_data_format: Optional[Union[str, ChannelDimension]]213    device: Optional[str]214 215 216class VideosKwargs(TypedDict, total=False):217    """218    Keyword arguments for video processing.219 220    Attributes:221        do_convert_rgb (`bool`):222            Whether to convert the video to RGB format.223        do_resize (`bool`):224            Whether to resize the video.225        size (`dict[str, int]`, *optional*):226            Resize the shorter side of the input to `size["shortest_edge"]`.227        default_to_square (`bool`, *optional*, defaults to `self.default_to_square`):228            Whether to default to a square when resizing, if size is an int.229        resample (`PILImageResampling`, *optional*):230            Resampling filter to use if resizing the video.231        do_rescale (`bool`, *optional*):232            Whether to rescale the video by the specified scale `rescale_factor`.233        rescale_factor (`int` or `float`, *optional*):234            Scale factor to use if rescaling the video.235        do_normalize (`bool`, *optional*):236            Whether to normalize the video.237        image_mean (`float` or `list[float]`, *optional*):238            Mean to use if normalizing the video.239        image_std (`float` or `list[float]`, *optional*):240            Standard deviation to use if normalizing the video.241        do_center_crop (`bool`, *optional*):242            Whether to center crop the video.243        do_sample_frames (`bool`, *optional*):244            Whether to sample frames from the video before processing or to process the whole video.245        video_metadata (`Union[VideoMetadata, dict]`, *optional*):246            Metadata of the video containing information about total duration, fps and total number of frames.247        num_frames (`int`, *optional*):248            Maximum number of frames to sample when `do_sample_frames=True`.249        fps (`int` or `float`, *optional*):250            Target frames to sample per second when `do_sample_frames=True`.251        crop_size (`dict[str, int]`, *optional*):252            Desired output size when applying center-cropping.253        data_format (`ChannelDimension` or `str`, *optional*):254            The channel dimension format for the output video.255        input_data_format (`ChannelDimension` or `str`, *optional*):256            The channel dimension format for the input video.257        return_metadata (`ChannelDimension` or `str`, *optional*):258            Whether to return video metadata or not.259    """260 261    do_convert_rgb: Optional[bool]262    do_resize: Optional[bool]263    size: Optional[dict[str, int]]264    default_to_square: Optional[bool]265    resample: Optional["PILImageResampling"]266    do_rescale: Optional[bool]267    rescale_factor: Optional[float]268    do_normalize: Optional[bool]269    image_mean: Optional[Union[float, list[float]]]270    image_std: Optional[Union[float, list[float]]]271    do_center_crop: Optional[bool]272    crop_size: Optional[dict[str, int]]273    data_format: Optional[ChannelDimension]274    input_data_format: Optional[Union[str, ChannelDimension]]275    device: Optional[str]276    do_sample_frames: Optional[bool]277    video_metadata: Optional[Union[VideoMetadata, dict]]278    fps: Optional[Union[int, float]]279    num_frames: Optional[int]280    return_metadata: Optional[bool]281 282 283class AudioKwargs(TypedDict, total=False):284    """285    Keyword arguments for audio processing.286 287    Attributes:288        sampling_rate (`int`, *optional*):289            The sampling rate at which the `raw_speech` input was sampled.290        raw_speech (`np.ndarray`, `list[float]`, `list[np.ndarray]`, `list[list[float]]`):291            The sequence or batch of sequences to be padded. Each sequence can be a numpy array, a list of float292            values, a list of numpy arrays or a list of list of float values. Must be mono channel audio, not293            stereo, i.e. single float per timestep.294        padding (`bool`, `str` or [`~utils.PaddingStrategy`], *optional*):295            Select a strategy to pad the returned sequences (according to the model's padding side and padding296            index) among:297 298            - `True` or `'longest'`: Pad to the longest sequence in the batch (or no padding if only a single299                sequence if provided).300            - `'max_length'`: Pad to a maximum length specified with the argument `max_length` or to the maximum301                acceptable input length for the model if that argument is not provided.302            - `False` or `'do_not_pad'`303        max_length (`int`, *optional*):304            Maximum length of the returned list and optionally padding length (see above).305        truncation (`bool`, *optional*):306            Activates truncation to cut input sequences longer than *max_length* to *max_length*.307        pad_to_multiple_of (`int`, *optional*):308            If set, will pad the sequence to a multiple of the provided value.309        return_attention_mask (`bool`, *optional*):310            Whether or not [`~ASTFeatureExtractor.__call__`] should return `attention_mask`.311    """312 313    sampling_rate: Optional[int]314    raw_speech: Optional[Union[np.ndarray, list[float], list[np.ndarray], list[list[float]]]]315    padding: Optional[Union[bool, str, PaddingStrategy]]316    max_length: Optional[int]317    truncation: Optional[bool]318    pad_to_multiple_of: Optional[int]319    return_attention_mask: Optional[bool]320 321 322class CommonKwargs(TypedDict, total=False):323    return_tensors: Optional[Union[str, TensorType]]324 325 326class ProcessingKwargs(TypedDict, total=False):327    """328    Base class for kwargs passing to processors.329    In case a model has specific kwargs that are not present in the base class or default values for existing keys,330    it should have its own `ModelProcessorKwargs` class that inherits from `ProcessingKwargs` to provide:331        1) Additional typed keys and that this model requires to process inputs.332        2) Default values for existing keys under a `_defaults` attribute.333    New keys have to be defined as follows to ensure type hinting is done correctly.334 335    ```python336    # adding a new image kwarg for this model337    class ModelImagesKwargs(ImagesKwargs, total=False):338        new_image_kwarg: Optional[bool]339 340    class ModelProcessorKwargs(ProcessingKwargs, total=False):341        images_kwargs: ModelImagesKwargs342        _defaults = {343            "images_kwargs: {344                "new_image_kwarg": False,345            }346            "text_kwargs": {347                "padding": "max_length",348            },349        }350 351    ```352 353    For Python 3.8 compatibility, when inheriting from this class and overriding one of the kwargs,354    you need to manually update the __annotations__ dictionary. This can be done as follows:355 356    ```python357    class CustomProcessorKwargs(ProcessingKwargs, total=False):358        images_kwargs: CustomImagesKwargs359 360    CustomProcessorKwargs.__annotations__["images_kwargs"] = CustomImagesKwargs  # python 3.8 compatibility361    ```python362 363    """364 365    _defaults = {}366 367    common_kwargs: CommonKwargs = {368        **CommonKwargs.__annotations__,369    }370    text_kwargs: TextKwargs = {371        **TextKwargs.__annotations__,372    }373    images_kwargs: ImagesKwargs = {374        **ImagesKwargs.__annotations__,375    }376    videos_kwargs: VideosKwargs = {377        **VideosKwargs.__annotations__,378    }379    audio_kwargs: AudioKwargs = {380        **AudioKwargs.__annotations__,381    }382 383 384class TokenizerChatTemplateKwargs(TypedDict, total=False):385    """386    Keyword arguments for tokenizer's `apply_chat_template`, when it is called from within a processor.387 388    tools (`list[Dict]`, *optional*):389        A list of tools (callable functions) that will be accessible to the model. If the template does not390        support function calling, this argument will have no effect. Each tool should be passed as a JSON Schema,391        giving the name, description and argument types for the tool. See our392        [chat templating guide](https://huggingface.co/docs/transformers/main/en/chat_templating#automated-function-conversion-for-tool-use)393        for more information.394    documents (`list[dict[str, str]]`, *optional*):395        A list of dicts representing documents that will be accessible to the model if it is performing RAG396        (retrieval-augmented generation). If the template does not support RAG, this argument will have no397        effect. We recommend that each document should be a dict containing "title" and "text" keys. Please398        see the RAG section of the [chat templating guide](https://huggingface.co/docs/transformers/main/en/chat_templating#arguments-for-RAG)399        for examples of passing documents with chat templates.400    add_generation_prompt (bool, *optional*):401        If this is set, a prompt with the token(s) that indicate402        the start of an assistant message will be appended to the formatted output. This is useful when you want to generate a response from the model.403        Note that this argument will be passed to the chat template, and so it must be supported in the404        template for this argument to have any effect.405    continue_final_message (bool, *optional*):406        If this is set, the chat will be formatted so that the final407        message in the chat is open-ended, without any EOS tokens. The model will continue this message408        rather than starting a new one. This allows you to "prefill" part of409        the model's response for it. Cannot be used at the same time as `add_generation_prompt`.410    return_assistant_tokens_mask (`bool`, defaults to `False`):411        Whether to return a mask of the assistant generated tokens. For tokens generated by the assistant,412        the mask will contain 1. For user and system tokens, the mask will contain 0.413        This functionality is only available for chat templates that support it via the `{% generation %}` keyword.414    """415 416    tools: Optional[list[dict]] = None417    documents: Optional[list[dict[str, str]]] = None418    add_generation_prompt: Optional[bool] = False419    continue_final_message: Optional[bool] = False420    return_assistant_tokens_mask: Optional[bool] = False421 422 423class ChatTemplateLoadKwargs(TypedDict, total=False):424    """425    Keyword arguments used to load multimodal data in processor chat templates.426 427    num_frames (`int`, *optional*):428        Number of frames to sample uniformly. If not passed, the whole video is loaded.429    load_audio_from_video (`bool`, *optional*):430            Whether to use the audio track of input video. If `True` the audio track will be loaded and passed to the431            processor. This flag has no effect if the model doesn't support audio modality.432    """433 434    sampling_rate: Optional[int] = 16_000435    load_audio_from_video: Optional[bool] = False436 437 438class ProcessorChatTemplateKwargs(ChatTemplateLoadKwargs, TokenizerChatTemplateKwargs, total=False):439    """440    Keyword arguments for processor's `apply_chat_template`.441 442    tokenize (`bool`, *optional*, defaults to `False`):443        Whether to tokenize the output or not.444    return_dict (`bool`, defaults to `False`):445        Whether to return a dictionary with named outputs. Has no effect if tokenize is `False`.446    """447 448    tokenize: Optional[bool] = False449    return_dict: Optional[bool] = False450 451 452class AllKwargsForChatTemplate(TypedDict, total=False):453    processor_kwargs: ProcessingKwargs454    mm_load_kwargs: ChatTemplateLoadKwargs455    template_kwargs: ProcessorChatTemplateKwargs456 457 458@dataclass459class MultiModalData:460    """461    Dataclass that holds extra useful data for processing462    multimodal data. Processors currently cannot return keys,463    unless it is used in model's forward. Thus we have helper464    methods that calculate and return useful data from processing465    input multimodals (images/videos).466    Note that this dataclass is aimed to be used only in vLLM467    and we might change its API in the future.468    """469 470    num_image_tokens: Optional[list[int]] = None471    num_video_tokens: Optional[list[int]] = None472    num_audio_tokens: Optional[list[int]] = None473    num_image_patches: Optional[list[int]] = None474 475    def __contains__(self, key):476        return hasattr(self, key) and getattr(self, key) is not None477 478    def __getitem__(self, key):479        if hasattr(self, key):480            return getattr(self, key)481        raise AttributeError(f"{self.__class__.__name__} has no attribute {key}")482 483 484class ProcessorMixin(PushToHubMixin):485    """486    This is a mixin used to provide saving/loading functionality for all processor classes.487    """488 489    attributes = ["feature_extractor", "tokenizer"]490    optional_attributes = ["chat_template", "audio_tokenizer"]491    optional_call_args: list[str] = []492    # Names need to be attr_class for attr in attributes493    feature_extractor_class = None494    tokenizer_class = None495    _auto_class = None496    valid_processor_kwargs = ProcessingKwargs497 498    # args have to match the attributes class attribute499    def __init__(self, *args, **kwargs):500        # First, extract optional attributes from kwargs if present501        # Optional attributes can never be positional arguments502        for optional_attribute in self.optional_attributes:503            optional_attribute_value = kwargs.pop(optional_attribute, None)504            setattr(self, optional_attribute, optional_attribute_value)505 506            # Check audio tokenizer for its class but do not treat it as attr to avoid saving weights507            if optional_attribute == "audio_tokenizer" and optional_attribute_value is not None:508                proper_class = self.check_argument_for_proper_class(optional_attribute, optional_attribute_value)509 510                if not (is_torch_available() and isinstance(optional_attribute_value, PreTrainedAudioTokenizerBase)):511                    raise ValueError(512                        f"Tried to use `{proper_class}` for audio tokenization. However, this class is not"513                        " registered for audio tokenization."514                    )515 516        # Sanitize args and kwargs517        for key in kwargs:518            if key not in self.attributes:519                raise TypeError(f"Unexpected keyword argument {key}.")520        for arg, attribute_name in zip(args, self.attributes):521            if attribute_name in kwargs:522                raise TypeError(f"Got multiple values for argument {attribute_name}.")523            else:524                kwargs[attribute_name] = arg525 526        if len(kwargs) != len(self.attributes):527            raise ValueError(528                f"This processor requires {len(self.attributes)} arguments: {', '.join(self.attributes)}. Got "529                f"{len(args)} arguments instead."530            )531 532        # Check each arg is of the proper class (this will also catch a user initializing in the wrong order)533        for attribute_name, arg in kwargs.items():534            self.check_argument_for_proper_class(attribute_name, arg)535            setattr(self, attribute_name, arg)536 537    def __call__(538        self,539        images: Optional[ImageInput] = None,540        text: Optional[Union[TextInput, PreTokenizedInput, list[TextInput], list[PreTokenizedInput]]] = None,541        videos: Optional[VideoInput] = None,542        audio: Optional[AudioInput] = None,543        **kwargs: Unpack[ProcessingKwargs],544    ):545        """546        Main method to prepare for model inputs. This method forwards the each modality argument to its own processor547        along with `kwargs`. Please refer to the docstring of the each processor attributes for more information.548 549        Args:550            images (`PIL.Image.Image`, `np.ndarray`, `torch.Tensor`, `list[PIL.Image.Image]`, `list[np.ndarray]`, `list[torch.Tensor]`):551                The image or batch of images to be prepared. Each image can be a PIL image, NumPy array or PyTorch552                tensor. Both channels-first and channels-last formats are supported.553            text (`TextInput`, `PreTokenizedInput`, `list[TextInput]`, `list[PreTokenizedInput]`, *optional*):554                The sequence or batch of sequences to be encoded. Each sequence can be a string or a list of strings555                (pretokenized string). If the sequences are provided as list of strings (pretokenized), you must set556                `is_split_into_words=True` (to lift the ambiguity with a batch of sequences).557            videos (`np.ndarray`, `torch.Tensor`, `List[np.ndarray]`, `List[torch.Tensor]`):558                The video or batch of videos to be prepared. Each video can be a 4D NumPy array or PyTorch559                tensor, or a nested list of 3D frames. Both channels-first and channels-last formats are supported.560            audio (`np.ndarray`, `torch.Tensor`, `list[np.ndarray]`, `list[torch.Tensor]`):561                The audio or batch of audio to be prepared. Each audio can be a NumPy array or PyTorch562                tensor.563            return_tensors (`str` or [`~utils.TensorType`], *optional*):564                If set, will return tensors of a particular framework. Acceptable values are:565 566                - `'tf'`: Return TensorFlow `tf.constant` objects.567                - `'pt'`: Return PyTorch `torch.Tensor` objects.568                - `'np'`: Return NumPy `np.ndarray` objects.569                - `'jax'`: Return JAX `jnp.ndarray` objects.570 571        Returns:572            [`BatchFeature`]: A [`BatchFeature`] object with processed inputs in a dict format.573        """574        if images is None and text is None and videos is None and audio is None:575            raise ValueError(f"You need to provide at least one input to call {self.__class__.__name__}")576 577        kwargs = self._merge_kwargs(578            self.valid_processor_kwargs,579            tokenizer_init_kwargs=self.tokenizer.init_kwargs if hasattr(self, "tokenizer") else {},580            **kwargs,581        )582 583        attribute_to_kwargs = {584            "tokenizer": (text, "text_kwargs"),585            "image_processor": (images, "images_kwargs"),586            "video_processor": (videos, "videos_kwargs"),587            "feature_extractor": (audio, "audio_kwargs"),588        }589        outputs = {}590        for attribute_name in self.attributes:591            attribute = getattr(self, attribute_name, None)592            input_data, input_kwargs = attribute_to_kwargs[attribute_name]593            if input_data is not None and attribute is not None:594                attribute_output = attribute(input_data, **kwargs[input_kwargs])595                outputs.update(attribute_output)596 597        return BatchFeature(outputs)598 599    def check_argument_for_proper_class(self, argument_name, argument):600        """601        Checks the passed argument's class against the expected transformers class. In case of an unexpected602        mismatch between expected and actual class, an error is raise. Otherwise, the proper retrieved class603        is returned.604        """605        class_name = getattr(self, f"{argument_name}_class")606        # Nothing is ever going to be an instance of "AutoXxx", in that case we check the base class.607        class_name = AUTO_TO_BASE_CLASS_MAPPING.get(class_name, class_name)608        if isinstance(class_name, tuple):609            proper_class = tuple(self.get_possibly_dynamic_module(n) for n in class_name if n is not None)610        else:611            proper_class = self.get_possibly_dynamic_module(class_name)612 613        if not isinstance(argument, proper_class):614            raise TypeError(615                f"Received a {type(argument).__name__} for argument {argument_name}, but a {class_name} was expected."616            )617 618        return proper_class619 620    def to_dict(self, legacy_serialization=True) -> dict[str, Any]:621        """622        Serializes this instance to a Python dictionary.623 624        Returns:625            `dict[str, Any]`: Dictionary of all the attributes that make up this processor instance.626        """627        output = copy.deepcopy(self.__dict__)628 629        # Get the kwargs in `__init__`.630        sig = inspect.signature(self.__init__)631        # Only save the attributes that are presented in the kwargs of `__init__`.632        attrs_to_save = list(sig.parameters)633        # extra attributes to be kept634        attrs_to_save += ["auto_map"]635 636        if legacy_serialization:637            # Don't save attributes like `tokenizer`, `image processor` etc. in processor config if `legacy=True`638            attrs_to_save = [x for x in attrs_to_save if x not in self.__class__.attributes]639 640        if "tokenizer" in output:641            del output["tokenizer"]642        if "qformer_tokenizer" in output:643            del output["qformer_tokenizer"]644        if "protein_tokenizer" in output:645            del output["protein_tokenizer"]646        if "chat_template" in output:647            del output["chat_template"]648 649        def cast_array_to_list(dictionary):650            """651            Numpy arrays are not serialiazable but can be in pre-processing dicts.652            This function casts arrays to list, recusring through the nested configs as well.653            """654            for key, value in dictionary.items():655                if isinstance(value, np.ndarray):656                    dictionary[key] = value.tolist()657                elif isinstance(value, dict):658                    dictionary[key] = cast_array_to_list(value)659            return dictionary660 661        # Serialize attributes as a dict662        output = {663            k: v.to_dict() if isinstance(v, PushToHubMixin) else v664            for k, v in output.items()665            if (666                k in attrs_to_save  # keep all attributes that have to be serialized667                and v.__class__.__name__ != "BeamSearchDecoderCTC"  # remove attributes with that are objects668                and (669                    (legacy_serialization and not isinstance(v, PushToHubMixin)) or not legacy_serialization670                )  # remove `PushToHubMixin` objects671            )672        }673        output = cast_array_to_list(output)674 675        # Special case, add `audio_tokenizer` dict which points to model weights and path676        if not legacy_serialization and "audio_tokenizer" in output:677            audio_tokenizer_dict = {678                "audio_tokenizer_class": self.audio_tokenizer.__class__.__name__,679                "audio_tokenizer_name_or_path": self.audio_tokenizer.name_or_path,680            }681            # Update or overwrite, what do audio tokenizers expect when loading?682            output["audio_tokenizer"] = audio_tokenizer_dict683 684        output["processor_class"] = self.__class__.__name__685 686        return output687 688    def to_json_string(self, legacy_serialization=True) -> str:689        """690        Serializes this instance to a JSON string.691 692        Returns:693            `str`: String containing all the attributes that make up this feature_extractor instance in JSON format.694        """695        dictionary = self.to_dict(legacy_serialization=legacy_serialization)696 697        return json.dumps(dictionary, indent=2, sort_keys=True) + "\n"698 699    def to_json_file(self, json_file_path: Union[str, os.PathLike], legacy_serialization=True):700        """701        Save this instance to a JSON file.702 703        Args:704            json_file_path (`str` or `os.PathLike`):705                Path to the JSON file in which this processor instance's parameters will be saved.706        """707        with open(json_file_path, "w", encoding="utf-8") as writer:708            writer.write(self.to_json_string(legacy_serialization=legacy_serialization))709 710    def __repr__(self):711        attributes_repr = [f"- {name}: {repr(getattr(self, name))}" for name in self.attributes]712        attributes_repr = "\n".join(attributes_repr)713        return f"{self.__class__.__name__}:\n{attributes_repr}\n\n{self.to_json_string()}"714 715    def save_pretrained(self, save_directory, push_to_hub: bool = False, legacy_serialization: bool = True, **kwargs):716        """717        Saves the attributes of this processor (feature extractor, tokenizer...) in the specified directory so that it718        can be reloaded using the [`~ProcessorMixin.from_pretrained`] method.719 720        <Tip>721 722        This class method is simply calling [`~feature_extraction_utils.FeatureExtractionMixin.save_pretrained`] and723        [`~tokenization_utils_base.PreTrainedTokenizerBase.save_pretrained`]. Please refer to the docstrings of the724        methods above for more information.725 726        </Tip>727 728        Args:729            save_directory (`str` or `os.PathLike`):730                Directory where the feature extractor JSON file and the tokenizer files will be saved (directory will731                be created if it does not exist).732            push_to_hub (`bool`, *optional*, defaults to `False`):733                Whether or not to push your model to the Hugging Face model hub after saving it. You can specify the734                repository you want to push to with `repo_id` (will default to the name of `save_directory` in your735                namespace).736            legacy_serialization (`bool`, *optional*, defaults to `True`):737                Whether or not to save processor attributes in separate config files (legacy) or in processor's config738                file as a nested dict. Saving all attributes in a single dict will become the default in future versions.739                Set to `legacy_serialization=True` until then.740            kwargs (`dict[str, Any]`, *optional*):741                Additional key word arguments passed along to the [`~utils.PushToHubMixin.push_to_hub`] method.742        """743        use_auth_token = kwargs.pop("use_auth_token", None)744 745        if use_auth_token is not None:746            warnings.warn(747                "The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers. Please use `token` instead.",748                FutureWarning,749            )750            if kwargs.get("token") is not None:751                raise ValueError(752                    "`token` and `use_auth_token` are both specified. Please set only the argument `token`."753                )754            kwargs["token"] = use_auth_token755 756        os.makedirs(save_directory, exist_ok=True)757 758        if push_to_hub:759            commit_message = kwargs.pop("commit_message", None)760            repo_id = kwargs.pop("repo_id", save_directory.split(os.path.sep)[-1])761            repo_id = self._create_repo(repo_id, **kwargs)762            files_timestamps = self._get_files_timestamps(save_directory)763        # If we have a custom config, we copy the file defining it in the folder and set the attributes so it can be764        # loaded from the Hub.765        if self._auto_class is not None:766            attrs = [getattr(self, attribute_name) for attribute_name in self.attributes]767            configs = [(a.init_kwargs if isinstance(a, PreTrainedTokenizerBase) else a) for a in attrs]768            configs.append(self)769            custom_object_save(self, save_directory, config=configs)770 771        save_jinja_files = kwargs.get("save_jinja_files", True)772 773        for attribute_name in self.attributes:774            # Save the tokenizer in its own vocab file. The other attributes are saved as part of `processor_config.json`775            if attribute_name == "tokenizer":776                attribute = getattr(self, attribute_name)777                if hasattr(attribute, "_set_processor_class"):778                    attribute._set_processor_class(self.__class__.__name__)779 780                # Propagate save_jinja_files to tokenizer to ensure we don't get conflicts781                attribute.save_pretrained(save_directory, save_jinja_files=save_jinja_files)782            elif legacy_serialization:783                attribute = getattr(self, attribute_name)784                # Include the processor class in attribute config so this processor can then be reloaded with `AutoProcessor` API.785                if hasattr(attribute, "_set_processor_class"):786                    attribute._set_processor_class(self.__class__.__name__)787                attribute.save_pretrained(save_directory)788 789        if self._auto_class is not None:790            # We added an attribute to the init_kwargs of the tokenizers, which needs to be cleaned up.791            for attribute_name in self.attributes:792                attribute = getattr(self, attribute_name)793                if isinstance(attribute, PreTrainedTokenizerBase):794                    del attribute.init_kwargs["auto_map"]795 796        # If we save using the predefined names, we can load using `from_pretrained`797        # plus we save chat_template in its own file798        output_processor_file = os.path.join(save_directory, PROCESSOR_NAME)799        output_chat_template_file_jinja = os.path.join(save_directory, CHAT_TEMPLATE_FILE)800        output_chat_template_file_legacy = os.path.join(801            save_directory, LEGACY_PROCESSOR_CHAT_TEMPLATE_FILE802        )  # Legacy filename803        chat_template_dir = os.path.join(save_directory, CHAT_TEMPLATE_DIR)804 805        # Save `chat_template` in its own file. We can't get it from `processor_dict` as we popped it in `to_dict`806        # to avoid serializing chat template in json config file. So let's get it from `self` directly807        if self.chat_template is not None:808            save_jinja_files = kwargs.get("save_jinja_files", True)809            is_single_template = isinstance(self.chat_template, str)810            if save_jinja_files and is_single_template:811                # New format for single templates is to save them as chat_template.jinja812                with open(output_chat_template_file_jinja, "w", encoding="utf-8") as f:813                    f.write(self.chat_template)814                logger.info(f"chat template saved in {output_chat_template_file_jinja}")815            elif save_jinja_files and not is_single_template:816                # New format for multiple templates is to save the default as chat_template.jinja817                # and the other templates in the chat_templates/ directory818                for template_name, template in self.chat_template.items():819                    if template_name == "default":820                        with open(output_chat_template_file_jinja, "w", encoding="utf-8") as f:821                            f.write(self.chat_template["default"])822                        logger.info(f"chat template saved in {output_chat_template_file_jinja}")823                    else:824                        os.makedirs(chat_template_dir, exist_ok=True)825                        template_filepath = os.path.join(chat_template_dir, f"{template_name}.jinja")826                        with open(template_filepath, "w", encoding="utf-8") as f:827                            f.write(template)828                        logger.info(f"chat template saved in {template_filepath}")829            elif is_single_template:830                # Legacy format for single templates: Put them in chat_template.json831                chat_template_json_string = (832                    json.dumps({"chat_template": self.chat_template}, indent=2, sort_keys=True) + "\n"833                )834                with open(output_chat_template_file_legacy, "w", encoding="utf-8") as writer:835                    writer.write(chat_template_json_string)836                logger.info(f"chat template saved in {output_chat_template_file_legacy}")837            elif self.chat_template is not None:838                # At this point we have multiple templates in the legacy format, which is not supported839                # chat template dicts are saved to chat_template.json as lists of dicts with fixed key names.840                raise ValueError(841                    "Multiple chat templates are not supported in the legacy format. Please save them as "842                    "separate files using the `save_jinja_files` argument."843                )844 845        if legacy_serialization:846            output_audio_tokenizer_file = os.path.join(save_directory, AUDIO_TOKENIZER_NAME)847            processor_dict = self.to_dict()848 849            # For now, let's not save to `processor_config.json` if the processor doesn't have extra attributes and850            # `auto_map` is not specified.851            if set(processor_dict.keys()) != {"processor_class"}:852                self.to_json_file(output_processor_file)853                logger.info(f"processor saved in {output_processor_file}")854 855            if set(processor_dict.keys()) == {"processor_class"}:856                return_files = []857            else:858                return_files = [output_processor_file]859 860            if self.audio_tokenizer is not None:861                audio_tokenizer_class = self.audio_tokenizer.__class__.__name__862                audio_tokenizer_name_or_path = self.audio_tokenizer.name_or_path863                audio_tokenizer_dict = {864                    "audio_tokenizer_class": audio_tokenizer_class,865                    "audio_tokenizer_name_or_path": audio_tokenizer_name_or_path,866                }867                audio_tokenizer_json = json.dumps(audio_tokenizer_dict, indent=2, sort_keys=True) + "\n"868                with open(output_audio_tokenizer_file, "w", encoding="utf-8") as writer:869                    writer.write(audio_tokenizer_json)870 871        # Create a unified `preprocessor_config.json` and save all attributes as a composite config, except for tokenizers872        # NOTE: this will become the default way to save all processor attrbiutes in future versions. Toggled off for now to give873        # us time for smoother transition874        else:875            self.to_json_file(output_processor_file, legacy_serialization=False)876            logger.info(f"processor saved in {output_processor_file}")877            return_files = [output_processor_file]878 879        if push_to_hub:880            self._upload_modified_files(881                save_directory,882                repo_id,883                files_timestamps,884                commit_message=commit_message,885                token=kwargs.get("token"),886            )887 888        return return_files889 890    @classmethod891    def get_processor_dict(892        cls, pretrained_model_name_or_path: Union[str, os.PathLike], **kwargs893    ) -> tuple[dict[str, Any], dict[str, Any]]:894        """895        From a `pretrained_model_name_or_path`, resolve to a dictionary of parameters, to be used for instantiating a896        processor of type [`~processing_utils.ProcessingMixin`] using `from_args_and_dict`.897 898        Parameters:899            pretrained_model_name_or_path (`str` or `os.PathLike`):900                The identifier of the pre-trained checkpoint from which we want the dictionary of parameters.901            subfolder (`str`, *optional*, defaults to `""`):902                In case the relevant files are located inside a subfolder of the model repo on huggingface.co, you can903                specify the folder name here.904 905        Returns:906            `tuple[Dict, Dict]`: The dictionary(ies) that will be used to instantiate the processor object.907        """908        # holding a copy for optionally loading the audio tokenizer (if available)909        audio_tokenizer_kwargs = copy.deepcopy(kwargs)910 911        cache_dir = kwargs.pop("cache_dir", None)912        force_download = kwargs.pop("force_download", False)913        resume_download = kwargs.pop("resume_download", None)914        proxies = kwargs.pop("proxies", None)915        token = kwargs.pop("token", None)916        local_files_only = kwargs.pop("local_files_only", False)917        revision = kwargs.pop("revision", None)918        subfolder = kwargs.pop("subfolder", "")919 920        from_pipeline = kwargs.pop("_from_pipeline", None)921        from_auto_class = kwargs.pop("_from_auto", False)922 923        user_agent = {"file_type": "processor", "from_auto_class": from_auto_class}924        if from_pipeline is not None:925            user_agent["using_pipeline"] = from_pipeline926 927        if is_offline_mode() and not local_files_only:928            logger.info("Offline mode: forcing local_files_only=True")929            local_files_only = True930 931        pretrained_model_name_or_path = str(pretrained_model_name_or_path)932        is_local = os.path.isdir(pretrained_model_name_or_path)933        if os.path.isdir(pretrained_model_name_or_path):934            processor_file = os.path.join(pretrained_model_name_or_path, PROCESSOR_NAME)935 936        additional_chat_template_files = {}937        resolved_additional_chat_template_files = {}938        if os.path.isfile(pretrained_model_name_or_path):939            resolved_processor_file = pretrained_model_name_or_path940            # can't load chat-template and audio tokenizer when given a file as pretrained_model_name_or_path941            resolved_chat_template_file = None942            resolved_raw_chat_template_file = None943            resolved_audio_tokenizer_file = None944            is_local = True945        elif is_remote_url(pretrained_model_name_or_path):946            processor_file = pretrained_model_name_or_path947            resolved_processor_file = download_url(pretrained_model_name_or_path)948            # can't load chat-template and audio tokenizer when given a file url as pretrained_model_name_or_path949            resolved_chat_template_file = None950            resolved_raw_chat_template_file = None951            resolved_audio_tokenizer_file = None952        else:953            if is_local:954                template_dir = Path(pretrained_model_name_or_path, CHAT_TEMPLATE_DIR)955                if template_dir.is_dir():956                    for template_file in template_dir.glob("*.jinja"):957                        template_name = template_file.stem958                        additional_chat_template_files[template_name] = f"{CHAT_TEMPLATE_DIR}/{template_file.name}"959            else:960                try:961                    for template in list_repo_templates(962                        pretrained_model_name_or_path,963                        local_files_only=local_files_only,964                        revision=revision,965                        cache_dir=cache_dir,966                        token=token,967                    ):968                        additional_chat_template_files[template] = f"{CHAT_TEMPLATE_DIR}/{template}.jinja"969                except EntryNotFoundError:970                    pass  # No template dir means no template files971            processor_file = PROCESSOR_NAME972 973            try:974                # Load from local folder or from cache or download from model Hub and cache975                resolved_processor_file = cached_file(976                    pretrained_model_name_or_path,977                    processor_file,978                    cache_dir=cache_dir,979                    force_download=force_download,980                    proxies=proxies,981                    resume_download=resume_download,982                    local_files_only=local_files_only,983                    token=token,984                    user_agent=user_agent,985                    revision=revision,986                    subfolder=subfolder,987                    _raise_exceptions_for_missing_entries=False,988                )989 990                # chat_template.json is a legacy file used by the processor class991                # a raw chat_template.jinja is preferred in future992                resolved_chat_template_file = cached_file(993                    pretrained_model_name_or_path,994                    LEGACY_PROCESSOR_CHAT_TEMPLATE_FILE,995                    cache_dir=cache_dir,996                    force_download=force_download,997                    proxies=proxies,998                    resume_download=resume_download,999                    local_files_only=local_files_only,1000                    token=token,1001                    user_agent=user_agent,1002                    revision=revision,1003                    subfolder=subfolder,1004                    _raise_exceptions_for_missing_entries=False,1005                )1006 1007                resolved_raw_chat_template_file = cached_file(1008                    pretrained_model_name_or_path,1009                    CHAT_TEMPLATE_FILE,1010                    cache_dir=cache_dir,1011                    force_download=force_download,1012                    proxies=proxies,1013                    resume_download=resume_download,1014                    local_files_only=local_files_only,1015                    token=token,1016                    user_agent=user_agent,1017                    revision=revision,1018                    subfolder=subfolder,1019                    _raise_exceptions_for_missing_entries=False,1020                )1021 1022                resolved_additional_chat_template_files = {1023                    template_name: cached_file(1024                        pretrained_model_name_or_path,1025                        template_file,1026                        cache_dir=cache_dir,1027                        force_download=force_download,1028                        proxies=proxies,1029                        resume_download=resume_download,1030                        local_files_only=local_files_only,1031                        token=token,1032                        user_agent=user_agent,1033                        revision=revision,1034                        subfolder=subfolder,1035                        _raise_exceptions_for_missing_entries=False,1036                    )1037                    for template_name, template_file in additional_chat_template_files.items()1038                }1039 1040                resolved_audio_tokenizer_file = cached_file(1041                    pretrained_model_name_or_path,1042                    AUDIO_TOKENIZER_NAME,1043                    cache_dir=cache_dir,1044                    force_download=force_download,1045                    proxies=proxies,1046                    resume_download=resume_download,1047                    local_files_only=local_files_only,1048                    token=token,1049                    user_agent=user_agent,1050                    revision=revision,1051                    subfolder=subfolder,1052                    _raise_exceptions_for_missing_entries=False,1053                )1054            except OSError:1055                # Raise any environment error raise by `cached_file`. It will have a helpful error message adapted to1056                # the original exception.1057                raise1058            except Exception:1059                # For any other exception, we throw a generic error.1060                raise OSError(1061                    f"Can't load processor for '{pretrained_model_name_or_path}'. If you were trying to load"1062                    " it from 'https://huggingface.co/models', make sure you don't have a local directory with the"1063                    f" same name. Otherwise, make sure '{pretrained_model_name_or_path}' is the correct path to a"1064                    f" directory containing a {PROCESSOR_NAME} file"1065                )1066 1067        # Add chat template as kwarg before returning because most models don't have processor config1068        if resolved_chat_template_file is not None:1069            # This is the legacy path1070            with open(resolved_chat_template_file, encoding="utf-8") as reader:1071                chat_template_json = json.loads(reader.read())1072                chat_templates = {"default": chat_template_json["chat_template"]}1073                if resolved_additional_chat_template_files:1074                    raise ValueError(1075                        "Cannot load chat template due to conflicting files - this checkpoint combines "1076                        "a legacy chat_template.json file with separate template files, which is not "1077                        "supported. To resolve this error, replace the legacy chat_template.json file "1078                        "with a modern chat_template.jinja file."1079                    )1080        else:1081            chat_templates = {1082                template_name: open(template_file, "r", encoding="utf-8").read()1083                for template_name, template_file in resolved_additional_chat_template_files.items()1084            }1085            if resolved_raw_chat_template_file is not None:1086                with open(resolved_raw_chat_template_file, "r", encoding="utf-8") as reader:1087                    chat_templates["default"] = reader.read()1088        if isinstance(chat_templates, dict) and "default" in chat_templates and len(chat_templates) == 1:1089            chat_templates = chat_templates["default"]  # Flatten when we just have a single template/file1090 1091        if chat_templates:1092            kwargs["chat_template"] = chat_templates1093 1094        # Existing processors on the Hub created before #27761 being merged don't have `processor_config.json` (if not1095        # updated afterward), and we need to keep `from_pretrained` work. So here it fallbacks to the empty dict.1096        # (`cached_file` called using `_raise_exceptions_for_missing_entries=False` to avoid exception)1097        # However, for models added in the future, we won't get the expected error if this file is missing.1098        if resolved_processor_file is None:1099            # In any case we need to pass `chat_template` if it is available1100            processor_dict = {}1101        else:1102            try:1103                # Load processor dict1104                with open(resolved_processor_file, encoding="utf-8") as reader:1105                    text = reader.read()1106                processor_dict = json.loads(text)1107 1108            except json.JSONDecodeError:1109                raise OSError(1110                    f"It looks like the config file at '{resolved_processor_file}' is not a valid JSON file."1111                )1112 1113        if is_local:1114            logger.info(f"loading configuration file {resolved_processor_file}")1115        else:1116            logger.info(f"loading configuration file {processor_file} from cache at {resolved_processor_file}")1117 1118        if "chat_template" in processor_dict and processor_dict["chat_template"] is not None:1119            logger.warning_once(1120                "Chat templates should be in a 'chat_template.jinja' file but found key='chat_template' "1121                "in the processor's config. Make sure to move your template to its own file."1122            )1123 1124        if "chat_template" in kwargs:1125            processor_dict["chat_template"] = kwargs.pop("chat_template")1126 1127        # Audio tokenizer needs to load the model checkpoint first, because the saved1128        # json file contains only references to the model path and repo id1129        if resolved_audio_tokenizer_file is not None or "audio_tokenizer" in processor_dict:1130            if resolved_audio_tokenizer_file is not None:1131                reader = open(resolved_audio_tokenizer_file, "r", encoding="utf-8")1132                audio_tokenizer_dict = reader.read()1133                audio_tokenizer_dict = json.loads(audio_tokenizer_dict)1134            else:1135                audio_tokenizer_dict = processor_dict["audio_tokenizer"]1136 1137            audio_tokenizer_class = cls.get_possibly_dynamic_module(audio_tokenizer_dict["audio_tokenizer_class"])1138            audio_tokenizer_path = audio_tokenizer_dict["audio_tokenizer_name_or_path"]1139            processor_dict["audio_tokenizer"] = audio_tokenizer_class.from_pretrained(1140                audio_tokenizer_path, **audio_tokenizer_kwargs1141            )1142 1143        # Pop attributes if saved in a single processor dict, they are loaded in `_get_arguments_from_pretrained`1144        for attribute in cls.attributes:1145            processor_dict.pop(attribute, None)1146 1147        return processor_dict, kwargs1148 1149    @classmethod1150    def from_args_and_dict(cls, args, processor_dict: dict[str, Any], **kwargs):1151        """1152        Instantiates a type of [`~processing_utils.ProcessingMixin`] from a Python dictionary of parameters.1153 1154        Args:1155            processor_dict (`dict[str, Any]`):1156                Dictionary that will be used to instantiate the processor object. Such a dictionary can be1157                retrieved from a pretrained checkpoint by leveraging the1158                [`~processing_utils.ProcessingMixin.to_dict`] method.1159            kwargs (`dict[str, Any]`):1160                Additional parameters from which to initialize the processor object.1161 1162        Returns:1163            [`~processing_utils.ProcessingMixin`]: The processor object instantiated from those1164            parameters.1165        """1166        processor_dict = processor_dict.copy()1167        return_unused_kwargs = kwargs.pop("return_unused_kwargs", False)1168 1169        # We have to pop up some unused (but specific) kwargs and then validate that it doesn't contain unused kwargs1170        # If we don't pop, some specific kwargs will raise a warning1171        if "processor_class" in processor_dict:1172            del processor_dict["processor_class"]1173 1174        if "auto_map" in processor_dict:1175            del processor_dict["auto_map"]1176 1177        # override processor_dict with given kwargs1178        processor_dict.update(kwargs)1179 1180        # check if there is an overlap between args and processor_dict1181        accepted_args_and_kwargs = cls.__init__.__code__.co_varnames[: cls.__init__.__code__.co_argcount][1:]1182 1183        # validate both processor_dict and given kwargs1184        unused_kwargs, valid_kwargs = cls.validate_init_kwargs(1185            processor_config=processor_dict, valid_kwargs=accepted_args_and_kwargs1186        )1187 1188        # update args that are already in processor_dict to avoid duplicate arguments1189        args_to_update = {1190            i: valid_kwargs.pop(arg)1191            for i, arg in enumerate(accepted_args_and_kwargs)1192            if (arg in valid_kwargs and i < len(args))1193        }1194        args = [args_to_update.get(i, arg) for i, arg in enumerate(args)]1195 1196        # instantiate processor with used (and valid) kwargs only1197        processor = cls(*args, **valid_kwargs)1198 1199        logger.info(f"Processor {processor}")1200        if return_unused_kwargs:

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

Aluode/PerceptionLabPortable · CoolFace