修復 preflight gate 與 OCLearn queue 時區
All checks were successful
CD Pipeline / deploy (push) Successful in 1m6s
All checks were successful
CD Pipeline / deploy (push) Successful in 1m6s
This commit is contained in:
@@ -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"]]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user