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
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:
@@ -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),
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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],
|
||||
|
||||
Reference in New Issue
Block a user