- Prepaid credit system: org wallet, debit on usage, chat gate, admin credit console - Engine UI: /manager, /tasks, /tasks/[id], /workforce, /dashboard, /traces pages - Direct task creation (HTTP POST /tasks, MCP tasks_create) at pending_manager_review - Pause/resume/cancel/answer actions in engine API and UI - Employee cap (7 per org default), review pipeline guard (active reviewer required) - Scheduler: wide window prevents org starvation; archived employees excluded from cap - Tenant isolation: project-id cross-org 404, engine never uses another tenant's sandbox - Order route fix: manager built with pipeline states + employee profiles - Contract extension: ModelGrantPort.get_org_policy with DefaultOrgPolicy - Engine event types in @agy/contracts (task.step, task.gate, run.log, run.tool, team.changed) - api-engine.ts typed client; api-credit.ts client - Alembic migration c4d5e6f7a8b9: prepaid credit + engine UI tables - 859 tests passed, 1 skipped; ruff/mypy/tsc/eslint/vitest all green Refs: PLAN-v2.md §9 (T1–T43), todo-v2-final.md
102 lines
3.9 KiB
Python
102 lines
3.9 KiB
Python
"""Internal, never-socket-bound FastAPI app that holds every HTTP handler.
|
|
|
|
The public app (``app.main.create_app``) exposes only ``/health`` and the
|
|
``/api/ws`` WebSocket. All request/response logic lives here and is reached in
|
|
exactly one of two ways:
|
|
|
|
* from the WS-RPC proxy (``app.interfaces.realtime.router``), which dispatches
|
|
client RPCs into this app over an in-process ASGI transport, and
|
|
* from the test-suite's ``AsyncClient``/``TestClient``.
|
|
|
|
It is intentionally never bound to a socket, so including the routers here adds no
|
|
public REST surface — the only public routes are the ones ``create_app``
|
|
registers. Keeping the handlers in one FastAPI app also means they run through the
|
|
real middleware, dependencies, validation and error handling, unchanged.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from fastapi import FastAPI
|
|
|
|
from app.core.config import settings
|
|
from app.interfaces.http.errors import register_exception_handlers
|
|
from app.interfaces.http.request_context import RequestContextMiddleware
|
|
from app.interfaces.http.routers import (
|
|
admin,
|
|
admin_credits,
|
|
admin_domains,
|
|
admin_grants,
|
|
admin_users,
|
|
agy,
|
|
app_setup,
|
|
auth,
|
|
billing,
|
|
delivery,
|
|
engine,
|
|
health,
|
|
invites,
|
|
knowledge,
|
|
mcp_tokens,
|
|
memory,
|
|
organizations,
|
|
projects,
|
|
sandbox,
|
|
workforce,
|
|
)
|
|
from app.interfaces.realtime.router import router as realtime_router
|
|
from app.main import lifespan # shared lifespan installs the realtime bus
|
|
|
|
#: Module-level singleton so the RPC proxy and the tests share one app instance.
|
|
_rpc_app: FastAPI | None = None
|
|
|
|
|
|
def build_rpc_app() -> FastAPI:
|
|
"""Construct the full handler app. Called once per process."""
|
|
application = FastAPI(
|
|
title=settings.app_name,
|
|
version="0.1.0",
|
|
description="AI Workforce Platform — internal RPC dispatch surface.",
|
|
lifespan=lifespan,
|
|
# This app is reached in-process, never over the network, so the
|
|
# interactive docs would only be a liability.
|
|
docs_url=None,
|
|
openapi_url=None,
|
|
)
|
|
application.add_middleware(RequestContextMiddleware)
|
|
register_exception_handlers(application)
|
|
|
|
# Same mounting as the old public app: health at the root, everything else
|
|
# under /api, and the live socket alongside the RPC surface.
|
|
application.include_router(health.router)
|
|
application.include_router(auth.router, prefix="/api")
|
|
application.include_router(invites.router, prefix="/api")
|
|
application.include_router(organizations.router, prefix="/api")
|
|
application.include_router(projects.router, prefix="/api")
|
|
application.include_router(workforce.router, prefix="/api")
|
|
application.include_router(billing.router, prefix="/api")
|
|
application.include_router(knowledge.router, prefix="/api")
|
|
application.include_router(sandbox.router, prefix="/api")
|
|
application.include_router(admin.router, prefix="/api")
|
|
application.include_router(admin_users.router, prefix="/api")
|
|
application.include_router(agy.router, prefix="/api")
|
|
# v2 routers (one workstream each; see docs/plan/PLAN-v2.md §6).
|
|
application.include_router(app_setup.router, prefix="/api")
|
|
application.include_router(memory.router, prefix="/api")
|
|
application.include_router(mcp_tokens.router, prefix="/api")
|
|
application.include_router(admin_grants.router, prefix="/api")
|
|
application.include_router(admin_credits.router, prefix="/api")
|
|
application.include_router(engine.router, prefix="/api")
|
|
application.include_router(delivery.router, prefix="/api")
|
|
application.include_router(admin_domains.router, prefix="/api")
|
|
application.include_router(sandbox.admin_router, prefix="/api")
|
|
application.include_router(realtime_router, prefix="/api")
|
|
return application
|
|
|
|
|
|
def get_rpc_app() -> FastAPI:
|
|
"""Return the process-wide internal app, building it on first use."""
|
|
global _rpc_app
|
|
if _rpc_app is None:
|
|
_rpc_app = build_rpc_app()
|
|
return _rpc_app
|