microsoft / microsoft/agent-framework
Python: Manipulating Static Files for DevUI
- Dominant language
- Python
- Stars
- 13.6k
- Forks
- 2.3k
- Avg merge
- 2d 45m
- Merged PRs (30d)
- 358
Description
```python
def _materialize_devui_assets() -> None:
"""Ensure DevUI static assets are real files when uv installs symlinks."""
ui_dir = pathlib.Path(agent_framework_devui.__file__).parent / "ui"
if not ui_dir.exists():
return
copied_assets = False
for asset in ui_dir.rglob("*"):
if not asset.is_symlink():
continue
try:
target = asset.resolve(strict=True)
except FileNotFoundError:
# Remove broken symlink so FastAPI does not expose stale paths.
asset.unlink(missing_ok=True)
print(f"⚠️ Removed broken DevUI asset symlink: {asset}")
continue
asset.unlink()
try:
if target.is_dir():
shutil.copytree(target, asset)
else:
asset.write_bytes(target.read_bytes())
copied_assets = True
except OSError as exc:
print(f"❌ Failed to materialize DevUI asset {asset}: {exc}")
if copied_assets:
print("🔁 Materialized DevUI static assets to satisfy FastAPI security checks")
```
The Problem When you ran DevUI, the server started successfully (the docs FastAPI endpoint worked), but accessing the root URL / returned {"detail":"Not Found"} instead of the DevUI web interface.
Root Cause UV package manager installs wheel contents as symlinks pointing to a cache directory (/home/vscode/.cache/uv/archive-v0/...). This means files like:
index.html /intelligent_orchestrator/.venv/.../agent_framework_devui/ui/assets/index.js ...were all symlinks to files outside the mounted directory.
FastAPI's StaticFiles has a security check that uses os.path.realpath() to prevent path-traversal attacks. When it resolved the symlink's real path, it found the file was outside the mounted directory (/home/vscode/.cache/... vs /intelligent_orchestrator/.venv/...). The os.path.commonpath() check failed, so FastAPI refused to serve the file and returned 404.
The attempted solution _materialize_devui_assets() walks the agent_framework_devui/ui directory tree and:
Finds any symlinked files or directories
Resolves them to their real target path
Replaces the symlink with the actual file content (or directory copy)
Now FastAPI's realpath check passes because files are inside the mount
Without materializing assets:
DevUI backend API works fine (/health, /v1/entities, etc.)
DevUI frontend fails to load (404 on /, index.html, /assets/index.js)
Users see {"detail":"Not Found"} instead of the UI
The helper is idempotent (safe to run multiple times) and only processes symlinks, so subsequent runs are fast. It's called once at startup before serve() to ensure the UI can load properly.
We really shouldn't be modifying the contents of site-packages (or anything within the .venv folder) outside of uv operations so this is not a sustainable solution. At least if the starlette option works it should leave those folders alone, so may be worth trying.
The underlying issue seems to be the way that devui assets are being packaged, so this should go back to AF PG to solve more permanently -- it should be a very common pattern where users are using uv and want to serve via fastAPI, so this isn't just an LSEG thing.
[shared with you the preview of the points raised for any feedback you may already have on this]
Contributor guide
Assessment
This issue has not been assessed yet.