Repository: huggingface/transformers
File: src/transformers/pipelines/automatic_speech_recognition.py (Python)

Code before:
                        generation_config.task_to_id = self.tokenizer.task_to_id
                    if not hasattr(generation_config, "no_timestamps_token_id") and hasattr(
                        self.tokenizer, "no_timestamps_token_id"
                    ):
                        generation_config.no_timestamps_token_id = self.tokenizer.no_timestamps_token_id

                # Get is_multilingual from model config if available
                if not hasattr(generation_config, "is_multilingual") and hasattr(self.model.config, "is_multilingual"):
                    generation_config.is_multilingual = self.model.config.is_multilingual

                # For return_timestamps, ensure no_timestamps_token_id is set
                if return_timestamps and not hasattr(generation_config, "no_timestamps_token_id"):
                    # Try to get it from tokenizer's special tokens
                    if self.tokenizer is not None:
                        # Whisper uses 
 token
                        no_timestamps_token = "
"
                        if hasattr(self.tokenizer, "convert_tokens_to_ids"):
                            try:
                                no_timestamps_id = self.tokenizer.convert_tokens_to_ids(no_timestamps_token)
                                if no_timestamps_id != self.tokenizer.unk_token_id:
                                    generation_config.no_timestamps_token_id = no_timestamps_id
                            except Exception:
                                pass
                        # Fallback: use vocab_size + 1 as a safe default (will be ignored in decoding)
                        if not hasattr(generation_config, "no_timestamps_token_id"):
                            generation_config.no_timestamps_token_id = self.model.config.vocab_size + 1

            generate_kwargs["generation_config"] = generation_config

            main_input_name = self.model.main_input_name if hasattr(self.model, "main_input_name") else "inputs"
            generate_kwargs = {
                main_input_name: inputs,
                "attention_mask": attention_mask,
                **generate_kwargs,
            }
            tokens = self.model.generate(**generate_kwargs)

            # whisper longform generation stores timestamps in "segments"
            if return_timestamps == "word" and self.type == "seq2seq_whisper":
                if "segments" not in tokens:
                    out = {"tokens": tokens["sequences"], "token_timestamps": tokens["token_timestamps"]}
                else:
                    token_timestamps = [
                        torch.cat([segment["token_timestamps"] for segment in segment_list])
                        for segment_list in tokens["segments"]
                    ]
                    out = {"tokens": tokens["sequences"], "token_timestamps": token_timestamps}
            else:
                out = {"tokens": tokens}
            if self.type == "seq2seq_whisper":
                if stride is not None:

Review comment (suggestion):
Pipelines are supposed to stay more or less model-agnostic - this is a very big exception for whisper cc @eustlb 

Code after:
            }
            outputs = self.model(**inputs)
            logits = outputs.logits

            if self.type == "ctc_with_lm":
                out = {"logits": logits}
            else:
                out = {"tokens": logits.argmax(dim=-1)}
            if stride is not None:
                # Send stride to `postprocess`.
                # it needs to be handled there where
                # the pieces are to be concatenated.
                ratio = 1 / self._align_to
                if isinstance(stride, tuple):
                    out["stride"] = rescale_stride([stride], ratio)[0]
                else:
                    out["stride"] = rescale_stride(stride, ratio)
        # Leftover
        extra = model_inputs
        return {"is_last": is_last, **out, **extra}

    def postprocess(
        self, model_outputs, decoder_kwargs: dict | None = None, return_timestamps=None, return_language=None
    ):
        # Optional return types
        optional = {}

        final_items = []
        key = "logits" if self.type == "ctc_with_lm" else "tokens"
        stride = None
        for outputs in model_outputs:
            if outputs[key].dtype in (torch.bfloat16, torch.float16):
                items = outputs[key].to(torch.float32).numpy()
            else:
                items = outputs[key].numpy()
            stride = outputs.get("stride", None)
            if stride is not None and self.type in {"ctc", "ctc_with_lm"}:
                total_n, left, right = stride
                # Total_n might be < logits.shape[1]
                # because of padding, that's why
                # we need to reconstruct this information
                # This won't work with left padding (which doesn't exist right now)
                right_n = total_n - right
                items = items[:, left:right_n]
            final_items.append(items)

        if stride and self.type == "seq2seq":
            items = _find_longest_common_sequence(final_items, self.tokenizer)
        elif self.type == "seq2seq_whisper":
            time_precision = self.feature_extractor.chunk_length / self.model.config.max_source_positions
            # Send the chunking back to seconds, it's easier to handle in whisper