* Fix PydanticSchemaGenerationError with PEP 563 annotations in @tool
_resolve_input_model used raw param.annotation from inspect.signature(),
which returns string annotations when 'from __future__ import annotations'
is active (PEP 563). This caused Pydantic's create_model to fail for
complex types like Optional[int] or FunctionInvocationContext.
Use typing.get_type_hints() to resolve annotations to actual types before
passing them to create_model, matching the approach already used by
_discover_injected_parameters.
Fixes#4809
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Apply pre-commit auto-fixes
* Remove reproduction report and unused test imports
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(tests): strengthen PEP 563 regression tests per review feedback (#4809)
- Verify type correctness in schema assertions (not just key presence)
- Fix ctx annotation to FunctionInvocationContext | None for type consistency
- Add test for Optional[CustomType] pattern (original bug trigger)
- Add test for get_type_hints() fallback with unresolvable forward refs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Apply pre-commit auto-fixes
* Address review feedback for #4809: Python: [Bug]: PydanticSchemaGenerationError in FunctionInvocationContext
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Deprecate Azure AI v1 (Persistent Agents API) helper methods
Add DeprecationWarning to v1 classes and functions that have been
superseded by the v2 (Projects/Responses) API:
- AzureAIAgentsProvider -> use AzureAIProjectAgentProvider
- AzureAIAgentClient -> use AzureAIClient
- AzureAIAgentOptions -> use AzureAIProjectAgentOptions
- to_azure_ai_agent_tools() -> use to_azure_ai_tools()
- from_azure_ai_agent_tools() -> use from_azure_ai_tools()
- AzureAIAgentClient static tool factory methods -> use AzureAIClient equivalents
All v1 components still function but emit warnings to guide migration.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add deprecation warnings to AzureAIAgentsProvider methods
Mark create_agent(), get_agent(), and as_agent() as deprecated
individually, pointing to AzureAIProjectAgentProvider equivalents.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix zero-argument MCP tool schema missing 'properties' key (#4540)
MCP servers for zero-argument tools (e.g. matlab-mcp-core-server's
detect_matlab_toolboxes) declare inputSchema as {"type": "object"}
without a "properties" key. OpenAI's API requires "properties" to
be present on object schemas, causing a 400 invalid_request_error.
Normalize inputSchema at MCP ingestion in load_tools() to inject an
empty "properties": {} when it is missing from object-type schemas.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #4540: improve test robustness and add defensive guard
- Look up loaded functions by name instead of index to avoid brittle
ordering assumptions
- Add negative-path test cases: non-object schema (type: string) and
empty schema ({}) to verify guard clause skips them correctly
- Assert original inputSchema dicts are not mutated by load_tools()
- Add defensive guard for tool.inputSchema being None
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #4540: Python: [Bug]: Local stdio MCP works for calculator but fails for official matlab-mcp-core-server on LM Studio /v1/responses
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: A2AAgent defaults name/description from AgentCard
When an AgentCard is provided but name/description are not explicitly
set, A2AAgent now falls back to agent_card.name and agent_card.description.
This avoids redundant duplication when constructing A2AAgent instances,
especially in GroupChat orchestrations where name and description are
essential for routing decisions.
Explicit values still take precedence over card values.
Fixes#4630
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Use 'is None' checks instead of truthiness for name/description fallback
Ensures explicitly provided empty strings are not overridden by
agent_card values. Adds test for the empty string edge case.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(python): allow @tool functions to return rich content (images, audio)
Add support for tool functions to return Content objects that the model can perceive natively. Closes#4272
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Anthropic logging + mypy fix
* Address PR review: fix MCP ordering, fold helper into from_function_result, fix Chat client
- Preserve original content order in MCP tool results instead of text-first
- Move _build_function_result logic into Content.from_function_result()
- Chat Completions: inject user message for rich items (API only supports string tool content)
- Update tests for ordering and new from_function_result behavior
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Use native Responses API multi-part output, warn+omit for Chat client
- Responses client: put rich items directly in function_call_output's
output field as list (native API support) instead of user message injection
- Chat client: warn and omit rich items (API doesn't support multi-part
tool results), matching Ollama/Bedrock pattern
- Unify test image: use sample_image.jpg across all integration tests
- Add Azure OpenAI Responses integration test
- Assert model describes house image to verify perception
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix lint: remove print statement, wrap long line
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback: bug fixes, single-pass MCP, unit tests
- Add isinstance guard in from_function_result for non-Content lists
- Fix Anthropic empty tool_content fallback to string result
- Fix Content(type='text', text=None) edge case in parse_result
- Rewrite MCP _parse_tool_result_from_mcp as single-pass (no index counters)
- Add Anthropic unit tests: data image, uri image, unsupported media, all-unsupported
- Add OpenAI Chat unit test: rich items warning and omission
- Add OpenAI Responses unit tests: function_result with/without items
- Add test_types tests: only-rich-items list, non-Content list fallback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix pyright errors: add type ignore comments for Any list iteration
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix mypy/pyright: ensure ToolExecutionException receives str
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix lint: remove duplicate test_prepare_options_excludes_conversation_id
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor: unify all tool results into Content items
* addressed copilot comments
* pyright fix
* small fix
* comments
* fix: address Copilot review - warnings, blob safety, dedup
- Add warning logs when rich content is dropped in Claude agent and
MCP server handlers (matching Chat/Bedrock/Ollama pattern)
- Defensive blob URI construction: wrap plain base64 in data: prefix
- Simplify Chat client _prepare_content_for_openai to use content.result
- Simplify Responses client text-only path, remove redundant nesting
- Add test for plain base64 blob without data: prefix
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix token double-counting in compaction and address review comments
- Exclude items from _serialize_content() to prevent double-counting
tokens when items mirrors result in function_result content
- Add rich content warning in GitHub Copilot agent tool handler
- Replace raw Content debug log with concise item count/type summary
- Update stale test comments about FunctionTool.invoke return type
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Add A2A server sample and fix client streaming bug
Add a pure Python A2A server sample so testing the A2A client no longer
requires running the .NET server. The server uses the a2a-sdk's
A2AStarletteApplication with uvicorn and supports three agent types
(invoice, policy, logistics) backed by AzureOpenAIResponsesClient.
New files:
- a2a_server.py: Main server entry point with CLI args
- agent_executor.py: Bridges a2a-sdk AgentExecutor to Agent Framework
- agent_definitions.py: Agent and AgentCard factory definitions
- invoice_data.py: Mock invoice data and query tool functions
- a2a_server.http: REST Client requests for testing
Also fixes a streaming bug in agent_with_a2a.py where async with was
used on ResponseStream which does not support the async context manager
protocol. Changed to async for to match all other samples.
Closes#4045
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review: handle CancelledError and fix end_date filtering
- Re-raise asyncio.CancelledError before the broad exception handler
so cooperative cancellation is not swallowed.
- Make end_date filter inclusive of the full day by comparing with
< end + timedelta(days=1) instead of <= midnight.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The sample was passing raw strings in a list to get_response(), which
expects Message objects. This caused an AttributeError since strings
don't have a 'role' attribute.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add multi-turn streaming sample and rename multi-turn samples
- Rename 03_multi_turn.py to 03a_multi_turn.py
- Add 03b_multi_turn_streaming.py showing streaming with session history
- The new sample demonstrates calling get_final_response() after
iterating the stream to persist conversation history
- Update READMEs to reflect the new file names
Closes#4447
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Auto-finalize ResponseStream on iteration completion
When a ResponseStream is fully consumed via async iteration,
automatically trigger finalization (finalizer + result hooks).
This ensures session history is persisted in streaming multi-turn
conversations without requiring an explicit get_final_response() call.
- Add auto-finalize call in __anext__ on StopAsyncIteration
- Guard inner stream finalization to prevent double-execution
- Re-check _finalized after iteration in get_final_response()
- Add tests for auto-finalization and streaming session history
- Revert sample file renames from previous commit
Closes#4447
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* README fix
* Fix SIM102 lint: combine nested if statements
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix as_agent() not defaulting name/description from client properties
AzureAIClient.as_agent() and AzureAIAgentClient.as_agent() now fall back
to self.agent_name and self.agent_description when name/description are
not explicitly passed. This ensures Agent.name is populated for
telemetry spans without requiring callers to repeat the name.
Fixes#4471
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review: use is None checks instead of truthiness
Switch from name or self.agent_name to explicit is None checks so
that callers can intentionally pass empty strings without them being
replaced by client defaults. Added edge-case tests for empty strings.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update docstrings to document name/description defaulting behavior
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Add propagate_session parameter to as_tool() for session sharing
Add opt-in session propagation in agent-as-tool scenarios. When
propagate_session=True, the parent agent's AgentSession is forwarded
to the sub-agent's run() call, allowing both agents to share session
state (history, metadata, session_id).
- Add propagate_session parameter to BaseAgent.as_tool() (default False)
- Include session in additional_function_arguments so it flows to tools
- Add 3 tests for propagation on/off and shared state verification
- Add sample showing session propagation with observability middleware
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Clarify propagate_session docstring per review feedback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix MCP tools duplicated on second turn when runtime tools are present
When AG-UI's collect_server_tools pre-expands MCP functions on turn 2
(after the MCP server is connected), _prepare_run_context unconditionally
appends them again from self.mcp_tools, duplicating every MCP tool.
Skip MCP functions whose names already exist in the final tool list,
following the same name-based dedup pattern used in _merge_options.
Fixes#4381
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* mypy fix
* Remove issue-specific references from test docstring
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix: Parse oauth_consent_request events in Azure AI client (#3950)
When Azure AI Agent Service returns an oauth_consent_request output item
for OAuth-protected MCP tools, the base OpenAI responses parser drops it
(hits case _ default branch). This causes agent runs to complete silently
with zero content.
Changes:
- Add oauth_consent_request ContentType and Content.from_oauth_consent_request()
factory with consent_link field and user_input_request=True
- Override _parse_response_from_openai and _parse_chunk_from_openai in
RawAzureAIClient to intercept Azure-specific oauth_consent_request items
- Add _emit_oauth_consent helper in AG-UI to emit CustomEvent for frontends
- Add tests proving base parser drops the event and Azure AI override catches it
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* addressed comment
* addressed comments
* addressed comments
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Add file_ids and data_sources support to AzureAIAgentClient.get_code_interpreter_tool()
Update the factory method to accept file_ids and data_sources keyword
arguments, matching the underlying azure.ai.agents SDK CodeInterpreterTool
constructor. This enables users to attach uploaded files for code
interpreter analysis.
Fixes#4050
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* addressed comments
* addressed comments
* Add per-message file attachment support for AzureAIAgentClient
Add hosted_file handling in _prepare_messages() to convert
Content.from_hosted_file() into MessageAttachment on ThreadMessageOptions.
This enables per-message file scoping for code interpreter, matching the
underlying Azure AI Agents SDK MessageAttachment pattern.
- Add hosted_file case in _prepare_messages() match statement
- Import MessageAttachment from azure.ai.agents.models
- Add sample for per-message CSV file attachment with code interpreter
- Add employees.csv test data file
- Add 3 unit tests for hosted_file attachment conversion
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review: validation, fix assertions, remove MessageAttachment
- Add empty string validation in resolve_file_ids()
- Add test for Content with file_id=None
- Add test for empty string file_ids
- Revert MessageAttachment/hosted_file handling from _prepare_messages()
(moved to separate issue #4352 for proper design)
- Remove per-message file upload sample and employees.csv
- Keep data_sources assertion as-is (dict keyed by asset_identifier)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Enhance Azure AI Search citations with document URLs in Foundry V2 (Responses API)
Override _parse_response_from_openai and _parse_chunk_from_openai in
RawAzureAIClient to extract get_urls from azure_ai_search_call_output
items and enrich url_citation annotations with document-specific URLs.
- Non-streaming: first pass collects get_urls, post-processes annotations
- Streaming: captures search output state, enriches url_citation events
(also handles url_citation annotation type not handled by base class)
- Updated V2 sample to demonstrate citation URL extraction
- Added 14 unit tests covering extraction, enrichment, and edge cases
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor: rework search citation enrichment to override _inner_get_response
- Remove all direct openai/pydantic imports from _client.py
- Override _inner_get_response instead of _parse_response_from_openai/_parse_chunk_from_openai
- Use closure-local state for streaming instead of instance-level _streaming_search_get_urls
- Add _build_url_citation_content helper for streaming url_citation handling
- Fix mypy errors by using str(value or '') for Annotation TypedDict fields
- Fix docstring to say 'citation' instead of 'url_citation'
- Update tests to match new approach
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: handle streaming search citations from output_item.done events
The azure_ai_search_call_output item only has populated output data
(including get_urls) in the response.output_item.done event, not in
the response.output_item.added event. Also removed the search_get_urls
guard on url_citation handling so annotations are always produced even
if get_urls haven't been captured yet.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* addressed comments
* refactor: address PR review - eliminate type: ignore[assignment] pattern
Call super()._inner_get_response() independently in each branch instead
of once at the top with union type reassignment. Non-streaming uses
two-arg super() in the closure; streaming uses cast() for type narrowing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor: remove defensive patterns per PR review
- Replace all getattr() with direct attribute access
- Remove cast() for streaming branch, use type: ignore[assignment]
- Simplify _build_url_citation_content to use dict access directly
- Simplify _extract_azure_search_urls to use item.type/item.output
- Handle empty list output from streaming 'added' events
- Update tests to match actual runtime types (objects, not dicts)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* mypy fix
* small fixes
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add core utilities unit tests to improve coverage (#3356)
* Address PR comments: remove redundant imports and fix misleading test
* Refactor tests to use module-level mock class instead of inline classes
* Remove unnecessary tests for trivial base class implementations
* Restore base class tests with module-level helper class