From 8707bb8c217a610b006e8193c7f31dc6659fea10 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Mon, 12 Oct 2026 14:50:05 +0200 Subject: [PATCH 1/5] Python: clarify meta-package promotion policy --- .../scripts/check_python_api_compatibility.py | 3 +- .../tests/test_python_api_compatibility.py | 10 ++ .../skills/python-package-management/SKILL.md | 74 +++++++++---- python/PACKAGE_STATUS.md | 100 ++++++++++-------- python/pyproject.toml | 2 +- 5 files changed, 123 insertions(+), 66 deletions(-) diff --git a/.github/scripts/check_python_api_compatibility.py b/.github/scripts/check_python_api_compatibility.py index c6969e96cc5..2174a4d40de 100644 --- a/.github/scripts/check_python_api_compatibility.py +++ b/.github/scripts/check_python_api_compatibility.py @@ -33,7 +33,8 @@ ) _GRIFFE_VERSION = version("griffe") _PACKAGE_STATUS_ROW = re.compile( - r"^\| `(?P[^`]+)` \| `(?Ppython/packages/[^`]+)` \| `(?P[^`]+)` \|$" + r"^\| `(?P[^`]+)` \| `(?Ppython/packages/[^`]+)` \| `(?P[^`]+)` \|" + r"(?: `[^`]+` \|)?$" ) diff --git a/.github/tests/test_python_api_compatibility.py b/.github/tests/test_python_api_compatibility.py index 4e8a5f519ea..516ee7b436a 100644 --- a/.github/tests/test_python_api_compatibility.py +++ b/.github/tests/test_python_api_compatibility.py @@ -116,6 +116,16 @@ def test_discovers_only_released_non_lab_modules(self) -> None: self.assertEqual([spec.module for spec in specs], ["stable_api"]) + def test_package_status_row_allows_optional_meta_package_column(self) -> None: + rows = [ + "| `agent-framework-core` | `python/packages/core` | `released` |", + "| `agent-framework-core` | `python/packages/core` | `released` | `yes` |", + ] + + for row in rows: + with self.subTest(row=row): + self.assertIsNotNone(api_checker._PACKAGE_STATUS_ROW.match(row)) + def test_filters_base_unreleased_apis_but_not_head_only_decorators(self) -> None: self.write_status([("agent-framework-core", "core", "released")]) self.write_module( diff --git a/python/.github/skills/python-package-management/SKILL.md b/python/.github/skills/python-package-management/SKILL.md index dab9399e1a9..2be3896701f 100644 --- a/python/.github/skills/python-package-management/SKILL.md +++ b/python/.github/skills/python-package-management/SKILL.md @@ -23,15 +23,15 @@ python/ - `agent-framework-core` contains core abstractions and OpenAI/Azure OpenAI built-in - Provider packages extend core with specific integrations -- Root `agent-framework` depends on `agent-framework-core[all]` +- Root `agent-framework` depends on `agent-framework-core[all]`. Despite the existing extra name, this is a + curated standard install set, not every package at a particular lifecycle stage. - Entries in `agent-framework-core[all]` carry lower and upper bounds. Workspace integrations use the version released with the current Core cohort as their lower bound so pip does not backtrack across old cyclic `core[all] -> integration -> core` combinations. - `packages/lab` is intentionally excluded from the root uv workspace. It has its own `pyproject.toml`, `uv.lock`, environment, CI matrix, and dependency updates so experimental - dependencies cannot constrain released provider packages. Lab is also an explicit exception - to the lifecycle rule that beta packages belong in `agent-framework-core[all]`. Lab resolves - released Core and provider distributions rather than local root-workspace packages. + dependencies cannot constrain released provider packages. Lab resolves released Core and provider + distributions rather than local root-workspace packages. ## Dependency Management @@ -114,6 +114,28 @@ uv run poe test - Run Lab dependency and validation commands from `packages/lab`; root workspace tasks deliberately do not discover or update the Lab project. +## Meta-package Membership + +Package lifecycle and membership in the root `agent-framework` meta-package are independent decisions. A package +starts outside `agent-framework-core[all]`, and promotion to `beta`, `rc`, or `released` does not add it +automatically. + +Adding a package to the standard install set requires an explicit core-team decision. Consider: + +- whether the package is useful across common Agent Framework workloads rather than for one specialized storage + backend, hosting adapter, channel, or deployment target; +- whether its dependency footprint is proportionate and compatible with the meta-package's supported Python and + platform matrix, especially for native, heavyweight, or conflict-prone dependencies; +- whether it belongs in the documented standard getting-started experience; and +- whether including one package would create an expectation that an open-ended family of alternative backends or + adapters must also be included. + +Default to a separate install when the case is unclear. Existing membership is not precedent for every package in +the same lifecycle stage or category. + +Track the decision in the `Meta package` column of `python/PACKAGE_STATUS.md`. When membership changes, update +`packages/core/pyproject.toml` `[project.optional-dependencies].all` and the status table in the same change. + ## Lazy Loading Pattern ### Root core API @@ -166,9 +188,9 @@ Every new package starts as `alpha`. 5. Include samples inside the package (e.g., `packages/my-connector/samples/`) 6. Do **NOT** add to `[all]` extra in `packages/core/pyproject.toml` 7. Do **NOT** create lazy loading in core yet -8. Add the package to `python/PACKAGE_STATUS.md` and keep that file updated when packages are added, - removed, renamed, or promoted. If the package exposes individually staged APIs, keep the feature list - there current too. +8. Add the package to `python/PACKAGE_STATUS.md` with lifecycle state `alpha` and meta-package membership `no`, + and keep that file updated when packages are added, removed, renamed, promoted, or have their membership + changed. If the package exposes individually staged APIs, keep the feature list there current too. 9. Keep the `Typing :: Typed` classifier and the `py.typed` marker in step. The classifier alone is a promise PEP 561 keeps through the `py.typed` file inside the package directory, and that file is the signal checkers act on: a package that declares `Typing :: Typed` without shipping it is not @@ -204,6 +226,17 @@ Recommended dependency workflow during connector implementation: ### Promotion path +Create a GitHub issue for every package promotion and get core-team agreement before implementing the promotion. +The issue must record: + +- the current and target lifecycle stages; +- whether the package should be included in the root meta-package, with rationale based on the criteria above; and +- whether the package should be exposed through a core lazy-loading namespace and, if so, the exact namespace to + use. + +Record an explicit `no` or `none` when the package should remain outside the meta-package or should not have a +lazy-loading namespace. Do not infer either decision from the target lifecycle stage. + Promotion work is not isolated to the package being promoted. If a promotion changes dependency metadata for downstream packages, also update the dependent packages' own versions so they publish new metadata alongside the promoted dependency bounds. @@ -212,14 +245,16 @@ promotions as well as standalone version update work. #### Alpha -> Beta -Move a package to `beta` when it is stable enough to be part of the main install surface. +Move a package to `beta` when it is stable enough for broader prerelease use. 1. Update the package version to the beta pattern: `1.0.0b` 2. Update the classifier to `Development Status :: 4 - Beta` -3. Add the package to `[all]` in `packages/core/pyproject.toml` -4. Move samples to the root `samples/` tree and remove package-local samples -5. Create or update the relevant lazy-loading namespace in core when the package belongs under one -6. Update `python/PACKAGE_STATUS.md` +3. Move samples to the root `samples/` tree and remove package-local samples +4. Apply the lazy-loading decision recorded in the promotion issue, creating or updating the approved namespace + when applicable +5. Apply the meta-package decision recorded in the promotion issue. The default is to remain excluded; add the + package to `[all]` only when inclusion is explicitly approved. +6. Update the lifecycle state and meta-package membership in `python/PACKAGE_STATUS.md` After `alpha`, there should be no samples left inside a package folder. @@ -230,9 +265,10 @@ Move a package to `rc` when its API is close to the final released shape. 1. Update the package version to the release-candidate pattern: `1.0.0rc` 2. Keep the classifier at `Development Status :: 4 - Beta` because PyPI does not have a separate release-candidate classifier -3. Keep the package in `core[all]` +3. Preserve the package's current meta-package membership unless it is reconsidered separately; the stage + transition alone does not add it to `core[all]` 4. Keep samples only in the root `samples/` tree -5. Update `python/PACKAGE_STATUS.md` to show the package as `rc` +5. Update `python/PACKAGE_STATUS.md` to show the package as `rc` and keep its membership value current #### RC -> Released @@ -240,9 +276,10 @@ Move a package to `released` when it no longer carries a prerelease qualifier. 1. Update the package version to the stable pattern: `1.0.0` 2. Update the classifier to `Development Status :: 5 - Production/Stable` -3. Keep the package in `core[all]` +3. Preserve the package's current meta-package membership unless it is reconsidered separately; release does not + imply inclusion in `core[all]` 4. Keep samples only in the root `samples/` tree -5. Update `python/PACKAGE_STATUS.md` to show the package as `released` +5. Update `python/PACKAGE_STATUS.md` to show the package as `released` and keep its membership value current 6. Update all `README.md` files that install that package with `pip install agent-framework-... --pre` so they use `pip install agent-framework-...` without the `--pre` suffix @@ -291,8 +328,8 @@ Move a package to `released` when it no longer carries a prerelease qualifier. ```bash pip install agent-framework-core # Core only -pip install agent-framework-core[all] # Core + all connectors -pip install agent-framework # Same as core[all] +pip install agent-framework-core[all] # Core + curated standard integrations +pip install agent-framework # Same standard install set as core[all] pip install agent-framework-foundry # Specific connector (pulls in core) ``` @@ -305,6 +342,7 @@ When changing a package, check if its `AGENTS.md` needs updates: Keep `python/PACKAGE_STATUS.md` updated when: - A package is added, removed, renamed, or promoted between lifecycle stages +- A package is added to or removed from the root meta-package - A package starts or stops exposing individually staged experimental or release-candidate APIs When a package adds, removes, or renames environment variables, update the related documentation in the same diff --git a/python/PACKAGE_STATUS.md b/python/PACKAGE_STATUS.md index 3820ead3ee4..dd68cb9bff2 100644 --- a/python/PACKAGE_STATUS.md +++ b/python/PACKAGE_STATUS.md @@ -10,54 +10,62 @@ Status is grouped into these buckets: - `released` - stable packages without a prerelease suffix, these are stable packages that should not have breaking changes between versions - `deprecated` - removed or deprecated packages that should not be used for new work +Lifecycle state and membership in the root `agent-framework` meta-package are independent. The `Meta package` +column records whether `pip install agent-framework` installs the workspace package: + +- `yes` - included directly or through `agent-framework-core[all]`; environment markers may still limit supported + Python versions or platforms +- `no` - installed separately when needed +- `n/a` - the root meta-package itself + ## Current packages -| Package | Path | State | -| --- | --- | --- | -| `agent-framework` | `python/` | `released` | -| `agent-framework-a2a` | `python/packages/a2a` | `beta` | -| `agent-framework-ag-ui` | `python/packages/ag-ui` | `released` | -| `agent-framework-anthropic` | `python/packages/anthropic` | `beta` | -| `agent-framework-azure-contentunderstanding` | `python/packages/azure-contentunderstanding` | `beta` | -| `agent-framework-azure-ai-search` | `python/packages/azure-ai-search` | `beta` | -| `agent-framework-azure-documentdb` | `python/packages/azure-documentdb` | `alpha` | -| `agent-framework-azure-cosmos` | `python/packages/azure-cosmos` | `beta` | -| `agent-framework-azure-cosmos-memory` | `python/packages/azure-cosmos-memory` | `alpha` | -| `agent-framework-bedrock` | `python/packages/bedrock` | `beta` | -| `agent-framework-chatkit` | `python/packages/chatkit` | `beta` | -| `agent-framework-claude` | `python/packages/claude` | `beta` | -| `agent-framework-copilotstudio` | `python/packages/copilotstudio` | `beta` | -| `agent-framework-core` | `python/packages/core` | `released` | -| `agent-framework-declarative` | `python/packages/declarative` | `released` | -| `agent-framework-devui` | `python/packages/devui` | `beta` | -| `agent-framework-duckdb` | `python/packages/duckdb` | `alpha` | -| `agent-framework-foundry` | `python/packages/foundry` | `released` | -| `agent-framework-foundry-hosting` | `python/packages/foundry_hosting` | `beta` | -| `agent-framework-foundry-local` | `python/packages/foundry_local` | `beta` | -| `agent-framework-gemini` | `python/packages/gemini` | `beta` | -| `agent-framework-github-copilot` | `python/packages/github_copilot` | `released` | -| `agent-framework-hosting` | `python/packages/hosting` | `alpha` | -| `agent-framework-hosting-a2a` | `python/packages/hosting-a2a` | `alpha` | -| `agent-framework-hosting-mcp` | `python/packages/hosting-mcp` | `alpha` | -| `agent-framework-hosting-responses` | `python/packages/hosting-responses` | `alpha` | -| `agent-framework-hosting-telegram` | `python/packages/hosting-telegram` | `alpha` | -| `agent-framework-hyperlight` | `python/packages/hyperlight` | `beta` | -| `agent-framework-lab` | `python/packages/lab` | `beta` | -| `agent-framework-mem0` | `python/packages/mem0` | `beta` | -| `agent-framework-mistral` | `python/packages/mistral` | `beta` | -| `agent-framework-mongodb` | `python/packages/mongodb` | `alpha` | -| `agent-framework-monty` | `python/packages/monty` | `beta` | -| `agent-framework-ollama` | `python/packages/ollama` | `beta` | -| `agent-framework-openai` | `python/packages/openai` | `released` | -| `agent-framework-oracle` | `python/packages/oracle` | `alpha` | -| `agent-framework-orchestrations` | `python/packages/orchestrations` | `released` | -| `agent-framework-postgres` | `python/packages/postgres` | `alpha` | -| `agent-framework-purview` | `python/packages/purview` | `beta` | -| `agent-framework-qdrant` | `python/packages/qdrant` | `alpha` | -| `agent-framework-redis` | `python/packages/redis` | `beta` | -| `agent-framework-sql-server` | `python/packages/sql-server` | `alpha` | -| `agent-framework-tools` | `python/packages/tools` | `beta` | -| `agent-framework-typesafe` | `python/packages/typesafe` | `alpha` | +| Package | Path | State | Meta package | +| --- | --- | --- | --- | +| `agent-framework` | `python/` | `released` | `n/a` | +| `agent-framework-a2a` | `python/packages/a2a` | `beta` | `yes` | +| `agent-framework-ag-ui` | `python/packages/ag-ui` | `released` | `yes` | +| `agent-framework-anthropic` | `python/packages/anthropic` | `beta` | `yes` | +| `agent-framework-azure-contentunderstanding` | `python/packages/azure-contentunderstanding` | `beta` | `yes` | +| `agent-framework-azure-ai-search` | `python/packages/azure-ai-search` | `beta` | `yes` | +| `agent-framework-azure-documentdb` | `python/packages/azure-documentdb` | `alpha` | `no` | +| `agent-framework-azure-cosmos` | `python/packages/azure-cosmos` | `beta` | `yes` | +| `agent-framework-azure-cosmos-memory` | `python/packages/azure-cosmos-memory` | `alpha` | `no` | +| `agent-framework-bedrock` | `python/packages/bedrock` | `beta` | `yes` | +| `agent-framework-chatkit` | `python/packages/chatkit` | `beta` | `yes` | +| `agent-framework-claude` | `python/packages/claude` | `beta` | `yes` | +| `agent-framework-copilotstudio` | `python/packages/copilotstudio` | `beta` | `yes` | +| `agent-framework-core` | `python/packages/core` | `released` | `yes` | +| `agent-framework-declarative` | `python/packages/declarative` | `released` | `yes` | +| `agent-framework-devui` | `python/packages/devui` | `beta` | `yes` | +| `agent-framework-duckdb` | `python/packages/duckdb` | `alpha` | `no` | +| `agent-framework-foundry` | `python/packages/foundry` | `released` | `yes` | +| `agent-framework-foundry-hosting` | `python/packages/foundry_hosting` | `beta` | `yes` | +| `agent-framework-foundry-local` | `python/packages/foundry_local` | `beta` | `yes` | +| `agent-framework-gemini` | `python/packages/gemini` | `beta` | `yes` | +| `agent-framework-github-copilot` | `python/packages/github_copilot` | `released` | `yes` | +| `agent-framework-hosting` | `python/packages/hosting` | `alpha` | `no` | +| `agent-framework-hosting-a2a` | `python/packages/hosting-a2a` | `alpha` | `no` | +| `agent-framework-hosting-mcp` | `python/packages/hosting-mcp` | `alpha` | `no` | +| `agent-framework-hosting-responses` | `python/packages/hosting-responses` | `alpha` | `no` | +| `agent-framework-hosting-telegram` | `python/packages/hosting-telegram` | `alpha` | `no` | +| `agent-framework-hyperlight` | `python/packages/hyperlight` | `beta` | `yes` | +| `agent-framework-lab` | `python/packages/lab` | `beta` | `no` | +| `agent-framework-mem0` | `python/packages/mem0` | `beta` | `yes` | +| `agent-framework-mistral` | `python/packages/mistral` | `beta` | `yes` | +| `agent-framework-mongodb` | `python/packages/mongodb` | `alpha` | `no` | +| `agent-framework-monty` | `python/packages/monty` | `beta` | `yes` | +| `agent-framework-ollama` | `python/packages/ollama` | `beta` | `yes` | +| `agent-framework-openai` | `python/packages/openai` | `released` | `yes` | +| `agent-framework-oracle` | `python/packages/oracle` | `alpha` | `no` | +| `agent-framework-orchestrations` | `python/packages/orchestrations` | `released` | `yes` | +| `agent-framework-postgres` | `python/packages/postgres` | `alpha` | `no` | +| `agent-framework-purview` | `python/packages/purview` | `beta` | `yes` | +| `agent-framework-qdrant` | `python/packages/qdrant` | `alpha` | `no` | +| `agent-framework-redis` | `python/packages/redis` | `beta` | `yes` | +| `agent-framework-sql-server` | `python/packages/sql-server` | `alpha` | `no` | +| `agent-framework-tools` | `python/packages/tools` | `beta` | `yes` | +| `agent-framework-typesafe` | `python/packages/typesafe` | `alpha` | `no` | ## Deprecated / removed packages diff --git a/python/pyproject.toml b/python/pyproject.toml index 996d8a5211a..4c154ea4143 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "agent-framework" -description = "Microsoft Agent Framework for building AI Agents with Python. This package contains all the core and optional packages." +description = "Microsoft Agent Framework for building AI Agents with Python. This package contains core and a curated set of optional integrations." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" From 91403f95e027cf714792a6f6b1e2ed439b8c4456 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Mon, 12 Oct 2026 15:07:45 +0200 Subject: [PATCH 2/5] Python: centralize package promotion guidance --- .../skills/python-feature-lifecycle/SKILL.md | 4 ++ .../skills/python-package-management/SKILL.md | 33 ++++++++---- python/AGENTS.md | 4 +- python/CODING_STANDARD.md | 50 +++---------------- 4 files changed, 36 insertions(+), 55 deletions(-) diff --git a/python/.github/skills/python-feature-lifecycle/SKILL.md b/python/.github/skills/python-feature-lifecycle/SKILL.md index 80e8af17c24..82aff5314dd 100644 --- a/python/.github/skills/python-feature-lifecycle/SKILL.md +++ b/python/.github/skills/python-feature-lifecycle/SKILL.md @@ -21,6 +21,10 @@ These are related, but they are **not the same thing**. - **Feature-stage decorators are only for exceptions** when a feature is behind the package's default stage. - Do **not** decorate every class or function just because the package is experimental or release candidate. +This skill describes staged API behavior within a package. For creating or promoting a package, including sample +placement, meta-package membership, versioning, and lazy-loading namespace decisions, follow the +`python-package-management` skill. + ### Important default If a package is still in **beta / experimental preview**, all public APIs in that package are experimental by default. diff --git a/python/.github/skills/python-package-management/SKILL.md b/python/.github/skills/python-package-management/SKILL.md index 2be3896701f..3e7a069005d 100644 --- a/python/.github/skills/python-package-management/SKILL.md +++ b/python/.github/skills/python-package-management/SKILL.md @@ -120,7 +120,8 @@ Package lifecycle and membership in the root `agent-framework` meta-package are starts outside `agent-framework-core[all]`, and promotion to `beta`, `rc`, or `released` does not add it automatically. -Adding a package to the standard install set requires an explicit core-team decision. Consider: +Adding a package to or removing a package from the standard install set requires an agreed GitHub issue and an +explicit core-team decision. For additions, consider: - whether the package is useful across common Agent Framework workloads rather than for one specialized storage backend, hosting adapter, channel, or deployment target; @@ -133,8 +134,14 @@ Adding a package to the standard install set requires an explicit core-team deci Default to a separate install when the case is unclear. Existing membership is not precedent for every package in the same lifecycle stage or category. +For removals, the issue must also assess compatibility for existing `pip install agent-framework` and +`agent-framework-core[all]` users, document any migration or replacement path, determine whether the change is +breaking, and identify the required Core and root meta-package version changes. Do not treat removal as ordinary +dependency cleanup. + Track the decision in the `Meta package` column of `python/PACKAGE_STATUS.md`. When membership changes, update -`packages/core/pyproject.toml` `[project.optional-dependencies].all` and the status table in the same change. +`packages/core/pyproject.toml` `[project.optional-dependencies].all`, the Core and root package versions as required, +and the status table in the same change. ## Lazy Loading Pattern @@ -265,10 +272,13 @@ Move a package to `rc` when its API is close to the final released shape. 1. Update the package version to the release-candidate pattern: `1.0.0rc` 2. Keep the classifier at `Development Status :: 4 - Beta` because PyPI does not have a separate release-candidate classifier -3. Preserve the package's current meta-package membership unless it is reconsidered separately; the stage +3. Apply the meta-package decision recorded in the promotion issue, preserving current membership when unchanged + or using the membership-change process above when an addition or removal is explicitly approved; the stage transition alone does not add it to `core[all]` -4. Keep samples only in the root `samples/` tree -5. Update `python/PACKAGE_STATUS.md` to show the package as `rc` and keep its membership value current +4. Apply the lazy-loading decision recorded in the promotion issue, preserving the existing namespace when + unchanged or updating/removing it when explicitly approved +5. Keep samples only in the root `samples/` tree +6. Update `python/PACKAGE_STATUS.md` to show the package as `rc` and keep its membership value current #### RC -> Released @@ -276,11 +286,14 @@ Move a package to `released` when it no longer carries a prerelease qualifier. 1. Update the package version to the stable pattern: `1.0.0` 2. Update the classifier to `Development Status :: 5 - Production/Stable` -3. Preserve the package's current meta-package membership unless it is reconsidered separately; release does not - imply inclusion in `core[all]` -4. Keep samples only in the root `samples/` tree -5. Update `python/PACKAGE_STATUS.md` to show the package as `released` and keep its membership value current -6. Update all `README.md` files that install that package with +3. Apply the meta-package decision recorded in the promotion issue, preserving current membership when unchanged + or using the membership-change process above when an addition or removal is explicitly approved; release does + not imply inclusion in `core[all]` +4. Apply the lazy-loading decision recorded in the promotion issue, preserving the existing namespace when + unchanged or updating/removing it when explicitly approved +5. Keep samples only in the root `samples/` tree +6. Update `python/PACKAGE_STATUS.md` to show the package as `released` and keep its membership value current +7. Update all `README.md` files that install that package with `pip install agent-framework-... --pre` so they use `pip install agent-framework-...` without the `--pre` suffix diff --git a/python/AGENTS.md b/python/AGENTS.md index 6cdac7c462f..86f19e45714 100644 --- a/python/AGENTS.md +++ b/python/AGENTS.md @@ -13,8 +13,8 @@ Instructions for AI coding agents working in the Python codebase. - `python-development` — coding standards, type annotations, docstrings, logging, performance - `python-testing` — test structure, fixtures, async mode, running tests - `python-code-quality` — linting, formatting, type checking, prek hooks, CI workflow -- `python-feature-lifecycle` — package vs feature lifecycle stages, decorators, enums, and promotion guidance -- `python-package-management` — monorepo structure, lazy loading, versioning, new packages +- `python-feature-lifecycle` — package-stage meaning plus staged API decorators, enums, and feature promotion +- `python-package-management` — package creation and promotion, meta-package membership, lazy loading, and versioning - `pull-requests` — writing PR descriptions (template) and handling/resolving PR review comments - `agent-framework-py-release` — Python release PR workflow, CHANGELOG-driven package bumps, lifecycle version rules, and dependency-floor validation diff --git a/python/CODING_STANDARD.md b/python/CODING_STANDARD.md index 2bbd56dc31f..3d4dc2fc486 100644 --- a/python/CODING_STANDARD.md +++ b/python/CODING_STANDARD.md @@ -351,7 +351,7 @@ python/ ├── samples/ # Sample code and examples ├── packages/ │ ├── core/ # agent-framework-core - Core abstractions and implementations -│ │ ├── pyproject.toml # Defines [all] extra that includes all connector packages +│ │ ├── pyproject.toml # Defines the curated standard-integration [all] extra │ │ ├── tests/ # Tests for core package │ │ └── agent_framework/ │ │ ├── __init__.py # Lazy runtime public API exports @@ -423,47 +423,11 @@ def __getattr__(name: str) -> Any: ) from exc ``` -### Adding a New Connector Package - -**Important:** Do not create a new package unless there is an issue that has been reviewed and approved by the core team. - -#### Initial Release (Preview Phase) - -For the first release of a new connector package: - -1. Create a new directory under `packages/` (e.g., `packages/my-connector/`) -2. Add the package to `tool.uv.sources` in the root `pyproject.toml` -3. Include samples inside the package itself (e.g., `packages/my-connector/samples/`) -4. **Do NOT** add the package to the `[all]` extra in `packages/core/pyproject.toml` -5. **Do NOT** create lazy loading in core yet - -#### Promotion to Stable - -After the package has been released and gained a measure of confidence: - -1. Move samples from the package to the root `samples/` folder -2. Add the package to the `[all]` extra in `packages/core/pyproject.toml` -3. Create a provider folder in `agent_framework/` with lazy loading `__init__.py` - -### Versioning and Core Dependency - -All non-core packages declare a lower bound on `agent-framework-core` (e.g., `"agent-framework-core>=1.0.0b260130"`). Follow these rules when bumping versions: - -- **Core version changes**: When `agent-framework-core` is updated with breaking or significant changes and its version is bumped, update the `agent-framework-core>=...` lower bound in every other package's `pyproject.toml` to match the new core version. -- **Non-core version changes**: Non-core packages (connectors, extensions) can have their own versions incremented independently while keeping the existing core lower bound pinned. Only raise the core lower bound if the non-core package actually depends on new core APIs. - -### External Dependency Version Bounds - -The guiding principle for external dependencies is to make the range of allowed versions as broad as possible, even if that means we have to do some conditional imports, and other tricks to allow small changes in versions. -So we use bounded ranges for external package dependencies in `pyproject.toml`: - - -- For stable dependencies (`>=1.0.0`), use a lower bound at a known-good version and an explicit upper bound that reflects the maximum major version we currently support (for example: `openai>=1.99.0,<3`). -- For prerelease (`dev`/`a`/`b`/`rc`) dependencies, use a known-good lower bound with a hard upper boundary in the same prerelease line (for example: `azure-ai-projects>=2.0.0b3,<2.0.0b4`). -- For `<1.0.0` dependencies, use a known-good bounded range with an explicit upper cap. Prefer the broadest validated range the package can actually support: that may be a patch line, a minor line, or multiple minor lines (for example: `a2a-sdk>=0.3.5,<0.4.0`, `fastapi>=0.115.0,<0.136.0`, `uvicorn>=0.30.0,<0.39.0`). -- For prerelease (`dev`/`a`/`b`/`rc`) dependencies, use a known-good bounded range with a hard upper cap and keep the range only as broad as the package's validation coverage justifies. -- Prefer keeping support for multiple major versions when practical. This may mean that the upper bound spans multiple major versions when the dependency maintains backward compatibility; if APIs differ between supported majors, version-conditional imports/branches are acceptable to preserve compatibility. -- When adding or changing an external dependency, first run `uv run poe validate-dependency-bounds-test` to validate workspace-wide lower/upper compatibility, then run `uv run poe validate-dependency-bounds-project --mode both --package --dependency ""` to expand package-scoped bounds. +The decision to expose a package through lazy loading, and the namespace to use, is a package-governance decision +rather than a coding convention. Follow +[`python-package-management`](.github/skills/python-package-management/SKILL.md) for package creation, lifecycle +promotion, sample placement, meta-package membership, lazy-loading approval, versioning, and dependency bounds. +This section only defines how to implement an approved lazy-loading surface. ### Installation Options @@ -473,7 +437,7 @@ Connectors are distributed as separate packages and are not imported by default # Install core only pip install agent-framework-core -# Install core with all connectors +# Install core with the curated standard integrations pip install agent-framework-core[all] # or (equivalently): pip install agent-framework From f56b6873d1f30deb531f125b33b14e2e34812e38 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Mon, 12 Oct 2026 15:12:09 +0200 Subject: [PATCH 3/5] Python: freeze namespaces after RC --- .../skills/python-package-management/SKILL.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/python/.github/skills/python-package-management/SKILL.md b/python/.github/skills/python-package-management/SKILL.md index 3e7a069005d..30ab80c4418 100644 --- a/python/.github/skills/python-package-management/SKILL.md +++ b/python/.github/skills/python-package-management/SKILL.md @@ -238,8 +238,9 @@ The issue must record: - the current and target lifecycle stages; - whether the package should be included in the root meta-package, with rationale based on the criteria above; and -- whether the package should be exposed through a core lazy-loading namespace and, if so, the exact namespace to - use. +- the lazy-loading namespace decision for the transition: `alpha` -> `beta` selects whether and where to expose the + package; `beta` -> `rc` should preserve that namespace but may change it by explicit core-team exception; and + `rc` -> `released` must preserve the RC namespace unchanged. Record an explicit `no` or `none` when the package should remain outside the meta-package or should not have a lazy-loading namespace. Do not infer either decision from the target lifecycle stage. @@ -275,8 +276,8 @@ Move a package to `rc` when its API is close to the final released shape. 3. Apply the meta-package decision recorded in the promotion issue, preserving current membership when unchanged or using the membership-change process above when an addition or removal is explicitly approved; the stage transition alone does not add it to `core[all]` -4. Apply the lazy-loading decision recorded in the promotion issue, preserving the existing namespace when - unchanged or updating/removing it when explicitly approved +4. Preserve the existing lazy-loading namespace by default. Changing or removing it at the RC boundary is + discouraged and requires explicit core-team approval recorded in the promotion issue 5. Keep samples only in the root `samples/` tree 6. Update `python/PACKAGE_STATUS.md` to show the package as `rc` and keep its membership value current @@ -289,8 +290,8 @@ Move a package to `released` when it no longer carries a prerelease qualifier. 3. Apply the meta-package decision recorded in the promotion issue, preserving current membership when unchanged or using the membership-change process above when an addition or removal is explicitly approved; release does not imply inclusion in `core[all]` -4. Apply the lazy-loading decision recorded in the promotion issue, preserving the existing namespace when - unchanged or updating/removing it when explicitly approved +4. Keep the lazy-loading namespace unchanged from RC; do not add, remove, or move the package's namespace as part + of release promotion 5. Keep samples only in the root `samples/` tree 6. Update `python/PACKAGE_STATUS.md` to show the package as `released` and keep its membership value current 7. Update all `README.md` files that install that package with From 2866aad2a531ffbaef97d9aa894d240b72704f7c Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Mon, 12 Oct 2026 15:13:50 +0200 Subject: [PATCH 4/5] Python: allow approved namespace exceptions --- python/.github/skills/python-package-management/SKILL.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/.github/skills/python-package-management/SKILL.md b/python/.github/skills/python-package-management/SKILL.md index 30ab80c4418..920265ef480 100644 --- a/python/.github/skills/python-package-management/SKILL.md +++ b/python/.github/skills/python-package-management/SKILL.md @@ -239,8 +239,8 @@ The issue must record: - the current and target lifecycle stages; - whether the package should be included in the root meta-package, with rationale based on the criteria above; and - the lazy-loading namespace decision for the transition: `alpha` -> `beta` selects whether and where to expose the - package; `beta` -> `rc` should preserve that namespace but may change it by explicit core-team exception; and - `rc` -> `released` must preserve the RC namespace unchanged. + package, while `beta` -> `rc` and `rc` -> `released` preserve the existing namespace by default and may change + it only by explicit core-team decision. Record an explicit `no` or `none` when the package should remain outside the meta-package or should not have a lazy-loading namespace. Do not infer either decision from the target lifecycle stage. @@ -290,8 +290,8 @@ Move a package to `released` when it no longer carries a prerelease qualifier. 3. Apply the meta-package decision recorded in the promotion issue, preserving current membership when unchanged or using the membership-change process above when an addition or removal is explicitly approved; release does not imply inclusion in `core[all]` -4. Keep the lazy-loading namespace unchanged from RC; do not add, remove, or move the package's namespace as part - of release promotion +4. Preserve the lazy-loading namespace used at RC by default. Adding, removing, or moving the namespace requires + explicit core-team approval recorded in the promotion issue 5. Keep samples only in the root `samples/` tree 6. Update `python/PACKAGE_STATUS.md` to show the package as `released` and keep its membership value current 7. Update all `README.md` files that install that package with From c05e83d034926d1e158cb49e28f130b7d39b55af Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Mon, 12 Oct 2026 15:47:28 +0200 Subject: [PATCH 5/5] Python: clarify lazy namespace compatibility --- .../skills/python-package-management/SKILL.md | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/python/.github/skills/python-package-management/SKILL.md b/python/.github/skills/python-package-management/SKILL.md index 920265ef480..7dd0f1e4ead 100644 --- a/python/.github/skills/python-package-management/SKILL.md +++ b/python/.github/skills/python-package-management/SKILL.md @@ -180,6 +180,19 @@ def __getattr__(name: str) -> Any: ) from exc ``` +### Namespace compatibility + +The core namespace module and the objects it re-exports have different compatibility rules: + +- While an owning package is `alpha`, `beta`, or `rc`, imports for objects from that package may be added, removed, + or moved between approved lazy-loading namespaces by explicit core-team decision. Those object paths follow the + owning package's prerelease lifecycle. +- Do not remove a core namespace module unless it is empty after the change and is used exclusively by packages + that have not reached `released`. +- If a namespace exposes objects from any released package, keep the namespace. Changing import paths for released + objects requires a deprecated forwarding path where feasible; otherwise the agreed issue must include migration + guidance, explicit breaking-change acknowledgement, and the required Core and root meta-package version changes. + ## Adding a New Connector Package **Important:** Do not create a new package unless approved by the core team. @@ -240,7 +253,8 @@ The issue must record: - whether the package should be included in the root meta-package, with rationale based on the criteria above; and - the lazy-loading namespace decision for the transition: `alpha` -> `beta` selects whether and where to expose the package, while `beta` -> `rc` and `rc` -> `released` preserve the existing namespace by default and may change - it only by explicit core-team decision. + the package's prerelease object imports only by explicit core-team decision and under the namespace compatibility + rules above. Record an explicit `no` or `none` when the package should remain outside the meta-package or should not have a lazy-loading namespace. Do not infer either decision from the target lifecycle stage. @@ -276,8 +290,9 @@ Move a package to `rc` when its API is close to the final released shape. 3. Apply the meta-package decision recorded in the promotion issue, preserving current membership when unchanged or using the membership-change process above when an addition or removal is explicitly approved; the stage transition alone does not add it to `core[all]` -4. Preserve the existing lazy-loading namespace by default. Changing or removing it at the RC boundary is - discouraged and requires explicit core-team approval recorded in the promotion issue +4. Preserve the existing lazy-loading namespace by default. Changes to the package's prerelease object imports are + discouraged and require explicit core-team approval recorded in the promotion issue; apply the namespace + compatibility rules above 5. Keep samples only in the root `samples/` tree 6. Update `python/PACKAGE_STATUS.md` to show the package as `rc` and keep its membership value current @@ -290,8 +305,9 @@ Move a package to `released` when it no longer carries a prerelease qualifier. 3. Apply the meta-package decision recorded in the promotion issue, preserving current membership when unchanged or using the membership-change process above when an addition or removal is explicitly approved; release does not imply inclusion in `core[all]` -4. Preserve the lazy-loading namespace used at RC by default. Adding, removing, or moving the namespace requires - explicit core-team approval recorded in the promotion issue +4. Preserve the lazy-loading namespace used at RC by default. Any final changes to the package's prerelease object + imports require explicit core-team approval recorded in the promotion issue; apply the namespace compatibility + rules above 5. Keep samples only in the root `samples/` tree 6. Update `python/PACKAGE_STATUS.md` to show the package as `released` and keep its membership value current 7. Update all `README.md` files that install that package with