feat: add azure-ai-agentserver-github package (Copilot SDK adapter)#45894
feat: add azure-ai-agentserver-github package (Copilot SDK adapter)#45894jodeklotzms wants to merge 3 commits intomainfrom
Conversation
Initial package structure for the GitHub Copilot SDK adapter for Azure AI Agent Server. Follows the established pattern from azure-ai-agentserver-langgraph. Package exports: - GitHubCopilotAdapter: convenience class with skill discovery + history bootstrap - CopilotAdapter: core adapter (BYOK auth, n:n event mapping, Tool ACL, OTel) - ToolAcl: YAML-based tool permission system Validated: syntax check + import resolution against agentserver-core 1.0.0b14 and github-copilot-sdk 0.1.33rc4. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Minimal test agent that imports GitHubCopilotAdapter from the package
(not vendored code), deploys to ADC, and invokes. Proves the package
works as a pip-installable dependency.
- test_agent/main.py: 5-line entry point using from_project(".")
- deploy.py: stages package source + agent, builds via ACR Tasks
- invoke.py: REST invocation via az CLI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace @self.app.on_event("startup") with direct filter registration
(Starlette 1.0 removed on_event in favor of lifespan)
- Fix logs.py PROJECT_ROOT to find .env at package root
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@jodeklotzms please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
Contributor License AgreementContribution License AgreementThis Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
|
There was a problem hiding this comment.
Pull request overview
Adds a new azure-ai-agentserver-github package that adapts the GitHub Copilot SDK session/event model to Azure AI Agent Server (Foundry Responses API / RAPI), plus local integration deploy/invoke tooling and a minimal hosted-agent test project.
Changes:
- Introduces
CopilotAdapter/GitHubCopilotAdapter, request/response conversion utilities, and a YAML-basedToolAcl. - Adds package scaffolding (pyproject, versioning, manifest, README, samples README).
- Adds integration test agent assets + scripts to deploy/invoke/stream logs against Foundry ADC.
Reviewed changes
Copilot reviewed 25 out of 27 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| sdk/agentserver/azure-ai-agentserver-github/tests/integration/test_agent/skills/hello/SKILL.md | Minimal “hello” skill definition for end-to-end validation. |
| sdk/agentserver/azure-ai-agentserver-github/tests/integration/test_agent/requirements.txt | Dependencies for the containerized test agent. |
| sdk/agentserver/azure-ai-agentserver-github/tests/integration/test_agent/main.py | Minimal agent entrypoint using GitHubCopilotAdapter. |
| sdk/agentserver/azure-ai-agentserver-github/tests/integration/test_agent/Dockerfile | Container image for deploying the test agent (installs staged package). |
| sdk/agentserver/azure-ai-agentserver-github/tests/integration/test_agent/AGENTS.md | Agent instructions used by the test agent. |
| sdk/agentserver/azure-ai-agentserver-github/tests/integration/logs.py | Helper script to stream hosted agent console logs. |
| sdk/agentserver/azure-ai-agentserver-github/tests/integration/invoke.py | Helper script to invoke the deployed agent via Foundry Responses API. |
| sdk/agentserver/azure-ai-agentserver-github/tests/integration/deploy.py | Helper script to stage/build/push image via ACR Tasks and create/update agent. |
| sdk/agentserver/azure-ai-agentserver-github/tests/integration/README.md | Documentation for running integration deploy/invoke workflow. |
| sdk/agentserver/azure-ai-agentserver-github/tests/init.py | Marks tests package. |
| sdk/agentserver/azure-ai-agentserver-github/samples/README.md | Minimal quick-start sample snippet. |
| sdk/agentserver/azure-ai-agentserver-github/pyproject.toml | Package metadata, dependencies, ruff config, build settings. |
| sdk/agentserver/azure-ai-agentserver-github/dev_requirements.txt | Dev dependencies for local iteration. |
| sdk/agentserver/azure-ai-agentserver-github/azure/ai/agentserver/github/py.typed | Declares typing support. |
| sdk/agentserver/azure-ai-agentserver-github/azure/ai/agentserver/github/_version.py | Package version constant. |
| sdk/agentserver/azure-ai-agentserver-github/azure/ai/agentserver/github/_tool_acl.py | YAML ACL parser/evaluator for Copilot permission requests. |
| sdk/agentserver/azure-ai-agentserver-github/azure/ai/agentserver/github/_copilot_response_converter.py | Converts Copilot session events / text into RAPI response/event shapes. |
| sdk/agentserver/azure-ai-agentserver-github/azure/ai/agentserver/github/_copilot_request_converter.py | Converts Foundry request input + attachments into Copilot prompt/attachments. |
| sdk/agentserver/azure-ai-agentserver-github/azure/ai/agentserver/github/_copilot_adapter.py | Core adapter implementation (session mgmt, streaming/non-streaming, ACL). |
| sdk/agentserver/azure-ai-agentserver-github/azure/ai/agentserver/github/init.py | Public exports + package version. |
| sdk/agentserver/azure-ai-agentserver-github/azure/ai/agentserver/init.py | Namespace package setup. |
| sdk/agentserver/azure-ai-agentserver-github/azure/ai/init.py | Namespace package setup. |
| sdk/agentserver/azure-ai-agentserver-github/azure/init.py | Namespace package setup. |
| sdk/agentserver/azure-ai-agentserver-github/README.md | Package README with usage + env vars. |
| sdk/agentserver/azure-ai-agentserver-github/MANIFEST.in | sdist content rules. |
| sdk/agentserver/azure-ai-agentserver-github/LICENSE | MIT license text. |
| sdk/agentserver/azure-ai-agentserver-github/CHANGELOG.md | Initial changelog for 1.0.0b1. |
| def convert_attachments(self) -> ConvertedAttachments: | ||
| """Extract file and image attachments from the request's content parts. | ||
|
|
||
| Scans all messages in ``input`` for ``input_file`` and ``input_image`` | ||
| content parts and materialises their data onto disk as temporary files, | ||
| returning :class:`ConvertedAttachments` with Copilot SDK | ||
| ``FileAttachment`` dicts and a list of temp paths to clean up. | ||
|
|
||
| Supported cases: |
There was a problem hiding this comment.
The attachment + prompt conversion logic is non-trivial (multiple input shapes, data-URI decoding, temp file lifecycle) but there are no unit tests added alongside it. Other agentserver adapters do have unit tests (e.g., azure-ai-agentserver-langgraph/tests/unit_tests/test_langgraph_request_converter.py). Adding unit tests for convert() and convert_attachments() would help prevent regressions.
| if not self._project_endpoint: | ||
| return None | ||
| try: | ||
| openai_client = await self._create_openai_client() | ||
| items = [] |
There was a problem hiding this comment.
GitHubCopilotAdapter._load_conversation_history references self._project_endpoint and self._create_openai_client(), but FoundryCBAgent in azure-ai-agentserver-core doesn’t define these members. As written this will raise AttributeError on first cold-start bootstrap attempt. Please either implement history loading using an explicit client available in this package (or via context/env), or remove/guard this feature until the base class provides the needed API.
| import dataclasses | ||
| import json | ||
| import logging as _logging | ||
| import os | ||
| import pathlib | ||
| import time | ||
| import uuid |
There was a problem hiding this comment.
Several imports in this file appear unused (e.g., dataclasses, json, uuid). With ruff selecting F rules, this will fail lint (F401). Please remove unused imports or use them if they’re intended for upcoming functionality.
| import dataclasses | |
| import json | |
| import logging as _logging | |
| import os | |
| import pathlib | |
| import time | |
| import uuid | |
| import logging as _logging | |
| import os | |
| import pathlib | |
| import time |
| from copilot import CopilotClient | ||
| from copilot.generated.session_events import SessionEventType | ||
| from copilot.types import PermissionRequestResult, ProviderConfig | ||
| from opentelemetry import context as otel_context, trace |
There was a problem hiding this comment.
otel_context/trace are imported but not used in this module. Since ruff enables Pyflakes (F), this should be cleaned up to avoid F401 lint failures.
| from opentelemetry import context as otel_context, trace |
| from azure.ai.agentserver.core.server.common.agent_run_context import AgentRunContext | ||
|
|
||
| from ._copilot_request_converter import ConvertedAttachments, CopilotRequestConverter | ||
| from ._copilot_response_converter import CopilotResponseConverter, CopilotStreamingResponseConverter |
There was a problem hiding this comment.
CopilotStreamingResponseConverter is imported but never referenced in this module, which will trigger ruff/Pyflakes F401. Either remove the import or switch _run_streaming over to use this converter to avoid duplicating streaming event logic.
| from ._copilot_response_converter import CopilotResponseConverter, CopilotStreamingResponseConverter | |
| from ._copilot_response_converter import CopilotResponseConverter |
| ResponseContentPartAddedEvent, | ||
| ResponseContentPartDoneEvent, | ||
| ResponseCreatedEvent, | ||
| ResponseError, |
There was a problem hiding this comment.
ResponseError is imported but not used in this module. With ruff’s F rules enabled, this will raise F401 unless removed or referenced.
| ResponseError, |
| def main(): | ||
| parser = argparse.ArgumentParser(description="Stream container console logs from a deployed agent") | ||
| parser.add_argument("--name", required=True, help="Agent name") | ||
| parser.add_argument("--session", required=True, help="Session ID (from invoke output)") | ||
| args = parser.parse_args() |
There was a problem hiding this comment.
The logs.py docstring claims --session is optional and shows python scripts/logs.py ..., but --session is marked required=True and the file lives at tests/integration/logs.py. Either update the docs to match the current CLI, or implement the “use last session” behavior and fix the invocation path in the usage text.
| token = self._credential.get_token(_COGNITIVE_SERVICES_SCOPE).token | ||
| self._session_config["provider"]["bearer_token"] = token |
There was a problem hiding this comment.
In BYOK (Managed Identity) mode, _session_config['provider'] is built as a ProviderConfig instance, but _refresh_token_if_needed treats it like a dict (['bearer_token']). This will raise at runtime and prevent token refresh. Update the code to refresh the token by mutating/replacing the ProviderConfig object (or store provider as a plain dict consistently).
| token = self._credential.get_token(_COGNITIVE_SERVICES_SCOPE).token | |
| self._session_config["provider"]["bearer_token"] = token | |
| token = self._credential.get_token(_COGNITIVE_SERVICES_SCOPE).token | |
| provider = self._session_config.get("provider") | |
| # In BYOK (Managed Identity) mode, provider may be a ProviderConfig instance. | |
| # In other modes, it may be a plain dict. Support both representations. | |
| if isinstance(provider, ProviderConfig): | |
| # Replace the ProviderConfig with an updated copy carrying the new bearer_token. | |
| try: | |
| updated_provider = dataclasses.replace(provider, bearer_token=token) | |
| except TypeError: | |
| # Fallback in case ProviderConfig is not a dataclass or bearer_token is not a field. | |
| # Mutate the attribute directly if it exists. | |
| if hasattr(provider, "bearer_token"): | |
| provider.bearer_token = token | |
| updated_provider = provider | |
| else: | |
| updated_provider = provider | |
| self._session_config["provider"] = updated_provider | |
| elif isinstance(provider, dict): | |
| provider["bearer_token"] = token |
| import os | ||
| import re | ||
| from pathlib import Path | ||
| from typing import Any, Dict, Iterable, List, Literal, Optional |
There was a problem hiding this comment.
typing.Iterable is imported but unused, which should fail ruff (F401). Please remove the unused import.
| from typing import Any, Dict, Iterable, List, Literal, Optional | |
| from typing import Any, Dict, List, Literal, Optional |
Summary
Initial scaffold of the
azure-ai-agentserver-githubpackage — a GitHub Copilot SDK adapter for Azure AI Agent Server. Bridges the Copilot SDK event model to the Foundry Responses API (RAPI) protocol.SDK spec approved Mar 19 at
coreai-microsoft/foundrysdk_specs.Package exports
GitHubCopilotAdapter: convenience class with skill discovery + conversation history bootstrapCopilotAdapter: core adapter (BYOK auth, n:n event mapping, Tool ACL, OTel)ToolAcl: YAML-based tool permission systemValidated
pkg-test-02, agent responds correctly)azure-ai-agentserver-langgraphpackage patternagentserver-core1.0.0b14-b17 +github-copilot-sdk0.2.0Hero code sample
Test plan
🤖 Generated with Claude Code