183 lines
6.8 KiB
Python
183 lines
6.8 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from fastapi.responses import JSONResponse
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from ...core.database import get_db
|
|
from ...core.response import error_response, success_response
|
|
from ...dependencies import get_current_user
|
|
from ...models import User
|
|
from ...schemas.rag import QASessionListQuery, RAGAskRequest
|
|
from ...schemas.space import SavedIdeaCreateRequest, SavedIdeaListResponse, SavedIdeaPromoteResponse, SavedIdeaUpdateRequest
|
|
from ...services.ai_pmo.ai_pmo_kernel import AIPMOKernelService
|
|
from ...services.adaptive_rag_service import AdaptiveRAGService
|
|
from ...services.pmflow_launch_service import PMFlowLaunchError
|
|
from ...services.space_saved_idea_service import SavedIdeaError, SpaceSavedIdeaService
|
|
from .. import rag_ask_contract
|
|
|
|
router = APIRouter(prefix="/space", tags=["space"])
|
|
|
|
|
|
@router.get("/ai-concierge/status")
|
|
async def get_ai_concierge_status(
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
return success_response(
|
|
{
|
|
"chat": "available",
|
|
"history": "available",
|
|
"actions": "available",
|
|
"tenant_id": int(current_user.tenant_id),
|
|
}
|
|
)
|
|
|
|
|
|
@router.post("/ai-concierge/ask")
|
|
async def ask_ai_concierge(
|
|
body: RAGAskRequest,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
service = AIPMOKernelService(db, tenant_id=int(current_user.tenant_id))
|
|
ingress_result = await service.process_ingress(
|
|
**rag_ask_contract.build_kernel_ingress_kwargs(
|
|
request=body,
|
|
current_user=current_user,
|
|
)
|
|
)
|
|
if not bool(ingress_result.get("handled")):
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=str(ingress_result.get("reason") or "space_ai_concierge_not_handled"),
|
|
)
|
|
payload = rag_ask_contract.build_rag_response_payload(ingress_result=ingress_result)
|
|
if not str(payload.get("answer") or "").strip() and not str(payload.get("reply_text") or "").strip():
|
|
return JSONResponse(status_code=500, content=error_response(500, "AI 管家没有返回可用回答。"))
|
|
return success_response(payload)
|
|
|
|
|
|
@router.get("/ai-concierge/sessions")
|
|
async def list_ai_concierge_sessions(
|
|
date_from: datetime | None = Query(default=None),
|
|
date_to: datetime | None = Query(default=None),
|
|
page: int = Query(default=1, ge=1),
|
|
page_size: int = Query(default=20, ge=1, le=100),
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
query = QASessionListQuery(
|
|
date_from=date_from,
|
|
date_to=date_to,
|
|
page=page,
|
|
page_size=page_size,
|
|
)
|
|
service = AdaptiveRAGService(db, tenant_id=int(current_user.tenant_id))
|
|
result = await service.list_qa_sessions(
|
|
user_id=int(current_user.id),
|
|
date_from=query.date_from,
|
|
date_to=query.date_to,
|
|
page=query.page,
|
|
page_size=query.page_size,
|
|
)
|
|
return success_response(result)
|
|
|
|
|
|
@router.get("/ai-concierge/sessions/{session_id}")
|
|
async def get_ai_concierge_session(
|
|
session_id: int,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
service = AdaptiveRAGService(db, tenant_id=int(current_user.tenant_id))
|
|
try:
|
|
result = await service.get_qa_session_detail(session_id=session_id, user_id=int(current_user.id))
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
return success_response(result)
|
|
|
|
|
|
@router.get("/saved-ideas")
|
|
async def list_saved_ideas(
|
|
limit: int = Query(default=20, ge=1, le=100),
|
|
include_archived: bool = False,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
service = SpaceSavedIdeaService(db, tenant_id=int(current_user.tenant_id))
|
|
items = await service.list_ideas(
|
|
user_id=int(current_user.id),
|
|
limit=limit,
|
|
include_archived=include_archived,
|
|
)
|
|
return success_response(SavedIdeaListResponse(items=items).model_dump(mode="json"))
|
|
|
|
|
|
@router.post("/saved-ideas")
|
|
async def create_saved_idea(
|
|
body: SavedIdeaCreateRequest,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
service = SpaceSavedIdeaService(db, tenant_id=int(current_user.tenant_id))
|
|
try:
|
|
item = await service.create_idea(body=body, user_id=int(current_user.id))
|
|
except SavedIdeaError as exc:
|
|
return JSONResponse(status_code=exc.status_code, content=error_response(exc.status_code, f"{exc.code}: {exc.message}"))
|
|
return success_response(item.model_dump(mode="json"))
|
|
|
|
|
|
@router.patch("/saved-ideas/{idea_id}")
|
|
async def update_saved_idea(
|
|
idea_id: str,
|
|
body: SavedIdeaUpdateRequest,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
service = SpaceSavedIdeaService(db, tenant_id=int(current_user.tenant_id))
|
|
try:
|
|
item = await service.update_idea(idea_id=idea_id, body=body, user_id=int(current_user.id))
|
|
except SavedIdeaError as exc:
|
|
return JSONResponse(status_code=exc.status_code, content=error_response(exc.status_code, f"{exc.code}: {exc.message}"))
|
|
return success_response(item.model_dump(mode="json"))
|
|
|
|
|
|
@router.post("/saved-ideas/{idea_id}/archive")
|
|
async def archive_saved_idea(
|
|
idea_id: str,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
service = SpaceSavedIdeaService(db, tenant_id=int(current_user.tenant_id))
|
|
try:
|
|
item = await service.archive_idea(idea_id=idea_id, user_id=int(current_user.id))
|
|
except SavedIdeaError as exc:
|
|
return JSONResponse(status_code=exc.status_code, content=error_response(exc.status_code, f"{exc.code}: {exc.message}"))
|
|
return success_response(item.model_dump(mode="json"))
|
|
|
|
|
|
@router.post("/saved-ideas/{idea_id}/promote")
|
|
async def promote_saved_idea(
|
|
idea_id: str,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
service = SpaceSavedIdeaService(db, tenant_id=int(current_user.tenant_id))
|
|
try:
|
|
idea, draft = await service.promote_to_launch_draft(idea_id=idea_id, user_id=int(current_user.id))
|
|
except SavedIdeaError as exc:
|
|
return JSONResponse(status_code=exc.status_code, content=error_response(exc.status_code, f"{exc.code}: {exc.message}"))
|
|
except PMFlowLaunchError as exc:
|
|
return JSONResponse(status_code=exc.status_code, content=error_response(exc.status_code, f"{exc.code}: {exc.message}"))
|
|
payload = SavedIdeaPromoteResponse(
|
|
idea=idea,
|
|
draft=draft,
|
|
redirect_url="/project-launch?state=human_review",
|
|
)
|
|
return success_response(payload.model_dump(mode="json"))
|
|
|
|
|
|
__all__ = ["router"]
|