Files

313 lines
5.6 KiB
Python

from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import RedirectResponse
from app.analytics_store import analytics_store
from app.assistant_engine import get_default_profile, process_message
from app.config import settings
from app.data.knowledge_base import QUICK_PROMPTS
from app.data.mock_data import get_services
from app.llm.client import get_llm_client
from app.models import (
AnalyticsSummary,
Appeal,
AssistantResult,
ChatRequest,
CreateAppealRequest,
FeedbackRequest,
NotificationItem,
ServiceItem,
UserProfile,
)
from app.notification_store import notification_store
from app.rag.service import init_local_rag
from app.store import appeal_store
_rag_status: dict[str, object] = {}
@asynccontextmanager
async def lifespan(app: FastAPI):
global _rag_status
_rag_status = init_local_rag()
yield
app = FastAPI(
title="ВКУС IT Assistant API",
description="Backend витрины корпоративных услуг — ядро обработки запросов",
version="1.0.0",
lifespan=lifespan,
)
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173", "http://127.0.0.1:5173"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
def root():
"""Корень backend — перенаправление на витрину (frontend)."""
return RedirectResponse(url="http://127.0.0.1:5173")
@app.get("/api")
def api_root() -> dict[str, str]:
return {
"message": "ВКУС IT Assistant API",
"docs": "/docs",
"health": "/api/health",
"frontend": "http://localhost:5173",
}
@app.get("/api/llm/test")
def test_llm() -> dict[str, object]:
result = get_llm_client().test_connection()
return {
"ok": result.ok,
"configured": result.configured,
"enabled": settings.llm_enabled,
"model": result.model,
"endpoint": result.endpoint,
"message": result.message,
"sample": result.sample,
"statusCode": result.status_code,
"error": result.error,
}
@app.get("/api/health")
def health() -> dict[str, object]:
llm = get_llm_client()
return {
"status": "ok",
"rag": {
**_rag_status,
"note": "Временный локальный RAG в backend. Целевой контур — платформа ИИ.",
},
"llm": {
"enabled": settings.llm_enabled,
"configured": llm.is_configured(),
"model": settings.llm_model,
"endpoint": settings.llm_chat_url,
},
}
@app.get("/api/profile", response_model=UserProfile)
def get_profile() -> UserProfile:
return get_default_profile()
@app.get("/api/prompts", response_model=list[str])
def get_prompts() -> list[str]:
return QUICK_PROMPTS
@app.post("/api/chat", response_model=AssistantResult)
def chat(request: ChatRequest) -> AssistantResult:
profile = get_default_profile()
conversation = [turn.model_dump() for turn in request.conversation]
return process_message(request.message, profile, request.is_first_message, conversation)
@app.get("/api/appeals", response_model=list[Appeal])
def list_appeals() -> list[Appeal]:
return appeal_store.list_all()
@app.post("/api/appeals", response_model=Appeal)
def create_appeal(request: CreateAppealRequest) -> Appeal:
appeal = appeal_store.create(request)
notification_store.add(
title="Обращение создано",
text=f"{appeal.number}: {appeal.title}. Отслеживайте статус во вкладке «История моих обращений».",
link_tab="appeals",
)
return appeal
@app.post("/api/feedback")
def submit_feedback(request: FeedbackRequest) -> dict[str, object]:
analytics_store.record(request)
if request.helped:
notification_store.add(
title="Спасибо за отзыв",
text="Ваш ответ «Помогло» учтён и поможет улучшить сервис.",
link_tab="vitrina",
)
else:
notification_store.add(
title="Переход к оформлению обращения",
text="Ответ не помог — подготовьте заявку с контекстом диалога.",
link_tab="vitrina",
)
return {"ok": True, "analytics": analytics_store.summary().model_dump(by_alias=True)}
@app.get("/api/analytics/summary", response_model=AnalyticsSummary)
def analytics_summary() -> AnalyticsSummary:
return analytics_store.summary()
@app.get("/api/notifications", response_model=list[NotificationItem])
def list_notifications() -> list[NotificationItem]:
return notification_store.list_all()
@app.get("/api/notifications/unread-count")
def notifications_unread_count() -> dict[str, int]:
return {"count": notification_store.unread_count()}
@app.post("/api/notifications/{notification_id}/read")
def mark_notification_read(notification_id: str) -> dict[str, bool]:
return {"ok": notification_store.mark_read(notification_id)}
@app.post("/api/notifications/read-all")
def mark_all_notifications_read() -> dict[str, int]:
return {"marked": notification_store.mark_all_read()}
@app.get("/api/services", response_model=list[ServiceItem])
def list_services() -> list[ServiceItem]:
return get_services()