muse-agent-example/tests/architecture/test_framework_protocol.py

122 lines
4.6 KiB
Python

"""框架协议与运行工件的离线合同测试。"""
from __future__ import annotations
import json
import pathlib
import sys
import tempfile
import unittest
from jsonschema import Draft202012Validator
ROOT = next(
parent
for parent in (pathlib.Path(__file__).resolve().parent, *pathlib.Path(__file__).resolve().parents)
if (parent / "AGENTS.md").is_file() and (parent / ".git").exists()
)
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from framework.adapters.pi.normalization import normalize_pi_transcript
from framework.primitives.artifacts import (
ArtifactError,
detect_seq_gaps,
read_jsonl,
write_jsonl_atomic,
)
from framework.primitives.execution import (
FrameworkEvent,
FrameworkExecutionRequest,
FrameworkExecutionResult,
)
SCHEMA_DIR = ROOT / "framework" / "primitives" / "schemas"
class FrameworkProtocolTest(unittest.TestCase):
def test_schemas_are_valid_draft_2020_documents(self) -> None:
for path in sorted(SCHEMA_DIR.glob("*.schema.json")):
with self.subTest(path=path.name):
document = json.loads(path.read_text(encoding="utf-8"))
Draft202012Validator.check_schema(document)
def test_request_has_no_business_state_fields(self) -> None:
request = FrameworkExecutionRequest(
system_prompt="system",
user_content="user",
tool_allowlist=("read",),
)
self.assertEqual(request.session_mode, "fresh")
self.assertFalse(hasattr(request, "work_id"))
self.assertFalse(hasattr(request, "candidate_status"))
request_schema = json.loads((SCHEMA_DIR / "framework_execution_request.schema.json").read_text(encoding="utf-8"))
Draft202012Validator(request_schema).validate(request.as_dict())
event = FrameworkEvent(
framework="pi",
framework_version="test",
session_id="s1",
source_seq=1,
kind="agent",
phase="completed",
observed_at="2026-08-24T00:00:00Z",
)
event_schema = json.loads((SCHEMA_DIR / "framework_event.schema.json").read_text(encoding="utf-8"))
Draft202012Validator(event_schema).validate(event.as_dict())
result = FrameworkExecutionResult(
status="completed", final_text="{}", requested_model="p/m"
)
result_schema = json.loads((SCHEMA_DIR / "framework_execution_result.schema.json").read_text(encoding="utf-8"))
Draft202012Validator(result_schema).validate(result.as_dict())
def test_pi_transcript_produces_common_events_and_preserves_unknown_type(self) -> None:
with tempfile.TemporaryDirectory() as directory:
source = pathlib.Path(directory) / "pi.jsonl"
target = pathlib.Path(directory) / "framework-events.jsonl"
source.write_text(
json.dumps({"type": "session", "id": "s1"}) + "\n"
+ json.dumps({"type": "future_event", "payload": "not copied"}) + "\n",
encoding="utf-8",
)
result = normalize_pi_transcript(source, target, run_id="run-1", framework_version="test")
self.assertEqual(result["unknownEventTypes"], ["future_event"])
normalized = read_jsonl(target)
self.assertEqual([event["sourceSeq"] for event in normalized], [1, 2])
self.assertEqual(normalized[1]["kind"], "unknown")
self.assertNotIn("payload", normalized[1]["safeDetails"])
def test_artifact_flush_replay_and_gap_detection(self) -> None:
events = [
{
"framework": "pi",
"frameworkVersion": "test",
"sessionId": "s1",
"sourceSeq": 1,
"kind": "session",
"phase": "started",
"safeDetails": {},
},
{
"framework": "pi",
"frameworkVersion": "test",
"sessionId": "s1",
"sourceSeq": 2,
"kind": "agent",
"phase": "completed",
"safeDetails": {},
},
]
with tempfile.TemporaryDirectory() as directory:
path = pathlib.Path(directory) / "transcript.jsonl"
digest = write_jsonl_atomic(path, events)
self.assertTrue(digest.startswith("sha256:"))
self.assertEqual(read_jsonl(path), events)
self.assertEqual(detect_seq_gaps([events[0], {**events[1], "sourceSeq": 4}]), [(2, 3)])
with self.assertRaises(ArtifactError):
write_jsonl_atomic(path, [{**events[0], "sourceSeq": 2}])
if __name__ == "__main__":
unittest.main()