From 0a9f7f7cb633ef175cbab5c61d8da5a8d52978c8 Mon Sep 17 00:00:00 2001 From: Abel Legese <73869888+Abellegese@users.noreply.github.com> Date: Sun, 9 Mar 2025 14:54:05 +0300 Subject: [PATCH] Hotfix for server title update and other stuff (#60) * some unittest fixes * adding static folder * creating info file * fix for server title, caching --- src/ersilia_pack/templates/app.py | 12 +++++------- src/ersilia_pack/templates/default.py | 5 ++++- src/ersilia_pack/templates/utils.py | 28 ++++++++++++++++++--------- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/ersilia_pack/templates/app.py b/src/ersilia_pack/templates/app.py index 5852910..f44f2ae 100644 --- a/src/ersilia_pack/templates/app.py +++ b/src/ersilia_pack/templates/app.py @@ -1,8 +1,6 @@ import sys -from typing import Any, Dict - -from fastapi import Depends, FastAPI, Request +from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from prometheus_fastapi_instrumentator import Instrumentator, metrics from slowapi.middleware import SlowAPIMiddleware @@ -17,16 +15,16 @@ from .exceptions.handlers import register_exception_handlers from .middleware.rcontext import RequestContextMiddleware from .routers import docs, metadata, run, health -from .utils import get_metadata, create_limiter, init_redis +from .utils import get_sync_metadata, create_limiter, init_redis sys.path.insert(0, ROOT) limiter = create_limiter() - +metadata_card = get_sync_metadata()["card"] app = FastAPI( - title="Ersilia Pack Model Server", - description=API_DESCIPTION, + title=f"{metadata_card['Identifier']}:{metadata_card['Slug']} Server", + description=f"{metadata_card['Title']}. {API_DESCIPTION}", docs_url=None, redoc_url=None, ) diff --git a/src/ersilia_pack/templates/default.py b/src/ersilia_pack/templates/default.py index 118df39..7df2a93 100644 --- a/src/ersilia_pack/templates/default.py +++ b/src/ersilia_pack/templates/default.py @@ -22,7 +22,7 @@ REDOC_JS_URL = "https://unpkg.com/redoc@next/bundles/redoc.standalone.js" MATE_CSS_URL = "https://cdn.jsdelivr.net/gh/ajatkj/swagger-ui-improved-theme/css/swagger-ui-improved.css" API_DESCIPTION = """ -Ersilia Pack is an open-source model serving framework built for researchers and scientists, offering endpoints for seamless model deployment and monitoring. It provides `/metrics` for Prometheus-based performance insights, `/metadata`\n for detailed model information, and `/info` to quickly retrieve the prediction endpoint name. The core `/run` endpoint offers sub-routes for example inputs/outputs, input/output column details, and a POST method that handles\n predictions with dynamic resource planning and multiprocessing. Additionally, the `/health` endpoint delivers system status and circuit breaker information—which trips after five consecutive failures with a 30-second reset—while enforcing a rate-\nlimit of 100 requests per minute. +This server offers endpoints for seamless model deployment and monitoring. It provides `/metrics` for Prometheus-based performance insights, `/metadata`\n for detailed model information, and `/info` to quickly retrieve the prediction endpoint name. The core `/run` endpoint offers sub-routes for example inputs/outputs, input/output column details, and a POST method that handles\n predictions with dynamic resource planning and multiprocessing. Additionally, the `/health` endpoint delivers system status and circuit breaker information—which trips after five consecutive failures with a 30-second reset—while enforcing a rate-\nlimit of 100 requests per minute. """ FRAMEWORK_FOLDER = os.path.abspath(os.path.join(ROOT, "..", "model", "framework")) MODEL_ROOT = os.path.abspath(os.path.join(ROOT, "..", "model")) @@ -76,8 +76,11 @@ class OrientEnum(str, Enum): class CardField(str, Enum): identifier = "Identifier" slug = "Slug" + title = "Title" description = "Description" input = "Input" + task = "Task" + subtask = "Subtask" API_ID = str(uuid.uuid4()) diff --git a/src/ersilia_pack/templates/utils.py b/src/ersilia_pack/templates/utils.py index 46de000..9de063a 100644 --- a/src/ersilia_pack/templates/utils.py +++ b/src/ersilia_pack/templates/utils.py @@ -123,8 +123,7 @@ def values_serializer(values): record = collections.OrderedDict() for j in range(len(columns)): record[columns[j]] = values_serializer([values[i][j]])[0] - key = make_hashable(index[i]) - data[key] = record + data[index[i]] = record return data elif orient == "columns": @@ -177,7 +176,7 @@ def get_api_names_from_sh(framework_dir): def get_example_path(example_file): example_path = os.path.join(FRAMEWORK_FOLDER, "examples", example_file) api_name = get_api_names_from_sh(FRAMEWORK_FOLDER) - if api_name: + if api_name: api_name = api_name[0] if not os.path.exists(example_path): example_path = os.path.join( @@ -203,6 +202,12 @@ async def get_metadata(): return data["card"] +def get_sync_metadata(): + file_path = os.path.join(BUNDLE_FOLDER, "information.json") + contents = _read_file(file_path) + return json.loads(contents) + + def read_example(): example_input_path = get_example_path(generic_example_input_file) if not os.path.exists(example_input_path): @@ -301,14 +306,19 @@ def get_cpu_count(logical): def run_in_parallel(num_workers, tag, chunks): + num_tasks = len(chunks) + chunksize = max(1, num_tasks // (num_workers * 4)) + with multiprocessing.Pool(processes=num_workers) as pool: chunk_args = [(chunk, idx, tag) for idx, chunk in enumerate(chunks)] - processed = pool.starmap_async(process_chunk, chunk_args, chunksize=1) - _results = processed.get() - results, headers = [], [] - for result, header in _results: - results.extend(result) - headers.append(header) + async_result = pool.starmap_async(process_chunk, chunk_args, chunksize=chunksize) + results_headers = async_result.get() + + results, headers = [], [] + for result, header in results_headers: + results.extend(result) + headers.append(header) + return results, headers[0]