| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- from __future__ import annotations
- import tempfile
- import unittest
- from pathlib import Path
- from production_build_agents.run.segment_dependencies import (
- SegmentDependencyIntegrityError,
- load_verified_segment_dependencies,
- )
- from production_build_agents.run.segment_inputs import (
- load_verified_segment_inputs,
- )
- from production_build_agents.agents.segment.executor import _user_payload
- from production_build_agents.agents.segment.provenance import (
- normalize_candidate_lineage,
- )
- from production_build_agents.contracts.models import ToolCallRecord
- from production_build_agents.contracts.production_evaluation import (
- evaluate_segment_delivery,
- )
- from production_build_agents.contracts.document_identity import (
- exact_document_sha256,
- )
- from production_build_agents.contracts.production_execution_models import (
- ImmutableJsonRef,
- SegmentDependencyRef,
- SegmentPackage,
- )
- from production_build_agents.run.artifacts import (
- content_identity_for_path,
- file_sha256,
- seal_shared_visual_anchor,
- )
- from tests.support.production_fixtures import (
- output_profile,
- segment_bundle,
- )
- from tests.support.production_planner_fixtures import (
- build_production_planner_scenario,
- )
- def _ref(path: Path) -> ImmutableJsonRef:
- return ImmutableJsonRef(
- uri=str(path.resolve()),
- document_sha256=exact_document_sha256(path.read_bytes()),
- )
- class SegmentDependencyTest(unittest.TestCase):
- def _records(self, root: Path):
- (
- source_package,
- _source_candidate,
- source_delivery,
- _validator_candidate,
- source_report,
- _accepted,
- ) = segment_bundle(1)
- package_path = root / "upstream-package.json"
- package_path.write_text(
- source_package.model_dump_json(),
- encoding="utf-8",
- )
- sealed_artifacts = []
- for artifact in source_delivery.artifacts:
- path = root / f"{artifact.artifact_id}.bin"
- path.write_bytes(artifact.artifact_id.encode())
- sealed_artifacts.append(
- artifact.model_copy(
- update={
- "uri": str(path.resolve()),
- "source_uri": None,
- **content_identity_for_path(path),
- }
- )
- )
- source_delivery = source_delivery.model_copy(
- update={
- "segment_package_uri": str(package_path.resolve()),
- "segment_package_sha256": file_sha256(package_path),
- "artifacts": sealed_artifacts,
- }
- )
- delivery_path = root / "upstream-delivery.json"
- report_path = root / "upstream-report.json"
- delivery_path.write_text(
- source_delivery.model_dump_json(),
- encoding="utf-8",
- )
- report_path.write_text(
- source_report.model_dump_json(),
- encoding="utf-8",
- )
- dependency = SegmentDependencyRef(
- segment_id="Segment1",
- segment_delivery_ref=_ref(delivery_path),
- validation_report_ref=_ref(report_path),
- )
- (
- downstream_base,
- downstream_candidate,
- downstream_delivery,
- _validator_candidate,
- _report,
- _accepted,
- ) = segment_bundle(2)
- downstream = downstream_base.model_copy(
- update={"dependencies": [dependency]}
- )
- return (
- downstream,
- downstream_candidate,
- downstream_delivery,
- source_delivery,
- report_path,
- )
- def test_exact_refs_pass_report_and_full_agent_payload(self) -> None:
- with tempfile.TemporaryDirectory() as directory:
- root = Path(directory)
- package, _candidate, _delivery, source, _report_path = (
- self._records(root)
- )
- verified = load_verified_segment_dependencies(package)
- self.assertEqual(len(verified), 1)
- sealed_package_artifacts = []
- for artifact in package.artifacts:
- path = root / f"selected-{artifact.artifact_id}.bin"
- path.write_bytes(artifact.artifact_id.encode())
- sealed_package_artifacts.append(
- artifact.model_copy(
- update={
- "uri": str(path.resolve()),
- "source_uri": None,
- **content_identity_for_path(path),
- }
- )
- )
- global_delivery = build_production_planner_scenario(
- run_id="dependency-global"
- ).global_data_delivery.model_copy(
- update={"active_artifacts": sealed_package_artifacts}
- )
- global_delivery_path = root / "global-delivery.json"
- global_delivery_path.write_text(
- global_delivery.model_dump_json(),
- encoding="utf-8",
- )
- anchor_path = root / "shared-anchor.png"
- anchor_path.write_bytes(b"shared-anchor")
- package = package.model_copy(
- update={
- "artifacts": sealed_package_artifacts,
- "global_data_delivery_ref": _ref(global_delivery_path),
- "shared_visual_anchor": seal_shared_visual_anchor(
- anchor_path
- ),
- }
- )
- verified_inputs = load_verified_segment_inputs(package)
- payload = _user_payload(
- package,
- executor_run_id="run:Segment2:v1:executor",
- verified_inputs=verified_inputs,
- )
- dependency_payload = payload[
- "verified_segment_inputs"
- ]["accepted_segments"][0]
- self.assertEqual(
- [
- item["artifact_id"]
- for item in dependency_payload["artifacts"]
- ],
- [item.artifact_id for item in source.artifacts],
- )
- self.assertTrue(
- all(
- item["uri"] == source.artifacts[index].uri
- for index, item in enumerate(
- dependency_payload["artifacts"]
- )
- )
- )
- self.assertTrue(
- all(
- "source_uri" not in item
- for item in dependency_payload["artifacts"]
- )
- )
- self.assertNotIn("tool_calls", dependency_payload)
- def test_tampered_or_failed_report_is_rejected_before_execution(
- self,
- ) -> None:
- with tempfile.TemporaryDirectory() as directory:
- root = Path(directory)
- package, _candidate, _delivery, _source, report_path = (
- self._records(root)
- )
- report_path.write_text("{}", encoding="utf-8")
- with self.assertRaisesRegex(
- SegmentDependencyIntegrityError,
- "精确字节已变化",
- ):
- load_verified_segment_dependencies(package)
- def test_accepted_artifact_uri_cannot_conflict_with_global_input(
- self,
- ) -> None:
- with tempfile.TemporaryDirectory() as directory:
- root = Path(directory)
- package, _candidate, _delivery, source, _report_path = (
- self._records(root)
- )
- package = package.model_copy(
- update={
- "artifacts": [
- package.artifacts[0].model_copy(
- update={"uri": source.artifacts[0].uri}
- )
- ]
- }
- )
- with self.assertRaisesRegex(
- SegmentDependencyIntegrityError,
- "ID 和 URI 必须全局无歧义",
- ):
- load_verified_segment_dependencies(package)
- def test_failed_dependency_report_is_rejected_before_execution(
- self,
- ) -> None:
- with tempfile.TemporaryDirectory() as directory:
- root = Path(directory)
- package, _candidate, _delivery, _source, report_path = (
- self._records(root)
- )
- report = package.dependencies[0].validation_report_ref
- from production_build_agents.contracts.production_models import (
- SegmentValidationReport,
- )
- failed = SegmentValidationReport.model_validate_json(
- report_path.read_bytes()
- ).model_copy(
- update={
- "criterion_results": [
- SegmentValidationReport.model_validate_json(
- report_path.read_bytes()
- ).criterion_results[0].model_copy(
- update={"verdict": "FAIL"}
- ),
- *SegmentValidationReport.model_validate_json(
- report_path.read_bytes()
- ).criterion_results[1:],
- ],
- "verdict": "FAIL",
- }
- )
- report_path.write_text(
- failed.model_dump_json(),
- encoding="utf-8",
- )
- package = package.model_copy(
- update={
- "dependencies": [
- package.dependencies[0].model_copy(
- update={
- "validation_report_ref": _ref(report_path)
- }
- )
- ]
- }
- )
- with self.assertRaisesRegex(
- SegmentDependencyIntegrityError,
- "不是已验收 PASS",
- ):
- load_verified_segment_dependencies(package)
- def test_dependency_media_is_an_allowed_formal_lineage_input(
- self,
- ) -> None:
- with tempfile.TemporaryDirectory() as directory:
- root = Path(directory)
- package, candidate, delivery, source, _report_path = (
- self._records(root)
- )
- upstream = source.artifacts[-1]
- shot_uri = candidate.shot_outputs[0].artifact_uri
- candidate = candidate.model_copy(
- update={
- "lineage_claims": [
- (
- claim.model_copy(
- update={
- "input_artifact_uris": [
- *claim.input_artifact_uris,
- upstream.uri,
- ]
- }
- )
- if claim.artifact_uri == shot_uri
- else claim
- )
- for claim in candidate.lineage_claims
- ]
- }
- )
- shot_id = delivery.shot_outputs[0].artifact_id
- delivery = delivery.model_copy(
- update={
- "artifact_graph": delivery.artifact_graph.model_copy(
- update={
- "lineages": [
- (
- lineage.model_copy(
- update={
- "input_artifact_ids": [
- *lineage.input_artifact_ids,
- upstream.artifact_id,
- ]
- }
- )
- if lineage.artifact_id == shot_id
- else lineage
- )
- for lineage in (
- delivery.artifact_graph.lineages
- )
- ]
- }
- )
- }
- )
- without_context = {
- issue.code
- for issue in evaluate_segment_delivery(
- package,
- candidate,
- delivery,
- )
- }
- self.assertIn(
- "unknown_lineage_artifact_uri",
- without_context,
- )
- verified = load_verified_segment_dependencies(package)
- self.assertEqual(
- evaluate_segment_delivery(
- package,
- candidate,
- delivery,
- dependency_artifacts=tuple(
- artifact
- for dependency in verified
- for artifact in dependency.delivery.artifacts
- ),
- ),
- [],
- )
- def test_tool_argument_is_normalized_to_dependency_lineage(self) -> None:
- with tempfile.TemporaryDirectory() as directory:
- root = Path(directory)
- package, candidate, _delivery, source, _report_path = (
- self._records(root)
- )
- verified = load_verified_segment_dependencies(package)
- upstream = source.artifacts[-1]
- shot_uri = candidate.shot_outputs[0].artifact_uri
- narration_uri = candidate.narration_artifact_uri
- subtitle_uri = candidate.subtitle_artifact_uri
- primary_uri = candidate.primary_artifact_uri
- records = [
- ToolCallRecord(
- tool_call_id="make-shot",
- tool_name="make_video",
- arguments={
- "source": package.artifacts[0].uri,
- "anchor": package.shared_visual_anchor.uri,
- "previous_segment": upstream.uri,
- },
- success=True,
- output_refs=[shot_uri],
- ),
- ToolCallRecord(
- tool_call_id="make-narration",
- tool_name="make_audio",
- arguments={"source": package.artifacts[0].uri},
- success=True,
- output_refs=[narration_uri],
- ),
- ToolCallRecord(
- tool_call_id="make-subtitle",
- tool_name="make_subtitle",
- arguments={"audio": narration_uri},
- success=True,
- output_refs=[subtitle_uri],
- ),
- ToolCallRecord(
- tool_call_id="make-final",
- tool_name="make_final",
- arguments={
- "video": shot_uri,
- "audio": narration_uri,
- "subtitle": subtitle_uri,
- },
- success=True,
- output_refs=[primary_uri],
- ),
- ]
- normalized = normalize_candidate_lineage(
- candidate,
- package=package,
- records=records,
- dependency_artifacts=tuple(
- artifact
- for dependency in verified
- for artifact in dependency.delivery.artifacts
- ),
- )
- shot_claim = next(
- item
- for item in normalized.lineage_claims
- if item.artifact_uri == shot_uri
- )
- self.assertIn(upstream.uri, shot_claim.input_artifact_uris)
- if __name__ == "__main__":
- unittest.main()
|