顯示 PChome controlled apply closeout 狀態
Some checks failed
CD Pipeline / deploy (push) Has been cancelled
Some checks failed
CD Pipeline / deploy (push) Has been cancelled
This commit is contained in:
@@ -2522,11 +2522,15 @@ def api_pchome_growth_ai_automation_readiness():
|
||||
try:
|
||||
from config import DATABASE_PATH
|
||||
from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
|
||||
from services.pchome_mapping_backlog_service import build_pchome_growth_ai_automation_readiness
|
||||
from services.pchome_mapping_backlog_service import (
|
||||
build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_receipt_replay_package,
|
||||
build_pchome_growth_ai_automation_readiness,
|
||||
)
|
||||
|
||||
force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
|
||||
execute_search = str(request.args.get('execute_search') or '').strip().lower() in {'1', 'true', 'yes'}
|
||||
execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
|
||||
include_receipt_replay = str(request.args.get('include_receipt_replay', 'true') or '').strip().lower() in {'1', 'true', 'yes'}
|
||||
limit = request.args.get('limit', 20, type=int)
|
||||
batch_size = request.args.get('batch_size', 8, type=int)
|
||||
limit = max(5, min(limit, 50))
|
||||
@@ -2544,11 +2548,23 @@ def api_pchome_growth_ai_automation_readiness():
|
||||
payload["cache_state"] = "fresh"
|
||||
_set_pchome_growth_cache(payload)
|
||||
|
||||
receipt_replay = None
|
||||
if include_receipt_replay:
|
||||
replay_engine = _create_icaim_dashboard_engine(DATABASE_PATH)
|
||||
try:
|
||||
receipt_replay = build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_receipt_replay_package(
|
||||
materialize_artifacts=False,
|
||||
engine=replay_engine,
|
||||
)
|
||||
finally:
|
||||
replay_engine.dispose()
|
||||
|
||||
readiness = build_pchome_growth_ai_automation_readiness(
|
||||
payload,
|
||||
batch_size=batch_size,
|
||||
execute_search=execute_search,
|
||||
execute_fetch=execute_fetch,
|
||||
controlled_apply_receipt_replay=receipt_replay,
|
||||
)
|
||||
readiness["source_endpoint"] = "/api/ai/pchome-growth/opportunities"
|
||||
return jsonify(readiness)
|
||||
|
||||
@@ -5100,6 +5100,7 @@ def build_pchome_growth_ai_automation_readiness(
|
||||
execute_search: bool = False,
|
||||
execute_fetch: bool = False,
|
||||
search_func: Any = None,
|
||||
controlled_apply_receipt_replay: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Build a single read-only product-facing AI automation readiness view."""
|
||||
mapping_summary = summarize_pchome_mapping_backlog(payload)
|
||||
@@ -5149,6 +5150,16 @@ def build_pchome_growth_ai_automation_readiness(
|
||||
waiting_candidate_count = selected_search_targets if not candidate_decision_count else 0
|
||||
receipt_count = int(receipt_summary.get("receipt_count") or 0)
|
||||
ready_receipt_count = int(receipt_summary.get("ready_for_auto_persistence_count") or 0)
|
||||
receipt_replay_summary = (controlled_apply_receipt_replay or {}).get("summary") or {}
|
||||
receipt_replay_selector_count = int(receipt_replay_summary.get("target_selector_count") or 0)
|
||||
receipt_replay_readback_pass_count = int(receipt_replay_summary.get("post_apply_readback_pass_count") or 0)
|
||||
receipt_replay_materialized_count = int(receipt_replay_summary.get("executor_receipt_materialized_count") or 0)
|
||||
receipt_replay_hash_match_count = int(receipt_replay_summary.get("executor_receipt_hash_match_count") or 0)
|
||||
controlled_apply_closeout_verified = (
|
||||
bool(receipt_replay_selector_count)
|
||||
and receipt_replay_readback_pass_count == receipt_replay_selector_count
|
||||
and int(receipt_replay_summary.get("executor_receipt_ready_count") or 0) > 0
|
||||
)
|
||||
exception_count = _summary_exception_count(receipt_summary) + int(
|
||||
decision_summary.get("machine_review_decision_count") or 0
|
||||
)
|
||||
@@ -5175,7 +5186,9 @@ def build_pchome_growth_ai_automation_readiness(
|
||||
"writes_database": False,
|
||||
}
|
||||
|
||||
if not direct_mapping_count and ready_receipt_count:
|
||||
if controlled_apply_closeout_verified:
|
||||
result = "AI_AUTOMATION_CONTROLLED_APPLY_CLOSEOUT_VERIFIED"
|
||||
elif not direct_mapping_count and ready_receipt_count:
|
||||
result = "AI_AUTOMATION_READY_FOR_CONTROLLED_APPLY"
|
||||
elif exception_resolution_closeout_receipt_count:
|
||||
result = "AI_AUTOMATION_EXCEPTION_RESOLUTION_CLOSEOUT_READY"
|
||||
@@ -5242,10 +5255,21 @@ def build_pchome_growth_ai_automation_readiness(
|
||||
_automation_lane(
|
||||
"controlled_apply",
|
||||
"受控落地",
|
||||
"blocked_until_verifier",
|
||||
0,
|
||||
"等待 verifier、rollback、readback",
|
||||
"P1-P4 穩定後才進 P5/P6",
|
||||
"completed" if controlled_apply_closeout_verified else "blocked_until_verifier",
|
||||
receipt_replay_selector_count,
|
||||
(
|
||||
f"readback {receipt_replay_readback_pass_count}/{receipt_replay_selector_count}"
|
||||
f" · receipt {receipt_replay_materialized_count}"
|
||||
) if controlled_apply_closeout_verified else "等待 verifier、rollback、readback",
|
||||
"維持 receipt replay / drift verifier" if controlled_apply_closeout_verified else "P1-P4 穩定後才進 P5/P6",
|
||||
),
|
||||
_automation_lane(
|
||||
"controlled_apply_receipt_replay",
|
||||
"落地收據重放",
|
||||
"completed" if controlled_apply_closeout_verified else "waiting",
|
||||
receipt_replay_materialized_count,
|
||||
f"hash match {receipt_replay_hash_match_count}",
|
||||
"從 artifact + DB readback 自動證明 apply 已收斂",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -5284,6 +5308,11 @@ def build_pchome_growth_ai_automation_readiness(
|
||||
),
|
||||
"receipt_count": receipt_count,
|
||||
"ready_receipt_count": ready_receipt_count,
|
||||
"controlled_apply_replay_selector_count": receipt_replay_selector_count,
|
||||
"controlled_apply_replay_readback_pass_count": receipt_replay_readback_pass_count,
|
||||
"controlled_apply_receipt_materialized_count": receipt_replay_materialized_count,
|
||||
"controlled_apply_receipt_hash_match_count": receipt_replay_hash_match_count,
|
||||
"controlled_apply_closeout_verified_count": 1 if controlled_apply_closeout_verified else 0,
|
||||
"exception_count": exception_count,
|
||||
"ai_exception_count": exception_count,
|
||||
AI_EXCEPTION_REQUIRED_COUNT_KEY: exception_count,
|
||||
@@ -5299,6 +5328,7 @@ def build_pchome_growth_ai_automation_readiness(
|
||||
PRIMARY_HUMAN_GATE_COUNT_KEY: 0,
|
||||
"exception_resolution": "ai_machine_verifiable",
|
||||
"machine_verifiable_decision_required": True,
|
||||
"controlled_apply_closeout": "receipt_replay_machine_verified" if controlled_apply_closeout_verified else "waiting_for_verifier",
|
||||
},
|
||||
"ai_exception_auto_resolution": ai_exception_auto_resolution,
|
||||
"manual_policy": {
|
||||
@@ -5321,6 +5351,7 @@ def build_pchome_growth_ai_automation_readiness(
|
||||
"dispatches_telegram": False,
|
||||
"llm_calls_in_preview": False,
|
||||
"gemini_allowed": False,
|
||||
"reads_database_for_receipt_replay": bool(controlled_apply_receipt_replay),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1434,6 +1434,40 @@ def test_ai_automation_readiness_makes_automation_visible_without_manual_primary
|
||||
assert readiness["safety"]["llm_calls_in_preview"] is False
|
||||
|
||||
|
||||
def test_ai_automation_readiness_surfaces_controlled_apply_receipt_replay_closeout():
|
||||
readiness = build_pchome_growth_ai_automation_readiness(
|
||||
_payload(),
|
||||
batch_size=1,
|
||||
controlled_apply_receipt_replay={
|
||||
"result": "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_RECEIPT_REPLAYED",
|
||||
"summary": {
|
||||
"target_selector_count": 4,
|
||||
"post_apply_readback_pass_count": 4,
|
||||
"executor_receipt_ready_count": 1,
|
||||
"executor_receipt_materialized_count": 1,
|
||||
"executor_receipt_hash_match_count": 1,
|
||||
},
|
||||
"safety": {
|
||||
"writes_database": False,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
lanes = {lane["key"]: lane for lane in readiness["automation_lanes"]}
|
||||
assert readiness["result"] == "AI_AUTOMATION_CONTROLLED_APPLY_CLOSEOUT_VERIFIED"
|
||||
assert readiness["summary"]["controlled_apply_replay_selector_count"] == 4
|
||||
assert readiness["summary"]["controlled_apply_replay_readback_pass_count"] == 4
|
||||
assert readiness["summary"]["controlled_apply_receipt_materialized_count"] == 1
|
||||
assert readiness["summary"]["controlled_apply_closeout_verified_count"] == 1
|
||||
assert readiness["automation_policy"]["controlled_apply_closeout"] == "receipt_replay_machine_verified"
|
||||
assert lanes["controlled_apply"]["status"] == "completed"
|
||||
assert lanes["controlled_apply"]["value"] == 4
|
||||
assert lanes["controlled_apply_receipt_replay"]["status"] == "completed"
|
||||
assert lanes["controlled_apply_receipt_replay"]["value"] == 1
|
||||
assert readiness["safety"]["reads_database_for_receipt_replay"] is True
|
||||
assert readiness["safety"]["writes_database"] is False
|
||||
|
||||
|
||||
def test_ai_automation_readiness_reports_candidate_decisions_after_controlled_search():
|
||||
call_count = {"search": 0}
|
||||
|
||||
@@ -16014,7 +16048,7 @@ def test_ai_automation_readiness_route_defaults_to_no_search_and_uses_cached_pay
|
||||
monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
|
||||
|
||||
app = Flask(__name__)
|
||||
with app.test_request_context("/api/ai/pchome-growth/ai-automation-readiness?batch_size=1"):
|
||||
with app.test_request_context("/api/ai/pchome-growth/ai-automation-readiness?batch_size=1&include_receipt_replay=false"):
|
||||
response = routes.api_pchome_growth_ai_automation_readiness.__wrapped__()
|
||||
|
||||
payload = response.get_json()
|
||||
|
||||
Reference in New Issue
Block a user