/
Vladimir_Py
/
cp_generator
Обзор
Документация
Войти
/
Vladimir_Py
/
cp_generator
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
app.py
117 строк
5 KB
Test Bot
version 0 pre deploy
04 май 2026, 00:22
04 май 2026, 00:22
d68b192
Код
Авторство
О чём код?
import uvicorn import logging from fastapi import FastAPI, Form, Request from fastapi.responses import StreamingResponse, JSONResponse from fastapi.exceptions import RequestValidationError from starlette.exceptions import HTTPException as StarletteHTTPException from starlette.templating import Jinja2Templates from backend.exceptions import RateLimitExceeded from backend.rate_limit import check_rate_limit from backend.ai_services import run_classifier, run_crew_agents from backend.stream_handlers import stream_rejection, stream_crew_pipeline, stream_error from src.file_utils import read_json # Настраиваем базовый логгер для всего проекта logging.basicConfig(level=logging.DEBUG, filename='app.log', format="%(asctime)s - %(levelname)s - %(name)s - %(message)s") logger = logging.getLogger(__name__) # загрузка сообщений для отказа try: rejection_messages = read_json('backend/exclused_answers.json') logger.debug(f'Загружено {len(rejection_messages)} сообщений для отказа.') except Exception as e: logger.warning(f'Не удалось загрузить сообщения для отказа. Применяем дефолтные. Ошибка: {e}') rejection_messages = {} app = FastAPI() templates = Jinja2Templates(directory="frontend/templates") # ========================================== # ЭНДПОИНТЫ ДЛЯ СТРАНИЦ (ФРОНТЕНД) # ========================================== # Главная страница. Форма ввода ТЗ @app.get("/") async def read_index(request: Request): # Отдаем главную страницу return templates.TemplateResponse({"request": request}, "index.html") # Страница с результатом @app.get("/result") async def read_result(request: Request): return templates.TemplateResponse({"request": request}, "result.html", ) # ========================================== # ГЛОБАЛЬНЫЕ ОБРАБОТЧИКИ ОШИБОК # ========================================== # Обработка 404 (Неверный URL) и других HTTP ошибок @app.exception_handler(StarletteHTTPException) async def http_exception_handler(request: Request, exc: StarletteHTTPException): logger.warning(f"Неверный запрос: {request.method} {request.url} - {exc.detail}") return JSONResponse( status_code=exc.status_code, content={"error": True, "message": f"Эндпоинт не найден или недоступен: {exc.detail}"} ) # Обработка ошибок валидации (например, не передали поле text) @app.exception_handler(RequestValidationError) async def validation_exception_handler(request: Request, exc: RequestValidationError): logger.error(f"Ошибка валидации данных от клиента: {exc}") return JSONResponse( status_code=422, content={"error": True, "message": "Переданы некорректные данные."} ) # Глобальный перехватчик вообще любых 500-х ошибок (Global Fallback) @app.exception_handler(Exception) async def global_exception_handler(request: Request, exc: Exception): logger.exception(f"НЕПРЕДВИДЕННАЯ ОШИБКА СЕРВЕРА при обращении к {request.url}:") return JSONResponse( status_code=500, content={"error": True, "message": "Внутренняя ошибка сервера. Мы уже чиним!"} ) # ========================================== # ЭНДПОИНТЫ # ========================================== @app.post("/api/generate_cp") async def generate_endpoint(request: Request, tz_text: str = Form(...)): user_input = tz_text.strip() # 0. Проверяем лимиты ДО начала работы try: await check_rate_limit(request) except RateLimitExceeded as e: # Для лимитов отдаем stream_error (останется на главной) return StreamingResponse(stream_error(str(e)), media_type="text/event-stream") if not user_input: # Для пустого ТЗ тоже отдаем stream_error return StreamingResponse(stream_error("ТЗ не может быть пустым"), media_type="text/event-stream") # 1. Запускаем классификатор (в отдельном потоке) logger.debug(f"Запускаем эндпоинт классификации") classification = await run_classifier(user_input) if classification is None: raise ValueError("Классификатор вернул None") # 2. Если проект нецелевой — возвращаем стрим отказа (stream_rejection с JSON) if not classification.is_target: exclused_answer = rejection_messages.get(classification.category_key, "Проект не относится к целевым.") return StreamingResponse( stream_rejection(exclused_answer), media_type="text/event-stream" ) # 3. Если целевой — возвращаем стрим работы агентов return StreamingResponse( stream_crew_pipeline(text=user_input, category=classification.category_key, run_agents_func=run_crew_agents), media_type="text/event-stream" ) if __name__ == "__main__": uvicorn.run("app:app", host="127.0.0.1", port=8000, reload=True)