Plugins
class PluginMethod
Section titled “class PluginMethod”class PluginMethod(BaseModel):Source
class PluginMethod(BaseModel): name: str label: Optional[str] = None description: Optional[str] = None input_ocels: dict[str, OCELAnnotation] = Field(default_factory=dict) input_resources: dict[str, tuple[str, ResourceAnnotation]] = Field(default_factory=dict)
results: list[PluginResult] = Field(default_factory=list)
_input_model: Optional[type[PluginInput]] = PrivateAttr(default=None) _method: Callable[..., PluginReturnType] = PrivateAttr() _resource_types: set[type[Resource]] = PrivateAttr(default_factory=set)
@computed_field def input_schema(self) -> dict[str, Any] | None: return self._input_model.model_json_schema() if self._input_model is not None else Noneclass PluginMeta
Section titled “class PluginMeta”class PluginMeta(BaseModel):Source
class PluginMeta(BaseModel): name: str version: str label: str description: Optional[str]class Plugin
Section titled “class Plugin”class Plugin(ABC):Source
class Plugin(ABC): version: ClassVar[str] label: ClassVar[str] description: ClassVar[Optional[str]] = None
@classmethod def meta(cls): return PluginMeta( name=cls.__name__, version=cls.version, description=cls.description, label=cls.label )
@classmethod def method_map(cls) -> dict[str, PluginMethod]: method_map: dict[str, PluginMethod] = {} for _, method in inspect.getmembers(cls, predicate=inspect.isfunction): method_meta = getattr(method, "__meta__", None)
if not isinstance(method_meta, PluginMethod): continue
method_map[method_meta.name] = method_meta
return method_mapfunction plugin_method
Section titled “function plugin_method”def plugin_method(label: Optional[str] = None, description: Optional[str] = None):Decorator that marks a plugin class method as an Ocelescope runnable function.
Parameters:
labelOptional[str]— Human-readable label shown in the UI for the method. If not provided, the UI may fall back to the Python method name.descriptionOptional[str]— Human-readable description shown in the UI for the method.
Source
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) # ty: ignore[unresolved-attribute] 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 decoratorclass OCELAnnotation
Section titled “class OCELAnnotation”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:
labelstr— Human-readable label to display in the UI.descriptionOptional[str]— Optional longer text shown in the UI to explain the OCEL.extensionOptional[str]— Optional extension identifier. If constructed with anOCELExtensionclass, it is coerced to that class’ name.
Source
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)class ResourceAnnotation
Section titled “class ResourceAnnotation”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:
labelstr— Human-readable label to display in the UI.descriptionOptional[str]— Optional longer text shown in the UI to explain the resource.
Source
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)attribute PluginResult
Section titled “attribute PluginResult”PluginResult: TypeAlias = Annotated[Union[OCELResult, ResourceResult], Field(discriminator='type')]class PluginInput
Section titled “class PluginInput”class PluginInput(ABC, BaseModel):Source
class PluginInput(ABC, BaseModel): passfunction OCEL_FIELD
Section titled “function OCEL_FIELD”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.
Parameters:
field_typeLiteral['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").ocel_idstr— Identifier/name of the OCEL input this field depends on.defaultAny— Default value, or...to make the field required.titleOptional[str]— Optional UI title for the field.descriptionOptional[str]— Optional UI help text for the field.
Source
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}, )function COMPUTED_SELECTION
Section titled “function COMPUTED_SELECTION”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.
Parameters:
titleOptional[str]— Optional UI title for the field.descriptionOptional[str]— Optional UI help text for the field.providerstr— The name (ID) of the provider function used by the frontend to compute the available options.depends_onlist[str] | None— Optional list of field names this selection depends on.defaultAny— Default value, or...to make the field required.
Source
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}, )