feat: add resolution output API endpoints

Adds GET /outputs, PATCH /outputs/{id}, and POST /outputs/{id}/push
endpoints under /ai-sessions/{session_id}/, plus integration tests.
Router registered before ai_sessions to avoid path conflict.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-03-24 08:47:09 +00:00
parent 5f3169bad4
commit a928901a2f
3 changed files with 144 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
"""Resolution output endpoints."""
import logging
from typing import Annotated
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_current_active_user, get_db
from app.models.user import User
from app.models.session_resolution_output import SessionResolutionOutput
from app.services.resolution_output_generator import ResolutionOutputGenerator
from app.schemas.session_resolution import (
ResolutionOutputResponse,
ResolutionOutputEditRequest,
ResolutionOutputPushRequest,
ResolutionOutputPushResponse,
AllResolutionOutputsResponse,
)
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/ai-sessions/{session_id}", tags=["session-resolutions"])
@router.get("/outputs", response_model=AllResolutionOutputsResponse)
async def get_outputs(
session_id: UUID,
current_user: Annotated[User, Depends(get_current_active_user)],
db: Annotated[AsyncSession, Depends(get_db)],
) -> AllResolutionOutputsResponse:
result = await db.execute(
select(SessionResolutionOutput)
.where(SessionResolutionOutput.session_id == session_id)
.order_by(SessionResolutionOutput.output_type)
)
outputs = result.scalars().all()
return AllResolutionOutputsResponse(
outputs=[ResolutionOutputResponse.model_validate(o) for o in outputs]
)
@router.patch("/outputs/{output_id}", response_model=ResolutionOutputResponse)
async def edit_output(
session_id: UUID,
output_id: UUID,
body: ResolutionOutputEditRequest,
current_user: Annotated[User, Depends(get_current_active_user)],
db: Annotated[AsyncSession, Depends(get_db)],
) -> ResolutionOutputResponse:
gen = ResolutionOutputGenerator(db)
try:
output = await gen.edit_output(output_id, body.edited_content)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
await db.commit()
return ResolutionOutputResponse.model_validate(output)
@router.post("/outputs/{output_id}/push", response_model=ResolutionOutputPushResponse)
async def push_output(
session_id: UUID,
output_id: UUID,
body: ResolutionOutputPushRequest,
current_user: Annotated[User, Depends(get_current_active_user)],
db: Annotated[AsyncSession, Depends(get_db)],
) -> ResolutionOutputPushResponse:
gen = ResolutionOutputGenerator(db)
try:
output = await gen.push_output(output_id, body.destination)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
await db.commit()
return ResolutionOutputPushResponse(
output_id=output.id,
status=output.status,
pushed_to=output.pushed_to or body.destination,
pushed_reference=output.pushed_reference,
)

View File

@@ -32,6 +32,7 @@ from app.api.endpoints import script_builder
from app.api.endpoints import beta_feedback
from app.api.endpoints import session_branches
from app.api.endpoints import session_handoffs
from app.api.endpoints import session_resolutions
api_router = APIRouter()
@@ -79,6 +80,7 @@ api_router.include_router(onboarding.router)
api_router.include_router(branding.router)
api_router.include_router(supporting_data.router)
api_router.include_router(session_handoffs.queue_router) # Must be before ai_sessions to avoid /{session_id} conflict
api_router.include_router(session_resolutions.router) # Must be before ai_sessions to avoid /{session_id} conflict
api_router.include_router(ai_sessions.router)
api_router.include_router(flow_proposals.router)
api_router.include_router(flowpilot_analytics.router)