simpleaudit-studio 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/.env.example +3 -3
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/.env.local.example +3 -3
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/Dockerfile +4 -4
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/PKG-INFO +3 -3
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/README.md +2 -2
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/accounts/services.py +1 -4
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/config/settings.py +1 -1
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/deploy/e2e_smoke.py +4 -4
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/deploy/seed_bulk.py +1 -1
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/docker-compose.yml +1 -1
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/e2e/test_ui.py +2 -2
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/management/commands/bootstrap_platform.py +4 -4
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/minimal_config.py +21 -4
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/startup_checks.py +2 -2
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_bootstrap.py +21 -1
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_minimal_config.py +27 -3
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_seed_platform.py +1 -1
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/pyproject.toml +1 -1
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/simpleaudit_studio/cli.py +5 -5
- simpleaudit_studio-0.2.2/uv.lock +2419 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/.dockerignore +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/.github/workflows/ci.yml +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/.github/workflows/publish.yml +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/.gitignore +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/LICENSE +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/Makefile +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/accounts/__init__.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/accounts/admin.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/accounts/apps.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/accounts/auth_urls.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/accounts/migrations/0001_initial.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/accounts/migrations/0002_user_workos_user_id.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/accounts/migrations/__init__.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/accounts/models.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/accounts/project_urls.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/accounts/serializers.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/accounts/views.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/accounts/workos_auth.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/audits/__init__.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/audits/admin.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/audits/apps.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/audits/comparison.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/audits/events.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/audits/migrations/0001_initial.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/audits/migrations/0002_add_audit_run_archived.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/audits/migrations/0003_auditrun_updated_at.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/audits/migrations/__init__.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/audits/models.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/audits/serializers.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/audits/services.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/audits/urls.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/audits/views.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/config/__init__.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/config/asgi.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/config/urls.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/config/wsgi.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/deploy/compose/Dockerfile +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/deploy/mock_openai_server.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/deploy/postgres-init.sql +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/docs/architecture.md +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/docs/deployment.md +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/docs/domain-model.md +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/__init__.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/admin.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/apps.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/context_processors.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/engine.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/exceptions.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/fixtures/demo_audit_results.json +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/hashing.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/health.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/health_api.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/logging.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/management/__init__.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/management/commands/__init__.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/management/commands/purge_test_data.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/management/commands/run_worker.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/management/commands/seed_demo_audits.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/management/commands/seed_platform.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/middleware.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/permissions.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/readiness.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/seed.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/simpleaudit_package.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/__init__.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/factories.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_api_e2e.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_audit_archive.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_audit_clone.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_audit_events.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_audit_poll_results.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_audit_rename.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_audit_run_freeze.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_audit_sse.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_audit_submission.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_audit_timing.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_auth_projects.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_comparison.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_engine_integration.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_health.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_health_panel.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_middleware.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_model_registry.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_purge_test_data.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_scenario_import_export.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_scenarios.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_smoke_all_pages.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_workspaces.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/tests/test_workspaces_ui.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/ui.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/infra/worker.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/manage.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/model_registry/__init__.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/model_registry/admin.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/model_registry/apps.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/model_registry/migrations/0001_initial.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/model_registry/migrations/__init__.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/model_registry/models.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/model_registry/serializers.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/model_registry/services.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/model_registry/urls.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/model_registry/views.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/requirements.txt +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/scenarios/__init__.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/scenarios/admin.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/scenarios/apps.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/scenarios/migrations/0001_initial.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/scenarios/migrations/__init__.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/scenarios/models.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/scenarios/serializers.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/scenarios/services.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/scenarios/urls.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/scenarios/views.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/simpleaudit_studio/__init__.py +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/static/favicon-16x16.svg +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/static/favicon-32x32.svg +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/static/favicon.svg +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/static/js/diff.min.js +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/static/logo.png +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/static/logo.svg +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/templates/404.html +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/templates/audit_detail.html +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/templates/auth/login.html +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/templates/auth/register.html +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/templates/auth/workos_login.html +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/templates/auth/workos_verify.html +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/templates/base.html +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/templates/compare.html +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/templates/dashboard.html +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/templates/health.html +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/templates/models.html +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/templates/new_audit.html +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/templates/scenario_result_detail.html +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/templates/scenarios.html +0 -0
- {simpleaudit_studio-0.2.0 → simpleaudit_studio-0.2.2}/templates/workspaces.html +0 -0
|
@@ -19,9 +19,9 @@ DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
|
|
|
19
19
|
LOG_LEVEL=INFO
|
|
20
20
|
|
|
21
21
|
# --- Bootstrap (idempotent; run automatically by the web container) ----------
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
BOOTSTRAP_USERNAME=studio
|
|
23
|
+
BOOTSTRAP_EMAIL=admin@example.local
|
|
24
|
+
BOOTSTRAP_PASSWORD=change-me
|
|
25
25
|
BOOTSTRAP_PROJECT_NAME=Default
|
|
26
26
|
|
|
27
27
|
# --- First-run seed (idempotent; run automatically by the web container) -----
|
|
@@ -22,9 +22,9 @@ LOG_LEVEL=DEBUG
|
|
|
22
22
|
|
|
23
23
|
# --- Bootstrap ----------------------------------------------------------------
|
|
24
24
|
# `python manage.py bootstrap_platform` reads these on first run (idempotent).
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
BOOTSTRAP_USERNAME=studio
|
|
26
|
+
BOOTSTRAP_EMAIL=admin@example.local
|
|
27
|
+
BOOTSTRAP_PASSWORD=localdevpass123
|
|
28
28
|
BOOTSTRAP_PROJECT_NAME=Default
|
|
29
29
|
|
|
30
30
|
# --- Database -----------------------------------------------------------------
|
|
@@ -53,12 +53,12 @@ ENV SIMPLEAUDIT_MINIMAL=1 \
|
|
|
53
53
|
DJANGO_SECRET_KEY=hf-space-demo-secret-key-change-in-production \
|
|
54
54
|
DJANGO_DEBUG=false \
|
|
55
55
|
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1,.hf.space,.huggingface.co \
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
BOOTSTRAP_USERNAME=studio \
|
|
57
|
+
BOOTSTRAP_EMAIL=studio@example.local \
|
|
58
|
+
BOOTSTRAP_PASSWORD=admin123 \
|
|
59
59
|
BOOTSTRAP_PROJECT_NAME=Default \
|
|
60
60
|
DEMO_MODE=true \
|
|
61
|
-
DEMO_USERNAME=
|
|
61
|
+
DEMO_USERNAME=studio \
|
|
62
62
|
DEMO_PASSWORD=admin123 \
|
|
63
63
|
MAX_CONCURRENT_AUDITS=1 \
|
|
64
64
|
MAX_SCENARIOS_PER_RUN=50
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: simpleaudit-studio
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Production AI model audit platform — one-liner demo mode via uvx
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -52,7 +52,7 @@ A self-hostable platform for running reproducible AI model audits using the [Sim
|
|
|
52
52
|
uvx simpleaudit-studio
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
[`uvx`](https://docs.astral.sh/uv/#uvx) installs the [`simpleaudit-studio`](https://pypi.org/project/simpleaudit-studio/) package and runs it in an isolated environment. No Docker, no Postgres, no manual setup. Opens at http://localhost:8000 (login: `admin` / `admin12345`). A mock model server is pre-seeded so you can
|
|
55
|
+
[`uvx`](https://docs.astral.sh/uv/#uvx) installs the [`simpleaudit-studio`](https://pypi.org/project/simpleaudit-studio/) package and runs it in an isolated environment. No Docker, no Postgres, no manual setup. Opens at http://localhost:8000 (login: `admin` / `admin12345`). A mock model server is pre-seeded so you can start exploring audit results immediately.
|
|
56
56
|
|
|
57
57
|
🌐 Or skip the setup entirely — try the live demo: <a href="https://sushantgautam-simpleaudit-studio.hf.space" target="_blank" rel="noopener noreferrer">sushantgautam-simpleaudit-studio.hf.space</a>
|
|
58
58
|
|
|
@@ -78,7 +78,7 @@ For teams, multi-user setups, or GPU worker pools, use Docker Compose:
|
|
|
78
78
|
git clone https://github.com/SushantGautam/SimpleAuditStudio
|
|
79
79
|
cd SimpleAuditStudio
|
|
80
80
|
cp .env.example .env
|
|
81
|
-
# edit POSTGRES_PASSWORD and
|
|
81
|
+
# edit POSTGRES_PASSWORD and BOOTSTRAP_PASSWORD at minimum
|
|
82
82
|
docker compose up -d
|
|
83
83
|
```
|
|
84
84
|
|
|
@@ -25,7 +25,7 @@ A self-hostable platform for running reproducible AI model audits using the [Sim
|
|
|
25
25
|
uvx simpleaudit-studio
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
[`uvx`](https://docs.astral.sh/uv/#uvx) installs the [`simpleaudit-studio`](https://pypi.org/project/simpleaudit-studio/) package and runs it in an isolated environment. No Docker, no Postgres, no manual setup. Opens at http://localhost:8000 (login: `admin` / `admin12345`). A mock model server is pre-seeded so you can
|
|
28
|
+
[`uvx`](https://docs.astral.sh/uv/#uvx) installs the [`simpleaudit-studio`](https://pypi.org/project/simpleaudit-studio/) package and runs it in an isolated environment. No Docker, no Postgres, no manual setup. Opens at http://localhost:8000 (login: `admin` / `admin12345`). A mock model server is pre-seeded so you can start exploring audit results immediately.
|
|
29
29
|
|
|
30
30
|
🌐 Or skip the setup entirely — try the live demo: <a href="https://sushantgautam-simpleaudit-studio.hf.space" target="_blank" rel="noopener noreferrer">sushantgautam-simpleaudit-studio.hf.space</a>
|
|
31
31
|
|
|
@@ -51,7 +51,7 @@ For teams, multi-user setups, or GPU worker pools, use Docker Compose:
|
|
|
51
51
|
git clone https://github.com/SushantGautam/SimpleAuditStudio
|
|
52
52
|
cd SimpleAuditStudio
|
|
53
53
|
cp .env.example .env
|
|
54
|
-
# edit POSTGRES_PASSWORD and
|
|
54
|
+
# edit POSTGRES_PASSWORD and BOOTSTRAP_PASSWORD at minimum
|
|
55
55
|
docker compose up -d
|
|
56
56
|
```
|
|
57
57
|
|
|
@@ -24,7 +24,7 @@ def bootstrap_admin_and_default_project(
|
|
|
24
24
|
"""Idempotently create the initial admin user and default project."""
|
|
25
25
|
user, created = User.objects.get_or_create(
|
|
26
26
|
username=username,
|
|
27
|
-
defaults={"email": email, "is_staff": True
|
|
27
|
+
defaults={"email": email, "is_staff": True},
|
|
28
28
|
)
|
|
29
29
|
# Always ensure the admin password matches the configured value. This makes
|
|
30
30
|
# the bootstrap idempotent across container restarts where the Postgres data
|
|
@@ -39,9 +39,6 @@ def bootstrap_admin_and_default_project(
|
|
|
39
39
|
if not user.is_staff:
|
|
40
40
|
user.is_staff = True
|
|
41
41
|
user.save(update_fields=["is_staff"])
|
|
42
|
-
if not user.is_superuser:
|
|
43
|
-
user.is_superuser = True
|
|
44
|
-
user.save(update_fields=["is_superuser"])
|
|
45
42
|
|
|
46
43
|
slug = slugify(project_name) or "default"
|
|
47
44
|
default_description = "Public common workspaces visible to all users, used for demo"
|
|
@@ -237,7 +237,7 @@ WORKOS_ENABLED = bool(WORKOS_CLIENT_ID and WORKOS_API_KEY)
|
|
|
237
237
|
APP_BASE_URL = os.environ.get("APP_BASE_URL", "http://localhost:8000").rstrip("/")
|
|
238
238
|
|
|
239
239
|
DEMO_MODE = env_bool("DEMO_MODE", False)
|
|
240
|
-
DEMO_USERNAME = os.environ.get("DEMO_USERNAME", "
|
|
240
|
+
DEMO_USERNAME = os.environ.get("DEMO_USERNAME", "studio")
|
|
241
241
|
DEMO_PASSWORD = os.environ.get("DEMO_PASSWORD", "admin123")
|
|
242
242
|
|
|
243
243
|
# Framing policy:
|
|
@@ -9,7 +9,7 @@ no real API keys (the mock-model service stands in for all three endpoints).
|
|
|
9
9
|
|
|
10
10
|
Usage:
|
|
11
11
|
python deploy/e2e_smoke.py [base_url] # default http://localhost:8000
|
|
12
|
-
Reads
|
|
12
|
+
Reads BOOTSTRAP_USERNAME/EMAIL/PASSWORD from .env.
|
|
13
13
|
"""
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
@@ -70,10 +70,10 @@ class Client:
|
|
|
70
70
|
def main():
|
|
71
71
|
base_url = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:8000"
|
|
72
72
|
env = _load_env()
|
|
73
|
-
username = env.get("
|
|
74
|
-
password = env.get("
|
|
73
|
+
username = env.get("BOOTSTRAP_USERNAME", "studio")
|
|
74
|
+
password = env.get("BOOTSTRAP_PASSWORD", "")
|
|
75
75
|
if not password:
|
|
76
|
-
print("FATAL:
|
|
76
|
+
print("FATAL: BOOTSTRAP_PASSWORD not set in .env")
|
|
77
77
|
sys.exit(2)
|
|
78
78
|
|
|
79
79
|
c = Client(base_url)
|
|
@@ -133,7 +133,7 @@ RUN_STATUS_DISTRIBUTION = (
|
|
|
133
133
|
|
|
134
134
|
def main():
|
|
135
135
|
project = Project.objects.get(id=1)
|
|
136
|
-
admin = User.objects.get(username="
|
|
136
|
+
admin = User.objects.get(username="studio")
|
|
137
137
|
|
|
138
138
|
# ─── 1. Model Endpoints (15) ───────────────────────────────────────────────
|
|
139
139
|
print(f"Model endpoints before: {ModelEndpoint.objects.count()}")
|
|
@@ -177,7 +177,7 @@ services:
|
|
|
177
177
|
# collection, then serve with gunicorn. The seed is safe on every start:
|
|
178
178
|
# it skips anything already present. Set SEED_ON_BOOT=false to opt out.
|
|
179
179
|
# Set SEED_DEMO_AUDITS=false to skip demo audit runs only.
|
|
180
|
-
command: 'sh -c "python manage.py migrate --noinput && python manage.py bootstrap_platform --username ${
|
|
180
|
+
command: 'sh -c "python manage.py migrate --noinput && python manage.py bootstrap_platform --username ${BOOTSTRAP_USERNAME:-studio} --email ${BOOTSTRAP_EMAIL:-admin@example.local} --password ${BOOTSTRAP_PASSWORD:?Set BOOTSTRAP_PASSWORD in .env} --project-name ${BOOTSTRAP_PROJECT_NAME:-Default} && if [ "${SEED_ON_BOOT:-true}" != "false" ]; then SEED_ARGS=\"\"; if [ \"${SEED_DEMO_AUDITS:-true}\" = \"false\" ]; then SEED_ARGS=\"--skip-demo-audits\"; fi; python manage.py seed_platform $SEED_ARGS || echo ''WARNING: seed_platform failed - continuing without seed data''; fi && python manage.py collectstatic --noinput && gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 2"'
|
|
181
181
|
healthcheck:
|
|
182
182
|
test: ["CMD", "curl", "-fsS", "http://localhost:8000/healthz"]
|
|
183
183
|
interval: 30s
|
|
@@ -7,7 +7,7 @@ with bootstrap_platform already run.
|
|
|
7
7
|
Run locally:
|
|
8
8
|
docker compose up -d
|
|
9
9
|
docker compose exec web python manage.py bootstrap_platform \
|
|
10
|
-
--username
|
|
10
|
+
--username studio --email studio@example.local --password e2etestpass123 \
|
|
11
11
|
--project-name Research
|
|
12
12
|
.venv/bin/python e2e/test_ui.py
|
|
13
13
|
"""
|
|
@@ -22,7 +22,7 @@ except ImportError:
|
|
|
22
22
|
sys.exit(1)
|
|
23
23
|
|
|
24
24
|
BASE_URL = "http://localhost:8000"
|
|
25
|
-
ADMIN_USER = "
|
|
25
|
+
ADMIN_USER = "studio"
|
|
26
26
|
ADMIN_PASS = "e2etestpass123"
|
|
27
27
|
|
|
28
28
|
|
|
@@ -11,9 +11,9 @@ class Command(BaseCommand):
|
|
|
11
11
|
help = "Create initial admin user and default project if missing."
|
|
12
12
|
|
|
13
13
|
def add_arguments(self, parser):
|
|
14
|
-
parser.add_argument("--username", default=os.environ.get("
|
|
15
|
-
parser.add_argument("--email", default=os.environ.get("
|
|
16
|
-
parser.add_argument("--password", default=os.environ.get("
|
|
14
|
+
parser.add_argument("--username", default=os.environ.get("BOOTSTRAP_USERNAME", "studio"))
|
|
15
|
+
parser.add_argument("--email", default=os.environ.get("BOOTSTRAP_EMAIL", "admin@example.local"))
|
|
16
|
+
parser.add_argument("--password", default=os.environ.get("BOOTSTRAP_PASSWORD", ""))
|
|
17
17
|
parser.add_argument("--project-name", default=os.environ.get("BOOTSTRAP_PROJECT_NAME", "Default"))
|
|
18
18
|
|
|
19
19
|
def handle(self, *args, **options):
|
|
@@ -25,7 +25,7 @@ class Command(BaseCommand):
|
|
|
25
25
|
raise CommandError("Refusing to bootstrap with unsafe configuration.")
|
|
26
26
|
if not password:
|
|
27
27
|
raise CommandError(
|
|
28
|
-
"Bootstrap password is required. Set
|
|
28
|
+
"Bootstrap password is required. Set BOOTSTRAP_PASSWORD or pass --password."
|
|
29
29
|
)
|
|
30
30
|
user, project = bootstrap_admin_and_default_project(
|
|
31
31
|
username=options["username"],
|
|
@@ -10,9 +10,9 @@ config:
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
+
import atexit
|
|
13
14
|
import logging
|
|
14
15
|
import os
|
|
15
|
-
import tempfile
|
|
16
16
|
import threading
|
|
17
17
|
from typing import Any
|
|
18
18
|
|
|
@@ -28,11 +28,25 @@ def is_minimal_config() -> bool:
|
|
|
28
28
|
return os.environ.get("SIMPLEAUDIT_MINIMAL", "").strip() == "1"
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
def _data_dir() -> str:
|
|
32
|
+
"""Return (and create) the persistent data dir for the embedded Postgres.
|
|
33
|
+
|
|
34
|
+
A fixed location avoids re-running initdb (~218M, ~15s) on every start.
|
|
35
|
+
Override with SIMPLEAUDIT_EMBEDDED_PG_DIR if needed.
|
|
36
|
+
"""
|
|
37
|
+
d = os.path.expanduser(
|
|
38
|
+
os.environ.get("SIMPLEAUDIT_EMBEDDED_PG_DIR", "~/.simpleaudit-studio/embedded-pg")
|
|
39
|
+
)
|
|
40
|
+
os.makedirs(d, exist_ok=True)
|
|
41
|
+
return d
|
|
42
|
+
|
|
43
|
+
|
|
31
44
|
def start_embedded_hatchet() -> Any:
|
|
32
45
|
"""Start an embedded Hatchet engine and return the client.
|
|
33
46
|
|
|
34
47
|
The sidecar binary downloads on first use (~53 MB) and caches at
|
|
35
|
-
~/.hatchet/embedded/.
|
|
48
|
+
~/.hatchet/embedded/. The Postgres cluster persists in
|
|
49
|
+
~/.simpleaudit-studio/embedded-pg/, so only the first start is slow.
|
|
36
50
|
|
|
37
51
|
Returns the Hatchet client instance. Raises on failure.
|
|
38
52
|
"""
|
|
@@ -43,8 +57,7 @@ def start_embedded_hatchet() -> Any:
|
|
|
43
57
|
|
|
44
58
|
from hatchet_sdk import ClientConfig, EmbeddedHatchetConfig, Hatchet
|
|
45
59
|
|
|
46
|
-
|
|
47
|
-
data_dir = tempfile.mkdtemp(prefix="simpleaudit-hatchet-pg-")
|
|
60
|
+
data_dir = _data_dir()
|
|
48
61
|
logger.info("Embedded Hatchet data dir: %s", data_dir)
|
|
49
62
|
|
|
50
63
|
config = ClientConfig(
|
|
@@ -76,3 +89,7 @@ def stop_embedded_hatchet() -> None:
|
|
|
76
89
|
def get_embedded_client() -> Any | None:
|
|
77
90
|
"""Return the running embedded Hatchet client, or None."""
|
|
78
91
|
return _embedded_client
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# Best-effort clean shutdown even if the caller forgets to stop explicitly.
|
|
95
|
+
atexit.register(stop_embedded_hatchet)
|
|
@@ -47,8 +47,8 @@ def validate_startup_environment(bootstrap_password: str | None = None) -> list[
|
|
|
47
47
|
if minio_access_key in {"", "change-me"} or minio_secret_key in {"", "change-me"}:
|
|
48
48
|
errors.append("MINIO_ACCESS_KEY and MINIO_SECRET_KEY must be set when MINIO_ENDPOINT is configured.")
|
|
49
49
|
|
|
50
|
-
effective_bootstrap_password = bootstrap_password if bootstrap_password is not None else os.environ.get("
|
|
50
|
+
effective_bootstrap_password = bootstrap_password if bootstrap_password is not None else os.environ.get("BOOTSTRAP_PASSWORD", "")
|
|
51
51
|
if effective_bootstrap_password in {"", "change-me"}:
|
|
52
|
-
errors.append("
|
|
52
|
+
errors.append("BOOTSTRAP_PASSWORD must be set and must not be change-me.")
|
|
53
53
|
|
|
54
54
|
return errors
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from contextlib import contextmanager
|
|
3
3
|
|
|
4
|
+
from django.contrib.auth import get_user_model
|
|
4
5
|
from django.core.management import call_command
|
|
5
6
|
from django.test import TestCase
|
|
6
7
|
|
|
7
8
|
from accounts.models import Project, ProjectMembership
|
|
8
9
|
|
|
10
|
+
User = get_user_model()
|
|
11
|
+
|
|
9
12
|
|
|
10
13
|
@contextmanager
|
|
11
14
|
def _safe_env():
|
|
@@ -36,7 +39,7 @@ class BootstrapTests(TestCase):
|
|
|
36
39
|
for _ in range(2):
|
|
37
40
|
call_command(
|
|
38
41
|
"bootstrap_platform",
|
|
39
|
-
username="
|
|
42
|
+
username="studio",
|
|
40
43
|
email="admin@example.local",
|
|
41
44
|
password="admin-pass-123",
|
|
42
45
|
project_name="Default",
|
|
@@ -46,3 +49,20 @@ class BootstrapTests(TestCase):
|
|
|
46
49
|
memberships = ProjectMembership.objects.filter(project=project)
|
|
47
50
|
self.assertEqual(memberships.count(), 1)
|
|
48
51
|
self.assertEqual(memberships.first().role, ProjectMembership.Role.ADMIN)
|
|
52
|
+
|
|
53
|
+
def test_bootstrap_admin_is_not_superuser(self):
|
|
54
|
+
"""The bootstrap admin must be a normal account (is_staff only), not a
|
|
55
|
+
Django superuser. Superuser status bypasses all workspace membership
|
|
56
|
+
checks, making the admin appear as owner of every workspace."""
|
|
57
|
+
with _safe_env():
|
|
58
|
+
call_command(
|
|
59
|
+
"bootstrap_platform",
|
|
60
|
+
username="studio",
|
|
61
|
+
email="admin@example.local",
|
|
62
|
+
password="admin-pass-123",
|
|
63
|
+
project_name="Default",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
user = User.objects.get(username="studio")
|
|
67
|
+
self.assertFalse(user.is_superuser)
|
|
68
|
+
self.assertTrue(user.is_staff)
|
|
@@ -26,12 +26,12 @@ class TestDemoBootSequence(TestCase):
|
|
|
26
26
|
from model_registry.models import ModelEndpoint
|
|
27
27
|
|
|
28
28
|
user, project = bootstrap_admin_and_default_project(
|
|
29
|
-
username="
|
|
29
|
+
username="studio",
|
|
30
30
|
email="admin@localhost",
|
|
31
31
|
password="admin12345",
|
|
32
32
|
project_name="Demo Project",
|
|
33
33
|
)
|
|
34
|
-
self.assertEqual(user.username, "
|
|
34
|
+
self.assertEqual(user.username, "studio")
|
|
35
35
|
self.assertEqual(project.name, "Demo Project")
|
|
36
36
|
|
|
37
37
|
call_command("seed_platform", project=project.id, verbosity=0)
|
|
@@ -56,7 +56,7 @@ class TestDemoBootSequence(TestCase):
|
|
|
56
56
|
from model_registry.models import ModelEndpoint
|
|
57
57
|
|
|
58
58
|
_, project = bootstrap_admin_and_default_project(
|
|
59
|
-
username="
|
|
59
|
+
username="studio", email="admin@localhost", password="admin12345",
|
|
60
60
|
project_name="Demo Project",
|
|
61
61
|
)
|
|
62
62
|
call_command("seed_platform", project=project.id, verbosity=0)
|
|
@@ -85,6 +85,30 @@ class TestEmbeddedHatchetLifecycle(TestCase):
|
|
|
85
85
|
(~15s first run) but verify the full lifecycle works.
|
|
86
86
|
"""
|
|
87
87
|
|
|
88
|
+
def test_data_dir_is_persistent(self):
|
|
89
|
+
"""The embedded Postgres data dir is a fixed location, not a temp dir."""
|
|
90
|
+
from infra.minimal_config import _data_dir
|
|
91
|
+
|
|
92
|
+
d1 = _data_dir()
|
|
93
|
+
d2 = _data_dir()
|
|
94
|
+
self.assertEqual(d1, d2)
|
|
95
|
+
self.assertTrue(os.path.isdir(d1))
|
|
96
|
+
# Must not be a per-run temp dir (the old leak: 37 x 218M in $TMPDIR)
|
|
97
|
+
self.assertNotIn("simpleaudit-hatchet-pg-", os.path.basename(d1))
|
|
98
|
+
|
|
99
|
+
def test_data_dir_env_override(self):
|
|
100
|
+
"""SIMPLEAUDIT_EMBEDDED_PG_DIR overrides the default location."""
|
|
101
|
+
import tempfile as _tempfile
|
|
102
|
+
|
|
103
|
+
from infra.minimal_config import _data_dir
|
|
104
|
+
|
|
105
|
+
with patch.dict(os.environ, {"SIMPLEAUDIT_EMBEDDED_PG_DIR": "/tmp/custom-pg-dir-test"}):
|
|
106
|
+
d = _data_dir()
|
|
107
|
+
self.assertEqual(d, "/tmp/custom-pg-dir-test")
|
|
108
|
+
self.assertTrue(os.path.isdir(d))
|
|
109
|
+
import shutil
|
|
110
|
+
shutil.rmtree("/tmp/custom-pg-dir-test", ignore_errors=True)
|
|
111
|
+
|
|
88
112
|
def test_start_and_stop(self):
|
|
89
113
|
from infra.minimal_config import start_embedded_hatchet, stop_embedded_hatchet
|
|
90
114
|
|
|
@@ -35,7 +35,7 @@ def _safe_env():
|
|
|
35
35
|
class SeedPlatformTests(TestCase):
|
|
36
36
|
def setUp(self):
|
|
37
37
|
self.user = User.objects.create_superuser(
|
|
38
|
-
username="
|
|
38
|
+
username="studio", email="admin@example.local", password="pass12345"
|
|
39
39
|
)
|
|
40
40
|
self.project = Project.objects.create(name="Default", slug="default")
|
|
41
41
|
ProjectMembership.objects.create(
|
|
@@ -99,8 +99,8 @@ def main() -> None:
|
|
|
99
99
|
# Give the web server a moment to bind
|
|
100
100
|
time.sleep(1)
|
|
101
101
|
|
|
102
|
-
username = os.environ.get("
|
|
103
|
-
password = os.environ.get("
|
|
102
|
+
username = os.environ.get("BOOTSTRAP_USERNAME", "studio")
|
|
103
|
+
password = os.environ.get("BOOTSTRAP_PASSWORD", "admin12345")
|
|
104
104
|
|
|
105
105
|
print("┌─────────────────────────────────────────────────────────┐")
|
|
106
106
|
print("│ │")
|
|
@@ -140,9 +140,9 @@ def _seed_demo_data() -> None:
|
|
|
140
140
|
from django.core.management import call_command
|
|
141
141
|
|
|
142
142
|
# Respect env vars (set by Dockerfile for HF Space, or defaults for local)
|
|
143
|
-
username = os.environ.get("
|
|
144
|
-
email = os.environ.get("
|
|
145
|
-
password = os.environ.get("
|
|
143
|
+
username = os.environ.get("BOOTSTRAP_USERNAME", "studio")
|
|
144
|
+
email = os.environ.get("BOOTSTRAP_EMAIL", "admin@localhost")
|
|
145
|
+
password = os.environ.get("BOOTSTRAP_PASSWORD", "admin12345")
|
|
146
146
|
project_name = os.environ.get("BOOTSTRAP_PROJECT_NAME", "Demo Project")
|
|
147
147
|
|
|
148
148
|
user, project = bootstrap_admin_and_default_project(
|