/
YoungFreddy
/
NetologyPipeLineService
Обзор
Документация
Войти
/
YoungFreddy
/
NetologyPipeLineService
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
api/main.py
166 строк
5 KB
Igor
Initial commit: LLM сервис с FastAPI, pipeline, ретраями, fallback, кэшем
18 июл 2026, 17:01
18 июл 2026, 17:01
5b26a48
Код
Авторство
О чём код?
"""FastAPI-эндпоинты сервиса: /, /chat, /cache/status.""" import json import logging import time from typing import Optional from fastapi import FastAPI, HTTPException from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse from pydantic import BaseModel, Field from config.loader import load_config from services.llm_service import LLMService logging.basicConfig( level=logging.INFO, format="%(message)s", handlers=[ logging.FileHandler("llm_service.log", encoding="utf-8"), logging.StreamHandler(), ], ) app = FastAPI(title="LLM Service API") _ERROR_MESSAGES = { "string_too_short": "должно содержать хотя бы {min_length} символ(а)", "string_too_long": "не может превышать {max_length} символов", "missing": "обязательное поле", "json_invalid": "невалидный JSON в теле запроса", } @app.exception_handler(RequestValidationError) def handle_validation_error(request, exc): first = exc.errors()[0] field = " -> ".join(str(l) for l in first["loc"][1:]) if first["loc"] else "body" err_type = first.get("type", "") msg = first.get("msg", "Ошибка валидации") ctx = first.get("ctx", {}) template = _ERROR_MESSAGES.get(err_type) if template: detail = f"Поле '{field}' {template.format(**ctx)}" else: detail = f"Поле '{field}': {msg}" logging.getLogger(__name__).error( json.dumps( { "event": "validation_error", "timestamp": time.time(), "field": field, "error_type": err_type, "detail": detail, }, ensure_ascii=False, ) ) return JSONResponse( status_code=422, content={"error": "validation_error", "detail": detail}, ) _service: Optional[LLMService] = None def get_service() -> LLMService: """Возвращает синглтон LLMService (ленивая инициализация).""" global _service if _service is None: cfg = load_config() _service = LLMService(config=cfg) return _service class MessageRequest(BaseModel): """Схема входящего запроса к /chat.""" message: str = Field( ..., min_length=1, max_length=1000, description="Сообщение для генерации" ) class MessageResponse(BaseModel): """Схема ответа /chat.""" response: str class CacheStatusResponse(BaseModel): """Схема ответа /cache/status.""" cache_size: int @app.get("/") def read_root(): """Корневой эндпоинт.""" return {"message": "Welcome to LLM Service API"} @app.post("/chat", response_model=MessageResponse) def chat(request: MessageRequest): """Принимает сообщение пользователя и возвращает ответ LLM.""" svc = get_service() start_time = time.time() try: response_text = svc.process_message(request.message) elapsed = time.time() - start_time logging.getLogger(__name__).info( json.dumps( { "event": "api_response", "timestamp": time.time(), "elapsed_seconds": round(elapsed, 3), "status": "success", }, ensure_ascii=False, ) ) return MessageResponse(response=response_text) except ValueError as e: elapsed = time.time() - start_time logging.getLogger(__name__).error( json.dumps( { "event": "api_error", "timestamp": time.time(), "elapsed_seconds": round(elapsed, 3), "error": str(e), "status": "validation_error", }, ensure_ascii=False, ) ) raise HTTPException(status_code=400, detail=str(e)) except Exception as e: elapsed = time.time() - start_time logging.getLogger(__name__).error( json.dumps( { "event": "api_error", "timestamp": time.time(), "elapsed_seconds": round(elapsed, 3), "error": str(e), "error_type": type(e).__name__, "status": "internal_error", }, ensure_ascii=False, ) ) raise HTTPException( status_code=503, detail="Сервис временно недоступен, попробуйте позже", ) @app.get("/cache/status") def cache_status(): """Возвращает текущий размер кэша.""" svc = get_service() return CacheStatusResponse(cache_size=len(svc.cache._cache))