* Python: Apply header_provider headers to ambient MCP requests
MCPStreamableHTTPTool.header_provider was only invoked from call_tool(),
so the initialize handshake, load_tools/load_prompts discovery, and
background pings all went out with no headers. MCP servers that require
auth on initialize (e.g. Azure AI Search knowledge-base MCP endpoints)
therefore returned 401 before any tool call could run.
Add an ambient fallback in the _inject_headers httpx request hook: when
neither the per-call ContextVar nor the active-call snapshot is set, the
hook invokes header_provider({}) so every ambient request is
authenticated. Providers that require per-call kwargs raise on the empty
dict; that is caught, logged, and the request proceeds unauthenticated,
preserving prior behavior. Calling the provider on demand also keeps
dynamic token refresh working for post-connect requests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cf0c1dbf-99bc-4f3f-bcf4-7791ce7dbe6a
* Python: address review - distinguish unset vs empty headers, warn once
Review feedback on the ambient header_provider fallback:
- Distinguish 'unset' (no active call) from 'set but empty' (call_tool
produced no headers). Use _mcp_call_headers.get(None) and the None-ness
of the snapshot instead of a truthiness check, so a provider that
legitimately returns {} during a real call is no longer re-invoked by
the ambient fallback mid-call.
- A kwargs-dependent provider raises on every ambient request (initialize,
discovery, recurring pings). Warn once per tool instance with a
traceback via _ambient_header_warning_emitted and drop subsequent
occurrences to DEBUG to avoid log spam.
Add regression tests for both behaviors.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cf0c1dbf-99bc-4f3f-bcf4-7791ce7dbe6a
* Python: narrow ambient header_provider catch to KeyError
Only the missing-per-call-kwargs case (KeyError, e.g. the
mcp_api_key_auth.py sample indexing kwargs['mcp_api_key']) is tolerated
during ambient requests. Any other exception - a token-refresh failure
or a provider bug - now propagates instead of being silently converted
into unauthenticated traffic, matching the call_tool path which does not
catch header_provider exceptions.
Add a regression test asserting a non-KeyError provider failure
surfaces from the request hook.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cf0c1dbf-99bc-4f3f-bcf4-7791ce7dbe6a
* Python: address review - raise instead of assert, simplify ambient logging
- Reword the ambient-fallback comment to describe the kwargs-dependent
provider pattern generically instead of naming a sample file, which
would go stale if the sample is renamed (also in a test docstring).
- Replace the type-narrowing assert with a RuntimeError carrying a
concise message for the unreachable no-provider state.
- Drop the warn-once/_ambient_header_warning_emitted machinery; the
KeyError ambient case is expected and benign, so log a single DEBUG
line and proceed without headers.
Update the corresponding test to assert behavior (request proceeds
without an Authorization header and no WARNING is emitted) instead of
log-count.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cf0c1dbf-99bc-4f3f-bcf4-7791ce7dbe6a
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
Copilot-Session: cf0c1dbf-99bc-4f3f-bcf4-7791ce7dbe6a
from_dict resolved the expected type identifier from the payload itself
(_get_type_identifier(value) prefers value["type"]), so the mismatch
guard could never fire: any supplied 'type' matched itself, and a payload
like {"type": "function_tool", ...} silently deserialized into a Message,
getting its type rewritten on the next to_dict. The docstring has always
promised a ValueError on mismatch.
Resolve the identifier from the class instead, matching what to_dict
emits, so a mismatched or foreign 'type' now raises as documented.
Payloads without a 'type' field and dependency-injection lookups are
unchanged: in every previously valid case the class-resolved identifier
is the same string the payload carried.
Fixes#7255
Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Preserve model emission order in AG-UI messages snapshot
* Address moonbox3's review: cover the remaining snapshot gaps
- Preopened message ids (tool-only path) now open a text segment when
the first text arrives, so their content can't drop out of the snapshot.
- A tool result closes the current tool-call segment, so
call A -> result A -> call B snapshots as two pairs in stream order.
- emitted_call_ids only marks calls actually emitted, keeping stale
segment ids eligible for the leftover fallback.
- The leftover path carries its tool results too instead of dropping them.
* Python: narrow leftover tool-call ids so pyright accepts the update
---------
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
* Python: Add GitHub Copilot BYOK sample
Demonstrates routing GitHubCopilotAgent requests through a custom OpenAI-compatible
endpoint via ProviderConfig instead of the default GitHub Copilot backend.
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Python: Address BYOK sample review feedback
- Make the provider type configurable via BYOK_PROVIDER_TYPE (default "openai") instead
of hardcoding "openai" — a partial autofix commit had already updated the docstring to
document this env var but left the code hardcoded, which this finishes.
- Stop calling the endpoint "OpenAI-compatible" everywhere; Anthropic isn't OpenAI-wire-
compatible, so reword to "your own endpoint" and list the actual supported providers
(mirrors the equivalent .NET sample fix).
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Python: Fix FoundryAgent inheriting OPENAI_CHAT_MODEL for agent-reference requests (#7272)
* Python: Fix FoundryAgent inheriting OPENAI_CHAT_MODEL for agent-reference requests
* fix(foundry): update test typing annotations to pass mypy, pyrefly, and ty
---------
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Bound summarization input before provider call
SummarizationStrategy now selects complete message groups that fit a configurable summary input token budget before calling the summary client. Only messages actually sent to the summarizer are annotated and excluded, leaving oversized later groups for a later compaction pass instead of shipping the whole transcript unbounded.
Validation: uv run pytest packages/core/tests/core/test_compaction.py -k bounds_summary_input -m "not integration" failed before the implementation and passed after it; uv run pytest packages/core/tests/core/test_compaction.py -m "not integration" passed; uv run poe test -P core passed; uv run poe install completed; uv run poe check -P core passed.
* Handle oversized leading summary groups
Skip individually over-budget leading groups when selecting summarization input so a large early transcript item does not prevent later compactable groups from being summarized.
Validation: uv run pytest packages/core/tests/core/test_compaction.py -k skips_oversized_first_group -q; uv run pytest packages/core/tests/core/test_compaction.py -q; uv run poe check -P core.
* Escalate repeated summary failures
Track consecutive SummarizationStrategy failures and emit a single error once the strategy has failed three times without a successful summary. Reset the escalation state after a successful summary so only persistent failures become loud.
Validation: uv run pytest packages/core/tests/core/test_compaction.py -k 'repeated_summary_failures or resets_failure_escalation' -q; uv run pytest packages/core/tests/core/test_compaction.py -q; uv run poe check -P core.
* Refine summary input selection
Avoid rebuilding and re-tokenizing the full selected summary transcript on every candidate group while preserving complete-group selection and oversized leading group skipping.
Tighten the scripted summarizer test helper to expected Exception failures instead of BaseException.
Verification: uv run pytest packages/core/tests/core/test_compaction.py -q; uv run poe syntax -P core.
---------
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Fix Copilot Actions token environment
Expose workflow tokens through GITHUB_TOKEN so Copilot CLI uses native Actions authentication, while preserving user-token integration test support.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d9fa4e9c-c32d-42fb-8ee4-4772473e6479
* Gate Copilot integration tests explicitly
Use GitHub Actions authentication only when both GITHUB_ACTIONS and GITHUB_TOKEN are present, and require an explicit local opt-in that relies on stored Copilot login.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d9fa4e9c-c32d-42fb-8ee4-4772473e6479
---------
Copilot-Session: d9fa4e9c-c32d-42fb-8ee4-4772473e6479
OpenAI validates the Chat Completions message 'name' against
^[^\s<|\/>]+$, so an agent display name containing a space (or
< | \ / >) failed every request with a 400. Sanitize at the three
assignment sites, mirroring SanitizeAuthorName in the .NET client
(dotnet/extensions): remove characters outside [a-zA-Z0-9_], omit the
name when nothing remains, truncate to 64 characters.
Fixes#7126
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* Python: fix Anthropic streaming double-counting token usage
* Python: address review on the Anthropic usage increment helper
- accumulate the emitted totals in a plain dict instead of string-cast
TypedDict views, so static checkers see real types throughout
- compute the increment through _types.add_usage_details with negated
emitted totals instead of a hand-rolled subtraction loop; keys absent
from a snapshot stay untouched, matching the partial-delta semantics
* feat(observability): add support for OpenAI cache write tokens in usage details
* feat(openai): add cache write tokens handling in usage details
* Fix test
* Python: Defer provider-injected approvals to in-run execution
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1ee1d250-d1e2-4c6f-8c36-aae0d94fe7a1
* Python: Remove vacuous AG-UI approval test
Drop the forged-approval test that was stripped by pending-approval validation; the real pause-approve-resume regression remains the authoritative provider-injected coverage.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1ee1d250-d1e2-4c6f-8c36-aae0d94fe7a1
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1ee1d250-d1e2-4c6f-8c36-aae0d94fe7a1
* fix(foundry-hosting): root hosted checkpoints under durable home directory
* fix: add None guard for _checkpoint_storage_path in test
* Disable Foundry image test
---------
Co-authored-by: Tao Chen <taochen@microsoft.com>
* Allow workflow checkpoint full replayability
Seed the initial run input through the start executor's internal self-edge and record an entry checkpoint (iteration 0) before any executor runs, plus a response-entry checkpoint when responses are delivered, so a run is fully replayable from its checkpoints. Simplify the runner to only checkpoint after each superstep. Drop stale events in apply_checkpoint on restore, and deprecate the unused RunnerContext.reset_for_new_run.
* Fix type
* Add max iteration detailed doc string
* Refine comments
* Python: Fix OpenAIChatCompletionClient passing raw JSON-Schema dict response_format through unwrapped
Raw schema dicts (e.g. {"type": "object", ...}) were forwarded to the
Chat Completions API verbatim, which OpenAI rejects with a 400. The
Responses client already auto-wraps the same input. Mirror its raw-schema
detection (primitive types / schema keywords), wrap into the
{"type": "json_schema", "json_schema": {...}} envelope with
additionalProperties: false injection and title -> name promotion, and
leave already-valid response_format dicts untouched.
Fixes#7197
(cherry picked from commit dce5c3b06328fbde45eb2a9a25638af5b1ec85e3)
* Python: Add live integration coverage for raw JSON-Schema response_format dicts
Adds a response_format_raw_json_schema param to test_integration_options in
both the Chat Completions and Responses client test suites, proving the same
bare schema dict (title set, additionalProperties omitted) round-trips through
both live APIs and yields parsed structured output.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Python: Fix response format dict typing
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
_WORD_PATTERN matched only ASCII (`[a-z0-9]...`), so a message written in
CJK, Cyrillic or any other non-Latin script produced an empty keyword set.
_select_topics returns early on an empty keyword set, so non-English users
never had memory topic files loaded automatically.
Make the pattern Unicode-aware (`[^\W_][\w-]+`, a letter/digit start plus
word chars/hyphen), which is the exact Unicode generalization of the old
pattern: English tokenization is unchanged and CJK/Cyrillic text now yields
keywords.
* Fix Gemini harness tool declarations
Forward Agent Framework FunctionTool JSON Schemas to the Gemini SDK parameters_json_schema field and enable Developer API server-side tool invocation reporting when native Gemini tools are mixed with function declarations.
Preserves Vertex AI behavior and existing function-calling tool_choice config.
Validation:
- uv run --directory python poe check -P gemini
- uv run --directory python poe build -P gemini
- uv run --directory python poe test -A -m 'not integration'
- uv run --directory python pytest packages/gemini/tests/test_gemini_client.py -q -m integration (8 skipped: credential-gated)
* Python: Use typing_extensions TypedDict in Gemini tests
Use typing_extensions.TypedDict for the Gemini JSON Schema test helper so Pydantic can build the model on Python 3.11.
This keeps the CI fix scoped to the failing test compatibility issue without changing Gemini client behavior.
---------
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Python: Add TodoProvider and AgentModeProvider context provider samples
Add two Python samples under samples/02-agents/context_providers/ mirroring the
.NET samples from #7262:
- todo_provider.py: scripted walkthrough of TodoProvider that plans multi-step
work and prints the evolving todo list after each turn.
- agent_mode_provider.py: interactive loop using AgentModeProvider with a /mode
slash command, demonstrating built-in plan/execute and custom modes.
Also index both samples in the context_providers README.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8725831c-086b-475f-90e6-cdba41d59c33
* Python: Address review comments on AgentModeProvider sample
- Replace the AGENT_MODE_USE_CUSTOM env var with an in-file USE_CUSTOM_MODES
constant for choosing between built-in and custom modes.
- Use plain input() in the interactive loop instead of asyncio.to_thread.
- Update the README prerequisites to reference the in-file toggle.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8725831c-086b-475f-90e6-cdba41d59c33
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8725831c-086b-475f-90e6-cdba41d59c33
* fix(python): handle callable class middleware safely in _determine_middleware_type (#6697)
* test(python): type-annotate test middleware lists to pass test-typing checks
* Fix sub-workflow checkpoint restore to preserve sub-workflow state
Add Runner.capture_checkpoint_object/restore_from_checkpoint_object (quiescent-only nested checkpoint) and embed a sub_workflow_checkpoint in WorkflowExecutor.on_checkpoint_save/on_checkpoint_restore so a resumed parent restores each sub-workflow's mid-progress state instead of only replaying pending request-info events. Keeps a backward-compat fallback when sub_workflow_checkpoint is absent.
* Move checkpoint-object construction into the runner context
Add RunnerContext.create_checkpoint_object alongside create_checkpoint (create_checkpoint now delegates to it and persists), so Runner.capture_checkpoint_object builds the snapshot via the context instead of a one-off get_messages peek primitive. In-flight messages are captured non-destructively (per-source lists copied). The checkpoint-less capturing contexts (azurefunctions, durabletask) raise NotImplementedError to match create_checkpoint.
* Remove per-execution bookkeeping from WorkflowExecutor
The sub-workflow is a single shared instance, so per-execution ExecutionContext/request routing never provided real isolation. Delegate request/response tracking to the sub-workflow itself: can_handle accepts targeted propagated responses, _handle_response validates against the sub-workflow's pending requests and forwards responses immediately, and on_checkpoint_save embeds only the sub-workflow checkpoint (on_checkpoint_restore keeps a legacy reader for older checkpoints). Also emit the fresh-message/checkpoint-while-pending warning from FunctionalWorkflow.run to match Workflow.run.
* Drop redundant decode in WorkflowExecutor.on_checkpoint_restore
The storage backend already materializes the full checkpoint on load (FileCheckpointStorage decodes recursively; InMemoryCheckpointStorage deep-copies), so the embedded sub_workflow_checkpoint (and legacy execution_contexts) arrive already decoded - like every other executor's on_checkpoint_restore state. Remove the no-op decode_checkpoint_value calls and the now-unused import.
* Clean up
* Do not allow checkpoint storage in sub workflow
* Address comments
* Fix syntax check
* Add warning
---------
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
* Python: Fix duplicate function call on approval round-trip (#7267)
`_replace_approval_contents_with_results` deduped restored function calls
against only the message currently being scanned. On an approval round-trip
the hosting layer replays the stored `function_call` item and its
`mcp_approval_request` item as two separate assistant messages, so the
per-message check never fired and the approval request restored a second
copy of the call.
Only one copy received the function result; the orphaned copy was left
unanswered, which the Responses API rejects with
"No tool output found for function call call_<id>".
Collect existing call ids across all messages instead, and add a restored
call to that set so two approval requests for the same call cannot both
expand.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Refactor approval placeholder result handling
Refactor approval handling logic to improve clarity and maintainability.
* Refactor test to support reused call IDs after completion
Updated the test to allow reused call IDs after completion, ensuring that a completed call does not suppress later approval requests with the same ID. Adjusted assertions to reflect the new behavior.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Promote the GitHub Copilot package from release candidate (1.0.0rc4) to released (1.0.0): bump the version, switch the classifier to Production/Stable, update PACKAGE_STATUS.md, and drop the --pre install flag from the package and sample READMEs. Add a github-copilot-1.0.0 CHANGELOG section covering the promotion and the input-attachment forwarding shipped in this release. No core/root bump: this is a standalone package promotion and the core[all] extra references the package without a version pin.
Copilot-Session: f523064c-60b4-4d18-bf95-c16c5fda9126
* Python: Forward GitHub Copilot input attachments as inline blobs
The Python GitHubCopilotAgent built the prompt from message text only, so
DataContent (images/documents) passed on input was silently dropped. The .NET
provider already forwards these as attachments.
Map input data content to the Copilot SDK's inline BlobAttachment (base64,
no temp files) in both the streaming and non-streaming send paths. Data content
without a media type is dropped with a warning instead of silently.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ac85c429-4115-42ef-a18a-f576e3cf03f7
* Python: Handle non-base64 data URIs and fix attachment docstring
Address PR review feedback:
- Guard _get_data_bytes_as_str against ContentError so a non-base64 data:
URI (which _validate_uri still classifies as type="data") is skipped with a
warning instead of failing the entire Copilot request.
- Correct the docstring: remote URIs and non-base64 data URIs are neither
attached nor added to the prompt (the prompt is built from text content only).
- Add tests for the non-base64 data URI path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ac85c429-4115-42ef-a18a-f576e3cf03f7
* Python: Fix flaky attachment test under telemetry
The end-to-end non-base64 data URI test failed in CI because GitHubCopilotAgent's
telemetry layer serializes message content (observability._to_otel_part ->
_get_data_bytes_as_str), which raises ContentError on a non-base64 data: URI
before the attachment code runs. That is an unrelated core-observability
limitation, not attachment behavior.
Use RawGitHubCopilotAgent (no telemetry layer) for that test so it isolates the
provider's send path. The direct helper test still covers the ContentError guard.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ac85c429-4115-42ef-a18a-f576e3cf03f7
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ac85c429-4115-42ef-a18a-f576e3cf03f7
* Python: Support async credentials in `FoundryToolbox`
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Refactor: Use AzureCredentialTypes for credential type annotations in Toolbox classes
* Remove auth_flow method from _ToolboxAuth class
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Prepare the focused alpha release for the progressive A2A adapters from #7258. No other package versions or dependency bounds change.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 003e02dd-dba0-40a5-9ebf-083901aefb57
Bump root and core to 1.12.1, OpenAI to 1.11.0 for new public prompt-cache options, Foundry to 1.10.3, and Gemini and Foundry Hosting to beta 260722 based on CHANGELOG entries. Promote AG-UI from 1.0.0rc9 to stable 1.0.0. No beta cohort bump was applied, and core floors remain unchanged under the strict affected-dependency policy because the connectors do not require a new core API.
* Python: Fix reasoning-paired client tool replay
* Python: Handle middleware-terminated reasoning tool loops
* Python: Replay encrypted reasoning function groups
Key decisions:
- Request encrypted reasoning on client-managed Responses calls while preserving caller include values.
- Store encrypted payloads in Content.protected_data and reconstruct one provider reasoning item per reasoning id.
- Replay active and completed function call/result groups; retain continuation-owned history behavior and the existing orphan-safe MCP path.
Files changed:
- python/packages/openai/agent_framework_openai/_chat_client.py
- python/packages/openai/tests/openai/test_openai_chat_client.py
Next iteration:
- Extend encrypted reasoning preservation to streaming and framework serialization boundaries.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Preserve encrypted reasoning through streaming
Key decisions:
- Capture encrypted reasoning from terminal streamed output items in Content.protected_data.
- Preserve summary and private reasoning as distinct framework contents while reconstructing one provider reasoning item per id.
- Prove replay after Message JSON and workflow checkpoint round trips, including encrypted-only and completed function groups.
Files changed:
- python/packages/core/agent_framework/_types.py
- python/packages/openai/agent_framework_openai/_chat_client.py
- python/packages/openai/tests/openai/test_openai_chat_client.py
Next iteration:
- Extend lossless stateless reasoning replay to hosted MCP call/output groups.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Replay hosted MCP reasoning groups
Key decisions:
- Preserve hosted MCP call/output groups in client-managed history instead of deleting them when reasoning cannot be reconstructed.
- Keep call/result coalescing and orphan-result exclusion intact, while retaining continuation-owned duplicate avoidance.
- Cover completed, active, and multi-call reasoning groups plus the public outgoing request boundary.
Files changed:
- python/packages/openai/agent_framework_openai/_chat_client.py
- python/packages/openai/tests/openai/test_openai_chat_client.py
Next iteration:
- Preserve middleware-terminated and parallel function groups atomically.
- Add preflight rejection for non-replayable reasoning groups in the dedicated validation slice.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Preserve terminated parallel reasoning groups
Key decisions:
- Return ordinary function results when middleware terminates a loop, removing the provider-specific durable marker.
- Preserve every parallel call and available sibling result as one encrypted reasoning group in stateless replay.
- Prove successful and policy-blocked batches through the public two-agent Foundry workflow and outgoing HTTP boundary.
Files changed:
- python/packages/core/agent_framework/_tools.py
- python/packages/core/tests/core/test_function_invocation_logic.py
- python/packages/openai/tests/openai/test_openai_chat_client.py
- python/packages/foundry/tests/foundry/test_foundry_agent.py
Next iteration:
- Add preflight rejection for non-replayable and partially compacted reasoning groups.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Reject unsafe stateless reasoning replay
Key decisions:
- Validate client-managed reasoning groups after compaction and report every affected reasoning and call identifier before transport.
- Permit service-owned continuation and fully excluded atomic groups while rejecting partial compaction projections.
- Surface encrypted-reasoning capability failures without lossy retries.
Files changed:
- python/packages/openai/agent_framework_openai/_chat_client.py
- python/packages/openai/tests/openai/test_openai_chat_client.py
Next iteration:
- Run the resource-specific Foundry proof and finish PR #7233; that live proof remains intentionally local and requires the configured developer resource.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Preserve reasoning metadata in Foundry hosting
* Python: Avoid duplicating reasoning text metadata
* Python: Gate encrypted reasoning for Foundry agents
* Python: Type stateless reasoning integration test
* Python: Narrow Foundry mock call arguments
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Support prompt cache breakpoints for GPT-5.6 models in OpenAI clients
Add request-level prompt_cache_options to OpenAIChatOptions and
OpenAIChatCompletionOptions, and forward a per-part prompt_cache_breakpoint
from Content.additional_properties onto the content blocks each API supports.
Text parts that carry a breakpoint keep typed list content, since the
plain-string form cannot hold one; without a breakpoint the existing string
forms are unchanged.
* Clarify system-message content-shape comment
* Address review: SDK prompt cache types, private helper, add sample
Replace the custom PromptCacheOptions TypedDict with the openai SDK's own
types for each API, which raises the openai floor to 2.45.0 where those
types were introduced. Make the breakpoint helper private to the two chat
clients. Add a prompt caching sample with a README entry, and unquote the
helper's Content annotation so the pyupgrade hook passes.
* Guard the prompt cache options import for older openai versions
The SDK's PromptCacheOptions types only exist in openai 2.45.0 and
later, so each client falls back to a local mirror when the import
fails and the dependency floor stays at 2.25.0. A TYPE_CHECKING-only
import is not enough because the options classes are introspected with
get_type_hints() at runtime. Verified against openai 2.25.0: the
package imports, the fallback resolves, and part-level breakpoints
still work; sending the option itself requires 2.45.0, which the field
docstrings now note.
* Make the old-openai fallback for PromptCacheOptions deliberately empty
Assigning None instead, as suggested in review, trips pyright's
reportInvalidTypeForm on the field annotation (the symbol becomes
type | None after the try/except). An empty TypedDict gives the same
effect for users on older openai versions: any content they put in
prompt_cache_options is flagged by their type checker, since the
option cannot be sent on those versions anyway, while
get_type_hints() on the options classes keeps working at runtime.
* Guard prompt_cache_options at runtime instead of via an empty fallback type
The empty-TypedDict fallback flagged valid `prompt_cache_options` usage under
pyright on every openai version — including this PR's own
`client_prompt_caching.py` sample (`poe check -S`) — because pyright resolves the
try/except symbol to the fallback shape regardless of the installed openai, while
mypy/ty resolve the failed import to `Any` and never warn. So a type-only "warn on
old openai" signal is not achievable cleanly across type checkers.
Restore the faithful fallback (mirrors the SDK's `mode`/`ttl` shape) so the option
type-checks identically on every supported openai version, and add a runtime guard:
setting `prompt_cache_options` on openai < 2.45 now raises a clear
ChatClientInvalidRequestException instead of forwarding an unusable option to the
SDK. This keeps the option non-silent for all users regardless of type checker,
without forcing an openai upgrade. Adds tests covering the guard for both clients.
* Gate system/developer breakpoint shape on a real mapping value
The system/developer branch switched to list-form content whenever
prompt_cache_breakpoint was set to any non-None value, but the option is
only attached when the value is a mapping. A malformed value (e.g. a
string) therefore changed the message shape without adding a breakpoint.
Decide the shape from the built part instead, matching the user-role path.