Skip to content

Python: Preserve refusals as marked text - #7992

Draft
Eduard van Valkenburg (eavanvalkenburg) wants to merge 4 commits into
microsoft:mainfrom
eavanvalkenburg:python-refusal-content
Draft

Python: Preserve refusals as marked text#7992
Eduard van Valkenburg (eavanvalkenburg) wants to merge 4 commits into
microsoft:mainfrom
eavanvalkenburg:python-refusal-content

Conversation

@eavanvalkenburg

@eavanvalkenburg Eduard van Valkenburg (eavanvalkenburg) commented Sep 1, 2026

Copy link
Copy Markdown
Member

Motivation & Context

Native provider refusals are currently flattened into ordinary Python text content. That keeps refusal wording visible, but loses the provider semantic across durable history, native provider replay, Responses-compatible hosting, and DevUI conversation/UI state. This change preserves that signal without adding a stable content discriminator before its broader usage and semantics are understood.

Description & Review Guide

  • What are the major changes? Refusals remain ordinary Content(type="text") and carry the experimental, serializable additional_properties["model_output_kind"] == "refusal" marker. Structured-output extraction skips marked refusal text, while normal message/response text and coalescing remain unchanged. OpenAI Responses, Chat Completions, Foundry hosting, Hosting Responses, and DevUI inspect the marker to reconstruct native refusal fields, parts, and streaming events. ADR 0039 records the metadata choice and the Microsoft.Extensions.AI ErrorContent(ErrorCode="Refusal") alternative.
  • What is the impact of these changes? The change preserves existing visible-text behavior and requires no refusal-specific changes in providers or protocols that already handle text. Marked text round-trips through serialization and history; older runtimes still display and retain the text/additional properties but do not reconstruct native refusal wire fields until upgraded.
  • What do you want reviewers to focus on? Please review the experimental marker shape, the structured-output exclusion, and native refusal reconstruction across OpenAI/Responses hosting and DevUI boundaries.

Related Issue

N/A — follow-up from the Responses conversion consistency sweep

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 is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • 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.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings September 1, 2026 13:39
@agent-framework-automation agent-framework-automation Bot added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python labels Sep 1, 2026

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.

Pull request overview

Adds durable typed refusal content across Python core, providers, hosting, and DevUI.

Changes:

  • Introduces Content.from_refusal(...) with serialization, aggregation, and display support.
  • Preserves native refusal shapes for OpenAI-compatible paths and text fallbacks elsewhere.
  • Adds cross-package tests, UI support, documentation, and ADR 0036.

Reviewed changes

Copilot reviewed 49 out of 51 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
python/packages/openai/tests/openai/test_openai_chat_completion_client.py Tests Chat Completions refusal conversion.
python/packages/openai/tests/openai/test_openai_chat_client.py Tests Responses refusal conversion.
python/packages/openai/AGENTS.md Documents OpenAI refusal handling.
python/packages/openai/agent_framework_openai/_chat_completion_client.py Parses and replays Chat Completions refusals.
python/packages/openai/agent_framework_openai/_chat_client.py Preserves Responses refusal parts and deltas.
python/packages/hosting-responses/tests/hosting_responses/test_parsing.py Tests hosted refusal parsing and streaming.
python/packages/hosting-responses/README.md Documents refusal round-tripping.
python/packages/hosting-responses/agent_framework_hosting_responses/_parsing.py Converts hosted refusal payloads and events.
python/packages/hosting-a2a/tests/hosting_a2a/test_conversion.py Tests refusal text fallback.
python/packages/hosting-a2a/agent_framework_hosting_a2a/_conversion.py Maps refusals to A2A text.
python/packages/gemini/tests/test_gemini_client.py Tests Gemini refusal fallback.
python/packages/gemini/agent_framework_gemini/_chat_client.py Maps refusals to Gemini text parts.
python/packages/foundry_hosting/tests/test_responses.py Tests Foundry refusal events and parsing.
python/packages/foundry_hosting/README.md Documents Foundry refusal support.
python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py Streams and converts native refusal content.
python/packages/devui/tests/devui/test_mapper.py Tests refusal event mapping.
python/packages/devui/tests/devui/test_conversations.py Tests refusal conversation persistence.
python/packages/devui/frontend/src/types/openai.ts Adds OpenAI refusal event and content types.
python/packages/devui/frontend/src/types/agent-framework.ts Adds framework refusal types and guard.
python/packages/devui/frontend/src/stores/devuiStore.ts Handles refusal deltas in debug state.
python/packages/devui/frontend/src/services/streaming-state.ts Persists streaming refusal previews.
python/packages/devui/frontend/src/services/api.ts Tracks refusal deltas during streaming.
python/packages/devui/frontend/src/components/layout/debug-panel.tsx Displays refusal stream events.
python/packages/devui/frontend/src/components/features/workflow/workflow-view.tsx Renders workflow refusal output.
python/packages/devui/frontend/src/components/features/workflow/execution-timeline.tsx Includes refusal text in timelines.
python/packages/devui/frontend/src/components/features/agent/message-renderers/OpenAIContentRenderer.tsx Renders refusal message parts.
python/packages/devui/frontend/src/components/features/agent/agent-view.tsx Handles streamed and restored refusals.
python/packages/devui/AGENTS.md Documents DevUI refusal mapping.
python/packages/devui/agent_framework_devui/models/__init__.py Exports refusal protocol models.
python/packages/devui/agent_framework_devui/_mapper.py Emits native refusal stream events.
python/packages/devui/agent_framework_devui/_conversations.py Stores and retrieves refusal parts.
python/packages/core/tests/core/test_types.py Tests refusal semantics and aggregation.
python/packages/core/tests/core/test_sessions.py Tests serialized refusal history.
python/packages/core/AGENTS.md Documents refusal content support.
python/packages/core/agent_framework/_types.py Defines the core refusal representation.
python/packages/chatkit/tests/test_streaming.py Tests ChatKit refusal fallback.
python/packages/chatkit/agent_framework_chatkit/_streaming.py Streams refusals as ChatKit text.
python/packages/bedrock/tests/test_bedrock_client.py Tests Bedrock refusal fallback.
python/packages/bedrock/agent_framework_bedrock/_chat_client.py Maps refusals to Bedrock text blocks.
python/packages/anthropic/tests/test_anthropic_client.py Tests Anthropic refusal fallback.
python/packages/anthropic/agent_framework_anthropic/_chat_client.py Maps refusals to Anthropic text blocks.
python/packages/ag-ui/tests/ag_ui/test_run.py Tests AG-UI refusal conversion.
python/packages/ag-ui/agent_framework_ag_ui/_workflow_run.py Includes refusals in workflow text.
python/packages/ag-ui/agent_framework_ag_ui/_run_common.py Emits refusals as AG-UI text events.
python/packages/a2a/tests/test_a2a_executor.py Tests executor refusal conversion.
python/packages/a2a/tests/test_a2a_agent.py Tests agent refusal conversion.
python/packages/a2a/agent_framework_a2a/_agent.py Maps refusals to A2A text parts.
python/packages/a2a/agent_framework_a2a/_a2a_executor.py Handles refusal output in A2A execution.
docs/decisions/0036-python-refusal-content.md Records the refusal representation decision.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread python/packages/core/agent_framework/_types.py Outdated
Comment thread python/packages/devui/agent_framework_devui/_conversations.py Outdated
Comment thread python/packages/devui/agent_framework_devui/_mapper.py Outdated
Comment thread python/packages/devui/frontend/src/services/streaming-state.ts
Comment thread python/packages/openai/agent_framework_openai/_chat_completion_client.py Outdated

