arozantsev commited on
Commit
c5fce40
·
verified ·
1 Parent(s): 1603d8f

Deploy simready-assets run 20261020-a3cb7f2 (GPU variant)

Browse files
Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- # deploy: 20261019-9f72902 (simready-assets)
2
  # GPU variant of the single-container MVP image for Hugging Face Spaces
3
  # (Docker SDK): everything docker/Dockerfile.hf-space has (OpenSearch + Neo4j
4
  # + Redis + deepsearch-api + asset-graph-service + nginx under supervisord),
@@ -349,12 +349,12 @@ ENV HF_STORAGE_API_TOKEN= \
349
  ASSERT_ADMIN_USER=false \
350
  USDSEARCH_VISION_VALIDATION_ENABLED=false \
351
  USDSEARCH_LLM_PARSING_ENABLED=false \
352
- # Validation limits are per deepsearch-api worker: 16 checks in flight and a 125k-verdict cache in
353
- # each of the 4 workers (DEEPSEARCH_API_WORKERS below), 64 and 500k in all. One search's page runs
354
- # at most 16 at once anyway (usdsearch's BATCH_CONCURRENCY); with 8, a 50-check page took ~3 s on
355
- # the endpoint, with 16 ~1.5 s (2026-10-15 benchmark on the endpoint's own vLLM args).
356
  USDSEARCH_VISION_VALIDATION_MAX_CONCURRENT=16 \
357
- USDSEARCH_VISION_VALIDATION_CACHE_SIZE=125000 \
358
  USDSEARCH_LLM_PARSING_FIELDS_FILEPATH=/app/search_fields.yaml \
359
  USDSEARCH_LLM_PARSING_PROMPT_FILEPATH=/app/llm_parse_prompt.txt \
360
  # Business telemetry — same as docker/Dockerfile.hf-space, see its comment.
@@ -384,11 +384,14 @@ ENV HF_STORAGE_API_TOKEN= \
384
  SIGLIP2_TRITON_SERVER_URL=localhost:8001 \
385
  TRITON_SERVER_URL=localhost:8001 \
386
  SIGLIP2_TRITON_SERVER_SSL=false \
387
- # deepsearch-api workers — see docker/Dockerfile.hf-space's comment. 4, not
388
- # 1, for the production t4-medium (8 vCPU / 30 GB): measured best there
389
- # (docs/search-performance.md) — 54 vs 26 req/s at 50 concurrent users;
390
- # 10 workers starve OpenSearch of CPU. Override with a Space Variable.
391
- DEEPSEARCH_API_WORKERS=4 \
 
 
 
392
  PORT=7860
393
 
394
  ARG RUN_ID
 
1
+ # deploy: 20261020-a3cb7f2 (simready-assets)
2
  # GPU variant of the single-container MVP image for Hugging Face Spaces
3
  # (Docker SDK): everything docker/Dockerfile.hf-space has (OpenSearch + Neo4j
4
  # + Redis + deepsearch-api + asset-graph-service + nginx under supervisord),
 
349
  ASSERT_ADMIN_USER=false \
350
  USDSEARCH_VISION_VALIDATION_ENABLED=false \
351
  USDSEARCH_LLM_PARSING_ENABLED=false \
352
+ # Validation limits are per deepsearch-api process: 16 checks in flight and an 83k-verdict cache in
353
+ # each of the 6 processes (DEEPSEARCH_API_PROCESSES below), 96 and ~500k in all (Redis keeps every verdict
354
+ # anyway). One search's page runs at most 16 at once (usdsearch's BATCH_CONCURRENCY); with 8, a 50-check
355
+ # page took ~3 s on the endpoint, with 16 ~1.5 s (2026-10-15 benchmark on the endpoint's own vLLM args).
356
  USDSEARCH_VISION_VALIDATION_MAX_CONCURRENT=16 \
357
+ USDSEARCH_VISION_VALIDATION_CACHE_SIZE=83333 \
358
  USDSEARCH_LLM_PARSING_FIELDS_FILEPATH=/app/search_fields.yaml \
359
  USDSEARCH_LLM_PARSING_PROMPT_FILEPATH=/app/llm_parse_prompt.txt \
360
  # Business telemetry — same as docker/Dockerfile.hf-space, see its comment.
 
384
  SIGLIP2_TRITON_SERVER_URL=localhost:8001 \
385
  TRITON_SERVER_URL=localhost:8001 \
386
  SIGLIP2_TRITON_SERVER_SSL=false \
387
+ # deepsearch-api processes, ONE uvicorn worker each, behind nginx's least_conn
388
+ # upstream (supervisord-gpu.conf, nginx.conf.template). 6 for a10g-large
389
+ # (12 vCPU / 46 GB): 6, 8 and 12 carried the same load in the lab (docs/
390
+ # search-performance.md), 6 costs the least memory. Must equal supervisord-gpu.conf's
391
+ # numprocs: nginx renders its upstream from this, so never set it as a Space
392
+ # Variable. DEEPSEARCH_API_WORKERS is no longer read (a leftover Space Variable
393
+ # of that name does nothing).
394
+ DEEPSEARCH_API_PROCESSES=6 \
395
  PORT=7860
396
 
397
  ARG RUN_ID
