local_service.py
1 import logging 2 import os 3 import time 4 from typing import Dict 5 from typing import Optional 6 7 import litestar 8 from litestar import Litestar 9 from litestar import Request 10 from litestar import Response 11 from litestar.di import Provide 12 from litestar.logging import LoggingConfig 13 14 from evidently.errors import EvidentlyError 15 from evidently.ui.service.api.artifacts import artifacts_router 16 from evidently.ui.service.api.llm_judges import llm_judges_router 17 from evidently.ui.service.api.projects import create_projects_api 18 from evidently.ui.service.api.projects import projects_api_dependencies 19 from evidently.ui.service.api.prompts import prompts_router 20 from evidently.ui.service.api.service import service_api 21 from evidently.ui.service.api.static import assets_router 22 from evidently.ui.service.components.base import AppBuilder 23 from evidently.ui.service.components.base import Component 24 from evidently.ui.service.components.base import ComponentContext 25 from evidently.ui.service.components.base import ServiceComponent 26 from evidently.ui.service.components.dashboard import DashboardComponent 27 from evidently.ui.service.components.datasets import DatasetComponent 28 from evidently.ui.service.components.security import NoSecurityComponent 29 from evidently.ui.service.components.security import SecurityComponent 30 from evidently.ui.service.components.storage import LocalStorageComponent 31 from evidently.ui.service.components.storage import StorageComponent 32 from evidently.ui.service.components.telemetry import TelemetryComponent 33 from evidently.ui.service.components.tracing import TracingComponent 34 from evidently.ui.service.config import AppConfig 35 from evidently.ui.service.config import ConfigContext 36 from evidently.ui.service.errors import EvidentlyServiceError 37 38 39 def evidently_service_exception_handler(_: Request, exc: EvidentlyServiceError) -> Response: 40 return exc.to_response() 41 42 43 def evidently_exception_handler(_: Request, exc: EvidentlyError) -> Response: 44 return Response(content={"detail": exc.get_message()}, status_code=500) 45 46 47 class LocalServiceComponent(ServiceComponent): 48 debug: bool = False 49 50 @property 51 def debug_enabled(self) -> bool: 52 return self.debug or os.environ.get("EVIDENTLY_DEBUG") is not None 53 54 def get_api_route_handlers(self, ctx: ComponentContext): 55 guard = ctx.get_component(SecurityComponent).get_auth_guard() 56 return [ 57 create_projects_api(guard), 58 service_api(), 59 artifacts_router(guard), 60 prompts_router(guard), 61 llm_judges_router(), 62 ] 63 64 def get_dependencies(self, ctx: ComponentContext) -> Dict[str, Provide]: 65 from evidently.ui.service.managers.artifacts import ArtifactManager 66 67 deps = super().get_dependencies(ctx) 68 deps.update(projects_api_dependencies) 69 deps["artifact_manager"] = Provide(ArtifactManager.provide) 70 return deps 71 72 def get_route_handlers(self, ctx: ComponentContext): 73 return [assets_router()] 74 75 def apply(self, ctx: ComponentContext, builder: AppBuilder): 76 super().apply(ctx, builder) 77 assert isinstance(ctx, ConfigContext) 78 builder.exception_handlers[EvidentlyServiceError] = evidently_service_exception_handler 79 builder.exception_handlers[EvidentlyError] = evidently_exception_handler 80 builder.kwargs["debug"] = self.debug_enabled 81 if self.debug_enabled: 82 log_config = create_logging() 83 builder.kwargs["logging_config"] = LoggingConfig(**log_config) 84 85 86 def create_logging() -> dict: 87 logging.Formatter.converter = time.gmtime 88 return { 89 "version": 1, 90 "log_exceptions": "always", 91 "disable_existing_loggers": False, 92 "formatters": { 93 "default": { 94 "()": "logging.Formatter", 95 "format": "%(asctime)s %(levelname)-8s %(name)-15s %(message)s", 96 "datefmt": "%Y-%m-%dT%H:%M:%SZ", 97 }, 98 "access": { 99 "()": "logging.Formatter", 100 "format": "%(asctime)s %(levelname)-8s %(name)-15s %(message)s", 101 "datefmt": "%Y-%m-%dT%H:%M:%SZ", 102 }, 103 "standard": { 104 "()": "logging.Formatter", 105 "format": "%(asctime)s %(levelname)-8s %(name)-15s %(message)s", 106 "datefmt": "%Y-%m-%dT%H:%M:%SZ", 107 }, 108 }, 109 "handlers": { 110 "default": { 111 "formatter": "default", 112 "class": "logging.StreamHandler", 113 "stream": "ext://sys.stdout", 114 }, 115 "access": { 116 "formatter": "access", 117 "class": "logging.StreamHandler", 118 "stream": "ext://sys.stdout", 119 }, 120 }, 121 "loggers": { 122 "litestar": {"handlers": ["default"]}, 123 "uvicorn": {"handlers": ["default"], "level": "INFO", "propagate": False}, 124 "uvicorn.error": {"level": "INFO"}, 125 "uvicorn.access": {"handlers": ["access"], "level": "INFO", "propagate": False}, 126 }, 127 } 128 129 130 class LitestarComponent(Component): 131 __section__ = "litestar" 132 request_max_body_size: Optional[int] = None 133 134 def finalize(self, ctx: ComponentContext, app: Litestar): 135 if self.request_max_body_size is not None: 136 if hasattr(app, "request_max_body_size"): 137 app.request_max_body_size = self.request_max_body_size 138 else: 139 logging.warning( 140 f"Litestar version {litestar.__version__.formatted()}" 141 f" does not support 'request_max_body_size' parameter" 142 ) 143 144 145 class LocalConfig(AppConfig): 146 security: SecurityComponent = NoSecurityComponent() 147 service: ServiceComponent = LocalServiceComponent() 148 storage: StorageComponent = LocalStorageComponent() 149 telemetry: TelemetryComponent = TelemetryComponent() 150 dashboard: DashboardComponent = DashboardComponent() 151 datasets: DatasetComponent = DatasetComponent() 152 tracing: TracingComponent = TracingComponent() 153 litestar: LitestarComponent = LitestarComponent()