fasterrag 0.1.0b1__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.
- fasterrag-0.1.0b1/.env.example +34 -0
- fasterrag-0.1.0b1/.github/scripts/check_licences.py +457 -0
- fasterrag-0.1.0b1/.github/scripts/release.py +226 -0
- fasterrag-0.1.0b1/.github/scripts/release_smoke.sh +103 -0
- fasterrag-0.1.0b1/.gitignore +47 -0
- fasterrag-0.1.0b1/LICENSE +202 -0
- fasterrag-0.1.0b1/NOTICE +4 -0
- fasterrag-0.1.0b1/PKG-INFO +513 -0
- fasterrag-0.1.0b1/README.md +382 -0
- fasterrag-0.1.0b1/config.yaml +215 -0
- fasterrag-0.1.0b1/docs/CHANGELOG.md +392 -0
- fasterrag-0.1.0b1/docs/CONTRIBUTING.md +106 -0
- fasterrag-0.1.0b1/docs/adr/ADR-0001-qdrant-as-reference-vector-db.md +36 -0
- fasterrag-0.1.0b1/docs/adr/ADR-0002-adapter-factory-pluggability.md +33 -0
- fasterrag-0.1.0b1/docs/adr/ADR-0003-config-yaml-env-split.md +34 -0
- fasterrag-0.1.0b1/docs/adr/ADR-0004-hybrid-search-plus-reranking.md +35 -0
- fasterrag-0.1.0b1/docs/adr/ADR-0005-api-cli-only-control-plane.md +32 -0
- fasterrag-0.1.0b1/docs/adr/ADR-0006-python-package-surface.md +34 -0
- fasterrag-0.1.0b1/docs/adr/ADR-0007-bm25-as-backend-sparse-vectors.md +40 -0
- fasterrag-0.1.0b1/docs/adr/ADR-0008-apache-2-license.md +46 -0
- fasterrag-0.1.0b1/docs/adr/ADR-0009-journal-stays-on-files.md +54 -0
- fasterrag-0.1.0b1/docs/adr/ADR-0010-reranker-off-by-default.md +47 -0
- fasterrag-0.1.0b1/docs/adr/ADR-0011-degradation-ladder-scope.md +45 -0
- fasterrag-0.1.0b1/docs/api-reference.md +410 -0
- fasterrag-0.1.0b1/docs/architecture.md +170 -0
- fasterrag-0.1.0b1/docs/archive-format.md +242 -0
- fasterrag-0.1.0b1/docs/assets/cistatus.svg +1 -0
- fasterrag-0.1.0b1/docs/assets/extra.css +5 -0
- fasterrag-0.1.0b1/docs/assets/favicon.png +0 -0
- fasterrag-0.1.0b1/docs/assets/help.svg +1 -0
- fasterrag-0.1.0b1/docs/assets/index.svg +1 -0
- fasterrag-0.1.0b1/docs/assets/intro.css +1203 -0
- fasterrag-0.1.0b1/docs/assets/logo-banner-dark.svg +1 -0
- fasterrag-0.1.0b1/docs/assets/logo-banner-light.svg +1 -0
- fasterrag-0.1.0b1/docs/assets/logo-mono.svg +1 -0
- fasterrag-0.1.0b1/docs/assets/logo.svg +1 -0
- fasterrag-0.1.0b1/docs/assets/social-card.png +0 -0
- fasterrag-0.1.0b1/docs/benchmarks.md +200 -0
- fasterrag-0.1.0b1/docs/blockers.md +86 -0
- fasterrag-0.1.0b1/docs/cli-reference.md +389 -0
- fasterrag-0.1.0b1/docs/code-notes.md +6453 -0
- fasterrag-0.1.0b1/docs/config-reference.md +537 -0
- fasterrag-0.1.0b1/docs/cookbook.md +302 -0
- fasterrag-0.1.0b1/docs/data-model.md +198 -0
- fasterrag-0.1.0b1/docs/deployment.md +100 -0
- fasterrag-0.1.0b1/docs/differentiators.md +136 -0
- fasterrag-0.1.0b1/docs/disaster-recovery.md +83 -0
- fasterrag-0.1.0b1/docs/engineering-audit.md +318 -0
- fasterrag-0.1.0b1/docs/engineering-notes.md +53 -0
- fasterrag-0.1.0b1/docs/engineering-scorecard.md +197 -0
- fasterrag-0.1.0b1/docs/failure-modes.md +211 -0
- fasterrag-0.1.0b1/docs/flow.md +118 -0
- fasterrag-0.1.0b1/docs/glossary.md +53 -0
- fasterrag-0.1.0b1/docs/how-to-use.md +112 -0
- fasterrag-0.1.0b1/docs/index.md +175 -0
- fasterrag-0.1.0b1/docs/integrations.md +154 -0
- fasterrag-0.1.0b1/docs/internals.md +79 -0
- fasterrag-0.1.0b1/docs/migration-guide.md +78 -0
- fasterrag-0.1.0b1/docs/observability.md +153 -0
- fasterrag-0.1.0b1/docs/overrides/main.html +58 -0
- fasterrag-0.1.0b1/docs/performance.md +121 -0
- fasterrag-0.1.0b1/docs/plugins.md +108 -0
- fasterrag-0.1.0b1/docs/prompts.md +189 -0
- fasterrag-0.1.0b1/docs/python-api.md +271 -0
- fasterrag-0.1.0b1/docs/quickstart.md +236 -0
- fasterrag-0.1.0b1/docs/rebuild-playbook.md +73 -0
- fasterrag-0.1.0b1/docs/references.md +30 -0
- fasterrag-0.1.0b1/docs/release-readiness.md +240 -0
- fasterrag-0.1.0b1/docs/release.md +67 -0
- fasterrag-0.1.0b1/docs/reliability.md +102 -0
- fasterrag-0.1.0b1/docs/robots.txt +3 -0
- fasterrag-0.1.0b1/docs/scope.md +113 -0
- fasterrag-0.1.0b1/docs/security.md +405 -0
- fasterrag-0.1.0b1/docs/slo.md +35 -0
- fasterrag-0.1.0b1/docs/structure.md +164 -0
- fasterrag-0.1.0b1/docs/task-briefs.md +470 -0
- fasterrag-0.1.0b1/docs/testing-strategy.md +382 -0
- fasterrag-0.1.0b1/docs/todo.md +704 -0
- fasterrag-0.1.0b1/docs/tour.md +277 -0
- fasterrag-0.1.0b1/docs/troubleshooting.md +163 -0
- fasterrag-0.1.0b1/docs/verification.md +117 -0
- fasterrag-0.1.0b1/examples/README.md +53 -0
- fasterrag-0.1.0b1/examples/plugin-vectordb/README.md +31 -0
- fasterrag-0.1.0b1/examples/plugin-vectordb/pyproject.toml +19 -0
- fasterrag-0.1.0b1/examples/plugin-vectordb/src/fasterrag_memory_vectordb/__init__.py +5 -0
- fasterrag-0.1.0b1/examples/plugin-vectordb/src/fasterrag_memory_vectordb/adapter.py +305 -0
- fasterrag-0.1.0b1/examples/plugin-vectordb/src/fasterrag_memory_vectordb/py.typed +0 -0
- fasterrag-0.1.0b1/examples/plugin-vectordb/tests/test_contract.py +55 -0
- fasterrag-0.1.0b1/examples/scripted-llm/README.md +12 -0
- fasterrag-0.1.0b1/examples/scripted-llm/pyproject.toml +19 -0
- fasterrag-0.1.0b1/examples/scripted-llm/src/fasterrag_scripted_llm/__init__.py +5 -0
- fasterrag-0.1.0b1/examples/scripted-llm/src/fasterrag_scripted_llm/adapter.py +79 -0
- fasterrag-0.1.0b1/examples/scripted-llm/src/fasterrag_scripted_llm/py.typed +0 -0
- fasterrag-0.1.0b1/pyproject.toml +312 -0
- fasterrag-0.1.0b1/scripts/_checkout.py +34 -0
- fasterrag-0.1.0b1/scripts/check_blockers.py +131 -0
- fasterrag-0.1.0b1/scripts/check_claims.py +291 -0
- fasterrag-0.1.0b1/scripts/check_comments.py +334 -0
- fasterrag-0.1.0b1/scripts/check_commit_message.py +143 -0
- fasterrag-0.1.0b1/scripts/check_contract_ran.py +254 -0
- fasterrag-0.1.0b1/scripts/check_doc_truth.py +144 -0
- fasterrag-0.1.0b1/scripts/check_error_taxonomy.py +678 -0
- fasterrag-0.1.0b1/scripts/check_layering.py +479 -0
- fasterrag-0.1.0b1/scripts/check_must_run.py +345 -0
- fasterrag-0.1.0b1/scripts/check_openapi_drift.py +114 -0
- fasterrag-0.1.0b1/scripts/check_package.py +376 -0
- fasterrag-0.1.0b1/scripts/check_task_ids.py +299 -0
- fasterrag-0.1.0b1/scripts/check_unbuilt_claims.py +935 -0
- fasterrag-0.1.0b1/scripts/compare_nightly.py +492 -0
- fasterrag-0.1.0b1/scripts/measure_cost_governor.py +418 -0
- fasterrag-0.1.0b1/scripts/measure_idempotency_reclaim.py +504 -0
- fasterrag-0.1.0b1/scripts/measure_rerank.py +967 -0
- fasterrag-0.1.0b1/scripts/mutation_run.py +1494 -0
- fasterrag-0.1.0b1/scripts/stamp_release.py +286 -0
- fasterrag-0.1.0b1/scripts/trim_docstrings.py +285 -0
- fasterrag-0.1.0b1/src/fasterrag/__init__.py +26 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/__init__.py +7 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/embeddings/__init__.py +36 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/embeddings/base.py +317 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/embeddings/cohere.py +153 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/embeddings/factory.py +83 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/embeddings/huggingface.py +174 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/embeddings/ollama.py +141 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/embeddings/openai.py +131 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/embeddings/sync.py +46 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/embeddings/tiering.py +135 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/llm/__init__.py +27 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/llm/anthropic.py +119 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/llm/base.py +233 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/llm/cohere.py +163 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/llm/factory.py +82 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/llm/ollama.py +126 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/llm/openai.py +136 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/plugin_api.py +66 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/vectordb/__init__.py +31 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/vectordb/base.py +339 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/vectordb/chroma.py +1169 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/vectordb/factory.py +110 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/vectordb/milvus.py +1251 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/vectordb/pgvector.py +1288 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/vectordb/qdrant.py +756 -0
- fasterrag-0.1.0b1/src/fasterrag/adapters/vectordb/weaviate.py +1409 -0
- fasterrag-0.1.0b1/src/fasterrag/api/__init__.py +6 -0
- fasterrag-0.1.0b1/src/fasterrag/api/admin.py +158 -0
- fasterrag-0.1.0b1/src/fasterrag/api/auth.py +695 -0
- fasterrag-0.1.0b1/src/fasterrag/api/collections.py +215 -0
- fasterrag-0.1.0b1/src/fasterrag/api/dependencies.py +129 -0
- fasterrag-0.1.0b1/src/fasterrag/api/health.py +110 -0
- fasterrag-0.1.0b1/src/fasterrag/api/idempotency.py +290 -0
- fasterrag-0.1.0b1/src/fasterrag/api/ingest.py +279 -0
- fasterrag-0.1.0b1/src/fasterrag/api/limits.py +106 -0
- fasterrag-0.1.0b1/src/fasterrag/api/main.py +393 -0
- fasterrag-0.1.0b1/src/fasterrag/api/metrics.py +95 -0
- fasterrag-0.1.0b1/src/fasterrag/api/problems.py +217 -0
- fasterrag-0.1.0b1/src/fasterrag/api/query.py +201 -0
- fasterrag-0.1.0b1/src/fasterrag/api/schemas.py +130 -0
- fasterrag-0.1.0b1/src/fasterrag/api/traces.py +133 -0
- fasterrag-0.1.0b1/src/fasterrag/chunking.py +34 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/__init__.py +13 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/commands/__init__.py +6 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/commands/autopilot.py +141 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/commands/backup.py +93 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/commands/benchmark.py +229 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/commands/diagnostics.py +360 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/commands/documents.py +87 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/commands/infrastructure.py +125 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/commands/pipeline.py +865 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/commands/portability.py +112 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/commands/processes.py +159 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/commands/replay.py +115 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/commands/traces.py +55 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/main.py +149 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/output.py +82 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/parser.py +508 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/settings.py +39 -0
- fasterrag-0.1.0b1/src/fasterrag/cli/sources.py +96 -0
- fasterrag-0.1.0b1/src/fasterrag/config/__init__.py +13 -0
- fasterrag-0.1.0b1/src/fasterrag/config/loader.py +289 -0
- fasterrag-0.1.0b1/src/fasterrag/config/plugins.py +118 -0
- fasterrag-0.1.0b1/src/fasterrag/config/schema.py +1089 -0
- fasterrag-0.1.0b1/src/fasterrag/config/template.py +82 -0
- fasterrag-0.1.0b1/src/fasterrag/core/__init__.py +6 -0
- fasterrag-0.1.0b1/src/fasterrag/core/breaker.py +251 -0
- fasterrag-0.1.0b1/src/fasterrag/core/cache/__init__.py +127 -0
- fasterrag-0.1.0b1/src/fasterrag/core/cache/embedding.py +308 -0
- fasterrag-0.1.0b1/src/fasterrag/core/cache/redis.py +287 -0
- fasterrag-0.1.0b1/src/fasterrag/core/cache/semantic.py +235 -0
- fasterrag-0.1.0b1/src/fasterrag/core/cache/stats.py +71 -0
- fasterrag-0.1.0b1/src/fasterrag/core/cache/store.py +419 -0
- fasterrag-0.1.0b1/src/fasterrag/core/chunking/__init__.py +87 -0
- fasterrag-0.1.0b1/src/fasterrag/core/chunking/enrichment.py +157 -0
- fasterrag-0.1.0b1/src/fasterrag/core/chunking/fixed.py +63 -0
- fasterrag-0.1.0b1/src/fasterrag/core/chunking/late.py +56 -0
- fasterrag-0.1.0b1/src/fasterrag/core/chunking/layout.py +120 -0
- fasterrag-0.1.0b1/src/fasterrag/core/chunking/models.py +235 -0
- fasterrag-0.1.0b1/src/fasterrag/core/chunking/pooling.py +154 -0
- fasterrag-0.1.0b1/src/fasterrag/core/chunking/recursive.py +131 -0
- fasterrag-0.1.0b1/src/fasterrag/core/chunking/semantic.py +205 -0
- fasterrag-0.1.0b1/src/fasterrag/core/chunking/tokenizer.py +113 -0
- fasterrag-0.1.0b1/src/fasterrag/core/confinement.py +52 -0
- fasterrag-0.1.0b1/src/fasterrag/core/context.py +208 -0
- fasterrag-0.1.0b1/src/fasterrag/core/evals/__init__.py +33 -0
- fasterrag-0.1.0b1/src/fasterrag/core/evals/generator.py +241 -0
- fasterrag-0.1.0b1/src/fasterrag/core/evals/golden.py +138 -0
- fasterrag-0.1.0b1/src/fasterrag/core/evals/harness.py +149 -0
- fasterrag-0.1.0b1/src/fasterrag/core/evals/metrics.py +67 -0
- fasterrag-0.1.0b1/src/fasterrag/core/faithfulness.py +176 -0
- fasterrag-0.1.0b1/src/fasterrag/core/generation.py +175 -0
- fasterrag-0.1.0b1/src/fasterrag/core/identity.py +202 -0
- fasterrag-0.1.0b1/src/fasterrag/core/parsing/__init__.py +215 -0
- fasterrag-0.1.0b1/src/fasterrag/core/parsing/docx.py +210 -0
- fasterrag-0.1.0b1/src/fasterrag/core/parsing/html.py +118 -0
- fasterrag-0.1.0b1/src/fasterrag/core/parsing/markdown.py +120 -0
- fasterrag-0.1.0b1/src/fasterrag/core/parsing/models.py +211 -0
- fasterrag-0.1.0b1/src/fasterrag/core/parsing/options.py +55 -0
- fasterrag-0.1.0b1/src/fasterrag/core/parsing/pdf.py +242 -0
- fasterrag-0.1.0b1/src/fasterrag/core/parsing/plaintext.py +97 -0
- fasterrag-0.1.0b1/src/fasterrag/core/parsing/tabular.py +191 -0
- fasterrag-0.1.0b1/src/fasterrag/core/rate_limit.py +259 -0
- fasterrag-0.1.0b1/src/fasterrag/core/rerank/__init__.py +9 -0
- fasterrag-0.1.0b1/src/fasterrag/core/rerank/cross_encoder.py +226 -0
- fasterrag-0.1.0b1/src/fasterrag/core/retrieval/__init__.py +30 -0
- fasterrag-0.1.0b1/src/fasterrag/core/retrieval/bm25.py +175 -0
- fasterrag-0.1.0b1/src/fasterrag/core/retrieval/fusion.py +121 -0
- fasterrag-0.1.0b1/src/fasterrag/core/retrieval/models.py +51 -0
- fasterrag-0.1.0b1/src/fasterrag/core/tenancy.py +19 -0
- fasterrag-0.1.0b1/src/fasterrag/core/tracing.py +199 -0
- fasterrag-0.1.0b1/src/fasterrag/data/config.local.yaml +30 -0
- fasterrag-0.1.0b1/src/fasterrag/errors.py +321 -0
- fasterrag-0.1.0b1/src/fasterrag/evals.py +31 -0
- fasterrag-0.1.0b1/src/fasterrag/facade.py +432 -0
- fasterrag-0.1.0b1/src/fasterrag/observability/__init__.py +6 -0
- fasterrag-0.1.0b1/src/fasterrag/observability/dashboard.py +195 -0
- fasterrag-0.1.0b1/src/fasterrag/observability/langfuse_export.py +220 -0
- fasterrag-0.1.0b1/src/fasterrag/observability/logging.py +263 -0
- fasterrag-0.1.0b1/src/fasterrag/observability/metrics.py +466 -0
- fasterrag-0.1.0b1/src/fasterrag/observability/otel_export.py +267 -0
- fasterrag-0.1.0b1/src/fasterrag/observability/otel_metrics.py +248 -0
- fasterrag-0.1.0b1/src/fasterrag/parsing.py +28 -0
- fasterrag-0.1.0b1/src/fasterrag/py.typed +0 -0
- fasterrag-0.1.0b1/src/fasterrag/rerank.py +9 -0
- fasterrag-0.1.0b1/src/fasterrag/retrieval.py +21 -0
- fasterrag-0.1.0b1/src/fasterrag/services/__init__.py +6 -0
- fasterrag-0.1.0b1/src/fasterrag/services/archive.py +385 -0
- fasterrag-0.1.0b1/src/fasterrag/services/archive_import.py +1077 -0
- fasterrag-0.1.0b1/src/fasterrag/services/autopilot.py +864 -0
- fasterrag-0.1.0b1/src/fasterrag/services/backfill.py +407 -0
- fasterrag-0.1.0b1/src/fasterrag/services/backup.py +331 -0
- fasterrag-0.1.0b1/src/fasterrag/services/benchmark.py +305 -0
- fasterrag-0.1.0b1/src/fasterrag/services/budget.py +250 -0
- fasterrag-0.1.0b1/src/fasterrag/services/ci_status.py +533 -0
- fasterrag-0.1.0b1/src/fasterrag/services/composition.py +133 -0
- fasterrag-0.1.0b1/src/fasterrag/services/degradation.py +135 -0
- fasterrag-0.1.0b1/src/fasterrag/services/doctor.py +708 -0
- fasterrag-0.1.0b1/src/fasterrag/services/documents.py +300 -0
- fasterrag-0.1.0b1/src/fasterrag/services/embedding_router.py +68 -0
- fasterrag-0.1.0b1/src/fasterrag/services/estimation.py +489 -0
- fasterrag-0.1.0b1/src/fasterrag/services/evaluation.py +206 -0
- fasterrag-0.1.0b1/src/fasterrag/services/generation.py +1299 -0
- fasterrag-0.1.0b1/src/fasterrag/services/golden_sets.py +96 -0
- fasterrag-0.1.0b1/src/fasterrag/services/grafana.py +542 -0
- fasterrag-0.1.0b1/src/fasterrag/services/idempotency.py +616 -0
- fasterrag-0.1.0b1/src/fasterrag/services/ingestion.py +534 -0
- fasterrag-0.1.0b1/src/fasterrag/services/jobs.py +358 -0
- fasterrag-0.1.0b1/src/fasterrag/services/journal.py +1217 -0
- fasterrag-0.1.0b1/src/fasterrag/services/langfuse.py +465 -0
- fasterrag-0.1.0b1/src/fasterrag/services/lockfile.py +443 -0
- fasterrag-0.1.0b1/src/fasterrag/services/provision_registry.py +111 -0
- fasterrag-0.1.0b1/src/fasterrag/services/provisioning.py +452 -0
- fasterrag-0.1.0b1/src/fasterrag/services/querying.py +387 -0
- fasterrag-0.1.0b1/src/fasterrag/services/regression.py +410 -0
- fasterrag-0.1.0b1/src/fasterrag/services/reindex.py +424 -0
- fasterrag-0.1.0b1/src/fasterrag/services/replay.py +191 -0
- fasterrag-0.1.0b1/src/fasterrag/services/sources.py +523 -0
- fasterrag-0.1.0b1/src/fasterrag/services/tenancy.py +98 -0
- fasterrag-0.1.0b1/src/fasterrag/services/traces.py +333 -0
- fasterrag-0.1.0b1/src/fasterrag/sync.py +322 -0
- fasterrag-0.1.0b1/src/fasterrag/workers/__init__.py +34 -0
- fasterrag-0.1.0b1/src/fasterrag/workers/cpu_pool.py +925 -0
- fasterrag-0.1.0b1/src/fasterrag/workers/embed_pool.py +484 -0
- fasterrag-0.1.0b1/src/fasterrag/workers/indexer.py +211 -0
- fasterrag-0.1.0b1/src/fasterrag/workers/ledger.py +179 -0
- fasterrag-0.1.0b1/src/fasterrag/workers/queues.py +197 -0
- fasterrag-0.1.0b1/tests/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/chaos/conftest.py +42 -0
- fasterrag-0.1.0b1/tests/chaos/disk_quota_probe.py +127 -0
- fasterrag-0.1.0b1/tests/chaos/interrupted_job_server.py +111 -0
- fasterrag-0.1.0b1/tests/chaos/test_chaos.py +397 -0
- fasterrag-0.1.0b1/tests/chaos/test_config_mutation.py +468 -0
- fasterrag-0.1.0b1/tests/chaos/test_generation_faults.py +290 -0
- fasterrag-0.1.0b1/tests/chaos/test_network_partition.py +375 -0
- fasterrag-0.1.0b1/tests/chaos/test_process_kill.py +124 -0
- fasterrag-0.1.0b1/tests/chaos/test_real_faults.py +370 -0
- fasterrag-0.1.0b1/tests/chaos/test_reindex_under_load.py +652 -0
- fasterrag-0.1.0b1/tests/chaos/test_reranker_down.py +373 -0
- fasterrag-0.1.0b1/tests/conftest.py +255 -0
- fasterrag-0.1.0b1/tests/contract/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/contract/vectordb.py +711 -0
- fasterrag-0.1.0b1/tests/eval/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/eval/datasets/handbook/baseline.json +35 -0
- fasterrag-0.1.0b1/tests/eval/datasets/handbook/corpus/expenses.md +8 -0
- fasterrag-0.1.0b1/tests/eval/datasets/handbook/corpus/incidents.md +9 -0
- fasterrag-0.1.0b1/tests/eval/datasets/handbook/corpus/leave-policy.md +9 -0
- fasterrag-0.1.0b1/tests/eval/datasets/handbook/corpus/onboarding.md +9 -0
- fasterrag-0.1.0b1/tests/eval/datasets/handbook/corpus/procurement.md +8 -0
- fasterrag-0.1.0b1/tests/eval/datasets/handbook/corpus/security.md +9 -0
- fasterrag-0.1.0b1/tests/eval/datasets/handbook/golden.jsonl +15 -0
- fasterrag-0.1.0b1/tests/eval/datasets/injection/cases.jsonl +8 -0
- fasterrag-0.1.0b1/tests/eval/datasets/policies/baseline.json +35 -0
- fasterrag-0.1.0b1/tests/eval/datasets/policies/corpus/access-administrator.md +18 -0
- fasterrag-0.1.0b1/tests/eval/datasets/policies/corpus/access-analyst.md +18 -0
- fasterrag-0.1.0b1/tests/eval/datasets/policies/corpus/access-contractor.md +18 -0
- fasterrag-0.1.0b1/tests/eval/datasets/policies/corpus/access-engineer.md +18 -0
- fasterrag-0.1.0b1/tests/eval/datasets/policies/corpus/expenses-uk-2025.md +20 -0
- fasterrag-0.1.0b1/tests/eval/datasets/policies/corpus/expenses-uk-2026.md +20 -0
- fasterrag-0.1.0b1/tests/eval/datasets/policies/corpus/expenses-us-2025.md +20 -0
- fasterrag-0.1.0b1/tests/eval/datasets/policies/corpus/expenses-us-2026.md +20 -0
- fasterrag-0.1.0b1/tests/eval/datasets/policies/corpus/parental-leave-uk-2025.md +18 -0
- fasterrag-0.1.0b1/tests/eval/datasets/policies/corpus/parental-leave-uk-2026.md +18 -0
- fasterrag-0.1.0b1/tests/eval/datasets/policies/corpus/parental-leave-us-2025.md +18 -0
- fasterrag-0.1.0b1/tests/eval/datasets/policies/corpus/parental-leave-us-2026.md +18 -0
- fasterrag-0.1.0b1/tests/eval/datasets/policies/golden.jsonl +19 -0
- fasterrag-0.1.0b1/tests/eval/test_handbook_baseline.py +188 -0
- fasterrag-0.1.0b1/tests/eval/test_live_provider_gating.py +302 -0
- fasterrag-0.1.0b1/tests/eval/test_live_providers.py +537 -0
- fasterrag-0.1.0b1/tests/eval/test_prompt_injection_live.py +139 -0
- fasterrag-0.1.0b1/tests/eval/test_real_models.py +145 -0
- fasterrag-0.1.0b1/tests/integration/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/integration/chroma/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/integration/chroma/conftest.py +50 -0
- fasterrag-0.1.0b1/tests/integration/chroma/test_chroma_contract.py +394 -0
- fasterrag-0.1.0b1/tests/integration/conftest.py +122 -0
- fasterrag-0.1.0b1/tests/integration/milvus/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/integration/milvus/conftest.py +107 -0
- fasterrag-0.1.0b1/tests/integration/milvus/test_milvus_contract.py +252 -0
- fasterrag-0.1.0b1/tests/integration/pgvector/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/integration/pgvector/conftest.py +143 -0
- fasterrag-0.1.0b1/tests/integration/pgvector/test_pgvector_contract.py +217 -0
- fasterrag-0.1.0b1/tests/integration/pgvector/test_pgvector_pooling.py +290 -0
- fasterrag-0.1.0b1/tests/integration/test_archive_round_trip.py +451 -0
- fasterrag-0.1.0b1/tests/integration/test_doctor_fix.py +135 -0
- fasterrag-0.1.0b1/tests/integration/test_ingest_pipeline.py +354 -0
- fasterrag-0.1.0b1/tests/integration/test_keyword_leg_write_paths.py +113 -0
- fasterrag-0.1.0b1/tests/integration/test_qdrant_contract.py +97 -0
- fasterrag-0.1.0b1/tests/integration/test_redis_cache.py +200 -0
- fasterrag-0.1.0b1/tests/integration/test_redis_rate_limit.py +170 -0
- fasterrag-0.1.0b1/tests/integration/test_redis_rate_limit_server.py +261 -0
- fasterrag-0.1.0b1/tests/integration/weaviate/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/integration/weaviate/conftest.py +140 -0
- fasterrag-0.1.0b1/tests/integration/weaviate/test_weaviate_contract.py +449 -0
- fasterrag-0.1.0b1/tests/load/__init__.py +6 -0
- fasterrag-0.1.0b1/tests/load/harness.py +936 -0
- fasterrag-0.1.0b1/tests/load/locustfile.py +332 -0
- fasterrag-0.1.0b1/tests/load/preflight.py +222 -0
- fasterrag-0.1.0b1/tests/load/requirements.txt +10 -0
- fasterrag-0.1.0b1/tests/load/run_load.py +323 -0
- fasterrag-0.1.0b1/tests/load/test_load_harness.py +399 -0
- fasterrag-0.1.0b1/tests/load/test_saturation.py +173 -0
- fasterrag-0.1.0b1/tests/soak/__init__.py +7 -0
- fasterrag-0.1.0b1/tests/soak/conftest.py +19 -0
- fasterrag-0.1.0b1/tests/soak/test_soak.py +148 -0
- fasterrag-0.1.0b1/tests/soak/test_trend.py +216 -0
- fasterrag-0.1.0b1/tests/soak/trend.py +191 -0
- fasterrag-0.1.0b1/tests/soak/workload.py +263 -0
- fasterrag-0.1.0b1/tests/support/__init__.py +1 -0
- fasterrag-0.1.0b1/tests/support/acme_plugin.py +266 -0
- fasterrag-0.1.0b1/tests/support/archive.py +38 -0
- fasterrag-0.1.0b1/tests/support/doc_examples.py +42 -0
- fasterrag-0.1.0b1/tests/support/plugins.py +48 -0
- fasterrag-0.1.0b1/tests/test_coverage_records_contexts.py +164 -0
- fasterrag-0.1.0b1/tests/test_git_environment_is_clean.py +50 -0
- fasterrag-0.1.0b1/tests/test_runs_record_the_tree_they_measured.py +65 -0
- fasterrag-0.1.0b1/tests/test_suite_targets_this_checkout.py +34 -0
- fasterrag-0.1.0b1/tests/unit/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/adapters/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/adapters/embeddings/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/adapters/embeddings/conftest.py +78 -0
- fasterrag-0.1.0b1/tests/unit/adapters/embeddings/test_base.py +128 -0
- fasterrag-0.1.0b1/tests/unit/adapters/embeddings/test_huggingface.py +159 -0
- fasterrag-0.1.0b1/tests/unit/adapters/embeddings/test_providers.py +110 -0
- fasterrag-0.1.0b1/tests/unit/adapters/embeddings/test_real_clients.py +245 -0
- fasterrag-0.1.0b1/tests/unit/adapters/embeddings/test_refusals.py +133 -0
- fasterrag-0.1.0b1/tests/unit/adapters/embeddings/test_tiering.py +179 -0
- fasterrag-0.1.0b1/tests/unit/adapters/embeddings/test_usage.py +319 -0
- fasterrag-0.1.0b1/tests/unit/adapters/llm/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/adapters/llm/test_call_paths.py +306 -0
- fasterrag-0.1.0b1/tests/unit/adapters/llm/test_call_paths_cohere_ollama.py +363 -0
- fasterrag-0.1.0b1/tests/unit/adapters/llm/test_llm.py +271 -0
- fasterrag-0.1.0b1/tests/unit/adapters/llm/test_real_clients.py +349 -0
- fasterrag-0.1.0b1/tests/unit/adapters/recordings.py +285 -0
- fasterrag-0.1.0b1/tests/unit/adapters/test_adapter_api_version.py +150 -0
- fasterrag-0.1.0b1/tests/unit/adapters/test_base.py +63 -0
- fasterrag-0.1.0b1/tests/unit/adapters/test_chroma.py +455 -0
- fasterrag-0.1.0b1/tests/unit/adapters/test_factory.py +100 -0
- fasterrag-0.1.0b1/tests/unit/adapters/test_pgvector.py +600 -0
- fasterrag-0.1.0b1/tests/unit/adapters/test_plugin_entry_points.py +86 -0
- fasterrag-0.1.0b1/tests/unit/adapters/test_qdrant.py +596 -0
- fasterrag-0.1.0b1/tests/unit/adapters/test_qdrant_iterate.py +194 -0
- fasterrag-0.1.0b1/tests/unit/adapters/vectordb/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/adapters/vectordb/test_milvus.py +1356 -0
- fasterrag-0.1.0b1/tests/unit/adapters/vectordb/test_weaviate.py +1767 -0
- fasterrag-0.1.0b1/tests/unit/api/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/api/conftest.py +125 -0
- fasterrag-0.1.0b1/tests/unit/api/test_admin_doctor.py +285 -0
- fasterrag-0.1.0b1/tests/unit/api/test_auth.py +765 -0
- fasterrag-0.1.0b1/tests/unit/api/test_body_limits.py +150 -0
- fasterrag-0.1.0b1/tests/unit/api/test_collections.py +313 -0
- fasterrag-0.1.0b1/tests/unit/api/test_documents.py +244 -0
- fasterrag-0.1.0b1/tests/unit/api/test_estimate_endpoint.py +90 -0
- fasterrag-0.1.0b1/tests/unit/api/test_health.py +203 -0
- fasterrag-0.1.0b1/tests/unit/api/test_idempotency.py +539 -0
- fasterrag-0.1.0b1/tests/unit/api/test_ingest.py +368 -0
- fasterrag-0.1.0b1/tests/unit/api/test_ingest_keyword_leg.py +186 -0
- fasterrag-0.1.0b1/tests/unit/api/test_ingest_lifecycle.py +267 -0
- fasterrag-0.1.0b1/tests/unit/api/test_ingest_tenancy.py +73 -0
- fasterrag-0.1.0b1/tests/unit/api/test_key_never_leaks.py +191 -0
- fasterrag-0.1.0b1/tests/unit/api/test_key_rotation.py +292 -0
- fasterrag-0.1.0b1/tests/unit/api/test_metrics_endpoint.py +96 -0
- fasterrag-0.1.0b1/tests/unit/api/test_portability_endpoints.py +300 -0
- fasterrag-0.1.0b1/tests/unit/api/test_problems.py +174 -0
- fasterrag-0.1.0b1/tests/unit/api/test_provisioning_endpoints.py +247 -0
- fasterrag-0.1.0b1/tests/unit/api/test_query.py +578 -0
- fasterrag-0.1.0b1/tests/unit/api/test_query_tenancy.py +433 -0
- fasterrag-0.1.0b1/tests/unit/api/test_response_headers.py +100 -0
- fasterrag-0.1.0b1/tests/unit/api/test_route_scopes.py +167 -0
- fasterrag-0.1.0b1/tests/unit/api/test_schema_fields_are_wired.py +55 -0
- fasterrag-0.1.0b1/tests/unit/api/test_shared_llm.py +149 -0
- fasterrag-0.1.0b1/tests/unit/api/test_shutdown_drain.py +223 -0
- fasterrag-0.1.0b1/tests/unit/api/test_startup_reaper.py +119 -0
- fasterrag-0.1.0b1/tests/unit/api/test_tenancy.py +155 -0
- fasterrag-0.1.0b1/tests/unit/api/test_traces_endpoints.py +246 -0
- fasterrag-0.1.0b1/tests/unit/cli/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/cli/conftest.py +97 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_autopilot_commands.py +482 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_backup_commands.py +269 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_benchmark_command.py +445 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_ci_status_command.py +285 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_cli.py +377 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_config_init.py +185 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_config_show.py +100 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_dispatch.py +170 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_doctor_command.py +98 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_document_listing.py +365 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_documents_command.py +184 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_flags_are_wired.py +100 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_ingest_resume.py +151 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_pipeline_commands.py +1095 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_portability_commands.py +443 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_processes_commands.py +367 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_query_stream.py +195 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_replay_command.py +228 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_sources.py +179 -0
- fasterrag-0.1.0b1/tests/unit/cli/test_traces_command.py +127 -0
- fasterrag-0.1.0b1/tests/unit/config/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/config/conftest.py +33 -0
- fasterrag-0.1.0b1/tests/unit/config/test_canonical_config.py +26 -0
- fasterrag-0.1.0b1/tests/unit/config/test_loader.py +376 -0
- fasterrag-0.1.0b1/tests/unit/config/test_overrides.py +271 -0
- fasterrag-0.1.0b1/tests/unit/config/test_planned_providers.py +80 -0
- fasterrag-0.1.0b1/tests/unit/config/test_plugin_keys.py +128 -0
- fasterrag-0.1.0b1/tests/unit/config/test_plugins.py +67 -0
- fasterrag-0.1.0b1/tests/unit/config/test_provider_names.py +210 -0
- fasterrag-0.1.0b1/tests/unit/config/test_schema.py +335 -0
- fasterrag-0.1.0b1/tests/unit/config/test_settings_are_wired.py +110 -0
- fasterrag-0.1.0b1/tests/unit/config/test_tuning_settings.py +211 -0
- fasterrag-0.1.0b1/tests/unit/conftest.py +60 -0
- fasterrag-0.1.0b1/tests/unit/core/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/core/cache/test_embedding.py +584 -0
- fasterrag-0.1.0b1/tests/unit/core/cache/test_redis_store.py +633 -0
- fasterrag-0.1.0b1/tests/unit/core/cache/test_semantic.py +277 -0
- fasterrag-0.1.0b1/tests/unit/core/cache/test_semantic_properties.py +139 -0
- fasterrag-0.1.0b1/tests/unit/core/cache/test_store.py +531 -0
- fasterrag-0.1.0b1/tests/unit/core/chunking/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/core/chunking/test_boundaries.py +762 -0
- fasterrag-0.1.0b1/tests/unit/core/chunking/test_enrichment.py +223 -0
- fasterrag-0.1.0b1/tests/unit/core/chunking/test_invariants.py +315 -0
- fasterrag-0.1.0b1/tests/unit/core/chunking/test_pooling.py +489 -0
- fasterrag-0.1.0b1/tests/unit/core/chunking/test_strategies.py +186 -0
- fasterrag-0.1.0b1/tests/unit/core/chunking/test_tokenizer.py +338 -0
- fasterrag-0.1.0b1/tests/unit/core/evals/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/core/evals/test_generator.py +294 -0
- fasterrag-0.1.0b1/tests/unit/core/evals/test_harness.py +247 -0
- fasterrag-0.1.0b1/tests/unit/core/evals/test_metrics.py +78 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/conftest.py +233 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/golden/csv.json +24 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/golden/docx.json +62 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/golden/html.json +62 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/golden/json.json +57 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/golden/markdown.json +75 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/golden/pdf.json +47 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/test_golden.py +48 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/test_hostile_inputs.py +222 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/test_invariants.py +814 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/test_models.py +126 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/test_parser_correctness.py +211 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/test_parsers.py +210 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/test_registry.py +73 -0
- fasterrag-0.1.0b1/tests/unit/core/parsing/test_source_formats.py +201 -0
- fasterrag-0.1.0b1/tests/unit/core/rerank/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/core/rerank/test_cross_encoder.py +243 -0
- fasterrag-0.1.0b1/tests/unit/core/retrieval/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/core/retrieval/test_bm25.py +187 -0
- fasterrag-0.1.0b1/tests/unit/core/retrieval/test_bm25_properties.py +340 -0
- fasterrag-0.1.0b1/tests/unit/core/retrieval/test_fusion.py +182 -0
- fasterrag-0.1.0b1/tests/unit/core/retrieval/test_fusion_properties.py +356 -0
- fasterrag-0.1.0b1/tests/unit/core/retrieval/test_models.py +69 -0
- fasterrag-0.1.0b1/tests/unit/core/test_breaker.py +556 -0
- fasterrag-0.1.0b1/tests/unit/core/test_context.py +238 -0
- fasterrag-0.1.0b1/tests/unit/core/test_context_properties.py +119 -0
- fasterrag-0.1.0b1/tests/unit/core/test_faithfulness.py +184 -0
- fasterrag-0.1.0b1/tests/unit/core/test_generation.py +264 -0
- fasterrag-0.1.0b1/tests/unit/core/test_identity.py +249 -0
- fasterrag-0.1.0b1/tests/unit/core/test_identity_properties.py +199 -0
- fasterrag-0.1.0b1/tests/unit/core/test_prompt_injection.py +132 -0
- fasterrag-0.1.0b1/tests/unit/core/test_rate_limit.py +409 -0
- fasterrag-0.1.0b1/tests/unit/core/test_redis_timeouts.py +129 -0
- fasterrag-0.1.0b1/tests/unit/core/test_source_normalisation.py +67 -0
- fasterrag-0.1.0b1/tests/unit/docs/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/docs/test_cli_flag_parity.py +347 -0
- fasterrag-0.1.0b1/tests/unit/docs/test_counts_parity.py +287 -0
- fasterrag-0.1.0b1/tests/unit/docs/test_doc_examples.py +181 -0
- fasterrag-0.1.0b1/tests/unit/docs/test_error_table_parity.py +142 -0
- fasterrag-0.1.0b1/tests/unit/docs/test_fmea_parity.py +222 -0
- fasterrag-0.1.0b1/tests/unit/docs/test_internals_parity.py +222 -0
- fasterrag-0.1.0b1/tests/unit/docs/test_python_api_parity.py +615 -0
- fasterrag-0.1.0b1/tests/unit/docs/test_reference_parity.py +131 -0
- fasterrag-0.1.0b1/tests/unit/docs/test_site_pages.py +111 -0
- fasterrag-0.1.0b1/tests/unit/docs/test_superlative_scan.py +256 -0
- fasterrag-0.1.0b1/tests/unit/examples/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/examples/test_sample_plugin.py +87 -0
- fasterrag-0.1.0b1/tests/unit/observability/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/observability/test_dashboard.py +211 -0
- fasterrag-0.1.0b1/tests/unit/observability/test_langfuse_export.py +290 -0
- fasterrag-0.1.0b1/tests/unit/observability/test_logging.py +221 -0
- fasterrag-0.1.0b1/tests/unit/observability/test_metrics.py +191 -0
- fasterrag-0.1.0b1/tests/unit/observability/test_otel_export.py +342 -0
- fasterrag-0.1.0b1/tests/unit/observability/test_otel_metrics.py +296 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_check_blockers.py +103 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_check_claims.py +394 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_check_comments.py +333 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_check_contract_ran.py +165 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_check_doc_truth.py +142 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_check_error_taxonomy.py +735 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_check_layering.py +267 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_check_licences.py +141 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_check_must_run.py +223 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_check_openapi_drift.py +63 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_check_package.py +139 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_check_task_ids.py +300 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_check_unbuilt_claims.py +761 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_checkout_bootstrap.py +97 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_compare_nightly.py +379 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_measure_cost_governor.py +154 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_measure_rerank.py +506 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_mutation_run.py +1419 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_release_pipeline.py +307 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_stamp_release.py +185 -0
- fasterrag-0.1.0b1/tests/unit/scripts/test_trim_docstrings.py +129 -0
- fasterrag-0.1.0b1/tests/unit/services/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/services/test_archive_export.py +618 -0
- fasterrag-0.1.0b1/tests/unit/services/test_archive_format_spec.py +307 -0
- fasterrag-0.1.0b1/tests/unit/services/test_archive_import.py +1846 -0
- fasterrag-0.1.0b1/tests/unit/services/test_autopilot.py +905 -0
- fasterrag-0.1.0b1/tests/unit/services/test_backup.py +233 -0
- fasterrag-0.1.0b1/tests/unit/services/test_benchmark.py +177 -0
- fasterrag-0.1.0b1/tests/unit/services/test_budget.py +136 -0
- fasterrag-0.1.0b1/tests/unit/services/test_budget_accounting.py +157 -0
- fasterrag-0.1.0b1/tests/unit/services/test_budget_sharing.py +405 -0
- fasterrag-0.1.0b1/tests/unit/services/test_ci_status.py +846 -0
- fasterrag-0.1.0b1/tests/unit/services/test_comparability.py +251 -0
- fasterrag-0.1.0b1/tests/unit/services/test_composition.py +402 -0
- fasterrag-0.1.0b1/tests/unit/services/test_cost_metric.py +216 -0
- fasterrag-0.1.0b1/tests/unit/services/test_cost_window.py +187 -0
- fasterrag-0.1.0b1/tests/unit/services/test_dead_letter_retry.py +134 -0
- fasterrag-0.1.0b1/tests/unit/services/test_degradation.py +118 -0
- fasterrag-0.1.0b1/tests/unit/services/test_doctor.py +676 -0
- fasterrag-0.1.0b1/tests/unit/services/test_document_updates.py +380 -0
- fasterrag-0.1.0b1/tests/unit/services/test_documents.py +341 -0
- fasterrag-0.1.0b1/tests/unit/services/test_enrichment_estimate.py +94 -0
- fasterrag-0.1.0b1/tests/unit/services/test_estimation.py +183 -0
- fasterrag-0.1.0b1/tests/unit/services/test_faithfulness_eval.py +39 -0
- fasterrag-0.1.0b1/tests/unit/services/test_generation.py +1906 -0
- fasterrag-0.1.0b1/tests/unit/services/test_grafana.py +233 -0
- fasterrag-0.1.0b1/tests/unit/services/test_idempotency_reclaim.py +278 -0
- fasterrag-0.1.0b1/tests/unit/services/test_identity_drift.py +67 -0
- fasterrag-0.1.0b1/tests/unit/services/test_ingest_size_refusals.py +69 -0
- fasterrag-0.1.0b1/tests/unit/services/test_ingestion.py +699 -0
- fasterrag-0.1.0b1/tests/unit/services/test_job_interruption.py +355 -0
- fasterrag-0.1.0b1/tests/unit/services/test_job_reaper.py +181 -0
- fasterrag-0.1.0b1/tests/unit/services/test_jobs.py +187 -0
- fasterrag-0.1.0b1/tests/unit/services/test_journal.py +766 -0
- fasterrag-0.1.0b1/tests/unit/services/test_journal_crashes.py +372 -0
- fasterrag-0.1.0b1/tests/unit/services/test_journal_retention.py +240 -0
- fasterrag-0.1.0b1/tests/unit/services/test_langfuse.py +201 -0
- fasterrag-0.1.0b1/tests/unit/services/test_lockfile.py +258 -0
- fasterrag-0.1.0b1/tests/unit/services/test_policies_fixture.py +69 -0
- fasterrag-0.1.0b1/tests/unit/services/test_provisioning.py +365 -0
- fasterrag-0.1.0b1/tests/unit/services/test_query_edges.py +294 -0
- fasterrag-0.1.0b1/tests/unit/services/test_query_lock.py +197 -0
- fasterrag-0.1.0b1/tests/unit/services/test_querying.py +522 -0
- fasterrag-0.1.0b1/tests/unit/services/test_regression.py +393 -0
- fasterrag-0.1.0b1/tests/unit/services/test_reindex.py +461 -0
- fasterrag-0.1.0b1/tests/unit/services/test_replay.py +345 -0
- fasterrag-0.1.0b1/tests/unit/services/test_sources.py +959 -0
- fasterrag-0.1.0b1/tests/unit/services/test_tenancy_properties.py +129 -0
- fasterrag-0.1.0b1/tests/unit/services/test_tenancy_scoping.py +66 -0
- fasterrag-0.1.0b1/tests/unit/services/test_tenant_backfill.py +378 -0
- fasterrag-0.1.0b1/tests/unit/services/test_traces.py +181 -0
- fasterrag-0.1.0b1/tests/unit/test_archive_support.py +68 -0
- fasterrag-0.1.0b1/tests/unit/test_commit_message_rule.py +81 -0
- fasterrag-0.1.0b1/tests/unit/test_errors.py +100 -0
- fasterrag-0.1.0b1/tests/unit/test_facade.py +346 -0
- fasterrag-0.1.0b1/tests/unit/test_facade_documents.py +161 -0
- fasterrag-0.1.0b1/tests/unit/test_facade_tenancy.py +253 -0
- fasterrag-0.1.0b1/tests/unit/test_facade_traces.py +97 -0
- fasterrag-0.1.0b1/tests/unit/test_no_blocking_journal_in_async.py +129 -0
- fasterrag-0.1.0b1/tests/unit/test_no_network_clients.py +43 -0
- fasterrag-0.1.0b1/tests/unit/test_package.py +6 -0
- fasterrag-0.1.0b1/tests/unit/test_plugin_selection.py +145 -0
- fasterrag-0.1.0b1/tests/unit/test_public_surface.py +153 -0
- fasterrag-0.1.0b1/tests/unit/test_slim_core.py +148 -0
- fasterrag-0.1.0b1/tests/unit/test_sync_facade.py +180 -0
- fasterrag-0.1.0b1/tests/unit/workers/__init__.py +0 -0
- fasterrag-0.1.0b1/tests/unit/workers/process_targets.py +106 -0
- fasterrag-0.1.0b1/tests/unit/workers/test_cpu_pool.py +719 -0
- fasterrag-0.1.0b1/tests/unit/workers/test_embed_pool.py +445 -0
- fasterrag-0.1.0b1/tests/unit/workers/test_embed_size_refusals.py +221 -0
- fasterrag-0.1.0b1/tests/unit/workers/test_embed_usage_accounting.py +239 -0
- fasterrag-0.1.0b1/tests/unit/workers/test_enrichment_wiring.py +92 -0
- fasterrag-0.1.0b1/tests/unit/workers/test_hostile_documents.py +187 -0
- fasterrag-0.1.0b1/tests/unit/workers/test_indexer.py +357 -0
- fasterrag-0.1.0b1/tests/unit/workers/test_late_chunking.py +184 -0
- fasterrag-0.1.0b1/tests/unit/workers/test_ledger.py +264 -0
- fasterrag-0.1.0b1/tests/unit/workers/test_queue_depth_metric.py +56 -0
- fasterrag-0.1.0b1/tests/unit/workers/test_queues.py +114 -0
- fasterrag-0.1.0b1/tests/unit/workers/test_real_process_pool.py +449 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# fasterRag secrets template — copy to .env and fill in what your config.yaml references.
|
|
2
|
+
# NEVER commit .env. config.yaml refers to these by NAME only (e.g. api_key_env: OPENAI_API_KEY).
|
|
3
|
+
# Full policy: docs/security.md. Variable inventory must stay in sync with that document.
|
|
4
|
+
|
|
5
|
+
# Control-plane auth (required when security.auth: true). The placeholder below is refused at
|
|
6
|
+
# startup, as is any key under 16 characters: replace it before turning auth on.
|
|
7
|
+
FASTERRAG_API_KEY=change-me
|
|
8
|
+
|
|
9
|
+
# Vector DB — Qdrant reference adapter (server enforces via QDRANT__SERVICE__API_KEY)
|
|
10
|
+
QDRANT_API_KEY=change-me
|
|
11
|
+
|
|
12
|
+
# Embedding / LLM providers — only those referenced by config.yaml are required at startup
|
|
13
|
+
OPENAI_API_KEY=
|
|
14
|
+
ANTHROPIC_API_KEY=
|
|
15
|
+
COHERE_API_KEY=
|
|
16
|
+
|
|
17
|
+
# pgvector adapter DSN (only when vector_db.provider: pgvector)
|
|
18
|
+
PGVECTOR_DSN=
|
|
19
|
+
|
|
20
|
+
# Langfuse self-hosted stack (observability.langfuse: true).
|
|
21
|
+
# Generated ONCE by the provisioner if absent — preserve forever after creation:
|
|
22
|
+
# changing SALT/ENCRYPTION_KEY/NEXTAUTH_SECRET invalidates existing Langfuse credentials.
|
|
23
|
+
NEXTAUTH_SECRET=
|
|
24
|
+
SALT=
|
|
25
|
+
ENCRYPTION_KEY=
|
|
26
|
+
LANGFUSE_INIT_ORG_ID=
|
|
27
|
+
LANGFUSE_INIT_ORG_NAME=
|
|
28
|
+
LANGFUSE_INIT_PROJECT_ID=
|
|
29
|
+
LANGFUSE_INIT_PROJECT_NAME=
|
|
30
|
+
LANGFUSE_INIT_PROJECT_PUBLIC_KEY=
|
|
31
|
+
LANGFUSE_INIT_PROJECT_SECRET_KEY=
|
|
32
|
+
LANGFUSE_INIT_USER_EMAIL=
|
|
33
|
+
LANGFUSE_INIT_USER_PASSWORD=
|
|
34
|
+
LANGFUSE_INIT_USER_NAME=
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
"""Fail when a locked dependency's licence is not one fasterRag has agreed to depend on.
|
|
2
|
+
|
|
3
|
+
fasterRag is Apache-2.0 (ADR-0008). Nothing checked what its dependencies were licensed
|
|
4
|
+
under: a transitive package could move to a licence the project cannot ship beside, the
|
|
5
|
+
lockfile would be refreshed, and every gate would stay green (TASK-0496).
|
|
6
|
+
|
|
7
|
+
The check reads the *lockfile*, as the dependency audit beside it does, and for the same
|
|
8
|
+
reason: an installed environment is whatever one platform resolved today, while ``uv.lock``
|
|
9
|
+
names every package any platform can be given. The Linux-only CUDA runtime that ``torch``
|
|
10
|
+
pulls in is the case that matters — it is absent from a Windows or macOS environment and is
|
|
11
|
+
the least permissively licensed thing in the set.
|
|
12
|
+
|
|
13
|
+
For each ``name==version`` in the export it asks PyPI what that release declares, in the
|
|
14
|
+
order a reader would trust it:
|
|
15
|
+
|
|
16
|
+
1. ``License-Expression`` (PEP 639) — an SPDX expression, evaluated as written: every side
|
|
17
|
+
of an ``AND`` must be allowed, one side of an ``OR`` is enough, and a ``WITH`` exception
|
|
18
|
+
only ever adds permissions, so it is judged by the licence it modifies.
|
|
19
|
+
2. the free-text ``License`` field, when it is an SPDX expression or a spelling in
|
|
20
|
+
``ALIASES``. A whole licence text pasted into that field is neither, and falls through.
|
|
21
|
+
3. the ``License ::`` Trove classifiers, all of which must be allowed. A classifier pair
|
|
22
|
+
does not say whether it means *and* or *or*, so it is read as *and*.
|
|
23
|
+
|
|
24
|
+
A package is then one of four things, and only the first two pass:
|
|
25
|
+
|
|
26
|
+
* **allowed** — everything it declares is in ``ALLOWED``;
|
|
27
|
+
* **reviewed** — named in ``REVIEWED`` with the licence it declared when it was reviewed
|
|
28
|
+
and the reason it is accepted. The exception holds only while the declaration is
|
|
29
|
+
unchanged, and an entry whose package has left the lockfile fails as stale;
|
|
30
|
+
* **refused** — it declares a licence that is not allowed;
|
|
31
|
+
* **undetermined** — it declares nothing this script can read. That is a failure, not a
|
|
32
|
+
pass: "the tool could not tell" is how an unlicensed package gets in.
|
|
33
|
+
|
|
34
|
+
``ALLOWED`` is deliberately short. It holds what the current dependency set uses, not every
|
|
35
|
+
permissive licence that exists, so a licence nobody here has looked at arrives as a red
|
|
36
|
+
build and a one-line decision rather than silently.
|
|
37
|
+
|
|
38
|
+
Usage::
|
|
39
|
+
|
|
40
|
+
uv export --format requirements-txt --locked --all-extras --no-extra dev \
|
|
41
|
+
--no-emit-project --no-hashes -o locked.txt
|
|
42
|
+
python .github/scripts/check_licences.py locked.txt [--summary report.md]
|
|
43
|
+
|
|
44
|
+
Exit codes: ``0`` every package is allowed or reviewed, ``1`` at least one is refused,
|
|
45
|
+
undetermined or stale, ``2`` bad arguments, ``3`` PyPI could not be asked.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from __future__ import annotations
|
|
49
|
+
|
|
50
|
+
import argparse
|
|
51
|
+
import json
|
|
52
|
+
import re
|
|
53
|
+
import sys
|
|
54
|
+
import time
|
|
55
|
+
import urllib.error
|
|
56
|
+
import urllib.request
|
|
57
|
+
from collections import Counter
|
|
58
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
59
|
+
from dataclasses import dataclass
|
|
60
|
+
from pathlib import Path
|
|
61
|
+
from typing import Any, Final
|
|
62
|
+
|
|
63
|
+
ALLOWED: Final[frozenset[str]] = frozenset(
|
|
64
|
+
{
|
|
65
|
+
"0BSD",
|
|
66
|
+
"Apache-2.0",
|
|
67
|
+
"BSD-2-Clause",
|
|
68
|
+
"BSD-3-Clause",
|
|
69
|
+
"BSL-1.0",
|
|
70
|
+
"CC0-1.0",
|
|
71
|
+
"CNRI-Python",
|
|
72
|
+
"ISC",
|
|
73
|
+
"MIT",
|
|
74
|
+
"MIT-0",
|
|
75
|
+
"MIT-CMU",
|
|
76
|
+
"MPL-2.0",
|
|
77
|
+
"PSF-2.0",
|
|
78
|
+
"Zlib",
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
"""SPDX identifiers a dependency may carry.
|
|
82
|
+
|
|
83
|
+
All permissive except MPL-2.0, which is copyleft per *file*: it binds whoever modifies or
|
|
84
|
+
redistributes those files, and fasterRag does neither — it names the package as a
|
|
85
|
+
dependency and pip fetches it.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
ALIASES: Final[dict[str, str]] = {
|
|
89
|
+
"3-clause bsd license": "BSD-3-Clause",
|
|
90
|
+
"apache 2.0": "Apache-2.0",
|
|
91
|
+
"apache 2.0 license": "Apache-2.0",
|
|
92
|
+
"apache license 2.0": "Apache-2.0",
|
|
93
|
+
"apache license version 2.0": "Apache-2.0",
|
|
94
|
+
"apache license, version 2.0": "Apache-2.0",
|
|
95
|
+
"bsd": "BSD-3-Clause",
|
|
96
|
+
"bsd 3-clause": "BSD-3-Clause",
|
|
97
|
+
"isc license": "ISC",
|
|
98
|
+
"mit license": "MIT",
|
|
99
|
+
}
|
|
100
|
+
"""Free-text spellings of an allowed licence, lower-cased. Each one was seen in the lockfile.
|
|
101
|
+
|
|
102
|
+
A bare ``BSD`` does not say which; every variant it can mean is allowed.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
CLASSIFIERS: Final[dict[str, str]] = {
|
|
106
|
+
"License :: OSI Approved :: Apache Software License": "Apache-2.0",
|
|
107
|
+
"License :: OSI Approved :: BSD License": "BSD-3-Clause",
|
|
108
|
+
"License :: OSI Approved :: ISC License (ISCL)": "ISC",
|
|
109
|
+
"License :: OSI Approved :: MIT License": "MIT",
|
|
110
|
+
"License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)": "MPL-2.0",
|
|
111
|
+
"License :: OSI Approved :: Python Software Foundation License": "PSF-2.0",
|
|
112
|
+
}
|
|
113
|
+
"""Trove classifiers and the SPDX identifier each is judged as.
|
|
114
|
+
|
|
115
|
+
``BSD License`` does not say which BSD; every variant it can mean is allowed, so it is
|
|
116
|
+
judged as the three-clause one.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
_NVIDIA: Final = (
|
|
120
|
+
"NVIDIA's proprietary CUDA runtime, pulled in by torch on Linux only and only by the "
|
|
121
|
+
"huggingface, rerank and all extras. fasterRag does not distribute it: pip downloads it "
|
|
122
|
+
"from PyPI under NVIDIA's own terms, and the published container image is built without "
|
|
123
|
+
"extras, so it holds none of it. Most of these packages declare no licence at all."
|
|
124
|
+
)
|
|
125
|
+
_PSYCOPG: Final = (
|
|
126
|
+
"LGPL-3.0, which is not on the allow-list. Accepted because it is reached only through "
|
|
127
|
+
"the optional pgvector extra, is imported and never copied or modified, and is not in "
|
|
128
|
+
"the published container image; the LGPL's terms fall on whoever redistributes psycopg."
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@dataclass(frozen=True)
|
|
133
|
+
class Review:
|
|
134
|
+
"""An accepted exception: what the package declared when reviewed, and why it passes."""
|
|
135
|
+
|
|
136
|
+
declared: frozenset[str]
|
|
137
|
+
reason: str
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
REVIEWED: Final[dict[str, Review]] = {
|
|
141
|
+
"psycopg": Review(frozenset({"LGPL-3.0-only"}), _PSYCOPG),
|
|
142
|
+
"psycopg-binary": Review(frozenset({"LGPL-3.0-only"}), _PSYCOPG),
|
|
143
|
+
"psycopg-pool": Review(frozenset({"LGPL-3.0-only"}), _PSYCOPG),
|
|
144
|
+
"pypdfium2": Review(
|
|
145
|
+
frozenset({"BSD-3-Clause, Apache-2.0, dependency licenses"}),
|
|
146
|
+
"Declared in prose rather than SPDX: the bindings are BSD-3-Clause or Apache-2.0, and "
|
|
147
|
+
"the PDFium binary in the wheel carries its own third-party notices, which this "
|
|
148
|
+
"script cannot read. A core dependency, through pdfplumber.",
|
|
149
|
+
),
|
|
150
|
+
}
|
|
151
|
+
"""Exceptions by exact package name. Delete an entry when its package leaves the lockfile."""
|
|
152
|
+
|
|
153
|
+
REVIEWED_FAMILIES: Final[dict[str, Review]] = {
|
|
154
|
+
"nvidia-": Review(
|
|
155
|
+
frozenset({"", "NVIDIA Proprietary Software", "LicenseRef-NVIDIA-Proprietary"}), _NVIDIA
|
|
156
|
+
),
|
|
157
|
+
"cuda-toolkit": Review(frozenset({""}), _NVIDIA),
|
|
158
|
+
}
|
|
159
|
+
"""Exceptions by name prefix, for a vendor family torch renames with every CUDA release."""
|
|
160
|
+
|
|
161
|
+
_REQUIREMENT: Final = re.compile(r"^(?P<name>[A-Za-z0-9][A-Za-z0-9._-]*)==(?P<version>[^\s;]+)")
|
|
162
|
+
_TOKEN: Final = re.compile(r"\s*(\(|\)|[A-Za-z0-9.+-]+)")
|
|
163
|
+
_OPERATORS: Final = frozenset({"AND", "OR", "WITH"})
|
|
164
|
+
_ATTEMPTS: Final = 4
|
|
165
|
+
_INDEX: Final = "https://pypi.org/pypi"
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class UnreadableError(Exception):
|
|
169
|
+
"""A string that is not an SPDX expression."""
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class UnreachableError(Exception):
|
|
173
|
+
"""PyPI could not be asked about a release, so nothing is known about it."""
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@dataclass(frozen=True)
|
|
177
|
+
class Verdict:
|
|
178
|
+
"""What one locked release declares and what the policy makes of it."""
|
|
179
|
+
|
|
180
|
+
name: str
|
|
181
|
+
version: str
|
|
182
|
+
declared: str
|
|
183
|
+
status: str
|
|
184
|
+
detail: str = ""
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def passed(self) -> bool:
|
|
188
|
+
"""Whether this release may stay in the lockfile."""
|
|
189
|
+
return self.status in {"allowed", "reviewed"}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def normalise(name: str) -> str:
|
|
193
|
+
"""Return a distribution name in its canonical form (PEP 503)."""
|
|
194
|
+
return re.sub(r"[-_.]+", "-", name).lower()
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def locked_releases(export: str) -> list[tuple[str, str]]:
|
|
198
|
+
"""Return every ``(name, version)`` a requirements export pins, once each."""
|
|
199
|
+
found = {
|
|
200
|
+
(normalise(match.group("name")), match.group("version"))
|
|
201
|
+
for match in map(_REQUIREMENT.match, export.splitlines())
|
|
202
|
+
if match is not None
|
|
203
|
+
}
|
|
204
|
+
return sorted(found)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _tokens(expression: str) -> list[str]:
|
|
208
|
+
tokens = []
|
|
209
|
+
position = 0
|
|
210
|
+
while position < len(expression.rstrip()):
|
|
211
|
+
found = _TOKEN.match(expression, position)
|
|
212
|
+
if found is None:
|
|
213
|
+
raise UnreadableError(expression)
|
|
214
|
+
tokens.append(found.group(1))
|
|
215
|
+
position = found.end()
|
|
216
|
+
return tokens
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def disallowed(expression: str) -> list[str]:
|
|
220
|
+
"""Return the identifiers that stop an SPDX expression being allowed; empty when it is."""
|
|
221
|
+
tokens = _tokens(expression)
|
|
222
|
+
position = 0
|
|
223
|
+
|
|
224
|
+
def peek() -> str | None:
|
|
225
|
+
return tokens[position] if position < len(tokens) else None
|
|
226
|
+
|
|
227
|
+
def take() -> str:
|
|
228
|
+
nonlocal position
|
|
229
|
+
if position >= len(tokens):
|
|
230
|
+
raise UnreadableError(expression)
|
|
231
|
+
position += 1
|
|
232
|
+
return tokens[position - 1]
|
|
233
|
+
|
|
234
|
+
def licence() -> list[str]:
|
|
235
|
+
token = take()
|
|
236
|
+
if token == "(":
|
|
237
|
+
inner = either()
|
|
238
|
+
if take() != ")":
|
|
239
|
+
raise UnreadableError(expression)
|
|
240
|
+
return inner
|
|
241
|
+
if token == ")" or token.upper() in _OPERATORS:
|
|
242
|
+
raise UnreadableError(expression)
|
|
243
|
+
if (peek() or "").upper() == "WITH":
|
|
244
|
+
take()
|
|
245
|
+
if take().upper() in _OPERATORS | {"(", ")"}:
|
|
246
|
+
raise UnreadableError(expression)
|
|
247
|
+
return [] if token in ALLOWED else [token]
|
|
248
|
+
|
|
249
|
+
def every() -> list[str]:
|
|
250
|
+
problems = licence()
|
|
251
|
+
while (peek() or "").upper() == "AND":
|
|
252
|
+
take()
|
|
253
|
+
problems = [*problems, *licence()]
|
|
254
|
+
return problems
|
|
255
|
+
|
|
256
|
+
def either() -> list[str]:
|
|
257
|
+
problems = every()
|
|
258
|
+
while (peek() or "").upper() == "OR":
|
|
259
|
+
take()
|
|
260
|
+
alternative = every()
|
|
261
|
+
problems = [] if not problems or not alternative else [*problems, *alternative]
|
|
262
|
+
return problems
|
|
263
|
+
|
|
264
|
+
result = either()
|
|
265
|
+
if position != len(tokens) or not tokens:
|
|
266
|
+
raise UnreadableError(expression)
|
|
267
|
+
return result
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def declaration(metadata: dict[str, Any]) -> tuple[str, str | None]:
|
|
271
|
+
"""Return what a release declares, verbatim, and the SPDX expression it is judged as."""
|
|
272
|
+
expression = (metadata.get("license_expression") or "").strip()
|
|
273
|
+
if expression:
|
|
274
|
+
return expression, expression
|
|
275
|
+
|
|
276
|
+
text = (metadata.get("license") or "").strip()
|
|
277
|
+
classifiers = [
|
|
278
|
+
entry for entry in metadata.get("classifiers") or [] if entry.startswith("License ::")
|
|
279
|
+
]
|
|
280
|
+
if text.lower() in ALIASES:
|
|
281
|
+
return text, ALIASES[text.lower()]
|
|
282
|
+
if text and "\n" not in text:
|
|
283
|
+
try:
|
|
284
|
+
disallowed(text)
|
|
285
|
+
except UnreadableError:
|
|
286
|
+
if not classifiers:
|
|
287
|
+
return text, None
|
|
288
|
+
else:
|
|
289
|
+
return text, text
|
|
290
|
+
|
|
291
|
+
if classifiers:
|
|
292
|
+
shown = "; ".join(classifiers)
|
|
293
|
+
if all(entry in CLASSIFIERS for entry in classifiers):
|
|
294
|
+
return shown, " AND ".join(CLASSIFIERS[entry] for entry in classifiers)
|
|
295
|
+
return shown, None
|
|
296
|
+
return (text.splitlines()[0][:60] + " ...", None) if text else ("", None)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def review_for(name: str) -> Review | None:
|
|
300
|
+
"""Return the exception that covers a package, by exact name first and family second."""
|
|
301
|
+
if name in REVIEWED:
|
|
302
|
+
return REVIEWED[name]
|
|
303
|
+
return next(
|
|
304
|
+
(review for prefix, review in REVIEWED_FAMILIES.items() if name.startswith(prefix)), None
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def judge(name: str, version: str, metadata: dict[str, Any]) -> Verdict:
|
|
309
|
+
"""Return the verdict on one release from the metadata PyPI holds for it."""
|
|
310
|
+
declared, expression = declaration(metadata)
|
|
311
|
+
review = review_for(name)
|
|
312
|
+
if review is not None:
|
|
313
|
+
if declared in review.declared:
|
|
314
|
+
return Verdict(name, version, declared, "reviewed", review.reason)
|
|
315
|
+
return Verdict(
|
|
316
|
+
name,
|
|
317
|
+
version,
|
|
318
|
+
declared,
|
|
319
|
+
"refused",
|
|
320
|
+
f"reviewed when it declared {sorted(review.declared)}; it now declares something "
|
|
321
|
+
"else, so the exception no longer applies and the review must be repeated",
|
|
322
|
+
)
|
|
323
|
+
if expression is None:
|
|
324
|
+
return Verdict(
|
|
325
|
+
name,
|
|
326
|
+
version,
|
|
327
|
+
declared,
|
|
328
|
+
"undetermined",
|
|
329
|
+
"declares no licence this check can read; read the project's own licence file, "
|
|
330
|
+
"then add an alias or a reviewed exception",
|
|
331
|
+
)
|
|
332
|
+
try:
|
|
333
|
+
problems = disallowed(expression)
|
|
334
|
+
except UnreadableError:
|
|
335
|
+
return Verdict(name, version, declared, "undetermined", "not an SPDX expression")
|
|
336
|
+
if problems:
|
|
337
|
+
return Verdict(
|
|
338
|
+
name, version, declared, "refused", f"not on the allow-list: {sorted(set(problems))}"
|
|
339
|
+
)
|
|
340
|
+
return Verdict(name, version, declared, "allowed")
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def stale_reviews(names: set[str]) -> list[str]:
|
|
344
|
+
"""Return the exceptions that no longer match any locked package."""
|
|
345
|
+
stale = [name for name in REVIEWED if name not in names]
|
|
346
|
+
stale.extend(
|
|
347
|
+
prefix for prefix in REVIEWED_FAMILIES if not any(name.startswith(prefix) for name in names)
|
|
348
|
+
)
|
|
349
|
+
return sorted(stale)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def fetch_metadata(name: str, version: str) -> dict[str, Any]:
|
|
353
|
+
"""Return the ``info`` mapping PyPI holds for one release, retrying a failed request."""
|
|
354
|
+
request = urllib.request.Request(
|
|
355
|
+
f"{_INDEX}/{name}/{version}/json", headers={"User-Agent": "licence-check"}
|
|
356
|
+
)
|
|
357
|
+
failure = ""
|
|
358
|
+
for attempt in range(_ATTEMPTS):
|
|
359
|
+
try:
|
|
360
|
+
with urllib.request.urlopen(request, timeout=30) as response:
|
|
361
|
+
info: Any = json.load(response)["info"]
|
|
362
|
+
except (urllib.error.URLError, TimeoutError, ValueError, KeyError) as error:
|
|
363
|
+
failure = f"{type(error).__name__}: {error}"
|
|
364
|
+
time.sleep(2**attempt)
|
|
365
|
+
else:
|
|
366
|
+
if isinstance(info, dict):
|
|
367
|
+
return info
|
|
368
|
+
failure = "the response carried no info mapping"
|
|
369
|
+
raise UnreachableError(f"{name}=={version}: {failure}")
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def report(verdicts: list[Verdict], stale: list[str]) -> str:
|
|
373
|
+
"""Return the Markdown report: counts, then every package that is not plainly allowed."""
|
|
374
|
+
counts = Counter(verdict.status for verdict in verdicts)
|
|
375
|
+
declared = Counter(verdict.declared or "(nothing declared)" for verdict in verdicts)
|
|
376
|
+
lines = [
|
|
377
|
+
"## Dependency licences",
|
|
378
|
+
"",
|
|
379
|
+
f"{len(verdicts)} locked releases: "
|
|
380
|
+
+ ", ".join(f"{count} {status}" for status, count in sorted(counts.items())),
|
|
381
|
+
"",
|
|
382
|
+
"| Declared | Releases |",
|
|
383
|
+
"|---|---|",
|
|
384
|
+
*(f"| `{text}` | {count} |" for text, count in declared.most_common()),
|
|
385
|
+
"",
|
|
386
|
+
"| Package | Declared | Status | Why |",
|
|
387
|
+
"|---|---|---|---|",
|
|
388
|
+
]
|
|
389
|
+
lines.extend(
|
|
390
|
+
f"| {verdict.name} {verdict.version} | `{verdict.declared or '(nothing declared)'}` "
|
|
391
|
+
f"| {verdict.status} | {verdict.detail} |"
|
|
392
|
+
for verdict in verdicts
|
|
393
|
+
if verdict.status != "allowed"
|
|
394
|
+
)
|
|
395
|
+
lines.extend(
|
|
396
|
+
f"| {name} | | stale | reviewed, but no longer in the lockfile |" for name in stale
|
|
397
|
+
)
|
|
398
|
+
return "\n".join(lines) + "\n"
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def main(argv: list[str] | None = None) -> int:
|
|
402
|
+
"""Judge every release in a requirements export; report, and exit per the docstring."""
|
|
403
|
+
parser = argparse.ArgumentParser(
|
|
404
|
+
description="Check locked dependencies against a licence list."
|
|
405
|
+
)
|
|
406
|
+
parser.add_argument("export", help="a `uv export --format requirements-txt` file")
|
|
407
|
+
parser.add_argument("--summary", help="append the Markdown report to this file")
|
|
408
|
+
arguments = parser.parse_args(argv)
|
|
409
|
+
|
|
410
|
+
releases = locked_releases(Path(arguments.export).read_text(encoding="utf-8"))
|
|
411
|
+
if not releases:
|
|
412
|
+
sys.stderr.write(f"check_licences: {arguments.export} pins nothing\n")
|
|
413
|
+
return 2
|
|
414
|
+
try:
|
|
415
|
+
with ThreadPoolExecutor(max_workers=8) as pool:
|
|
416
|
+
metadata = list(pool.map(lambda release: fetch_metadata(*release), releases))
|
|
417
|
+
except UnreachableError as error:
|
|
418
|
+
sys.stderr.write(f"check_licences: could not ask PyPI about {error}\n")
|
|
419
|
+
return 3
|
|
420
|
+
|
|
421
|
+
verdicts = [
|
|
422
|
+
judge(name, version, info) for (name, version), info in zip(releases, metadata, strict=True)
|
|
423
|
+
]
|
|
424
|
+
stale = stale_reviews({name for name, _ in releases})
|
|
425
|
+
document = report(verdicts, stale)
|
|
426
|
+
if arguments.summary:
|
|
427
|
+
with Path(arguments.summary).open("a", encoding="utf-8") as handle:
|
|
428
|
+
handle.write(document)
|
|
429
|
+
|
|
430
|
+
failures = [verdict for verdict in verdicts if not verdict.passed]
|
|
431
|
+
for verdict in verdicts:
|
|
432
|
+
if verdict.status == "reviewed":
|
|
433
|
+
sys.stdout.write(
|
|
434
|
+
f"reviewed: {verdict.name} {verdict.version} "
|
|
435
|
+
f"declares {verdict.declared or 'nothing'!r}\n"
|
|
436
|
+
)
|
|
437
|
+
for verdict in failures:
|
|
438
|
+
sys.stderr.write(
|
|
439
|
+
f"check_licences: {verdict.status}: {verdict.name} {verdict.version} "
|
|
440
|
+
f"declares {verdict.declared or 'nothing'!r} -- {verdict.detail}\n"
|
|
441
|
+
)
|
|
442
|
+
for name in stale:
|
|
443
|
+
sys.stderr.write(
|
|
444
|
+
f"check_licences: stale: the reviewed exception for {name!r} matches nothing in "
|
|
445
|
+
"the lockfile; delete it\n"
|
|
446
|
+
)
|
|
447
|
+
counts = Counter(verdict.status for verdict in verdicts)
|
|
448
|
+
sys.stdout.write(
|
|
449
|
+
f"licences: {len(verdicts)} locked releases -- "
|
|
450
|
+
+ ", ".join(f"{count} {status}" for status, count in sorted(counts.items()))
|
|
451
|
+
+ "\n"
|
|
452
|
+
)
|
|
453
|
+
return 1 if failures or stale else 0
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
if __name__ == "__main__":
|
|
457
|
+
sys.exit(main())
|