* Fix streaming response losing created_at from response.completed event (#5347)
The streaming path in _parse_chunk_from_openai did not extract created_at
from the response.completed event, unlike the non-streaming path in
_parse_responses_response. This caused durabletask persistence warnings
when created_at was None.
Extract created_at in the response.completed case and pass it to the
returned ChatResponseUpdate.
Also fix pre-existing pyright errors for optional orjson import in sample
files.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix orjson import suppression to use pyright instead of mypy (#5347)
Replace `# type: ignore[import-not-found]` with
`# pyright: ignore[reportMissingImports]` on optional orjson imports
in conversation sample files, matching the repo's Pyright strict
configuration.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix OpenAIEmbeddingClient with /openai/v1 endpoint (#5068)
When base_url ends with /openai/v1/ and a credential is provided,
load_openai_service_settings was creating an AsyncAzureOpenAI client.
The Azure SDK rewrites deployment-based endpoints (including /embeddings)
by inserting /deployments/{model}/ into the URL, producing 404s on the
OpenAI-compatible /openai/v1 endpoint.
Use AsyncOpenAI instead of AsyncAzureOpenAI when the resolved base_url
targets /openai/v1, converting the Azure token provider to an async
api_key callable. The responses_mode path is unaffected because the
Responses API (/responses) is not in the SDK's rewrite list.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix OpenAIEmbeddingClient to use AsyncOpenAI for /openai/v1 endpoints
Fixes#5068
* Address review feedback: improve test coverage and remove unrelated changes
- Revert unrelated formatting change in test_a2a_agent.py
- Fix test_init_with_openai_v1_base_url_and_api_key_uses_openai_client to
exercise the Azure settings path (via AZURE_OPENAI_BASE_URL env var)
instead of the plain OpenAI path, covering the elif api_key branch
- Add _ensure_async_token_provider unit tests for both sync and async
token providers
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #5068: Python: [Bug]: `OpenAIEmbeddingClient` does not work with `/openai/v1` endpoint
---------
Co-authored-by: MAF Dashboard Bot <maf-dashboard-bot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
* Fix url_citation annotations dropped in streaming (#5029)
Add url_citation branch to the streaming annotation handler in
_parse_chunk_from_openai, mirroring the existing non-streaming path.
The handler creates an Annotation with type='citation', title, url,
and annotated_regions (TextSpanRegion), wrapped in Content.from_text.
Update test_streaming_annotation_added_with_unknown_type to use a
truly unknown type, and add new tests for url_citation (with and
without url).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #5029: Python: [Bug]: url_citation annotations silently dropped in Foundry streaming (SharePoint grounding citations lost)
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
* Fix reasoning text done events duplicating streamed delta content (#5157)
The OpenAI Responses API sends both reasoning_text.delta (incremental
chunks) and reasoning_text.done (full accumulated text) events. The
chat client was emitting Content for both, causing ag-ui to append the
full done text onto already-accumulated delta text, producing
duplicated reasoning output.
Stop emitting Content for reasoning_text.done and
reasoning_summary_text.done events, matching how output_text.done is
already handled (not emitted). The deltas contain all the content;
the done event is redundant.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(openai): emit reasoning done content as fallback when no deltas observed (#5157)
Address PR review feedback:
- Track item_ids that received reasoning deltas via seen_reasoning_delta_item_ids set
- Emit content from done events only when no deltas were received for the
item_id, preventing silent content loss on stream resumption
- Add comment documenting code_interpreter done event asymmetry
- Replace redundant ag-ui test with deduplication-focused test
- Add integration test for delta+done sequence in OpenAI chat client tests
- Add fallback path tests for done events without preceding deltas
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #5157: Python: [Bug]: "type": "response.reasoning_text.delta" and "response.reasoning_text.done" both get exposed as "text_reasoning"
* Fix AG-UI reasoning streaming to use proper Start/End pattern (#5157)
_emit_text_reasoning now follows the same streaming pattern as _emit_text:
- Emits ReasoningStartEvent/ReasoningMessageStartEvent only on the first
delta for a given message_id
- Emits only ReasoningMessageContentEvent for subsequent deltas
- Defers ReasoningMessageEndEvent/ReasoningEndEvent until
_close_reasoning_block is called (on content type switch or end-of-run)
This produces the correct protocol pattern:
ReasoningStartEvent
ReasoningMessageStartEvent
ReasoningMessageContentEvent(delta1)
ReasoningMessageContentEvent(delta2)
ReasoningMessageEndEvent
ReasoningEndEvent
Instead of wrapping every delta in a full Start→End sequence.
Backward compatibility is preserved: calling _emit_text_reasoning without
a flow argument still produces the full sequence per call.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix import ordering lint error in AG-UI test file (#5157)
Move inline import of TextMessageContentEvent to the top-level import
block and ensure alphabetical ordering to satisfy ruff I001 rule.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix mypy error: rename loop variable to avoid type conflict with WorkflowEvent
The 'event' variable was already typed as WorkflowEvent[Any] from the
async for loop at line 590. Reusing it in the _close_reasoning_block
loop (which returns list[BaseEvent]) caused an incompatible assignment
error. Renamed to 'reasoning_evt' to avoid the conflict.
Fixes#5162
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #5157: review comment fixes
* narrow test result reporting to explicit pytest JUnit XML
* Fix test args
* Fix pytest-results-action in merge workflow and remove committed test artifacts
Apply the same JUnit XML fix from python-tests.yml to python-merge-tests.yml:
add --junitxml=pytest.xml to all test commands and narrow the results action
path from ./python/**.xml to ./python/pytest.xml. Also remove accidentally
committed pytest.xml and python-coverage.xml and add them to .gitignore.
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Raise clear handler registration error for unresolved TypeVar (#4943)
Detect unresolved TypeVar in message parameter annotations during handler
registration in both _validate_handler_signature (Executor) and
_validate_function_signature (FunctionExecutor). Raises a ValueError with
an actionable message recommending @handler(input=..., output=...) or
@executor(input=..., output=...) instead of letting TypeVar leak through
to a confusing TypeCompatibilityError during workflow edge validation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #4943: reorder checks and harden function executor
- Move TypeVar check before validate_workflow_context_annotation in
_executor.py so users see the more actionable error first
- Wrap get_type_hints in try/except in _function_executor.py matching
the defensive pattern in _executor.py
- Repurpose duplicate test to cover bounded TypeVar rejection
- Add test_function_executor_allows_concrete_types for test symmetry
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Narrow get_type_hints except clause and add missing tests (#4943)
- Narrow `except Exception` to `except (NameError, AttributeError, RecursionError)`
in both _executor.py and _function_executor.py so unexpected failures in
get_type_hints are not silently swallowed.
- Add test_handler_unresolvable_annotation_raises to test_function_executor_future.py
exercising the except branch of get_type_hints in the function executor path.
- Add test_function_executor_rejects_bounded_typevar_in_message_annotation to
test_function_executor.py for parity with the Executor bounded TypeVar test.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add error ordering test for TypeVar vs WorkflowContext priority (#4943)
Add test_handler_typevar_error_takes_priority_over_context_error to verify
that when a handler has both a TypeVar message and an unannotated ctx, the
TypeVar error is raised first (the more actionable issue).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix image content serialization sending null file_id to Foundry API
Omit file_id from input_image dict when not present instead of including
it as null, which Azure AI Foundry's stricter schema validation rejects.
* Python: Fix Foundry API rejecting rich content in function_call_output
Azure AI Foundry does not support list-format output in function_call_output
items. Add SUPPORTS_RICH_FUNCTION_OUTPUT flag (default True) to
RawOpenAIChatClient, set to False in RawFoundryChatClient so Foundry
falls back to string output for tool results with images/files.
Also omit file_id from input_image dicts when not set, since Foundry
rejects explicit nulls.
* Python: Surface rich tool content as user message when Foundry lacks support
When SUPPORTS_RICH_FUNCTION_OUTPUT is False, image/file items from tool
results are injected as a follow-up user message so the model can still
process the visual content via Foundry's supported user message format.
* Xfail Foundry image integration test for the meantime
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Refactor Anthropic model option and provider clients
Rename the Anthropic client model option from model_id to model, add provider-specific Anthropic wrappers for Foundry, Bedrock, and Vertex, and expose them through the Anthropic, Foundry, Amazon, and Google namespaces. Update core option handling, docs, samples, and tests accordingly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix Anthropic skills sample typing
Cast the Anthropic beta client to Any in the skills sample so the pre-commit sample pyright check no longer fails on beta skills and files endpoints that are not exposed by the current SDK stubs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* undo sample mypy
* Retry CI after transient external failures
Retrigger PR validation after an unrelated Copilot review workflow SAML failure and a transient external tau2 git fetch failure in the Windows Python test setup.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback on model option merging
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address Anthropic compatibility review feedback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* moved all to `model`
* fixes for azure ai search
* Python: standardize remaining sample env var names
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: fix foundry-local pyright compatibility
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* updated env vars in cicd
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix broken samples for GitHub Copilot, declarative, and Responses API
- Add missing on_permission_request handler to github_copilot_basic and
github_copilot_with_session samples (required by copilot SDK)
- Increase timeout for remote MCP query in github_copilot_with_mcp sample
- Soften session isolation claim in github_copilot_with_session sample
- Fix inline_yaml sample: pass project_endpoint via client_kwargs instead
of relying on YAML connection block (AzureAIClient expects
project_endpoint, not endpoint)
- Handle raw JSON schemas in Responses client _convert_response_format
so declarative outputSchema works with the Responses API
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Improve raw JSON schema detection heuristic and add tests
- Broaden raw schema detection to handle anyOf, oneOf, allOf, $ref, $defs
keywords and JSON Schema primitive types, not just 'properties'
- Apply same raw schema handling to azure-ai _shared.py for consistency
- Add unit tests for both openai and azure-ai response_format conversion
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Support MCP sampling tools capability (#4625)
Forward systemPrompt, tools, and toolChoice from MCP sampling requests
to the chat client's get_response() call. Also advertise the
sampling.tools capability to MCP servers when a client is configured.
- Pass SamplingCapability with tools support to ClientSession
- Convert systemPrompt to instructions in options
- Convert MCP Tool objects to FunctionTool instances for options
- Map MCP ToolChoice.mode to tool_choice in options
- Add tests for all new behaviors and update existing sampling tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix#4625: Support MCP sampling tool with proper typing and structured content
- Fix mypy error by typing sampling callback options as ChatOptions[None]
instead of dict[str, Any], and importing ChatOptions from _types
- Handle structuredContent from CallToolResult in _parse_tool_result_from_mcp,
serializing it as JSON text Content when present
- Add tests for structuredContent parsing (with and without regular content)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix lint: add author to TODO comment
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #4625: remove default=str, add edge-case tests
- Remove default=str from json.dumps for structuredContent to fail fast
on non-JSON-serializable values instead of silently converting
- Add test for non-JSON-serializable structuredContent (TypeError)
- Add tests for empty systemPrompt ('') and empty tools list ([]) edge
cases in sampling callback
- Expand TODO comment noting list[Content] return type constraint for
future result_type support
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Sanitize sampling callback error to avoid leaking internals (#4625)
Log exception details at DEBUG level instead of including them in the
ErrorData message returned to the MCP server, which may be untrusted.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #4625: move params to options, restore error info
- Remove stale TODO comment about response_format (ChatOptions already has it)
- Restore {ex} in sampling callback error message for useful debugging info
- Set structuredContent as additional_property on Content for structured access
- Move temperature, max_tokens, stop into options dict (not top-level kwargs)
- Only set temperature when provided (not all models support it)
- Add tests for generation params in options and temperature omission
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix MCP sampling callback and structured content error handling (#4625)
- Guard max_tokens like temperature: only set when not None, so options
can properly evaluate to None when all params are absent
- Wrap json.dumps of structuredContent in try/except to fall back to
str() for non-serializable values instead of propagating TypeError
- Extract test_connect_sampling_capabilities_with_client into its own
test function so pytest can discover it independently
- Add test for max_tokens=None omission from options
- Update structured content non-serializable test to expect fallback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #4625: review comment fixes
* Fix MCP and Azure validation regressions
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix streaming path to deliver mcp_server_tool_result content (#4814)
Remove premature mcp_server_tool_result emission from the
response.output_item.added/mcp_call handler — at that point the MCP
server has not yet responded and output is always None.
Add a handler for response.mcp_call.completed that emits
mcp_server_tool_result with the actual tool output, matching the
non-streaming path behavior.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix streaming path to deliver mcp_server_tool_result content (#4814)
Stop eagerly emitting mcp_server_tool_result on response.output_item.added
(when output is always None). Instead, handle response.output_item.done for
mcp_call items, which carries the full McpCall with populated output.
This matches the non-streaming path which guards with 'if item.output is not
None' before emitting the result.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix test docstring to match actual implementation event name
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review: call_id fallback and raw_representation consistency (#4814)
- Add call_id fallback in response.output_item.done mcp_call handler to
match the output_item.added handler pattern
- Use done_item instead of event for raw_representation to keep
consistent with other output_item branches and non-streaming path
- Add test for call_id fallback when id attribute is missing
- Add raw_representation assertions to existing done handler tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review: call_id fallback for non-streaming path and test coverage (#4814)
- Apply defensive call_id fallback (getattr with id/call_id/empty) to
non-streaming mcp_call path for consistency with streaming path
- Add raw_representation assertion to call_id fallback test
- Add test for empty-string fallback when neither id nor call_id exist
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>