149 lines
5.4 KiB
Python
149 lines
5.4 KiB
Python
from __future__ import annotations
|
|
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from .shared import *
|
|
from .helpers import _resolve_tenant_id
|
|
from .. import rag_ask_contract
|
|
from ...core.response import ErrorCode, error_response
|
|
from ...services.ai_pmo.ai_pmo_kernel import AIPMOKernelService
|
|
|
|
@router.post("/ask")
|
|
async def rag_ask(
|
|
request: RAGAskRequest,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
service = AIPMOKernelService(
|
|
db,
|
|
tenant_id=rag_ask_contract.resolve_effective_tenant_id(current_user, tenant_id),
|
|
)
|
|
ingress_result = await service.process_ingress(
|
|
**rag_ask_contract.build_kernel_ingress_kwargs(
|
|
request=request,
|
|
current_user=current_user,
|
|
)
|
|
)
|
|
if not bool(ingress_result.get("handled")):
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=str(ingress_result.get("reason") or "ai_pmo_kernel_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(ErrorCode.INTERNAL_ERROR, "RAG 返回空回答,未生成有效响应。"),
|
|
)
|
|
return success_response(payload)
|
|
|
|
@router.get("/manifest")
|
|
async def rag_manifest(
|
|
project_id: int | None = Query(default=None),
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
data = await service.get_project_manifest(project_id=project_id)
|
|
return success_response(data)
|
|
|
|
@router.get("/features/catalog")
|
|
async def rag_feature_catalog(
|
|
query: str | None = Query(default=None),
|
|
limit: int = Query(default=20, ge=1, le=200),
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
data = await service.get_feature_catalog(query=query, limit=limit)
|
|
return success_response(data)
|
|
|
|
@router.post("/entity-resolution")
|
|
async def rag_entity_resolution(
|
|
request: EntityResolutionRequest,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
data = await service.entity_resolution(
|
|
question=request.question,
|
|
project_id=request.project_id,
|
|
limit=request.limit,
|
|
)
|
|
return success_response(data)
|
|
|
|
@router.post("/multi-step-search")
|
|
async def rag_multi_step_search(
|
|
request: MultiStepSearchRequest,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
data = await service.multi_step_search(
|
|
question=request.question,
|
|
project_id=request.project_id,
|
|
source_chat_id=request.source_chat_id,
|
|
session_state=None,
|
|
)
|
|
return success_response(data)
|
|
|
|
@router.post("/symbols/sync")
|
|
async def rag_symbols_sync(
|
|
request: SymbolSyncRequest,
|
|
current_user: User = Depends(require_roles("REVIEWER", "OWNER")),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
service = SymbolIndexService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
data = await service.sync_codebase_symbols(
|
|
base_dir=request.base_dir,
|
|
max_files=request.max_files,
|
|
force_reindex=request.force_reindex,
|
|
)
|
|
return success_response(data)
|
|
|
|
@router.post("/symbols/search")
|
|
async def rag_symbols_search(
|
|
request: SymbolSearchRequest,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
service = SymbolIndexService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
data = await service.search_symbols(
|
|
query=request.query,
|
|
language=request.language,
|
|
symbol_type=request.symbol_type,
|
|
limit=request.limit,
|
|
)
|
|
return success_response(data)
|
|
|
|
@router.get("/symbols/overview")
|
|
async def rag_symbols_overview(
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
service = SymbolIndexService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
data = await service.get_overview(top_n_files=10, auto_sync_if_empty=False)
|
|
return success_response(data)
|
|
|
|
@router.get("/symbols/{symbol_id}")
|
|
async def rag_symbol_detail(
|
|
symbol_id: int,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
service = SymbolIndexService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
try:
|
|
data = await service.get_symbol_detail(symbol_id=symbol_id)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
return success_response(data)
|