diff --git a/.env.example b/.env.example index dc4fd60..1cfa011 100644 --- a/.env.example +++ b/.env.example @@ -122,7 +122,7 @@ GRAFANA_ADMIN_PASSWORD= # PII detection engine: spacy-regex (default) | presidio | the name of an # engine another package registers in the admina.pii_engines entry-point group. -# presidio requires: pip install "admina-framework[presidio]" +# presidio requires: pip install "admina[presidio]" # ADMINA_PII_ENGINE=spacy-regex # PII masks: typed (default: [EMAIL], [PERSON], ...) | omissis ([OMISSIS] for diff --git a/.github/workflows/release-core.yml b/.github/workflows/release-core.yml index 809e576..7058669 100644 --- a/.github/workflows/release-core.yml +++ b/.github/workflows/release-core.yml @@ -29,7 +29,7 @@ # Workflow name: release-core.yml (this file) # Environment name: pypi-core # -# The matching trusted publisher for admina-framework is configured for +# The matching trusted publisher for admina is configured for # release.yml (the other workflow). # # ── How to cut a release ─────────────────────────────────────────────────── diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a0f28c9..64e9cfd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,10 +14,12 @@ # Release pipeline — fires on version tags (vX.Y.Z). # -# Publishes the pure-Python root package admina-framework to PyPI, then the -# alias package admina (packaging/admina: no code, it installs -# admina-framework at the same version), and creates the GitHub Release. The Rust accelerator admina-core is built -# and published by a sibling workflow (.github/workflows/release-core.yml) +# Publishes the pure-Python root package admina to PyPI and creates the +# GitHub Release. On the v0.14.0 tag it also publishes the last +# admina-framework release (packaging/admina-framework, built by +# scripts/build-admina-framework-sdist.py): a source distribution that stops +# its installation with the commands to switch to admina. The Rust +# accelerator admina-core is built and published by a sibling workflow (.github/workflows/release-core.yml) # triggered by the same tag — they run in parallel, independently. # # Why two workflows? PyPI Trusted Publishers had a transient issue @@ -29,15 +31,15 @@ # Before the first run, configure the publisher on pypi.org: # # https://pypi.org/manage/account/publishing/ → Add pending publisher -# PyPI Project Name: admina-framework +# PyPI Project Name: admina # Owner: admina-org # Repository name: admina # Workflow name: release.yml # Environment name: pypi # -# The publisher of the alias project "admina" has the same values, with -# PyPI Project Name: admina. The matching publisher for admina-core is -# configured for release-core.yml. +# The publisher of the project "admina-framework" has the same values, with +# PyPI Project Name: admina-framework. The matching publisher for +# admina-core is configured for release-core.yml. # See https://docs.pypi.org/trusted-publishers/ for details. # # ── How to cut a release ─────────────────────────────────────────────────── @@ -105,9 +107,10 @@ jobs: run: | uv build --out-dir dist/ - - name: Build the admina alias (sdist + wheel) + - name: Build the last admina-framework release (sdist) + if: github.ref_name == 'v0.14.0' run: | - uv build packaging/admina --out-dir dist-alias/ + python3 scripts/build-admina-framework-sdist.py dist-framework/ - name: Upload artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 @@ -117,17 +120,18 @@ jobs: if-no-files-found: error retention-days: 7 - - name: Upload alias artifact + - name: Upload admina-framework artifact + if: github.ref_name == 'v0.14.0' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: admina-alias-dist - path: dist-alias/* + name: admina-framework-dist + path: dist-framework/* if-no-files-found: error retention-days: 7 - # ── 4. Publish root admina-framework to PyPI ──────────────────────────── + # ── 4. Publish root admina to PyPI ────────────────────────────────────── publish-root: - name: Publish admina-framework to PyPI + name: Publish admina to PyPI needs: [build-root, tests] # admina-core (the optional Rust accelerator) is published by the # sibling workflow release-core.yml, in parallel with this one. @@ -136,7 +140,7 @@ jobs: runs-on: ubuntu-latest environment: name: pypi - url: https://pypi.org/project/admina-framework/ + url: https://pypi.org/project/admina/ steps: - name: Download root artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -153,20 +157,20 @@ jobs: packages-dir: dist/ skip-existing: true - # ── 5. Publish the admina alias to PyPI ───────────────────────────────── - publish-alias: - name: Publish admina (alias) to PyPI - # After admina-framework, so that the version the alias pins exists. + # ── 5. Publish the last admina-framework release (v0.14.0 only) ───────── + publish-framework-final: + name: Publish the last admina-framework release to PyPI + if: github.ref_name == 'v0.14.0' needs: [build-root, tests, publish-root] runs-on: ubuntu-latest environment: name: pypi - url: https://pypi.org/project/admina/ + url: https://pypi.org/project/admina-framework/ steps: - - name: Download alias artifacts + - name: Download admina-framework artifact uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: admina-alias-dist + name: admina-framework-dist path: dist - name: List artifacts @@ -188,7 +192,7 @@ jobs: with: fetch-depth: 0 # needed to extract the CHANGELOG section for this tag - - name: Download admina-framework artifacts + - name: Download admina artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: admina-root-dist diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b1f4ad..7fb8cd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,40 @@ stability commitment. See [ROADMAP.md](ROADMAP.md) for planned milestones. ## [Unreleased] +## [0.14.0] — 2026-10-07 + +Minor release with a declared breaking change: Admina is published on PyPI +as `admina`. The module (`import admina`), the `admina` command, the extras, +`admina-core` and the container images do not change. Upgrading an +installation of `admina-framework`: see `docs/guides/package-name.md`. + +### Changed + +- **Breaking:** the distribution is named `admina` (`pip install admina`, + `pip install "admina[proxy]"`). `admina-framework` 0.14.0 is its last + release: a source distribution that stops the installation with the + commands to switch (`pip uninstall -y admina admina-framework && pip + install admina`) and leaves the installed packages as they are. A pin + such as `admina-framework<0.14` keeps installing the 0.13 releases. +- The `admina` package holds the code; in 0.13.2 it was an alias that + installed `admina-framework`. +- The `[rust]` extra requires `admina-core>=0.14.0,<0.15`. +- The `pyproject.toml` that `admina plugin create` writes depends on + `admina>=`. A plugin created by an earlier release depends on + `admina-framework`: change that line (see the guide). +- Install hints in error messages, warnings, the README and the docs name + `admina[...]`. +- The Markdown compliance report of the dashboard ends with the forensic + section. + +### Documentation + +- `docs/guides/package-name.md`: the package names by release, the last + `admina-framework` release, the upgrade with pip, uv and dependency + lists, and the reinstall that restores the module when + `admina-framework` is uninstalled after `admina` was installed over it. +- ROADMAP: the 0.14.0 entry and the released milestones. + ## [0.13.2] — 2026-10-07 Patch release: the PyPI package `admina`, an alias of `admina-framework`, @@ -2232,7 +2266,8 @@ environment in `docker-compose.benchmark.yml`. --- -[Unreleased]: https://github.com/admina-org/admina/compare/v0.13.2...HEAD +[Unreleased]: https://github.com/admina-org/admina/compare/v0.14.0...HEAD +[0.14.0]: https://github.com/admina-org/admina/compare/v0.13.2...v0.14.0 [0.13.2]: https://github.com/admina-org/admina/compare/v0.13.1...v0.13.2 [0.13.1]: https://github.com/admina-org/admina/compare/v0.13.0...v0.13.1 [0.13.0]: https://github.com/admina-org/admina/compare/v0.12.2...v0.13.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dc6f2f4..0332f86 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -178,7 +178,7 @@ forwarded by the proxy. **Import conventions**: - Everything is nested under `admina.*` — there are no other top-level - packages on `sys.path` after `pip install admina-framework`. + packages on `sys.path` after `pip install admina`. - Inside the package, prefer absolute imports: `from admina.core.types import RiskLevel` - Relative imports (`from .types import ...`) are accepted inside the same sub-package. @@ -231,8 +231,8 @@ maintainers before it is merged. ### Areas Where Help Is Welcome -See [ROADMAP.md](ROADMAP.md) for the planned direction from 0.9.x to 1.0 -and beyond. In addition, the following areas accept contributions on an +See [ROADMAP.md](ROADMAP.md) for the released milestones and the next +planned release. In addition, the following areas accept contributions on an ongoing basis regardless of the current milestone: - New injection detection patterns (`admina/domains/agent_security/firewall.py`) diff --git a/MODEL_CARD.md b/MODEL_CARD.md index 35195f1..b0743a9 100644 --- a/MODEL_CARD.md +++ b/MODEL_CARD.md @@ -270,10 +270,10 @@ Detects and redacts PII in text. Three modes: opt-in via `ADMINA_ENGINE=rust`. Categories are individually toggleable from `admina.yaml` (`admina/domains/data_sovereignty/pii.py:41-108`). -- **Regex + spaCy NER** (`pip install admina-framework[nlp]`): adds named-entity +- **Regex + spaCy NER** (`pip install admina[nlp]`): adds named-entity detection for `PERSON`, `ORG`, `GPE`, `LOC`. Python only (`admina/domains/data_sovereignty/pii.py:60-77`). -- **Microsoft Presidio** (`pip install admina-framework[presidio]`, +- **Microsoft Presidio** (`pip install admina[presidio]`, selected with `ADMINA_PII_ENGINE=presidio` or `pii_engine: presidio` in `admina.yaml`): a third, opt-in detection engine. Presidio does **detection only** — Admina keeps its own masking, so the output @@ -285,7 +285,7 @@ Detects and redacts PII in text. Three modes: - **English-trained NER model.** The shipped `en_core_web_sm` is a small English model. It under-detects names and organizations in Italian, French, German, Spanish, etc. For multilingual deployments, - switch to the Presidio engine (`admina-framework[presidio]` + + switch to the Presidio engine (`admina[presidio]` + `ADMINA_PII_ENGINE=presidio`) and download the per-language spaCy models it needs. Note that on Admina's own corpus Presidio measures *lower* type-level recall than the default spaCy+regex engine on @@ -939,7 +939,7 @@ to `minimal`. - **No coverage of national implementing legislation** beyond EU. Member states may enact additional obligations (e.g. on biometric identification by law enforcement). Admina does not model these. -- **ISO/IEC 42001 and SOC 2** are not implemented in OSS. +- **ISO/IEC 42001 and SOC 2** are not implemented. --- diff --git a/README.md b/README.md index a586772..c4a7663 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

