eb46f0ff54
CI / push-validation (push) Successful in 40s
CI / helm (push) Successful in 44s
CI / build (push) Successful in 1m14s
CI / lint (push) Successful in 1m18s
CI / quality (push) Successful in 1m36s
CI / e2e_tests (push) Successful in 1m19s
CI / typecheck (push) Successful in 2m22s
CI / security (push) Successful in 2m23s
CI / benchmark-regression (push) Failing after 41s
CI / integration_tests (push) Successful in 4m46s
CI / unit_tests (push) Failing after 20m13s
CI / benchmark-publish (push) Failing after 28m28s
CI / coverage (push) Has been cancelled
CI / docker (push) Has been cancelled
CI / status-check (push) Has been cancelled
## Summary This PR fixes issue #10878 where architecture reviews were truncated because the regex pattern for parsing file output would stop at the first ``` encountered in the Markdown report. ## Changes - Change file delimiters from ``` to >>>>>>>/<<<<<<< to avoid Markdown conflicts - Add tier hydration before strategize phase in plan_executor.py - Increase max_tokens to 16384 in llm_actors.py for longer outputs - Increase context_max_tokens_hot from 16000 to 32000 in settings.py - Fix get_hot_view → get_hot_fragments in strategy_actor.py and plan_executor.py - Add opencode to skip directories in context_tier_hydrator.py - Change sandbox output location to plan-output/ directory in plan.py - Add get_context_summary stub method to acms_service.py ## Testing Run architecture review action and verify the output report is complete with all sections. Reviewed-on: #10938
214 lines
6.1 KiB
Python
214 lines
6.1 KiB
Python
"""Robot Framework helper for error recovery smoke tests.
|
|
|
|
Provides a CLI-style interface for Robot to invoke error recovery
|
|
operations. Exit code 0 = success, 1 = failure.
|
|
|
|
Usage:
|
|
python robot/helper_error_recovery.py classify
|
|
python robot/helper_error_recovery.py record-error
|
|
python robot/helper_error_recovery.py retry-policy
|
|
python robot/helper_error_recovery.py format-output
|
|
python robot/helper_error_recovery.py model-checks
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock
|
|
|
|
# Ensure the src directory is on the import path.
|
|
_SRC = str(Path(__file__).resolve().parents[1] / "src")
|
|
if _SRC not in sys.path:
|
|
sys.path.insert(0, _SRC)
|
|
|
|
from cleveragents.application.services.error_recovery_service import ( # noqa: E402
|
|
ErrorRecoveryService,
|
|
)
|
|
from cleveragents.domain.models.core.error_recovery import ( # noqa: E402
|
|
ErrorCategory,
|
|
ErrorHistory,
|
|
ErrorRecord,
|
|
RecoveryAction,
|
|
RecoveryHint,
|
|
classify_error,
|
|
)
|
|
from cleveragents.domain.models.core.plan import ( # noqa: E402
|
|
PlanPhase,
|
|
PlanTimestamps,
|
|
ProcessingState,
|
|
)
|
|
|
|
_PLAN_ID = "01ERRTEST000000000000001"
|
|
|
|
|
|
def _make_plan() -> MagicMock:
|
|
plan = MagicMock()
|
|
plan.identity.plan_id = _PLAN_ID
|
|
plan.phase = PlanPhase.EXECUTE
|
|
plan.processing_state = ProcessingState.ERRORED
|
|
plan.is_terminal = False
|
|
plan.error_message = None
|
|
plan.error_details = None
|
|
plan.timestamps = PlanTimestamps()
|
|
return plan
|
|
|
|
|
|
def _make_service(
|
|
plan: MagicMock,
|
|
threshold: float = 0.0,
|
|
max_retries: int = 3,
|
|
) -> ErrorRecoveryService:
|
|
lifecycle = MagicMock()
|
|
lifecycle.get_plan.return_value = plan
|
|
lifecycle.commit_plan = MagicMock()
|
|
return ErrorRecoveryService(
|
|
lifecycle_service=lifecycle,
|
|
auto_retry_threshold=threshold,
|
|
max_retries=max_retries,
|
|
)
|
|
|
|
|
|
def _cmd_classify() -> int:
|
|
"""Test error classification."""
|
|
assert classify_error("Connection timed out") == ErrorCategory.TRANSIENT
|
|
assert classify_error("Schema validation failed") == ErrorCategory.VALIDATION
|
|
assert classify_error("Missing configuration") == ErrorCategory.CONFIGURATION
|
|
assert classify_error("Merge conflict in file") == ErrorCategory.MERGE_CONFLICT
|
|
assert classify_error("Authentication failed") == ErrorCategory.AUTHENTICATION
|
|
assert classify_error("Model not available") == ErrorCategory.PROVIDER
|
|
assert classify_error("Unexpected internal error") == ErrorCategory.INTERNAL
|
|
assert classify_error("Something novel") == ErrorCategory.UNKNOWN
|
|
# Exception type classification
|
|
assert classify_error("fail", "RateLimitError") == ErrorCategory.TRANSIENT
|
|
print("classify-ok")
|
|
return 0
|
|
|
|
|
|
def _cmd_record_error() -> int:
|
|
"""Test error recording."""
|
|
plan = _make_plan()
|
|
svc = _make_service(plan)
|
|
record = svc.record_error(
|
|
plan_id=_PLAN_ID,
|
|
phase="execute",
|
|
message="Connection timed out after 30s",
|
|
)
|
|
assert record.category == ErrorCategory.TRANSIENT
|
|
assert record.phase == "execute"
|
|
assert len(record.recovery_hints) > 0
|
|
|
|
history = svc.get_error_history(_PLAN_ID)
|
|
assert history.total_errors == 1
|
|
|
|
print("record-error-ok")
|
|
return 0
|
|
|
|
|
|
def _cmd_retry_policy() -> int:
|
|
"""Test retry policy decisions."""
|
|
plan = _make_plan()
|
|
# Auto-retry enabled
|
|
svc = _make_service(plan, threshold=0.0)
|
|
svc.record_error(_PLAN_ID, "execute", "Connection timed out")
|
|
assert svc.should_retry(_PLAN_ID), "Expected should_retry=True"
|
|
|
|
# Auto-retry disabled (human threshold)
|
|
svc2 = _make_service(plan, threshold=1.0)
|
|
svc2.record_error(_PLAN_ID, "execute", "Connection timed out")
|
|
assert svc2.should_escalate(_PLAN_ID), "Expected should_escalate=True"
|
|
|
|
print("retry-policy-ok")
|
|
return 0
|
|
|
|
|
|
def _cmd_format_output() -> int:
|
|
"""Test error output formatting."""
|
|
plan = _make_plan()
|
|
svc = _make_service(plan)
|
|
svc.record_error(_PLAN_ID, "execute", "Provider timed out")
|
|
|
|
plain = svc.format_error_output(_PLAN_ID, fmt="plain")
|
|
assert "transient" in plain
|
|
assert "Provider timed out" in plain
|
|
|
|
json_out = svc.format_error_output(_PLAN_ID, fmt="json")
|
|
assert "plan_id" in json_out
|
|
|
|
print("format-output-ok")
|
|
return 0
|
|
|
|
|
|
def _cmd_model_checks() -> int:
|
|
"""Test model construction and enums."""
|
|
# ErrorCategory values
|
|
assert ErrorCategory.TRANSIENT == "transient"
|
|
assert ErrorCategory.VALIDATION == "validation"
|
|
assert ErrorCategory.UNKNOWN == "unknown"
|
|
|
|
# RecoveryAction values
|
|
assert RecoveryAction.RETRY == "retry"
|
|
assert RecoveryAction.CANCEL == "cancel"
|
|
|
|
# RecoveryHint construction
|
|
hint = RecoveryHint(
|
|
action=RecoveryAction.RETRY,
|
|
message="Retry the operation",
|
|
cli_command="agents plan prompt TEST",
|
|
priority=0,
|
|
)
|
|
assert hint.action == RecoveryAction.RETRY
|
|
|
|
# ErrorRecord construction and properties
|
|
record = ErrorRecord(
|
|
error_id="ERR-001",
|
|
phase="execute",
|
|
category=ErrorCategory.TRANSIENT,
|
|
message="test",
|
|
retry_count=0,
|
|
max_retries=3,
|
|
)
|
|
assert record.is_retriable
|
|
assert not record.retries_exhausted
|
|
|
|
# ErrorHistory construction
|
|
history = ErrorHistory(plan_id="TEST")
|
|
history.add(record)
|
|
assert history.total_errors == 1
|
|
assert history.latest == record
|
|
|
|
# to_cli_dict
|
|
cli_dict = record.to_cli_dict()
|
|
assert "error_id" in cli_dict
|
|
assert "category" in cli_dict
|
|
|
|
print("model-checks-ok")
|
|
return 0
|
|
|
|
|
|
def main() -> int:
|
|
if len(sys.argv) < 2:
|
|
print(
|
|
"Usage: helper_error_recovery.py "
|
|
"<classify|record-error|retry-policy|format-output|model-checks>"
|
|
)
|
|
return 1
|
|
|
|
cmd = sys.argv[1]
|
|
commands = {
|
|
"classify": _cmd_classify,
|
|
"record-error": _cmd_record_error,
|
|
"retry-policy": _cmd_retry_policy,
|
|
"format-output": _cmd_format_output,
|
|
"model-checks": _cmd_model_checks,
|
|
}
|
|
func = commands.get(cmd)
|
|
if func is None:
|
|
print(f"Unknown command: {cmd}")
|
|
return 1
|
|
return func()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|