{"record":{"id":"d9010e1318b3a489","repo":"sgl-project/sglang","slug":"spec-class-name-is-missing-duck-typed-method","errorCode":null,"errorMessage":"{spec_class.__name__} is missing duck-typed methods from SpeculativeAlgorithm: {missing}. Add them to {spec_class.__name__} so plugin-registered algorithms stay dispatchable.","messagePattern":"(.+?) is missing duck-typed methods from SpeculativeAlgorithm: (.+?)\\. Add them to (.+?) so plugin-registered algorithms stay dispatchable\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/speculative/spec_registry.py","lineNumber":218,"sourceCode":"\n    Called from ``register_algorithm`` rather than at import time because\n    ``spec_info`` imports this module, so ``SpeculativeAlgorithm`` does not yet\n    exist while this module is loading; at registration time it is fully\n    defined.\n    \"\"\"\n    # NOTE: use ``vars()`` not ``dir()`` for the enum — ``EnumMeta.__dir__``\n    # hides instance methods, so ``dir(SpeculativeAlgorithm)`` would yield an\n    # empty interface and turn this guard into a silent no-op.\n    from sglang.srt.speculative.spec_info import SpeculativeAlgorithm\n\n    interface = {\n        name\n        for name in vars(SpeculativeAlgorithm)\n        if name.startswith((\"is_\", \"supports_\"))\n    }\n    missing = sorted(interface - set(dir(spec_class)))\n    if missing:\n        raise TypeError(\n            f\"{spec_class.__name__} is missing duck-typed methods from \"\n            f\"SpeculativeAlgorithm: {missing}. Add them to {spec_class.__name__} \"\n            \"so plugin-registered algorithms stay dispatchable.\"\n        )\n\n\ndef register_algorithm(\n    name: str,\n    *,\n    supports_overlap: bool = False,\n    validate_server_args: Optional[ServerArgsValidator] = None,\n    spec_class: Type[CustomSpecAlgo] = CustomSpecAlgo,\n) -> Callable[[WorkerFactory], WorkerFactory]:\n    \"\"\"Return a decorator that registers a plugin algorithm under ``name``.\n\n    Pass a ``spec_class`` subclass of ``CustomSpecAlgo`` to override any\n    ``is_*()`` / ``supports_*()`` / ``create_worker`` method.\n    \"\"\"","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/speculative/spec_registry.py#L200-L236","documentation":"Plugin-registered algorithms are duck-typed against SpeculativeAlgorithm: any is_*() / supports_*() predicate defined on the enum must exist on the custom spec class so dispatch code can call it uniformly. register_algorithm runs _assert_custom_spec_algo_conforms and raises TypeError listing the missing names.","triggerScenarios":"Calling register_algorithm with a CustomSpecAlgo subclass that does not define (or inherit) one of the is_*/supports_* predicates added to SpeculativeAlgorithm — commonly after upgrading SGLang when new predicates were added to the interface.","commonSituations":"Version upgrades adding new predicates (e.g. supports_overlap, is_eagle) break older plugin classes; hand-written spec classes missing methods.","solutions":["Add the listed missing methods (usually returning False) to your spec class","Alternatively inherit from a conforming base like CustomSpecAlgo's latest version that provides defaults","Pin/align your plugin with the SGLang version that added the predicates"],"exampleFix":"# before\nclass MyAlgo(CustomSpecAlgo):\n    ...\n# after\nclass MyAlgo(CustomSpecAlgo):\n    def supports_overlap(self) -> bool:\n        return False\n    def is_eagle(self) -> bool:\n        return False","handlingStrategy":"type-guard","validationCode":"required = {n for n in vars(SpeculativeAlgorithm) if n.startswith(('is_','supports_'))}\nmissing = sorted(required - set(dir(MyAlgo)))\nassert not missing, missing","typeGuard":"def conforms(spec_class: type) -> bool:\n    required = {n for n in vars(SpeculativeAlgorithm) if n.startswith(('is_','supports_'))}\n    return not (required - set(dir(spec_class)))","tryCatchPattern":null,"preventionTips":["Subclass the latest CustomSpecAlgo base so new predicates come for free","Add a conformance smoke test to your plugin's CI against the installed SGLang version"],"tags":["speculative-decoding","plugin-api","duck-typing"],"backgroundTag":"interface-conformance-error","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}