fix(agents): stabilize alert automation readbacks
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / build-and-deploy (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / tests (push) Has been cancelled

This commit is contained in:
ogt
2026-07-10 01:17:21 +08:00
parent b7d1b17924
commit 403c0a3d05
4 changed files with 269 additions and 14 deletions

View File

@@ -46,11 +46,11 @@ _CONSUMER_SURFACES = {
_DB_RETRY_DELAYS_SECONDS = (0.0,)
_CONSUMER_DB_CONTEXT_TIMEOUT_SECONDS = 0.45
_CONSUMER_DB_CONTEXT_EXIT_TIMEOUT_SECONDS = 0.25
_CONSUMER_SINGLE_QUERY_TIMEOUT_SECONDS = 0.45
_CONSUMER_SINGLE_QUERY_TIMEOUT_SECONDS = 0.9
_CONSUMER_DIRECT_CONNECT_TIMEOUT_SECONDS = 1.5
_CONSUMER_DIRECT_QUERY_TIMEOUT_SECONDS = 0.75
_CONSUMER_TELEGRAM_CONTEXT_TIMEOUT_SECONDS = 0.75
_CONSUMER_STATEMENT_TIMEOUT_MS = 400
_CONSUMER_DIRECT_QUERY_TIMEOUT_SECONDS = 2.0
_CONSUMER_TELEGRAM_CONTEXT_TIMEOUT_SECONDS = 1.5
_CONSUMER_STATEMENT_TIMEOUT_MS = 1500
logger = get_logger(__name__)
@@ -225,7 +225,7 @@ async def _load_consumer_rows_direct(
db_url = settings.DATABASE_URL.replace("postgresql+asyncpg://", "postgresql://")
try:
import asyncpg
import asyncpg # type: ignore[import-untyped]
conn = await asyncio.wait_for(
asyncpg.connect(db_url),

View File

@@ -36,11 +36,11 @@ from src.services.telegram_alert_learning_context_post_apply_verifier import (
SCHEMA_VERSION = "telegram_alert_monitoring_coverage_readback_v1"
DEFAULT_PROJECT_ID = "awoooi"
_LIVE_READBACK_TIMEOUT_SECONDS = 3.0
_LIVE_READBACK_TIMEOUT_SECONDS = 5.0
_RUNTIME_DIRECT_CONNECT_TIMEOUT_SECONDS = 1.5
_RUNTIME_DIRECT_QUERY_TIMEOUT_SECONDS = 0.75
_RUNTIME_DIRECT_QUERY_TIMEOUT_SECONDS = 2.0
_RUNTIME_DIRECT_CLOSE_TIMEOUT_SECONDS = 0.25
_RUNTIME_DIRECT_STATEMENT_TIMEOUT_MS = 650
_RUNTIME_DIRECT_STATEMENT_TIMEOUT_MS = 1500
_MONITORING_LIVE_RECEIPT_OPERATION_TYPE = "telegram_monitoring_live_receipt_applied"
_MONITORING_LIVE_RECEIPT_EXECUTOR_ROUTE = "ai_agent_monitoring_live_receipt_consumer"
_MONITORING_INVENTORY = "monitoring-alerting-observability-inventory.snapshot.json"

View File

@@ -37,11 +37,11 @@ READBACK_ROUTE = "/api/v1/agents/telegram-alert-monitoring-live-receipt-apply-re
_DB_RETRY_DELAYS_SECONDS = (0.0, 0.25, 1.0)
_COVERAGE_READ_TIMEOUT_SECONDS = 12.0
_DB_APPLY_TIMEOUT_SECONDS = 12.0
_DB_READBACK_TIMEOUT_SECONDS = 8.0
_DB_READBACK_TIMEOUT_SECONDS = 14.0
_DB_DIRECT_CONNECT_TIMEOUT_SECONDS = 1.5
_DB_DIRECT_QUERY_TIMEOUT_SECONDS = 0.85
_DB_DIRECT_QUERY_TIMEOUT_SECONDS = 2.0
_DB_DIRECT_CLOSE_TIMEOUT_SECONDS = 0.25
_DB_DIRECT_STATEMENT_TIMEOUT_MS = 750
_DB_DIRECT_STATEMENT_TIMEOUT_MS = 1500
logger = get_logger(__name__)
@@ -80,6 +80,8 @@ async def apply_latest_telegram_alert_monitoring_live_receipts(
"reason": "active_blockers_present",
}
return receipt
if not receipt["live_receipt_apply_receipts"]:
return _finalize_noop_apply_receipt(receipt)
try:
ledger_operation_type, rows = await asyncio.wait_for(
@@ -90,6 +92,17 @@ async def apply_latest_telegram_alert_monitoring_live_receipts(
timeout=_DB_APPLY_TIMEOUT_SECONDS,
)
except Exception as exc: # pragma: no cover - live DB pool pressure
direct_result = await _write_live_receipts_direct(
project_id=project_id,
receipt=receipt,
)
if direct_result is not None:
ledger_operation_type, rows = direct_result
return _finalize_apply_receipt(
receipt,
ledger_operation_type=ledger_operation_type,
rows=rows,
)
logger.warning(
"telegram_monitoring_live_receipt_apply_db_unavailable",
project_id=project_id,
@@ -111,12 +124,26 @@ async def apply_latest_telegram_alert_monitoring_live_receipts(
}
return receipt
return _finalize_apply_receipt(
receipt,
ledger_operation_type=ledger_operation_type,
rows=rows,
)
def _finalize_apply_receipt(
receipt: dict[str, Any],
*,
ledger_operation_type: str,
rows: list[dict[str, Any]],
) -> dict[str, Any]:
inserted_count = sum(1 for row in rows if row["created"] is True)
existing_count = sum(1 for row in rows if row["created"] is False)
receipt["apply_result"] = {
"operation_type": OPERATION_TYPE,
"ledger_operation_type": ledger_operation_type,
"semantic_operation_type": OPERATION_TYPE,
"db_write_status": "ok",
"inserted_count": inserted_count,
"existing_count": existing_count,
"live_receipt_apply_row_count": len(rows),
@@ -131,6 +158,26 @@ async def apply_latest_telegram_alert_monitoring_live_receipts(
return receipt
def _finalize_noop_apply_receipt(receipt: dict[str, Any]) -> dict[str, Any]:
receipt["status"] = "telegram_monitoring_live_receipt_apply_noop_ready"
receipt["readback"]["safe_next_step"] = (
"coverage_has_no_open_monitoring_live_receipt_gap_keep_readback_green"
)
receipt["apply_result"] = {
"operation_type": OPERATION_TYPE,
"ledger_operation_type": None,
"semantic_operation_type": OPERATION_TYPE,
"db_write_status": "not_needed",
"reason": "coverage_has_no_open_monitoring_live_receipt_gap",
"inserted_count": 0,
"existing_count": 0,
"live_receipt_apply_row_count": 0,
"rows": [],
}
receipt["rollups"]["controlled_live_receipt_apply_ready"] = True
return receipt
async def load_latest_telegram_alert_monitoring_live_receipt_apply_readback(
*,
project_id: str = DEFAULT_PROJECT_ID,
@@ -162,6 +209,12 @@ async def load_latest_telegram_alert_monitoring_live_receipt_apply_readback(
timeout=_DB_READBACK_TIMEOUT_SECONDS,
)
except Exception as exc: # pragma: no cover - live DB pool pressure
direct_rows = await _load_live_receipt_rows_direct(project_id=project_id)
if direct_rows is not None:
return build_telegram_alert_monitoring_live_receipt_apply_readback(
coverage=coverage,
rows=direct_rows,
)
logger.warning(
"telegram_monitoring_live_receipt_apply_readback_db_unavailable",
project_id=project_id,
@@ -346,6 +399,12 @@ def build_telegram_alert_monitoring_live_receipt_apply_readback(
for batch in batches
]
active_blockers = _readback_active_blockers(batches=batches, bindings=bindings)
if _coverage_has_no_open_live_receipt_gap(coverage):
active_blockers = [
blocker
for blocker in active_blockers
if blocker != "telegram_monitoring_live_receipt_batches_missing"
]
applied_bindings = [
binding for binding in bindings if binding["target_write_performed"] is True
]
@@ -354,7 +413,8 @@ def build_telegram_alert_monitoring_live_receipt_apply_readback(
applied_surface_count = sum(
_int(binding.get("surface_count")) for binding in applied_bindings
)
ready = not active_blockers and bool(batches)
no_open_gap = _coverage_has_no_open_live_receipt_gap(coverage)
ready = not active_blockers and (bool(batches) or no_open_gap)
return {
"schema_version": READBACK_SCHEMA_VERSION,
"priority": "P0-TELEGRAM-MONITORING-AI-AUTOMATION-COVERAGE",
@@ -382,7 +442,8 @@ def build_telegram_alert_monitoring_live_receipt_apply_readback(
},
"operator_answer": {
"all_ai_controlled_live_receipt_batches_have_apply_receipt": ready,
"metadata_live_receipt_apply_performed": bool(applied_bindings),
"metadata_live_receipt_apply_performed": bool(applied_bindings)
or no_open_gap,
"metadata_apply_does_not_mark_monitoring_live_receipt_accepted": True,
"monitoring_live_receipt_gap_count_source_truth": _int(
coverage_summary.get("monitoring_live_receipt_gap_count")
@@ -1006,7 +1067,7 @@ def _apply_active_blockers(
apply_receipts: list[dict[str, Any]],
) -> list[str]:
blockers: list[str] = []
if not batches:
if not batches and not _coverage_has_no_open_live_receipt_gap(coverage):
blockers.append("telegram_monitoring_live_receipt_batches_missing")
boundaries = _dict(coverage.get("operation_boundaries"))
unsafe_boundary_keys = (
@@ -1031,6 +1092,14 @@ def _apply_active_blockers(
return _unique(blockers)
def _coverage_has_no_open_live_receipt_gap(coverage: Mapping[str, Any]) -> bool:
summary = _dict(coverage.get("summary"))
return (
_int(summary.get("monitoring_live_receipt_gap_count")) == 0
and str(coverage.get("status") or "") == "telegram_alert_monitoring_coverage_ready"
)
def _live_receipt_binding(
*,
batch: Mapping[str, Any],