horde-model-reference 2.1.0__tar.gz → 2.1.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.
- {horde_model_reference-2.1.0/src/horde_model_reference.egg-info → horde_model_reference-2.1.2}/PKG-INFO +1 -1
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/_version.py +2 -2
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/analytics/text_model_parser.py +3 -1
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/integrations/horde_api_models.py +258 -29
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2/src/horde_model_reference.egg-info}/PKG-INFO +1 -1
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference.egg-info/SOURCES.txt +1 -0
- horde_model_reference-2.1.2/tests/integrations/test_stats_aggregation.py +247 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/statistics_and_audit/test_text_model_parser.py +40 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/.CONTRIBUTING.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/.dockerignore +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/.env.example +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/.env.primary.example +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/.env.sync.example +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/.github/workflows/codeql.yml +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/.github/workflows/docker-validation.yml +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/.github/workflows/lint.yml +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/.github/workflows/maintests.yml +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/.github/workflows/prtests.yml +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/.github/workflows/release.yml +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/.gitignore +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/.gitmodules +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/.pre-commit-config.yaml +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/.readthedocs.yaml +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/DEPLOYMENT.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/Dockerfile +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/LICENSE +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/MANIFEST.in +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/README.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docker-compose.redis.yml +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docker-compose.sync.example.yml +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docker-compose.yml +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/build_docs.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/.pages +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/_version.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/backends/.pages +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/backends/base.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/backends/filesystem_backend.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/backends/github_backend.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/backends/http_backend.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/backends/redis_backend.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/backends/replica_backend_base.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/legacy/.pages +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/legacy/classes/.pages +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/legacy/classes/legacy_converters.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/legacy/classes/legacy_models.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/legacy/convert_all_legacy_dbs.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/legacy/validate_sd.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/meta_consts.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/model_reference_manager.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/model_reference_metadata.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/model_reference_records.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/path_consts.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/service/.pages +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/service/app.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/service/shared.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/service/v1/.pages +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/service/v1/routers/.pages +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/service/v1/routers/create_update.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/service/v1/routers/metadata.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/service/v1/routers/references.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/service/v1/routers/shared.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/service/v2/.pages +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/service/v2/models.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/service/v2/routers/.pages +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/service/v2/routers/metadata.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/service/v2/routers/references.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/showcase/.pages +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/sync/.pages +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/sync/comparator.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/sync/config.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/sync/github_client.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/sync/watch_mode.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/horde_model_reference/util.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/index.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/legacy_csv_conversion.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/model_reference_backend.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/primary_deployments.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/replica_backend_base.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/docs/stylesheets/extra.css +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/mkdocs.yml +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/pyproject.toml +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/schemas/stable_diffusion.example.json +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/schemas/stable_diffusion.schema.json +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/scripts/README.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/scripts/get_all_names.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/scripts/legacy_text/convert.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/scripts/legacy_text/defaults.json +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/scripts/legacy_text/generation_params.json +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/scripts/legacy_text/reverse_convert.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/scripts/sync/README.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/scripts/sync/github_app_auth_example.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/scripts/sync/sync_github_references.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/setup.cfg +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/analytics/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/analytics/audit_analysis.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/analytics/audit_cache.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/analytics/base_cache.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/analytics/constants.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/analytics/filter_presets.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/analytics/statistics.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/analytics/statistics_cache.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/analytics/text_model_grouping.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/backends/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/backends/base.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/backends/filesystem_backend.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/backends/github_backend.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/backends/http_backend.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/backends/redis_backend.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/backends/replica_backend_base.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/integrations/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/integrations/data_merger.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/integrations/horde_api_integration.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/legacy/README.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/legacy/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/legacy/classes/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/legacy/classes/legacy_converters.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/legacy/classes/legacy_models.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/legacy/convert_all_legacy_dbs.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/legacy/text_csv_utils.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/legacy/validate_sd.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/meta_consts.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/model_reference_manager.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/model_reference_metadata.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/model_reference_records.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/path_consts.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/py.typed +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/app.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/shared.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/statistics/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/statistics/routers/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/statistics/routers/audit.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/statistics/routers/statistics.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/v1/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/v1/routers/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/v1/routers/create_update.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/v1/routers/metadata.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/v1/routers/references.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/v1/routers/shared.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/v2/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/v2/models.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/v2/routers/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/v2/routers/metadata.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/service/v2/routers/references.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/showcase/README.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/sync/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/sync/comparator.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/sync/config.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/sync/github_client.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/sync/legacy_text_validator.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/sync/watch_mode.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/util.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference.egg-info/dependency_links.txt +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference.egg-info/entry_points.txt +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference.egg-info/requires.txt +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference.egg-info/top_level.txt +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/test_endpoint.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/README.md +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/backends/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/backends/test_http_backend.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/backends/test_primary_mode.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/backends/test_redis_backend.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/check_model_ref_type_blocks.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/conftest.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/create_env_file_example.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/create_example_json.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/helpers.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/horde_api/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/horde_api/conftest.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/horde_api/test_audit_analysis_live.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/horde_api/test_audit_worker_count.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/horde_api/test_data_merger.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/horde_api/test_horde_api_integration.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/horde_api/test_horde_api_integration_live.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/horde_api/test_indexed_horde_types.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/service/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/service/test_replica_backend_base.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/service/test_v1_api.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/service/test_v2_api.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/statistics_and_audit/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/statistics_and_audit/test_audit_analysis.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/statistics_and_audit/test_statistics.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/statistics_and_audit/test_statistics_cache.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/statistics_and_audit/test_text_model_grouping.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/sync/__init__.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/sync/test_comparator.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/sync/test_comparator_integration.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/sync/test_config.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/sync/test_legacy_text_validator.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/test_broken_tutu_grouping.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/test_canonical_format.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/test_combined_model_statistics.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/test_consts.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/test_convert_legacy_database.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/test_converters.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/test_env_example.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/test_examples.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/test_metadata.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/test_model_reference_manager.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/test_records.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/test_scripts.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/test_text_generation_csv_conversion.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/test_text_generation_file_paths.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/tests/test_text_model_group.py +0 -0
- {horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: horde_model_reference
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Summary: A helper library providing a way to work with the lists of generation models, utility models, and any other related files required for the AI-Horde ecosystem.
|
|
5
5
|
Author-email: tazlin <tazlin.on.github@gmail.com>, db0 <mail@dbzer0.com>
|
|
6
6
|
License: GNU AFFERO GENERAL PUBLIC LICENSE
|
{horde_model_reference-2.1.0 → horde_model_reference-2.1.2}/src/horde_model_reference/_version.py
RENAMED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = 'v2.1.
|
|
32
|
-
__version_tuple__ = version_tuple = (2, 1,
|
|
31
|
+
__version__ = version = 'v2.1.2'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 1, 2)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -49,7 +49,9 @@ VARIANT_PATTERNS = [
|
|
|
49
49
|
|
|
50
50
|
# Quantization patterns
|
|
51
51
|
QUANT_PATTERNS = [
|
|
52
|
-
r"\b(Q[2-8]
|
|
52
|
+
r"\b(Q[2-8]_K(?:_[SMLH])?)\b", # Q4_K_M, Q5_K_S, Q6_K (K-quants with optional size)
|
|
53
|
+
r"\b(Q[2-8]_[01])\b", # Q4_0, Q5_0, Q5_1, Q8_0 (legacy/standard quants)
|
|
54
|
+
r"\b(Q[2-8])\b", # Q4, Q8 (bare quant indicators)
|
|
53
55
|
r"\b(GGUF|GGML|GPTQ|AWQ|EXL2)\b",
|
|
54
56
|
r"\b(fp16|fp32|int8|int4)\b",
|
|
55
57
|
]
|
|
@@ -236,12 +236,168 @@ class _StatsLookup(BaseModel):
|
|
|
236
236
|
total: dict[str, int] = Field(default_factory=dict)
|
|
237
237
|
|
|
238
238
|
|
|
239
|
+
def _strip_quantization_suffix(model_name: str) -> str:
|
|
240
|
+
"""Strip quantization suffix from a model name, preserving size.
|
|
241
|
+
|
|
242
|
+
This is different from get_base_model_name which strips BOTH size and quantization.
|
|
243
|
+
This function only strips quantization, keeping the size suffix.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
model_name: Model name potentially with quantization suffix.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
Model name without quantization suffix, but with size preserved.
|
|
250
|
+
|
|
251
|
+
Example:
|
|
252
|
+
"Lumimaid-v0.2-8B-Q8_0" -> "Lumimaid-v0.2-8B"
|
|
253
|
+
"Lumimaid-v0.2-8B" -> "Lumimaid-v0.2-8B"
|
|
254
|
+
"koboldcpp/Lumimaid-v0.2-8B-Q4_K_M" -> "koboldcpp/Lumimaid-v0.2-8B"
|
|
255
|
+
"""
|
|
256
|
+
import re
|
|
257
|
+
|
|
258
|
+
# Quantization patterns to strip (same as text_model_parser.QUANT_PATTERNS but as suffix)
|
|
259
|
+
quant_suffix_patterns = [
|
|
260
|
+
r"[-_](Q[2-8]_K(?:_[SMLH])?)$", # -Q4_K_M, -Q5_K_S
|
|
261
|
+
r"[-_](Q[2-8]_[01])$", # -Q4_0, -Q5_0, -Q8_0
|
|
262
|
+
r"[-_](Q[2-8])$", # -Q4, -Q8
|
|
263
|
+
r"[-_](GGUF|GGML|GPTQ|AWQ|EXL2)$",
|
|
264
|
+
r"[-_](fp16|fp32|int8|int4)$",
|
|
265
|
+
]
|
|
266
|
+
|
|
267
|
+
result = model_name
|
|
268
|
+
for pattern in quant_suffix_patterns:
|
|
269
|
+
result = re.sub(pattern, "", result, flags=re.IGNORECASE)
|
|
270
|
+
|
|
271
|
+
return result
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _build_base_name_index(model_names: list[str]) -> dict[str, list[str]]:
|
|
275
|
+
"""Build an index mapping base model names to all matching model names.
|
|
276
|
+
|
|
277
|
+
This enables aggregating stats across quantization variants (e.g., Q4_K_M, Q8_0)
|
|
278
|
+
and different backend prefixes (aphrodite/, koboldcpp/).
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
model_names: List of model names from API stats (may include backend prefixes
|
|
282
|
+
and quantization suffixes).
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
Dictionary mapping lowercase base model names to lists of original model names
|
|
286
|
+
(lowercase) that match that base.
|
|
287
|
+
|
|
288
|
+
Example:
|
|
289
|
+
Input: ["koboldcpp/Lumimaid-v0.2-8B", "koboldcpp/Lumimaid-v0.2-8B-Q8_0",
|
|
290
|
+
"aphrodite/NeverSleep/Lumimaid-v0.2-8B"]
|
|
291
|
+
Output: {"lumimaid-v0.2": ["koboldcpp/lumimaid-v0.2-8b",
|
|
292
|
+
"koboldcpp/lumimaid-v0.2-8b-q8_0",
|
|
293
|
+
"aphrodite/neversleep/lumimaid-v0.2-8b"]}
|
|
294
|
+
"""
|
|
295
|
+
from horde_model_reference.analytics.text_model_parser import get_base_model_name
|
|
296
|
+
from horde_model_reference.meta_consts import strip_backend_prefix
|
|
297
|
+
|
|
298
|
+
base_name_index: dict[str, list[str]] = {}
|
|
299
|
+
|
|
300
|
+
for model_name in model_names:
|
|
301
|
+
# Store lowercase version for case-insensitive matching
|
|
302
|
+
model_name_lower = model_name.lower()
|
|
303
|
+
|
|
304
|
+
# Strip backend prefix first, then extract base name
|
|
305
|
+
stripped = strip_backend_prefix(model_name)
|
|
306
|
+
|
|
307
|
+
# Also strip org prefix for base name extraction (e.g., "NeverSleep/Lumimaid-v0.2-8B" -> "Lumimaid-v0.2-8B")
|
|
308
|
+
if "/" in stripped:
|
|
309
|
+
stripped = stripped.split("/")[-1]
|
|
310
|
+
|
|
311
|
+
base_name = get_base_model_name(stripped).lower()
|
|
312
|
+
|
|
313
|
+
if base_name not in base_name_index:
|
|
314
|
+
base_name_index[base_name] = []
|
|
315
|
+
if model_name_lower not in base_name_index[base_name]:
|
|
316
|
+
base_name_index[base_name].append(model_name_lower)
|
|
317
|
+
|
|
318
|
+
return base_name_index
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _build_model_with_size_index(model_names: list[str]) -> dict[str, list[str]]:
|
|
322
|
+
"""Build an index mapping model names (with size, without quant) to all matching names.
|
|
323
|
+
|
|
324
|
+
This enables aggregating stats across quantization variants only (e.g., Q4_K_M, Q8_0)
|
|
325
|
+
while keeping different sizes separate.
|
|
326
|
+
|
|
327
|
+
Unlike _build_base_name_index which groups ALL variants (including different sizes),
|
|
328
|
+
this index only groups quantization variants of the SAME sized model.
|
|
329
|
+
|
|
330
|
+
The key normalizes:
|
|
331
|
+
- Backend prefix (stripped for matching, but preserved in values)
|
|
332
|
+
- Org prefix (stripped for matching)
|
|
333
|
+
- Quantization suffix (stripped for matching)
|
|
334
|
+
|
|
335
|
+
But preserves:
|
|
336
|
+
- Size suffix (8B, 12B, etc.)
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
model_names: List of model names from API stats (may include backend prefixes
|
|
340
|
+
and quantization suffixes).
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
Dictionary mapping normalized model names (backend/model-size) to lists of
|
|
344
|
+
original model names (lowercase) that match that model.
|
|
345
|
+
|
|
346
|
+
Example:
|
|
347
|
+
Input: ["koboldcpp/Lumimaid-v0.2-8B", "koboldcpp/Lumimaid-v0.2-8B-Q8_0",
|
|
348
|
+
"koboldcpp/Lumimaid-v0.2-12B", "aphrodite/NeverSleep/Lumimaid-v0.2-8B"]
|
|
349
|
+
Output: {
|
|
350
|
+
"koboldcpp/lumimaid-v0.2-8b": [
|
|
351
|
+
"koboldcpp/lumimaid-v0.2-8b",
|
|
352
|
+
"koboldcpp/lumimaid-v0.2-8b-q8_0"
|
|
353
|
+
],
|
|
354
|
+
"koboldcpp/lumimaid-v0.2-12b": ["koboldcpp/lumimaid-v0.2-12b"],
|
|
355
|
+
"aphrodite/lumimaid-v0.2-8b": ["aphrodite/neversleep/lumimaid-v0.2-8b"]
|
|
356
|
+
}
|
|
357
|
+
"""
|
|
358
|
+
model_with_size_index: dict[str, list[str]] = {}
|
|
359
|
+
|
|
360
|
+
for model_name in model_names:
|
|
361
|
+
model_name_lower = model_name.lower()
|
|
362
|
+
|
|
363
|
+
# Extract backend prefix if present
|
|
364
|
+
backend_prefix = ""
|
|
365
|
+
stripped = model_name_lower
|
|
366
|
+
if stripped.startswith("aphrodite/"):
|
|
367
|
+
backend_prefix = "aphrodite/"
|
|
368
|
+
stripped = stripped[len("aphrodite/") :]
|
|
369
|
+
elif stripped.startswith("koboldcpp/"):
|
|
370
|
+
backend_prefix = "koboldcpp/"
|
|
371
|
+
stripped = stripped[len("koboldcpp/") :]
|
|
372
|
+
|
|
373
|
+
# Strip org prefix (e.g., "neversleep/lumimaid-v0.2-8b" -> "lumimaid-v0.2-8b")
|
|
374
|
+
if "/" in stripped:
|
|
375
|
+
stripped = stripped.split("/")[-1]
|
|
376
|
+
|
|
377
|
+
# Strip quantization suffix
|
|
378
|
+
stripped_no_quant = _strip_quantization_suffix(stripped)
|
|
379
|
+
|
|
380
|
+
# Build key: backend_prefix + model_name (no org, no quant, but with size)
|
|
381
|
+
key = f"{backend_prefix}{stripped_no_quant}"
|
|
382
|
+
|
|
383
|
+
if key not in model_with_size_index:
|
|
384
|
+
model_with_size_index[key] = []
|
|
385
|
+
if model_name_lower not in model_with_size_index[key]:
|
|
386
|
+
model_with_size_index[key].append(model_name_lower)
|
|
387
|
+
|
|
388
|
+
return model_with_size_index
|
|
389
|
+
|
|
390
|
+
|
|
239
391
|
class IndexedHordeModelStats(RootModel[_StatsLookup]):
|
|
240
392
|
"""Indexed model stats for O(1) lookups by model name.
|
|
241
393
|
|
|
242
394
|
This wraps the stats response and provides case-insensitive dictionary access.
|
|
243
395
|
Time complexity: O(1) for lookups instead of O(n) for dict iteration.
|
|
244
396
|
|
|
397
|
+
Two indexes are built:
|
|
398
|
+
- _base_name_index: Groups ALL variants (including different sizes) for group-level aggregation
|
|
399
|
+
- _model_with_size_index: Groups only quantization variants for per-model stats
|
|
400
|
+
|
|
245
401
|
Usage:
|
|
246
402
|
indexed = IndexedHordeModelStats(stats_response)
|
|
247
403
|
day_count = indexed.get_day("model_name") # Case-insensitive
|
|
@@ -249,6 +405,8 @@ class IndexedHordeModelStats(RootModel[_StatsLookup]):
|
|
|
249
405
|
"""
|
|
250
406
|
|
|
251
407
|
root: _StatsLookup
|
|
408
|
+
_base_name_index: dict[str, list[str]] = {}
|
|
409
|
+
_model_with_size_index: dict[str, list[str]] = {}
|
|
252
410
|
|
|
253
411
|
def __init__(self, stats_response: HordeModelStatsResponse) -> None:
|
|
254
412
|
"""Build indexed lookups from stats response.
|
|
@@ -264,6 +422,14 @@ class IndexedHordeModelStats(RootModel[_StatsLookup]):
|
|
|
264
422
|
)
|
|
265
423
|
super().__init__(root=lookups)
|
|
266
424
|
|
|
425
|
+
# Build indexes from all unique model names across all time periods
|
|
426
|
+
all_model_names = (
|
|
427
|
+
set(stats_response.day.keys()) | set(stats_response.month.keys()) | set(stats_response.total.keys())
|
|
428
|
+
)
|
|
429
|
+
model_names_list = list(all_model_names)
|
|
430
|
+
self._base_name_index = _build_base_name_index(model_names_list)
|
|
431
|
+
self._model_with_size_index = _build_model_with_size_index(model_names_list)
|
|
432
|
+
|
|
267
433
|
def get_day(self, model_name: str) -> int | None:
|
|
268
434
|
"""Get day count for a model (case-insensitive). O(1)."""
|
|
269
435
|
return self.root.day.get(model_name.lower())
|
|
@@ -282,12 +448,12 @@ class IndexedHordeModelStats(RootModel[_StatsLookup]):
|
|
|
282
448
|
return name_lower in self.root.day or name_lower in self.root.month or name_lower in self.root.total
|
|
283
449
|
|
|
284
450
|
def get_aggregated_stats(self, canonical_name: str) -> tuple[int, int, int]:
|
|
285
|
-
"""Get aggregated stats across all backend variants of a model.
|
|
451
|
+
"""Get aggregated stats across all backend variants and quantization versions of a model.
|
|
286
452
|
|
|
287
|
-
This method aggregates stats from
|
|
288
|
-
-
|
|
289
|
-
-
|
|
290
|
-
-
|
|
453
|
+
This method aggregates stats from:
|
|
454
|
+
- Exact name variants (canonical, aphrodite/, koboldcpp/ prefixed)
|
|
455
|
+
- All quantization variants sharing the same base model name (Q4_K_M, Q8_0, etc.)
|
|
456
|
+
- Different org-prefixed variants (e.g., "NeverSleep/Lumimaid" matches "Lumimaid")
|
|
291
457
|
|
|
292
458
|
Args:
|
|
293
459
|
canonical_name: The canonical model name from the model reference.
|
|
@@ -297,73 +463,136 @@ class IndexedHordeModelStats(RootModel[_StatsLookup]):
|
|
|
297
463
|
|
|
298
464
|
Example:
|
|
299
465
|
>>> indexed = IndexedHordeModelStats(stats_response)
|
|
300
|
-
>>> day, month, total = indexed.get_aggregated_stats("
|
|
466
|
+
>>> day, month, total = indexed.get_aggregated_stats("Lumimaid-v0.2-8B")
|
|
467
|
+
# Will aggregate: Lumimaid-v0.2-8B, koboldcpp/Lumimaid-v0.2-8B,
|
|
468
|
+
# koboldcpp/Lumimaid-v0.2-8B-Q8_0, aphrodite/NeverSleep/Lumimaid-v0.2-8B, etc.
|
|
301
469
|
"""
|
|
470
|
+
from horde_model_reference.analytics.text_model_parser import get_base_model_name
|
|
302
471
|
from horde_model_reference.meta_consts import get_model_name_variants
|
|
303
472
|
|
|
304
|
-
|
|
473
|
+
# Collect all model names to aggregate (use set to avoid double-counting)
|
|
474
|
+
names_to_aggregate: set[str] = set()
|
|
305
475
|
|
|
476
|
+
# First, add exact variants from get_model_name_variants
|
|
477
|
+
variants = get_model_name_variants(canonical_name)
|
|
478
|
+
for variant in variants:
|
|
479
|
+
names_to_aggregate.add(variant.lower())
|
|
480
|
+
|
|
481
|
+
# Then, add all model names that share the same base model name
|
|
482
|
+
# This catches quantization variants and org-prefixed variants
|
|
483
|
+
# Strip org prefix from canonical name if present (e.g., "NeverSleep/Lumimaid-v0.2" -> "Lumimaid-v0.2")
|
|
484
|
+
canonical_without_org = canonical_name.split("/")[-1] if "/" in canonical_name else canonical_name
|
|
485
|
+
base_name = get_base_model_name(canonical_without_org).lower()
|
|
486
|
+
if base_name in self._base_name_index:
|
|
487
|
+
for api_model_name in self._base_name_index[base_name]:
|
|
488
|
+
names_to_aggregate.add(api_model_name)
|
|
489
|
+
|
|
490
|
+
# Aggregate stats across all matched names
|
|
306
491
|
day_total = 0
|
|
307
492
|
month_total = 0
|
|
308
493
|
total_total = 0
|
|
309
494
|
|
|
310
|
-
for
|
|
311
|
-
day_total += self.get_day(
|
|
312
|
-
month_total += self.get_month(
|
|
313
|
-
total_total += self.get_total(
|
|
495
|
+
for name in names_to_aggregate:
|
|
496
|
+
day_total += self.get_day(name) or 0
|
|
497
|
+
month_total += self.get_month(name) or 0
|
|
498
|
+
total_total += self.get_total(name) or 0
|
|
314
499
|
|
|
315
500
|
return (day_total, month_total, total_total)
|
|
316
501
|
|
|
317
502
|
def get_stats_with_variations(
|
|
318
503
|
self, canonical_name: str
|
|
319
504
|
) -> tuple[tuple[int, int, int], dict[str, tuple[int, int, int]]]:
|
|
320
|
-
"""Get
|
|
505
|
+
"""Get stats for a specific model broken down by backend.
|
|
321
506
|
|
|
322
|
-
|
|
323
|
-
|
|
507
|
+
Unlike get_aggregated_stats which aggregates across all models with the same
|
|
508
|
+
base name (e.g., all Lumimaid-v0.2 sizes), this method returns stats only for
|
|
509
|
+
the exact model specified (including its quantization variants), broken down
|
|
510
|
+
by backend prefix.
|
|
511
|
+
|
|
512
|
+
This enables showing per-model stats in the UI when displaying grouped models,
|
|
513
|
+
where each model variant (8B, 12B, etc.) shows its own stats by backend.
|
|
324
514
|
|
|
325
515
|
Args:
|
|
326
516
|
canonical_name: The canonical model name from the model reference.
|
|
327
517
|
|
|
328
518
|
Returns:
|
|
329
519
|
Tuple of (aggregated_stats, variations_dict) where:
|
|
330
|
-
- aggregated_stats: (day_total, month_total, total_total)
|
|
520
|
+
- aggregated_stats: (day_total, month_total, total_total) for this exact model
|
|
331
521
|
- variations_dict: Dict of backend_name -> (day, month, total)
|
|
332
522
|
Keys are 'canonical', 'aphrodite', 'koboldcpp' depending on what's found
|
|
333
523
|
"""
|
|
334
524
|
from horde_model_reference.meta_consts import get_model_name_variants
|
|
335
525
|
|
|
526
|
+
# Collect all model names that are variants of this specific model
|
|
527
|
+
# Use _model_with_size_index to include quantization variants, but NOT size variants
|
|
528
|
+
names_to_aggregate: set[str] = set()
|
|
529
|
+
|
|
530
|
+
# Get exact backend-prefixed variants
|
|
336
531
|
variants = get_model_name_variants(canonical_name)
|
|
337
|
-
|
|
532
|
+
for variant in variants:
|
|
533
|
+
variant_lower = variant.lower()
|
|
534
|
+
names_to_aggregate.add(variant_lower)
|
|
535
|
+
|
|
536
|
+
# Build the normalized key to look up in _model_with_size_index
|
|
537
|
+
# The key format is: [backend_prefix/]model_name (no org, no quant)
|
|
538
|
+
backend_prefix = ""
|
|
539
|
+
stripped = variant_lower
|
|
540
|
+
if stripped.startswith("aphrodite/"):
|
|
541
|
+
backend_prefix = "aphrodite/"
|
|
542
|
+
stripped = stripped[len("aphrodite/") :]
|
|
543
|
+
elif stripped.startswith("koboldcpp/"):
|
|
544
|
+
backend_prefix = "koboldcpp/"
|
|
545
|
+
stripped = stripped[len("koboldcpp/") :]
|
|
546
|
+
|
|
547
|
+
# Strip org prefix if present
|
|
548
|
+
if "/" in stripped:
|
|
549
|
+
stripped = stripped.split("/")[-1]
|
|
550
|
+
|
|
551
|
+
# Strip quantization suffix and build key
|
|
552
|
+
stripped_no_quant = _strip_quantization_suffix(stripped)
|
|
553
|
+
key = f"{backend_prefix}{stripped_no_quant}"
|
|
554
|
+
|
|
555
|
+
if key in self._model_with_size_index:
|
|
556
|
+
for api_model_name in self._model_with_size_index[key]:
|
|
557
|
+
names_to_aggregate.add(api_model_name)
|
|
558
|
+
|
|
559
|
+
# Track stats by backend for variations dict
|
|
560
|
+
backend_stats: dict[str, tuple[int, int, int]] = {
|
|
561
|
+
"canonical": (0, 0, 0),
|
|
562
|
+
"aphrodite": (0, 0, 0),
|
|
563
|
+
"koboldcpp": (0, 0, 0),
|
|
564
|
+
}
|
|
338
565
|
|
|
339
566
|
day_total = 0
|
|
340
567
|
month_total = 0
|
|
341
568
|
total_total = 0
|
|
342
569
|
|
|
343
|
-
# Look up each
|
|
344
|
-
for
|
|
345
|
-
day = self.get_day(
|
|
346
|
-
month = self.get_month(
|
|
347
|
-
total = self.get_total(
|
|
570
|
+
# Look up each name and aggregate by backend
|
|
571
|
+
for name in names_to_aggregate:
|
|
572
|
+
day = self.get_day(name) or 0
|
|
573
|
+
month = self.get_month(name) or 0
|
|
574
|
+
total = self.get_total(name) or 0
|
|
348
575
|
|
|
349
|
-
# Only store if there's actual data
|
|
350
576
|
if day > 0 or month > 0 or total > 0:
|
|
351
|
-
# Determine backend name from
|
|
352
|
-
if
|
|
353
|
-
backend_name = "canonical"
|
|
354
|
-
elif variant.startswith("aphrodite/"):
|
|
577
|
+
# Determine backend name from the model name
|
|
578
|
+
if name.startswith("aphrodite/"):
|
|
355
579
|
backend_name = "aphrodite"
|
|
356
|
-
elif
|
|
580
|
+
elif name.startswith("koboldcpp/"):
|
|
357
581
|
backend_name = "koboldcpp"
|
|
358
582
|
else:
|
|
359
|
-
backend_name = "
|
|
583
|
+
backend_name = "canonical"
|
|
360
584
|
|
|
361
|
-
|
|
585
|
+
# Accumulate stats for this backend
|
|
586
|
+
prev_day, prev_month, prev_total = backend_stats[backend_name]
|
|
587
|
+
backend_stats[backend_name] = (prev_day + day, prev_month + month, prev_total + total)
|
|
362
588
|
|
|
363
589
|
day_total += day
|
|
364
590
|
month_total += month
|
|
365
591
|
total_total += total
|
|
366
592
|
|
|
593
|
+
# Filter to only backends with data
|
|
594
|
+
variations = {k: v for k, v in backend_stats.items() if v != (0, 0, 0)}
|
|
595
|
+
|
|
367
596
|
return (day_total, month_total, total_total), variations
|
|
368
597
|
|
|
369
598
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: horde_model_reference
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Summary: A helper library providing a way to work with the lists of generation models, utility models, and any other related files required for the AI-Horde ecosystem.
|
|
5
5
|
Author-email: tazlin <tazlin.on.github@gmail.com>, db0 <mail@dbzer0.com>
|
|
6
6
|
License: GNU AFFERO GENERAL PUBLIC LICENSE
|
|
@@ -187,6 +187,7 @@ tests/horde_api/test_data_merger.py
|
|
|
187
187
|
tests/horde_api/test_horde_api_integration.py
|
|
188
188
|
tests/horde_api/test_horde_api_integration_live.py
|
|
189
189
|
tests/horde_api/test_indexed_horde_types.py
|
|
190
|
+
tests/integrations/test_stats_aggregation.py
|
|
190
191
|
tests/service/__init__.py
|
|
191
192
|
tests/service/test_replica_backend_base.py
|
|
192
193
|
tests/service/test_v1_api.py
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""Tests for stats aggregation across quantization variants."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from horde_model_reference.integrations.horde_api_models import (
|
|
6
|
+
HordeModelStatsResponse,
|
|
7
|
+
IndexedHordeModelStats,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestIndexedHordeModelStatsAggregation:
|
|
12
|
+
"""Test suite for stats aggregation with quantization variants."""
|
|
13
|
+
|
|
14
|
+
def test_aggregate_stats_with_quantization_variants(self) -> None:
|
|
15
|
+
"""Test that stats aggregate correctly across quantization variants."""
|
|
16
|
+
stats = HordeModelStatsResponse(
|
|
17
|
+
day={
|
|
18
|
+
"koboldcpp/Lumimaid-v0.2-8B": 4080,
|
|
19
|
+
"koboldcpp/Lumimaid-v0.2-8B-Q8_0": 1500,
|
|
20
|
+
"koboldcpp/Lumimaid-v0.2-8B-Q4_K_M": 800,
|
|
21
|
+
},
|
|
22
|
+
month={
|
|
23
|
+
"koboldcpp/Lumimaid-v0.2-8B": 40000,
|
|
24
|
+
"koboldcpp/Lumimaid-v0.2-8B-Q8_0": 15000,
|
|
25
|
+
"koboldcpp/Lumimaid-v0.2-8B-Q4_K_M": 8000,
|
|
26
|
+
},
|
|
27
|
+
total={
|
|
28
|
+
"koboldcpp/Lumimaid-v0.2-8B": 400000,
|
|
29
|
+
"koboldcpp/Lumimaid-v0.2-8B-Q8_0": 150000,
|
|
30
|
+
"koboldcpp/Lumimaid-v0.2-8B-Q4_K_M": 80000,
|
|
31
|
+
},
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
indexed = IndexedHordeModelStats(stats)
|
|
35
|
+
day, month, total = indexed.get_aggregated_stats("Lumimaid-v0.2-8B")
|
|
36
|
+
|
|
37
|
+
# Should aggregate all quantization variants
|
|
38
|
+
assert day == 4080 + 1500 + 800
|
|
39
|
+
assert month == 40000 + 15000 + 8000
|
|
40
|
+
assert total == 400000 + 150000 + 80000
|
|
41
|
+
|
|
42
|
+
def test_aggregate_stats_with_org_prefix_variants(self) -> None:
|
|
43
|
+
"""Test that stats aggregate across org-prefixed variants."""
|
|
44
|
+
stats = HordeModelStatsResponse(
|
|
45
|
+
day={
|
|
46
|
+
"Lumimaid-v0.2-8B": 1000,
|
|
47
|
+
"aphrodite/NeverSleep/Lumimaid-v0.2-8B": 2000,
|
|
48
|
+
"koboldcpp/Lumimaid-v0.2-8B": 3000,
|
|
49
|
+
},
|
|
50
|
+
month={},
|
|
51
|
+
total={},
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
indexed = IndexedHordeModelStats(stats)
|
|
55
|
+
day, _month, _total = indexed.get_aggregated_stats("Lumimaid-v0.2-8B")
|
|
56
|
+
|
|
57
|
+
# Should aggregate across all prefixes
|
|
58
|
+
assert day == 1000 + 2000 + 3000
|
|
59
|
+
|
|
60
|
+
def test_aggregate_stats_with_all_variants(self) -> None:
|
|
61
|
+
"""Test that stats aggregate across backend, org, and quantization variants."""
|
|
62
|
+
stats = HordeModelStatsResponse(
|
|
63
|
+
day={
|
|
64
|
+
"Lumimaid-v0.2-8B": 100,
|
|
65
|
+
"aphrodite/Lumimaid-v0.2-8B": 200,
|
|
66
|
+
"aphrodite/NeverSleep/Lumimaid-v0.2-8B": 300,
|
|
67
|
+
"koboldcpp/Lumimaid-v0.2-8B": 400,
|
|
68
|
+
"koboldcpp/Lumimaid-v0.2-8B-Q8_0": 500,
|
|
69
|
+
"koboldcpp/Lumimaid-v0.2-8B-Q4_K_M": 600,
|
|
70
|
+
},
|
|
71
|
+
month={
|
|
72
|
+
"Lumimaid-v0.2-8B": 1000,
|
|
73
|
+
"aphrodite/NeverSleep/Lumimaid-v0.2-8B": 3000,
|
|
74
|
+
},
|
|
75
|
+
total={},
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
indexed = IndexedHordeModelStats(stats)
|
|
79
|
+
day, month, _total = indexed.get_aggregated_stats("Lumimaid-v0.2-8B")
|
|
80
|
+
|
|
81
|
+
assert day == 100 + 200 + 300 + 400 + 500 + 600
|
|
82
|
+
assert month == 1000 + 3000
|
|
83
|
+
|
|
84
|
+
def test_get_stats_with_variations_backend_breakdown(self) -> None:
|
|
85
|
+
"""Test that variations are correctly grouped by backend."""
|
|
86
|
+
stats = HordeModelStatsResponse(
|
|
87
|
+
day={
|
|
88
|
+
"Lumimaid-v0.2-8B": 100,
|
|
89
|
+
"aphrodite/NeverSleep/Lumimaid-v0.2-8B": 200,
|
|
90
|
+
"koboldcpp/Lumimaid-v0.2-8B": 300,
|
|
91
|
+
"koboldcpp/Lumimaid-v0.2-8B-Q8_0": 400,
|
|
92
|
+
},
|
|
93
|
+
month={},
|
|
94
|
+
total={},
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
indexed = IndexedHordeModelStats(stats)
|
|
98
|
+
(day_total, _month, _total), variations = indexed.get_stats_with_variations("Lumimaid-v0.2-8B")
|
|
99
|
+
|
|
100
|
+
assert day_total == 100 + 200 + 300 + 400
|
|
101
|
+
assert "canonical" in variations
|
|
102
|
+
assert "aphrodite" in variations
|
|
103
|
+
assert "koboldcpp" in variations
|
|
104
|
+
assert variations["canonical"][0] == 100
|
|
105
|
+
assert variations["aphrodite"][0] == 200
|
|
106
|
+
assert variations["koboldcpp"][0] == 300 + 400 # Aggregates quant variants
|
|
107
|
+
|
|
108
|
+
def test_aggregate_stats_different_base_models_not_mixed(self) -> None:
|
|
109
|
+
"""Test that different base models are not incorrectly aggregated."""
|
|
110
|
+
stats = HordeModelStatsResponse(
|
|
111
|
+
day={
|
|
112
|
+
"Lumimaid-v0.2-8B": 100,
|
|
113
|
+
"Lumimaid-v0.2-8B-Q8_0": 200,
|
|
114
|
+
"Llama-3-8B": 1000,
|
|
115
|
+
"Llama-3-8B-Q4_K_M": 2000,
|
|
116
|
+
},
|
|
117
|
+
month={},
|
|
118
|
+
total={},
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
indexed = IndexedHordeModelStats(stats)
|
|
122
|
+
|
|
123
|
+
lumimaid_day, _m, _t = indexed.get_aggregated_stats("Lumimaid-v0.2-8B")
|
|
124
|
+
llama_day, _m, _t = indexed.get_aggregated_stats("Llama-3-8B")
|
|
125
|
+
|
|
126
|
+
# Each base model should only aggregate its own variants
|
|
127
|
+
assert lumimaid_day == 100 + 200
|
|
128
|
+
assert llama_day == 1000 + 2000
|
|
129
|
+
|
|
130
|
+
def test_aggregate_stats_case_insensitive(self) -> None:
|
|
131
|
+
"""Test that aggregation is case-insensitive."""
|
|
132
|
+
stats = HordeModelStatsResponse(
|
|
133
|
+
day={
|
|
134
|
+
"KOBOLDCPP/Lumimaid-v0.2-8B": 100,
|
|
135
|
+
"koboldcpp/lumimaid-v0.2-8b-q8_0": 200,
|
|
136
|
+
},
|
|
137
|
+
month={},
|
|
138
|
+
total={},
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
indexed = IndexedHordeModelStats(stats)
|
|
142
|
+
day, _m, _t = indexed.get_aggregated_stats("Lumimaid-v0.2-8B")
|
|
143
|
+
|
|
144
|
+
assert day == 100 + 200
|
|
145
|
+
|
|
146
|
+
def test_aggregate_stats_no_duplicates(self) -> None:
|
|
147
|
+
"""Test that stats are not double-counted when exact match exists."""
|
|
148
|
+
stats = HordeModelStatsResponse(
|
|
149
|
+
day={
|
|
150
|
+
"Lumimaid-v0.2-8B": 100, # Exact match and base name match
|
|
151
|
+
},
|
|
152
|
+
month={},
|
|
153
|
+
total={},
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
indexed = IndexedHordeModelStats(stats)
|
|
157
|
+
day, _m, _t = indexed.get_aggregated_stats("Lumimaid-v0.2-8B")
|
|
158
|
+
|
|
159
|
+
# Should only count once, not twice
|
|
160
|
+
assert day == 100
|
|
161
|
+
|
|
162
|
+
def test_aggregate_stats_empty_response(self) -> None:
|
|
163
|
+
"""Test aggregation with empty stats response."""
|
|
164
|
+
stats = HordeModelStatsResponse(day={}, month={}, total={})
|
|
165
|
+
|
|
166
|
+
indexed = IndexedHordeModelStats(stats)
|
|
167
|
+
day, month, total = indexed.get_aggregated_stats("Lumimaid-v0.2-8B")
|
|
168
|
+
|
|
169
|
+
assert day == 0
|
|
170
|
+
assert month == 0
|
|
171
|
+
assert total == 0
|
|
172
|
+
|
|
173
|
+
def test_aggregate_stats_model_not_found(self) -> None:
|
|
174
|
+
"""Test aggregation when model doesn't exist in stats."""
|
|
175
|
+
stats = HordeModelStatsResponse(
|
|
176
|
+
day={"SomeOtherModel": 100},
|
|
177
|
+
month={},
|
|
178
|
+
total={},
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
indexed = IndexedHordeModelStats(stats)
|
|
182
|
+
day, month, total = indexed.get_aggregated_stats("Lumimaid-v0.2-8B")
|
|
183
|
+
|
|
184
|
+
assert day == 0
|
|
185
|
+
assert month == 0
|
|
186
|
+
assert total == 0
|
|
187
|
+
|
|
188
|
+
def test_aggregate_stats_canonical_name_with_org_prefix(self) -> None:
|
|
189
|
+
"""Test that canonical names with org prefix correctly match API stats.
|
|
190
|
+
|
|
191
|
+
This is a critical test case because model reference entries often have
|
|
192
|
+
org prefixes (e.g., "NeverSleep/Lumimaid-v0.2") but API stats may have
|
|
193
|
+
different prefixing patterns.
|
|
194
|
+
"""
|
|
195
|
+
stats = HordeModelStatsResponse(
|
|
196
|
+
day={
|
|
197
|
+
"koboldcpp/Lumimaid-v0.2-8B": 4080,
|
|
198
|
+
"koboldcpp/Lumimaid-v0.2-8B-Q8_0": 1500,
|
|
199
|
+
"aphrodite/NeverSleep/Lumimaid-v0.2-8B": 2000,
|
|
200
|
+
},
|
|
201
|
+
month={
|
|
202
|
+
"koboldcpp/Lumimaid-v0.2-8B": 40000,
|
|
203
|
+
"koboldcpp/Lumimaid-v0.2-8B-Q8_0": 15000,
|
|
204
|
+
"aphrodite/NeverSleep/Lumimaid-v0.2-8B": 20000,
|
|
205
|
+
},
|
|
206
|
+
total={},
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
indexed = IndexedHordeModelStats(stats)
|
|
210
|
+
|
|
211
|
+
# Query with canonical name that HAS org prefix (like in model reference)
|
|
212
|
+
canonical_with_org = "NeverSleep/Lumimaid-v0.2"
|
|
213
|
+
day, month, _total = indexed.get_aggregated_stats(canonical_with_org)
|
|
214
|
+
|
|
215
|
+
# Should aggregate all variants even though canonical has org prefix
|
|
216
|
+
assert day == 4080 + 1500 + 2000
|
|
217
|
+
assert month == 40000 + 15000 + 20000
|
|
218
|
+
|
|
219
|
+
def test_get_stats_with_variations_canonical_with_org_prefix(self) -> None:
|
|
220
|
+
"""Test variations breakdown with canonical name that has org prefix.
|
|
221
|
+
|
|
222
|
+
When querying for a specific model like 'NeverSleep/Lumimaid-v0.2-8B',
|
|
223
|
+
get_stats_with_variations should find:
|
|
224
|
+
- The aphrodite variant: aphrodite/NeverSleep/Lumimaid-v0.2-8B
|
|
225
|
+
- The koboldcpp variant: koboldcpp/Lumimaid-v0.2-8B
|
|
226
|
+
- Quantization variants: koboldcpp/Lumimaid-v0.2-8B-Q8_0
|
|
227
|
+
"""
|
|
228
|
+
stats = HordeModelStatsResponse(
|
|
229
|
+
day={
|
|
230
|
+
"koboldcpp/Lumimaid-v0.2-8B": 300,
|
|
231
|
+
"koboldcpp/Lumimaid-v0.2-8B-Q8_0": 400,
|
|
232
|
+
"aphrodite/NeverSleep/Lumimaid-v0.2-8B": 200,
|
|
233
|
+
},
|
|
234
|
+
month={},
|
|
235
|
+
total={},
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
indexed = IndexedHordeModelStats(stats)
|
|
239
|
+
|
|
240
|
+
# Query with canonical name that HAS org prefix (like in model reference)
|
|
241
|
+
(day_total, _m, _t), variations = indexed.get_stats_with_variations("NeverSleep/Lumimaid-v0.2-8B")
|
|
242
|
+
|
|
243
|
+
assert day_total == 300 + 400 + 200
|
|
244
|
+
assert "aphrodite" in variations
|
|
245
|
+
assert "koboldcpp" in variations
|
|
246
|
+
assert variations["aphrodite"][0] == 200
|
|
247
|
+
assert variations["koboldcpp"][0] == 300 + 400
|