Add degraded-mode workflow regeneration criteria and endpoint

This commit is contained in:
ي
2026-03-06 21:40:29 +05:30
parent 5d49351c2d
commit 4621107988
2 changed files with 234 additions and 32 deletions

View File

@@ -1,13 +1,14 @@
from fastapi import APIRouter, Depends, HTTPException
from typing import Any, Dict, Optional
from datetime import datetime
from datetime import datetime, timezone
from collections import defaultdict, deque
from loguru import logger
from sqlalchemy.orm import Session
from middleware.auth_middleware import get_current_user
from services.database import get_db
from services.today_workflow_service import get_or_create_daily_workflow_plan, update_task_status
from services.today_workflow_service import get_or_create_daily_workflow_plan, regenerate_daily_workflow_plan, update_task_status
from models.daily_workflow_models import DailyWorkflowPlan, DailyWorkflowTask
import asyncio
from services.intelligence.txtai_service import TxtaiIntelligenceService
@@ -15,6 +16,27 @@ from services.intelligence.txtai_service import TxtaiIntelligenceService
router = APIRouter(prefix="/api/today-workflow", tags=["Today Workflow"])
REGENERATE_WINDOW_SECONDS = 60
REGENERATE_MAX_REQUESTS_PER_WINDOW = 3
_regen_request_log: dict[str, deque[float]] = defaultdict(deque)
def _check_regenerate_rate_limit(user_id: str) -> None:
import time
now = time.time()
window_start = now - REGENERATE_WINDOW_SECONDS
history = _regen_request_log[user_id]
while history and history[0] < window_start:
history.popleft()
if len(history) >= REGENERATE_MAX_REQUESTS_PER_WINDOW:
raise HTTPException(status_code=429, detail="Regeneration rate limit exceeded")
history.append(now)
async def _index_tasks_to_sif(user_id: str, date: str, tasks: list[dict], label: str):
svc = TxtaiIntelligenceService(user_id)
items = []
@@ -160,6 +182,9 @@ async def get_today_workflow(
"source": plan.source,
"created_at": plan.created_at.isoformat() if plan.created_at else None,
"updated_at": plan.updated_at.isoformat() if plan.updated_at else None,
"generation_mode": (plan.plan_json or {}).get("generation_mode"),
"quality_score": (plan.plan_json or {}).get("quality_score"),
"generated_with_agents": (plan.plan_json or {}).get("generated_with_agents"),
},
},
"timestamp": datetime.utcnow().isoformat(),
@@ -167,6 +192,67 @@ async def get_today_workflow(
}
@router.post("/regenerate")
async def regenerate_today_workflow(
date: Optional[str] = None,
current_user: dict = Depends(get_current_user),
db: Session = Depends(get_db),
) -> Dict[str, Any]:
from starlette.concurrency import run_in_threadpool
user_id = str(current_user.get("id"))
_check_regenerate_rate_limit(user_id)
plan = await regenerate_daily_workflow_plan(db, user_id, date=date)
tasks = await run_in_threadpool(
lambda: (
db.query(DailyWorkflowTask)
.filter(DailyWorkflowTask.plan_id == plan.id, DailyWorkflowTask.user_id == user_id)
.order_by(DailyWorkflowTask.created_at.asc())
.all()
)
)
response_tasks = [
{
"id": str(t.id),
"pillarId": t.pillar_id,
"title": t.title,
"description": t.description,
"status": "skipped" if t.status == "dismissed" else t.status,
"priority": t.priority,
"estimatedTime": t.estimated_time,
"dependencies": t.dependencies or [],
"actionUrl": t.action_url,
"actionType": t.action_type,
"metadata": t.metadata_json or {},
"enabled": bool(t.enabled),
}
for t in tasks
]
asyncio.create_task(_index_tasks_to_sif(user_id, plan.date, response_tasks, label="today_regenerated"))
return {
"success": True,
"data": {
"plan": {
"id": plan.id,
"date": plan.date,
"source": plan.source,
"generation_mode": (plan.plan_json or {}).get("generation_mode"),
"quality_score": (plan.plan_json or {}).get("quality_score"),
"generated_with_agents": (plan.plan_json or {}).get("generated_with_agents"),
"regenerated_at": datetime.now(timezone.utc).isoformat(),
},
"tasks": response_tasks,
},
"timestamp": datetime.utcnow().isoformat(),
"user_id": user_id,
}
from services.task_memory_service import TaskMemoryService
@router.post("/tasks/{task_id}/status")