[BREAKING] Python: Update github-copilot-sdk integration to use ToolInvocation/ToolResult types (#4551)

* Update github_copilot package for github-copilot-sdk>=0.1.32 (#4549)

- Update requires-python from >=3.10 to >=3.11
- Remove Python 3.10 classifier
- Update mypy python_version to 3.11
- Update dependency to github-copilot-sdk>=0.1.32
- Fix ToolResult API: use snake_case kwargs (text_result_for_llm,
  result_type) instead of camelCase (textResultForLlm, resultType)
- Update test assertions to use attribute access on ToolResult
- Add ToolResult type assertions to tool handler tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix tests to use ToolInvocation dataclass instead of plain dict (#4549)

Update test_github_copilot_agent.py to pass ToolInvocation objects to tool
handlers instead of plain dicts, matching the github-copilot-sdk>=0.1.32 API
where ToolInvocation is a dataclass with an .arguments attribute.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add regression tests for ToolInvocation contract (#4549)

Add tests to lock in the new ToolInvocation-based calling convention:
- test_tool_handler_rejects_raw_dict_invocation: verifies passing a raw
  dict (old calling convention) raises TypeError/AttributeError
- test_tool_handler_with_empty_arguments: verifies ToolInvocation with
  empty arguments works correctly for no-arg tools

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Revert requires-python to >=3.10 to avoid breaking CI (#4549)

The repo CI runs with Python 3.10 (uv sync --all-packages) and all other
packages require >=3.10. Raising this package to >=3.11 would break the
shared install flow. The SDK dependency version constraint (>=0.1.32) will
enforce any Python version requirement from the SDK itself.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix min Python version for github_copilot package to >=3.11

github-copilot-sdk>=0.1.32 requires Python>=3.11, which conflicts
with the package's declared >=3.10 minimum, breaking uv sync.

* Bump py version for GH workflows to 3.11, exclude GHCP sdk from 3.10 items

* Fix uv command

* Fixes

* Update samples

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Evan Mattson
2026-03-09 18:57:51 +09:00
committed by GitHub
Unverified
parent 1ca43f9643
commit d5e240b375
17 changed files with 169 additions and 984 deletions
@@ -16,6 +16,7 @@ from agent_framework import (
)
from agent_framework.exceptions import AgentException
from copilot.generated.session_events import Data, SessionEvent, SessionEventType
from copilot.types import ToolInvocation, ToolResult
from agent_framework_github_copilot import GitHubCopilotAgent, GitHubCopilotOptions
@@ -745,10 +746,11 @@ class TestGitHubCopilotAgentToolConversion:
config = call_args[0][0]
copilot_tool = config["tools"][0]
result = await copilot_tool.handler({"arguments": {"arg": "test"}})
result = await copilot_tool.handler(ToolInvocation(arguments={"arg": "test"}))
assert result["resultType"] == "success"
assert result["textResultForLlm"] == "Result: test"
assert isinstance(result, ToolResult)
assert result.result_type == "success"
assert result.text_result_for_llm == "Result: test"
async def test_tool_handler_returns_failure_result_on_error(
self,
@@ -770,11 +772,61 @@ class TestGitHubCopilotAgentToolConversion:
config = call_args[0][0]
copilot_tool = config["tools"][0]
result = await copilot_tool.handler({"arguments": {"arg": "test"}})
result = await copilot_tool.handler(ToolInvocation(arguments={"arg": "test"}))
assert result["resultType"] == "failure"
assert "Something went wrong" in result["textResultForLlm"]
assert "Something went wrong" in result["error"]
assert isinstance(result, ToolResult)
assert result.result_type == "failure"
assert "Something went wrong" in result.text_result_for_llm
assert "Something went wrong" in result.error
async def test_tool_handler_rejects_raw_dict_invocation(
self,
mock_client: MagicMock,
mock_session: MagicMock,
) -> None:
"""Test that tool handler raises TypeError when called with a raw dict instead of ToolInvocation."""
def my_tool(arg: str) -> str:
"""A test tool."""
return f"Result: {arg}"
agent = GitHubCopilotAgent(client=mock_client, tools=[my_tool])
await agent.start()
await agent._get_or_create_session(AgentSession()) # type: ignore
call_args = mock_client.create_session.call_args
config = call_args[0][0]
copilot_tool = config["tools"][0]
with pytest.raises((TypeError, AttributeError)):
await copilot_tool.handler({"arguments": {"arg": "test"}})
async def test_tool_handler_with_empty_arguments(
self,
mock_client: MagicMock,
mock_session: MagicMock,
) -> None:
"""Test that tool handler handles ToolInvocation with empty arguments."""
def no_args_tool() -> str:
"""A tool with no arguments."""
return "no args result"
agent = GitHubCopilotAgent(client=mock_client, tools=[no_args_tool])
await agent.start()
await agent._get_or_create_session(AgentSession()) # type: ignore
call_args = mock_client.create_session.call_args
config = call_args[0][0]
copilot_tool = config["tools"][0]
result = await copilot_tool.handler(ToolInvocation(arguments={}))
assert isinstance(result, ToolResult)
assert result.result_type == "success"
assert result.text_result_for_llm == "no args result"
def test_copilot_tool_passthrough(
self,
@@ -784,7 +836,7 @@ class TestGitHubCopilotAgentToolConversion:
from copilot.types import Tool as CopilotTool
async def tool_handler(invocation: Any) -> Any:
return {"textResultForLlm": "result", "resultType": "success"}
return {"text_result_for_llm": "result", "result_type": "success"}
copilot_tool = CopilotTool(
name="direct_tool",
@@ -813,7 +865,7 @@ class TestGitHubCopilotAgentToolConversion:
return arg
async def tool_handler(invocation: Any) -> Any:
return {"textResultForLlm": "result", "resultType": "success"}
return {"text_result_for_llm": "result", "result_type": "success"}
copilot_tool = CopilotTool(
name="direct_tool",