simpleaudit-studio 0.3.0__tar.gz → 0.4.0__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.3.0 → simpleaudit_studio-0.4.0}/.env.example +3 -2
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/.env.local.example +13 -28
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/PKG-INFO +27 -17
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/README.md +25 -15
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/accounts/services.py +8 -1
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/audits/comparison.py +17 -11
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/audits/experiments.py +111 -25
- simpleaudit_studio-0.4.0/audits/migrations/0003_judges.py +92 -0
- simpleaudit_studio-0.4.0/audits/migrations/0004_judge_model_per_run.py +72 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/audits/models.py +10 -2
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/audits/monitors.py +57 -18
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/audits/serializers.py +5 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/audits/services.py +106 -10
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/audits/views.py +13 -1
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/config/settings.py +10 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/config/urls.py +9 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/docs/architecture.md +4 -2
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/docs/deployment.md +3 -1
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/docs/domain-model.md +65 -9
- simpleaudit_studio-0.4.0/infra/codegen.py +356 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/context_processors.py +40 -0
- simpleaudit_studio-0.4.0/infra/engine.py +464 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/hashing.py +5 -1
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/health.py +12 -1
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/management/commands/dev_server.py +30 -8
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/management/commands/seed_demo_audits.py +12 -1
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/management/commands/seed_platform.py +4 -0
- simpleaudit_studio-0.4.0/infra/minimal_config.py +210 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/runs_table.py +17 -8
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/seed.py +19 -1
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/seo.py +15 -1
- simpleaudit_studio-0.4.0/infra/templatetags/json_filters.py +13 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/factories.py +22 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_api_e2e.py +2 -2
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_audit_clone.py +2 -1
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_audit_poll_results.py +2 -1
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_audit_run_freeze.py +5 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_audit_sse.py +2 -1
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_audit_submission.py +2 -0
- simpleaudit_studio-0.4.0/infra/tests/test_bootstrap.py +82 -0
- simpleaudit_studio-0.4.0/infra/tests/test_codegen.py +392 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_comparison.py +2 -1
- simpleaudit_studio-0.4.0/infra/tests/test_connection_sharing.py +260 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_engine_integration.py +80 -11
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_experiments.py +79 -5
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_health_panel.py +18 -0
- simpleaudit_studio-0.4.0/infra/tests/test_judges.py +401 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_monitors.py +10 -2
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_profile_ui.py +1 -1
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_purge_test_data.py +3 -2
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_scenarios.py +55 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_snapshot_secrets.py +38 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_write_guard.py +25 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/ui.py +343 -162
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/worker.py +10 -0
- simpleaudit_studio-0.4.0/judges/admin.py +19 -0
- simpleaudit_studio-0.4.0/judges/apps.py +7 -0
- simpleaudit_studio-0.4.0/judges/migrations/0001_judges.py +122 -0
- simpleaudit_studio-0.4.0/judges/migrations/0002_judge_model_per_run.py +18 -0
- simpleaudit_studio-0.4.0/judges/migrations/0003_composable_judges.py +96 -0
- simpleaudit_studio-0.4.0/judges/models.py +86 -0
- simpleaudit_studio-0.4.0/judges/services.py +400 -0
- simpleaudit_studio-0.4.0/judges/views.py +271 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/manage.py +5 -0
- simpleaudit_studio-0.4.0/model_registry/migrations/0002_descriptions.py +22 -0
- simpleaudit_studio-0.4.0/model_registry/migrations/0003_modelconnection_shared_with_and_more.py +33 -0
- simpleaudit_studio-0.4.0/model_registry/migrations/__init__.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/model_registry/models.py +35 -1
- simpleaudit_studio-0.4.0/model_registry/services.py +203 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/model_registry/views.py +13 -3
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/pyproject.toml +2 -2
- simpleaudit_studio-0.4.0/scenarios/__init__.py +0 -0
- simpleaudit_studio-0.4.0/scenarios/migrations/0002_add_severity_ceiling_documents_file_uri.py +27 -0
- simpleaudit_studio-0.4.0/scenarios/migrations/__init__.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/scenarios/models.py +16 -2
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/scenarios/serializers.py +17 -1
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/scenarios/services.py +64 -5
- simpleaudit_studio-0.4.0/static/js/version-diff.js +86 -0
- simpleaudit_studio-0.4.0/static/og-image.png +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/admin.html +2 -0
- simpleaudit_studio-0.4.0/templates/base.html +160 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/compare.html +4 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/dashboard.html +3 -1
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/experiment_new.html +44 -11
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/experiment_review.html +5 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/health.html +23 -13
- simpleaudit_studio-0.4.0/templates/judge_detail.html +368 -0
- simpleaudit_studio-0.4.0/templates/judges.html +104 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/models.html +111 -22
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/monitor_detail.html +3 -3
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/monitors.html +1 -1
- simpleaudit_studio-0.4.0/templates/partials/judge_picker.html +71 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/partials/model_pickers.html +19 -6
- simpleaudit_studio-0.4.0/templates/partials/need_help.html +5 -0
- simpleaudit_studio-0.4.0/templates/partials/pinned_version.html +4 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/partials/result_rep_panel.html +21 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/partials/run_settings.html +11 -5
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/partials/run_settings_js.html +1 -1
- simpleaudit_studio-0.4.0/templates/partials/severity_badge.html +1 -0
- simpleaudit_studio-0.4.0/templates/partials/sidebar.html +83 -0
- simpleaudit_studio-0.4.0/templates/partials/text_file_modal.html +96 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/partials/tooltips.html +2 -2
- simpleaudit_studio-0.4.0/templates/partials/version_badge.html +6 -0
- simpleaudit_studio-0.4.0/templates/partials/version_diff_js.html +2 -0
- simpleaudit_studio-0.4.0/templates/partials/version_diff_modal.html +29 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/run_detail.html +47 -4
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/run_result.html +2 -2
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/scenarios.html +131 -200
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/seo/head.html +4 -1
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/uv.lock +7 -7
- simpleaudit_studio-0.3.0/infra/engine.py +0 -636
- simpleaudit_studio-0.3.0/infra/minimal_config.py +0 -95
- simpleaudit_studio-0.3.0/infra/tests/test_bootstrap.py +0 -42
- simpleaudit_studio-0.3.0/model_registry/services.py +0 -78
- simpleaudit_studio-0.3.0/templates/base.html +0 -241
- simpleaudit_studio-0.3.0/templates/partials/severity_badge.html +0 -1
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/.dockerignore +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/.github/workflows/ci.yml +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/.github/workflows/docker.yml +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/.github/workflows/publish.yml +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/.gitignore +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/Dockerfile +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/LICENSE +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/accounts/__init__.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/accounts/admin.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/accounts/admin_urls.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/accounts/apps.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/accounts/auth_urls.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/accounts/migrations/0001_initial.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/accounts/migrations/0002_user_preferences.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/accounts/migrations/__init__.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/accounts/models.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/accounts/project_urls.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/accounts/serializers.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/accounts/views.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/accounts/workos_auth.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/audits/__init__.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/audits/admin.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/audits/apps.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/audits/events.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/audits/migrations/0001_initial.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/audits/migrations/0002_strip_api_keys_from_snapshots.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/audits/migrations/__init__.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/audits/urls.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/config/__init__.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/config/asgi.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/config/wsgi.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/deploy/compose/Dockerfile +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/deploy/mock_openai_server.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/deploy/postgres-init.sql +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/docker-compose.yml +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/e2e/test_ui.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/__init__.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/admin.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/apps.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/db.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/exceptions.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/fixtures/demo_audit_results.json +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/health_api.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/logging.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/management/__init__.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/management/commands/__init__.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/management/commands/bootstrap_platform.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/management/commands/purge_test_data.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/management/commands/run_monitors.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/management/commands/run_worker.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/management/commands/setup_local.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/middleware.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/readiness.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/simpleaudit_package.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/startup_checks.py +0 -0
- {simpleaudit_studio-0.3.0/model_registry → simpleaudit_studio-0.4.0/infra/templatetags}/__init__.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/__init__.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_admin_api.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_admin_page.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_audit_archive.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_audit_events.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_audit_rename.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_audit_results_live.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_audit_timing.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_auth_projects.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_connection_delete.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_db_resilience.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_health.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_middleware.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_minimal_config.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_model_registry.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_profile.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_runs_table.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_scenario_import_export.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_seed_platform.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_smoke_all_pages.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_workspace_archive.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_workspaces.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/test_workspaces_ui.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/infra/tests/utils.py +0 -0
- {simpleaudit_studio-0.3.0/model_registry/migrations → simpleaudit_studio-0.4.0/judges}/__init__.py +0 -0
- {simpleaudit_studio-0.3.0/scenarios → simpleaudit_studio-0.4.0/judges/migrations}/__init__.py +0 -0
- {simpleaudit_studio-0.3.0/scenarios/migrations → simpleaudit_studio-0.4.0/model_registry}/__init__.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/model_registry/admin.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/model_registry/apps.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/model_registry/migrations/0001_initial.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/model_registry/serializers.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/model_registry/urls.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/scenarios/admin.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/scenarios/apps.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/scenarios/migrations/0001_initial.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/scenarios/urls.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/scenarios/views.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/simpleaudit_studio/__init__.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/simpleaudit_studio/cli.py +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/static/favicon-16x16.svg +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/static/favicon-32x32.svg +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/static/favicon.svg +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/static/js/diff.min.js +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/static/logo.png +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/static/logo.svg +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/404.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/auth/login.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/auth/register.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/auth/workos_login.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/auth/workos_verify.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/experiment_detail.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/experiments.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/partials/auth_header.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/partials/chip_input.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/partials/compare_run_header.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/partials/footer.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/partials/local_time_js.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/partials/multi_filter.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/partials/not_found.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/partials/run_results.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/partials/ui_js.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/profile.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/public/base_public.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/public/landing.html +0 -0
- {simpleaudit_studio-0.3.0 → simpleaudit_studio-0.4.0}/templates/workspaces.html +0 -0
|
@@ -76,8 +76,9 @@ WORKER_POOL=cpu
|
|
|
76
76
|
# against an unmerged revision, create a gitignored simpleaudit-dependency.yaml
|
|
77
77
|
# at the project root with a single `git_ref:` key — do NOT edit pyproject.toml.
|
|
78
78
|
|
|
79
|
-
# --- Model API
|
|
80
|
-
#
|
|
79
|
+
# --- Model API keys (optional) ---------------------------------------------------
|
|
80
|
+
# Keys can be stored on a connection in the Models page. For connections set to
|
|
81
|
+
# "Environment variable", add that variable here (read when a run starts), e.g.:
|
|
81
82
|
# SIMULACHAT_API_KEY=sk-...
|
|
82
83
|
|
|
83
84
|
# --- Demo audit seeding ---------------------------------------------------------
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
# =============================================================================
|
|
2
|
-
# SimpleAudit
|
|
2
|
+
# SimpleAudit Studio — local development
|
|
3
3
|
#
|
|
4
|
-
# Copy to `.env`
|
|
4
|
+
# Copy to `.env` (manage.py loads it automatically), then:
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# (see `.env.example`).
|
|
6
|
+
# uv sync --extra dev
|
|
7
|
+
# uv run manage.py setup_local # first time: + seed data
|
|
8
|
+
# uv run manage.py dev_server --embedded # daily
|
|
10
9
|
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# 2. Docker, for Postgres + Hatchet (the "partial Docker" pattern below)
|
|
14
|
-
# 3. A SimpleAudit engine checkout (for the worker to execute audits)
|
|
10
|
+
# SQLite holds the app data and `--embedded` starts its own Hatchet queue, so
|
|
11
|
+
# no Docker is needed. For production use Docker Compose with `.env.example`.
|
|
15
12
|
# =============================================================================
|
|
16
13
|
|
|
17
14
|
# --- Django -----------------------------------------------------------------
|
|
@@ -40,23 +37,10 @@ POSTGRES_HOST=localhost
|
|
|
40
37
|
POSTGRES_PORT=5432
|
|
41
38
|
|
|
42
39
|
# --- Hatchet (durable job queue) ----------------------------------------------
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
# That gives you:
|
|
48
|
-
# - Postgres on localhost:5432 (Hatchet's own queue DB, created by
|
|
49
|
-
# deploy/postgres-init.sql on first init)
|
|
50
|
-
# - Hatchet HTTP API on localhost:8888 (dashboard at http://localhost:8888)
|
|
51
|
-
# - Hatchet gRPC on localhost:7077
|
|
52
|
-
#
|
|
53
|
-
# The auth-disabled hatchet-lite-dev image writes its worker JWT to its config
|
|
54
|
-
# volume. To use it from a local (non-container) worker, copy the token out:
|
|
55
|
-
#
|
|
40
|
+
# Not used with `dev_server --embedded`, which starts its own engine.
|
|
41
|
+
# Only for plain `dev_server` against an external Hatchet, e.g.
|
|
42
|
+
# `docker compose up -d postgres hatchet-server`, then copy its worker token:
|
|
56
43
|
# docker compose cp hatchet-server:/config/authdisabled-token ./hatchet-token
|
|
57
|
-
#
|
|
58
|
-
# and point HATCHET_TOKEN_FILE at it (below). Alternatively set HATCHET_API_KEY
|
|
59
|
-
# directly to the token's contents.
|
|
60
44
|
HATCHET_SERVER_URL=http://localhost:8888
|
|
61
45
|
HATCHET_GRPC_URL=localhost:7077
|
|
62
46
|
HATCHET_API_KEY=
|
|
@@ -75,8 +59,9 @@ WORKER_POOL=cpu
|
|
|
75
59
|
# That value is recorded as the run's git_commit provenance. Do NOT edit
|
|
76
60
|
# pyproject.toml for a dev override.
|
|
77
61
|
|
|
78
|
-
# --- Model API
|
|
79
|
-
#
|
|
62
|
+
# --- Model API keys (optional) ---------------------------------------------------
|
|
63
|
+
# Keys can be stored on a connection in the Models page. For connections set to
|
|
64
|
+
# "Environment variable", add that variable here (read when a run starts), e.g.:
|
|
80
65
|
# SIMULACHAT_API_KEY=sk-...
|
|
81
66
|
|
|
82
67
|
# --- Optional (leave empty to disable) ------------------------------------------
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: simpleaudit-studio
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Production AI model audit platform — one-liner demo mode via uvx
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -15,7 +15,7 @@ Requires-Dist: pydantic<3,>=2.7
|
|
|
15
15
|
Requires-Dist: python-dotenv==1.1.1
|
|
16
16
|
Requires-Dist: requests==2.32.5
|
|
17
17
|
Requires-Dist: sentry-sdk[django]>=2.0
|
|
18
|
-
Requires-Dist: simpleaudit==0.2.
|
|
18
|
+
Requires-Dist: simpleaudit==0.2.3
|
|
19
19
|
Requires-Dist: workos==10.4.0
|
|
20
20
|
Provides-Extra: dev
|
|
21
21
|
Requires-Dist: factory-boy==3.3.2; extra == 'dev'
|
|
@@ -82,37 +82,45 @@ Set the API key in the connection form (leave blank for local servers that don't
|
|
|
82
82
|
|
|
83
83
|
## 🛠️ Local Development
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
Needs Python 3.11+ and [uv](https://docs.astral.sh/uv/). No Docker, no Makefile.
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
- **`uv run <cmd>`** — run any command inside `.venv` (auto-syncs first if the lockfile changed)
|
|
89
|
-
|
|
90
|
-
### Setup (one time)
|
|
87
|
+
### Setup (once)
|
|
91
88
|
|
|
92
89
|
```bash
|
|
93
90
|
git clone https://github.com/SushantGautam/SimpleAuditStudio
|
|
94
91
|
cd SimpleAuditStudio
|
|
95
|
-
cp .env.local.example .env
|
|
92
|
+
cp .env.local.example .env # SQLite + embedded queue; admin is studio / BOOTSTRAP_PASSWORD
|
|
93
|
+
uv sync --extra dev # create .venv with app + dev tools
|
|
94
|
+
uv run manage.py setup_local # migrate, create admin + workspace, seed scenario packs & models
|
|
95
|
+
```
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
### Daily work
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
uv run manage.py dev_server --embedded # web UI + API + worker + embedded Hatchet → http://localhost:8000
|
|
99
101
|
```
|
|
100
102
|
|
|
101
|
-
|
|
103
|
+
Every start applies migrations and makes sure the admin (a superuser) and default workspace exist, so pulling new code needs no extra steps. Keep in mind:
|
|
102
104
|
|
|
103
|
-
|
|
105
|
+
- Only one `dev_server --embedded` (or `uvx simpleaudit-studio`) can run at a time: they share the embedded queue database.
|
|
106
|
+
- The web server reloads on code changes; the worker does not. Restart `dev_server` after changing `infra/worker.py` or `infra/engine.py`.
|
|
107
|
+
|
|
108
|
+
### Tests and lint
|
|
104
109
|
|
|
105
110
|
```bash
|
|
106
|
-
uv run manage.py
|
|
111
|
+
SIMPLEAUDIT_LOCAL_SQLITE=1 uv run manage.py test infra --exclude-tag embedded_hatchet
|
|
112
|
+
uv run ruff check .
|
|
107
113
|
```
|
|
108
114
|
|
|
109
|
-
Other
|
|
115
|
+
### Other setups
|
|
116
|
+
|
|
110
117
|
```bash
|
|
111
|
-
uv run manage.py
|
|
112
|
-
uv run manage.py dev_server
|
|
113
|
-
SIMPLEAUDIT_LOCAL_SQLITE=1 uv run manage.py test infra # tests
|
|
118
|
+
uv run manage.py dev_server --no-worker # web UI + API only
|
|
119
|
+
uv run manage.py dev_server # use the Postgres + Hatchet configured in .env (see .env.example)
|
|
114
120
|
```
|
|
115
121
|
|
|
122
|
+
See [docs/deployment.md](docs/deployment.md) for every environment variable and management command.
|
|
123
|
+
|
|
116
124
|
## 🐳 Self-Hosting
|
|
117
125
|
|
|
118
126
|
For teams or multi-user setups, use Docker Compose:
|
|
@@ -132,6 +140,8 @@ See [docs/deployment.md](docs/deployment.md) for production hardening, backups,
|
|
|
132
140
|
## ✨ What You Can Do
|
|
133
141
|
|
|
134
142
|
- Build versioned scenario sets and register OpenAI-compatible models
|
|
143
|
+
- Define versioned judges: start from a SimpleAudit judge (safety, harm, helpfulness, factuality, abstention, checklist, …) and edit its criteria, or write your own criteria as a severity, 1–10 score (with your own dimensions) or yes/no judge; run with any judge model
|
|
144
|
+
- Test your deployment's target system prompt, and compare prompts or judges side by side
|
|
135
145
|
- Run experiments across models, scenario versions and settings, with a review step before launch
|
|
136
146
|
- Watch runs live, including per-repetition results
|
|
137
147
|
- Monitor models for drift on a schedule (interval or cron)
|
|
@@ -52,37 +52,45 @@ Set the API key in the connection form (leave blank for local servers that don't
|
|
|
52
52
|
|
|
53
53
|
## 🛠️ Local Development
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
Needs Python 3.11+ and [uv](https://docs.astral.sh/uv/). No Docker, no Makefile.
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
- **`uv run <cmd>`** — run any command inside `.venv` (auto-syncs first if the lockfile changed)
|
|
59
|
-
|
|
60
|
-
### Setup (one time)
|
|
57
|
+
### Setup (once)
|
|
61
58
|
|
|
62
59
|
```bash
|
|
63
60
|
git clone https://github.com/SushantGautam/SimpleAuditStudio
|
|
64
61
|
cd SimpleAuditStudio
|
|
65
|
-
cp .env.local.example .env
|
|
62
|
+
cp .env.local.example .env # SQLite + embedded queue; admin is studio / BOOTSTRAP_PASSWORD
|
|
63
|
+
uv sync --extra dev # create .venv with app + dev tools
|
|
64
|
+
uv run manage.py setup_local # migrate, create admin + workspace, seed scenario packs & models
|
|
65
|
+
```
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
### Daily work
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
uv run manage.py dev_server --embedded # web UI + API + worker + embedded Hatchet → http://localhost:8000
|
|
69
71
|
```
|
|
70
72
|
|
|
71
|
-
|
|
73
|
+
Every start applies migrations and makes sure the admin (a superuser) and default workspace exist, so pulling new code needs no extra steps. Keep in mind:
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
- Only one `dev_server --embedded` (or `uvx simpleaudit-studio`) can run at a time: they share the embedded queue database.
|
|
76
|
+
- The web server reloads on code changes; the worker does not. Restart `dev_server` after changing `infra/worker.py` or `infra/engine.py`.
|
|
77
|
+
|
|
78
|
+
### Tests and lint
|
|
74
79
|
|
|
75
80
|
```bash
|
|
76
|
-
uv run manage.py
|
|
81
|
+
SIMPLEAUDIT_LOCAL_SQLITE=1 uv run manage.py test infra --exclude-tag embedded_hatchet
|
|
82
|
+
uv run ruff check .
|
|
77
83
|
```
|
|
78
84
|
|
|
79
|
-
Other
|
|
85
|
+
### Other setups
|
|
86
|
+
|
|
80
87
|
```bash
|
|
81
|
-
uv run manage.py
|
|
82
|
-
uv run manage.py dev_server
|
|
83
|
-
SIMPLEAUDIT_LOCAL_SQLITE=1 uv run manage.py test infra # tests
|
|
88
|
+
uv run manage.py dev_server --no-worker # web UI + API only
|
|
89
|
+
uv run manage.py dev_server # use the Postgres + Hatchet configured in .env (see .env.example)
|
|
84
90
|
```
|
|
85
91
|
|
|
92
|
+
See [docs/deployment.md](docs/deployment.md) for every environment variable and management command.
|
|
93
|
+
|
|
86
94
|
## 🐳 Self-Hosting
|
|
87
95
|
|
|
88
96
|
For teams or multi-user setups, use Docker Compose:
|
|
@@ -102,6 +110,8 @@ See [docs/deployment.md](docs/deployment.md) for production hardening, backups,
|
|
|
102
110
|
## ✨ What You Can Do
|
|
103
111
|
|
|
104
112
|
- Build versioned scenario sets and register OpenAI-compatible models
|
|
113
|
+
- Define versioned judges: start from a SimpleAudit judge (safety, harm, helpfulness, factuality, abstention, checklist, …) and edit its criteria, or write your own criteria as a severity, 1–10 score (with your own dimensions) or yes/no judge; run with any judge model
|
|
114
|
+
- Test your deployment's target system prompt, and compare prompts or judges side by side
|
|
105
115
|
- Run experiments across models, scenario versions and settings, with a review step before launch
|
|
106
116
|
- Watch runs live, including per-repetition results
|
|
107
117
|
- Monitor models for drift on a schedule (interval or cron)
|
|
@@ -68,6 +68,10 @@ def bootstrap_admin_and_default_project(
|
|
|
68
68
|
user=user,
|
|
69
69
|
defaults={"role": ProjectMembership.Role.ADMIN},
|
|
70
70
|
)
|
|
71
|
+
if created:
|
|
72
|
+
from infra.seed import seed_workspace
|
|
73
|
+
|
|
74
|
+
seed_workspace(project, user) # standard scenario sets and SimpleAudit's judges
|
|
71
75
|
return user, project
|
|
72
76
|
|
|
73
77
|
|
|
@@ -140,11 +144,14 @@ def create_workspace(*, user, name: str, description: str = "") -> Project:
|
|
|
140
144
|
with transaction.atomic():
|
|
141
145
|
project = Project.objects.create(name=clean_name, slug=candidate, description=(description or "").strip())
|
|
142
146
|
ProjectMembership.objects.create(project=project, user=user, role=ProjectMembership.Role.ADMIN)
|
|
143
|
-
return project
|
|
144
147
|
except IntegrityError:
|
|
145
148
|
if attempt == 4:
|
|
146
149
|
raise StableAPIError(detail="A workspace with this name already exists.", code="workspace_name_conflict", http_status=409)
|
|
147
150
|
continue
|
|
151
|
+
from infra.seed import seed_workspace
|
|
152
|
+
|
|
153
|
+
seed_workspace(project, user) # standard scenario sets and SimpleAudit's judges
|
|
154
|
+
return project
|
|
148
155
|
raise StableAPIError(detail="A workspace with this name already exists.", code="workspace_name_conflict", http_status=409)
|
|
149
156
|
|
|
150
157
|
|
|
@@ -10,6 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
from audits.events import ScenarioResult
|
|
12
12
|
from audits.models import AuditRun
|
|
13
|
+
from audits.services import frozen_judge, frozen_name
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class ComparisonIncompatible(Exception):
|
|
@@ -46,15 +47,15 @@ def compare_runs(project, run_ids: list[int]) -> dict:
|
|
|
46
47
|
warnings.append(f"Scenario set versions differ: {', '.join('v'+str(v) for v in vnums)}. Results may not be directly comparable.")
|
|
47
48
|
|
|
48
49
|
# Check: same judge?
|
|
49
|
-
judge_ids = {r.
|
|
50
|
+
judge_ids = {r.judge_version_id for r in runs}
|
|
50
51
|
if len(judge_ids) > 1:
|
|
51
|
-
judge_names = sorted({r
|
|
52
|
-
warnings.append(f"Different
|
|
52
|
+
judge_names = sorted({f"{frozen_judge(r)['label']} ({frozen_name(r, 'judge')})" for r in runs})
|
|
53
|
+
warnings.append(f"Different judges used: {', '.join(judge_names)}. Judge effects can dominate model effects — interpret with caution.")
|
|
53
54
|
|
|
54
55
|
# Check: same auditor?
|
|
55
56
|
auditor_ids = {r.auditor_model_id for r in runs}
|
|
56
57
|
if len(auditor_ids) > 1:
|
|
57
|
-
auditor_names = sorted({r
|
|
58
|
+
auditor_names = sorted({frozen_name(r, "auditor", with_id=True) for r in runs})
|
|
58
59
|
warnings.append(f"Different auditor models used: {', '.join(auditor_names)}.")
|
|
59
60
|
|
|
60
61
|
# Check: same SimpleAudit version?
|
|
@@ -119,7 +120,7 @@ def compare_runs(project, run_ids: list[int]) -> dict:
|
|
|
119
120
|
r = run_results.get(run.id, {}).get(key)
|
|
120
121
|
entry["runs"][str(run.id)] = {
|
|
121
122
|
"name": run.name,
|
|
122
|
-
"target": run
|
|
123
|
+
"target": frozen_name(run, "target"),
|
|
123
124
|
**(r or {"status": "missing", "severity": None}),
|
|
124
125
|
}
|
|
125
126
|
results.append(entry)
|
|
@@ -127,10 +128,13 @@ def compare_runs(project, run_ids: list[int]) -> dict:
|
|
|
127
128
|
# Build inputs comparison: key parameters that differ between runs
|
|
128
129
|
input_rows = []
|
|
129
130
|
param_defs = [
|
|
130
|
-
("Target model", lambda r: r
|
|
131
|
-
("Auditor model", lambda r: r
|
|
132
|
-
("Judge
|
|
133
|
-
("
|
|
131
|
+
("Target model", lambda r: frozen_name(r, "target", with_id=True)),
|
|
132
|
+
("Auditor model", lambda r: frozen_name(r, "auditor", with_id=True)),
|
|
133
|
+
("Judge", lambda r: frozen_judge(r)["label"]),
|
|
134
|
+
("Judge format", lambda r: frozen_judge(r)["output_label"]),
|
|
135
|
+
("Judge model", lambda r: frozen_name(r, "judge", with_id=True)),
|
|
136
|
+
("System prompt", lambda r: (r.generation_parameters_snapshot or {}).get("system_prompt") or "none"),
|
|
137
|
+
("Scenario set version", lambda r: r.scenario_set_version.label if r.scenario_set_version else "—"),
|
|
134
138
|
("SimpleAudit version", lambda r: f"{r.simpleaudit_version} ({r.git_commit[:8]}…)" if r.simpleaudit_version and r.git_commit else (r.simpleaudit_version or "—")),
|
|
135
139
|
("Temperature (target)", lambda r: (r.generation_parameters_snapshot or {}).get("temperature_target", "—")),
|
|
136
140
|
("Temperature (auditor)", lambda r: (r.generation_parameters_snapshot or {}).get("temperature_auditor", "—")),
|
|
@@ -155,6 +159,8 @@ def compare_runs(project, run_ids: list[int]) -> dict:
|
|
|
155
159
|
url = f"/models/#conn-{r.target_model.connection_id}"
|
|
156
160
|
elif label == "Auditor model" and r.auditor_model_id:
|
|
157
161
|
url = f"/models/#conn-{r.auditor_model.connection_id}"
|
|
162
|
+
elif label == "Judge" and frozen_judge(r)["judge_id"]:
|
|
163
|
+
url = f"/judges/{frozen_judge(r)['judge_id']}/?v={frozen_judge(r)['version']}"
|
|
158
164
|
elif label == "Judge model" and r.judge_model_id:
|
|
159
165
|
url = f"/models/#conn-{r.judge_model.connection_id}"
|
|
160
166
|
elif label == "Scenario set version" and r.scenario_set_version:
|
|
@@ -171,8 +177,8 @@ def compare_runs(project, run_ids: list[int]) -> dict:
|
|
|
171
177
|
"id": r.id,
|
|
172
178
|
"name": r.name,
|
|
173
179
|
"status": r.status,
|
|
174
|
-
"target": r
|
|
175
|
-
"judge": r
|
|
180
|
+
"target": frozen_name(r, "target"),
|
|
181
|
+
"judge": frozen_judge(r)["label"],
|
|
176
182
|
"scenario_set_version": f"v{r.scenario_set_version.version}" if r.scenario_set_version else None,
|
|
177
183
|
"total_scenarios": r.total_scenarios,
|
|
178
184
|
"successful_scenarios": r.successful_scenarios,
|
|
@@ -15,6 +15,7 @@ from django.db import transaction
|
|
|
15
15
|
|
|
16
16
|
from audits.models import AuditRun, Experiment
|
|
17
17
|
from audits.monitors import pass_counts, wilson
|
|
18
|
+
from audits.services import frozen_judge, frozen_name
|
|
18
19
|
|
|
19
20
|
MAX_RUNS_PER_EXPERIMENT = 50
|
|
20
21
|
|
|
@@ -23,15 +24,22 @@ DESIGN_AXES = {
|
|
|
23
24
|
"scenario_set": "Scenario set",
|
|
24
25
|
"target": "Target",
|
|
25
26
|
"auditor": "Auditor",
|
|
27
|
+
"judge_model": "Judge model",
|
|
26
28
|
"judge": "Judge",
|
|
27
29
|
"max_turns": "Max turns",
|
|
28
30
|
"language": "Language",
|
|
29
31
|
}
|
|
30
32
|
# Everything an experiment can compare: the design axes plus per-run edits
|
|
31
|
-
# made on the review screen (repetitions, generation config).
|
|
32
|
-
FACTORS = {**DESIGN_AXES, "n_repetitions": "Repetitions", "params": "Parameters"}
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
# made on the review screen (repetitions, system prompt, generation config).
|
|
34
|
+
FACTORS = {**DESIGN_AXES, "n_repetitions": "Repetitions", "system_prompt": "System prompt", "params": "Parameters"}
|
|
35
|
+
# Axes whose values are registered models (axis -> form field and label);
|
|
36
|
+
# "judge" values are JudgeVersions.
|
|
37
|
+
_MODEL_AXES = {"target": ("target_model", "target"), "auditor": ("auditor_model", "auditor"),
|
|
38
|
+
"judge_model": ("judge_model", "judge")}
|
|
39
|
+
_MODEL_ROLES = tuple(_MODEL_AXES)
|
|
40
|
+
_ROLE_AXES = ("target", "auditor", "judge_model", "judge")
|
|
41
|
+
# Generation keys with their own form fields (not "parameters").
|
|
42
|
+
_FORM_KEYS = ("max_turns", "language", "n_repetitions", "system_prompt")
|
|
35
43
|
|
|
36
44
|
|
|
37
45
|
class DesignError(ValueError):
|
|
@@ -66,6 +74,7 @@ def parse_design(post, project) -> dict:
|
|
|
66
74
|
|
|
67
75
|
Raises DesignError with a user-facing message.
|
|
68
76
|
"""
|
|
77
|
+
from judges.models import Judge, JudgeVersion
|
|
69
78
|
from model_registry.models import RegisteredModel
|
|
70
79
|
from scenarios.models import ScenarioSet, ScenarioSetVersion
|
|
71
80
|
|
|
@@ -106,15 +115,45 @@ def parse_design(post, project) -> dict:
|
|
|
106
115
|
)
|
|
107
116
|
|
|
108
117
|
models = {}
|
|
109
|
-
for
|
|
110
|
-
ids = [m for m in post.getlist(
|
|
118
|
+
for axis, (field, label) in _MODEL_AXES.items():
|
|
119
|
+
ids = [m for m in post.getlist(field) if m]
|
|
111
120
|
if not ids:
|
|
112
|
-
raise DesignError(f"Pick at least one {
|
|
113
|
-
|
|
121
|
+
raise DesignError(f"Pick at least one {label} model.")
|
|
122
|
+
# A model is usable when it belongs to this workspace OR its connection
|
|
123
|
+
# is shared into this workspace (public / admin-shared / explicit).
|
|
124
|
+
from model_registry.services import visible_connection_ids_for
|
|
125
|
+
|
|
126
|
+
allowed_conn_ids = set(visible_connection_ids_for(project))
|
|
127
|
+
found = {
|
|
128
|
+
str(m.pk): m
|
|
129
|
+
for m in RegisteredModel.objects.filter(pk__in=ids).select_related("connection")
|
|
130
|
+
if m.project_id == project.id or m.connection_id in allowed_conn_ids
|
|
131
|
+
}
|
|
114
132
|
missing = [i for i in ids if i not in found]
|
|
115
133
|
if missing:
|
|
116
|
-
raise DesignError(f"A selected {
|
|
117
|
-
models[
|
|
134
|
+
raise DesignError(f"A selected {label} model isn't available in this workspace.")
|
|
135
|
+
models[axis] = [found[i] for i in dict.fromkeys(ids)]
|
|
136
|
+
|
|
137
|
+
# Judges: version ids, and/or "latest:<judge id>" (always latest: unpinned,
|
|
138
|
+
# so repeating runs follow new versions).
|
|
139
|
+
raw = [v for v in dict.fromkeys(post.getlist("judge")) if v]
|
|
140
|
+
if not raw:
|
|
141
|
+
raise DesignError("Pick at least one judge.")
|
|
142
|
+
follow_ids = [v.split(":", 1)[1] for v in raw if v.startswith("latest:")]
|
|
143
|
+
pinned_ids = [v for v in raw if not v.startswith("latest:")]
|
|
144
|
+
judges = list(
|
|
145
|
+
JudgeVersion.objects.filter(pk__in=pinned_ids, judge__project=project).select_related("judge")
|
|
146
|
+
)
|
|
147
|
+
follow = list(Judge.objects.filter(pk__in=follow_ids, project=project))
|
|
148
|
+
if len(judges) != len(pinned_ids) or len(follow) != len(follow_ids):
|
|
149
|
+
raise DesignError("A selected judge is not in this workspace.")
|
|
150
|
+
for judge in follow:
|
|
151
|
+
latest = judge.versions.select_related("judge").order_by("-version").first()
|
|
152
|
+
if latest is None:
|
|
153
|
+
raise DesignError(f"Judge “{judge.name}” has no version.")
|
|
154
|
+
latest.follow_latest = True
|
|
155
|
+
judges.append(latest)
|
|
156
|
+
judges.sort(key=lambda v: (v.judge.name, is_follow(v), v.version))
|
|
118
157
|
|
|
119
158
|
n_reps_raw = (post.get("n_repetitions") or "").strip()
|
|
120
159
|
try:
|
|
@@ -140,11 +179,18 @@ def parse_design(post, project) -> dict:
|
|
|
140
179
|
raise DesignError(f"Max turns: {exc} Use whole numbers from 1 to 50.") from exc
|
|
141
180
|
languages = _split_values(post.get("language", ""), str)
|
|
142
181
|
|
|
182
|
+
# The target's system prompt lives in the generation config (the engine
|
|
183
|
+
# reads it there); the field wins over a key typed into the JSON.
|
|
184
|
+
system_prompt = (post.get("system_prompt") or "").strip()
|
|
185
|
+
if system_prompt:
|
|
186
|
+
gen_config = {**(gen_config or {}), "system_prompt": system_prompt}
|
|
187
|
+
|
|
143
188
|
return {
|
|
144
189
|
"scenario_set": versions,
|
|
145
190
|
"target": models["target"],
|
|
146
191
|
"auditor": models["auditor"],
|
|
147
|
-
"
|
|
192
|
+
"judge_model": models["judge_model"],
|
|
193
|
+
"judge": judges,
|
|
148
194
|
"max_turns": max_turns,
|
|
149
195
|
"language": languages,
|
|
150
196
|
"n_repetitions": n_reps if n_reps and n_reps > 1 else None,
|
|
@@ -153,7 +199,7 @@ def parse_design(post, project) -> dict:
|
|
|
153
199
|
|
|
154
200
|
|
|
155
201
|
def is_follow(version) -> bool:
|
|
156
|
-
"""Whether this scenario set version was picked as "Always latest"."""
|
|
202
|
+
"""Whether this scenario set / judge version was picked as "Always latest"."""
|
|
157
203
|
return bool(getattr(version, "follow_latest", False))
|
|
158
204
|
|
|
159
205
|
|
|
@@ -184,13 +230,34 @@ def expand(design: dict) -> list[dict]:
|
|
|
184
230
|
return specs
|
|
185
231
|
|
|
186
232
|
|
|
233
|
+
def spec_to_run(spec: dict, name: str) -> dict:
|
|
234
|
+
"""A design spec (from ``expand``) as a planned run: the ``launch_experiment``
|
|
235
|
+
run item shape, also used by script generation (infra.codegen)."""
|
|
236
|
+
return {
|
|
237
|
+
"name": name,
|
|
238
|
+
"version": spec["scenario_set"],
|
|
239
|
+
"target": spec["target"],
|
|
240
|
+
"auditor": spec["auditor"],
|
|
241
|
+
"judge_model": spec["judge_model"],
|
|
242
|
+
"judge": spec["judge"],
|
|
243
|
+
"max_turns": spec["max_turns"],
|
|
244
|
+
"language": spec["language"],
|
|
245
|
+
"n_repetitions": spec["n_repetitions"],
|
|
246
|
+
"gen_config": spec["gen_config"],
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
|
|
187
250
|
def factor_value_label(key: str, value) -> str:
|
|
188
251
|
if value is None:
|
|
189
252
|
return "default"
|
|
190
253
|
if key == "scenario_set":
|
|
191
254
|
if is_follow(value):
|
|
192
255
|
return f"{value.scenario_set.name} (always latest, now v{value.version})"
|
|
193
|
-
return
|
|
256
|
+
return value.label
|
|
257
|
+
if key == "judge":
|
|
258
|
+
if is_follow(value):
|
|
259
|
+
return f"{value.judge.name} (always latest, now v{value.version})"
|
|
260
|
+
return value.label
|
|
194
261
|
if key in _MODEL_ROLES:
|
|
195
262
|
return value.display_name
|
|
196
263
|
return str(value)
|
|
@@ -203,17 +270,19 @@ def spec_label(spec: dict, factors: list[str]) -> str:
|
|
|
203
270
|
|
|
204
271
|
def spec_warnings(spec: dict) -> list[str]:
|
|
205
272
|
warnings = []
|
|
206
|
-
if spec["
|
|
207
|
-
warnings.append("Judge is the target model: it grades itself.")
|
|
208
|
-
for role in
|
|
209
|
-
if not
|
|
210
|
-
warnings.append(f"{role
|
|
273
|
+
if spec["judge_model"].pk == spec["target"].pk:
|
|
274
|
+
warnings.append("Judge model is the target model: it grades itself.")
|
|
275
|
+
for role, model in (("Target", spec["target"]), ("Auditor", spec["auditor"]), ("Judge", spec["judge_model"])):
|
|
276
|
+
if not model.has_key:
|
|
277
|
+
warnings.append(f"{role} model has no API key set.")
|
|
278
|
+
elif not (model.enabled and model.connection.enabled):
|
|
279
|
+
warnings.append(f"{role} model is disabled.")
|
|
211
280
|
return warnings
|
|
212
281
|
|
|
213
282
|
|
|
214
283
|
def design_warnings(factors: list[str]) -> list[str]:
|
|
215
284
|
"""Experiment-level cautions about how results can be read."""
|
|
216
|
-
roles = [f for f in factors if f in
|
|
285
|
+
roles = [f for f in factors if f in _ROLE_AXES]
|
|
217
286
|
warnings = []
|
|
218
287
|
if "target" in roles and len(roles) > 1:
|
|
219
288
|
others = " and ".join(FACTORS[r].lower() for r in roles if r != "target")
|
|
@@ -238,6 +307,14 @@ def params_label(params: dict | None) -> str:
|
|
|
238
307
|
return json.dumps(rest, sort_keys=True, separators=(", ", ": ")) if rest else "default"
|
|
239
308
|
|
|
240
309
|
|
|
310
|
+
def prompt_label(prompt: str | None) -> str:
|
|
311
|
+
"""Short text for a system prompt in tables: its first line, clipped."""
|
|
312
|
+
first = (prompt or "").strip().splitlines()[0] if (prompt or "").strip() else ""
|
|
313
|
+
if not first:
|
|
314
|
+
return "none"
|
|
315
|
+
return first if len(first) <= 48 else first[:47] + "…"
|
|
316
|
+
|
|
317
|
+
|
|
241
318
|
def factors_from_runs(runs: list[dict]) -> list[str]:
|
|
242
319
|
"""Which inputs actually differ across the runs about to be launched.
|
|
243
320
|
|
|
@@ -247,8 +324,12 @@ def factors_from_runs(runs: list[dict]) -> list[str]:
|
|
|
247
324
|
def value(run, key):
|
|
248
325
|
if key == "scenario_set":
|
|
249
326
|
return (run["version"].pk, is_follow(run["version"]))
|
|
327
|
+
if key == "judge":
|
|
328
|
+
return (run["judge"].pk, is_follow(run["judge"]))
|
|
250
329
|
if key in _MODEL_ROLES:
|
|
251
330
|
return run[key].pk
|
|
331
|
+
if key == "system_prompt":
|
|
332
|
+
return (run["gen_config"] or {}).get("system_prompt") or ""
|
|
252
333
|
if key == "params":
|
|
253
334
|
return params_label(run["gen_config"])
|
|
254
335
|
return run[key]
|
|
@@ -263,14 +344,16 @@ def spec_to_row(spec: dict) -> str:
|
|
|
263
344
|
"f": 1 if is_follow(spec["scenario_set"]) else 0,
|
|
264
345
|
"t": spec["target"].pk,
|
|
265
346
|
"a": spec["auditor"].pk,
|
|
347
|
+
"jm": spec["judge_model"].pk,
|
|
266
348
|
"j": spec["judge"].pk,
|
|
349
|
+
"jf": 1 if is_follow(spec["judge"]) else 0,
|
|
267
350
|
})
|
|
268
351
|
|
|
269
352
|
|
|
270
353
|
def launch_experiment(*, project, user, name: str, runs: list[dict], repeat: dict | None = None) -> Experiment:
|
|
271
354
|
"""Create the Experiment and its frozen runs, then submit them.
|
|
272
355
|
|
|
273
|
-
``runs`` items: {"name", "version", "target", "auditor", "judge",
|
|
356
|
+
``runs`` items: {"name", "version", "target", "auditor", "judge_model", "judge",
|
|
274
357
|
"max_turns", "language", "n_repetitions", "gen_config"}. With ``repeat``
|
|
275
358
|
(see audits.monitors.parse_repeat) each run setup also gets a Monitor
|
|
276
359
|
linked to the experiment; runs start now unless repeat["start"] is "at".
|
|
@@ -302,7 +385,8 @@ def launch_experiment(*, project, user, name: str, runs: list[dict], repeat: dic
|
|
|
302
385
|
scenario_set_version=r["version"],
|
|
303
386
|
target_model=r["target"],
|
|
304
387
|
auditor_model=r["auditor"],
|
|
305
|
-
judge_model=r["
|
|
388
|
+
judge_model=r["judge_model"],
|
|
389
|
+
judge=r["judge"],
|
|
306
390
|
max_turns_override=r["max_turns"],
|
|
307
391
|
language_override=r["language"],
|
|
308
392
|
n_repetitions_override=r["n_repetitions"],
|
|
@@ -334,13 +418,15 @@ def launch_experiment(*, project, user, name: str, runs: list[dict], repeat: dic
|
|
|
334
418
|
def run_factor_values(run: AuditRun) -> dict:
|
|
335
419
|
params = run.generation_parameters_snapshot or {}
|
|
336
420
|
return {
|
|
337
|
-
"scenario_set":
|
|
338
|
-
"target": run
|
|
339
|
-
"auditor": run
|
|
340
|
-
"
|
|
421
|
+
"scenario_set": run.scenario_set_version.label,
|
|
422
|
+
"target": frozen_name(run, "target"),
|
|
423
|
+
"auditor": frozen_name(run, "auditor"),
|
|
424
|
+
"judge_model": frozen_name(run, "judge"),
|
|
425
|
+
"judge": frozen_judge(run)["label"],
|
|
341
426
|
"max_turns": str(params.get("max_turns") or "default"),
|
|
342
427
|
"language": params.get("language") or "default",
|
|
343
428
|
"n_repetitions": f"{params.get('n_repetitions') or 1}×",
|
|
429
|
+
"system_prompt": prompt_label(params.get("system_prompt")),
|
|
344
430
|
"params": params_label(params),
|
|
345
431
|
}
|
|
346
432
|
|