Conversation
There was a problem hiding this comment.
Pull request overview
Update Python connector/hosting packages to emit a User-Agent that can automatically include an environment-derived prefix (notably for Foundry hosted environments), so telemetry/SDK calls can be attributed correctly.
Changes:
- Introduce
agent_framework._telemetry.get_user_agent()with hosted-environment detection and prefixing logic. - Replace direct uses of
AGENT_FRAMEWORK_USER_AGENTacross multiple provider clients/tests withget_user_agent(). - Remove Foundry-hosting-specific
user_agent_prefix(...)scoping in hosting servers in favor of automatic environment detection.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| python/packages/core/agent_framework/_telemetry.py | Adds hosted-environment detection and get_user_agent() prefix composition. |
| python/packages/core/tests/core/test_telemetry.py | Updates tests to cover new prefix registration + hosted detection paths. |
| python/packages/purview/agent_framework_purview/_client.py | Switches HTTP User-Agent header to get_user_agent(). |
| python/packages/gemini/agent_framework_gemini/_chat_client.py | Switches Google client header to get_user_agent(). |
| python/packages/bedrock/agent_framework_bedrock/_embedding_client.py | Switches boto user_agent_extra to get_user_agent(). |
| python/packages/bedrock/agent_framework_bedrock/_chat_client.py | Switches boto user_agent_extra to get_user_agent(). |
| python/packages/azure-cosmos/agent_framework_azure_cosmos/_history_provider.py | Switches Cosmos user_agent_suffix to get_user_agent(). |
| python/packages/azure-cosmos/agent_framework_azure_cosmos/_checkpoint_storage.py | Switches Cosmos user_agent_suffix to get_user_agent(). |
| python/packages/azure-ai-search/agent_framework_azure_ai_search/_context_provider.py | Switches Azure Search clients’ user_agent to get_user_agent(). |
| python/packages/foundry/agent_framework_foundry/_memory_provider.py | Switches Foundry project client user_agent to get_user_agent(). |
| python/packages/foundry/agent_framework_foundry/_chat_client.py | Switches Foundry project client user_agent to get_user_agent(). |
| python/packages/foundry/agent_framework_foundry/_agent.py | Switches Foundry project client user_agent to get_user_agent(). |
| python/packages/foundry/tests/foundry/test_foundry_memory_provider.py | Updates assertions to expect get_user_agent(). |
| python/packages/foundry/tests/foundry/test_foundry_chat_client.py | Updates assertions to expect get_user_agent(). |
| python/packages/foundry_hosting/agent_framework_foundry_hosting/_invocations.py | Removes per-request UA prefix context usage. |
| python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py | Refactors handler flow and removes per-request UA prefix context usage. |
| python/packages/anthropic/agent_framework_anthropic/_vertex_client.py | Switches Anthropic client default headers to get_user_agent(). |
| python/packages/anthropic/agent_framework_anthropic/_foundry_client.py | Switches Anthropic client default headers to get_user_agent(). |
| python/packages/anthropic/agent_framework_anthropic/_bedrock_client.py | Switches Anthropic client default headers to get_user_agent(). |
| python/packages/anthropic/agent_framework_anthropic/_chat_client.py | Switches extra headers/default headers to get_user_agent(). |
| python/packages/anthropic/tests/test_anthropic_provider_clients.py | Updates test expectations to get_user_agent(). |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
python/packages/core/tests/core/test_telemetry.py:98
- These assertions expect the User-Agent to equal
AGENT_FRAMEWORK_USER_AGENT, butget_user_agent()can now prepend a hosted prefix based on process environment (e.g.,FOUNDRY_HOSTING_ENVIRONMENT). That makes this test suite environment-dependent. Patch/unsetFOUNDRY_HOSTING_ENVIRONMENT(and reset_hosted_env_detected) in these tests before callingprepend_agent_framework_to_user_agent()to keep them deterministic.
def test_prepend_to_empty_headers():
"""Test prepending to headers without User-Agent."""
headers = {"Content-Type": "application/json"}
result = prepend_agent_framework_to_user_agent(headers)
assert "User-Agent" in result
assert result["User-Agent"] == AGENT_FRAMEWORK_USER_AGENT
assert "Content-Type" in result
def test_prepend_to_empty_dict():
"""Test prepending to empty headers dict."""
headers: dict[str, str] = {}
result = prepend_agent_framework_to_user_agent(headers)
assert "User-Agent" in result
assert result["User-Agent"] == AGENT_FRAMEWORK_USER_AGENT
There was a problem hiding this comment.
Automated Code Review
Reviewers: 3 | Confidence: 88%
✓ Correctness
The PR replaces the static
AGENT_FRAMEWORK_USER_AGENTconstant with a dynamicget_user_agent()function that auto-detects hosted environments. The refactoring is clean and consistent across all client packages. One correctness issue:_detect_hosted_environment()catchesModuleNotFoundErrorfromfind_specbut not the broaderImportErrorthat CPython'sfind_speccan raise when parent packages exist but a sub-package has broken imports. Sinceget_user_agent()is now called on the critical path (client init), an uncaughtImportErrorwould prevent application startup.
✗ Security Reliability
The refactoring from ContextVar-based user_agent_prefix to global _user_agent_prefixes with lazy hosted-environment detection is generally sound. One reliability gap:
importlib.util.find_speccan raise a bareImportError(not justModuleNotFoundError) when a parent package exists but fails to import (e.g., broken native extension). The current except clause only catchesModuleNotFoundError, so anImportErrorwould propagate throughget_user_agent()and crash client initialization at every call site in this diff.
✗ Design Approach
I found two design-level problems. First, the telemetry change moves Foundry-hosting detection into the core
agent_frameworklayer by probing hosting-specific environment and SDK state, which is a leaky abstraction and changes semantics from an explicit hosting-layer tag to an inferred process-wide tag. Second,_responses.pyregresses cancellation handling: the new dispatcher returns inner streams without honoringcancellation_signal, so disconnected clients can still drive long-running agent/workflow work to completion.
Flagged Issues
- Response streaming in
_handle_responseno longer honors the providedcancellation_signal, so client disconnect or shutdown will continue consuming the underlying agent/workflow stream until completion, wasting resources.
Suggestions
- Widen the
find_specexception handler fromModuleNotFoundErrortoImportError(its parent class, so both are caught):except (ImportError, ValueError):. - Wrap the selected response stream with cancellation checks (or pass the signal through to the inner handlers) so hosting can stop work promptly when the request is cancelled.
Automated review by TaoChenOSU's agents
moonbox3
left a comment
There was a problem hiding this comment.
Automated Code Review
Reviewers: 3 | Confidence: 85%
✓ Correctness
This PR replaces the per-request
ContextVar-baseduser_agent_prefixcontext manager with a process-globalsetand lazy_detect_hosted_environment()detection, and updates all user-agent construction sites to callget_user_agent()instead of reading a static constant. The_handle_responsechange in_responses.py(from async generator to sync method returning AsyncIterable) is compatible with the framework'sCreateHandlerFntype and_normalize_handler_resultdispatch logic — the returnedAsyncGeneratorfrom_handle_workflow_agent/_handle_regular_agentcorrectly passes through theisinstance/iscoroutine/hasattrchecks and is consumed as anAsyncIterator. The unresolved review comment aboutcancellation_signalbeing silently dropped remains active — both_handle_regular_agentand_handle_workflow_agenthave had the parameter removed in this diff and the framework's orchestrator still passes the signal to the registered handler, but the handler no longer threads it through. No new correctness bugs were found beyond that pre-existing concern.
✗ Security Reliability
This PR replaces the per-request ContextVar-based
user_agent_prefixmechanism with a process-global mutablesetdetected lazily at startup, and removes theuser_agent_prefixcontext-manager from the hosting layers. Two reliability issues warrant attention: (1) the unresolved carry-over concern thatcancellation_signalis silently dropped in_handle_response— it is accepted in the signature but never forwarded to either inner handler — meaning client disconnects and server shutdowns will not stop the agent stream; (2) the new global_user_agent_prefixes: set[str]is mutated via_add_user_agent_prefixwhile concurrently iterated bysorted(_user_agent_prefixes)inget_user_agent(), which can raiseRuntimeError: Set changed size during iterationin multi-threaded server scenarios (this was the subject of a previously-resolved review comment that is reintroduced by this diff).
✗ Test Coverage
The new
_detect_hosted_environmenttests are well-structured and cover the main paths, buttest_detect_hosted_fallback_import_erroris environment-dependent: it patchesbuiltins.__import__without also mockingimportlib.util.find_spec, so in environments withoutazure.ai.agentserver.coreinstalled the function exits early and theexcept (ImportError, AttributeError)branch is never reached. Additionally, the foundry-package assertions useget_user_agent()on both sides of the equality check, which means they cannot catch a regression to the staticAGENT_FRAMEWORK_USER_AGENTconstant in non-hosted environments.
Automated review by moonbox3's agents
Previous commit incorrectly renamed the [1.1.1] header to [1.2.0], which wiped the historical 1.1.1 entries and wrongly attributed them to 1.2.0. This restores [1.1.1] to its origin/main content and adds a new [1.2.0] section above containing only the commits in python-1.1.1..HEAD: - microsoft#4238 functional workflow API - microsoft#5142 GitHub Copilot OpenTelemetry - microsoft#2403 A2A bridge support - microsoft#5070 oauth_consent_request events in Foundry clients - microsoft#5447 FoundryAgent hosted agent sessions - microsoft#5459 hosting server dependency upgrade + types - microsoft#5389 AG-UI reasoning/multimodal parsing fix - microsoft#5440 stop [TOOLBOXES] warning spam - microsoft#5455 user agent prefix fix Also corrects the [1.2.0] compare base to python-1.1.1 (not 1.1.0) and adds the missing [1.1.1] reference link.
* Bump Python package versions for 1.2.0 release Released tier bumps 1.1.1 -> 1.2.0 (core, openai, foundry, root) to reflect additive public APIs landed since 1.1.0: functional workflow API (#4238) and FunctionTool SKIP_PARSING sentinel (#5424). All beta packages stamped 1.0.0b260424, alpha packages 1.0.0a260424. All 26 non-core agent-framework-core floors raised to >=1.2.0,<2. CHANGELOG consolidates the never-tagged 1.1.1 entries with the post-merge additions into [1.2.0]. * Update CHANGELOG footer links for 1.2.0 Advance [Unreleased] comparison base from python-1.1.0 to python-1.2.0 and add a [1.2.0] reference link comparing python-1.1.0...python-1.2.0 so the heading links resolve correctly. * Fix CHANGELOG: restore [1.1.1] section and add proper [1.2.0] Previous commit incorrectly renamed the [1.1.1] header to [1.2.0], which wiped the historical 1.1.1 entries and wrongly attributed them to 1.2.0. This restores [1.1.1] to its origin/main content and adds a new [1.2.0] section above containing only the commits in python-1.1.1..HEAD: - #4238 functional workflow API - #5142 GitHub Copilot OpenTelemetry - #2403 A2A bridge support - #5070 oauth_consent_request events in Foundry clients - #5447 FoundryAgent hosted agent sessions - #5459 hosting server dependency upgrade + types - #5389 AG-UI reasoning/multimodal parsing fix - #5440 stop [TOOLBOXES] warning spam - #5455 user agent prefix fix Also corrects the [1.2.0] compare base to python-1.1.1 (not 1.1.0) and adds the missing [1.1.1] reference link.
Motivation and Context
Automatically add user agent prefix according to the environment the code runs in.
Contribution Checklist