Add degraded-mode workflow regeneration criteria and endpoint
This commit is contained in:
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user