Repository: huggingface/transformers
Pull request: FIX Broken PEFT adapter loading

# What does this PR do?

For some time now, loading PEFT adapters directly with transformers is broken when using revisions or subfolders.

To check, run:

`RUN_SLOW=1 pytest tests/peft_integration/test_peft_integration.py -k test_peft_from_pretrained_hub_kwargs`

This PR makes the PEFT tests pass.

The PR causing this is #41445 (bad commit: `1ee3b288a62c9de658e8be117d869c2a9b835a7c`, previous good comit: `cad74496ca19c463a5fcc0b35ef4a1c9da2b8c4e`). However, that PR also caused other errors (see #41604), which is why this error was not immediately obvious.


## Before submitting
- [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case).
- [x] Did you read the [contributor guideline](https://github.com/huggingface/transformers/blob/main/CONTRIBUTING.md#create-a-pull-request),
      Pull Request section?
- [ ] Was this discussed/approved via a Github issue or the [forum](https://discuss.huggingface.co/)? Please add a link
      to it if that's the case.
- [ ] Did you make sure to update the documentation with your changes? Here are the
      [documentation guidelines](https://github.com/huggingface/transformers/tree/main/docs), and
      [here are tips on formatting docstrings](https://github.com/huggingface/transformers/tree/main/docs#writing-source-documentation).
- [x] Did you write any new necessary tests?

Changed files:
filename: src/transformers/integrations/peft.py
patch: @@ -654,17 +654,18 @@ def maybe_load_adapters(
     token_from_adapter_kwargs = adapter_kwargs.pop("token", None)
 
     if _adapter_model_path is None:
+        peft_kwargs = adapter_kwargs.copy()
+        for arg_name in ("cache_dir", "proxies", "subfolder"):  # don't override revision
+            if (arg_name not in peft_kwargs) and (arg_name in download_kwargs):
+                peft_kwargs[arg_name] = download_kwargs[arg_name]
+        if "commit_hash" in download_kwargs:
+            peft_kwargs["_commit_hash"] = download_kwargs["commit_hash"]
+        peft_kwargs["force_download"] = bool(download_kwargs.get("force_download", False))
+        peft_kwargs["local_files_only"] = bool(download_kwargs.get("local_files_only", False))
+        peft_kwargs["token"] = token or token_from_adapter_kwargs
         _adapter_model_path = find_adapter_config_file(
             pretrained_model_name_or_path,
-            cache_dir=download_kwargs.get("cache_dir"),
-            force_download=bool(download_kwargs.get("force_download", False)),
-            proxies=download_kwargs.get("proxies"),
-            token=token or token_from_adapter_kwargs,
-            revision=download_kwargs.get("revision"),
-            local_files_only=bool(download_kwargs.get("local_files_only", False)),
-            subfolder=download_kwargs.get("subfolder", ""),
-            _commit_hash=download_kwargs.get("commit_hash"),
-            **adapter_kwargs,
+            **peft_kwargs,
         )
 
     if _adapter_model_path is not None and os.path.isfile(_adapter_model_path):

filename: src/transformers/modeling_utils.py
patch: @@ -3881,7 +3881,7 @@ def from_pretrained(
         subfolder = kwargs.pop("subfolder", "")
         commit_hash = kwargs.pop("_commit_hash", None)
         variant = kwargs.pop("variant", None)
-        adapter_kwargs = kwargs.pop("adapter_kwargs", {})
+        adapter_kwargs = (kwargs.pop("adapter_kwargs", {}) or {}).copy()
         adapter_name = kwargs.pop("adapter_name", "default")
         generation_config = kwargs.pop("generation_config", None)
         gguf_file = kwargs.pop("gguf_file", None)

filename: src/transformers/models/auto/auto_factory.py
patch: @@ -288,8 +288,9 @@ def from_pretrained(cls, pretrained_model_name_or_path: Union[str, os.PathLike[s
         if is_peft_available():
             if adapter_kwargs is None:
                 adapter_kwargs = {}
-                if token is not None:
-                    adapter_kwargs["token"] = token
+            adapter_kwargs = adapter_kwargs.copy()  # avoid mutating original
+            if token is not None:
+                adapter_kwargs["token"] = token
 
             maybe_adapter_path = find_adapter_config_file(
                 pretrained_model_name_or_path, _commit_hash=commit_hash, **adapter_kwargs

filename: tests/peft_integration/test_peft_integration.py
patch: @@ -699,20 +699,21 @@ def test_peft_from_pretrained_hub_kwargs(self):
         with self.assertRaises(OSError):
             _ = AutoModelForCausalLM.from_pretrained(peft_model_id)
 
-        adapter_kwargs = {"revision": "test"}
-
         # This should work
+        adapter_kwargs = {"revision": "test"}
         model = AutoModelForCausalLM.from_pretrained(peft_model_id, adapter_kwargs=adapter_kwargs)
         self.assertTrue(self._check_lora_correctly_converted(model))
 
+        # note: always create new adapter_kwargs, avoid the test relying on the previous calls possibly mutating them
+        adapter_kwargs = {"revision": "test"}
         model = OPTForCausalLM.from_pretrained(peft_model_id, adapter_kwargs=adapter_kwargs)
         self.assertTrue(self._check_lora_correctly_converted(model))
 
         adapter_kwargs = {"revision": "main", "subfolder": "test_subfolder"}
-
         model = AutoModelForCausalLM.from_pretrained(peft_model_id, adapter_kwargs=adapter_kwargs)
         self.assertTrue(self._check_lora_correctly_converted(model))
 
+        adapter_kwargs = {"revision": "main", "subfolder": "test_subfolder"}
         model = OPTForCausalLM.from_pretrained(peft_model_id, adapter_kwargs=adapter_kwargs)
         self.assertTrue(self._check_lora_correctly_converted(model))