Repository: huggingface/transformers
Pull request: Fix serving continuous batching

# What does this PR do?

Serving is broken with continuous batching to due recent PRs. This PR fixes it 

Changed files:
filename: docs/source/en/serving.md
patch: @@ -380,7 +380,7 @@ CB is opt-in and currently applies to chat completions.
 ```sh
 transformers serve \
   --continuous-batching
-  --attn_implementation sdpa_paged
+  --attn_implementation "sdpa"
 ```
 
 ### Performance tips
@@ -390,11 +390,10 @@ transformers serve \
 ```sh
 transformers serve \
   --continuous_batching \
-  --attn_implementation paged_attention
+  --attn_implementation "flash_attention_2"
 ```
 
 > [!TIP]
-> If you choose `paged_attention`, you must install `flash-attn` separately: `pip install flash-attn --no-build-isolation`
 
 - `--dtype {bfloat16|float16}` typically improve throughput and memory use vs. `float32`
 

filename: src/transformers/generation/continuous_batching/continuous_api.py
patch: @@ -929,14 +929,6 @@ def request_id_iter(self, request_id: str) -> Generator[GenerationOutput]:
             if self.batch_processor is not None:
                 request_cancelled = self.batch_processor.scheduler.request_is_cancelled(request_id)
 
-    @staticmethod
-    def supported_attention_implementations() -> set[str]:
-        return {"eager_paged", "sdpa_paged", "flash_attention_2"}
-
-    @staticmethod
-    def default_attention_implementation() -> str:
-        return "sdpa_paged"
-
     @traced
     def warmup(self, batch_processor: ContinuousBatchProcessor) -> None:
         stream = torch.cuda.Stream(device=self.model.device)

filename: src/transformers/modeling_utils.py
patch: @@ -2419,30 +2419,30 @@ def get_correct_attn_implementation(self, requested_attention: Optional[str], is
         if applicable_attention not in ["eager"] + ALL_ATTENTION_FUNCTIONS.valid_keys():
             message = (
                 f'Specified `attn_implementation="{applicable_attention}"` is not supported. The only possible arguments are '
-                '`attn_implementation="eager"`'
+                '`attn_implementation="eager"`, `"paged|eager"`'
             )
             # check `supports_flash_attn_2` for BC with custom code. TODO: remove after a few releases
             if self._supports_flash_attn or getattr(self, "_supports_flash_attn_2", False):
-                message += ', `"attn_implementation=flash_attention_3"`, `"attn_implementation=flash_attention_2"`'
+                message += ', `"attn_implementation=flash_attention_3"`, `"attn_implementation=flash_attention_2"`, `"attn_implementation=paged|flash_attention_2"`'
             if self._supports_sdpa:
-                message += ', `"attn_implementation=sdpa"'
+                message += ', `"attn_implementation=sdpa"`, `"attn_implementation=paged|spda"`'
             if self._supports_flex_attn:
                 message += ', `"attn_implementation=flex_attention"`'
             raise ValueError(message + ".")
 
         # Perform relevant checks
-        if applicable_attention == "flash_attention_2":
+        if "flash_attention_2" in applicable_attention:
             self._flash_attn_2_can_dispatch(is_init_check)
-        elif applicable_attention == "flash_attention_3":
+        elif "flash_attention_3" in applicable_attention:
             self._flash_attn_3_can_dispatch(is_init_check)
-        elif applicable_attention == "flex_attention":
+        elif "flex_attention" in applicable_attention:
             self._flex_attn_can_dispatch(is_init_check)
-        elif applicable_attention == "sdpa":
+        elif "sdpa" in applicable_attention:
             # Sdpa is the default, so we try it and fallback to eager otherwise when not possible
             try:
                 self._sdpa_can_dispatch(is_init_check)
             except (ValueError, ImportError) as e:
-                if requested_attention == "sdpa":
+                if requested_attention is not None and "sdpa" in requested_attention:
                     raise e
                 applicable_attention = "eager"
 

filename: src/transformers/utils/import_utils.py
patch: @@ -1167,6 +1167,13 @@ def is_mistral_common_available() -> bool:
     return _is_package_available("mistral_common")
 
 
+@lru_cache
+def is_opentelemetry_available() -> bool:
+    return _is_package_available("opentelemetry") and version.parse(
+        importlib.metadata.version("opentelemetry-api")
+    ) >= version.parse("1.30.0")
+
+
 def check_torch_load_is_safe() -> None:
     if not is_torch_greater_or_equal("2.6"):
         raise ValueError(

filename: src/transformers/utils/metrics.py
patch: @@ -5,6 +5,8 @@
 from enum import Enum
 from typing import Any, Optional, Union
 
+from .import_utils import is_opentelemetry_available
+
 
 class RequestStatus(Enum):
     """Status of a generation request through its lifecycle."""
@@ -18,12 +20,12 @@ class RequestStatus(Enum):
     FAILED = "failed"
 
 
-try:
+if is_opentelemetry_available():
     from opentelemetry import metrics
     from opentelemetry.trace import Status, StatusCode, get_tracer
 
     _has_opentelemetry = True
-except ImportError:
+else:
     _has_opentelemetry = False
 
 
@@ -183,7 +185,10 @@ def _setup_metrics(self):
         """Initialize OpenTelemetry metrics and tracing if the library is available."""
 
         if not _has_opentelemetry:
-            logger.info("OpenTelemetry is not installed. Metrics and tracing will not be recorded.")
+            logger.info(
+                "OpenTelemetry is not installed. Metrics and tracing will not be recorded."
+                "You can install it with `pip install opentelemetry-api>=1.30.0`"
+            )
             return
 
         self.meter = metrics.get_meter("transformers.generation.continuous_batch_processor")