152 lines
5.7 KiB
Python
152 lines
5.7 KiB
Python
from __future__ import annotations
|
|
|
|
from .shared import *
|
|
from .helpers import _ensure_project_signal_link
|
|
from .schemas import SignalSourceListResponse, SignalSourceOnboardRequest, SignalSourceRepairRequest
|
|
|
|
@router.post("/rag/ask")
|
|
async def ask_ai_steward_rag(
|
|
request: RAGAskRequest,
|
|
current_user: User = Depends(get_current_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)
|
|
service = AdaptiveRAGService(db, tenant_id=effective_tenant_id)
|
|
result = await service.answer_question(
|
|
question=request.question,
|
|
asked_by=int(current_user.id),
|
|
asked_via="pmflow",
|
|
project_id=request.project_id,
|
|
source_chat_id=request.source_chat_id,
|
|
session_id=request.session_id,
|
|
force_new_session=bool(request.new_session),
|
|
thinking_mode=request.thinking_mode,
|
|
action_mode=request.action_mode,
|
|
sync_requirement=bool(request.sync_requirement),
|
|
)
|
|
return success_response(result)
|
|
|
|
@router.post("/signal-sources/onboard")
|
|
@audited_write(caller="user")
|
|
async def onboard_signal_source(
|
|
request: SignalSourceOnboardRequest,
|
|
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)
|
|
signal = Signal(
|
|
tenant_id=effective_tenant_id,
|
|
source="PMFlow",
|
|
content=f"[PMFlow用户#{int(current_user.id)}]: {request.instruction[:1800]}",
|
|
impact_score=0.7,
|
|
priority="medium",
|
|
processed=False,
|
|
strategic_analysis=json.dumps(
|
|
{
|
|
"source": "pmflow_manual_signal_source_onboard",
|
|
"instruction": request.instruction[:2000],
|
|
"created_at": now_cn_aware().isoformat(),
|
|
},
|
|
ensure_ascii=False,
|
|
),
|
|
tasks_generated=json.dumps([], ensure_ascii=False),
|
|
matched_project_id=int(request.project_id),
|
|
title=(request.instruction[:80] or "新增信号源"),
|
|
signal_type="新需求",
|
|
summary=request.instruction[:300],
|
|
reasoning=json.dumps(["来自 AI Steward 手动信号源接入请求。"], ensure_ascii=False),
|
|
confidence=0.82,
|
|
recommended_action="enter_only",
|
|
status="待处理",
|
|
)
|
|
if not request.dry_run:
|
|
db.add(signal)
|
|
await db.flush()
|
|
await _ensure_project_signal_link(
|
|
db,
|
|
tenant_id=effective_tenant_id,
|
|
project_id=int(request.project_id),
|
|
signal_id=int(signal.id),
|
|
)
|
|
await db.commit()
|
|
await db.refresh(signal)
|
|
signal_id_value = int(signal.id) if not request.dry_run and getattr(signal, "id", None) is not None else None
|
|
|
|
service = SignalSourceOrchestrationService(db)
|
|
result = await service.orchestrate_from_signal(
|
|
tenant_id=effective_tenant_id,
|
|
project_id=int(request.project_id),
|
|
signal_id=signal_id_value,
|
|
initiating_text=request.instruction,
|
|
requested_via="pmflow",
|
|
operator_user_id=int(current_user.id),
|
|
dry_run=bool(request.dry_run),
|
|
response_mode=request.response_mode,
|
|
)
|
|
return success_response(
|
|
{
|
|
"signal_id": signal_id_value,
|
|
**result,
|
|
}
|
|
)
|
|
|
|
@router.get("/orchestration-runs/{trace_id}")
|
|
async def get_orchestration_run(
|
|
trace_id: str,
|
|
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)
|
|
row = await AIOrchestrationRunService(db).get_run(
|
|
tenant_id=effective_tenant_id,
|
|
trace_id=str(trace_id or "").strip(),
|
|
)
|
|
if row is None:
|
|
raise HTTPException(status_code=404, detail="orchestration run not found")
|
|
return success_response(AIOrchestrationRunService.serialize_run(row))
|
|
|
|
@router.get("/projects/{project_id}/signal-sources")
|
|
async def list_project_signal_sources(
|
|
project_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)
|
|
items = await SignalSourceOrchestrationService(db).list_project_sources(
|
|
tenant_id=effective_tenant_id,
|
|
project_id=int(project_id),
|
|
)
|
|
return success_response(SignalSourceListResponse(items=items).model_dump())
|
|
|
|
@router.post("/signal-sources/repair-dirty")
|
|
@audited_write(caller="system")
|
|
async def repair_dirty_signal_sources(
|
|
request: SignalSourceRepairRequest,
|
|
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:
|
|
rows = await SignalSourceRepairService(db).repair_registries(
|
|
tenant_id=effective_tenant_id,
|
|
project_id=int(request.project_id),
|
|
registry_ids=[int(item) for item in request.registry_ids],
|
|
sender=str(request.sender or "PMFlow修复器").strip()[:64] or "PMFlow修复器",
|
|
)
|
|
except SignalSourceRepairTimeoutError as exc:
|
|
raise HTTPException(status_code=504, detail=str(exc)) from exc
|
|
except SignalSourceRepairMissingSignalError as exc:
|
|
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
|
return success_response(
|
|
{
|
|
"project_id": int(request.project_id),
|
|
"items": rows,
|
|
}
|
|
)
|
|
|