{"record":{"id":"497fd90bb6f5d5e4","repo":"huggingface/transformers","slug":"is-an-abstract-class-only-classes-inheriting-t-497fd9","errorCode":null,"errorMessage":"{} is an abstract class. Only classes inheriting this class can call `update_candidate_strategy`.","messagePattern":"(.+?) is an abstract class\\. Only classes inheriting this class can call `update_candidate_strategy`\\.","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"src/transformers/generation/candidate_generator.py","lineNumber":74,"sourceCode":"        \"\"\"\n        raise NotImplementedError(\n            f\"{self.__class__} is an abstract class. Only classes inheriting this class can call `get_candidates`.\"\n        )\n\n    def update_candidate_strategy(self, input_ids: torch.LongTensor, scores: torch.FloatTensor, num_matches: int):\n        \"\"\"\n        Updates the candidate generation strategy based on the outcomes.\n\n        Args:\n            input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`):\n                Indices of input sequence tokens in the vocabulary. [What are input IDs?](../glossary#input-ids)\n            scores (`torch.FloatTensor` of shape `(batch_size, candidate_length, config.vocab_size)`):\n                Prediction scores of a language modeling head. These can be logits for each vocabulary when not using\n                beam search or log softmax for each vocabulary token when using beam search\n            num_matches (`int`):\n                The number of matches between the candidate sequences and the model predictions.\n        \"\"\"\n        raise NotImplementedError(\n            f\"{self.__class__} is an abstract class. Only classes inheriting this class can call \"\n            \"`update_candidate_strategy`.\"\n        )\n\n\nclass AssistedCandidateGenerator(CandidateGenerator):\n    \"\"\"\n    `CandidateGenerator` class to be used for assisted generation and speculative decoding. This class generates\n    candidates through the use of a smaller model. Read the following blog post for more information:\n    https://huggingface.co/blog/assisted-generation\n\n    Args:\n        input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`):\n            Indices of input sequence tokens in the vocabulary. [What are input IDs?](../glossary#input-ids)\n        assistant_model (`PreTrainedModel`):\n            The model to be used for generating candidates. This model should be smaller than the main model.\n        generation_config (`~generation.GenerationConfig`, *optional*):\n            The generation configuration to be used as base parametrization for the generation call.","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/huggingface/transformers/blob/a597f974857b3d92939971296bc0deb93d33d780/src/transformers/generation/candidate_generator.py#L56-L92","documentation":"The second abstract method of CandidateGenerator: update_candidate_strategy lets generators adapt after each verification round (e.g. dynamic num_assistant_tokens). A subclass that does not override it will crash during assisted generation after the first candidate validation step.","triggerScenarios":"Custom CandidateGenerator subclass implementing only get_candidates; the error surfaces inside model.generate's _assisted_decoding loop after the first batch of candidates is scored, when update_candidate_strategy is invoked.","commonSituations":"Partial implementations of custom candidate generators, or copy-pasting an old generator whose signature changed after a transformers upgrade.","solutions":["Implement update_candidate_strategy(input_ids, scores, num_matches) in your subclass (a no-op pass is acceptable if no adaptation is needed)","Mirror the signatures from AssistedCandidateGenerator to stay compatible with the current loop"],"exampleFix":"class MyGenerator(CandidateGenerator):\n    def get_candidates(self, input_ids, **kwargs): ...\n    # after: add the missing method\n    def update_candidate_strategy(self, input_ids, scores, num_matches):\n        pass","handlingStrategy":"type-guard","validationCode":"from transformers.generation.candidate_generator import CandidateGenerator\n\ndef implements_update_strategy(gen) -> bool:\n    return type(gen).update_candidate_strategy is not CandidateGenerator.update_candidate_strategy","typeGuard":"from transformers.generation.candidate_generator import CandidateGenerator\n\ndef is_complete_candidate_generator(cls) -> bool:\n    return (\n        isinstance(cls, type) and issubclass(cls, CandidateGenerator)\n        and cls.get_candidates is not CandidateGenerator.get_candidates\n        and cls.update_candidate_strategy is not CandidateGenerator.update_candidate_strategy\n    )","tryCatchPattern":null,"preventionTips":["When subclassing CandidateGenerator, implement both abstract methods immediately","Consider collections.abc-style registration or a base test that instantiates and calls both methods"],"tags":["python","transformers","generation","assisted-decoding","abstract-class"],"backgroundTag":null,"analyzedSha":"a597f974857b3d92939971296bc0deb93d33d780","analyzedAt":"2026-08-14T18:24:08.354Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}