File size: 4,224 Bytes
dd24a31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cf2697b
 
 
 
 
 
dd24a31
 
 
 
 
 
 
 
654c8c7
 
dd24a31
dfe5268
bba6f8a
654c8c7
 
dd24a31
dfe5268
dd24a31
 
 
654c8c7
dd24a31
 
654c8c7
 
dd24a31
bba6f8a
dd24a31
 
 
dfe5268
 
 
 
 
 
 
 
cf2697b
 
 
 
 
 
 
 
 
 
 
 
923f89f
 
 
cf2697b
 
 
 
 
 
 
923f89f
cf2697b
 
 
 
4dd5987
cf2697b
 
 
dd24a31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7f70b66
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.

"""
FastAPI application for the AntiAtropos Environment.

This module creates an HTTP server that exposes the AntiAtroposEnvironment
over HTTP and WebSocket endpoints, compatible with EnvClient.

Endpoints:
    - POST /reset: Reset the environment
    - POST /step: Execute an action
    - GET /state: Get current environment state
    - GET /schema: Get action/observation schemas
    - WS /ws: WebSocket endpoint for persistent sessions

Usage:
    # Development (with auto-reload):
    uvicorn server.app:app --reload --host 0.0.0.0 --port 8000

    # Production:
    uvicorn server.app:app --host 0.0.0.0 --port 8000 --workers 4

    # Or run directly:
    python -m server.app
"""

import json
import os
from dotenv import load_dotenv

load_dotenv()

try:
    from openenv.core.env_server.http_server import create_app
except Exception as e:  # pragma: no cover
    raise ImportError(
        "openenv is required for the web interface. Install dependencies with '\n    uv sync\n'"
    ) from e

try:
    # Change these names to match models.py
    from ..models import SREAction, ClusterObservation
    from .AntiAtropos_environment import AntiAtroposEnvironment
    from ..telemetry import render_prometheus_metrics
except (ModuleNotFoundError, ImportError):
    # And here as well
    from models import SREAction, ClusterObservation
    from server.AntiAtropos_environment import AntiAtroposEnvironment
    from telemetry import render_prometheus_metrics


# Create the app with web interface and README integration
# Create the app with the correct class names
app = create_app(
    AntiAtroposEnvironment,
    SREAction,           # Changed from AntiAtroposAction
    ClusterObservation,  # Changed from AntiAtroposObservation
    env_name="AntiAtropos",
    max_concurrent_envs=100,
)


@app.get("/metrics")
def metrics():
    from fastapi import Response

    payload = render_prometheus_metrics()
    return Response(content=payload, media_type="text/plain; version=0.0.4; charset=utf-8")


@app.get("/config/runtime")
def runtime_config():
    raw_map = os.getenv("ANTIATROPOS_WORKLOAD_MAP", "")
    mapped_nodes: list[str] = []
    if raw_map:
        try:
            parsed = json.loads(raw_map)
            if isinstance(parsed, dict):
                mapped_nodes = sorted(str(k) for k in parsed.keys())
        except Exception:
            mapped_nodes = []

    raw_max_replicas = os.getenv("ANTIATROPOS_MAX_REPLICAS", "")
    max_replicas_display = raw_max_replicas if raw_max_replicas.strip() else "unbounded"

    return {
        "env_mode": os.getenv("ANTIATROPOS_ENV_MODE", "simulated"),
        "reward_output_mode": os.getenv("ANTIATROPOS_REWARD_OUTPUT_MODE", "normalized"),
        "prometheus_url_configured": bool(os.getenv("PROMETHEUS_URL")),
        "kubeconfig_configured": bool(os.getenv("KUBECONFIG")),
        "k8s_namespace": os.getenv("ANTIATROPOS_K8S_NAMESPACE", "default"),
        "min_replicas": os.getenv("ANTIATROPOS_MIN_REPLICAS", "1"),
        "max_replicas": max_replicas_display,
        "scale_step": os.getenv("ANTIATROPOS_SCALE_STEP", "3"),
        "strict_real": os.getenv("ANTIATROPOS_STRICT_REAL", "false"),
        "workload_map_configured": bool(raw_map),
        "workload_map_nodes": mapped_nodes,
        "supported_modes": ["simulated", "hybrid", "live"],
    }


def main(host: str = "0.0.0.0", port: int = 8000):
    """
    Entry point for direct execution via uv run or python -m.

    This function enables running the server without Docker:
        uv run --project . server
        uv run --project . server --port 8001
        python -m AntiAtropos.server.app

    Args:
        host: Host address to bind to (default: "0.0.0.0")
        port: Port number to listen on (default: 8000)

    For production deployments, consider using uvicorn directly with
    multiple workers:
        uvicorn AntiAtropos.server.app:app --workers 4
    """
    import uvicorn

    uvicorn.run(app, host=host, port=port)


if __name__ == "__main__":
    main()