test_redaction.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. from __future__ import annotations
  2. import json
  3. from pathlib import Path
  4. from production_build_agents.observability.redaction import (
  5. summarize_global_data_delivery,
  6. summarize_global_data_state,
  7. summarize_run_metrics,
  8. )
  9. def test_state_summary_omits_raw_content_urls_paths_and_errors() -> None:
  10. secret_path = "/Users/private/客户标题_production_final.json"
  11. state = {
  12. "run_id": "global-data-safe-1",
  13. "protocol_version": "0.3",
  14. "input_path": secret_path,
  15. "input_sha256": "a" * 64,
  16. "output_dir": "/Users/private/output",
  17. "status": "FAILED",
  18. "phase": "VALIDATE_TASK",
  19. "failure_code": "VALIDATOR_OUTPUT_INVALID",
  20. "error": "客户正文 https://secret.example/token?sig=secret",
  21. "plan_history": {"1": {"json_uri": secret_path}},
  22. "task_records": {
  23. "Task1": {
  24. "task_id": "Task1",
  25. "status": "failed",
  26. "active_plan_version": 1,
  27. "executor_delivery_path": secret_path,
  28. "validation_report_path": secret_path,
  29. }
  30. },
  31. "event_log": ["客户正文"],
  32. "planner_calls": 1,
  33. }
  34. summary = summarize_global_data_state(state)
  35. encoded = json.dumps(summary, ensure_ascii=False)
  36. assert summary["error_present"] is True
  37. assert summary["tasks"][0]["task_id"] == "Task1"
  38. assert summary["records_present"]["global_data_plan"] is False
  39. assert "/Users/private" not in encoded
  40. assert "客户正文" not in encoded
  41. assert "secret.example" not in encoded
  42. assert "sig=secret" not in encoded
  43. def test_delivery_summary_keeps_artifact_identity_but_omits_locations(
  44. tmp_path: Path,
  45. ) -> None:
  46. path = tmp_path / "global_data_stage_delivery.json"
  47. path.write_text(
  48. json.dumps(
  49. {
  50. "schema_version": "0.3",
  51. "run_id": "safe-run",
  52. "plan_id": "safe-plan",
  53. "final_plan_version": 2,
  54. "active_artifacts": [
  55. {
  56. "artifact_id": "Artifact-1",
  57. "artifact_type": "image",
  58. "mime_type": "image/png",
  59. "size_bytes": 123,
  60. "content_sha256": "b" * 64,
  61. "description": "客户人物和真实文案",
  62. "source_uri": "https://secret.example/source.png",
  63. "uri": "/private/output.png",
  64. }
  65. ],
  66. "requirement_evaluations": [
  67. {
  68. "expectation_evaluations": [
  69. {"satisfied": True}
  70. ]
  71. }
  72. ],
  73. },
  74. ensure_ascii=False,
  75. ),
  76. encoding="utf-8",
  77. )
  78. summary = summarize_global_data_delivery(str(path))
  79. encoded = json.dumps(summary, ensure_ascii=False)
  80. assert summary is not None
  81. assert summary["artifact_count"] == 1
  82. assert summary["artifacts"][0]["artifact_id"] == "Artifact-1"
  83. assert summary["satisfied_requirement_count"] == 1
  84. assert "客户人物" not in encoded
  85. assert "secret.example" not in encoded
  86. assert "/private/output.png" not in encoded
  87. def test_run_metrics_summary_omits_internal_event_ids_and_details(
  88. tmp_path: Path,
  89. ) -> None:
  90. (tmp_path / "run_metrics.json").write_text(
  91. json.dumps(
  92. {
  93. "model_totals": {
  94. "calls": 2,
  95. "input_tokens": 10,
  96. "output_tokens": 5,
  97. "total_tokens": 15,
  98. "reported_cost_usd": 0.1,
  99. "cost_status": "reported",
  100. },
  101. "tools": {
  102. "calls": 1,
  103. "successful_calls": 1,
  104. "failed_calls": 0,
  105. "replayed_calls": 0,
  106. "total_duration_ms": 20,
  107. "by_name": {
  108. "inspect_tool": {
  109. "calls": 1,
  110. "successful_calls": 1,
  111. "failed_calls": 0,
  112. "replayed_calls": 0,
  113. "total_duration_ms": 20,
  114. }
  115. },
  116. },
  117. "phases": {
  118. "execute_task": {
  119. "calls": 1,
  120. "total_duration_ms": 50,
  121. "last_duration_ms": 50,
  122. "last_outcome": "RUNNING",
  123. }
  124. },
  125. "corrections": [{"secret": "raw details"}],
  126. "replans": [{"details": "raw failure text"}],
  127. "_seen_events": ["private-event-id"],
  128. }
  129. ),
  130. encoding="utf-8",
  131. )
  132. summary = summarize_run_metrics(str(tmp_path))
  133. encoded = json.dumps(summary, ensure_ascii=False)
  134. assert summary is not None
  135. assert summary["model_totals"]["total_tokens"] == 15
  136. assert summary["tools_by_name"]["inspect_tool"]["calls"] == 1
  137. assert summary["correction_count"] == 1
  138. assert "raw details" not in encoded
  139. assert "raw failure text" not in encoded
  140. assert "private-event-id" not in encoded