README.md CHANGED
@@ -5,7 +5,7 @@ colorFrom: green
5
  colorTo: blue
6
  sdk: docker
7
  app_port: 7860
8
- hardware: t4-medium
9
  ---
10
 
11
  # SimReady Central USD Search
@@ -13,7 +13,7 @@ hardware: t4-medium
13
  SimReady Central USD Search — single-container demo (OpenSearch + Neo4j +
14
  Redis + deepsearch-api + asset-graph-service, SigLIP2 embeddings via a local
15
  siglip2-triton process — see docker/Dockerfile.hf-space-gpu). Runs on a GPU
16
- hardware tier (t4-medium), unlike the free-tier
17
  docker/Dockerfile.hf-space variant, which routes SigLIP2 to NVCF instead.
18
  The index itself is pulled from the artifact S3 bucket at container
19
  startup, not baked in. Deployed by
 
5
  colorTo: blue
6
  sdk: docker
7
  app_port: 7860
8
+ hardware: a10g-large
9
  ---
10
 
11
  # SimReady Central USD Search
 
13
  SimReady Central USD Search — single-container demo (OpenSearch + Neo4j +
14
  Redis + deepsearch-api + asset-graph-service, SigLIP2 embeddings via a local
15
  siglip2-triton process — see docker/Dockerfile.hf-space-gpu). Runs on a GPU
16
+ hardware tier (a10g-large), unlike the free-tier
17
  docker/Dockerfile.hf-space variant, which routes SigLIP2 to NVCF instead.
18
  The index itself is pulled from the artifact S3 bucket at container
19
  startup, not baked in. Deployed by
docker/hf-space/nginx.conf.template CHANGED
@@ -1,10 +1,11 @@
1
  # Friendly external paths remapped to deepsearch-api's/asset-graph-service's
2
  # actual routes — same mapping as
3
  # usdsearch/helm/usdsearch/templates/api_gateway_config_map.yaml.
4
- # deepsearch-api listens on 127.0.0.1:8000, asset-graph-service on
5
- # 127.0.0.1:8010 (see docker/hf-space/supervisord.conf) — nginx is the sole
6
- # process bound to the externally proxied port, so it can front every route
7
- # with these rewrites plus CORS.
 
8
  #
9
  # The Explorer UI is served at `/` (root), not proxied to deepsearch-api at
10
  # all — served directly from the static bundle baked into this image at
@@ -103,6 +104,14 @@ map $http_accept_encoding $slim_accepts_br {
103
  "~*(^|,)\s*br\s*(;|,|$)" 1;
104
  }
105
  # <<< simready-central slim explorer (http) <<<
 
 
 
 
 
 
 
 