- PyPI version + PyPI version  License  Python 3.11+  Last commit @@ -20,7 +20,7 @@ CI  Release  Security scan -  PyPI downloads +  PyPI downloads

@@ -80,7 +80,7 @@ | Audit trail | logs you hope nobody deletes | **SHA-256 hash chain** — tamper-evident by design | | Adding governance to existing code | rewrite the call sites | **Zero code changes** via proxy, or 3 lines via SDK | | Performance overhead | unknown | **Measured** — engine microbenchmarks and a gateway benchmark to run on your hardware ([Performance](#performance--hybrid-python--rust-engine)) | -| License | varies | **Apache 2.0**, open core | +| License | varies | **Apache 2.0** | > Admina is **decision-support and defense-in-depth**, not legal advice. See [Compliance scope](#compliance-scope) for the full disclaimer and limitations. @@ -117,52 +117,58 @@ report = kit.gap_analysis(risk_category="high", current_compliance={...}) ### Install from PyPI +> **Package name since 0.14.0.** Admina is published as `admina`; up to +> 0.13.x it was `admina-framework`, which is no longer released. The module, +> the `admina` command and the extras did not change. To upgrade an +> installation of `admina-framework`, see +> [The `admina` package name](https://github.com/admina-org/admina/blob/main/docs/guides/package-name.md). + ```bash # Recommended for new users: SDK + proxy + dashboard. # Lets you run `admina dev` and see the dashboard out of the box. -pip install "admina-framework[proxy]" +pip install "admina[proxy]" # Everything (proxy + NLP + telemetry). Use this if you also want # spaCy-based NER for PII detection or OpenTelemetry export. -pip install "admina-framework[full]" +pip install "admina[full]" python -m spacy download en_core_web_sm # for [full] only # Model-adapter provider SDKs (per-provider extras): -pip install "admina-framework[openai]" # openai>=1.0 -pip install "admina-framework[ollama]" # ollama>=0.3 -pip install "admina-framework[anthropic]" # anthropic>=0.39 -pip install "admina-framework[mistral]" # mistralai>=1.0 -pip install "admina-framework[gemini]" # google-genai>=1.0 -pip install "admina-framework[bedrock]" # boto3>=1.34 (AWS Bedrock) +pip install "admina[openai]" # openai>=1.0 +pip install "admina[ollama]" # ollama>=0.3 +pip install "admina[anthropic]" # anthropic>=0.39 +pip install "admina[mistral]" # mistralai>=1.0 +pip install "admina[gemini]" # google-genai>=1.0 +pip install "admina[bedrock]" # boto3>=1.34 (AWS Bedrock) # All provider SDKs at once: -pip install "admina-framework[adapters]" +pip install "admina[adapters]" # Everything (proxy + NLP + telemetry + all adapters): -pip install "admina-framework[all]" +pip install "admina[all]" python -m spacy download en_core_web_sm # for [all] only # Optional: Rust-accelerated engine (auto-detected at runtime). # Opt-in extra — pulls in the admina-core wheel from PyPI. -pip install "admina-framework[rust]" +pip install "admina[rust]" # The proxy for the OpenAI-compatible gateway only: without Redis, # ClickHouse, boto3 and the scientific stack (see "Embedded deployment"). -pip install "admina-framework[proxy-minimal]" +pip install "admina[proxy-minimal]" # Advanced: SDK only (no proxy, no dashboard, no `admina dev`). # Use this when embedding the SDK into another service and you don't # need the local dev server. -pip install admina-framework +pip install admina ``` -> The PyPI distribution name is `admina-framework`; the Python import +> The PyPI distribution name is `admina`; the Python import > name is `admina` (e.g. `from admina import GovernedModel`). This is > a normal Python pattern — same as `python-dateutil` → `import dateutil`. > The Rust engine is an **optional, opt-in** accelerator. The default -> `pip install admina-framework` ships only the pure-Python implementation; -> `admina-framework[rust]` adds the `admina-core` wheel, which Admina +> `pip install admina` ships only the pure-Python implementation; +> `admina[rust]` adds the `admina-core` wheel, which Admina > auto-detects at runtime: under `ADMINA_ENGINE=auto` (the default) the > firewall and the loop breaker run on Rust whenever it is installed, as in > the official proxy image (see [Engine selection](#engine-selection)). @@ -174,11 +180,6 @@ pip install admina-framework > matters more than that extra coverage. See > [Performance](#performance--hybrid-python--rust-engine) for the trade-off. -The PyPI package `admina` is an alias: `pip install admina` installs -`admina-framework` at the same version, and `admina[X]` installs -`admina-framework[X]` for every extra above. It has no code of its own, so -both names install the same `admina` module. - ### Or install from source ```bash @@ -272,11 +273,11 @@ from admina import GovernedModel, GovernedData, GovernedAgent, ComplianceKit | Transport Adapter | MCP, HTTP REST | | Forensic Store | Filesystem, S3-compatible (boto3 — AWS S3, MinIO, R2, …) | | Auth Provider | API Key | -| PII Engine | spaCy + Regex (default), Microsoft Presidio (`pip install admina-framework[presidio]`, `ADMINA_PII_ENGINE=presidio`); engines of other packages through the `admina.pii_engines` entry-point group (see [PII engines](#pii-engines)) | +| PII Engine | spaCy + Regex (default), Microsoft Presidio (`pip install admina[presidio]`, `ADMINA_PII_ENGINE=presidio`); engines of other packages through the `admina.pii_engines` entry-point group (see [PII engines](#pii-engines)) | | Alert Channel | Log, Webhook | Model adapters lazy-import their provider SDK, so install the ones you -use: `pip install admina-framework[adapters]` for all of them, or a +use: `pip install admina[adapters]` for all of them, or a single provider (`[openai]`, `[ollama]`, `[anthropic]`, `[mistral]`, `[gemini]`, `[bedrock]`). vLLM has no extra of its own — it serves an OpenAI-compatible API and subclasses the OpenAI adapter, so install @@ -472,7 +473,7 @@ that only the Python firewall applies: `agent_security.firewall` `pattern_packs` (an empty list is not set). The error names the cause: ``` -ADMINA_ENGINE=rust, but admina-core is not installed: install admina-framework[rust], or set ADMINA_ENGINE=python (or auto) to run the Python engines +ADMINA_ENGINE=rust, but admina-core is not installed: install admina[rust], or set ADMINA_ENGINE=python (or auto) to run the Python engines ADMINA_ENGINE=rust, but admina.yaml sets agent_security.firewall.pattern_packs, which only the Python firewall applies: remove them, or set ADMINA_ENGINE=python (or auto) to run the Python firewall ``` @@ -976,7 +977,7 @@ attempt. The `proxy-minimal` extra installs the proxy without Redis, ClickHouse, boto3, typer and the scientific stack of the Python loop breaker: ```bash -pip install "admina-framework[proxy-minimal]" +pip install "admina[proxy-minimal]" ADMINA_ENABLED_SURFACES=gateway REDIS_URL= CLICKHOUSE_HOST= \ ADMINA_API_KEY_FILE=/run/secrets/admina_api_key \ uvicorn admina.proxy.main:app --host 0.0.0.0 --port 8080 @@ -1670,7 +1671,7 @@ cd integrations/cheshirecat/admina-plugin
Governs every LLM call and tool invocation in-process. Install -`admina-framework[proxy,nlp]`: the loop detection that the callbacks turn +`admina[proxy,nlp]`: the loop detection that the callbacks turn on by default needs scikit-learn from `[proxy]` (or pass `loop_detection=False`). @@ -1702,9 +1703,9 @@ See [full integration docs](https://github.com/admina-org/admina/blob/main/docs/ ## Performance — Hybrid Python + Rust engine The Rust core engine is an optional accelerator. The default -`pip install admina-framework` ships only the pure-Python implementation; +`pip install admina` ships only the pure-Python implementation; enable the Rust engine with the opt-in extra `pip install -"admina-framework[rust]"` (or build from source for local development — +"admina[rust]"` (or build from source for local development — `maturin develop --release --manifest-path core-rust/Cargo.toml`, see [CONTRIBUTING.md](https://github.com/admina-org/admina/blob/main/CONTRIBUTING.md)). Under `ADMINA_ENGINE=auto` Admina runs the Rust firewall and loop breaker when the extension is installed and @@ -1934,7 +1935,7 @@ guard error under `ADMINA_GUARD_FAIL_MODE=closed`) or `response_blocked` - [CONTRIBUTING.md](https://github.com/admina-org/admina/blob/main/CONTRIBUTING.md) — development setup, testing, and pull request workflow - [MODEL_CARD.md](https://github.com/admina-org/admina/blob/main/MODEL_CARD.md) — transparency artifact for every Admina governance component (intended use, scope, limitations, known failure modes), aligned with EU AI Act Art. 13 and NIST AI RMF -- [ROADMAP.md](https://github.com/admina-org/admina/blob/main/ROADMAP.md) — planned milestones from 0.9.x to 1.0 and beyond +- [ROADMAP.md](https://github.com/admina-org/admina/blob/main/ROADMAP.md) — released milestones and the next planned release - [CHANGELOG.md](https://github.com/admina-org/admina/blob/main/CHANGELOG.md) — release notes - [SECURITY.md](https://github.com/admina-org/admina/blob/main/SECURITY.md) — coordinated disclosure policy - [CODE_OF_CONDUCT.md](https://github.com/admina-org/admina/blob/main/CODE_OF_CONDUCT.md) — Contributor Covenant 2.1 diff --git a/ROADMAP.md b/ROADMAP.md index 917f037..956e733 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -6,7 +6,7 @@ not on a calendar. Scope may shift in response to user feedback, security findings, or upstream changes in the governance landscape (EU AI Act implementing acts, new frameworks, new attack classes). -The current release is **0.13.2**. Admina is pre-1.0: the public API may +The current release is **0.14.0**. Admina is pre-1.0: the public API may still evolve before the 1.0 stability commitment, so a minor release may carry a declared breaking change once its replacement is in place. Shipped detail lives in [CHANGELOG.md](CHANGELOG.md). @@ -130,85 +130,14 @@ deployment, and a forensic store that can be verified record by record. --- -## 0.14.0 — Compliance template expansion +## 0.14.0 — Package name -Beyond the EU AI Act. - -- NIS2 template (incident response, supply-chain obligations) -- ISO / IEC 42001 template (AI management system controls) -- SOC 2 template (Trust Services Criteria mapping) -- Cross-framework gap analysis: surface obligations that satisfy - multiple frameworks in a single control -- Report export in PDF and DOCX, signed with the forensic key - ---- - -## Not yet scheduled - -Planned, without a release assigned yet. - -**Observability** - -- Emission of the streaming-request metadata shaped in 0.11 onto the - OpenTelemetry GenAI semantic conventions (`gen_ai.request.model`, - `gen_ai.usage.*`, `gen_ai.response.finish_reasons`, - `gen_ai.client.operation.duration`) — no metadata field is renamed at - the emission boundary. -- An SLO panel on the Prometheus metrics. -- Structured error taxonomy: stable error codes across proxy, SDK, and - REST API. -- Benchmark regression gate in CI (fail on a >10% regression versus - baseline). -- Tracing correlation from the SDK through the proxy to the upstream LLM - (the gateway already records W3C trace context). - -**Multi-tenancy and RBAC** — operating Admina as a shared service. - -- Organisation / workspace isolation in the proxy -- Per-tenant quotas (request rate, forensic retention, compliance - templates enabled) -- Role-based access control on proxy endpoints (read / write / admin) -- OIDC authentication provider as a built-in plugin -- Shared async pool for data connectors; decision cache for the injection - firewall fast path. -- Per-tenant forensic namespace with independent hash chains - ---- - -## 1.0.0 — API freeze and long-term support - -The stability commitment. Shipped when the public surface has settled -through real-world use. - -- Plugin ABI v1 frozen with contract tests; third-party plugin - certification suite -- Official client SDKs: TypeScript, Go -- Deprecation policy formalised; 18-month LTS window for the 1.0 line -- Security advisory process documented; CVE assignment workflow -- Removal of the pre-0.3.0 compatibility shims in `proxy/` - ---- - -## 1.1.0 — Horizontal scale - -Running Admina as stateless, horizontally-scaled infrastructure. - -- Stateless proxy mode backed by shared Redis / ClickHouse -- Hot-reload of governance configuration without restart -- Official Helm chart and Kubernetes operator -- Cross-region federation for the forensic store - ---- - -## 2.0.0 — Only if required - -Reserved for breaking changes that cannot be delivered under 1.x. -No breaking changes are planned. Candidate drivers: - -- Plugin ABI v2 if real-world use reveals design limitations that cannot be - extended under v1 -- Streaming-first pipeline rearchitecture if chunk-level governance - becomes the dominant workload +- Admina is published on PyPI as `admina`; `admina-framework` is no longer + released after a final 0.14.0 that stops the installation with the + commands to switch. A declared breaking change for installs of + `admina-framework`: the module, the `admina` command and the extras do + not change. See + [docs/guides/package-name.md](docs/guides/package-name.md). --- diff --git a/SECURITY.md b/SECURITY.md index 9e83c56..2a92bd5 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,8 +4,8 @@ | Version | Supported | |---------|-----------| -| 0.13.x | Yes | -| < 0.13 | No | +| 0.14.x | Yes | +| < 0.14 | No | During the pre-1.0 phase only the latest minor line receives security fixes. Once 1.0 ships, an LTS window will be defined in diff --git a/admina/__init__.py b/admina/__init__.py index ab5a834..a9e5ab0 100644 --- a/admina/__init__.py +++ b/admina/__init__.py @@ -23,7 +23,7 @@ from admina.sdk import ComplianceKit, GovernedAgent, GovernedData, GovernedModel -__version__ = "0.13.2" +__version__ = "0.14.0" __all__ = [ "__version__", diff --git a/admina/cli/main.py b/admina/cli/main.py index ff2c1c8..4aef347 100644 --- a/admina/cli/main.py +++ b/admina/cli/main.py @@ -300,7 +300,7 @@ def _format_next_steps(project_name: str) -> str: [ "", " To run the local proxy + dashboard (admina dev), install the [proxy] extra:", - " pip install 'admina-framework[proxy]' --upgrade", + " pip install 'admina[proxy]' --upgrade", ] ) if docker_ok: @@ -555,11 +555,11 @@ def _require_proxy_extra_for_local_dev() -> None: click.echo(" admina dev (local mode) requires the [proxy] extra.", err=True) click.echo(" Install one of:", err=True) click.echo( - " pip install 'admina-framework[proxy]' --upgrade", + " pip install 'admina[proxy]' --upgrade", err=True, ) click.echo( - " pip install 'admina-framework[full]' --upgrade # adds NLP + telemetry", + " pip install 'admina[full]' --upgrade # adds NLP + telemetry", err=True, ) click.echo("", err=True) @@ -1226,11 +1226,11 @@ def doctor() -> None: if extras_status.get("proxy") != "ok": click.echo( f" {warn_mark} admina dev (local mode) needs the [proxy] extra — " - "pip install 'admina-framework[proxy]' --upgrade" + "pip install 'admina[proxy]' --upgrade" ) issues.append( "admina dev (local mode) requires the [proxy] extra — " - "run: pip install 'admina-framework[proxy]' --upgrade" + "run: pip install 'admina[proxy]' --upgrade" ) # ── spaCy NER model ────────────────────────────────────── @@ -1736,8 +1736,7 @@ def configure(output: Path, non_interactive: bool) -> None: to disable, loop-breaker thresholds, PII categories. Defaults are restrictive: enforce mode, all firewall categories on, - EU-aware PII set on. The wizard never recommends a "Pro" upgrade - or a paid feature — the OSS edition is fully usable as-is. + EU-aware PII set on. """ if output.exists(): diff --git a/admina/cli/templates/plugin_pyproject.toml.j2 b/admina/cli/templates/plugin_pyproject.toml.j2 index efa238e..fa239c0 100644 --- a/admina/cli/templates/plugin_pyproject.toml.j2 +++ b/admina/cli/templates/plugin_pyproject.toml.j2 @@ -8,10 +8,10 @@ version = "0.1.0" description = "Admina {{ plugin_type.replace('_', ' ') }} plugin: {{ plugin_name }}" requires-python = ">=3.11" dependencies = [ - # Floor pinned to the admina-framework version that generated this + # Floor pinned to the admina version that generated this # scaffold: the plugin contract (class-attr name, async ABC methods) # requires at least that runtime. - "admina-framework>={{ admina_version }}", + "admina>={{ admina_version }}", ] [project.entry-points."admina.plugins"] diff --git a/admina/domains/compliance/gdpr.py b/admina/domains/compliance/gdpr.py index dd7b286..2977d2a 100644 --- a/admina/domains/compliance/gdpr.py +++ b/admina/domains/compliance/gdpr.py @@ -23,7 +23,7 @@ this release. - :func:`render_dpia_template` — Art. 35 DPIA scaffold rendered - as Markdown from the operator's input. The OSS module produces + as Markdown from the operator's input. The module produces a *blank template populated with the operator's facts* — it is NOT a guided wizard, it does NOT score or recommend mitigations, and it is NOT legal advice. A real DPIA always involves the DPO diff --git a/admina/domains/compliance/nis2.py b/admina/domains/compliance/nis2.py index a75e93f..afc4323 100644 --- a/admina/domains/compliance/nis2.py +++ b/admina/domains/compliance/nis2.py @@ -18,7 +18,7 @@ (EU) 2022/2555) Art. 21 cybersecurity risk-management measures and Art. 23 incident reporting. -Scope of this OSS module: a *triage tool*. It enumerates the ten +Scope of this module: a *triage tool*. It enumerates the ten measure areas required by Art. 21 and lets the operator declare for each one which of a small number of standard controls is in place, producing a coverage score and a list of gaps. diff --git a/admina/domains/data_sovereignty/pii.py b/admina/domains/data_sovereignty/pii.py index 688ff2a..1629245 100644 --- a/admina/domains/data_sovereignty/pii.py +++ b/admina/domains/data_sovereignty/pii.py @@ -208,7 +208,7 @@ def __init__(self, config=None, *, mask_style: str = "typed"): if _spacy is None: logger.info( "spaCy not installed — PII redaction running in regex-only mode " - "(install admina-framework[nlp] for NER-based detection)" + "(install admina[nlp] for NER-based detection)" ) self.nlp = None else: diff --git a/admina/engines/__init__.py b/admina/engines/__init__.py index 989da75..7558aac 100644 --- a/admina/engines/__init__.py +++ b/admina/engines/__init__.py @@ -129,7 +129,7 @@ def _require_rust() -> None: if not _rust_available: raise EngineSelectionError( "ADMINA_ENGINE=rust, but admina-core is not installed: install " - "admina-framework[rust], or set ADMINA_ENGINE=python (or auto) to run " + "admina[rust], or set ADMINA_ENGINE=python (or auto) to run " "the Python engines" ) @@ -458,7 +458,7 @@ def __init__(self, **kwargs): except ImportError as exc: raise ImportError( "The Python loop breaker needs numpy and scikit-learn: " - "pip install 'admina-framework[proxy]' (or 'admina-framework[rust]' " + "pip install 'admina[proxy]' (or 'admina[rust]' " "for the Rust loop breaker)" ) from exc diff --git a/admina/engines/presidio.py b/admina/engines/presidio.py index df53f20..60aa591 100644 --- a/admina/engines/presidio.py +++ b/admina/engines/presidio.py @@ -36,7 +36,7 @@ E-mail domains are checked against the public suffix list bundled with tldextract, with no cache and no download, so the engine makes no network access. Presidio is an optional extra: - pip install 'admina-framework[presidio]' + pip install 'admina[presidio]' """ from __future__ import annotations @@ -240,7 +240,7 @@ def __init__( except ImportError as exc: raise ImportError( "The Presidio PII engine requires the [presidio] extra. Install it with " - "`pip install 'admina-framework[presidio]'`." + "`pip install 'admina[presidio]'`." ) from exc pipelines = _pipelines(nlp_models) diff --git a/admina/integrations/_engines.py b/admina/integrations/_engines.py index e3dd563..b84d059 100644 --- a/admina/integrations/_engines.py +++ b/admina/integrations/_engines.py @@ -74,8 +74,8 @@ def get_loop_breaker(): raise ImportError( "Loop detection (loop_detection=True, the default of the " "LangChain and CrewAI callbacks) needs numpy and scikit-learn: " - "pip install 'admina-framework[proxy]' (or " - "'admina-framework[rust]' for the Rust loop breaker), or pass " + "pip install 'admina[proxy]' (or " + "'admina[rust]' for the Rust loop breaker), or pass " "loop_detection=False" ) from exc return _loop_breaker diff --git a/admina/integrations/crewai/README.md b/admina/integrations/crewai/README.md index 3b1c0a1..8b9ca94 100644 --- a/admina/integrations/crewai/README.md +++ b/admina/integrations/crewai/README.md @@ -9,7 +9,7 @@ pip install -e ".[proxy,nlp]" # Admina: loop breaker (numpy, scikit-learn) + N pip install crewai # Your CrewAI deps ``` -From PyPI: `pip install "admina-framework[proxy,nlp]"`. Loop detection is on +From PyPI: `pip install "admina[proxy,nlp]"`. Loop detection is on by default and needs numpy and scikit-learn (`[proxy]`), or the Rust loop breaker (`[rust]`); without either, pass `loop_detection=False`. diff --git a/admina/integrations/langchain/README.md b/admina/integrations/langchain/README.md index dd620aa..8911741 100644 --- a/admina/integrations/langchain/README.md +++ b/admina/integrations/langchain/README.md @@ -9,7 +9,7 @@ pip install -e ".[proxy,nlp]" # Admina: loop breaker (numpy, scikit-learn) + N pip install langchain # Your LangChain deps ``` -From PyPI: `pip install "admina-framework[proxy,nlp]"`. Loop detection is on +From PyPI: `pip install "admina[proxy,nlp]"`. Loop detection is on by default and needs numpy and scikit-learn (`[proxy]`), or the Rust loop breaker (`[rust]`); without either, pass `loop_detection=False`. diff --git a/admina/plugins/builtin/adapters/anthropic.py b/admina/plugins/builtin/adapters/anthropic.py index 7e26a39..7c51c6d 100644 --- a/admina/plugins/builtin/adapters/anthropic.py +++ b/admina/plugins/builtin/adapters/anthropic.py @@ -17,7 +17,7 @@ Wraps the ``anthropic`` Python client (sync) to provide inference through the Anthropic Messages API. -Requires: ``pip install 'admina-framework[anthropic]'`` (optional dependency). +Requires: ``pip install 'admina[anthropic]'`` (optional dependency). """ from __future__ import annotations @@ -74,7 +74,7 @@ def _get_client(self) -> Any: except ImportError as exc: raise ImportError( "The 'anthropic' package is required for AnthropicAdapter. " - "Install it with: pip install 'admina-framework[anthropic]'" + "Install it with: pip install 'admina[anthropic]'" ) from exc kwargs: dict[str, Any] = {} if self._api_key: diff --git a/admina/plugins/builtin/adapters/bedrock.py b/admina/plugins/builtin/adapters/bedrock.py index c1606a0..97a300d 100644 --- a/admina/plugins/builtin/adapters/bedrock.py +++ b/admina/plugins/builtin/adapters/bedrock.py @@ -18,7 +18,7 @@ Converse API. Authentication follows the standard AWS credential chain (environment variables, ``~/.aws/credentials``, IAM instance role, etc.). -Requires: ``pip install 'admina-framework[bedrock]'`` (optional dependency). +Requires: ``pip install 'admina[bedrock]'`` (optional dependency). """ from __future__ import annotations @@ -80,7 +80,7 @@ def _get_client(self) -> Any: except ImportError as exc: raise ImportError( "The 'boto3' package is required for BedrockAdapter. " - "Install it with: pip install 'admina-framework[bedrock]'" + "Install it with: pip install 'admina[bedrock]'" ) from exc self._client = boto3.client("bedrock-runtime", region_name=self._region) return self._client diff --git a/admina/plugins/builtin/adapters/gemini.py b/admina/plugins/builtin/adapters/gemini.py index 3cd0df9..5e80a4d 100644 --- a/admina/plugins/builtin/adapters/gemini.py +++ b/admina/plugins/builtin/adapters/gemini.py @@ -17,7 +17,7 @@ Wraps the ``google-genai`` Python client (sync) to provide inference through the Google Gemini GenerateContent API. -Requires: ``pip install 'admina-framework[gemini]'`` (optional dependency). +Requires: ``pip install 'admina[gemini]'`` (optional dependency). """ from __future__ import annotations @@ -70,7 +70,7 @@ def _get_client(self) -> Any: except ImportError as exc: raise ImportError( "The 'google-genai' package is required for GeminiAdapter. " - "Install it with: pip install 'admina-framework[gemini]'" + "Install it with: pip install 'admina[gemini]'" ) from exc kwargs: dict[str, Any] = {} if self._api_key: diff --git a/admina/plugins/builtin/adapters/mistral.py b/admina/plugins/builtin/adapters/mistral.py index aa570e1..de982aa 100644 --- a/admina/plugins/builtin/adapters/mistral.py +++ b/admina/plugins/builtin/adapters/mistral.py @@ -17,7 +17,7 @@ Wraps the ``mistralai`` Python client (sync) to provide inference through the Mistral Chat Completions API. -Requires: ``pip install 'admina-framework[mistral]'`` (optional dependency). +Requires: ``pip install 'admina[mistral]'`` (optional dependency). """ from __future__ import annotations @@ -70,7 +70,7 @@ def _get_client(self) -> Any: except ImportError as exc: raise ImportError( "The 'mistralai' package is required for MistralAdapter. " - "Install it with: pip install 'admina-framework[mistral]'" + "Install it with: pip install 'admina[mistral]'" ) from exc kwargs: dict[str, Any] = {} if self._api_key: diff --git a/admina/proxy/main.py b/admina/proxy/main.py index c9787f5..d5182cf 100644 --- a/admina/proxy/main.py +++ b/admina/proxy/main.py @@ -485,7 +485,7 @@ def _build_loop_breaker() -> Any: raise RuntimeError( "The mcp and integration surfaces need the loop breaker, whose Python " f"engine needs numpy and scikit-learn ({exc}). Install " - "admina-framework[proxy] (or admina-framework[rust]), or serve only the " + "admina[proxy] (or admina[rust]), or serve only the " "gateway: ADMINA_ENABLED_SURFACES=gateway." ) from exc @@ -548,7 +548,7 @@ async def _connect_redis(url: str) -> Any: except ImportError: logger.warning( "REDIS_URL is set but the redis package is not installed " - "(pip install 'admina-framework[proxy]') — continuing without rate-limit cache" + "(pip install 'admina[proxy]') — continuing without rate-limit cache" ) return None try: @@ -573,7 +573,7 @@ def _connect_clickhouse() -> Any: except ImportError: logger.warning( "CLICKHOUSE_HOST is set but clickhouse-connect is not installed " - "(pip install 'admina-framework[proxy]') — analytics disabled" + "(pip install 'admina[proxy]') — analytics disabled" ) return None try: @@ -1659,12 +1659,6 @@ def _emit(section: str, obj: Any, prefix: str = "") -> None: lines.append(f"- Chain head: `{(fb.get('chain_head') or 'GENESIS')[:16]}...`") else: lines.append("- Forensic backend not configured") - lines += [ - "", - "---", - "*OSS-tier report. PDF / Excel / branded reporting are not " - "included in admina-framework.*", - ] return Response( content="\n".join(lines) + "\n", media_type="text/markdown; charset=utf-8", diff --git a/admina/redteam/__init__.py b/admina/redteam/__init__.py index f96ea98..267ca60 100644 --- a/admina/redteam/__init__.py +++ b/admina/redteam/__init__.py @@ -119,7 +119,7 @@ def _no_engine_reason(adapter: Any, engines: list[str], config: str | Path | Non """Why none of *engines* can run the corpora of *adapter*.""" if "rust" in engines: if not detectors.rust_available(): - return "admina-core is not installed (install admina-framework[rust])" + return "admina-core is not installed (install admina[rust])" keys = adapter.python_only_keys() if hasattr(adapter, "python_only_keys") else [] if keys: names = ", ".join(f"agent_security.firewall.{key}" for key in keys) diff --git a/core-rust/Cargo.lock b/core-rust/Cargo.lock index 8d68a72..3ebecfe 100644 --- a/core-rust/Cargo.lock +++ b/core-rust/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "admina_core" -version = "0.13.2" +version = "0.14.0" dependencies = [ "chrono", "hex", diff --git a/core-rust/Cargo.toml b/core-rust/Cargo.toml index e626d2f..17ec386 100644 --- a/core-rust/Cargo.toml +++ b/core-rust/Cargo.toml @@ -15,7 +15,7 @@ [package] name = "admina_core" -version = "0.13.2" +version = "0.14.0" edition = "2021" description = "Admina governance engines — Rust core with Python bindings" license = "Apache-2.0" diff --git a/core-rust/README.md b/core-rust/README.md index 4e82aa2..c1987ba 100644 --- a/core-rust/README.md +++ b/core-rust/README.md @@ -17,7 +17,7 @@ pip install admina-core The Python framework auto-detects the Rust engine at runtime and falls back to a pure-Python implementation if `admina-core` is not installed, -so `admina-framework` always works on its own. +so `admina` always works on its own. ## License diff --git a/core-rust/pyproject.toml b/core-rust/pyproject.toml index 43d2f1b..bd984ff 100644 --- a/core-rust/pyproject.toml +++ b/core-rust/pyproject.toml @@ -19,7 +19,7 @@ build-backend = "maturin" [project] name = "admina-core" -version = "0.13.2" +version = "0.14.0" description = "Admina governance engines — Rust core with Python bindings (firewall, PII, loop breaker, hash chain)" readme = "README.md" requires-python = ">=3.11" diff --git a/core-rust/uv.lock b/core-rust/uv.lock index 82123dc..9a206d9 100644 --- a/core-rust/uv.lock +++ b/core-rust/uv.lock @@ -4,5 +4,5 @@ requires-python = ">=3.11" [[package]] name = "admina-core" -version = "0.13.2" +version = "0.14.0" source = { editable = "." } diff --git a/docs/guides/airgapped-deployment.md b/docs/guides/airgapped-deployment.md index 0d39e06..c82dfcb 100644 --- a/docs/guides/airgapped-deployment.md +++ b/docs/guides/airgapped-deployment.md @@ -63,7 +63,7 @@ If you operate a private registry, `docker tag` + `docker push` to ### 2. Python wheels -`pip install admina-framework[full]` resolves ~150 wheels from PyPI. +`pip install admina[full]` resolves ~150 wheels from PyPI. Pre-download them once on a connected host, then point pip at the local directory. @@ -71,13 +71,13 @@ local directory. # On a host with Internet access: mkdir -p admina-wheels uv pip download \ - admina-framework[full] \ + admina[full] \ --dest admina-wheels \ --python-version 3.11 \ --platform-extra manylinux2014_x86_64 # Transfer admina-wheels/ to the air-gapped host, then: -uv pip install --no-index --find-links ./admina-wheels admina-framework[full] +uv pip install --no-index --find-links ./admina-wheels admina[full] ``` For full reproducibility, pin the lock file: `uv lock --frozen` diff --git a/docs/guides/package-name.md b/docs/guides/package-name.md new file mode 100644 index 0000000..5ad5557 --- /dev/null +++ b/docs/guides/package-name.md @@ -0,0 +1,85 @@ +# The `admina` package name + +From **0.14.0**, Admina is published on PyPI as **`admina`**. The package +`admina-framework`, which held the code up to 0.13.x, is no longer released +after that. This is a breaking change for anything that installs +`admina-framework`. + +What does not change: the Python module (`import admina`), the `admina` +command, the extras, the Rust accelerator `admina-core` and the container +images (`ghcr.io/admina-org/admina-proxy`, `ghcr.io/admina-org/admina-dashboard`). + +## Releases and package names + +| Releases | `admina-framework` | `admina` | +|---|---|---| +| up to 0.13.1 | the code | — | +| 0.13.2 – 0.13.x | the code | an alias: no code, it installs `admina-framework` at the same version | +| from 0.14.0 | 0.14.0 only: a final release that cannot be installed (see below) | the code | + +`admina-framework` 0.14.0 is a source distribution that stops the +installation with this message, and leaves the installed packages as they +are: + +```text +admina-framework is now published as admina (since 0.14.0). +Run: pip uninstall -y admina admina-framework && pip install admina +Extras keep their names: admina-framework[proxy] is admina[proxy]. +See https://github.com/admina-org/admina/blob/main/docs/guides/package-name.md +``` + +So `pip install -U admina-framework`, or a requirement `admina-framework` +without an upper bound, fails from 0.14.0 on. A pin such as +`admina-framework==0.13.2` or `admina-framework<0.14` keeps installing the +0.13 releases. + +## How to upgrade + +Both packages contain the `admina` module. Remove the installed ones first, +then install `admina`: + +```bash +pip uninstall -y admina admina-framework +pip install "admina[proxy]" # the extras you used: same names +``` + +With uv: + +```bash +uv pip uninstall admina admina-framework +uv pip install "admina[proxy]" +# in a uv project: uv remove admina-framework && uv add "admina[proxy]" +``` + +In `requirements.txt`, `pyproject.toml` or any other dependency list, +replace `admina-framework` with `admina` and keep the extras and the +version constraint: + +```diff +- admina-framework[proxy,rust]>=0.13,<0.14 ++ admina[proxy,rust]>=0.14,<0.15 +``` + +A plugin created with `admina plugin create` depends on `admina-framework` +in its `pyproject.toml`: change that line in the same way. + +Check the result: + +```bash +python -c "import admina; print(admina.__version__)" # 0.14.0 or later +pip list | grep -i admina # admina (and admina-core with [rust]) +``` + +## If `import admina` fails after the upgrade + +Installing `admina` 0.14 while `admina-framework` 0.13 is installed, and +then uninstalling `admina-framework`, removes the files of the `admina` +module: pip and uv delete every file that the uninstalled package had +installed, and both packages installed the same files. `pip list` still +shows `admina`, but `import admina` raises `ModuleNotFoundError`. Reinstall +it: + +```bash +pip install --force-reinstall "admina[proxy]" +# or: uv pip install --reinstall "admina[proxy]" +``` diff --git a/docs/index.md b/docs/index.md index 76be9f1..75bfcd2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -32,13 +32,14 @@ audited, EU AI Act compliance tracked. Works in-process (SDK) and over the netwo ## Install ```bash -pip install "admina-framework[proxy]" # Recommended: SDK + proxy + dashboard -pip install "admina-framework[full]" # + NLP (spaCy) + telemetry (OTEL) -pip install admina-framework # SDK only — embed it in another service +pip install "admina[proxy]" # Recommended: SDK + proxy + dashboard +pip install "admina[full]" # + NLP (spaCy) + telemetry (OTEL) +pip install admina # SDK only — embed it in another service ``` -> Distribution name `admina-framework`, import name `admina` -> (same pattern as `python-dateutil` → `import dateutil`). +> Distribution name and import name: `admina` (up to 0.13.x the +> distribution was `admina-framework`; see the +> [package name guide](guides/package-name.md)). ## 30-second example diff --git a/mkdocs.yml b/mkdocs.yml index 3cd95a9..fe096ff 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -51,6 +51,7 @@ nav: - Integrations: guides/integrations.md - Air-gapped deployment: guides/airgapped-deployment.md - Upgrading to 0.13: guides/upgrade-0.13.md + - The admina package name: guides/package-name.md - SDK Reference: - Overview: api/sdk.md - GovernedModel: api/governed-model.md diff --git a/packaging/admina-framework/README.md b/packaging/admina-framework/README.md new file mode 100644 index 0000000..6b5e68c --- /dev/null +++ b/packaging/admina-framework/README.md @@ -0,0 +1,16 @@ +# admina-framework + +Admina is published as [`admina`](https://pypi.org/project/admina/) since +0.14.0. This last release of `admina-framework` cannot be installed: it +stops with the commands to switch. + +```bash +pip uninstall -y admina admina-framework +pip install admina # extras keep their names: "admina[proxy]" +``` + +The module (`import admina`), the `admina` command and the extras did not +change. Releases up to 0.13.x of `admina-framework` remain installable with a +pin such as `admina-framework<0.14`. + +Upgrade guide: https://github.com/admina-org/admina/blob/main/docs/guides/package-name.md diff --git a/packaging/admina-framework/setup.py b/packaging/admina-framework/setup.py new file mode 100644 index 0000000..369ed0b --- /dev/null +++ b/packaging/admina-framework/setup.py @@ -0,0 +1,23 @@ +# Copyright © 2025–2026 Stefano Noferi & Admina contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + +# The last release of admina-framework: the code is published as admina +# since 0.14.0. Installing this source distribution stops with the commands +# to switch, before pip or uv change the installed packages. + +raise SystemExit( + "\n\nadmina-framework is now published as admina (since 0.14.0).\n" + "Run: pip uninstall -y admina admina-framework && pip install admina\n" + "Extras keep their names: admina-framework[proxy] is admina[proxy].\n" + "See https://github.com/admina-org/admina/blob/main/docs/guides/package-name.md\n" +) diff --git a/packaging/admina/README.md b/packaging/admina/README.md deleted file mode 100644 index 9b8fed1..0000000 --- a/packaging/admina/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# admina - -`pip install admina` installs [`admina-framework`](https://pypi.org/project/admina-framework/), -the Admina governed AI development framework, at the same version. This -package has no code of its own; `import admina` comes from `admina-framework`. - -Every extra of `admina-framework` is available under the same name: - -```bash -pip install admina # SDK -pip install "admina[proxy]" # SDK + proxy + dashboard -pip install "admina[full]" # proxy + NLP + telemetry -``` - -Documentation, changelog and source: https://github.com/admina-org/admina diff --git a/packaging/admina/pyproject.toml b/packaging/admina/pyproject.toml deleted file mode 100644 index 5c8544c..0000000 --- a/packaging/admina/pyproject.toml +++ /dev/null @@ -1,69 +0,0 @@ - -# Copyright © 2025–2026 Stefano Noferi & Admina contributors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# The PyPI project "admina": no code of its own, it installs admina-framework -# at the same version. Every extra of admina-framework has an extra of the same -# name here (admina[proxy] installs admina-framework[proxy]). The version and -# the pin are checked by scripts/check-versions.py, the extras by -# tests/test_admina_alias.py; release.yml publishes it after admina-framework. - -[build-system] -requires = ["setuptools>=68.0"] -build-backend = "setuptools.build_meta" - -[project] -name = "admina" -version = "0.13.2" -description = "Admina — governed AI development framework (installs admina-framework)" -readme = "README.md" -requires-python = ">=3.11" -license = "Apache-2.0" -authors = [ - {name = "Stefano Noferi", email = "info@admina.org"}, -] -maintainers = [ - {name = "Stefano Noferi", email = "info@admina.org"}, -] -classifiers = [ - "Development Status :: 4 - Beta", - "Programming Language :: Python :: 3", -] -dependencies = ["admina-framework==0.13.2"] - -[project.urls] -Homepage = "https://admina.org" -Repository = "https://github.com/admina-org/admina" -Documentation = "https://github.com/admina-org/admina#readme" -Changelog = "https://github.com/admina-org/admina/blob/main/CHANGELOG.md" - -[project.optional-dependencies] -proxy = ["admina-framework[proxy]==0.13.2"] -proxy-minimal = ["admina-framework[proxy-minimal]==0.13.2"] -nlp = ["admina-framework[nlp]==0.13.2"] -telemetry = ["admina-framework[telemetry]==0.13.2"] -rust = ["admina-framework[rust]==0.13.2"] -full = ["admina-framework[full]==0.13.2"] -openai = ["admina-framework[openai]==0.13.2"] -ollama = ["admina-framework[ollama]==0.13.2"] -anthropic = ["admina-framework[anthropic]==0.13.2"] -mistral = ["admina-framework[mistral]==0.13.2"] -gemini = ["admina-framework[gemini]==0.13.2"] -bedrock = ["admina-framework[bedrock]==0.13.2"] -adapters = ["admina-framework[adapters]==0.13.2"] -presidio = ["admina-framework[presidio]==0.13.2"] -all = ["admina-framework[all]==0.13.2"] - -[tool.setuptools] -packages = [] diff --git a/pyproject.toml b/pyproject.toml index 0c5ae60..d6d8864 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,8 +18,8 @@ requires = ["setuptools>=68.0"] build-backend = "setuptools.build_meta" [project] -name = "admina-framework" -version = "0.13.2" +name = "admina" +version = "0.14.0" description = "Admina — governed AI development framework" readme = "README.md" requires-python = ">=3.11" @@ -101,7 +101,7 @@ nlp = [ # NOTE: the `en_core_web_sm` model is NOT declared as a direct # dependency here because PyPI rejects wheels with URL-pinned # dependencies (PEP 440 "direct references"). After installing - # `admina-framework[nlp]`, run: + # `admina[nlp]`, run: # python -m spacy download en_core_web_sm # The PIIRedactor logs a clear error if the model is missing. ] @@ -126,14 +126,14 @@ rust = [ # stops). It is opt-in so the default install keeps the broader Python # detection coverage; enable [rust] when latency matters more. # - # admina-core and admina-framework are released in lockstep from the same + # admina-core and admina are released in lockstep from the same # tag (scripts/check-versions.py enforces manifest alignment). The # published metadata carries the version range below; uv.lock resolves # admina-core from ./core-rust instead ([tool.uv.sources]), so # `uv sync --extra rust` (or --all-extras) builds the engine of the same # checkout, which needs a Rust toolchain. The range takes the 0.13.x # releases of admina-core, the line released with this framework. - "admina-core>=0.13.0,<0.14", + "admina-core>=0.14.0,<0.15", ] # NOTE: the [guardrailsai] extra was removed in v0.9.0 because the # upstream `guardrails-ai` package is currently in PyPI quarantine @@ -141,7 +141,7 @@ rust = [ # admina/plugins/builtin/guards/guardrailsai_guard.py is preserved; if # you have a local copy of guardrails-ai installed, the plugin will # detect it and work. Re-introduce the extra once upstream is restored. -full = ["admina-framework[proxy,nlp,telemetry]"] +full = ["admina[proxy,nlp,telemetry]"] # Model-adapter provider SDKs — each adapter lazy-imports its SDK, so the # extra only controls whether the provider wheel is present. openai = ["openai>=1.0,<4"] @@ -150,7 +150,7 @@ anthropic = ["anthropic>=0.39,<2"] mistral = ["mistralai>=1.0,<4"] gemini = ["google-genai>=1.0,<3"] bedrock = ["boto3>=1.34,<2"] -adapters = ["admina-framework[openai,ollama,anthropic,mistral,gemini,bedrock]"] +adapters = ["admina[openai,ollama,anthropic,mistral,gemini,bedrock]"] # Microsoft Presidio as a selectable PII engine. Presidio does detection only; # Admina keeps its own masking, so the output format matches the default # spaCy+regex engine. ADMINA_PRESIDIO_NLP_MODELS sets the spaCy pipeline of each @@ -159,7 +159,7 @@ adapters = ["admina-framework[openai,ollama,anthropic,mistral,gemini,bedrock]"] # installed separately, like [nlp]: # python -m spacy download en_core_web_sm presidio = ["presidio-analyzer>=2.2,<3"] -all = ["admina-framework[proxy,nlp,telemetry,adapters,presidio]"] +all = ["admina[proxy,nlp,telemetry,adapters,presidio]"] [dependency-groups] dev = [ diff --git a/scripts/build-admina-framework-sdist.py b/scripts/build-admina-framework-sdist.py new file mode 100755 index 0000000..db0c717 --- /dev/null +++ b/scripts/build-admina-framework-sdist.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 + +# Copyright © 2025–2026 Stefano Noferi & Admina contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Build the last admina-framework release: a source distribution that +stops its installation with the commands to switch to ``admina``. + +Usage (from the repository root):: + + python scripts/build-admina-framework-sdist.py dist-framework/ + +The version is the one of ``pyproject.toml``. The archive holds +``PKG-INFO`` (with ``packaging/admina-framework/README.md`` as the +description), a ``pyproject.toml`` that names the setuptools backend, and +``packaging/admina-framework/setup.py``, which raises ``SystemExit`` with the +message: pip and uv run it before they change the installed packages. +""" + +from __future__ import annotations + +import io +import sys +import tarfile +import tomllib +from pathlib import Path + +REPO = Path(__file__).resolve().parent.parent + +_PYPROJECT = """[build-system] +requires = ["setuptools>=68.0"] +build-backend = "setuptools.build_meta" +""" + + +def pkg_info(version: str, description: str) -> str: + """The core metadata of the release.""" + return ( + "Metadata-Version: 2.4\n" + "Name: admina-framework\n" + f"Version: {version}\n" + "Summary: Admina is published as admina: this release cannot be installed\n" + "Project-URL: Homepage, https://pypi.org/project/admina/\n" + "Project-URL: Repository, https://github.com/admina-org/admina\n" + "License-Expression: Apache-2.0\n" + "Requires-Python: >=3.11\n" + "Description-Content-Type: text/markdown\n" + "\n" + f"{description}" + ) + + +def build(out_dir: Path, repo: Path = REPO) -> Path: + """Write ``admina_framework-.tar.gz`` into *out_dir*.""" + version = tomllib.loads((repo / "pyproject.toml").read_text(encoding="utf-8"))["project"][ + "version" + ] + sources = repo / "packaging" / "admina-framework" + top = f"admina_framework-{version}" + files = { + "PKG-INFO": pkg_info(version, (sources / "README.md").read_text(encoding="utf-8")), + "pyproject.toml": _PYPROJECT, + "setup.py": (sources / "setup.py").read_text(encoding="utf-8"), + "README.md": (sources / "README.md").read_text(encoding="utf-8"), + } + out_dir.mkdir(parents=True, exist_ok=True) + target = out_dir / f"{top}.tar.gz" + with tarfile.open(target, "w:gz", format=tarfile.PAX_FORMAT) as archive: + for name, text in files.items(): + data = text.encode("utf-8") + info = tarfile.TarInfo(f"{top}/{name}") + info.size = len(data) + info.mode = 0o644 + archive.addfile(info, io.BytesIO(data)) + return target + + +if __name__ == "__main__": + if len(sys.argv) != 2: + sys.exit("usage: build-admina-framework-sdist.py OUT_DIR") + print(build(Path(sys.argv[1]))) diff --git a/scripts/check-versions.py b/scripts/check-versions.py index ae96c14..4b4e48e 100755 --- a/scripts/check-versions.py +++ b/scripts/check-versions.py @@ -25,16 +25,14 @@ (see ``normalise()``). Any other difference is drift. Tracked points of truth: - 1. pyproject.toml → admina-framework (canonical) + 1. pyproject.toml → admina (canonical) 2. admina/__init__.py → runtime __version__ 3. core-rust/pyproject.toml → admina-core (PyPI) 4. core-rust/Cargo.toml → admina_core crate 5. core-rust/Cargo.lock → resolved admina_core entry - 6. uv.lock → resolved admina-framework entry + 6. uv.lock → resolved admina entry 7. uv.lock → resolved admina-core entry ([rust] extra) 8. core-rust/uv.lock → resolved admina-core entry - 9. packaging/admina/pyproject.toml → admina (PyPI alias) - 10. packaging/admina/pyproject.toml → its admina-framework pin Docker images and dashboard HTML derive their version dynamically from pyproject.toml at build time, so they need no separate check here. @@ -105,29 +103,16 @@ def _uv_lock_package(path: Path, pkg: str) -> str: raise RuntimeError(f"{pkg!r} entry not found in {path}") -def _alias_framework_pin(path: Path) -> str: - """The version of the ``admina-framework==X`` dependency of the alias.""" - data = tomllib.loads(path.read_text(encoding="utf-8")) - for dep in data["project"].get("dependencies", []): - m = re.fullmatch(r"admina-framework==(\S+)", dep.strip()) - if m: - return m.group(1) - raise RuntimeError(f"no admina-framework==X dependency in {path}") - - def main(repo: Path = REPO) -> int: - alias = repo / "packaging" / "admina" / "pyproject.toml" versions: dict[str, str] = { "pyproject.toml": _toml_version(repo / "pyproject.toml"), "admina/__init__.py": _python_dunder_version(repo / "admina" / "__init__.py"), - "uv.lock": _uv_lock_package(repo / "uv.lock", "admina-framework"), + "uv.lock": _uv_lock_package(repo / "uv.lock", "admina"), "uv.lock (admina-core)": _uv_lock_package(repo / "uv.lock", "admina-core"), "core-rust/pyproject.toml": _toml_version(repo / "core-rust" / "pyproject.toml"), "core-rust/Cargo.toml": _toml_version(repo / "core-rust" / "Cargo.toml", table="package"), "core-rust/Cargo.lock": _cargo_lock_admina_core(repo / "core-rust" / "Cargo.lock"), "core-rust/uv.lock": _uv_lock_package(repo / "core-rust" / "uv.lock", "admina-core"), - "packaging/admina/pyproject.toml": _toml_version(alias), - "packaging/admina (admina-framework pin)": _alias_framework_pin(alias), } canonical = versions["pyproject.toml"] diff --git a/tests/test_admina_alias.py b/tests/test_admina_alias.py deleted file mode 100644 index ab63bac..0000000 --- a/tests/test_admina_alias.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright © 2025–2026 Stefano Noferi & Admina contributors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""The ``admina`` alias package (``packaging/admina``) mirrors admina-framework. - -``pip install admina[X]`` installs ``admina-framework[X]`` at the same -version, for every extra X of admina-framework, and the alias ships no -module of its own (``import admina`` comes from admina-framework only). -""" - -from __future__ import annotations - -import tomllib -from pathlib import Path - -REPO = Path(__file__).resolve().parent.parent - - -def _project(path: Path) -> dict: - return tomllib.loads(path.read_text(encoding="utf-8")) - - -ROOT = _project(REPO / "pyproject.toml") -ALIAS = _project(REPO / "packaging" / "admina" / "pyproject.toml") - - -def test_the_alias_installs_admina_framework_at_its_version(): - version = ROOT["project"]["version"] - assert ALIAS["project"]["name"] == "admina" - assert ALIAS["project"]["version"] == version - assert ALIAS["project"]["dependencies"] == [f"admina-framework=={version}"] - - -def test_every_extra_of_admina_framework_has_an_alias_extra(): - version = ROOT["project"]["version"] - expected = { - extra: [f"admina-framework[{extra}]=={version}"] - for extra in ROOT["project"]["optional-dependencies"] - } - assert ALIAS["project"]["optional-dependencies"] == expected - - -def test_the_alias_ships_no_module(): - assert ALIAS["tool"]["setuptools"]["packages"] == [] - assert ALIAS["project"]["requires-python"] == ROOT["project"]["requires-python"] diff --git a/tests/test_admina_framework_sdist.py b/tests/test_admina_framework_sdist.py new file mode 100644 index 0000000..0e1fd01 --- /dev/null +++ b/tests/test_admina_framework_sdist.py @@ -0,0 +1,84 @@ +# Copyright © 2025–2026 Stefano Noferi & Admina contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The last admina-framework release (``scripts/build-admina-framework-sdist.py``). + +A source distribution named after the version of ``pyproject.toml`` whose +``setup.py`` stops the installation with the commands to switch to +``admina``. +""" + +from __future__ import annotations + +import importlib.util +import os +import subprocess +import sys +import tarfile +import tomllib +from email.parser import Parser +from pathlib import Path + +REPO = Path(__file__).resolve().parent.parent +VERSION = tomllib.loads((REPO / "pyproject.toml").read_text(encoding="utf-8"))["project"]["version"] + + +def _build(out_dir: Path) -> Path: + spec = importlib.util.spec_from_file_location( + "build_admina_framework_sdist", REPO / "scripts" / "build-admina-framework-sdist.py" + ) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module.build(out_dir) + + +def test_the_archive_is_named_and_laid_out_as_an_sdist(tmp_path): + archive = _build(tmp_path) + top = f"admina_framework-{VERSION}" + assert archive.name == f"{top}.tar.gz" + with tarfile.open(archive) as tar: + names = sorted(tar.getnames()) + assert names == sorted( + f"{top}/{name}" for name in ("PKG-INFO", "pyproject.toml", "setup.py", "README.md") + ) + + +def test_the_metadata_names_admina_framework_at_the_project_version(tmp_path): + archive = _build(tmp_path) + with tarfile.open(archive) as tar: + raw = tar.extractfile(f"admina_framework-{VERSION}/PKG-INFO").read().decode() + meta = Parser().parsestr(raw) + assert meta["Name"] == "admina-framework" + assert meta["Version"] == VERSION + assert meta["Description-Content-Type"] == "text/markdown" + assert "pip install admina" in meta.get_payload() + + +def test_setup_py_stops_with_the_commands_to_switch(tmp_path): + archive = _build(tmp_path) + with tarfile.open(archive) as tar: + tar.extractall(tmp_path, filter="data") + result = subprocess.run( + [sys.executable, "setup.py", "egg_info"], + cwd=tmp_path / f"admina_framework-{VERSION}", + capture_output=True, + text=True, + check=False, + # Only PATH: no coverage variables, so the subprocess writes no data + # file outside the configuration of the repository. + env={"PATH": os.environ.get("PATH", "")}, + ) + assert result.returncode != 0 + assert "admina-framework is now published as admina" in result.stderr + assert "pip uninstall -y admina admina-framework && pip install admina" in result.stderr diff --git a/tests/test_check_versions.py b/tests/test_check_versions.py index edf29ac..a72729a 100644 --- a/tests/test_check_versions.py +++ b/tests/test_check_versions.py @@ -44,38 +44,27 @@ def _write_repo( cargo: str | None = None, runtime: str | None = None, locked_core: str | None = None, - alias: str | None = None, - alias_pin: str | None = None, ) -> Path: """Write the tracked manifests of a repository at ``version``. ``cargo`` is the crate version (Cargo.toml and Cargo.lock), ``runtime`` the ``__version__`` and ``locked_core`` the admina-core entry of uv.lock; - each defaults to ``version``. ``alias`` and ``alias_pin`` are the - version of the ``admina`` alias and its admina-framework pin. + each defaults to ``version``. """ - alias = alias or version - alias_pin = alias_pin or version - (root / "packaging" / "admina").mkdir(parents=True) - (root / "packaging" / "admina" / "pyproject.toml").write_text( - f'[project]\nname = "admina"\nversion = "{alias}"\n' - f'dependencies = ["admina-framework=={alias_pin}"]\n', - encoding="utf-8", - ) cargo = cargo or version runtime = runtime or version locked_core = locked_core or version (root / "admina").mkdir() (root / "core-rust").mkdir() (root / "pyproject.toml").write_text( - f'[project]\nname = "admina-framework"\nversion = "{version}"\n', encoding="utf-8" + f'[project]\nname = "admina"\nversion = "{version}"\n', encoding="utf-8" ) (root / "admina" / "__init__.py").write_text(f'__version__ = "{runtime}"\n', encoding="utf-8") (root / "uv.lock").write_text( "version = 1\nrevision = 3\n\n" f'[[package]]\nname = "admina-core"\nversion = "{locked_core}"\n' 'source = { directory = "core-rust" }\n\n' - f'[[package]]\nname = "admina-framework"\nversion = "{version}"\n' + f'[[package]]\nname = "admina"\nversion = "{version}"\n' 'source = { editable = "." }\n', encoding="utf-8", ) @@ -139,8 +128,6 @@ def test_aligned_final_release_passes(tmp_path): ({"cargo": "0.13.0"}, "core-rust/Cargo.toml"), ({"runtime": "0.12.1"}, "admina/__init__.py"), ({"locked_core": "0.9.3"}, "uv.lock (admina-core)"), - ({"alias": "0.12.2"}, "packaging/admina/pyproject.toml"), - ({"alias_pin": "0.12.2"}, "packaging/admina (admina-framework pin)"), ], ) def test_real_drift_fails(tmp_path, capsys, overrides, drifting): diff --git a/tests/test_cli/test_init.py b/tests/test_cli/test_init.py index b06376b..3297638 100644 --- a/tests/test_cli/test_init.py +++ b/tests/test_cli/test_init.py @@ -375,7 +375,7 @@ def test_next_steps_when_proxy_installed_shows_admina_dev( out = cli_main._format_next_steps("foo") assert "admina dev" in out # No upgrade hint when [proxy] is already there. - assert "pip install 'admina-framework[proxy]'" not in out + assert "pip install 'admina[proxy]'" not in out def test_next_steps_when_proxy_missing_omits_admina_dev_command( self, monkeypatch: pytest.MonkeyPatch @@ -392,7 +392,7 @@ def test_next_steps_when_proxy_missing_omits_admina_dev_command( if stripped == "admina dev" or stripped.startswith("admina dev "): # Only --stack is acceptable here, and only if docker is around. assert stripped.startswith("admina dev --stack"), line - assert "pip install 'admina-framework[proxy]'" in out + assert "pip install 'admina[proxy]'" in out def test_next_steps_python_main_always_shown(self, monkeypatch: pytest.MonkeyPatch) -> None: from admina.cli import main as cli_main @@ -419,7 +419,7 @@ def test_require_proxy_extra_exits_with_actionable_message( captured = capsys.readouterr() combined = captured.out + captured.err assert "[proxy]" in combined - assert "pip install 'admina-framework[proxy]'" in combined + assert "pip install 'admina[proxy]'" in combined assert "Traceback" not in combined def test_require_proxy_extra_noop_when_installed(self, monkeypatch: pytest.MonkeyPatch) -> None: diff --git a/tests/test_domains.py b/tests/test_domains.py index ca56bfb..ea38fb3 100644 --- a/tests/test_domains.py +++ b/tests/test_domains.py @@ -290,7 +290,7 @@ def test_pii_factory(self): def test_pii_factory_without_spacy_installed(self, monkeypatch): """Regression: admina dev must boot even when spaCy is not installed. - Simulates a user who ran `pip install admina-framework[proxy]` without + Simulates a user who ran `pip install admina[proxy]` without the [nlp] extra. The PII bridge must fall back to regex-only mode instead of crashing the proxy lifespan with ModuleNotFoundError. """ diff --git a/tests/test_firewall_parity.py b/tests/test_firewall_parity.py index 4820fb7..d96d935 100644 --- a/tests/test_firewall_parity.py +++ b/tests/test_firewall_parity.py @@ -14,7 +14,7 @@ """Detection-parity tests between the pure-Python and Rust firewalls. -The Rust engine is an OPT-IN accelerator (`pip install admina-framework[rust]`). +The Rust engine is an OPT-IN accelerator (`pip install admina[rust]`). Because enabling it swaps out the detection engine, its behaviour must not silently regress below the pure-Python default. These tests pin the parity contract as of 0.9.4: diff --git a/tests/test_integrations/test_loop_breaker_dependency.py b/tests/test_integrations/test_loop_breaker_dependency.py index e1c29b9..8330ac4 100644 --- a/tests/test_integrations/test_loop_breaker_dependency.py +++ b/tests/test_integrations/test_loop_breaker_dependency.py @@ -46,7 +46,7 @@ def no_sklearn(monkeypatch: pytest.MonkeyPatch) -> None: def _assert_actionable(exc: pytest.ExceptionInfo[ImportError]) -> None: message = str(exc.value) - assert "admina-framework[proxy]" in message + assert "admina[proxy]" in message assert "loop_detection=False" in message assert isinstance(exc.value.__cause__, ImportError) diff --git a/tests/test_lazy_imports.py b/tests/test_lazy_imports.py index a2df867..449ca5d 100644 --- a/tests/test_lazy_imports.py +++ b/tests/test_lazy_imports.py @@ -230,7 +230,7 @@ def test_loop_breaker_surfaces_explain_the_missing_dependencies(tmp_path): proc = _run(code, tmp_path) # every surface assert proc.returncode != 0 assert "ADMINA_ENABLED_SURFACES=gateway" in proc.stderr - assert "admina-framework[proxy]" in proc.stderr + assert "admina[proxy]" in proc.stderr def test_configured_redis_without_the_package_is_a_warning(tmp_path): diff --git a/tests/test_plugins/test_scaffold_contracts.py b/tests/test_plugins/test_scaffold_contracts.py index c15e994..ea5376e 100644 --- a/tests/test_plugins/test_scaffold_contracts.py +++ b/tests/test_plugins/test_scaffold_contracts.py @@ -82,7 +82,7 @@ def test_scaffolded_pyproject_metadata(tmp_path): _scaffold_plugin("my-guard", "governance_guard", tmp_path / "my-guard") text = (tmp_path / "my-guard" / "pyproject.toml").read_text() - assert f"admina-framework>={__version__}" in text + assert f"admina>={__version__}" in text assert "admina>=0.9.0" not in text assert 'requires-python = ">=3.11"' in text assert '[project.entry-points."admina.plugins"]' in text diff --git a/tests/test_presidio_engine.py b/tests/test_presidio_engine.py index 0770f11..ba61347 100644 --- a/tests/test_presidio_engine.py +++ b/tests/test_presidio_engine.py @@ -32,7 +32,7 @@ def fake_import(name, *args, **kwargs): monkeypatch.setattr(builtins, "__import__", fake_import) from admina.engines.presidio import PresidioPIIEngine - with pytest.raises(ImportError, match=r"admina-framework\[presidio\]"): + with pytest.raises(ImportError, match=r"admina\[presidio\]"): PresidioPIIEngine() diff --git a/tests/test_redteam_external_corpora.py b/tests/test_redteam_external_corpora.py index f1f222d..8b88ea6 100644 --- a/tests/test_redteam_external_corpora.py +++ b/tests/test_redteam_external_corpora.py @@ -344,7 +344,7 @@ def test_rust_engine_alone_is_refused_without_admina_core(monkeypatch): redteam.run_suite(engines=["rust"], baseline=redteam.BASELINE_PATH) assert str(info.value) == ( "the selected engines (rust) cannot run the corpora injection, pii, loop: " - "admina-core is not installed (install admina-framework[rust])" + "admina-core is not installed (install admina[rust])" ) diff --git a/tests/test_sdk/test_top_level_imports.py b/tests/test_sdk/test_top_level_imports.py index 3d7280f..154a6c5 100644 --- a/tests/test_sdk/test_top_level_imports.py +++ b/tests/test_sdk/test_top_level_imports.py @@ -67,7 +67,7 @@ class TestSDKOnlyImport: """SDK-only install must not require any [proxy]/[nlp]/[telemetry] extras. A user following the README's "SDK only (lightweight)" path runs - `pip install admina-framework` (no extras) and then + `pip install admina` (no extras) and then `from admina import GovernedModel`. That import chain must not transitively pull in boto3, spacy, sklearn, numpy, or any other optional dependency, otherwise the lightweight install is broken. diff --git a/tests/test_sdk/test_without_numpy.py b/tests/test_sdk/test_without_numpy.py index 93c166c..72867d5 100644 --- a/tests/test_sdk/test_without_numpy.py +++ b/tests/test_sdk/test_without_numpy.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""The SDK installed without the scientific stack (``pip install admina-framework``). +"""The SDK installed without the scientific stack (``pip install admina``). numpy and scikit-learn come with the ``proxy`` extra, for the Python loop breaker. Without them, ``GovernedModel.ask()`` governs a prompt (firewall, @@ -89,4 +89,4 @@ def test_loop_detection_without_numpy_names_the_extra(): ) assert proc.returncode == 0, proc.stderr assert "ImportError:" in proc.stdout - assert "admina-framework[proxy]" in proc.stdout + assert "admina[proxy]" in proc.stdout diff --git a/uv.lock b/uv.lock index 6a7ce8c..ce5d5a2 100644 --- a/uv.lock +++ b/uv.lock @@ -9,13 +9,8 @@ resolution-markers = [ ] [[package]] -name = "admina-core" -version = "0.13.2" -source = { directory = "core-rust" } - -[[package]] -name = "admina-framework" -version = "0.13.2" +name = "admina" +version = "0.14.0" source = { editable = "." } dependencies = [ { name = "click" }, @@ -148,10 +143,10 @@ dev = [ [package.metadata] requires-dist = [ + { name = "admina", extras = ["openai", "ollama", "anthropic", "mistral", "gemini", "bedrock"], marker = "extra == 'adapters'" }, + { name = "admina", extras = ["proxy", "nlp", "telemetry"], marker = "extra == 'full'" }, + { name = "admina", extras = ["proxy", "nlp", "telemetry", "adapters", "presidio"], marker = "extra == 'all'" }, { name = "admina-core", marker = "extra == 'rust'", directory = "core-rust" }, - { name = "admina-framework", extras = ["openai", "ollama", "anthropic", "mistral", "gemini", "bedrock"], marker = "extra == 'adapters'" }, - { name = "admina-framework", extras = ["proxy", "nlp", "telemetry"], marker = "extra == 'full'" }, - { name = "admina-framework", extras = ["proxy", "nlp", "telemetry", "adapters", "presidio"], marker = "extra == 'all'" }, { name = "anthropic", marker = "extra == 'anthropic'", specifier = ">=0.39,<2" }, { name = "boto3", marker = "extra == 'bedrock'", specifier = ">=1.34,<2" }, { name = "boto3", marker = "extra == 'proxy'", specifier = ">=1.34,<2" }, @@ -202,6 +197,11 @@ dev = [ { name = "safety", specifier = ">=3.0,<4" }, ] +[[package]] +name = "admina-core" +version = "0.14.0" +source = { directory = "core-rust" } + [[package]] name = "annotated-doc" version = "0.0.4"