Files
cleveragents-core/robot/helper_unified_context_models.py
freemo fae438a7a7 refactor(acms): unify ContextFragment model hierarchies by extending CRP base types
Core domain types (FragmentProvenance, ContextFragment, ContextBudget,
ContextPayload) now extend their CRP counterparts via Pydantic v2
inheritance, ensuring isinstance compatibility across the model
hierarchy.

Key changes:
- CRP base types made frozen=True (no consumer mutates them)
- CRP AssembledContext fields changed from list to tuple (frozen consistency)
- Core types extend CRP bases: FragmentProvenance(CRPFragmentProvenance),
  ContextFragment(CRPContextFragment), ContextBudget(CRPContextBudget),
  ContextPayload(CRPAssembledContext)
- Removed duplicate ContextFragment dataclass from skeleton_compressor
- Updated project_context.py to pass tuples to frozen AssembledContext
- Added Behave tests (10 scenarios), Robot integration tests (3 cases),
  and ASV benchmarks for the unified hierarchy
- Updated Known Limitations table in docs/reference/acms.md

ISSUES CLOSED: #569
2026-03-05 21:38:55 +00:00

179 lines
5.3 KiB
Python

"""Robot Framework helper for unified context model integration tests.
Verifies isinstance compatibility across the CRP/core model hierarchy
and basic construction/immutability contracts.
Usage:
python robot/helper_unified_context_models.py isinstance-check
python robot/helper_unified_context_models.py construction-check
python robot/helper_unified_context_models.py immutability-check
"""
from __future__ import annotations
import sys
from collections.abc import Callable
from pathlib import Path
_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
sys.path.insert(0, _SRC)
from cleveragents.domain.models.acms.crp import ( # noqa: E402
AssembledContext as CRPAssembledContext,
)
from cleveragents.domain.models.acms.crp import ( # noqa: E402
ContextBudget as CRPContextBudget,
)
from cleveragents.domain.models.acms.crp import ( # noqa: E402
ContextFragment as CRPContextFragment,
)
from cleveragents.domain.models.acms.crp import ( # noqa: E402
FragmentProvenance as CRPFragmentProvenance,
)
from cleveragents.domain.models.core.context_fragment import ( # noqa: E402
ContextBudget,
ContextFragment,
ContextPayload,
FragmentProvenance,
)
_DEFAULT_PROV = FragmentProvenance(resource_uri="test://robot-unified")
def _cmd_isinstance_check() -> int:
"""Verify isinstance compatibility for all four model types."""
prov = FragmentProvenance(resource_uri="test://robot")
if not isinstance(prov, CRPFragmentProvenance):
print("FAIL: FragmentProvenance is not instance of CRP type")
return 1
frag = ContextFragment(
uko_node="test://robot",
content="hello",
token_count=5,
provenance=prov,
)
if not isinstance(frag, CRPContextFragment):
print("FAIL: ContextFragment is not instance of CRP type")
return 1
budget = ContextBudget(max_tokens=4096, reserved_tokens=512)
if not isinstance(budget, CRPContextBudget):
print("FAIL: ContextBudget is not instance of CRP type")
return 1
payload = ContextPayload(plan_id="01JQTESTPN00000000000000AA")
if not isinstance(payload, CRPAssembledContext):
print("FAIL: ContextPayload is not instance of CRP AssembledContext")
return 1
print("unified-isinstance-ok")
return 0
def _cmd_construction_check() -> int:
"""Verify construction with inherited and extended fields."""
prov = FragmentProvenance(
resource_uri="test://construct",
strategy="test_strategy",
resource_type="git-checkout",
)
if prov.strategy != "test_strategy":
print(f"FAIL: expected strategy='test_strategy', got {prov.strategy!r}")
return 1
if prov.resource_type != "git-checkout":
print(
f"FAIL: expected resource_type='git-checkout', got {prov.resource_type!r}"
)
return 1
frag = ContextFragment(
uko_node="project://test",
content="constructed fragment",
token_count=20,
tier="hot",
provenance=prov,
)
if frag.tier != "hot":
print(f"FAIL: expected tier='hot', got {frag.tier!r}")
return 1
if not frag.fragment_id:
print("FAIL: fragment_id is empty")
return 1
if len(frag.fragment_id) != 26:
print(f"FAIL: expected ULID (26 chars), got {len(frag.fragment_id)}")
return 1
budget = ContextBudget(max_tokens=8192, reserved_tokens=1024)
if budget.available_tokens != 7168:
print(f"FAIL: expected available=7168, got {budget.available_tokens}")
return 1
print("unified-construction-ok")
return 0
def _cmd_immutability_check() -> int:
"""Verify that frozen models reject mutation."""
frag = ContextFragment(
uko_node="project://immut",
content="test",
token_count=4,
provenance=_DEFAULT_PROV,
)
try:
frag.uko_node = "project://mutated" # type: ignore[misc]
print("FAIL: expected frozen error for ContextFragment mutation")
return 1
except Exception:
pass
budget = ContextBudget(max_tokens=4096, reserved_tokens=512)
try:
budget.max_tokens = 9999 # type: ignore[misc]
print("FAIL: expected frozen error for ContextBudget mutation")
return 1
except Exception:
pass
prov = CRPFragmentProvenance(resource_uri="test://crp")
try:
prov.resource_uri = "test://mutated" # type: ignore[misc]
print("FAIL: expected frozen error for CRP FragmentProvenance mutation")
return 1
except Exception:
pass
print("unified-immutability-ok")
return 0
_COMMANDS: dict[str, Callable[[], int]] = {
"isinstance-check": _cmd_isinstance_check,
"construction-check": _cmd_construction_check,
"immutability-check": _cmd_immutability_check,
}
def main() -> int:
"""Entry point called by Robot Framework ``Run Process``."""
if len(sys.argv) < 2:
print(
"Usage: helper_unified_context_models.py "
"<isinstance-check|construction-check|immutability-check>"
)
return 1
command = sys.argv[1]
handler = _COMMANDS.get(command)
if handler is None:
print(f"Unknown command: {command}")
return 1
return handler()
if __name__ == "__main__":
sys.exit(main())