UPDATE
This commit is contained in:
+34
-72
@@ -142,60 +142,32 @@ def _build_structured_context(farm_uuid: str) -> tuple[dict[str, Any], dict[str,
|
||||
return context, structured
|
||||
|
||||
|
||||
def _build_fallback_notifications(tracker: dict[str, Any], endpoint: str) -> list[dict[str, Any]]:
|
||||
notifications: list[dict[str, Any]] = []
|
||||
for alert in tracker.get("alerts", [])[:5]:
|
||||
notifications.append(
|
||||
{
|
||||
"level": _severity_to_level(alert.get("severity")),
|
||||
"title": alert.get("title") or "هشدار مزرعه",
|
||||
"message": alert.get("summary") or alert.get("explanation") or "",
|
||||
"suggested_action": alert.get("recommended_action") or "",
|
||||
"source_alert_id": _alert_identifier(alert),
|
||||
"source_metric_type": alert.get("metric_type") or "",
|
||||
"payload": {
|
||||
"endpoint": endpoint,
|
||||
"alert": alert,
|
||||
},
|
||||
}
|
||||
def _validate_tracker_response(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
required_keys = {"headline", "overview", "status_level", "notifications"}
|
||||
missing = [key for key in required_keys if key not in payload]
|
||||
if missing:
|
||||
raise ValueError(
|
||||
"Farm alerts tracker response is missing required fields: "
|
||||
+ ", ".join(missing)
|
||||
)
|
||||
return notifications
|
||||
if not isinstance(payload.get("notifications"), list):
|
||||
raise ValueError("Farm alerts tracker notifications must be a list.")
|
||||
return payload
|
||||
|
||||
|
||||
def _build_fallback_tracker_response(tracker: dict[str, Any]) -> dict[str, Any]:
|
||||
top_alert = tracker.get("mostCriticalIssue") or {}
|
||||
status_level = _severity_to_level(top_alert.get("severity")) if top_alert else FarmAlertNotification.LEVEL_INFO
|
||||
if tracker.get("totalAlerts", 0) <= 0:
|
||||
overview = "در حال حاضر هشدار فعالی برای مزرعه شناسایی نشده است."
|
||||
else:
|
||||
overview = top_alert.get("summary") or "چند هشدار فعال برای مزرعه شناسایی شده است."
|
||||
return {
|
||||
"headline": "ارزیابی فعلی هشدارهای مزرعه",
|
||||
"overview": overview,
|
||||
"status_level": status_level,
|
||||
"notifications": _build_fallback_notifications(tracker, FarmAlertNotification.ENDPOINT_TRACKER),
|
||||
}
|
||||
|
||||
|
||||
def _build_fallback_timeline_response(tracker: dict[str, Any]) -> dict[str, Any]:
|
||||
timeline = []
|
||||
for alert in tracker.get("alerts", [])[:6]:
|
||||
timeline.append(
|
||||
{
|
||||
"timestamp": alert.get("timestamp"),
|
||||
"level": _severity_to_level(alert.get("severity")),
|
||||
"title": alert.get("title") or "رویداد هشدار",
|
||||
"description": alert.get("explanation") or alert.get("summary") or "",
|
||||
"source_alert_id": _alert_identifier(alert),
|
||||
"source_metric_type": alert.get("metric_type") or "",
|
||||
}
|
||||
def _validate_timeline_response(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
required_keys = {"headline", "overview", "timeline", "notifications"}
|
||||
missing = [key for key in required_keys if key not in payload]
|
||||
if missing:
|
||||
raise ValueError(
|
||||
"Farm alerts timeline response is missing required fields: "
|
||||
+ ", ".join(missing)
|
||||
)
|
||||
return {
|
||||
"headline": "خط زمانی هشدارهای مزرعه",
|
||||
"overview": "timeline بر اساس هشدارهای محاسبه شده مزرعه ساخته شد.",
|
||||
"timeline": timeline,
|
||||
"notifications": _build_fallback_notifications(tracker, FarmAlertNotification.ENDPOINT_TIMELINE),
|
||||
}
|
||||
if not isinstance(payload.get("timeline"), list):
|
||||
raise ValueError("Farm alerts timeline must be a list.")
|
||||
if not isinstance(payload.get("notifications"), list):
|
||||
raise ValueError("Farm alerts timeline notifications must be a list.")
|
||||
return payload
|
||||
|
||||
|
||||
def _notification_fingerprint(
|
||||
@@ -354,12 +326,14 @@ def _llm_response(
|
||||
response = client.chat.completions.create(model=model, messages=messages)
|
||||
raw = response.choices[0].message.content.strip()
|
||||
parsed = _clean_json_response(raw)
|
||||
if not parsed:
|
||||
raise ValueError("farm_alerts LLM returned an empty or invalid JSON payload.")
|
||||
_complete_audit_log(audit_log, raw)
|
||||
return parsed, raw, service.tone_file or ""
|
||||
except Exception as exc:
|
||||
logger.error("farm_alerts llm error for %s: %s", farm_uuid, exc)
|
||||
_fail_audit_log(audit_log, str(exc))
|
||||
return {}, "", service.tone_file or ""
|
||||
raise RuntimeError(f"Farm alerts generation failed for farm {farm_uuid}.") from exc
|
||||
|
||||
|
||||
def get_farm_alerts_tracker(*, farm_uuid: str, query: str | None = None) -> dict[str, Any]:
|
||||
@@ -373,12 +347,9 @@ def get_farm_alerts_tracker(*, farm_uuid: str, query: str | None = None) -> dict
|
||||
query=user_query,
|
||||
structured_context=structured_context,
|
||||
)
|
||||
if not llm_result:
|
||||
llm_result = _build_fallback_tracker_response(tracker)
|
||||
llm_result = _validate_tracker_response(llm_result)
|
||||
|
||||
notifications_input = llm_result.get("notifications")
|
||||
if not isinstance(notifications_input, list):
|
||||
notifications_input = _build_fallback_notifications(tracker, FarmAlertNotification.ENDPOINT_TRACKER)
|
||||
notifications_input = llm_result["notifications"]
|
||||
saved_notifications = _save_notifications(
|
||||
farm_uuid=farm_uuid,
|
||||
endpoint=FarmAlertNotification.ENDPOINT_TRACKER,
|
||||
@@ -387,11 +358,9 @@ def get_farm_alerts_tracker(*, farm_uuid: str, query: str | None = None) -> dict
|
||||
return {
|
||||
"farm_uuid": farm_uuid,
|
||||
"service_id": SERVICE_ID,
|
||||
"knowledge_base": KB_NAME,
|
||||
"tone_file": tone_file,
|
||||
"tracker": tracker,
|
||||
"headline": llm_result.get("headline") or "ارزیابی فعلی هشدارهای مزرعه",
|
||||
"overview": llm_result.get("overview") or "",
|
||||
"headline": llm_result["headline"],
|
||||
"overview": llm_result["overview"],
|
||||
"status_level": _normalize_level(llm_result.get("status_level")),
|
||||
"notifications": [_serialize_notification(item) for item in saved_notifications],
|
||||
"raw_llm_response": raw_response or None,
|
||||
@@ -410,15 +379,10 @@ def get_farm_alerts_timeline(*, farm_uuid: str, query: str | None = None) -> dic
|
||||
query=user_query,
|
||||
structured_context=structured_context,
|
||||
)
|
||||
if not llm_result:
|
||||
llm_result = _build_fallback_timeline_response(tracker)
|
||||
llm_result = _validate_timeline_response(llm_result)
|
||||
|
||||
timeline = llm_result.get("timeline")
|
||||
if not isinstance(timeline, list):
|
||||
timeline = _build_fallback_timeline_response(tracker).get("timeline", [])
|
||||
notifications_input = llm_result.get("notifications")
|
||||
if not isinstance(notifications_input, list):
|
||||
notifications_input = _build_fallback_notifications(tracker, FarmAlertNotification.ENDPOINT_TIMELINE)
|
||||
timeline = llm_result["timeline"]
|
||||
notifications_input = llm_result["notifications"]
|
||||
|
||||
saved_notifications = _save_notifications(
|
||||
farm_uuid=farm_uuid,
|
||||
@@ -428,11 +392,9 @@ def get_farm_alerts_timeline(*, farm_uuid: str, query: str | None = None) -> dic
|
||||
return {
|
||||
"farm_uuid": farm_uuid,
|
||||
"service_id": SERVICE_ID,
|
||||
"knowledge_base": KB_NAME,
|
||||
"tone_file": tone_file,
|
||||
"tracker": tracker,
|
||||
"headline": llm_result.get("headline") or "خط زمانی هشدارهای مزرعه",
|
||||
"overview": llm_result.get("overview") or "",
|
||||
"headline": llm_result["headline"],
|
||||
"overview": llm_result["overview"],
|
||||
"timeline": timeline,
|
||||
"notifications": [_serialize_notification(item) for item in saved_notifications],
|
||||
"raw_llm_response": raw_response or None,
|
||||
|
||||
Reference in New Issue
Block a user