mlflow/mlflow · warning · NotImplementedError

LlamaIndex Workflow is not an engine

Error message

LlamaIndex Workflow is not an engine

What it means

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.

Source

Thrown at mlflow/llama_index/pyfunc_wrapper.py:179

    def engine_type(self):
        return RETRIEVER_ENGINE_NAME

    def _predict_single(self, *args, **kwargs) -> list[dict[str, Any]]:
        response = self._llama_model.retrieve(*args, **kwargs)
        return [node.dict() for node in response]

    def _format_predict_input(self, data) -> "QueryBundle":
        return _format_predict_input_query_engine_and_retriever(data)


class WorkflowWrapper(_LlamaIndexModelWrapperBase):
    @property
    def index(self):
        raise NotImplementedError("LlamaIndex Workflow does not have an index")

    @property
    def engine_type(self):
        raise NotImplementedError("LlamaIndex Workflow is not an engine")

    def predict(self, data, params: dict[str, Any] | None = None) -> list[str] | str:
        inputs = self._format_predict_input(data, params)

        # LlamaIndex Workflow runs async but MLflow pyfunc doesn't support async inference yet.
        predictions = self._wait_async_task(self._run_predictions(inputs))

        # Even if the input is single instance, the signature enforcement convert it to a Pandas
        # DataFrame with a single row. In this case, we should unwrap the result (list) so it
        # won't be inconsistent with the output without signature enforcement.
        should_unwrap = len(data) == 1 and isinstance(predictions, list)
        return predictions[0] if should_unwrap else predictions

    def _format_predict_input(
        self, data, params: dict[str, Any] | None = None
    ) -> list[dict[str, Any]]:
        inputs = _convert_llm_input_data_with_unwrapping(data)
        params = params or {}

View on GitHub (pinned to 6a27f2decc)

Solutions

  1. Guard with try/except NotImplementedError or check the underlying type (Workflow) before reading engine_type.
  2. If downstream code requires an engine_type, save an index/engine-based model instead of a Workflow.
  3. Treat Workflow models as predict()-only and adjust dispatch logic to a no-op/"workflow" branch.

Example fix

// before
print(f"engine: {model.engine_type}")
// after
try:
    print(f"engine: {model.engine_type}")
except NotImplementedError:
    print("engine: workflow (no engine)")
Defensive patterns

Strategy: type-guard

Validate before calling

from llama_index.core.workflow import Workflow
def safe_engine_type(model):
    if isinstance(getattr(model, "_llama_model", None), Workflow):
        return "workflow"
    return model.engine_type

Type guard

def has_engine_type(model) -> bool:
    from llama_index.core.workflow import Workflow
    return not isinstance(getattr(model, "_llama_model", None), Workflow)

Try / catch

try:
    etype = model.engine_type
except NotImplementedError:
    etype = "workflow"

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of mlflow/mlflow@6a27f2decc (2026-08-29). Data as JSON: /api/errors/8ba62655ca952a58. Report an issue: GitHub.