Files
2026-07-13 12:24:33 +08:00

156 lines
4.8 KiB
Python

#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""
Reference implementation of a remote config server for LMCache.
This is a simple Flask-based config server that demonstrates the protocol
between LMCache workers and a centralized configuration service.
Usage:
python config_server.py [--port PORT] [--host HOST]
The server exposes a single endpoint:
POST /config?appId=<app_id>
See README.md for the full protocol specification.
"""
# Standard
from typing import Any
import argparse
import logging
# Third Party
from flask import Flask, jsonify, request
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Example configuration database
# In production, this could be fetched from a database, config file, or other service
CONFIG_DATABASE: dict[str, list[dict[str, Any]]] = {
# Default configs applied to all apps
"default": [
{"key": "chunk_size", "value": 512, "override": False},
{"key": "max_local_cpu_size", "value": 1, "override": False},
],
# App-specific configs
"test-app": [
{"key": "chunk_size", "value": 1024, "override": False},
{"key": "max_local_cpu_size", "value": 2, "override": False},
{"key": "lmcache_worker_heartbeat_time", "value": "14", "override": True},
{
"key": "extra_config",
"value": '{"internal_api_server_access_log": true, '
'"internal_api_server_log_level":"info", '
'"save_only_first_rank": true, '
'"first_rank_max_local_cpu_size": 1.1}',
"override": True,
},
],
"production-app": [
{"key": "chunk_size", "value": 2048, "override": True},
{"key": "max_local_cpu_size", "value": 100, "override": True},
],
}
def get_configs_for_app(
app_id: str | None,
current_config: dict[str, Any],
env_variables: dict[str, str],
) -> list[dict[str, Any]]:
"""
Determine which configs to return based on app_id and current state.
This is where you would implement your custom logic, such as:
- Looking up configs from a database
- Applying rules based on environment variables
- A/B testing different configurations
"""
# Use dict to deduplicate by key, later entries override earlier ones
config_map: dict[str, dict[str, Any]] = {}
# Start with default configs
if "default" in CONFIG_DATABASE:
for item in CONFIG_DATABASE["default"]:
config_map[item["key"]] = item
# Add app-specific configs (override defaults with same keys)
if app_id and app_id in CONFIG_DATABASE:
for item in CONFIG_DATABASE[app_id]:
config_map[item["key"]] = item
return list(config_map.values())
@app.route("/config", methods=["GET", "POST"])
def get_config():
"""
Handle config requests from LMCache workers.
Expected request body:
{
"current_config": {...},
"env_variables": {...}
}
Query parameters:
- appId: Optional application identifier
"""
try:
# Parse request - supports both JSON body and form data
data = request.get_json(silent=True)
if data is None:
# Fallback to form data or query parameters
data = request.form.to_dict() or request.args.to_dict()
current_config = data.get("current_config", {})
env_variables = data.get("env_variables", {})
app_id = request.args.get("appId") or data.get("appId")
logger.info(
"Config request received - app_id: %s, current_config keys: %s",
app_id,
list(current_config.keys()),
)
# Get configs for this app
configs = get_configs_for_app(app_id, current_config, env_variables)
response = {"configs": configs}
logger.info("Returning %d config items for app_id: %s", len(configs), app_id)
return jsonify(response)
except Exception as e:
logger.exception("Error processing config request")
return jsonify({"error": str(e)}), 500
@app.route("/health", methods=["GET"])
def health_check():
"""Health check endpoint."""
return jsonify({"status": "healthy"})
def main():
parser = argparse.ArgumentParser(
description="LMCache Remote Config Server Reference Implementation"
)
parser.add_argument("--host", default="0.0.0.0", help="Host to bind to")
parser.add_argument("--port", type=int, default=8088, help="Port to listen on")
parser.add_argument("--debug", action="store_true", help="Enable debug mode")
args = parser.parse_args()
logger.info("Starting config server on %s:%d", args.host, args.port)
logger.info("Available app configs: %s", list(CONFIG_DATABASE.keys()))
app.run(host=args.host, port=args.port, debug=args.debug)
if __name__ == "__main__":
main()