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()