Implements issue #27 v4: file-level HF-compatible metadata preview computed entirely in the browser via HTTP Range reads against the existing /resolve/ 302 → presigned S3/MinIO URL. Zero new backend preview code, zero LRU, zero precomputation, zero new DB state. Backend (minimal CORS plumbing only): - main.py CORSMiddleware: add `expose_headers` so browsers can read Content-Range / X-Linked-* / X-Repo-Commit / ETag / Location off the final 206 response that follows the /resolve/ 302. - docker-compose.example.yml + scripts/dev/up_infra.sh: wire `MINIO_API_CORS_ALLOW_ORIGIN` so the SPA can cross-origin Range-read presigned targets. Configurable via `DEV_MINIO_CORS_ALLOW_ORIGIN`. - docs/development/local-dev.md: MinIO CORS section explaining the hard prerequisite + smoke-test probe + how to recreate the container. Frontend: - utils/safetensors.js (~190 LOC): pure-JS parser mirroring huggingface_hub.parse_safetensors_file_metadata byte-for-byte (speculative 100 KB first read, two-read fallback for fat headers, SAFETENSORS_MAX_HEADER_LENGTH guard). Exposes parseSafetensorsMetadata + summarizeSafetensors. - utils/parquet.js: thin wrapper over hyparquet's asyncBufferFromUrl + parquetMetadataAsync with mode:"cors" + credentials:"omit" so cookies never leak onto presigned URLs. Normalizes BigInt row counts. - components/repo/preview/FilePreviewDialog.vue: ElDialog with per-phase spinner text (range-head → parsing → done for safetensors, head → footer → parsing → done for parquet), dtype/row-group tables, and an explicit "CORS likely misconfigured" placeholder on failure. - RepoViewer.vue: HF-style chart-line-data icon next to .safetensors and .parquet rows; click opens the modal with the resolved /resolve/ URL for the current branch. Tests + fixtures: - test_files.py::test_resolve_get_302_exposes_cors_headers_for_browser_preview pins the `Access-Control-Expose-Headers` list against regressions. - test/kohaku-hub-ui/utils/test_safetensors.test.js: 6 cases covering the real-HF-format fixture, dtype summary, progress phases, fat-header fallback, oversized-header guard, and non-206 error paths. - test/kohaku-hub-ui/utils/test_parquet.test.js: footer parse + progress phase assertions. - test/kohaku-hub-ui/fixtures/previews/{tiny.safetensors,tiny.parquet}: byte-identical-to-HF fixtures produced by the real safetensors / pyarrow libs via scripts/dev/generate_preview_test_fixtures.py (committed so tests stay offline per AGENTS.md §5.2). Seed: - seed_demo_data.py: add two RemoteAsset entries for real HF-hosted small fixtures pinned by sha256, and wire them into visible paths (open-media-lab/vision-language-assistant-3b/fixtures/hf-tiny-random-bert.safetensors, open-media-lab/multimodal-benchmark-suite/fixtures/hf-no-robots-test.parquet) so the preview can be exercised against files that actually came off huggingface.co rather than purely local pyarrow/safetensors output. SEED_VERSION bumped to local-dev-demo-v4. Verified end-to-end against the dev stack: safetensors parser output on the seeded fixtures matches huggingface_hub.parse_safetensors_file_metadata byte-for-byte on the same file (100 tensors, 126,851 params, I64=512 / F32=126,339, metadata `{format: pt, ...}`). Browser preview modal renders both file kinds correctly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.5 KiB
Local Development
This setup runs the KohakuHub backend locally in your Python virtualenv, while Docker provides the supporting services:
- PostgreSQL for application metadata
- MinIO for local S3-compatible storage
- LakeFS for repository versioning
- Vite dev servers for the main UI and admin UI
It does not require docker compose. The scripts below use plain docker.
Prerequisites
- Docker Engine
- Python 3.10+
- Node.js 18+
- An existing virtualenv for backend work
One-Time Setup
If you prefer a single command surface, run make help from the repo root to see the shortcuts below.
1. Backend dependencies
./venv/bin/pip install -e ".[dev]"
If you already activated the virtualenv:
pip install -e ".[dev]"
2. Frontend dependencies
npm install --prefix src/kohaku-hub-ui
npm install --prefix src/kohaku-hub-admin
3. Create your local env file
cp .env.dev.example .env.dev
Or:
make init-env
The defaults are already wired to the local Docker services and Vite dev servers.
Start Local Infra
./scripts/dev/up_infra.sh
Or:
make infra-up
This starts:
- Postgres on
127.0.0.1:25432 - MinIO API on
127.0.0.1:29001 - MinIO console on
127.0.0.1:29000 - LakeFS on
127.0.0.1:28000
Persistent dev data is stored under hub-meta/dev/.
MinIO CORS (required for in-browser preview)
The pure-client safetensors / parquet preview (issue #27) issues cross-origin
HTTP Range reads against the presigned S3 URL that /resolve/ 302s to.
Browsers will block those reads unless MinIO advertises CORS.
up_infra.sh already passes MINIO_API_CORS_ALLOW_ORIGIN=* to the MinIO
container by default, which is what both the Vite dev origin
(http://127.0.0.1:28300) and production deploys need. Override it with the
DEV_MINIO_CORS_ALLOW_ORIGIN variable in .env.dev if you want to restrict
it to a specific origin (the value is forwarded verbatim as
Access-Control-Allow-Origin; comma-separated origins also work).
If you already have a MinIO container from before this change, recreate it so the env var lands:
docker rm -f kohakuhub-dev-minio
./scripts/dev/up_infra.sh
Smoke-test the CORS response (should include
Access-Control-Allow-Origin: * and Access-Control-Allow-Methods: GET):
curl -i -X OPTIONS http://127.0.0.1:29001/hub-storage \
-H 'Origin: http://127.0.0.1:28300' \
-H 'Access-Control-Request-Method: GET'
Without this, the preview modal opens, shows its spinner, then surfaces a
browser-level CORS error instead of the parsed metadata. Downloads via
hf_hub_download / direct /resolve/ hits are unaffected — only the
cross-origin Range probe the SPA does breaks.
Start The Backend
./scripts/dev/run_backend.sh
Or:
make backend
What this script does:
- loads
.env.dev - initializes LakeFS on first run
- writes LakeFS credentials to
hub-meta/dev/lakefs/credentials.env - runs database migrations
- auto-seeds fixed demo users/orgs/repos on a fresh local environment when
KOHAKU_HUB_DEV_AUTO_SEED=true - starts
uvicornwith--reloadon127.0.0.1:48888
Swagger docs will be available at http://127.0.0.1:48888/docs.
If you want the migrations + demo seed without holding the terminal open for uvicorn, run:
make seed-demo
This writes a local manifest to hub-meta/dev/demo-seed-manifest.json.
Start The Frontends
Main UI:
npm run dev --prefix src/kohaku-hub-ui
Or:
make ui
Admin UI:
npm run dev --prefix src/kohaku-hub-admin
Or:
make admin
Access:
- Main UI:
http://127.0.0.1:5173 - Admin UI:
http://127.0.0.1:5174
The Vite configs already proxy API traffic to the backend at 127.0.0.1:48888.
Why KOHAKU_HUB_INTERNAL_BASE_URL Exists
For local development, the backend should generate public links that point to the main UI dev server (5173), but its own internal follow-up requests should still hit the backend directly (48888).
Set in .env.dev:
KOHAKU_HUB_BASE_URL=http://127.0.0.1:5173
KOHAKU_HUB_INTERNAL_BASE_URL=http://127.0.0.1:48888
This keeps:
- browser-facing links on the frontend dev server
- backend self-calls off the Vite proxy path
First Login / Admin
Main UI seeded account:
- Username:
mai_lin - Password:
KohakuDev123!
Additional seeded users use the same password:
leo_parksara_chennoah_kimivy_ops
The seeded data also includes fixed organizations and repositories, including public/private repos, model/dataset/space types, branches, tags, likes, LFS files, and dataset preview files.
Admin UI login does not use a username/password. Open http://127.0.0.1:5174 and use the token from .env.dev.
Default local token:
KOHAKU_HUB_ADMIN_SECRET_TOKEN=dev-admin-token-change-me
Common Commands
Restart infra:
./scripts/dev/down_infra.sh
./scripts/dev/up_infra.sh
Or:
make infra-down
make infra-up
Stop infra only:
./scripts/dev/down_infra.sh
Or:
make infra-down
Tail a container log:
docker logs -f kohakuhub-dev-lakefs
docker logs -f kohakuhub-dev-minio
docker logs -f kohakuhub-dev-postgres
Backend Tests
Backend tests run against the real Postgres, MinIO, and LakeFS services. The same make test entrypoint is used locally and in GitHub Actions.
Start the local infra first:
make infra-up
Run the full backend suite with coverage:
make test
Run only one backend submodule by passing a path relative to both test/kohakuhub/ and src/kohakuhub/:
make test RANGE_DIR=api
make test RANGE_DIR=api/repo/routers
When RANGE_DIR is set, pytest runs test/kohakuhub/${RANGE_DIR} and coverage focuses on src/kohakuhub/${RANGE_DIR}.
If you keep local test overrides in a repo-root .env, load them into your shell before running tests:
source .env
make test
The test code reads environment variables only. It does not load .env directly.
Reset Local Data
make reset-local-data is intentionally destructive. The script prints a bold red warning, explains the consequences, and asks for a single y/N confirmation before it clears the local app state through the in-process local reset helper.
The reset flow no longer deletes hub-meta/dev/ directly. Instead, it:
- deletes all LakeFS repositories through the local LakeFS API
- clears the configured S3 bucket through the storage client
- rebuilds the KohakuHub application schema
- removes the local demo seed manifest
This avoids Docker bind-mount ownership issues and keeps the local infra containers running so you can re-seed immediately.
If you want a clean local reset followed by fresh demo data bootstrapping:
make reset-and-seed
That command still goes through the same single y/N confirmation before anything is deleted.
Troubleshooting
Docker service ports are already taken
Adjust the port mappings inside scripts/dev/up_infra.sh and keep .env.dev in sync.
LakeFS says it is already initialized but credentials are missing
The bootstrap credentials are only returned once. If hub-meta/dev/lakefs-data/ still exists but hub-meta/dev/lakefs/credentials.env was removed, either:
- restore the credentials file, or
- delete
hub-meta/dev/lakefs-data/and initialize again
Backend cannot connect to Postgres
Check:
docker logs kohakuhub-dev-postgres
cat .env.dev
Make sure KOHAKU_HUB_DATABASE_URL matches DEV_POSTGRES_*.