@github-actions github-actions Bot 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.

MAF Automated Review — Iteration 1

Result: Findings reported
Scope: full PR (2 commit(s)): 941cc0120512, 35f30a931d9a
Model: gpt-5.6-sol

Overview

The review found 5 verified inline finding(s).

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
5 verified findings remained after source verification (2 high, 3 medium) across 4 files. Details are attached to the affected lines below.

Affected areas: python/packages/devui/frontend/src/services/streaming-state.ts, python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py, python/packages/hosting-responses/agent_framework_hosting_responses/_parsing.py, python/packages/openai/agent_framework_openai/_chat_completion_client.py

Comment thread python/packages/openai/agent_framework_openai/_chat_completion_client.py Outdated
Comment thread python/packages/hosting-responses/agent_framework_hosting_responses/_parsing.py Outdated
Comment thread python/packages/devui/frontend/src/services/streaming-state.ts
Comment thread python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py Outdated
Comment thread python/packages/hosting-responses/agent_framework_hosting_responses/_parsing.py Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@eavanvalkenburg Eduard van Valkenburg (eavanvalkenburg) changed the title Python: Preserve provider refusal content Python: Preserve refusals as marked text Sep 1, 2026
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@eavanvalkenburg

Copy link
Copy Markdown
Member Author

/review

@github-actions github-actions Bot 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.

MAF Automated Review — Iteration 2

Result: No findings
Scope: 34 net-new commit(s): dc7714295fff, 52a376453994, 34d1ac70b2fc, 178f365dc69e, 44c8cf6a2012, 6ed829cf713e, f7e78c689bb9, 341d0ec35bbc, 947d933f2385, de02975ee606, 6d532cf77e26, edfe115ea06b, 1aca2a95e3b9, 4c0bff8b77d5, a617392cc79b, 5996105a1bf2, d2a934d53530, 018dea570b84, 11d628d60ff2, 6a0773ba2180, 8b0aea17663c, 2955888d5512, 9ca957148efd, 0822dd7495d9, 575b408e0ec8, 362972afb701, b03792465563, 486b5d0267f8, ab9ce3d4649d, e2f7db207cb6, 8ea6f8345dbd, baf0ea5252eb, 5715b358b324, 60dbe8f701b9
Model: gpt-5.6-sol

Overview

The change consistently represents refusals as text carrying an experimental marker, preserves that marker through serialization and storage, excludes refusals from structured-output parsing, and reconstructs native refusal shapes at OpenAI, hosting, Foundry, and DevUI boundaries. The added tests cover visibility, persistence, provider round-tripping, and refusal streaming. After reconciling the proposed risks against those guards, the supported mixed-content issue duplicates existing feedback and the remaining candidates lack a demonstrated publishable impact.

Reviewed the supplied incremental change set across correctness, security/reliability, architecture, and failure behavior.
No publishable findings remained after source verification for this scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants