Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@

from ._acquire_token import acquire_token

# aiohttp caps how much it buffers while reading a single response line; at the default
# read_bufsize this limit is 512 KB, which Copilot Studio can exceed for large activities
# (for example, sizeable connector payloads), raising ``aiohttp.http_exceptions.LineTooLong``.
# Copilot Studio streams each activity as one SSE ``data:`` line, so we raise the buffer to
# lift that per-line limit well above the default. See https://github.com/microsoft/agent-framework/issues/7257.
DEFAULT_READ_BUFSIZE = 1024 * 1024


class CopilotStudioSettings(TypedDict, total=False):
"""Copilot Studio model settings.
Expand Down Expand Up @@ -70,6 +77,7 @@ def __init__(
cloud: PowerPlatformCloud | None = None,
agent_type: AgentType | None = None,
custom_power_platform_cloud: str | None = None,
client_session_settings: dict[str, Any] | None = None,
username: str | None = None,
token_cache: Any | None = None,
scopes: list[str] | None = None,
Expand Down Expand Up @@ -105,6 +113,12 @@ def __init__(
agent_type: The type of Copilot Studio agent (Copilot, Agent, etc.).
custom_power_platform_cloud: Custom Power Platform cloud URL if using
a custom environment.
client_session_settings: Optional keyword arguments forwarded to the underlying
aiohttp ``ClientSession`` used by the created client. Only applied when the agent
builds the client internally (ignored when ``client`` or ``settings`` is provided).
Defaults to ``read_bufsize`` of ``DEFAULT_READ_BUFSIZE`` (1 MiB) so large Copilot
Studio activities do not trigger ``aiohttp`` ``LineTooLong`` errors; override
``read_bufsize`` here to raise or lower that limit.
username: Optional username for token acquisition.
token_cache: Optional token cache for storing authentication tokens.
scopes: Optional list of authentication scopes. Defaults to Power Platform
Expand Down Expand Up @@ -150,12 +164,15 @@ def __init__(
"or 'COPILOTSTUDIOAGENT__SCHEMANAME' environment variable."
)

resolved_session_settings: dict[str, Any] = dict(client_session_settings or {})
resolved_session_settings.setdefault("read_bufsize", DEFAULT_READ_BUFSIZE)
settings = ConnectionSettings(
environment_id=resolved_environment_id,
agent_identifier=resolved_agent_identifier,
cloud=cloud,
copilot_agent_type=agent_type,
custom_power_platform_cloud=custom_power_platform_cloud,
client_session_settings=resolved_session_settings,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only fixes the internally-built-client path. The package's own explicit configuration examples still create ConnectionSettings(...) and CopilotStudioAgent(client=client) without client_session_settings (python/packages/copilotstudio/README.md:63-73, python/samples/02-agents/providers/copilotstudio/README.md:76-85), so a >512 KB activity will still fail with LineTooLong there. Please either apply the same default when settings is supplied (and client is not), or update the documented explicit path and add coverage for it.

)

if not token:
Expand Down
2 changes: 1 addition & 1 deletion python/packages/copilotstudio/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ classifiers = [
]
dependencies = [
"agent-framework-core>=1.11.0,<2",
"microsoft-agents-copilotstudio-client>=0.3.1,<0.3.2",
"microsoft-agents-copilotstudio-client>=1.2.0,<2",
]

[tool.uv]
Expand Down
34 changes: 34 additions & 0 deletions python/packages/copilotstudio/tests/test_copilot_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from microsoft_agents.copilotstudio.client import CopilotClient

from agent_framework_copilotstudio import CopilotStudioAgent
from agent_framework_copilotstudio._agent import DEFAULT_READ_BUFSIZE


def create_async_generator(items: list[Any]) -> Any:
Expand Down Expand Up @@ -98,6 +99,39 @@ def test_init_with_client(self, mock_copilot_client: MagicMock) -> None:
assert agent.client == mock_copilot_client
assert agent.id is not None

def test_init_applies_default_read_bufsize(self) -> None:
"""The internally built client raises aiohttp's per-line limit to avoid LineTooLong (issue #7257)."""
agent = CopilotStudioAgent(
environment_id="env-id",
agent_identifier="agent-id",
token="fake-token",
)
assert agent.client.settings.client_session_settings["read_bufsize"] == DEFAULT_READ_BUFSIZE

def test_init_respects_custom_client_session_settings(self) -> None:
"""User-provided client_session_settings are honored, and other keys are preserved."""
agent = CopilotStudioAgent(
environment_id="env-id",
agent_identifier="agent-id",
token="fake-token",
client_session_settings={"read_bufsize": 123, "trust_env": True},
)
session_settings = agent.client.settings.client_session_settings
assert session_settings["read_bufsize"] == 123
assert session_settings["trust_env"] is True

def test_init_injects_default_read_bufsize_into_partial_settings(self) -> None:
"""When client_session_settings omits read_bufsize, the default is added without dropping other keys."""
agent = CopilotStudioAgent(
environment_id="env-id",
agent_identifier="agent-id",
token="fake-token",
client_session_settings={"trust_env": True},
)
session_settings = agent.client.settings.client_session_settings
assert session_settings["read_bufsize"] == DEFAULT_READ_BUFSIZE
assert session_settings["trust_env"] is True

@patch("agent_framework_copilotstudio._acquire_token.acquire_token")
def test_init_empty_environment_id(self, mock_acquire_token: MagicMock) -> None:
mock_acquire_token.return_value = "fake-token"
Expand Down
Loading
Loading