from __future__ import annotations from fastapi import Depends, HTTPException, Query from sqlalchemy.ext.asyncio import AsyncSession from ...core.response import success_response from ...dependencies.permissions import require_manager_user from ...dependencies.tenant import get_tenant_id from ...models import User from ...schemas.reply_iteration import ( FeedbackAssetPromoteRequest, PromptReleaseCreateRequest, PromptTrialCreateRequest, PromptTrialDecisionRequest, ReplyIterationCandidateCreateRequest, ReplyIterationJudgeRunCreateRequest, ) from ...services.prompt_release import PromptReleaseService from ...services.reply_iteration import ReplyIterationService from .shared import * @router.post("/feedback-center/items/{record_id}/promote") async def promote_feedback_center_item_to_asset( record_id: int, request: FeedbackAssetPromoteRequest, current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await ReplyIterationService(db).promote_feedback_record( tenant_id=effective_tenant_id, record_id=record_id, actor_id=int(current_user.id), asset_type=request.asset_type, prompt_key=request.prompt_key, workflow_type=request.workflow_type, notes=request.notes, pair_with_asset_id=request.pair_with_asset_id, pair_role=request.pair_role, pair_reason=request.pair_reason, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return success_response(data) @router.post("/reply-iteration/backfill-mounted-assets") async def backfill_reply_iteration_assets_from_real_feedback( limit: int = Query(default=200, ge=1, le=2000), current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await ReplyIterationService(db).backfill_mounted_training_assets_from_feedback( tenant_id=effective_tenant_id, actor_id=int(current_user.id), limit=limit, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return success_response(data) @router.get("/reply-iteration/assets") async def list_reply_iteration_assets( asset_type: str | None = Query(default=None), prompt_key: str | None = Query(default=None), limit: int = Query(default=20, ge=1, le=100), offset: int = Query(default=0, ge=0), current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) data = await ReplyIterationService(db).list_assets( tenant_id=effective_tenant_id, asset_type=asset_type, prompt_key=prompt_key, limit=limit, offset=offset, ) return success_response(data) @router.post("/reply-iteration/judge-runs") async def create_reply_iteration_judge_run( request: ReplyIterationJudgeRunCreateRequest, current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await ReplyIterationService(db).create_judge_run( tenant_id=effective_tenant_id, asset_id=request.asset_id, actor_id=int(current_user.id), ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return success_response(data) @router.get("/reply-iteration/judge-runs") async def list_reply_iteration_judge_runs( limit: int = Query(default=20, ge=1, le=100), offset: int = Query(default=0, ge=0), current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) return success_response( await ReplyIterationService(db).list_judge_runs( tenant_id=effective_tenant_id, limit=limit, offset=offset, ) ) @router.post("/reply-iteration/candidates") async def create_reply_iteration_candidate( request: ReplyIterationCandidateCreateRequest, current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await ReplyIterationService(db).create_optimizer_candidate( tenant_id=effective_tenant_id, judge_run_id=request.judge_run_id, actor_id=int(current_user.id), ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return success_response(data) @router.get("/reply-iteration/candidates") async def list_reply_iteration_candidates( limit: int = Query(default=20, ge=1, le=100), offset: int = Query(default=0, ge=0), current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) return success_response( await ReplyIterationService(db).list_candidates( tenant_id=effective_tenant_id, limit=limit, offset=offset, ) ) @router.post("/reply-iteration/prompt-releases") async def create_prompt_release( request: PromptReleaseCreateRequest, current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptReleaseService(db).create_release( tenant_id=effective_tenant_id, prompt_key=request.prompt_key, body_snapshot=request.body_snapshot, created_by=int(current_user.id), notes=request.notes, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return success_response(data) @router.get("/reply-iteration/prompt-releases") async def list_prompt_releases( prompt_key: str | None = Query(default=None), current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) return success_response( await PromptReleaseService(db).list_releases( tenant_id=effective_tenant_id, prompt_key=prompt_key, ) ) @router.post("/reply-iteration/prompt-releases/{release_id}/activate") async def activate_prompt_release( release_id: int, current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptReleaseService(db).activate_release( tenant_id=effective_tenant_id, release_id=release_id, actor_id=int(current_user.id), ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return success_response(data) @router.get("/reply-iteration/trials") async def list_prompt_release_trials( limit: int = Query(default=20, ge=1, le=100), offset: int = Query(default=0, ge=0), current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) return success_response( await ReplyIterationService(db).list_trials( tenant_id=effective_tenant_id, limit=limit, offset=offset, ) ) @router.post("/reply-iteration/trials") async def create_prompt_release_trial( request: PromptTrialCreateRequest, current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await ReplyIterationService(db).create_trial( tenant_id=effective_tenant_id, actor_id=int(current_user.id), title=request.title, hypothesis=request.hypothesis, target_metric=request.target_metric, prompt_key=request.prompt_key, baseline_release_id=request.baseline_release_id, candidate_release_id=request.candidate_release_id, scope_json=request.scope_json, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return success_response(data) @router.post("/reply-iteration/trials/{trial_id}/decision") async def decide_prompt_release_trial( trial_id: int, request: PromptTrialDecisionRequest, current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await ReplyIterationService(db).decide_trial( tenant_id=effective_tenant_id, trial_id=trial_id, actor_id=int(current_user.id), decision=request.decision, result_summary=request.result_summary, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return success_response(data)