feat: surface L2 agent pool and escalations on supervisor screen
- routing-service: GET /escalations (list recent escalation attempts) - supervisor UI: new panel showing the real agent pool (status, level, tenant, skills, calls handled) fed by GET /agents, with a form to add operators to the pool - supervisor UI: new live escalation feed (AI->L2 handoffs, status, assigned operator), auto-refreshed every 5s alongside live calls
This commit is contained in:
@@ -12,6 +12,7 @@ from services.shared.models import (
|
||||
AgentCreate,
|
||||
AgentPoolOut,
|
||||
AgentStatusUpdateIn,
|
||||
EscalationOut,
|
||||
HealthResponse,
|
||||
QueueCreate,
|
||||
QueueOut,
|
||||
@@ -20,7 +21,7 @@ from services.shared.models import (
|
||||
)
|
||||
from services.shared.security import require_roles
|
||||
from services.shared.sql_init import init_sql_schema
|
||||
from services.shared.sql_models import AgentRow, IvrSessionRow, Queue, RoutingCounter
|
||||
from services.shared.sql_models import AgentRow, EscalationRow, IvrSessionRow, Queue, RoutingCounter
|
||||
|
||||
app = FastAPI(title="routing-service", version="1.1.0")
|
||||
|
||||
@@ -328,6 +329,43 @@ def update_agent_status(
|
||||
session.close()
|
||||
|
||||
|
||||
def _escalation_to_out(row: EscalationRow) -> EscalationOut:
|
||||
return EscalationOut(
|
||||
escalation_id=row.escalation_id,
|
||||
call_id=row.call_id,
|
||||
tenant_id=row.tenant_id,
|
||||
from_level=row.from_level,
|
||||
to_level=row.to_level,
|
||||
reason_code=row.reason_code,
|
||||
required_skills=routing_engine.parse_list_json(row.required_skills_json),
|
||||
priority=row.priority,
|
||||
topic=row.topic,
|
||||
summary=row.summary,
|
||||
status=row.status,
|
||||
assigned_agent_id=row.assigned_agent_id,
|
||||
requested_at=row.requested_at,
|
||||
connected_at=row.connected_at,
|
||||
completed_at=row.completed_at,
|
||||
)
|
||||
|
||||
|
||||
@app.get("/escalations", response_model=list[EscalationOut])
|
||||
def list_escalations(
|
||||
status: str | None = None,
|
||||
limit: int = 50,
|
||||
_: dict = Depends(require_roles(Role.ADMIN, Role.SUPERVISOR)),
|
||||
) -> list[EscalationOut]:
|
||||
session = get_session()
|
||||
try:
|
||||
stmt = select(EscalationRow).order_by(EscalationRow.id.desc()).limit(max(1, min(limit, 200)))
|
||||
if status:
|
||||
stmt = stmt.where(EscalationRow.status == status)
|
||||
rows = session.execute(stmt).scalars().all()
|
||||
return [_escalation_to_out(r) for r in rows]
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
@app.post("/internal/routing/reserve-agent", response_model=RoutingAgentReserveOut)
|
||||
def reserve_agent_endpoint(
|
||||
payload: RoutingAgentReserveIn,
|
||||
|
||||
Reference in New Issue
Block a user