# A-EYE backend. The application code is private; the container fetches it when it starts. FROM python:3.11-slim@sha256:e88e9763f943ec1834f992a4b51e0f24500486803e8bc534e5767af9ea65f6ce ENV PYTHONUNBUFFERED=1 \ PIP_NO_CACHE_DIR=1 RUN useradd -m -u 1000 appuser ENV HOME=/home/appuser \ HF_HOME=/home/appuser/.cache/huggingface WORKDIR /app RUN pip install --upgrade pip \ && pip install torch==2.8.0 torchvision==0.23.0 --index-url https://download.pytorch.org/whl/cpu \ && pip install 'fastapi==0.141.1' 'starlette>=1.6.0' 'uvicorn[standard]==0.34.0' 'pydantic>=2.10.4' 'python-multipart==0.0.32' 'pillow>=12.0.0' 'numpy>=2.1' 'scipy>=1.14' 'requests>=2.31' 'transformers==4.57.6' RUN python -c "from transformers import CLIPVisionModelWithProjection as M; M.from_pretrained('openai/clip-vit-large-patch14', revision='32bd64288804d66eefd0ccbe215aa642df71cc41')" \ && python -c "from transformers import AutoModel; AutoModel.from_pretrained('facebook/dinov2-small', revision='ed25f3a31f01632728cabb09d1542f84ab7b0056')" \ && chown -R appuser:appuser /home/appuser/.cache RUN chown appuser:appuser /app ENV AEYE_CODE_REPO=wonjun12/aeye-weights \ AEYE_CODE_REVISION=f4027eefea7002c78e3030d381f27dd0e43bcfb5 \ AEYE_CODE_FILE=code/aeye-backend-735636a3a3e17cc9.tar.gz \ AEYE_CODE_SHA256=735636a3a3e17cc9d0cdf950c4c4812d970e517797adce1b04219cb9e65f967d USER appuser ENV AEYE_DEVICE=cpu \ AEYE_TORCH_THREADS=2 \ AEYE_MAX_CONCURRENCY=1 \ AEYE_KEY_PER_MINUTE=10 \ AEYE_KEY_PER_DAY=1000 \ AEYE_HEATMAP_STYLE=spotlight \ AEYE_INPUT_KIND=on \ TRANSFORMERS_OFFLINE=1 ENV AEYE_WEIGHTS_REPO=wonjun12/aeye-weights \ AEYE_WEIGHTS_REVISION=f042633672aa30361234674c43d6c35373e0d71a \ AEYE_WEIGHTS_SHA256_BEST49=145c18f4cd7fc430d223cdec7f6abac1e29124155f8628648a5ef86173975134 \ AEYE_WEIGHTS_SHA256_BEST63=b93733e88c69b76581d49150d097dafd686e21f9d355d4665dcb0ffbee30742d \ AEYE_WEIGHTS_SHA256_BEST69=0f423cc566a7dfc8aa991f81ddf4e6410291106309841fdc09265c387aebf0df \ AEYE_WEIGHTS_SHA256_BEST69_LOC=6562f22ecd262aecdad5aaf54a8554cd239f2e1d40b6671adbdb2d9ecdc0ee82 \ AEYE_WEIGHTS_SHA256_BEST91=d3dc13a6f68947c187af515a5b75aa2609696b44f3c829b9e5297836db7e8ffe \ AEYE_WEIGHTS_SHA256_BEST91_LOC=fe5997eb90c22a75e3a31a66decfdf73c0775fe6650ad1d178ccbafa3bd0f1d9 ENV VERDICT_MODEL_PATH=/app/models/best91.pt \ HEATMAP_MODEL_PATH=/app/models/best91_loc.pt \ AEYE_BAND_T5=0.854339 \ AEYE_BAND_T1=0.981525 \ AEYE_BANDS_FOR=246d25f0c991bb95 \ AEYE_FUSION=on \ AEYE_FUSION_PATH=./fusion/fusion_rc2.json ENV AEYE_SCREEN_PATH=on \ AEYE_SCREEN_PATH_RULE=./screen_rules/screen_head_v3.json \ AEYE_SCREEN_PATH_RULE_ID=39f10adf3dbdae13 \ AEYE_SCREEN_PATH_FOR=246d25f0c991bb95 EXPOSE 7860 CMD ["python", "-c", "import compileall, hashlib, io, os, sys, tarfile, time, requests\nARGV = ['python', '-m', 'uvicorn', 'main_hybrid:app', '--host', '0.0.0.0', '--port', '7860', '--workers', '1', '--limit-concurrency', '64', '--timeout-keep-alive', '5', '--no-access-log']\nrepo, rev, path, want = (os.environ[k] for k in ('AEYE_CODE_REPO', 'AEYE_CODE_REVISION', 'AEYE_CODE_FILE', 'AEYE_CODE_SHA256'))\nurl = 'https://huggingface.co/%s/resolve/%s/%s' % (repo, rev, path)\ntoken = os.environ.get('HF_TOKEN', '').strip()\ndelay, deadline = 5, time.time() + 600\nwhile True:\n try:\n r = requests.get(url, headers={'Authorization': 'Bearer ' + token}, timeout=(10, 300))\n why = 'HTTP %d' % r.status_code\n except requests.RequestException as exc:\n r, why = None, type(exc).__name__\n if r is not None and r.status_code == 200:\n break\n if r is not None and r.status_code in (401, 403, 404):\n sys.exit('[start] the code bundle is not readable (%s) - is the Space secret HF_TOKEN set?' % why)\n if time.time() > deadline:\n sys.exit('[start] the code bundle could not be downloaded for 10 minutes (%s)' % why)\n print('[start] code download: %s - retrying in %ds' % (why, delay), flush=True)\n time.sleep(delay)\n delay = min(delay * 2, 60)\ndata = r.content\ngot = hashlib.sha256(data).hexdigest()\nif got != want:\n sys.exit('[start] code bundle sha256 %s is not the pinned %s' % (got, want))\ntarfile.open(fileobj=io.BytesIO(data), mode='r:gz').extractall('/app', filter='data')\nif not compileall.compile_dir('/app', legacy=True, quiet=1):\n sys.exit('[start] the code bundle did not compile')\nfor folder, _, names in os.walk('/app'):\n for name in names:\n if name.endswith('.py'):\n os.remove(os.path.join(folder, name))\nprint('[start] code %s (%s) ready' % (path, got[:16]), flush=True)\nos.chdir('/app')\nos.execvp(ARGV[0], ARGV)"]