mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
[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:
committed by
GitHub
Unverified
parent
1ca43f9643
commit
d5e240b375
@@ -26,12 +26,11 @@ from agent_framework._tools import FunctionTool, ToolTypes
|
||||
from agent_framework._types import AgentRunInputs, normalize_tools
|
||||
from agent_framework.exceptions import AgentException
|
||||
from copilot import CopilotClient, CopilotSession
|
||||
from copilot.generated.session_events import SessionEvent, SessionEventType
|
||||
from copilot.generated.session_events import PermissionRequest, SessionEvent, SessionEventType
|
||||
from copilot.types import (
|
||||
CopilotClientOptions,
|
||||
MCPServerConfig,
|
||||
MessageOptions,
|
||||
PermissionRequest,
|
||||
PermissionRequestResult,
|
||||
ResumeSessionConfig,
|
||||
SessionConfig,
|
||||
@@ -529,7 +528,7 @@ class GitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
||||
"""Convert an FunctionTool to a Copilot SDK tool."""
|
||||
|
||||
async def handler(invocation: ToolInvocation) -> ToolResult:
|
||||
args = invocation.get("arguments", {})
|
||||
args: dict[str, Any] = invocation.arguments or {}
|
||||
try:
|
||||
if ai_func.input_model:
|
||||
args_instance = ai_func.input_model(**args)
|
||||
@@ -537,13 +536,13 @@ class GitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
||||
else:
|
||||
result = await ai_func.invoke(arguments=args)
|
||||
return ToolResult(
|
||||
textResultForLlm=str(result),
|
||||
resultType="success",
|
||||
text_result_for_llm=str(result),
|
||||
result_type="success",
|
||||
)
|
||||
except Exception as e:
|
||||
return ToolResult(
|
||||
textResultForLlm=f"Error: {e}",
|
||||
resultType="failure",
|
||||
text_result_for_llm=f"Error: {e}",
|
||||
result_type="failure",
|
||||
error=str(e),
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ name = "agent-framework-github-copilot"
|
||||
description = "GitHub Copilot integration for Microsoft Agent Framework."
|
||||
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
requires-python = ">=3.11"
|
||||
version = "1.0.0b260304"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://aka.ms/agent-framework"
|
||||
@@ -15,7 +15,6 @@ classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
@@ -24,7 +23,7 @@ classifiers = [
|
||||
]
|
||||
dependencies = [
|
||||
"agent-framework-core>=1.0.0rc3",
|
||||
"github-copilot-sdk>=0.1.0",
|
||||
"github-copilot-sdk>=0.1.32",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
@@ -66,7 +65,7 @@ include = ["agent_framework_github_copilot"]
|
||||
[tool.mypy]
|
||||
plugins = ['pydantic.mypy']
|
||||
strict = true
|
||||
python_version = "3.10"
|
||||
python_version = "3.11"
|
||||
ignore_missing_imports = true
|
||||
disallow_untyped_defs = true
|
||||
no_implicit_optional = true
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user