修復 preflight gate 與 OCLearn queue 時區
All checks were successful
CD Pipeline / deploy (push) Successful in 1m6s

This commit is contained in:
OoO
2026-05-19 14:28:08 +08:00
parent 026d11b295
commit fd34e9e97a
3 changed files with 35 additions and 24 deletions

View File

@@ -327,6 +327,7 @@ def build_candidate_queue_review_decision_writer_preflight(
)
payload_ready = bool(statement_payloads and not invalid_payloads)
writer_safe = _writer_status_safe(writer_status)
writer_status_loaded = bool(writer_status)
writer_implementation_enabled = bool(
writer_status.get("writer_implementation_enabled")
)
@@ -340,11 +341,12 @@ def build_candidate_queue_review_decision_writer_preflight(
and not operator_summary["approval_token_submitted_to_api"]
and not apply_real_write
)
mode = (
"candidate_queue_review_decision_writer_preflight_read_only"
if read_only_probe_loaded
else "candidate_queue_review_decision_writer_preflight_preview"
)
if read_only_probe_loaded:
mode = "candidate_queue_review_decision_writer_preflight_read_only"
elif writer_status_loaded:
mode = "candidate_queue_review_decision_writer_preflight_preview"
else:
mode = "candidate_queue_review_decision_writer_preflight_planned"
gates = [
{
"key": "writer_status_is_blocked_cli_gate",
@@ -373,8 +375,8 @@ def build_candidate_queue_review_decision_writer_preflight(
},
{
"key": "preflight_execute_not_requested_from_api",
"label": "API preflight 不執行任何 shell/DB 動作",
"passed": not execute_requested,
"label": "API preflight 不執行 shell/DB 動作;只讀 probe 需注入 engine",
"passed": bool(not execute_requested or engine is not None),
},
{
"key": "preflight_apply_real_write_not_requested_from_api",
@@ -394,7 +396,7 @@ def build_candidate_queue_review_decision_writer_preflight(
{
"key": "review_decision_writer_implementation_enabled",
"label": "review_state writer 實作尚未啟用,本階段只做 preflight",
"passed": writer_implementation_enabled,
"passed": bool(not writer_status_loaded or writer_implementation_enabled),
},
]
blocked_reasons = [gate["key"] for gate in gates if not gate["passed"]]

View File

@@ -117,7 +117,10 @@ def build_deployment_readiness_preview(
candidate_queue_review_decision_approval = build_candidate_queue_review_decision_approval(review_decision=candidate_queue_review_decision)
candidate_queue_review_decision_transaction = build_candidate_queue_review_decision_transaction(decision_approval=candidate_queue_review_decision_approval)
candidate_queue_review_decision_writer_status = build_candidate_queue_review_decision_writer_cli_plan(transaction_preview=candidate_queue_review_decision_transaction)
candidate_queue_review_decision_writer_preflight = build_candidate_queue_review_decision_writer_preflight(writer_status=candidate_queue_review_decision_writer_status, transaction_preview=candidate_queue_review_decision_transaction)
candidate_queue_review_decision_writer_preflight = build_candidate_queue_review_decision_writer_preflight(
writer_status=candidate_queue_review_decision_writer_status,
transaction_preview=candidate_queue_review_decision_transaction,
)
checks = {
"schema_smoke_passed": bool(schema_smoke["passed"]),
"feature_flags_default_safe": bool(
@@ -356,9 +359,9 @@ def build_deployment_readiness_preview(
candidate_queue_review_decision_transaction,
"candidate_queue_review_decision_transaction_preview",
),
"candidate_queue_review_decision_writer_preflight_safe": _run_review_preview_safe(
"candidate_queue_review_decision_writer_preflight_planned_safe": _run_review_preview_safe(
candidate_queue_review_decision_writer_preflight,
"candidate_queue_review_decision_writer_preflight_preview",
"candidate_queue_review_decision_writer_preflight_planned",
),
"candidate_queue_review_decision_writer_cli_status_safe": _run_review_preview_safe(
candidate_queue_review_decision_writer_status,

View File

@@ -2,7 +2,7 @@ import json
import math
import threading
import time
from datetime import datetime, timezone
from datetime import datetime, timedelta, timezone
from sqlalchemy import text
from services.logger_manager import SystemLogger
from database.manager import get_session
@@ -23,6 +23,12 @@ EMBED_BATCH_SIZE = 10 # 單次最多處理筆數
EMBED_MAX_ATTEMPTS = 5 # 超過則標記 failed
DECAY_RATE = 0.01 # ADR-005半衰期約 70 天
_EMBEDDING_TARGET_TABLES = {"ai_insights", "learning_episodes"}
TAIPEI_TZ = timezone(timedelta(hours=8))
def _now_taipei_naive() -> datetime:
"""DB DateTime 欄位統一存台北時間 naive避免 UTC stale 判斷誤判。"""
return datetime.now(TAIPEI_TZ).replace(tzinfo=None)
def _enqueue_embedding(target_table: str, target_id: int, text_content: str,
@@ -45,7 +51,7 @@ def _enqueue_embedding(target_table: str, target_id: int, text_content: str,
"i": target_id,
"txt": text_content,
"m": model,
"now": datetime.now(),
"now": _now_taipei_naive(),
},
)
session.commit()
@@ -109,7 +115,7 @@ def _process_one_embedding(row_id: int, target_table: str, target_id: int,
try:
session.execute(
text("UPDATE embedding_retry_queue SET status='processing', updated_at=:now WHERE id=:id"),
{"now": datetime.now(), "id": row_id},
{"now": _now_taipei_naive(), "id": row_id},
)
session.commit()
@@ -139,7 +145,7 @@ def _process_one_embedding(row_id: int, target_table: str, target_id: int,
SET status='done', processed_at=:now, updated_at=:now
WHERE id=:id
"""),
{"now": datetime.now(), "id": row_id},
{"now": _now_taipei_naive(), "id": row_id},
)
session.commit()
sys_log.debug(f"[OCLearn] embedding 寫入成功 {target_table}#{target_id}")
@@ -160,7 +166,7 @@ def _process_one_embedding(row_id: int, target_table: str, target_id: int,
{
"err": str(e)[:500],
"max": EMBED_MAX_ATTEMPTS,
"now": datetime.now(),
"now": _now_taipei_naive(),
"id": row_id,
},
)
@@ -189,8 +195,8 @@ def _claim_pending_embeddings(limit: int = EMBED_BATCH_SIZE,
AND updated_at < :cutoff
"""),
{
"now": datetime.now(),
"cutoff": datetime.fromtimestamp(time.time() - 15 * 60),
"now": _now_taipei_naive(),
"cutoff": _now_taipei_naive() - timedelta(minutes=15),
},
)
session.commit()
@@ -213,7 +219,7 @@ def _claim_pending_embeddings(limit: int = EMBED_BATCH_SIZE,
RETURNING q.id, q.target_table, q.target_id, q.text_content, q.model
"""),
{
"now": datetime.now(),
"now": _now_taipei_naive(),
"max": max_attempts,
"lim": limit,
},
@@ -275,7 +281,7 @@ def store_insight(insight_type: str, content: str, period: str = None,
existing.content = content
if meta_str:
existing.metadata_json = meta_str
existing.updated_at = datetime.now()
existing.updated_at = _now_taipei_naive()
session.commit()
insight_id = existing.id
sys_log.info(f"[OCLearn] 更新 insight_type={insight_type} period={period}")
@@ -287,8 +293,8 @@ def store_insight(insight_type: str, content: str, period: str = None,
"content": content,
"metadata_json": meta_str,
"status": status or "approved",
"created_at": datetime.now(),
"updated_at": datetime.now(),
"created_at": _now_taipei_naive(),
"updated_at": _now_taipei_naive(),
}
if confidence is not None:
insight_kwargs["confidence"] = confidence
@@ -317,7 +323,7 @@ def store_insight(insight_type: str, content: str, period: str = None,
params["i"] = insight_id
session.execute(
text(f"UPDATE ai_insights SET {assignments}, updated_at = :now WHERE id = :i"),
{**params, "now": datetime.now()},
{**params, "now": _now_taipei_naive()},
)
session.commit()
except Exception:
@@ -339,7 +345,7 @@ def store_insight(insight_type: str, content: str, period: str = None,
"""),
{
"meta": json.dumps(metadata_payload, ensure_ascii=False),
"now": datetime.now(),
"now": _now_taipei_naive(),
"id": insight_id,
},
)