CI and images / lint (push) Successful in 3s
CI and images / extension-version (push) Successful in 3s
extension / lint (push) Successful in 16s
CI and images / frontend-build (push) Successful in 19s
CI and images / backend-lint-and-test (push) Successful in 31s
CI and images / integration (push) Successful in 2m22s
CI and images / sign-extension (push) Successful in 3s
CI and images / build-web (push) Successful in 3m7s
CI and images / smoke-web (push) Successful in 59s
CI and images / build-agent (push) Successful in 6m41s
CI and images / promote (push) Successful in 2s
Agent: - The image ran PyPI's CUDA-13 torch 2.14 and onnxruntime-gpu 1.30 on a CUDA 12.9 cudnn-runtime base. requirements.txt had silently replaced the Dockerfile's torch 2.6+cu124, because ultralytics pulls torchvision, which pulls its own torch. That left ~3 GB of base libraries and a ~3 GB torch nothing loaded: 10 GB compressed. - Now: an nvidia/cuda 13.0.3 `base` image, with torch and torchvision installed together from cu130. CUDA and cuDNN come from the nvidia-* pip packages; onnxruntime-gpu declares its [cuda,cudnn] extras. - fc_agent/accel.py preloads those libraries for onnxruntime. It then logs, and reports in /status, whether torch and the ONNX CUDA provider actually got the GPU, since both fall back to the CPU silently. Web image: - Drop opencv-python-headless and onnxruntime, plus the opencv-only apt libs. Both have been listed since the scaffold and nothing in backend/ imports them. - torch/torchvision move to 2.14/0.29, and the unexplained caps are lifted (rule 154). Redis: 8-alpine in both compose files and both CI service containers. That gives an AGPLv3 licence option, where 7.4 was RSAL/SSPL only. The client moves to >=8.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR
87 lines
3.2 KiB
Python
87 lines
3.2 KiB
Python
"""Which accelerator each runtime actually got — reported once, at startup.
|
|
|
|
The agent has two GPU runtimes and both fall back to the CPU without raising:
|
|
torch when the driver is too old for its CUDA build, and onnxruntime (the imgutils
|
|
detector + CCIP models) when its CUDA provider cannot load its libraries. A
|
|
fallback shows up only as slower work, and nothing reported it. On 2026-09-24 the
|
|
image turned out to be running a CUDA-13 torch and onnxruntime on a CUDA-12 base
|
|
(#1451), and whether the ONNX half was on the GPU could not be answered from
|
|
anything the agent had ever logged.
|
|
|
|
Also the fix for the likeliest way the ONNX half misses: onnxruntime-gpu's CUDA
|
|
provider finds libcudart/cuBLAS/cuDNN only on the loader path, and in this image
|
|
they live in the `nvidia-*` pip packages torch installs. `preload_dlls()` (ORT
|
|
1.21+) loads them from there, so the provider resolves them by soname.
|
|
|
|
Stdlib-only at import, so the unit suite can import it — torch and onnxruntime
|
|
are imported inside the functions.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import ctypes
|
|
import importlib
|
|
import logging
|
|
from pathlib import Path
|
|
|
|
log = logging.getLogger("fc_agent.accel")
|
|
|
|
# Filled by report(); /status carries it so the page can show it too.
|
|
LAST: dict = {}
|
|
|
|
|
|
def torch_status(imp=importlib.import_module) -> dict:
|
|
try:
|
|
torch = imp("torch")
|
|
except Exception as e:
|
|
return {"device": "unavailable", "error": str(e)}
|
|
out = {"version": torch.__version__, "cuda_build": torch.version.cuda}
|
|
if torch.cuda.is_available():
|
|
out["device"] = "cuda"
|
|
out["gpu"] = torch.cuda.get_device_name(0)
|
|
else:
|
|
out["device"] = "cpu"
|
|
return out
|
|
|
|
|
|
def onnx_status(imp=importlib.import_module, load=ctypes.CDLL) -> dict:
|
|
try:
|
|
ort = imp("onnxruntime")
|
|
except Exception as e:
|
|
return {"device": "unavailable", "error": str(e)}
|
|
out = {"version": ort.__version__, "providers": list(ort.get_available_providers())}
|
|
if "CUDAExecutionProvider" not in out["providers"]:
|
|
out["device"] = "cpu"
|
|
return out
|
|
preload = getattr(ort, "preload_dlls", None)
|
|
if preload is not None:
|
|
try:
|
|
preload()
|
|
except Exception as e:
|
|
out["preload_error"] = str(e)
|
|
# "Available" only means the build HAS the provider. Loading its library is
|
|
# what resolves libcudart/cuBLAS/cuDNN — the step that fails when they are
|
|
# missing, and the one a session would otherwise fail silently on.
|
|
capi = Path(ort.__file__).parent / "capi"
|
|
try:
|
|
load(str(capi / "libonnxruntime_providers_shared.so"), mode=ctypes.RTLD_GLOBAL)
|
|
load(str(capi / "libonnxruntime_providers_cuda.so"))
|
|
except OSError as e:
|
|
out["device"] = "cpu"
|
|
out["error"] = str(e)
|
|
else:
|
|
out["device"] = "cuda"
|
|
return out
|
|
|
|
|
|
def report() -> dict:
|
|
"""Check both runtimes, log the result, and keep it for /status."""
|
|
LAST.clear()
|
|
LAST.update(torch=torch_status(), onnx=onnx_status())
|
|
for name, s in LAST.items():
|
|
if s.get("device") == "cuda":
|
|
log.info("accel: %s on GPU (%s)", name, s)
|
|
else:
|
|
log.warning("accel: %s is NOT on the GPU — work runs on the CPU (%s)", name, s)
|
|
return dict(LAST)
|