Files
KohakuHub/docs/development/local-dev.md
narugo1992 d0445282fb feat: pure-client safetensors/parquet metadata preview (#27)
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>
2026-04-24 13:41:26 +08:00

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 uvicorn with --reload on 127.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_park
  • sara_chen
  • noah_kim
  • ivy_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_*.