{"record":{"id":"8ba62655ca952a58","repo":"mlflow/mlflow","slug":"llamaindex-workflow-is-not-an-engine","errorCode":null,"errorMessage":"LlamaIndex Workflow is not an engine","messagePattern":"LlamaIndex Workflow is not an engine","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"warning","filePath":"mlflow/llama_index/pyfunc_wrapper.py","lineNumber":179,"sourceCode":"    def engine_type(self):\n        return RETRIEVER_ENGINE_NAME\n\n    def _predict_single(self, *args, **kwargs) -> list[dict[str, Any]]:\n        response = self._llama_model.retrieve(*args, **kwargs)\n        return [node.dict() for node in response]\n\n    def _format_predict_input(self, data) -> \"QueryBundle\":\n        return _format_predict_input_query_engine_and_retriever(data)\n\n\nclass WorkflowWrapper(_LlamaIndexModelWrapperBase):\n    @property\n    def index(self):\n        raise NotImplementedError(\"LlamaIndex Workflow does not have an index\")\n\n    @property\n    def engine_type(self):\n        raise NotImplementedError(\"LlamaIndex Workflow is not an engine\")\n\n    def predict(self, data, params: dict[str, Any] | None = None) -> list[str] | str:\n        inputs = self._format_predict_input(data, params)\n\n        # LlamaIndex Workflow runs async but MLflow pyfunc doesn't support async inference yet.\n        predictions = self._wait_async_task(self._run_predictions(inputs))\n\n        # Even if the input is single instance, the signature enforcement convert it to a Pandas\n        # DataFrame with a single row. In this case, we should unwrap the result (list) so it\n        # won't be inconsistent with the output without signature enforcement.\n        should_unwrap = len(data) == 1 and isinstance(predictions, list)\n        return predictions[0] if should_unwrap else predictions\n\n    def _format_predict_input(\n        self, data, params: dict[str, Any] | None = None\n    ) -> list[dict[str, Any]]:\n        inputs = _convert_llm_input_data_with_unwrapping(data)\n        params = params or {}","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/mlflow/mlflow/blob/6a27f2decc0b76eb1b54af31849784addb357dbc/mlflow/llama_index/pyfunc_wrapper.py#L161-L197","documentation":"Workflows are not query/chat engines, so WorkflowWrapper.engine_type raises NotImplementedError intentionally. Any code path that introspects .engine_type (e.g. routing logic, serialization metadata readers, or tools expecting a query/chat/retriever engine) hits this when the loaded model is a Workflow.","triggerScenarios":"Reading model.engine_type on a pyfunc loaded from a model saved as a LlamaIndex Workflow; MLflow-adjacent utilities or logging code that unconditionally reads engine_type.","commonSituations":"Generic model-metadata collectors; pipelines that dispatch on engine_type after swapping an engine-based model for a Workflow; test harnesses asserting engine_type for all LlamaIndex models.","solutions":["Guard with try/except NotImplementedError or check the underlying type (Workflow) before reading engine_type.","If downstream code requires an engine_type, save an index/engine-based model instead of a Workflow.","Treat Workflow models as predict()-only and adjust dispatch logic to a no-op/\"workflow\" branch."],"exampleFix":"// before\nprint(f\"engine: {model.engine_type}\")\n// after\ntry:\n    print(f\"engine: {model.engine_type}\")\nexcept NotImplementedError:\n    print(\"engine: workflow (no engine)\")","handlingStrategy":"type-guard","validationCode":"from llama_index.core.workflow import Workflow\ndef safe_engine_type(model):\n    if isinstance(getattr(model, \"_llama_model\", None), Workflow):\n        return \"workflow\"\n    return model.engine_type","typeGuard":"def has_engine_type(model) -> bool:\n    from llama_index.core.workflow import Workflow\n    return not isinstance(getattr(model, \"_llama_model\", None), Workflow)","tryCatchPattern":"try:\n    etype = model.engine_type\nexcept NotImplementedError:\n    etype = \"workflow\"","preventionTips":["Guard engine_type reads behind a helper with a 'workflow' default.","Don't dispatch strictly on engine_type; handle the Workflow case.","Keep metadata collectors tolerant of NotImplementedError.","Prefer explicit model-type checks over property probing."],"tags":["mlflow","llama-index","workflow","not-implemented"],"backgroundTag":"api-not-supported-on-this-type","analyzedSha":"6a27f2decc0b76eb1b54af31849784addb357dbc","analyzedAt":"2026-08-29T20:54:51.419Z","schemaVersion":2},"datasetVersion":"2026-08-29T22:17:34.462Z"}