trace-agent-ui 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {trace_agent_ui-0.2.0/src/trace_agent_ui.egg-info → trace_agent_ui-0.3.0}/PKG-INFO +3 -1
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0}/pyproject.toml +6 -1
- trace_agent_ui-0.3.0/src/trace_agent_ui/app.py +111 -0
- trace_agent_ui-0.2.0/src/trace_agent_ui/static/assets/index-CUMFgaU0.js → trace_agent_ui-0.3.0/src/trace_agent_ui/static/assets/index-CVfISqiI.js +1 -1
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0}/src/trace_agent_ui/static/index.html +1 -1
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0/src/trace_agent_ui.egg-info}/PKG-INFO +3 -1
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0}/src/trace_agent_ui.egg-info/SOURCES.txt +3 -2
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0}/src/trace_agent_ui.egg-info/requires.txt +3 -0
- trace_agent_ui-0.3.0/tests/test_app.py +143 -0
- trace_agent_ui-0.2.0/src/trace_agent_ui/app.py +0 -77
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0}/MANIFEST.in +0 -0
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0}/README.md +0 -0
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0}/setup.cfg +0 -0
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0}/src/trace_agent_ui/__init__.py +0 -0
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0}/src/trace_agent_ui/static/assets/index-hExWyjb_.css +0 -0
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0}/src/trace_agent_ui/static/favicon.svg +0 -0
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0}/src/trace_agent_ui/static/icon.png +0 -0
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0}/src/trace_agent_ui/static/icons.svg +0 -0
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0}/src/trace_agent_ui.egg-info/dependency_links.txt +0 -0
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0}/src/trace_agent_ui.egg-info/entry_points.txt +0 -0
- {trace_agent_ui-0.2.0 → trace_agent_ui-0.3.0}/src/trace_agent_ui.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trace-agent-ui
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: TraceAgent UI static server.
|
|
5
5
|
Author-email: Enrique Javier Villar Cea <villarcea.enriquejavier@gmail.com>, Javier Rodriguez Castellano <javirodriguezcastellano@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -21,6 +21,8 @@ Description-Content-Type: text/markdown
|
|
|
21
21
|
Requires-Dist: fastapi<1.0.0,>=0.115.0
|
|
22
22
|
Requires-Dist: httpx<1.0.0,>=0.27.0
|
|
23
23
|
Requires-Dist: uvicorn[standard]<1.0.0,>=0.30.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest<9.0.0,>=8.3.2; extra == "dev"
|
|
24
26
|
|
|
25
27
|
# TraceAgent UI
|
|
26
28
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "trace-agent-ui"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "TraceAgent UI static server."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -36,6 +36,11 @@ Homepage = "https://github.com/LixusSoftware/TraceAgent"
|
|
|
36
36
|
Repository = "https://github.com/LixusSoftware/TraceAgent"
|
|
37
37
|
Issues = "https://github.com/LixusSoftware/TraceAgent/issues"
|
|
38
38
|
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
dev = [
|
|
41
|
+
"pytest>=8.3.2,<9.0.0",
|
|
42
|
+
]
|
|
43
|
+
|
|
39
44
|
[project.scripts]
|
|
40
45
|
trace-agent-ui = "trace_agent_ui.app:main"
|
|
41
46
|
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""TraceAgent UI static file server.
|
|
2
|
+
|
|
3
|
+
Serves the built React SPA from package data and proxies /api/* requests
|
|
4
|
+
to the TraceAgent backend server.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import httpx
|
|
13
|
+
from fastapi import FastAPI, Request
|
|
14
|
+
from fastapi.responses import FileResponse, JSONResponse, StreamingResponse
|
|
15
|
+
from starlette.background import BackgroundTask
|
|
16
|
+
|
|
17
|
+
# Headers that describe one connection, not the message: they must not be copied across the proxy.
|
|
18
|
+
_HOP_BY_HOP = {
|
|
19
|
+
"host",
|
|
20
|
+
"content-length",
|
|
21
|
+
"connection",
|
|
22
|
+
"keep-alive",
|
|
23
|
+
"transfer-encoding",
|
|
24
|
+
"upgrade",
|
|
25
|
+
"te",
|
|
26
|
+
"trailers",
|
|
27
|
+
"proxy-authenticate",
|
|
28
|
+
"proxy-authorization",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def create_app(
|
|
33
|
+
static_dir: str | None = None,
|
|
34
|
+
api_base_url: str | None = None,
|
|
35
|
+
transport: httpx.AsyncBaseTransport | None = None,
|
|
36
|
+
) -> FastAPI:
|
|
37
|
+
app = FastAPI(title="TraceAgent UI")
|
|
38
|
+
|
|
39
|
+
if static_dir is None:
|
|
40
|
+
import importlib.resources as pkg_resources
|
|
41
|
+
|
|
42
|
+
static_dir = str(pkg_resources.files("trace_agent_ui") / "static")
|
|
43
|
+
static_root = Path(static_dir).resolve()
|
|
44
|
+
|
|
45
|
+
api_base = (api_base_url or os.getenv("TRACE_AGENT_SERVER_URL", "http://127.0.0.1:8000")).rstrip("/")
|
|
46
|
+
|
|
47
|
+
# The API proxy has to be registered before the catch-all below: routes match in order, so
|
|
48
|
+
# anything registered earlier (or a static mount at "/") would answer /api/* with a 404.
|
|
49
|
+
@app.api_route(
|
|
50
|
+
"/api/{path:path}",
|
|
51
|
+
methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
|
|
52
|
+
response_model=None,
|
|
53
|
+
)
|
|
54
|
+
async def proxy_to_backend(request: Request, path: str) -> StreamingResponse | JSONResponse:
|
|
55
|
+
"""Proxy API requests to the TraceAgent backend server, streaming the response (SSE included)."""
|
|
56
|
+
headers = {k: v for k, v in request.headers.items() if k.lower() not in _HOP_BY_HOP}
|
|
57
|
+
# No read timeout: the live event stream stays open as long as the run does.
|
|
58
|
+
client = httpx.AsyncClient(transport=transport, timeout=httpx.Timeout(30.0, read=None))
|
|
59
|
+
upstream_request = client.build_request(
|
|
60
|
+
request.method,
|
|
61
|
+
f"{api_base}/api/{path}",
|
|
62
|
+
headers=headers,
|
|
63
|
+
params=request.query_params,
|
|
64
|
+
content=await request.body(),
|
|
65
|
+
)
|
|
66
|
+
try:
|
|
67
|
+
upstream = await client.send(upstream_request, stream=True)
|
|
68
|
+
except httpx.RequestError as exc:
|
|
69
|
+
await client.aclose()
|
|
70
|
+
return JSONResponse(
|
|
71
|
+
{"detail": f"The TraceAgent server at {api_base} is not reachable ({type(exc).__name__})."},
|
|
72
|
+
status_code=502,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
async def close() -> None:
|
|
76
|
+
await upstream.aclose()
|
|
77
|
+
await client.aclose()
|
|
78
|
+
|
|
79
|
+
return StreamingResponse(
|
|
80
|
+
upstream.aiter_raw(),
|
|
81
|
+
status_code=upstream.status_code,
|
|
82
|
+
headers={k: v for k, v in upstream.headers.items() if k.lower() not in _HOP_BY_HOP},
|
|
83
|
+
background=BackgroundTask(close),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
@app.get("/health")
|
|
87
|
+
async def health() -> dict[str, str]:
|
|
88
|
+
return {"status": "ok"}
|
|
89
|
+
|
|
90
|
+
@app.get("/{full_path:path}")
|
|
91
|
+
async def serve_spa(full_path: str) -> FileResponse:
|
|
92
|
+
"""Serve a built asset if it exists, and index.html for any other path (SPA fallback)."""
|
|
93
|
+
candidate = (static_root / full_path).resolve()
|
|
94
|
+
if full_path and candidate.is_file() and static_root in candidate.parents:
|
|
95
|
+
return FileResponse(candidate)
|
|
96
|
+
return FileResponse(static_root / "index.html")
|
|
97
|
+
|
|
98
|
+
return app
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def main() -> None:
|
|
102
|
+
import uvicorn
|
|
103
|
+
|
|
104
|
+
port = int(os.getenv("TRACE_AGENT_UI_PORT", "8080"))
|
|
105
|
+
host = os.getenv("TRACE_AGENT_UI_HOST", "0.0.0.0")
|
|
106
|
+
app = create_app()
|
|
107
|
+
uvicorn.run(app, host=host, port=port)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
if __name__ == "__main__":
|
|
111
|
+
main()
|