Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Microsoft 365 Agents SDK for Python - Release Notes v1.4.0 (Unreleased)

## New Models & APIs
- **Regionalized UserTokenClient Support**: Added optional argument to `CloudAdapter` to configure Token Service endpoint used by `RestChannelServiceClientFactory` when creating `UserTokenClient` instances.

# Microsoft 365 Agents SDK for Python - Release Notes v1.3.0

**Release Date:** 2026-07-30
Expand Down
Empty file.
52 changes: 52 additions & 0 deletions dev/integration/tests/adapter/test_aiohttp_cloud_adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from typing import cast

import pytest

from microsoft_agents.activity import Activity, ConversationAccount
from microsoft_agents.hosting.aiohttp import CloudAdapter
from microsoft_agents.hosting.core import TurnContext
from microsoft_agents.hosting.core.authorization import ClaimsIdentity, Connections
from microsoft_agents.hosting.core.connector.client import UserTokenClient


@pytest.mark.asyncio
@pytest.mark.parametrize(
"token_service_endpoint",
[
"https://europe.api.botframework.com",
"https://unitedstates.api.botframework.com",
"https://india.api.botframework.com",
],
)
async def test_cloud_adapter_configures_user_token_client_endpoint(
token_service_endpoint: str,
):
adapter = CloudAdapter(
connection_manager=cast(Connections, object()),
channel_service_client_factory_options={
"token_service_endpoint": token_service_endpoint
},
)
identity = ClaimsIdentity({"aud": "test-app-id"}, True)
context = TurnContext(
adapter,
Activity(
type="message",
conversation=ConversationAccount(id="conversation-id"),
),
identity,
)

client = await adapter._channel_service_client_factory.create_user_token_client(
context,
identity,
use_anonymous=True,
)
assert isinstance(client, UserTokenClient)
try:
assert str(client.client._base_url) == f"{token_service_endpoint}/"
finally:
await client.close()
Comment thread
rodrigobr-msft marked this conversation as resolved.
52 changes: 52 additions & 0 deletions dev/integration/tests/adapter/test_fastapi_cloud_adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from typing import cast

import pytest

from microsoft_agents.activity import Activity, ConversationAccount
from microsoft_agents.hosting.core import TurnContext
from microsoft_agents.hosting.core.authorization import ClaimsIdentity, Connections
from microsoft_agents.hosting.core.connector.client import UserTokenClient
from microsoft_agents.hosting.fastapi import CloudAdapter


@pytest.mark.asyncio
@pytest.mark.parametrize(
"token_service_endpoint",
[
"https://europe.api.botframework.com",
"https://unitedstates.api.botframework.com",
"https://india.api.botframework.com",
],
)
async def test_cloud_adapter_configures_user_token_client_endpoint(
token_service_endpoint: str,
):
adapter = CloudAdapter(
connection_manager=cast(Connections, object()),
channel_service_client_factory_options={
"token_service_endpoint": token_service_endpoint
},
)
identity = ClaimsIdentity({"aud": "test-app-id"}, True)
context = TurnContext(
adapter,
Activity(
type="message",
conversation=ConversationAccount(id="conversation-id"),
),
identity,
)

client = await adapter._channel_service_client_factory.create_user_token_client(
context,
identity,
use_anonymous=True,
)
assert isinstance(client, UserTokenClient)
try:
assert str(client.client._base_url) == f"{token_service_endpoint}/"
finally:
await client.close()
Comment thread
rodrigobr-msft marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,20 @@ def __init__(
*,
connection_manager: Connections | None = None,
channel_service_client_factory: ChannelServiceClientFactoryBase | None = None,
channel_service_client_factory_options: dict | None = None,
):
"""
Initializes a new instance of the CloudAdapter class.

:param connection_manager: Optional connection manager for OAuth.
:param channel_service_client_factory: The factory to use to create the channel service client.
:param channel_service_client_factory: Factory for creating channel service clients.
:param channel_service_client_factory_options: Optional dictionary of options to pass to the channel service client factory
This is only used if channel_service_client_factory is not provided and connection_manager is provided.
"""
super().__init__(
connection_manager=connection_manager,
channel_service_client_factory=channel_service_client_factory,
channel_service_client_factory_options=channel_service_client_factory_options,
)

async def process(self, request: Request, agent: Agent) -> Optional[Response]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ def __init__(
*,
connection_manager: Connections | None = None,
channel_service_client_factory: ChannelServiceClientFactoryBase | None = None,
channel_service_client_factory_options: dict | None = None,
):
"""Initialize the HTTP adapter.

Args:
connection_manager: Optional connection manager for OAuth.
channel_service_client_factory: Factory for creating channel service clients.
:param connection_manager: Optional connection manager for OAuth.
:param channel_service_client_factory: Factory for creating channel service clients.
:param channel_service_client_factory_options: Optional dictionary of options to pass to the channel service client factory
This is only used if channel_service_client_factory is not provided and connection_manager is provided.
"""

async def on_turn_error(context: TurnContext, error: Exception):
Expand Down Expand Up @@ -67,7 +69,10 @@ async def on_turn_error(context: TurnContext, error: Exception):
raise ValueError(
"HttpAdapterBase.__init__: Either channel_service_client_factory or connection_manager must be provided."
)
factory = RestChannelServiceClientFactory(connection_manager)
factory = RestChannelServiceClientFactory(
connection_manager,
**(channel_service_client_factory_options or {}),
)
Comment thread
rodrigobr-msft marked this conversation as resolved.

super().__init__(factory)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,20 @@ def __init__(
*,
connection_manager: Connections | None = None,
channel_service_client_factory: ChannelServiceClientFactoryBase | None = None,
channel_service_client_factory_options: dict | None = None,
):
"""
Initializes a new instance of the CloudAdapter class.

:param connection_manager: Optional connection manager for OAuth.
:param channel_service_client_factory: The factory to use to create the channel service client.
:param channel_service_client_factory: Factory for creating channel service clients.
:param channel_service_client_factory_options: Optional dictionary of options to pass to the channel service client factory
This is only used if channel_service_client_factory is not provided and connection_manager is provided.
"""
super().__init__(
connection_manager=connection_manager,
channel_service_client_factory=channel_service_client_factory,
channel_service_client_factory_options=channel_service_client_factory_options,
)

async def process(self, request: Request, agent: Agent) -> Optional[Response]:
Expand Down
Loading