Skip to content

Plugins

OCELAnnotation

Bases: Annotation

UI annotation metadata for an OCEL-typed parameter or result.

In addition to the base Annotation fields, this annotation may specify an OCEL extension. To keep the annotation JSON-serializable and stable for frontend consumption, the constructor accepts an OCELExtension class and coerces it to its class name (a string).

Attributes:

Name Type Description
label str

Human-readable label to display in the UI.

description Optional[str]

Optional longer text shown in the UI to explain the OCEL.

extension Optional[str]

Optional extension identifier. If constructed with an OCELExtension class, it is coerced to that class' name.

Source code in src/ocelescope/src/ocelescope/plugin/decorators.py
class OCELAnnotation(Annotation):
    """UI annotation metadata for an `OCEL`-typed parameter or result.

    In addition to the base `Annotation` fields, this annotation may specify an
    OCEL extension. To keep the annotation JSON-serializable and stable for
    frontend consumption, the constructor accepts an `OCELExtension` class and
    coerces it to its class name (a string).

    Attributes:
        label: Human-readable label to display in the UI.
        description: Optional longer text shown in the UI to explain the OCEL.
        extension: Optional extension identifier. If constructed with an
            `OCELExtension` class, it is coerced to that class' name.

    """

    extension: Optional[str] = None

    @overload
    def __init__(
        self, *, label: str, description: Optional[str] = ..., extension: None = ...
    ) -> None: ...
    @overload
    def __init__(
        self, *, label: str, description: Optional[str] = ..., extension: type[OCELExtension]
    ) -> None: ...

    def __init__(self, **data: Any) -> None:
        ext = data.get("extension", None)
        if isinstance(ext, type) and issubclass(ext, OCELExtension):
            data["extension"] = ext.__name__  # coerce class → str

        super().__init__(**data)

ResourceAnnotation

Bases: Annotation

UI annotation metadata for a Resource-typed parameter or result.

This annotation is used to provide frontend-facing text (label/description) for resources.

Attributes:

Name Type Description
label str

Human-readable label to display in the UI.

description Optional[str]

Optional longer text shown in the UI to explain the resource.

Source code in src/ocelescope/src/ocelescope/plugin/decorators.py
class ResourceAnnotation(Annotation):
    """UI annotation metadata for a `Resource`-typed parameter or result.

    This annotation is used to provide frontend-facing text (label/description)
    for resources.

    Attributes:
        label: Human-readable label to display in the UI.
        description: Optional longer text shown in the UI to explain the resource.
    """

    annotation_resources: list[type[Resource]] | None = Field(exclude=True, default=None)

plugin_method

plugin_method(label=None, description=None)

Decorator that marks a plugin class method as an Ocelescope runnable function.

Parameters:

Name Type Description Default
label Optional[str]

Human-readable label shown in the UI for the method. If not provided, the UI may fall back to the Python method name.

None
description Optional[str]

Human-readable description shown in the UI for the method.

None
Source code in src/ocelescope/src/ocelescope/plugin/decorators.py
def plugin_method(
    label: Optional[str] = None,
    description: Optional[str] = None,
):
    """Decorator that marks a plugin class method as an Ocelescope runnable function.

    Args:
        label: Human-readable label shown in the UI for the method. If not provided,
            the UI may fall back to the Python method name.
        description: Human-readable description shown in the UI for the method.

    """

    def decorator(func: Callable[..., PluginReturnType]):
        plugin_method_meta = PluginMethod(name=func.__name__, label=label, description=description)
        method_hints = get_type_hints(func, include_extras=True)

        for arg_name, hint in method_hints.items():
            base_type, annotation = extract_info(hint)

            if not isinstance(base_type, type) or arg_name == "return":
                continue

            if issubclass(base_type, PluginInput):
                plugin_method_meta._input_model = base_type
            elif issubclass(base_type, OCEL):
                plugin_method_meta.input_ocels[arg_name] = (
                    OCELAnnotation(**annotation.model_dump())
                    if annotation is not None
                    else OCELAnnotation(label=arg_name)
                )
            elif issubclass(base_type, Resource):
                plugin_method_meta._resource_types.add(base_type)

                plugin_method_meta.input_resources[arg_name] = (
                    base_type.get_type(),
                    ResourceAnnotation(**annotation.model_dump())
                    if annotation is not None
                    else ResourceAnnotation(label=arg_name),
                )
            else:
                raise TypeError(
                    f"Argument {arg_name} must be either an OCEL, Resource or Input Schema"
                )

        return_type = method_hints.get("return", None)

        if return_type is not None:
            origin = get_origin(return_type)

            types_to_parse = []

            if origin is tuple:
                types_to_parse = get_args(return_type)
            else:
                types_to_parse = [return_type]

            for typ in types_to_parse:
                base_type, annotation = extract_info(typ)

                if get_origin(base_type) is list:
                    inner_type = get_args(base_type)[0]
                    base_type, annotation = extract_info(inner_type)
                    is_list = True
                else:
                    is_list = False

                # Now determine what kind of result it is
                if issubclass(base_type, OCEL):
                    plugin_method_meta.results.append(
                        OCELResult(
                            type="ocel",
                            is_list=is_list,
                            annotation=OCELAnnotation(**annotation.model_dump())
                            if annotation is not None
                            else None,
                        )
                    )
                elif issubclass(base_type, Resource):
                    plugin_method_meta._resource_types.add(base_type)

                    annotation_obj = (
                        annotation if isinstance(annotation, ResourceAnnotation) else None
                    )
                    if annotation_obj and annotation_obj.annotation_resources:
                        plugin_method_meta._resource_types.update(
                            annotation_obj.annotation_resources
                        )

                    plugin_method_meta.results.append(
                        ResourceResult(
                            type="resource",
                            is_list=is_list,
                            annotation=annotation_obj,
                            resource_type=base_type.get_type(),
                        )
                    )
                else:
                    raise TypeError(f"Unsupported return type: {base_type}")

        plugin_method_meta._method = func

        setattr(
            func,
            "__meta__",
            plugin_method_meta,
        )

        return func

    return decorator