106
  server {
107
  listen __PORT__;
108
 
@@ -123,25 +132,25 @@ server {
123
  location /search {
124
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
125
  rewrite ^/search(.*)$ /v2/deepsearch/search$1 break;
126
- proxy_pass http://127.0.0.1:8000;
127
  }
128
 
129
  location /search_hybrid {
130
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
131
  rewrite ^/search_hybrid(.*)$ /v3/deepsearch/search$1 break;
132
- proxy_pass http://127.0.0.1:8000;
133
  }
134
 
135
  location /vlm_validate/search_result {
136
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
137
  rewrite ^/vlm_validate/search_result(.*)$ /v3/deepsearch/vlm_validate/search_result$1 break;
138
- proxy_pass http://127.0.0.1:8000;
139
  }
140
 
141
  location /llm_parse/ {
142
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
143
  rewrite ^/llm_parse/(.*)$ /v3/deepsearch/llm_parse/$1 break;
144
- proxy_pass http://127.0.0.1:8000;
145
  }
146
 
147
  # package-download (127.0.0.1:8020, pipeline/package_download/), not deepsearch-api: it answers
@@ -187,7 +196,7 @@ server {
187
  location /images {
188
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
189
  rewrite ^/images(.*)$ /v3/images$1 break;
190
- proxy_pass http://127.0.0.1:8000;
191
  }
192
 
193
  location /static/ {
@@ -227,8 +236,8 @@ server {
227
  # below serves the Explorer SPA instead of proxying everything
228
  # unmatched. /docs and /redoc are prefix matches, not exact: Swagger
229
  # UI/ReDoc can request sub-resources under their own path.
230
- location = /health { proxy_pass http://127.0.0.1:8000; }
231
- location = /openapi.json { proxy_pass http://127.0.0.1:8000; }
232
  # /metrics is NOT deepsearch-api's (127.0.0.1:8000/metrics stays private to
233
  # the container): it is pipeline/cache_metrics.py's page, an allow-list of
234
  # cache hit/miss counters (supervisord program cache-metrics). Not up yet:
@@ -241,8 +250,8 @@ server {
241
  default_type application/json;
242
  return 503 '{"detail":"cache metrics are not running"}';
243
  }
244
- location /docs { proxy_pass http://127.0.0.1:8000; }
245
- location /redoc { proxy_pass http://127.0.0.1:8000; }
246
 
247
  # The Explorer SPA (see header comment) at root: try_files serves any of
248
  # its own literal files (index.html, ds.png, favicon.ico, manifest.json,
@@ -389,7 +398,7 @@ server {
389
  location = /search/stats/usd_properties {
390
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
391
  rewrite ^/search(.*)$ /v2/deepsearch/search$1 break;
392
- proxy_pass http://127.0.0.1:8000;
393
  proxy_ignore_headers Cache-Control Expires;
394
  proxy_cache slim_stats_cache;
395
  proxy_cache_key "$request_uri";
@@ -415,7 +424,7 @@ server {
415
  location = /images {
416
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
417
  rewrite ^/images(.*)$ /v3/images$1 break;
418
- proxy_pass http://127.0.0.1:8000;
419
  proxy_cache slim_images_cache;
420
  proxy_cache_key "$request_uri";
421
  proxy_ignore_headers Cache-Control Expires;
@@ -446,7 +455,7 @@ server {
446
  location = /llm_parse/query {
447
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
448
  rewrite ^/llm_parse/(.*)$ /v3/deepsearch/llm_parse/$1 break;
449
- proxy_pass http://127.0.0.1:8000;
450
  proxy_cache slim_parse_cache;
451
  proxy_cache_key "$slim_today|$request_uri|$request_body";
452
  proxy_cache_bypass $slim_not_post $request_body_file;
@@ -473,7 +482,7 @@ server {
473
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
474
  if ($slim_count_probe) { rewrite ^ /_slim_count_probe last; }
475
  rewrite ^/search_hybrid(.*)$ /v3/deepsearch/search$1 break;
476
- proxy_pass http://127.0.0.1:8000;
477
  }
478
  # The Explorer's background count probes (limit-1 and file-name searches for its totals; SimReady Central's
479
  # gateway caches them the same way): their own zone, 30 min, stale while one request refreshes an entry. Never a
@@ -481,7 +490,7 @@ server {
481
  location = /_slim_count_probe {
482
  internal;
483
  rewrite ^ /v3/deepsearch/search break;
484
- proxy_pass http://127.0.0.1:8000;
485
  proxy_cache slim_count_cache;
486
  proxy_cache_key "$request_uri|$request_body";
487
  proxy_cache_bypass $slim_not_post $request_body_file $slim_probe_skip;
@@ -533,7 +542,7 @@ server {
533
  location = /vlm_validate/batch_stream {
534
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
535
  rewrite ^ /v3/deepsearch/vlm_validate/search_result_batch break;
536
- proxy_pass http://127.0.0.1:8000;
537
  proxy_buffering off;
538
  proxy_read_timeout 120s;
539
  gzip off;
@@ -616,3 +625,21 @@ server {
616
  try_files $uri $uri/ /index.html;
617
  }
618
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # Friendly external paths remapped to deepsearch-api's/asset-graph-service's
2
  # actual routes — same mapping as
3
  # usdsearch/helm/usdsearch/templates/api_gateway_config_map.yaml.
4
+ # deepsearch-api runs as single-worker processes on 127.0.0.1:8101, 8102, ...
5
+ # (the `deepsearch` upstream below; how many: DEEPSEARCH_API_PROCESSES, see
6
+ # docker/hf-space/supervisord-gpu.conf), asset-graph-service on
7
+ # 127.0.0.1:8010 — nginx is the sole process bound to the externally proxied
8
+ # port, so it can front every route with these rewrites plus CORS.
9
  #
10
  # The Explorer UI is served at `/` (root), not proxied to deepsearch-api at
11
  # all — served directly from the static bundle baked into this image at
 
104
  "~*(^|,)\s*br\s*(;|,|$)" 1;
105
  }
106
  # <<< simready-central slim explorer (http) <<<
107
+ # deepsearch-api's processes, one uvicorn worker each. least_conn, not `uvicorn --workers N`: with N workers on one
108
+ # socket the kernel hands nearly every connection to 2-3 of them, and the rest of the CPU sits idle (lab load test
109
+ # 2026-10-15, docs/search-performance.md). __DEEPSEARCH_API_SERVERS__ is rendered at container start from
110
+ # DEEPSEARCH_API_PROCESSES (the nginx program in supervisord*.conf).
111
+ upstream deepsearch {
112
+ least_conn;
113
+ __DEEPSEARCH_API_SERVERS__
114
+ }
115
  server {
116
  listen __PORT__;
117
 
 
132
  location /search {
133
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
134
  rewrite ^/search(.*)$ /v2/deepsearch/search$1 break;
135
+ proxy_pass http://deepsearch;
136
  }
137
 
138
  location /search_hybrid {
139
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
140
  rewrite ^/search_hybrid(.*)$ /v3/deepsearch/search$1 break;
141
+ proxy_pass http://deepsearch;
142
  }
143
 
144
  location /vlm_validate/search_result {
145
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
146
  rewrite ^/vlm_validate/search_result(.*)$ /v3/deepsearch/vlm_validate/search_result$1 break;
147
+ proxy_pass http://deepsearch;
148
  }
149
 
150
  location /llm_parse/ {
151
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
152
  rewrite ^/llm_parse/(.*)$ /v3/deepsearch/llm_parse/$1 break;
153
+ proxy_pass http://deepsearch;
154
  }
155
 
156
  # package-download (127.0.0.1:8020, pipeline/package_download/), not deepsearch-api: it answers
 
196
  location /images {
197
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
198
  rewrite ^/images(.*)$ /v3/images$1 break;
199
+ proxy_pass http://deepsearch;
200
  }
201
 
202
  location /static/ {
 
236
  # below serves the Explorer SPA instead of proxying everything
237
  # unmatched. /docs and /redoc are prefix matches, not exact: Swagger
238
  # UI/ReDoc can request sub-resources under their own path.
239
+ location = /health { proxy_pass http://deepsearch; }
240
+ location = /openapi.json { proxy_pass http://deepsearch; }
241
  # /metrics is NOT deepsearch-api's (127.0.0.1:8000/metrics stays private to
242
  # the container): it is pipeline/cache_metrics.py's page, an allow-list of
243
  # cache hit/miss counters (supervisord program cache-metrics). Not up yet:
 
250
  default_type application/json;
251
  return 503 '{"detail":"cache metrics are not running"}';
252
  }
253
+ location /docs { proxy_pass http://deepsearch; }
254
+ location /redoc { proxy_pass http://deepsearch; }
255
 
256
  # The Explorer SPA (see header comment) at root: try_files serves any of
257
  # its own literal files (index.html, ds.png, favicon.ico, manifest.json,
 
398
  location = /search/stats/usd_properties {
399
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
400
  rewrite ^/search(.*)$ /v2/deepsearch/search$1 break;
401
+ proxy_pass http://deepsearch;
402
  proxy_ignore_headers Cache-Control Expires;
403
  proxy_cache slim_stats_cache;
404
  proxy_cache_key "$request_uri";
 
424
  location = /images {
425
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
426
  rewrite ^/images(.*)$ /v3/images$1 break;
427
+ proxy_pass http://deepsearch;
428
  proxy_cache slim_images_cache;
429
  proxy_cache_key "$request_uri";
430
  proxy_ignore_headers Cache-Control Expires;
 
455
  location = /llm_parse/query {
456
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
457
  rewrite ^/llm_parse/(.*)$ /v3/deepsearch/llm_parse/$1 break;
458
+ proxy_pass http://deepsearch;
459
  proxy_cache slim_parse_cache;
460
  proxy_cache_key "$slim_today|$request_uri|$request_body";
461
  proxy_cache_bypass $slim_not_post $request_body_file;
 
482
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
483
  if ($slim_count_probe) { rewrite ^ /_slim_count_probe last; }
484
  rewrite ^/search_hybrid(.*)$ /v3/deepsearch/search$1 break;
485
+ proxy_pass http://deepsearch;
486
  }
487
  # The Explorer's background count probes (limit-1 and file-name searches for its totals; SimReady Central's
488
  # gateway caches them the same way): their own zone, 30 min, stale while one request refreshes an entry. Never a
 
490
  location = /_slim_count_probe {
491
  internal;
492
  rewrite ^ /v3/deepsearch/search break;
493
+ proxy_pass http://deepsearch;
494
  proxy_cache slim_count_cache;
495
  proxy_cache_key "$request_uri|$request_body";
496
  proxy_cache_bypass $slim_not_post $request_body_file $slim_probe_skip;
 
542
  location = /vlm_validate/batch_stream {
543
  if ($request_method = OPTIONS) { add_header Content-Length 0 always; add_header Content-Type "text/plain" always; return 204; }
544
  rewrite ^ /v3/deepsearch/vlm_validate/search_result_batch break;
545
+ proxy_pass http://deepsearch;
546
  proxy_buffering off;
547
  proxy_read_timeout 120s;
548
  gzip off;
 
625
  try_files $uri $uri/ /index.html;
626
  }
627
  }
628
+ # The API's old address, for the in-container callers that use it directly (package-download's fallback to the
629
+ # API's own /v3/download, its size and spell-vocab passes): spread over the same processes. Loopback only.
630
+ server {
631
+ listen 127.0.0.1:8000;
632
+ access_log off;
633
+ client_max_body_size 0;
634
+ proxy_http_version 1.1;
635
+ proxy_set_header Connection "";
636
+ proxy_set_header Host $host;
637
+ proxy_buffering off;
638
+ proxy_request_buffering off;
639
+ proxy_connect_timeout 5s;
640
+ proxy_send_timeout 600s;
641
+ proxy_read_timeout 600s;
642
+ location / {
643
+ proxy_pass http://deepsearch;
644
+ }
645
+ }
docker/hf-space/supervisord-gpu.conf CHANGED
@@ -159,19 +159,45 @@ stdout_logfile_maxbytes=0
159
  stderr_logfile=/dev/stderr
160
  stderr_logfile_maxbytes=0
161
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  [program:deepsearch-api]
163
- ; Waits on siglip2-triton's own readiness (via its remapped HTTP port,
164
- ; 8003 — see [program:siglip2-triton] above) in addition to running
165
- ; pipeline/hf_space_seed.py, same "don't exec uvicorn until every backend
166
- ; this process depends on is actually ready" rule the cpu-basic/NVCF
167
- ; variant already follows for OpenSearch.
168
- ; PROMETHEUS_MULTIPROC_DIR (emptied at every start): the workers share their
169
- ; metrics through files there, so a counter is the sum over all workers;
170
- ; [program:cache-metrics] reads its cache counters from them.
171
  ; USDSEARCH_LLM_PARSING_CACHE_SIZE 0 (a Space variable overrides it): no
172
- ; per-worker parse LRU. Its key has no date, so after midnight it answered
173
  ; "made last week" with yesterday's parse; nginx caches parses per day instead.
174
- command=/usr/bin/env -u PYTHONPATH /bin/sh -c "until curl -sf http://localhost:8003/v2/health/ready >/dev/null 2>&1; do sleep 2; done; cd /app/hf-space-pipeline && /app/.venv/bin/python -m pipeline.hf_space_seed && cd /app && rm -rf /tmp/deepsearch-api-prometheus && mkdir -p /tmp/deepsearch-api-prometheus && exec env PROMETHEUS_MULTIPROC_DIR=/tmp/deepsearch-api-prometheus USDSEARCH_LLM_PARSING_CACHE_SIZE=\"${USDSEARCH_LLM_PARSING_CACHE_SIZE:-0}\" /app/.venv/bin/python -m uvicorn deepsearch_api.main:app --host 127.0.0.1 --port 8000 --workers \"${DEEPSEARCH_API_WORKERS:-1}\""
 
 
 
175
  directory=/app
176
  user=1000
177
  autorestart=true
@@ -200,10 +226,12 @@ stderr_logfile=/dev/stderr
200
  stderr_logfile_maxbytes=0
201
 
202
  [program:nginx]
203
- ; See docker/hf-space/supervisord.conf's own copy (identical command). Never put
 
 
204
  ; a space before a ";" or "#" in a command: supervisord reads it as an inline
205
  ; comment and cuts the command there (2026-10-15: nginx FATAL on prod).
206
- command=/bin/sh -c "until curl -sf http://localhost:8000/health >/dev/null 2>&1 && curl -sf http://localhost:8010/docs >/dev/null 2>&1; do sleep 2; done; dl=false; case '%(ENV_DOWNLOAD_ENABLED)s' in [Tt]rue|1) if [ -f /app/hf-space-pipeline/.build/restore/hf-storage-api-catalog.yaml ]; then dl=true; else echo 'nginx: no hf-storage-api catalog, rendering download:false' >&2; fi;; esac; vf=glb; case $VIEWER_FORMAT in glb|usd|auto) vf=$VIEWER_FORMAT;; *) [ -z \"$VIEWER_FORMAT\" ] || echo 'nginx: VIEWER_FORMAT is not glb, usd or auto: rendering viewerFormat glb' >&2;; esac; sed -e 's/__PORT__/%(ENV_PORT)s/' -e 's|__CORPUS_NAME__|%(ENV_CORPUS_NAME)s|g' -e 's/__DOWNLOAD_ENABLED__/'$dl'/' -e 's|__SUBMIT_CONTENT_URL__|%(ENV_SUBMIT_CONTENT_URL)s|g' -e 's/__VIEWER_FORMAT__/'$vf'/' /etc/nginx/hf-space.conf.template > /etc/nginx/conf.d/hf-space.conf && exec nginx -g 'daemon off;'"
207
  user=root
208
  autorestart=true
209
  startsecs=3
 
159
  stderr_logfile=/dev/stderr
160
  stderr_logfile_maxbytes=0
161
 
162
+ [program:deepsearch-seed]
163
+ ; Runs once per container start, before any deepsearch-api process: waits on
164
+ ; siglip2-triton's own readiness (via its remapped HTTP port, 8003 — see
165
+ ; [program:siglip2-triton] above), runs pipeline/hf_space_seed.py, then
166
+ ; empties PROMETHEUS_MULTIPROC_DIR, where the processes share their metrics
167
+ ; (a counter is the sum over all of them; [program:cache-metrics] reads its
168
+ ; cache counters there), and leaves /tmp/deepsearch-seed.done for them. Not
169
+ ; in deepsearch-api's own command: each of its processes would wipe the
170
+ ; directory the others write to.
171
+ command=/usr/bin/env -u PYTHONPATH /bin/sh -c "rm -f /tmp/deepsearch-seed.done && until curl -sf http://localhost:8003/v2/health/ready >/dev/null 2>&1; do sleep 2; done; cd /app/hf-space-pipeline && /app/.venv/bin/python -m pipeline.hf_space_seed && rm -rf /tmp/deepsearch-api-prometheus && mkdir -p /tmp/deepsearch-api-prometheus && touch /tmp/deepsearch-seed.done"
172
+ directory=/app
173
+ user=1000
174
+ autorestart=unexpected
175
+ exitcodes=0
176
+ startsecs=0
177
+ ; Started before the API processes (lower priority), which also wait 3 s before looking for
178
+ ; the marker: a /tmp kept across a container restart can't let them skip this run's seed.
179
+ priority=10
180
+ stdout_logfile=/dev/stdout
181
+ stdout_logfile_maxbytes=0
182
+ stderr_logfile=/dev/stderr
183
+ stderr_logfile_maxbytes=0
184
+
185
  [program:deepsearch-api]
186
+ ; 6 processes with ONE uvicorn worker each, on 127.0.0.1:8101-8106, behind
187
+ ; nginx's least_conn upstream (nginx.conf.template). Not `uvicorn --workers 6`:
188
+ ; there the kernel hands nearly every connection to 2-3 workers and most of
189
+ ; the CPU idles (docs/search-performance.md). numprocs is read from
190
+ ; DEEPSEARCH_API_PROCESSES (Dockerfile.hf-space-gpu), the same value nginx renders its
191
+ ; upstream from, so the two can't differ (a Space Variable changes both). Supervisord
192
+ ; restarts one process that dies without touching the others. Each waits for
193
+ ; [program:deepsearch-seed].
194
  ; USDSEARCH_LLM_PARSING_CACHE_SIZE 0 (a Space variable overrides it): no
195
+ ; per-process parse LRU. Its key has no date, so after midnight it answered
196
  ; "made last week" with yesterday's parse; nginx caches parses per day instead.
197
+ process_name=%(program_name)s_%(process_num)02d
198
+ numprocs=%(ENV_DEEPSEARCH_API_PROCESSES)s
199
+ numprocs_start=1
200
+ command=/usr/bin/env -u PYTHONPATH /bin/sh -c "sleep 3; until [ -f /tmp/deepsearch-seed.done ]; do sleep 2; done; cd /app && exec env PROMETHEUS_MULTIPROC_DIR=/tmp/deepsearch-api-prometheus USDSEARCH_LLM_PARSING_CACHE_SIZE=\"${USDSEARCH_LLM_PARSING_CACHE_SIZE:-0}\" /app/.venv/bin/python -m uvicorn deepsearch_api.main:app --host 127.0.0.1 --port 81%(process_num)02d --workers 1"
201
  directory=/app
202
  user=1000
203
  autorestart=true
 
226
  stderr_logfile_maxbytes=0
227
 
228
  [program:nginx]
229
+ ; See docker/hf-space/supervisord.conf's own copy (identical command): it waits
230
+ ; for all DEEPSEARCH_API_PROCESSES API processes (after ~90 s, for at least one: nginx's
231
+ ; upstream skips a process that doesn't answer) and renders the upstream from the same list. Never put
232
  ; a space before a ";" or "#" in a command: supervisord reads it as an inline
233
  ; comment and cuts the command there (2026-10-15: nginx FATAL on prod).
234
+ command=/bin/sh -c "n=${DEEPSEARCH_API_PROCESSES:-1}; ports=; ups=; i=1; while [ $i -le $n ]; do ports=\"$ports $((8100+i))\"; ups=\"$ups server 127.0.0.1:$((8100+i));\"; i=$((i+1)); done; t=0; until curl -sf http://localhost:8010/docs >/dev/null 2>&1 && { (for p in $ports; do curl -sf http://localhost:$p/health >/dev/null 2>&1 || exit 1; done) || { [ $t -ge 90 ] && (for p in $ports; do curl -sf http://localhost:$p/health >/dev/null 2>&1 && exit 0; done; exit 1); }; }; do sleep 2; t=$((t+2)); done; dl=false; case '%(ENV_DOWNLOAD_ENABLED)s' in [Tt]rue|1) if [ -f /app/hf-space-pipeline/.build/restore/hf-storage-api-catalog.yaml ]; then dl=true; else echo 'nginx: no hf-storage-api catalog, rendering download:false' >&2; fi;; esac; vf=glb; case $VIEWER_FORMAT in glb|usd|auto) vf=$VIEWER_FORMAT;; *) [ -z \"$VIEWER_FORMAT\" ] || echo 'nginx: VIEWER_FORMAT is not glb, usd or auto: rendering viewerFormat glb' >&2;; esac; sed -e 's/__PORT__/%(ENV_PORT)s/' -e 's|__CORPUS_NAME__|%(ENV_CORPUS_NAME)s|g' -e 's/__DOWNLOAD_ENABLED__/'$dl'/' -e 's|__SUBMIT_CONTENT_URL__|%(ENV_SUBMIT_CONTENT_URL)s|g' -e 's/__VIEWER_FORMAT__/'$vf'/' -e \"s/__DEEPSEARCH_API_SERVERS__/$ups/\" /etc/nginx/hf-space.conf.template > /etc/nginx/conf.d/hf-space.conf && exec nginx -g 'daemon off;'"
235
  user=root
236
  autorestart=true
237
  startsecs=3
docker/hf-space/supervisord.conf CHANGED
@@ -171,9 +171,11 @@ stderr_logfile=/dev/stderr
171
  stderr_logfile_maxbytes=0
172
 
173
  [program:deepsearch-api]
174
- ; Bound to localhost only — nginx (below) is the sole process bound to the
175
- ; externally proxied port, so it can front every route with the friendly-URL
176
- ; rewrites and CORS headers in nginx.conf.template.
 
 
177
  ;
178
  ; Runs pipeline/hf_space_seed.py first and only execs uvicorn once it
179
  ; succeeds: that script waits for opensearch (started in parallel by
@@ -190,7 +192,7 @@ stderr_logfile_maxbytes=0
190
  ; USDSEARCH_LLM_PARSING_CACHE_SIZE 0 (a Space variable overrides it): no
191
  ; per-worker parse LRU. Its key has no date, so after midnight it answered
192
  ; "made last week" with yesterday's parse; nginx caches parses per day instead.
193
- command=/bin/sh -c "cd /app/hf-space-pipeline && /app/.venv/bin/python -m pipeline.hf_space_seed && cd /app && rm -rf /tmp/deepsearch-api-prometheus && mkdir -p /tmp/deepsearch-api-prometheus && exec env PROMETHEUS_MULTIPROC_DIR=/tmp/deepsearch-api-prometheus USDSEARCH_LLM_PARSING_CACHE_SIZE=\"${USDSEARCH_LLM_PARSING_CACHE_SIZE:-0}\" /app/.venv/bin/python -m uvicorn deepsearch_api.main:app --host 127.0.0.1 --port 8000 --workers \"${DEEPSEARCH_API_WORKERS:-1}\""
194
  directory=/app
195
  user=1000
196
  autorestart=true
@@ -240,11 +242,15 @@ stderr_logfile_maxbytes=0
240
  ; %(ENV_PORT)s is substituted by supervisord itself, so no extra templating
241
  ; tool (envsubst etc.) is needed in the image.
242
  ;
 
 
 
 
243
  ; Never put a space before a ";" or "#" in a command: supervisord's parser
244
  ; reads " ;" and " #" as the start of an inline comment and cuts the command
245
  ; there (2026-10-15: a " ;;" case arm left nginx FATAL, "No closing
246
  ; quotation"). tests/test_hf_space_runtime.py parses with supervisor itself.
247
- command=/bin/sh -c "until curl -sf http://localhost:8000/health >/dev/null 2>&1 && curl -sf http://localhost:8010/docs >/dev/null 2>&1; do sleep 2; done; dl=false; case '%(ENV_DOWNLOAD_ENABLED)s' in [Tt]rue|1) if [ -f /app/hf-space-pipeline/.build/restore/hf-storage-api-catalog.yaml ]; then dl=true; else echo 'nginx: no hf-storage-api catalog, rendering download:false' >&2; fi;; esac; vf=glb; case $VIEWER_FORMAT in glb|usd|auto) vf=$VIEWER_FORMAT;; *) [ -z \"$VIEWER_FORMAT\" ] || echo 'nginx: VIEWER_FORMAT is not glb, usd or auto: rendering viewerFormat glb' >&2;; esac; sed -e 's/__PORT__/%(ENV_PORT)s/' -e 's|__CORPUS_NAME__|%(ENV_CORPUS_NAME)s|g' -e 's/__DOWNLOAD_ENABLED__/'$dl'/' -e 's|__SUBMIT_CONTENT_URL__|%(ENV_SUBMIT_CONTENT_URL)s|g' -e 's/__VIEWER_FORMAT__/'$vf'/' /etc/nginx/hf-space.conf.template > /etc/nginx/conf.d/hf-space.conf && exec nginx -g 'daemon off;'"
248
  user=root
249
  autorestart=true
250
  startsecs=3
 
171
  stderr_logfile_maxbytes=0
172
 
173
  [program:deepsearch-api]
174
+ ; Bound to localhost only, on 127.0.0.1:8101: the one server of nginx's
175
+ ; `deepsearch` upstream in this image (DEEPSEARCH_API_PROCESSES=1; the GPU
176
+ ; image runs several, see supervisord-gpu.conf). nginx (below) is the sole
177
+ ; process bound to the externally proxied port, so it can front every route
178
+ ; with the friendly-URL rewrites and CORS headers in nginx.conf.template.
179
  ;
180
  ; Runs pipeline/hf_space_seed.py first and only execs uvicorn once it
181
  ; succeeds: that script waits for opensearch (started in parallel by
 
192
  ; USDSEARCH_LLM_PARSING_CACHE_SIZE 0 (a Space variable overrides it): no
193
  ; per-worker parse LRU. Its key has no date, so after midnight it answered
194
  ; "made last week" with yesterday's parse; nginx caches parses per day instead.
195
+ command=/bin/sh -c "cd /app/hf-space-pipeline && /app/.venv/bin/python -m pipeline.hf_space_seed && cd /app && rm -rf /tmp/deepsearch-api-prometheus && mkdir -p /tmp/deepsearch-api-prometheus && exec env PROMETHEUS_MULTIPROC_DIR=/tmp/deepsearch-api-prometheus USDSEARCH_LLM_PARSING_CACHE_SIZE=\"${USDSEARCH_LLM_PARSING_CACHE_SIZE:-0}\" /app/.venv/bin/python -m uvicorn deepsearch_api.main:app --host 127.0.0.1 --port 8101 --workers 1"
196
  directory=/app
197
  user=1000
198
  autorestart=true
 
242
  ; %(ENV_PORT)s is substituted by supervisord itself, so no extra templating
243
  ; tool (envsubst etc.) is needed in the image.
244
  ;
245
+ ; Waits for asset-graph-service and every deepsearch-api process
246
+ ; (127.0.0.1:8101 .. 8100+DEEPSEARCH_API_PROCESSES), and renders the
247
+ ; template's `deepsearch` upstream from that same list.
248
+ ;
249
  ; Never put a space before a ";" or "#" in a command: supervisord's parser
250
  ; reads " ;" and " #" as the start of an inline comment and cuts the command
251
  ; there (2026-10-15: a " ;;" case arm left nginx FATAL, "No closing
252
  ; quotation"). tests/test_hf_space_runtime.py parses with supervisor itself.
253
+ command=/bin/sh -c "n=${DEEPSEARCH_API_PROCESSES:-1}; ports=; ups=; i=1; while [ $i -le $n ]; do ports=\"$ports $((8100+i))\"; ups=\"$ups server 127.0.0.1:$((8100+i));\"; i=$((i+1)); done; t=0; until curl -sf http://localhost:8010/docs >/dev/null 2>&1 && { (for p in $ports; do curl -sf http://localhost:$p/health >/dev/null 2>&1 || exit 1; done) || { [ $t -ge 90 ] && (for p in $ports; do curl -sf http://localhost:$p/health >/dev/null 2>&1 && exit 0; done; exit 1); }; }; do sleep 2; t=$((t+2)); done; dl=false; case '%(ENV_DOWNLOAD_ENABLED)s' in [Tt]rue|1) if [ -f /app/hf-space-pipeline/.build/restore/hf-storage-api-catalog.yaml ]; then dl=true; else echo 'nginx: no hf-storage-api catalog, rendering download:false' >&2; fi;; esac; vf=glb; case $VIEWER_FORMAT in glb|usd|auto) vf=$VIEWER_FORMAT;; *) [ -z \"$VIEWER_FORMAT\" ] || echo 'nginx: VIEWER_FORMAT is not glb, usd or auto: rendering viewerFormat glb' >&2;; esac; sed -e 's/__PORT__/%(ENV_PORT)s/' -e 's|__CORPUS_NAME__|%(ENV_CORPUS_NAME)s|g' -e 's/__DOWNLOAD_ENABLED__/'$dl'/' -e 's|__SUBMIT_CONTENT_URL__|%(ENV_SUBMIT_CONTENT_URL)s|g' -e 's/__VIEWER_FORMAT__/'$vf'/' -e \"s/__DEEPSEARCH_API_SERVERS__/$ups/\" /etc/nginx/hf-space.conf.template > /etc/nginx/conf.d/hf-space.conf && exec nginx -g 'daemon off;'"
254
  user=root
255
  autorestart=true
256
  startsecs=3
services/explorer/dist/static/css/main.ed151f6a.css DELETED
The diff for this file is too large to render. See raw diff
 
services/explorer/dist/static/js/main.01872f5b.js DELETED
The diff for this file is too large to render. See raw diff
 
services/explorer/dist/static/js/main.01872f5b.js.LICENSE.txt DELETED
@@ -1,39 +0,0 @@
1
- /**
2
- * @license React
3
- * react-dom.production.min.js
4
- *
5
- * Copyright (c) Facebook, Inc. and its affiliates.
6
- *
7
- * This source code is licensed under the MIT license found in the
8
- * LICENSE file in the root directory of this source tree.
9
- */
10
-
11
- /**
12
- * @license React
13
- * react-jsx-runtime.production.min.js
14
- *
15
- * Copyright (c) Facebook, Inc. and its affiliates.
16
- *
17
- * This source code is licensed under the MIT license found in the
18
- * LICENSE file in the root directory of this source tree.
19
- */
20
-
21
- /**
22
- * @license React
23
- * react.production.min.js
24
- *
25
- * Copyright (c) Facebook, Inc. and its affiliates.
26
- *
27
- * This source code is licensed under the MIT license found in the
28
- * LICENSE file in the root directory of this source tree.
29
- */
30
-
31
- /**
32
- * @license React
33
- * scheduler.production.min.js
34
- *
35
- * Copyright (c) Facebook, Inc. and its affiliates.
36
- *
37
- * This source code is licensed under the MIT license found in the
38
- * LICENSE file in the root directory of this source tree.
39
- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
services/explorer/dist/static/js/main.0ed32d3d.js DELETED
The diff for this file is too large to render. See raw diff
 
services/explorer/dist/static/js/main.0ed32d3d.js.LICENSE.txt DELETED
@@ -1,39 +0,0 @@
1
- /**
2
- * @license React
3
- * react-dom.production.min.js
4
- *
5
- * Copyright (c) Facebook, Inc. and its affiliates.
6
- *
7
- * This source code is licensed under the MIT license found in the
8
- * LICENSE file in the root directory of this source tree.
9
- */
10
-
11
- /**
12
- * @license React
13
- * react-jsx-runtime.production.min.js
14
- *
15
- * Copyright (c) Facebook, Inc. and its affiliates.
16
- *
17
- * This source code is licensed under the MIT license found in the
18
- * LICENSE file in the root directory of this source tree.
19
- */
20
-
21
- /**
22
- * @license React
23
- * react.production.min.js
24
- *
25
- * Copyright (c) Facebook, Inc. and its affiliates.
26
- *
27
- * This source code is licensed under the MIT license found in the
28
- * LICENSE file in the root directory of this source tree.
29
- */
30
-
31
- /**
32
- * @license React
33
- * scheduler.production.min.js
34
- *
35
- * Copyright (c) Facebook, Inc. and its affiliates.
36
- *
37
- * This source code is licensed under the MIT license found in the
38
- * LICENSE file in the root directory of this source tree.
39
- */