70 lines
2.3 KiB
Python
70 lines
2.3 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from datetime import UTC, datetime
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
from sqlalchemy import select
|
||
|
|
from sqlalchemy.orm import Session
|
||
|
|
|
||
|
|
from app.core.agent_enums import AgentRunStatus
|
||
|
|
from app.models.agent_run import AgentRun
|
||
|
|
from app.services.knowledge_constants import (
|
||
|
|
KNOWLEDGE_INGEST_STATUS_META,
|
||
|
|
KNOWLEDGE_INGEST_STATUS_PUBLISHED,
|
||
|
|
KNOWLEDGE_INGEST_SYNC_STALE_SECONDS,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def normalize_ingest_status_code(value: Any) -> int:
|
||
|
|
try:
|
||
|
|
status_code = int(value)
|
||
|
|
except (TypeError, ValueError):
|
||
|
|
return KNOWLEDGE_INGEST_STATUS_PUBLISHED
|
||
|
|
if status_code not in KNOWLEDGE_INGEST_STATUS_META:
|
||
|
|
return KNOWLEDGE_INGEST_STATUS_PUBLISHED
|
||
|
|
return status_code
|
||
|
|
|
||
|
|
|
||
|
|
def is_syncing_status_stale(entry: dict[str, Any]) -> bool:
|
||
|
|
raw_value = str(entry.get("ingest_status_updated_at") or "").strip()
|
||
|
|
if not raw_value:
|
||
|
|
return True
|
||
|
|
try:
|
||
|
|
updated_at = datetime.fromisoformat(raw_value)
|
||
|
|
except ValueError:
|
||
|
|
return True
|
||
|
|
if updated_at.tzinfo is None:
|
||
|
|
updated_at = updated_at.replace(tzinfo=UTC)
|
||
|
|
age_seconds = (datetime.now(UTC) - updated_at.astimezone(UTC)).total_seconds()
|
||
|
|
return age_seconds >= KNOWLEDGE_INGEST_SYNC_STALE_SECONDS
|
||
|
|
|
||
|
|
|
||
|
|
def should_preserve_syncing_status(entry: dict[str, Any], *, db: Session | None) -> bool:
|
||
|
|
agent_run_id = str(entry.get("ingest_agent_run_id") or "").strip()
|
||
|
|
if not agent_run_id or db is None:
|
||
|
|
return not is_syncing_status_stale(entry)
|
||
|
|
|
||
|
|
run = db.scalar(select(AgentRun).where(AgentRun.run_id == agent_run_id))
|
||
|
|
if run is None:
|
||
|
|
return not is_syncing_status_stale(entry)
|
||
|
|
if run.status != AgentRunStatus.RUNNING.value:
|
||
|
|
return False
|
||
|
|
|
||
|
|
heartbeat_at = str((run.route_json or {}).get("heartbeat_at") or "").strip()
|
||
|
|
if heartbeat_at:
|
||
|
|
probe_entry = {"ingest_status_updated_at": heartbeat_at}
|
||
|
|
return not is_syncing_status_stale(probe_entry)
|
||
|
|
|
||
|
|
return not is_syncing_status_stale(entry)
|
||
|
|
|
||
|
|
|
||
|
|
def resolve_linked_ingest_run_status(entry: dict[str, Any], *, db: Session | None) -> str:
|
||
|
|
agent_run_id = str(entry.get("ingest_agent_run_id") or "").strip()
|
||
|
|
if not agent_run_id or db is None:
|
||
|
|
return ""
|
||
|
|
|
||
|
|
run = db.scalar(select(AgentRun).where(AgentRun.run_id == agent_run_id))
|
||
|
|
if run is None:
|
||
|
|
return ""
|
||
|
|
return str(run.status or "").strip()
|