def __init__(
        self,
        model_path: str,
        tokenizer: PreTrainedTokenizer,
        execution_provider: str,
        device_id: int = 0,
        adapters: dict[str, Any] = None,
        adapter_mode: AdapterMode = AdapterMode.inputs,
    ):
        """Initialize the generator.

        :param model_path: Path to the model.
        :param tokenizer: The tokenizer to use.
        :param execution_provider: The execution provider to use.
        :param device_id: The device id to use.
        :param adapters: Dictionary of adapter information. Each key is the adapter name and the value is a
            dictionary with the following keys
            - "weights": Path to the weights file or dictionary of weights.
            - "template": The template to use for the adapter.
            If not provided, will assume the model has no adapters.
        :param adapter_mode: The mode to use for the adapters. Can be "inputs" or "initializers".
        """
        self.model_path = model_path
        self.tokenizer = tokenizer
        self.execution_provider = execution_provider
        self.device_id = device_id
        self.adapter_info = adapters or {"default": {"weights": None, "template": None}}
        self.adapter_mode = AdapterMode(adapter_mode)

        # Determine attention type
        self.attn_type = "default"
        model_proto = onnx.load(self.model_path, load_external_data=False)
        for node in model_proto.graph.node:
            if node.op_type == "GroupQueryAttention":
                self.attn_type = "gqa"
                break
            if node.op_type == "MultiHeadAttention":
                self.attn_type = "mha"
                break
        # Get io info
        self.input_info = {}
        self.cache_info = {"past_names": [], "present_names": [], "dtype": None, "num_kv_heads": None, "head_dim": None}
        for i in model_proto.graph.input:
            if ".key" not in i.name and ".value" not in i.name:
                self.input_info[i.name] = self.get_io_type_shape(i)
                continue
            if not self.cache_info["past_names"]:
                past_info = self.get_io_type_shape(i)
                self.cache_info["dtype"] = past_info["dtype"]
                self.cache_info["num_kv_heads"] = past_info["shape"][1]
                self.cache_info["head_dim"] = past_info["shape"][3]
            self.cache_info["past_names"].append(i.name)
        for i in model_proto.graph.output:
            if ".key" not in i.name and ".value" not in i.name:
                continue
            self.cache_info["present_names"].append(i.name)
        del model_proto

        # Determine device to use for io binding
        # NOTE: QNNExecutionProvider does not support IO binding but keep it for future compatibility
        ep_to_device_map = {
            "CPUExecutionProvider": "cpu",
            "CUDAExecutionProvider": "cuda",
            "DmlExecutionProvider": "dml",
            "QNNExecutionProvider": "qnn",
        }
        self.device = ep_to_device_map[self.execution_provider]

        # create the session
        self.sessions, self.adapters = self.prepare_sessions(
            self.model_path, self.execution_provider, self.device, self.device_id, self.adapter_info, self.adapter_mode
        )
        self.default_adapter = next(iter(self.adapters.keys()))
Initialize the generator.

        :param model_path: Path to the model.
        :param tokenizer: The tokenizer to use.
        :param execution_provider: The execution provider to use.
        :param device_id: The device id to use.
        :param adapters: Dictionary of adapter information. Each key is the adapter name and the value is a
            dictionary with the following keys
            - "weights": Path to the weights file or dictionary of weights.
            - "template": The template to use for the adapter.
            If not provided, will assume the model has no adapters.
        :param adapter_mode: The mode to use for the adapters. Can be "inputs" or "initializers".
        
examples/utils/generator.py