Skip to content

Python: Support response_format in GitHubCopilotAgent - #9810

Open
Dineshsuriya D (droideronline) wants to merge 2 commits into
microsoft:mainfrom
droideronline:feature/github-copilot-structured-output
Open

Dineshsuriya D (droideronline) wants to merge 2 commits into
microsoft:mainfrom
droideronline:feature/github-copilot-structured-output

Conversation

@droideronline

Copy link
Copy Markdown
Contributor

Motivation & Context

GitHubCopilotAgent ignored response_format, so structured output was unavailable even though the Copilot SDK supports it (github/copilot-sdk#2590, in SDK v1.0.15 and later, within the existing >=1.0.16 pin).

Description & Review Guide

  • What are the major changes?
    • Added response_format (Pydantic model type or JSON Schema mapping) to GitHubCopilotOptions.
    • The agent forwards it as response_schema to send_and_wait (non-streaming) and send (streaming). Per-run options override default_options.
    • response_format is stripped from the create_session kwargs so it no longer reaches the SDK session factory.
    • AgentResponse.value is parsed from the final text: via response_format= on the non-streaming response and output_format_type= in the streaming finalizer.
  • What is the impact of these changes? Opt-in. Without response_format the SDK receives response_schema=None, which is the existing behavior.
  • What do you want reviewers to focus on? Streaming still yields text deltas, with the value parsed once the stream completes, matching the SDK behavior.

Related Issue

Fixes #9809

AI Assistance

  • No material AI assistance was used.
  • This is an AI-assisted contribution. I reviewed, understood, and verified all submitted content and accept responsibility for it.

AI assistance details: implementation and tests.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR links to an agreed issue with no competing open PR, or the Related Issue section documents a trivial-change or repository-automation exception.
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

Forward response_format as response_schema to the Copilot SDK send and
send_and_wait calls, keep it out of create_session kwargs, and parse
AgentResponse.value for streaming and non-streaming runs.

Copilot AI left a comment

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.

🟡 Changes recommended

Structured-output parsing can incorrectly use trailing subagent text instead of the root answer, while mapping schemas and aliased models can trigger serialization or validation failures.

3 open issues
What changed in this PR

Adds opt-in structured output support to GitHubCopilotAgent, bringing it closer to other Agent Framework providers and addressing #9809.

Changes:

  • Accepts Pydantic models or JSON Schema mappings through response_format.
  • Forwards schemas to streaming and non-streaming SDK calls, excludes them from session creation, and enables AgentResponse.value parsing.
  • Adds tests for schema forwarding, default options, unstructured requests, and streaming results.
File Description
python/​packages/​github_copilot/​tests/​test_github_copilot_agent.py Adds structured-output tests for both response modes.
python/​packages/​github_copilot/​agent_framework_github_copilot/​_agent.py Adds the option, SDK forwarding, and structured response parsing.

Review effort: Balanced

def _finalize(updates: Sequence[AgentResponseUpdate]) -> AgentResponse:
return AgentResponse.from_updates(updates)
response_format = (options or {}).get("response_format") or self._default_options.get("response_format")
return AgentResponse.from_updates(updates, output_format_type=response_format)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed. The streaming path now accumulates deltas from root events only (no agent_id) and parses the value from the last root message; sub-agent text still streams. Non-streaming uses the SDK's send_and_wait, which already ignores sub-agent events. Regression test added.

response_event = await copilot_session.send_and_wait(
prompt,
attachments=attachments,
response_schema=response_format,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed. Mapping schemas are recursively converted to plain dicts before both send_and_wait and send; Pydantic model classes are passed through unchanged. Test added with MappingProxyType.

finish_reason=cast(Any, finish_reason),
usage_details=usage_details,
additional_properties={"model": model} if model else None,
response_format=response_format,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed. The agent now parses with by_alias=True, by_name=False, matching the SDK's typed parser. If that parse fails, the response falls back to core lazy parsing so .value raises as before. Test added for an aliased model.

Parse the streaming structured value from root-agent text only, convert
mapping schemas to plain dicts before they reach the SDK, and validate
Pydantic models by alias to match the schema the SDK requests.

This branch was successfully deployed

1 active deployment
github-app-auth — 10fc98ef Deployed Oct 29, 2026 by droideronline via add_label #26498
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Feature]: Support response_format (structured output) in GitHubCopilotAgent

2 participants