Mark agentserver experimental APIs - #48401
Conversation
Add a shared agentserver experimental decorator and apply it to the resilient task and Foundry storage public surfaces. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines: Successfully started running 1 pipeline(s). 9 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Apply @experimental directly on resilient task and Foundry storage public API definitions instead of wrapping re-exports. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines: Successfully started running 1 pipeline(s). 9 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
Adds a shared experimental API marker and applies it to preview resilient-task and Foundry-storage surfaces.
Changes:
- Adds public
experimentaldecorator with documentation notes and cached warnings. - Marks selected core and responses APIs experimental.
- Adds focused tests and changelog entries.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
sdk/agentserver/azure-ai-agentserver-responses/tests/test_experimental.py |
Tests responses markers and host exclusion. |
sdk/agentserver/azure-ai-agentserver-responses/CHANGELOG.md |
Documents experimental storage APIs. |
sdk/agentserver/azure-ai-agentserver-responses/azure/ai/agentserver/responses/store/_foundry_settings.py |
Marks storage settings experimental. |
sdk/agentserver/azure-ai-agentserver-responses/azure/ai/agentserver/responses/store/_foundry_provider.py |
Marks storage provider experimental. |
sdk/agentserver/azure-ai-agentserver-responses/azure/ai/agentserver/responses/store/_foundry_errors.py |
Marks public storage errors experimental. |
sdk/agentserver/azure-ai-agentserver-core/tests/test_experimental.py |
Tests decorator behavior and suppression. |
sdk/agentserver/azure-ai-agentserver-core/CHANGELOG.md |
Documents the experimental marker. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_run.py |
Marks task runs experimental. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_retry.py |
Marks retry policies experimental. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_metadata.py |
Marks task metadata experimental. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_exceptions.py |
Marks public task exceptions experimental. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_enablement.py |
Marks enablement functions experimental. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_decorator.py |
Marks resilient-task decorators and classes. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_context.py |
Marks task context experimental. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/storage/_state.py |
Marks state store experimental. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/storage/_errors.py |
Marks core storage errors experimental. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/storage/_endpoint.py |
Marks storage endpoint experimental. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/storage/_client.py |
Marks storage client experimental. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py |
Implements the experimental decorator. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/__init__.py |
Exports the decorator publicly. |
Suppressed comments (1)
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_decorator.py:1758
- As with
task, decorating an overload stub has no effect on the runtimemulti_turn_taskobject because the concrete definition at line 1784 replaces it. Move@experimentalto that implementation so callers and generated documentation see the marker.
@experimental
Use qualified warning cache keys, prevent inherited initializer warning stacking, mark concrete task decorators, and raise the responses core dependency floor. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated no new comments.
Suppressed comments (3)
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:99
- This wrapper is always synchronous, so decorating an
async defmakesinspect.iscoroutinefunction()returnFalse. Async dispatchers that rely on that contract may stop awaiting the function, despite this decorator being public and intended not to change behavior. Preserve coroutine functions with anasync defwrapper and add an async regression test.
def wrapped(*args: P.args, **kwargs: P.kwargs) -> T:
sdk/agentserver/azure-ai-agentserver-core/CHANGELOG.md:7
- The claim that Foundry storage public APIs are now marked is incomplete.
azure.ai.agentserver.core.storage.__all__also exposes the constructibleStateStore,DeletedStateStore,StateStoreItem*, andStateStoreItemKeyPageclasses (core/storage/__init__.py:29-50), but these classes remain undecorated in_state_serializer.py, so customers receive neither the documentation note nor runtime warning. Mark these public model/page classes too, or explicitly narrow the claimed scope.
- Added a shared `experimental` decorator for marking Agent Server preview feature surfaces with docstring notes and one-time runtime warnings. The resilient task primitive and Foundry storage public APIs are now marked experimental.
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:132
- The membership check and insertion are separate operations, so two threads can both observe a missing key and each emit the warning. This violates the advertised once-per-process guarantee during concurrent first use. Serialize the check-and-add operation with a lock and cover simultaneous calls in a regression test.
def _is_warning_cached(cache_key: str) -> bool:
if cache_key in _warning_cache:
return True
_warning_cache.add(cache_key)
This comment has been minimized.
This comment has been minimized.
Avoid exposing inherited or builtin __init__ wrappers through functools.wraps so apistub can inspect experimental classes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated no new comments.
Suppressed comments (3)
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:87
- For decorated subclasses that inherit
__init__(for exampleFoundryStorageNotFoundError), this condition skipsfunctools.wraps, so the newly assigned wrapper changes the public constructor signature to(*args, **kwargs)instead of preserving the base error's parameters. Applywrapsto inherited Python__init__methods as well so marking a class experimental does not alter introspection/API surfaces.
if "__init__" in cls.__dict__ and inspect.isfunction(original_init):
wrapped_init = functools.wraps(original_init)(wrapped_init)
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:101
- This always returns a synchronous wrapper, so decorating an
async defor async-generator function makesinspect.iscoroutinefunction/inspect.isasyncgenfunctionreturn false. Agent Server relies on those checks when accepting handlers (for example_decorator.py:1414and responses_routing.py:199), so the decorator can change whether an otherwise valid async callable is accepted. Preserve the original callable kind with async and async-generator wrapper paths.
@functools.wraps(func)
def wrapped(*args: P.args, **kwargs: P.kwargs) -> T:
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:134
- The cache membership check and insertion are not synchronized. Concurrent first uses of the same experimental class/function can both observe a miss and both log, violating the advertised once-per-process behavior in a multithreaded server. Guard this check-and-add operation with a lock.
def _is_warning_cached(cache_key: str) -> bool:
if cache_key in _warning_cache:
return True
_warning_cache.add(cache_key)
Preserve inherited initializer signatures for experimental classes and commit regenerated API metadata for core and responses. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Align core API stub metadata with CI Python 3.12 apistub output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.
Suppressed comments (3)
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:147
- The membership check and insertion are not atomic as a pair. Concurrent first uses of the same experimental API can both observe a cache miss and each log, violating the once-per-process guarantee. Protect this check-and-add operation with a module-level lock.
def _is_warning_cached(cache_key: str) -> bool:
if cache_key in _warning_cache:
return True
_warning_cache.add(cache_key)
return False
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:80
- Classes that inherit an unwrapped
__init__are returned without any runtime wrapper, so a marked public class such asTaskManagerNotInitializednever emits the promised warning when instantiated. Only skip when the inherited initializer is already an experimental wrapper; otherwise assign a wrapper for the inherited initializer too.
if "__init__" not in cls.__dict__ or getattr(original_init, _EXPERIMENTAL_WRAPPED_INIT_ATTR, False):
return cls
sdk/agentserver/azure-ai-agentserver-responses/pyproject.toml:21
- This dependency bump leaves the explanatory comment at line 75 referring to
azure-ai-agentserver-core>=2.0.0b9, although the disabled minimum-dependency check now concerns>=2.0.0b10. Update that comment with the dependency so the build configuration does not document the previous constraint.
"azure-ai-agentserver-core>=2.0.0b10",
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.
Suppressed comments (4)
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:80
- Classes that inherit an unwrapped initializer are returned without a runtime wrapper. For example, the newly marked
TaskManagerNotInitializedhas no own__init__and inheritsRuntimeError.__init__, so raising or instantiating it never emits the promised experimental warning. Only skip wrapping when the inherited initializer is already an experimental wrapper; otherwise wrap it too.
if "__init__" not in cls.__dict__ or getattr(original_init, _EXPERIMENTAL_WRAPPED_INIT_ATTR, False):
return cls
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_decorator.py:1783
- Because this function is overloaded, API extraction emits the overload declarations rather than this implementation. Decorating only the implementation leaves both emitted
multi_turn_tasksignatures unmarked—the regeneratedapi.mdstill shows only@overload—so APIView does not expose the experimental status promised by this PR. Apply@experimentalto each overload declaration as well, retain this runtime decorator, and regenerate the API files.
@experimental
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_decorator.py:1353
- Because this function is overloaded, API extraction emits the overload declarations rather than this implementation. Decorating only the implementation leaves both emitted
tasksignatures unmarked—the regeneratedapi.mdstill shows only@overload—so APIView does not expose the experimental status promised by this PR. Apply@experimentalto each overload declaration as well, retain this runtime decorator, and regenerate the API files.
@experimental
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:147
- The cache lookup and insertion are separate operations, so two threads can both observe a missing key and each log the warning. This violates the documented once-per-process behavior under concurrent server requests. Guard the check-and-add operation with a module-level lock and cover simultaneous calls with a regression test.
def _is_warning_cached(cache_key: str) -> bool:
if cache_key in _warning_cache:
return True
_warning_cache.add(cache_key)
return False
Use a responses-local compatibility shim for the shared experimental decorator so Sphinx can import when the docs environment resolves an older core package. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.
Suppressed comments (3)
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:80
- Classes that inherit an unwrapped
__init__return here without installing the runtime warning wrapper. This already affects the newly markedTaskManagerNotInitialized, which has no local__init__and inherits directly fromRuntimeError, so constructing/raising it never emits the promised warning. Only skip when the inherited initializer is already an experimental wrapper; otherwise assign the wrapper on the subclass as this function does for locally defined initializers.
if "__init__" not in cls.__dict__ or getattr(original_init, _EXPERIMENTAL_WRAPPED_INIT_ATTR, False):
return cls
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_decorator.py:1784
- The implementation-only decorator is also absent from the generated public overload signatures for
multi_turn_task(api.mdlines 588–608), so this resilient task API is not identified as experimental in APIView despite the PR requirement. Mark the overload declarations in the API extractor-compatible form, keep the implementation decorator for runtime behavior, and regenerate the API files.
@experimental
def multi_turn_task(
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_decorator.py:1354
- Applying
@experimentalonly to the overload implementation does not mark the public overload signatures emitted by APIView: the regeneratedapi.mdentries fortaskat lines 619–637 contain only@overload, unlike the other experimental APIs. This contradicts the stated requirement that the experimental marker appear on APIView-style surfaces. Mark the overload declarations in the form understood by the API extractor, retain the implementation decorator for runtime warnings, and regenerate the API files.
@experimental
def task(
This comment has been minimized.
This comment has been minimized.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.
Suppressed comments (4)
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:80
- Decorated classes that inherit
__init__from an undecorated base return here without installing a warning wrapper. This already affectsTaskManagerNotInitialized, which inheritsRuntimeError.__init__, so constructing/raising that newly marked public class never emits the promised experimental warning. Skip only an already-wrapped initializer, and preserve the signature for inherited Python initializers as well.
if "__init__" not in cls.__dict__ or getattr(original_init, _EXPERIMENTAL_WRAPPED_INIT_ATTR, False):
return cls
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_decorator.py:1783
- The checked-in
api.mdstill renders bothmulti_turn_taskoverloads with only@overload, unlike the other newly marked resilient APIs. Because API extraction uses the overload declarations rather than this implementation decorator, the experimental marker is absent from the APIView-style surface promised by the PR. Mark the overload declarations in the extractor-recognized form and regenerateapi.md.
@experimental
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_decorator.py:1353
- The checked-in
api.mdstill renders bothtaskoverloads with only@overload, unlike the other newly marked resilient APIs. Because API extraction uses the overload declarations rather than this implementation decorator, the experimental marker is absent from the APIView-style surface promised by the PR. Mark the overload declarations in the extractor-recognized form and regenerateapi.md.
This issue also appears on line 1783 of the same file.
@experimental
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:147
- The cache check and insertion are separate operations, so two threads can both observe a missing key and both log the same warning. This violates the stated once-per-process behavior for concurrently constructed agent objects. Protect the membership/add sequence with a shared lock (and mirror that behavior in the compatibility fallback).
def _is_warning_cached(cache_key: str) -> bool:
if cache_key in _warning_cache:
return True
_warning_cache.add(cache_key)
return False
This comment has been minimized.
This comment has been minimized.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.
Suppressed comments (3)
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_decorator.py:1354
- The experimental marker is only on the concrete implementation, so the exported overloads remain unmarked: the regenerated
api.mdlists bothtaskoverloads without@experimental(lines 619–637), unlike the marked non-overloaded functions immediately above. This leaves APIView presenting this central resilient-task factory as non-experimental. Apply the marker in a way the API extractor associates with the overload declarations, then regenerate the API surface.
@experimental
def task(
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:98
- Classes that inherit an unwrapped initializer are never instrumented because this returns whenever
__init__is absent from the class dictionary.TaskManagerNotInitializedis decorated but inheritsRuntimeError.__init__, so constructing/raising it emits no experimental warning. Wrap inherited initializers too, returning early only when the inherited initializer is already an experimental wrapper.
if "__init__" not in cls.__dict__ or getattr(original_init, _EXPERIMENTAL_WRAPPED_INIT_ATTR, False):
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_decorator.py:1784
- As with
task, marking only this implementation does not propagate to its public overloads:api.mdlines 588–608 contain no@experimentalmarker formulti_turn_task. APIView therefore does not identify this preview factory as experimental. Apply the marker in a form recognized on the overload declarations and regenerate the API surface.
@experimental
def multi_turn_task(
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.
Suppressed comments (4)
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_decorator.py:1784
- As with
task, the API extractor emits only the two overload declarations formulti_turn_task, so this implementation-only marker is absent from the updatedapi.md. Mark the overload declarations in a parser-compatible way and regenerate the API snapshot so this public resilient-task entry point is visibly experimental in APIView.
@experimental
def multi_turn_task(
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:80
- Classes that inherit an unwrapped
__init__return here without installing a warning wrapper.TaskManagerNotInitializedis newly marked but has no local constructor, so the raise in_manager.pywill never emit the promised runtime warning. Please wrap inherited initializers as well (while retaining the existing guard for an already wrapped experimental base) and add a case that inherits from an unmarked base.
if "__init__" not in cls.__dict__ or getattr(original_init, _EXPERIMENTAL_WRAPPED_INIT_ATTR, False):
return cls
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/tasks/_decorator.py:1354
- Applying
@experimentalonly to the implementation does not mark this overloaded public API in the generated APIView surface: the updatedapi.mdemits only the two@overloaddeclarations fortaskand no experimental marker. This contradicts the PR's goal of exposing the experimental status in APIView-style documentation. Please mark the overload declarations in a parser-compatible way and regenerate the API snapshot.
This issue also appears on line 1783 of the same file.
@experimental
def task(
sdk/agentserver/azure-ai-agentserver-responses/azure/ai/agentserver/responses/_experimental.py:22
- This uses the core package's private module and dereferences private implementation state such as
_warning_cache, even though this package requiresazure-ai-agentserver-core>=2.0.0b10andexperimentalis publicly exported there. With no upper dependency bound, an otherwise compatible core release can refactor these internals and makeimport azure.ai.agentserver.responsesfail. Please consume the public export and avoid coupling production code/tests to core's private cache and constants.
try:
_core_experimental_module: Any | None = importlib.import_module("azure.ai.agentserver.core._experimental")
except ImportError:
_core_experimental_module = None
[Pilot] PR Pipeline Failure AnalysisA CI pipeline failed on this pull request. Here is an automated analysis of what went wrong and how to get the build green. What failedTwo groups of test failures across multiple platforms (Windows, Ubuntu, macOS) and Python versions (3.11–3.14):
Both are test failures in the Recommended next steps
Raw pipeline analysis (azsdk ci analyze)
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.
Suppressed comments (2)
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:80
- Classes without their own
__init__never receive a runtime warning because this guard returns before installing the wrapper.TaskManagerNotInitializedis newly marked in this PR but inheritsRuntimeError.__init__, so constructing or raising it emits no warning, contrary to the decorator contract. Only skip when an inherited initializer is already wrapped; otherwise wrap the inherited initializer as well.
if "__init__" not in cls.__dict__ or getattr(original_init, _EXPERIMENTAL_WRAPPED_INIT_ATTR, False):
return cls
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_experimental.py:150
- The cache check and insertion are separate operations, so two threads instantiating the same experimental API for the first time can both observe a miss and both log. That violates the promised once-per-process warning behavior. Guard the check/add operation with a shared lock, and apply the same synchronization to the responses fallback if that implementation is retained.
def _is_warning_cached(cache_key: str) -> bool:
if cache_key in _warning_cache:
return True
_warning_cache.add(cache_key)
return False
Description
Adds a shared Agent Server experimental decorator in azure-ai-agentserver-core and applies it only to scoped preview surfaces:
Protocol hosts such as ResponsesAgentServerHost and InvocationAgentServerHost are intentionally left unmarked.
When a customer uses a class marked with experimental , two things happen:
• The class docstring starts with a Sphinx note:
.. note:: This is an experimental class, and may change at any time...• This should show up in generated docs/APIView-style surfaces.
• When the customer instantiates the class, the wrapped init logs a warning through:
azure.ai.agentserver.core._experimental
• Example warning:
Class FoundryStateStore: This is an experimental class, and may change at any time...• The warning is cached, so the same class warning is emitted only once per process/session.
They can suppress runtime warnings with:
AZURE_AI_AGENTSERVER_DISABLE_EXPERIMENTAL_WARNING=trueIt does not change behavior, block usage, raise exceptions, or emit Python warnings.warn ; it only updates docs and logs a warning.
Validation