Kairo-Jinkendo/backend/steering/methods/registry.py
Lars d3bf3c36e0
Some checks failed
Deploy Development / deploy (push) Successful in 46s
Test Suite / pytest-backend (push) Failing after 3m17s
Test Suite / k6 /api/health Baseline (push) Has been skipped
Test Suite / playwright-smoke (push) Has been skipped
Test Suite / lint-backend (push) Successful in 2s
Test Suite / compose-smoke (push) Has been skipped
AP2.4: Steuerungselement-Registry und Methoden-Vertrag.
MethodDefinition traegt steering_elements/ui_features/graph_profile; operating-context und FE-Registry ohne Archetyp-Ifs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-25 11:27:59 +02:00

111 lines
3.3 KiB
Python

"""Method Registry — built-in steering methods — AP2.3e / AP2.4."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Literal
from steering.elements.registry import validate_steering_element_keys
from steering.graph.profiles import GraphMethodProfile, LIGHT
_METHODS: dict[str, "MethodDefinition"] = {}
@dataclass(frozen=True)
class MethodDefinition:
key: str
version: str
label: str
description: str
default_lifecycle_steps: tuple[str, ...]
next_action_strategy_key: str = "default"
data_slices: frozenset[str] = frozenset()
compatible_archetype_keys: frozenset[str] | Literal["*"] = "*"
steering_elements: frozenset[str] = frozenset()
ui_features: frozenset[str] = frozenset()
graph_profile: GraphMethodProfile = LIGHT
method_role: Literal["primary", "modifier"] = "primary"
composes_with: frozenset[str] = frozenset()
def ui_features_as_dict(features: frozenset[str]) -> dict[str, bool]:
return {key: True for key in sorted(features)}
def method_compatible_with_archetype(
method: MethodDefinition,
archetype_key: str,
*,
om_capabilities: frozenset[str] | None = None,
) -> bool:
compat = method.compatible_archetype_keys
if compat != "*" and archetype_key not in compat:
return False
if om_capabilities is not None and method.data_slices:
if not method.data_slices.issubset(om_capabilities):
return False
return True
def list_compatible_methods(
archetype_key: str,
*,
om_capabilities: frozenset[str] | None = None,
) -> tuple[MethodDefinition, ...]:
return tuple(
m
for m in _METHODS.values()
if method_compatible_with_archetype(
m, archetype_key, om_capabilities=om_capabilities
)
)
def register_method(defn: MethodDefinition) -> None:
if defn.key in _METHODS:
raise ValueError(f"Method already registered: {defn.key}")
if defn.steering_elements:
validate_steering_element_keys(defn.steering_elements)
_METHODS[defn.key] = defn
def get_method(key: str) -> MethodDefinition | None:
return _METHODS.get(key)
def list_methods() -> tuple[MethodDefinition, ...]:
return tuple(_METHODS.values())
def method_to_dict(
method: MethodDefinition,
*,
include_compatibility: bool = True,
) -> dict[str, Any]:
payload: dict[str, Any] = {
"key": method.key,
"version": method.version,
"label": method.label,
"description": method.description,
"next_action_strategy_key": method.next_action_strategy_key,
"steering_elements": sorted(method.steering_elements),
"ui_features": ui_features_as_dict(method.ui_features),
"graph_profile": {
"enforce_gate_blocking": method.graph_profile.enforce_gate_blocking,
"emphasize_fulfillment": method.graph_profile.emphasize_fulfillment,
},
"method_role": method.method_role,
"composes_with": sorted(method.composes_with),
"data_slices": sorted(method.data_slices),
}
if include_compatibility:
compat = method.compatible_archetype_keys
payload["compatible_archetype_keys"] = (
"*" if compat == "*" else sorted(compat)
)
return payload
def clear_methods_for_tests() -> None:
_METHODS.clear()