Merge PR #395: Normalize dependencies in today workflow API payloads

- Add _normalize_dependencies() helper to handle all dependency type variations
- Handle None, list, JSON string, and invalid types with safe fallback to []
- Apply normalization to today and yesterday task payloads for consistency
- Ensure indexing pipeline receives normalized list dependencies
- Preserve task status feedback scoring logic (uses task.status, handles all negative cases)
- Keep contextuality validation and quality status response fields
- Improve data consistency across API and indexing surfaces
This commit is contained in:
ajaysi
2026-03-08 18:31:48 +05:30

View File

@@ -1,6 +1,7 @@
from fastapi import APIRouter, Depends, HTTPException from fastapi import APIRouter, Depends, HTTPException
from typing import Any, Dict, Optional from typing import Any, Dict, Optional
from datetime import datetime from datetime import datetime
import json
from loguru import logger from loguru import logger
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
@@ -15,6 +16,20 @@ from services.intelligence.txtai_service import TxtaiIntelligenceService
router = APIRouter(prefix="/api/today-workflow", tags=["Today Workflow"]) router = APIRouter(prefix="/api/today-workflow", tags=["Today Workflow"])
def _normalize_dependencies(dependencies: Any) -> list:
if dependencies is None:
return []
if isinstance(dependencies, list):
return dependencies
if isinstance(dependencies, str):
try:
parsed = json.loads(dependencies)
return parsed if isinstance(parsed, list) else []
except json.JSONDecodeError:
return []
return []
async def _index_tasks_to_sif(user_id: str, date: str, tasks: list[dict], label: str): async def _index_tasks_to_sif(user_id: str, date: str, tasks: list[dict], label: str):
svc = TxtaiIntelligenceService(user_id) svc = TxtaiIntelligenceService(user_id)
items = [] items = []
@@ -73,7 +88,7 @@ async def get_today_workflow(
"status": "skipped" if t.status == "dismissed" else t.status, "status": "skipped" if t.status == "dismissed" else t.status,
"priority": t.priority, "priority": t.priority,
"estimatedTime": t.estimated_time, "estimatedTime": t.estimated_time,
"dependencies": t.dependencies or [], "dependencies": _normalize_dependencies(t.dependencies),
"actionUrl": t.action_url, "actionUrl": t.action_url,
"actionType": t.action_type, "actionType": t.action_type,
"metadata": t.metadata_json or {}, "metadata": t.metadata_json or {},
@@ -133,6 +148,7 @@ async def get_today_workflow(
"title": t.title, "title": t.title,
"description": t.description, "description": t.description,
"status": "skipped" if t.status == "dismissed" else t.status, "status": "skipped" if t.status == "dismissed" else t.status,
"dependencies": _normalize_dependencies(t.dependencies),
} }
) )
asyncio.create_task(_index_tasks_to_sif(user_id, y_str, y_response, label="yesterday")) asyncio.create_task(_index_tasks_to_sif(user_id, y_str, y_response, label="yesterday"))