diff --git a/lib/crewai/src/crewai/agents/agent_builder/base_agent.py b/lib/crewai/src/crewai/agents/agent_builder/base_agent.py index 8792ed753..b50799811 100644 --- a/lib/crewai/src/crewai/agents/agent_builder/base_agent.py +++ b/lib/crewai/src/crewai/agents/agent_builder/base_agent.py @@ -82,6 +82,7 @@ _LLM_TYPE_REGISTRY: dict[str, str] = { def _validate_llm_ref(value: Any) -> Any: if isinstance(value, dict): import importlib + import inspect llm_type = value.get("llm_type") if not llm_type or llm_type not in _LLM_TYPE_REGISTRY: @@ -92,6 +93,12 @@ def _validate_llm_ref(value: Any) -> Any: dotted = _LLM_TYPE_REGISTRY[llm_type] mod_path, cls_name = dotted.rsplit(".", 1) cls = getattr(importlib.import_module(mod_path), cls_name) + if inspect.isabstract(cls): + from crewai.llm import LLM + + return LLM( + **{k: v for k, v in value.items() if v is not None and k != "llm_type"} + ) return cls(**value) return value diff --git a/lib/crewai/tests/test_checkpoint.py b/lib/crewai/tests/test_checkpoint.py index 9113a6669..8cd7cf399 100644 --- a/lib/crewai/tests/test_checkpoint.py +++ b/lib/crewai/tests/test_checkpoint.py @@ -2,6 +2,7 @@ from __future__ import annotations +import inspect import json import os import sqlite3 @@ -742,3 +743,26 @@ class TestCheckpointReusedExecutor: assert len(result.tasks_output) == 2 assert result.tasks_output[1].raw + + +class TestCustomLLMCheckpointRestore: + """A custom BaseLLM subclass serializes with the inherited llm_type "base". + + Restoring it must not try to instantiate the abstract BaseLLM; it is rebuilt + as a concrete LLM from the saved config instead. + """ + + def test_restore_does_not_instantiate_abstract_base_llm(self) -> None: + agent = Agent(role="r", goal="g", backstory="b", llm=_FinalAnswerLLM()) + task = Task(description="d", expected_output="e", agent=agent) + crew = Crew(agents=[agent], tasks=[task], verbose=False) + + raw = RuntimeState(root=[crew]).model_dump_json() + restored = RuntimeState.model_validate_json( + raw, context={"from_checkpoint": True} + ) + + llm = restored.root[0].agents[0].llm + assert isinstance(llm, BaseLLM) + assert not inspect.isabstract(type(llm)) + assert llm.model == "stub"