mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
55b6e7a9f4
* Add Python feature lifecycle decorators Introduce reusable experimental and release-candidate decorators for released packages, migrate the Skills APIs to the new staged metadata and warning system, and add lifecycle guidance plus samples. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix Python CI follow-ups Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Preserve protocol runtime checks Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
239 lines
7.6 KiB
Markdown
239 lines
7.6 KiB
Markdown
# Copyright (c) Microsoft. All rights reserved.
|
|
|
|
---
|
|
name: python-feature-lifecycle
|
|
description: >
|
|
Guidance for package and feature lifecycle in the Agent Framework Python
|
|
codebase, including stage meanings, feature-stage decorators, feature enums,
|
|
and how to move APIs from one stage to the next.
|
|
---
|
|
|
|
# Python Feature Lifecycle
|
|
|
|
## Two lifecycle levels
|
|
|
|
Agent Framework uses lifecycle at two different levels:
|
|
|
|
1. **Package lifecycle** — the maturity of the package as a whole
|
|
2. **Feature lifecycle** — the maturity of a specific API or feature inside that package
|
|
|
|
These are related, but they are **not the same thing**.
|
|
|
|
- The **package stage is the default** for everything in the package.
|
|
- **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.
|
|
|
|
### Important default
|
|
|
|
If a package is still in **beta / experimental preview**, all public APIs in that package are experimental by default.
|
|
|
|
- Do **not** add `@experimental(...)` everywhere in that package.
|
|
- The package stage already communicates that default.
|
|
|
|
Once a package moves forward, you can keep individual features behind:
|
|
|
|
- If a package moves to **release candidate**, a feature may remain **experimental**
|
|
- If a package moves to **released / GA**, a feature may remain **experimental** or **release candidate**
|
|
|
|
That is the main use case for feature-stage decorators.
|
|
|
|
## The four stages
|
|
|
|
### 1. Experimental
|
|
|
|
Use for features that are still unstable and may change or be removed without notice.
|
|
|
|
Feature-level code pattern:
|
|
|
|
```python
|
|
from ._feature_stage import ExperimentalFeature, experimental
|
|
|
|
|
|
@experimental(feature_id=ExperimentalFeature.MY_FEATURE)
|
|
class MyFeature:
|
|
...
|
|
```
|
|
|
|
Behavior:
|
|
|
|
- Adds an experimental warning block to the docstring
|
|
- Records feature metadata on the decorated object
|
|
- Emits a runtime warning the first time the feature is used (once per feature by default)
|
|
|
|
Enum setup:
|
|
|
|
- Add an all-caps member to `ExperimentalFeature`
|
|
- Reuse the same feature ID across all APIs that belong to the same conceptual feature
|
|
|
|
### 2. Release candidate
|
|
|
|
Use for features that are nearly stable but may still receive small refinements before GA.
|
|
|
|
Feature-level code pattern:
|
|
|
|
```python
|
|
from ._feature_stage import ReleaseCandidateFeature, release_candidate
|
|
|
|
|
|
@release_candidate(feature_id=ReleaseCandidateFeature.MY_FEATURE)
|
|
class MyFeature:
|
|
...
|
|
```
|
|
|
|
Behavior:
|
|
|
|
- Adds a release-candidate note to the docstring
|
|
- Records feature metadata on the decorated object
|
|
- Does **not** emit the experimental warning
|
|
|
|
Enum setup:
|
|
|
|
- Add an all-caps member to `ReleaseCandidateFeature`
|
|
|
|
### 3. Released
|
|
|
|
Use for stable GA APIs.
|
|
|
|
Code pattern:
|
|
|
|
- **No feature-stage decorator**
|
|
- **No entry** in `ExperimentalFeature`
|
|
- **No entry** in `ReleaseCandidateFeature`
|
|
|
|
If a feature is fully released, remove any stage-specific feature annotation.
|
|
|
|
### 4. Deprecated
|
|
|
|
Use for APIs that still exist but should not be used for new code.
|
|
|
|
Code pattern:
|
|
|
|
```python
|
|
import sys
|
|
|
|
if sys.version_info >= (3, 13):
|
|
from warnings import deprecated # type: ignore # pragma: no cover
|
|
else:
|
|
from typing_extensions import deprecated # type: ignore # pragma: no cover
|
|
|
|
|
|
@deprecated("MyOldFeature is deprecated. Use MyNewFeature instead.")
|
|
class MyOldFeature:
|
|
...
|
|
```
|
|
|
|
Behavior:
|
|
|
|
- Uses the repository's version-conditional deprecation import pattern
|
|
- Should describe what to use instead
|
|
|
|
Deprecated APIs should not also carry feature-stage decorators.
|
|
|
|
## Expected decorators by stage
|
|
|
|
| Feature stage | Expected annotation |
|
|
| --- | --- |
|
|
| Experimental | `@experimental(feature_id=ExperimentalFeature.X)` |
|
|
| Release candidate | `@release_candidate(feature_id=ReleaseCandidateFeature.X)` |
|
|
| Released | No feature-stage decorator |
|
|
| Deprecated | `@deprecated("...")` |
|
|
|
|
## Feature enums
|
|
|
|
The feature enums are the inventory of currently staged features:
|
|
|
|
- `ExperimentalFeature`
|
|
- `ReleaseCandidateFeature`
|
|
|
|
Guidance:
|
|
|
|
- Use one enum member per conceptual feature, not per class
|
|
- Ideally, an ADR already defines the overall feature boundary and therefore the feature ID that staged APIs for that feature should reuse
|
|
- Keep feature IDs all caps
|
|
- Reuse the same member across related APIs for the same feature
|
|
- Remove enum members when the feature no longer belongs to that stage
|
|
- Treat these enums as **current-stage inventories**, not as a stable consumer introspection API
|
|
|
|
Minimal consumer guidance:
|
|
|
|
- Treat `__feature_stage__` and `__feature_id__` as optional staged metadata, not as stable contracts
|
|
- Use `getattr(obj, "__feature_stage__", None)` and `getattr(obj, "__feature_id__", None)` rather than direct attribute access
|
|
- Treat missing metadata as "no explicit feature-stage annotation"
|
|
- For warning filters while a feature is staged, match the literal feature ID string
|
|
- Do **not** rely on `ExperimentalFeature.X`, `ReleaseCandidateFeature.X`, or the continued presence of `__feature_id__` after a feature moves stages or is released
|
|
|
|
For consumers, the enums are also re-exported from `agent_framework`.
|
|
|
|
For internal implementation code inside `agent_framework`, continue to import the enums and decorators from `._feature_stage`.
|
|
|
|
## Package stage vs feature stage
|
|
|
|
Use the following rules:
|
|
|
|
### Package is experimental / beta
|
|
|
|
- All public APIs are experimental by default
|
|
- Do **not** add feature-stage decorators just to restate that
|
|
- Only introduce feature-level annotations later if the package advances first
|
|
|
|
### Package is release candidate
|
|
|
|
- All public APIs are RC by default
|
|
- Do **not** decorate everything
|
|
- Add `@experimental(...)` only for features that are intentionally still behind the package
|
|
|
|
### Package is released / GA
|
|
|
|
- All public APIs are released by default
|
|
- Add `@experimental(...)` or `@release_candidate(...)` only for features still being held back
|
|
|
|
## Moving a feature from one stage to the next
|
|
|
|
### Experimental -> Release candidate
|
|
|
|
1. Move the feature ID from `ExperimentalFeature` to `ReleaseCandidateFeature`
|
|
2. Replace `@experimental(...)` with `@release_candidate(...)`
|
|
3. Update any tests or docs that mention the old stage
|
|
|
|
### Experimental -> Released
|
|
|
|
1. Remove `@experimental(...)`
|
|
2. Remove the feature from `ExperimentalFeature`
|
|
3. Do not add a replacement feature-stage decorator
|
|
|
|
### Release candidate -> Released
|
|
|
|
1. Remove `@release_candidate(...)`
|
|
2. Remove the feature from `ReleaseCandidateFeature`
|
|
3. Leave the API undecorated
|
|
|
|
### Any stage -> Deprecated
|
|
|
|
1. Remove any feature-stage decorator
|
|
2. Remove the feature from the stage enum
|
|
3. Add `@deprecated("...")`
|
|
4. Update docs/tests to reflect the replacement path
|
|
|
|
## Promotion guidance
|
|
|
|
Features do **not** have to pass through every stage.
|
|
|
|
- It is usually a good idea to move features in order when that reflects reality
|
|
- But it is completely acceptable to go **experimental -> released**
|
|
- Do **not** force a feature through release candidate if there is no real RC period
|
|
|
|
Likewise, when a package advances, do not automatically move every feature with it.
|
|
|
|
- Promote features based on actual readiness
|
|
- Keep lagging features explicitly marked only when they are behind the package default
|
|
|
|
## Practical rules of thumb
|
|
|
|
- **Package default first, feature exceptions second**
|
|
- **Do not decorate everything in preview packages**
|
|
- **Do not double-annotate members of an already-staged class**
|
|
- **Use enums only for currently staged features**
|
|
- **Do not treat stage enums as a compatibility contract**
|
|
- **Treat `__feature_stage__` and `__feature_id__` as optional metadata; use `getattr`**
|
|
- **Remove stage annotations once a feature is released or deprecated**
|