OCEL_FIELD

OCEL_FIELD(
    *,
    field_type,
    ocel_id,
    default=...,
    title=None,
    description=None,
)

Create a Pydantic Field with Ocelescope UI metadata for OCEL-based inputs.

Parameters:

Name Type Description Default
field_type Literal['object_type', 'event_type', 'event_id', 'object_id', 'event_attribute', 'object_attribute', 'time_frame']

What kind of OCEL field the user should select (e.g. "event_attribute" or "object_type").

required
ocel_id str

Identifier/name of the OCEL input this field depends on.

required
default Any

Default value, or ... to make the field required.

...
title Optional[str]

Optional UI title for the field.

None
description Optional[str]

Optional UI help text for the field.

None
Source code in src/ocelescope/src/ocelescope/plugin/input.py
def OCEL_FIELD(
    *,
    field_type: Literal[
        "object_type",
        "event_type",
        "event_id",
        "object_id",
        "event_attribute",
        "object_attribute",
        "time_frame",
    ],
    ocel_id: str,
    default: Any = ...,
    title: Optional[str] = None,
    description: Optional[str] = None,
) -> Any:
    """Create a Pydantic `Field` with Ocelescope UI metadata for OCEL-based inputs.

    Args:
        field_type: What kind of OCEL field the user should select (e.g.
            `"event_attribute"` or `"object_type"`).
        ocel_id: Identifier/name of the OCEL input this field depends on.
        default: Default value, or `...` to make the field required.
        title: Optional UI title for the field.
        description: Optional UI help text for the field.
    """
    extra: dict[str, Any] = {
        "type": "ocel",
        "field_type": field_type,
        "ocel_id": ocel_id,
    }

    return Field(
        default=default,
        title=title,
        description=description,
        json_schema_extra={"x-ui-meta": extra},
    )

COMPUTED_SELECTION

COMPUTED_SELECTION(
    *,
    title=None,
    description=None,
    provider,
    depends_on=None,
    default=...,
)

Create a Pydantic Field for a UI selection computed by a provider.

Parameters:

Name Type Description Default
title Optional[str]

Optional UI title for the field.

None
description Optional[str]

Optional UI help text for the field.

None
provider str

The name (ID) of the provider function used by the frontend to compute the available options.

required
depends_on list[str] | None

Optional list of field names this selection depends on.

None
default Any

Default value, or ... to make the field required.

...
Source code in src/ocelescope/src/ocelescope/plugin/input.py
def COMPUTED_SELECTION(
    *,
    title: Optional[str] = None,
    description: Optional[str] = None,
    provider: str,
    depends_on: list[str] | None = None,
    default: Any = ...,
):
    """Create a Pydantic `Field` for a UI selection computed by a provider.

    Args:
        title: Optional UI title for the field.
        description: Optional UI help text for the field.
        provider: The name (ID) of the provider function used by the frontend to compute the available options.
        depends_on: Optional list of field names this selection depends on.
        default: Default value, or `...` to make the field required.

    """
    meta = {
        "type": "computed_select",
        "provider": provider,
        "dependsOn": depends_on or [],
    }

    return Field(
        default=default,
        title=title,
        description=description,
        json_schema_extra={"x-ui-meta": meta},
    )