Commit ·
e0b4d0a
1
Parent(s): f3acecd
- Dockerfile +11 -8
- app.py +38 -3
Dockerfile
CHANGED
|
@@ -29,20 +29,20 @@
|
|
| 29 |
|
| 30 |
FROM python:3.10-slim
|
| 31 |
|
| 32 |
-
# System deps for OpenCV headless
|
| 33 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 34 |
libgl1 \
|
| 35 |
libglib2.0-0 \
|
| 36 |
-
|
| 37 |
|
| 38 |
-
# Non-root user
|
| 39 |
RUN useradd -m -u 1000 user
|
| 40 |
USER user
|
| 41 |
ENV PATH="/home/user/.local/bin:$PATH"
|
| 42 |
|
| 43 |
WORKDIR /app
|
| 44 |
|
| 45 |
-
#
|
| 46 |
COPY --chown=user requirements.txt .
|
| 47 |
RUN pip install --no-cache-dir --upgrade pip \
|
| 48 |
&& pip install --no-cache-dir -r requirements.txt
|
|
@@ -50,17 +50,20 @@ RUN pip install --no-cache-dir --upgrade pip \
|
|
| 50 |
# App code
|
| 51 |
COPY --chown=user . /app
|
| 52 |
|
| 53 |
-
# Perf
|
| 54 |
ENV OMP_NUM_THREADS=1 \
|
| 55 |
OPENBLAS_NUM_THREADS=1 \
|
| 56 |
UVICORN_WORKERS=2 \
|
| 57 |
YOLO_CONCURRENCY=1 \
|
| 58 |
HTTP_MAX_CONNECTIONS=50 \
|
| 59 |
DB_POOL_MAX=10 \
|
| 60 |
-
AUTH_CACHE_TTL_SECONDS=45
|
|
|
|
|
|
|
|
|
|
| 61 |
|
| 62 |
EXPOSE 7860
|
| 63 |
|
| 64 |
-
# Run
|
| 65 |
-
CMD ["
|
| 66 |
|
|
|
|
| 29 |
|
| 30 |
FROM python:3.10-slim
|
| 31 |
|
| 32 |
+
# System deps for OpenCV headless
|
| 33 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 34 |
libgl1 \
|
| 35 |
libglib2.0-0 \
|
| 36 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 37 |
|
| 38 |
+
# Non-root user
|
| 39 |
RUN useradd -m -u 1000 user
|
| 40 |
USER user
|
| 41 |
ENV PATH="/home/user/.local/bin:$PATH"
|
| 42 |
|
| 43 |
WORKDIR /app
|
| 44 |
|
| 45 |
+
# Python deps
|
| 46 |
COPY --chown=user requirements.txt .
|
| 47 |
RUN pip install --no-cache-dir --upgrade pip \
|
| 48 |
&& pip install --no-cache-dir -r requirements.txt
|
|
|
|
| 50 |
# App code
|
| 51 |
COPY --chown=user . /app
|
| 52 |
|
| 53 |
+
# Perf + Ultralytics config dir
|
| 54 |
ENV OMP_NUM_THREADS=1 \
|
| 55 |
OPENBLAS_NUM_THREADS=1 \
|
| 56 |
UVICORN_WORKERS=2 \
|
| 57 |
YOLO_CONCURRENCY=1 \
|
| 58 |
HTTP_MAX_CONNECTIONS=50 \
|
| 59 |
DB_POOL_MAX=10 \
|
| 60 |
+
AUTH_CACHE_TTL_SECONDS=45 \
|
| 61 |
+
YOLO_CONFIG_DIR=/tmp/Ultralytics
|
| 62 |
+
|
| 63 |
+
RUN mkdir -p /tmp/Ultralytics
|
| 64 |
|
| 65 |
EXPOSE 7860
|
| 66 |
|
| 67 |
+
# Run via import string so multi-workers work cleanly
|
| 68 |
+
CMD ["bash", "-lc", "uvicorn app:app --host 0.0.0.0 --port 7860 --workers ${UVICORN_WORKERS:-2}"]
|
| 69 |
|
app.py
CHANGED
|
@@ -775,26 +775,61 @@ def category_to_slug(category: str | None) -> str:
|
|
| 775 |
.replace("-", "_")
|
| 776 |
)
|
| 777 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 778 |
def parse_extracted_json(raw_text: str) -> dict:
|
| 779 |
if not raw_text:
|
| 780 |
return {}
|
| 781 |
text = str(raw_text).strip()
|
|
|
|
|
|
|
| 782 |
s, e = text.find("{"), text.rfind("}")
|
| 783 |
if s != -1 and e != -1 and e > s:
|
|
|
|
| 784 |
try:
|
| 785 |
-
return json.loads(
|
| 786 |
except Exception:
|
| 787 |
pass
|
|
|
|
|
|
|
| 788 |
if text.startswith("```") and text.endswith("```"):
|
| 789 |
lines = text.splitlines()
|
| 790 |
-
if lines and lines[0].startswith("```"):
|
| 791 |
-
|
|
|
|
|
|
|
| 792 |
try:
|
| 793 |
return json.loads("\n".join(lines).strip())
|
| 794 |
except Exception:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 795 |
return {}
|
| 796 |
return {}
|
| 797 |
|
|
|
|
| 798 |
def parse_total(value: str | float | int | None) -> float:
|
| 799 |
if value is None:
|
| 800 |
return 0.0
|
|
|
|
| 775 |
.replace("-", "_")
|
| 776 |
)
|
| 777 |
|
| 778 |
+
# def parse_extracted_json(raw_text: str) -> dict:
|
| 779 |
+
# if not raw_text:
|
| 780 |
+
# return {}
|
| 781 |
+
# text = str(raw_text).strip()
|
| 782 |
+
# s, e = text.find("{"), text.rfind("}")
|
| 783 |
+
# if s != -1 and e != -1 and e > s:
|
| 784 |
+
# try:
|
| 785 |
+
# return json.loads(text[s:e+1])
|
| 786 |
+
# except Exception:
|
| 787 |
+
# pass
|
| 788 |
+
# if text.startswith("```") and text.endswith("```"):
|
| 789 |
+
# lines = text.splitlines()
|
| 790 |
+
# if lines and lines[0].startswith("```"): lines = lines[1:]
|
| 791 |
+
# if lines and lines[-1].startswith("```"): lines = lines[:-1]
|
| 792 |
+
# try:
|
| 793 |
+
# return json.loads("\n".join(lines).strip())
|
| 794 |
+
# except Exception:
|
| 795 |
+
# return {}
|
| 796 |
+
# return {}
|
| 797 |
def parse_extracted_json(raw_text: str) -> dict:
|
| 798 |
if not raw_text:
|
| 799 |
return {}
|
| 800 |
text = str(raw_text).strip()
|
| 801 |
+
|
| 802 |
+
# 1) Try largest JSON object between first '{' and last '}'
|
| 803 |
s, e = text.find("{"), text.rfind("}")
|
| 804 |
if s != -1 and e != -1 and e > s:
|
| 805 |
+
candidate = text[s:e + 1]
|
| 806 |
try:
|
| 807 |
+
return json.loads(candidate)
|
| 808 |
except Exception:
|
| 809 |
pass
|
| 810 |
+
|
| 811 |
+
# 2) Handle fenced code blocks ```json ... ```
|
| 812 |
if text.startswith("```") and text.endswith("```"):
|
| 813 |
lines = text.splitlines()
|
| 814 |
+
if lines and lines[0].startswith("```"):
|
| 815 |
+
lines = lines[1:]
|
| 816 |
+
if lines and lines[-1].startswith("```"):
|
| 817 |
+
lines = lines[:-1]
|
| 818 |
try:
|
| 819 |
return json.loads("\n".join(lines).strip())
|
| 820 |
except Exception:
|
| 821 |
+
# last-chance bracket slice inside the fence
|
| 822 |
+
inner = "\n".join(lines).strip()
|
| 823 |
+
s2, e2 = inner.find("{"), inner.rfind("}")
|
| 824 |
+
if s2 != -1 and e2 != -1 and e2 > s2:
|
| 825 |
+
try:
|
| 826 |
+
return json.loads(inner[s2:e2 + 1])
|
| 827 |
+
except Exception:
|
| 828 |
+
return {}
|
| 829 |
return {}
|
| 830 |
return {}
|
| 831 |
|
| 832 |
+
|
| 833 |
def parse_total(value: str | float | int | None) -> float:
|
| 834 |
if value is None:
|
| 835 |
return 0.0
|