From a66a6bc153826bb7d3d5a7bc73f51c4ed29612c8 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Mon, 3 Aug 2026 21:41:25 +0300 Subject: [PATCH] (feat) add compression to bots data --- main.py | 5 +++++ routers/bot_orchestration.py | 7 ++++++- services/bots_orchestrator.py | 26 ++++++++++++++++++++------ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/main.py b/main.py index e086e107..54275db4 100644 --- a/main.py +++ b/main.py @@ -31,6 +31,7 @@ def patched_save_to_yml(yml_path, cm): from fastapi import Depends, FastAPI, HTTPException, Request, status # noqa: E402 from fastapi.exceptions import RequestValidationError # noqa: E402 from fastapi.middleware.cors import CORSMiddleware # noqa: E402 +from fastapi.middleware.gzip import GZipMiddleware # noqa: E402 from fastapi.responses import JSONResponse # noqa: E402 from fastapi.security import HTTPBasic, HTTPBasicCredentials # noqa: E402 from hummingbot.client.config.client_config_map import GatewayConfigMap # noqa: E402 @@ -364,6 +365,10 @@ async def lifespan(app: FastAPI): allow_headers=settings.cors.allow_headers, ) +# Compress responses for clients that send Accept-Encoding: gzip. These payloads are highly +# repetitive JSON and compress ~14x; small responses are left alone via minimum_size. +app.add_middleware(GZipMiddleware, minimum_size=1000) + @app.exception_handler(RequestValidationError) async def validation_exception_handler(request: Request, exc: RequestValidationError): diff --git a/routers/bot_orchestration.py b/routers/bot_orchestration.py index ec48d66f..ed76b98d 100644 --- a/routers/bot_orchestration.py +++ b/routers/bot_orchestration.py @@ -256,6 +256,7 @@ async def get_bot_runs( deployment_status: str = None, limit: int = 100, offset: int = 0, + include_final_status: bool = False, bots_manager: BotsOrchestrator = Depends(get_bots_orchestrator) ): """ @@ -270,6 +271,9 @@ async def get_bot_runs( deployment_status: Filter by deployment status (DEPLOYED, FAILED, ARCHIVED) limit: Maximum number of results to return offset: Number of results to skip + include_final_status: Include the final status snapshot for each run. Off by + default because the blob can be ~89 KB per record (~99% of the payload); + use GET /bot-runs/{bot_run_id} to fetch it for a single run. bots_manager: Bot orchestrator service dependency Returns: @@ -284,7 +288,8 @@ async def get_bot_runs( run_status=run_status, deployment_status=deployment_status, limit=limit, - offset=offset + offset=offset, + include_final_status=include_final_status ) return { diff --git a/services/bots_orchestrator.py b/services/bots_orchestrator.py index b274a5c2..6392701e 100644 --- a/services/bots_orchestrator.py +++ b/services/bots_orchestrator.py @@ -482,8 +482,13 @@ async def get_bot_runs( deployment_status: Optional[str] = None, limit: int = 100, offset: int = 0, + include_final_status: bool = False, ) -> List[Dict]: - """Get bot runs with optional filtering, serialized as dictionaries.""" + """Get bot runs with optional filtering, serialized as dictionaries. + + ``final_status`` is omitted by default: the blob is ~99% of a run record's + bytes (up to ~89 KB each) and is only meaningful on the detail endpoint. + """ async with self.db_manager.get_session_context() as session: bot_run_repo = BotRunRepository(session) bot_runs = await bot_run_repo.get_bot_runs( @@ -496,7 +501,10 @@ async def get_bot_runs( limit=limit, offset=offset, ) - return [self._serialize_bot_run(run) for run in bot_runs] + return [ + self._serialize_bot_run(run, include_final_status=include_final_status) + for run in bot_runs + ] async def get_bot_run_stats(self) -> Dict[str, Any]: """Get statistics about bot runs.""" @@ -560,9 +568,13 @@ async def create_bot_run(self, **kwargs): # Don't fail the deployment if bot run creation fails @staticmethod - def _serialize_bot_run(run) -> Dict: - """Serialize a BotRun ORM object into a JSON-friendly dictionary.""" - return { + def _serialize_bot_run(run, include_final_status: bool = True) -> Dict: + """Serialize a BotRun ORM object into a JSON-friendly dictionary. + + Set ``include_final_status=False`` to drop the (large) final status blob, + e.g. for list responses. + """ + serialized = { "id": run.id, "bot_name": run.bot_name, "instance_name": run.instance_name, @@ -576,9 +588,11 @@ def _serialize_bot_run(run) -> Dict: "deployment_status": run.deployment_status, "run_status": run.run_status, "deployment_config": run.deployment_config, - "final_status": run.final_status, "error_message": run.error_message, } + if include_final_status: + serialized["final_status"] = run.final_status + return serialized # ============================================ # Stop & Archive orchestration