All checks were successful
Deploy Development / deploy (push) Successful in 48s
Test Suite / pytest-backend (push) Successful in 4m14s
Test Suite / lint-backend (push) Successful in 2s
Test Suite / compose-smoke (push) Has been skipped
Test Suite / k6 /api/health Baseline (push) Successful in 18s
Test Suite / playwright-smoke (push) Successful in 12s
GET /api/workspace/steering; Portfolio-Widget nutzt Kernel-Attention, Vorschlaege und Agent-Slots. Co-authored-by: Cursor <cursoragent@cursor.com>
126 lines
4.0 KiB
Python
126 lines
4.0 KiB
Python
"""Workspace read API — delegates to Data Layer (AP0.7)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Optional
|
|
|
|
from capabilities import require_capability
|
|
from data_layer import actions as dl_actions
|
|
from data_layer import actors as dl_actors
|
|
from data_layer import attention as dl_attention
|
|
from data_layer import initiatives as dl_initiatives
|
|
from data_layer import portfolio_steering as dl_portfolio_steering
|
|
from data_layer import workspace as dl_workspace
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from pydantic import BaseModel, Field
|
|
from services import initiatives as initiative_service
|
|
from tenant_context import TenantContext
|
|
|
|
router = APIRouter(prefix="/api/workspace", tags=["workspace"])
|
|
|
|
|
|
class PortfolioReorderRequest(BaseModel):
|
|
initiative_ids: list[str] = Field(min_length=1)
|
|
|
|
|
|
def _require_actor_ctx(ctx: TenantContext) -> TenantContext:
|
|
if not ctx.actor_id:
|
|
raise HTTPException(status_code=400, detail="Kein Actor im TenantContext")
|
|
return ctx
|
|
|
|
|
|
@router.get("/summary")
|
|
def workspace_summary(
|
|
ctx: TenantContext = Depends(require_capability("kairo.workspace.read")),
|
|
):
|
|
return dl_workspace.get_workspace_summary(ctx)
|
|
|
|
|
|
@router.get("/actions/open")
|
|
def workspace_open_actions(
|
|
ctx: TenantContext = Depends(require_capability("kairo.workspace.read")),
|
|
):
|
|
try:
|
|
return dl_actions.get_my_open_actions(_require_actor_ctx(ctx))
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
|
|
|
|
@router.get("/actions/blocked")
|
|
def workspace_blocked_actions(
|
|
ctx: TenantContext = Depends(require_capability("kairo.workspace.read")),
|
|
):
|
|
try:
|
|
return dl_actions.get_my_blocked_actions(_require_actor_ctx(ctx))
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
|
|
|
|
@router.get("/actions/today")
|
|
def workspace_today_actions(
|
|
limit: Optional[int] = Query(default=15, ge=1, le=50),
|
|
ctx: TenantContext = Depends(require_capability("kairo.workspace.read")),
|
|
):
|
|
try:
|
|
return dl_actions.get_my_today_actions(_require_actor_ctx(ctx), limit=limit or 15)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
|
|
|
|
@router.get("/initiatives/active")
|
|
def workspace_active_initiatives(
|
|
limit: Optional[int] = Query(default=None, ge=1, le=100),
|
|
ctx: TenantContext = Depends(require_capability("kairo.workspace.read")),
|
|
):
|
|
return dl_initiatives.get_active_initiatives(ctx, limit=limit)
|
|
|
|
|
|
@router.get("/actors/workload")
|
|
def workspace_actor_workload(
|
|
ctx: TenantContext = Depends(require_capability("kairo.workspace.read")),
|
|
):
|
|
return dl_actors.get_actor_workload(ctx)
|
|
|
|
|
|
@router.get("/attention")
|
|
def workspace_attention(
|
|
ctx: TenantContext = Depends(require_capability("kairo.attention.read")),
|
|
):
|
|
return dl_attention.get_attention_items(ctx)
|
|
|
|
|
|
@router.get("/next-actions")
|
|
def workspace_next_actions(
|
|
limit: Optional[int] = Query(default=10, ge=1, le=50),
|
|
ctx: TenantContext = Depends(require_capability("kairo.attention.read")),
|
|
):
|
|
return dl_attention.get_next_action_candidates(ctx, limit=limit or 10)
|
|
|
|
|
|
@router.get("/steering")
|
|
def workspace_steering(
|
|
limit: Optional[int] = Query(default=30, ge=1, le=100),
|
|
ctx: TenantContext = Depends(require_capability("kairo.attention.read")),
|
|
):
|
|
"""Portfolio-Steuerung — Kernel-Aggregation über alle aktiven Initiativen (K-Ext-5)."""
|
|
return dl_portfolio_steering.get_portfolio_steering(
|
|
ctx,
|
|
total_attention=limit or 30,
|
|
total_next_work=min(limit or 30, 15),
|
|
)
|
|
|
|
|
|
@router.post("/portfolio/reorder")
|
|
def workspace_portfolio_reorder(
|
|
body: PortfolioReorderRequest,
|
|
ctx: TenantContext = Depends(require_capability("kairo.initiative.manage")),
|
|
):
|
|
try:
|
|
return initiative_service.reorder_portfolio(
|
|
tenant_id=ctx.tenant_id,
|
|
initiative_ids=body.initiative_ids,
|
|
user_id=ctx.user_id,
|
|
)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|