- Fix off-by-one in FoundryToolboxBearerTokenHandler retry loop (4 attempts → 3)
- URI-encode version parameter in HostedMcpToolboxAITool.BuildAddress
- Add XML doc clarifying version pinning is reserved for future use
- Add comment clarifying AddHostedService deduplication safety
- Fix DevTemporaryTokenCredential expiry to use DateTimeOffset.MaxValue
- Fix AgentCard ambiguity in A2AServer sample with using alias
- Add 18 new unit tests for retry handler and ReadMcpToolboxMarkers
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduces HostedMcpToolboxAITool, a marker tool subclassing HostedMcpServerTool that rides the OpenAI Responses 'mcp' wire format to let clients request a specific Foundry toolbox per request.
- New FoundryAITool.CreateHostedMcpToolbox(name, version?) factory.
- FoundryToolboxOptions.StrictMode (default true) rejects unregistered toolboxes; set to false to allow lazy-open on first use.
- FoundryToolboxService.GetToolboxToolsAsync(name, version?) resolves cached or lazy-opened MCP tools.
- AgentFrameworkResponseHandler parses request.Tools for foundry-toolbox://name[?version=v] markers and injects resolved tools per request, merging with pre-registered ones.
- Unit tests for marker parsing and strict-mode resolution.
Adds support for Foundry Toolsets MCP proxy integration in the hosted agent
response handler. Toolsets connect at startup via IHostedService, gating the
readiness probe per spec §3.1. MCP tools are injected into every request's
ChatOptions and OAuth consent errors (-32006) are intercepted and surfaced as
mcp_approval_request + incomplete SSE events.
New files:
- FoundryToolboxOptions.cs: configuration POCO for toolset names and API version
- FoundryToolboxBearerTokenHandler.cs: DelegatingHandler with Azure Bearer token
auth, Foundry-Features header injection, and 3x exponential backoff on 429/5xx
- McpConsentContext.cs: AsyncLocal-based per-request consent state shared between
the tool wrapper and the response handler
- ConsentAwareMcpClientTool.cs: AIFunction wrapper that catches -32006 errors and
signals consent via shared state and linked CancellationTokenSource
- FoundryToolboxService.cs: IHostedService that creates McpClient per toolset at
startup and exposes cached tools
Modified files:
- AgentFrameworkResponseHandler.cs: injects toolbox tools into ChatOptions, sets
up linked CTS consent interception, emits mcp_approval_request on -32006
- ServiceCollectionExtensions.cs: adds AddFoundryToolboxes(params string[]) extension
- Microsoft.Agents.AI.Foundry.csproj: adds ModelContextProtocol and Azure.Identity
dependencies under NETCoreApp condition
Sample:
- Hosted-Toolbox: minimal hosted agent sample using AddFoundryToolboxes
Revises the Foundry pre-release approach to publish ALL normally packable src projects as preview packages stamped 0.0.1-preview.260417.2, including projects previously flagged IsReleased=true or with a non-default VersionSuffix (rc/alpha).
nuget-package.props:
- Collapse the four conditional PackageVersion expressions (IsReleaseCandidate, VersionSuffix, default preview, IsReleased stable) into a single unconditional 0.0.1-preview.260417.2. On this preview-only branch every package ships with the same pre-release stamp regardless of per-project flags.
- Restore the global IsPackable=true default (offsetting the repo-wide IsPackable=false in Directory.Build.props). Projects that opt out (Mem0, Declarative) already set IsPackable=false AFTER importing this file so they remain non-packable.
- Remove the IsReleased-gated EnablePackageValidation line. Package validation does not apply to a 0.0.1 preview.
csproj reverts (Abstractions, Agents.AI, Workflows, Workflows.Generators, Foundry):
- Revert the IsPackable=true opt-in block introduced in #5336 (now redundant since the props default is true again).
- Restore IsReleased=true to its pre-PR value. The setting is now a no-op because the props no longer branches on it.
* Prepare Foundry preview release 1.2.0-preview.*
Bump VersionPrefix to 1.2.0 and update the preview stamp date. Invert packaging opt-in so only the Foundry preview set produces NuGet packages:
- Microsoft.Agents.AI.Abstractions
- Microsoft.Agents.AI
- Microsoft.Agents.AI.Workflows
- Microsoft.Agents.AI.Workflows.Generators
- Microsoft.Agents.AI.Foundry
Flip IsReleased=false on the preview set so they pick up the -preview.YYMMDD.N suffix. Gate GeneratePackageOnBuild on IsPackable=true. Remove the global IsPackable=true from nuget-package.props so the repo-level default (false) applies to everything else.
* Lower preview VersionPrefix to 0.0.1
Retroactive preview publish: bump VersionPrefix and GitTag from 1.2.0 to 0.0.1 so the 5 Foundry preview packages emit as 0.0.1-preview.260417.1.
- Add Dockerfile and Dockerfile.contributor for Docker-based testing
- Add agent.yaml and agent.manifest.yaml with triage-workflow as primary agent
- Add README.md following sibling pattern, noting Azure OpenAI vs Foundry endpoint
- Add DevTemporaryTokenCredential and ChainedTokenCredential for Docker auth
- Register triage-workflow as non-keyed default so azd invoke works without model
- Update .env.example with AZURE_BEARER_TOKEN sentinel
- Add .gitignore to 04-hosting to suppress VS-generated launchSettings.json
- Fix docker run image name in Hosted-Workflow-Simple README
- Delete all launchSettings.json files (port 8088 now comes from ASPNETCORE_URLS in .env)
- Add DotNetEnv to Hosted-Invocations-EchoAgent so it loads .env like the responses samples
- Create .env.example for EchoAgent with ASPNETCORE_URLS and ASPNETCORE_ENVIRONMENT
- Add AGENT_NAME to ChatClientAgent and FoundryAgent .env.example (required by those samples)
- Add AZURE_BEARER_TOKEN=DefaultAzureCredential to all .env.example files
- Update DevTemporaryTokenCredential in all 6 samples to treat the sentinel value
as unavailable, allowing ChainedTokenCredential to fall through to DefaultAzureCredential
- Update EchoAgent README with Configuration section
Align dotnet hosted agent samples with the Python side (PR #5281) by
reorganizing the directory structure:
- Remove HostedAgentsV1 entirely (old API pattern)
- Split HostedAgentsV2 into invocations/ and responses/ based on protocol
- Move Using-Samples accordingly (SimpleAgent to responses, SimpleInvocationsAgent to invocations)
- Update slnx with new project paths and add previously missing invocations projects
- Update README cd paths from HostedAgentsV2 to invocations or responses
- Rename .env.local to .env.example to match Python naming convention
- Fix format violations in newly included invocations projects
Add Hosted-Invocations-EchoAgent: a minimal echo agent hosted via the
Invocations protocol (POST /invocations) using AddInvocationsServer and
MapInvocationsServer, bridged to an Agent Framework AIAgent through a
custom InvocationHandler.
Add SimpleInvocationsAgent: a console REPL client that wraps HttpClient
calls to the /invocations endpoint in a custom InvocationsAIAgent,
demonstrating programmatic consumption of the Invocations protocol.
Both samples default to port 8088 for consistency with other hosted
agent samples.
Add InMemoryAgentSessionStore registration to all ServiceCollection
setups in AgentFrameworkResponseHandlerTests and WorkflowIntegrationTests.
This is needed after the AgentSessionStore infrastructure was introduced
in the responses-hosting feature. Tests still have NotImplementedException
stubs for CreateSessionCoreAsync which will be fixed when the session
infrastructure is fully available.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Demonstrates two MCP integration layers in a single hosted agent:
- Client-side MCP: McpClient connects to Microsoft Learn, agent handles
tool invocations locally (docs_search, code_sample_search, docs_fetch)
- Server-side MCP: HostedMcpServerTool delegates tool discovery and
invocation to the LLM provider (Responses API), no local connection
Includes DevTemporaryTokenCredential for Docker local debugging,
Dockerfile.contributor for ProjectReference builds, and the openai/v1
route mapping for AIProjectClient compatibility in Development mode.
Previously, unhandled exceptions from agent execution would bubble up
to the SDK orchestrator, which emits a generic 'An internal server
error occurred.' message — hiding the actual cause (e.g., 401 auth
failures, model not found, etc.).
Now AgentFrameworkResponseHandler catches non-cancellation exceptions
and emits a proper response.failed event containing the real error
message, making it visible to clients and in logs.
OperationCanceledException still propagates for proper cancellation
handling by the SDK.
Also bumps package version to 0.9.0-hosted.260403.2.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- InputConverter: stop propagating request.Model to ChatOptions.ModelId
Hosted agents use their own model; client-provided model values like
'hosted-agent' were being passed through and causing server errors.
- Add FoundryResponsesRepl sample: interactive CLI client that connects
to a Foundry Responses endpoint using ResponsesClient.AsAIAgent()
- Bump package version to 0.9.0-hosted.260403.1
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move source and test files from the standalone Hosting.AzureAIResponses project
into the Foundry package under a Hosting/ subfolder. This consolidates the
Foundry-specific hosting adapter into the main Foundry package.
- Source: Microsoft.Agents.AI.Foundry.Hosting namespace
- Tests: merged into Foundry.UnitTests/Hosting/
- Conditionally compiled for .NETCoreApp TFMs only (net8.0+)
- Deleted standalone Hosting.AzureAIResponses project and test project
- Updated sample and solution references
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement Microsoft.Agents.AI.Hosting.AzureAIResponses to host agent-framework
AIAgents and workflows within Azure Foundry as hosted agents via the
Azure.AI.AgentServer.Responses SDK.
- AgentFrameworkResponseHandler: bridges ResponseHandler to AIAgent execution
- InputConverter: converts Responses API inputs/history to MEAI ChatMessage
- OutputConverter: converts agent response updates to SSE event stream
- ServiceCollectionExtensions: DI registration helpers
- 336 unit tests across net8.0/net9.0/net10.0 (112 per TFM)
- ResponseStreamValidator: SSE protocol validation tool for samples
- FoundryResponsesHosting sample app
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Demonstrates two MCP integration layers in a single hosted agent:
- Client-side MCP: McpClient connects to Microsoft Learn, agent handles
tool invocations locally (docs_search, code_sample_search, docs_fetch)
- Server-side MCP: HostedMcpServerTool delegates tool discovery and
invocation to the LLM provider (Responses API), no local connection
Includes DevTemporaryTokenCredential for Docker local debugging,
Dockerfile.contributor for ProjectReference builds, and the openai/v1
route mapping for AIProjectClient compatibility in Development mode.
* Add agent-framework-gemini package
* Add AGENTS.md documentation
* Add LICENSE file
* Add README.md for agent-framework-gemini package
* Add Google Gemini API keys to .env.example
* Add Google Gemini chat client implementation
* Add tests for GeminiChatClient
* Add Google Gemini agent examples
* Fix client inheritence order
* Update Gemini agent examples
* Update documentation
* Update AGENTS.md
* Add tests for JSON string handling in GeminiChatClient
* Add final response assembly test in GeminiChatClient
* Add tests for handling empty candidates in GeminiChatClient
* Improve Pydantic response handling in GeminiChatClient
* Add tests for function result resolution and callable tool normalization
* Add test for function result resolution when call_id is generated
* Refactor GeminiChatClient to correct inheritance order
Also updates constructor parameter order for environment file handling
* Enhance documentation and clarify Gemini-specific fields
* Update ThinkingConfig with new attributes and type
* Add tests for GoogleSearch and GoogleMaps configs
* Suppress valid-type mypy error on GeminiChatOptionsT
* Move service_url method near overrides
* Order _prepare_config kwargs by base then Gemini-specific
* Use FunctionCallingConfigMode for clarity and type safety
* Fix code_execution doc
* Add agent-framework-gemini to project dependencies
* Remove package from core dependencies
Initial release will be done without agent-framework-gemini in
core[all].
* Move integration tests into one file
* Remove __init__.py file from gemini tests directory
* Introduce RawGeminiChatClient as lightweight chat client
Updated GeminiChatClient to inherit from RawGeminiChatClient, maintaining full functionality with added features.
* Updated variable names from `model_id` to `model`
Across the codebase, including environment variables and client initialization. Adjusted related tests and sample scripts to reflect this change, ensuring consistency in the usage of the Gemini model identifier.
* Update AGENTS.md
* Update Gemini package to alpha status
* Fix docstrings in Gemini tests
* Change 'model_id' to 'model' in response handling
* Fix model property change in response handling
* Add built-in tool factory methods to Gemini client
Replaces boolean tool options (code_execution, google_search_grounding,
google_maps_grounding) with static factory methods that return types.Tool
objects: get_code_interpreter_tool, get_web_search_tool, get_mcp_tool,
get_file_search_tool, and get_maps_grounding_tool.
Simplifies _prepare_tools to a single translation boundary between
FunctionTool (framework) and FunctionDeclaration (Gemini API), with
types.Tool objects passed through unchanged.
* Surface code execution parts
_parse_parts now maps executable_code and code_execution_result
parts to text Content objects so callers can see the code run
and its output. Unknown part types log at debug level rather than
being silently dropped.
* Update Gemini client documentation
* Unify Gemini model name
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Update Agent Framework core version
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Add Python 3.14 in classifiers
* Replace kwargs with parameters in tool factories
* Refactor chat options handling in Gemini client
* Add tests for handling unknown and consumed keys
* Update Gemini documentation
Now reflects new options and built-in tool factory methods
* Change build system to flit
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Fix build system in pyproject.toml
* Fix type checking for generate_content_stream
---------
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Python: Skip get_final_response in OTel _finalize_stream when stream errored
When a streaming error occurs, _finalize_stream (a cleanup hook registered by
AgentTelemetryLayer) was unconditionally calling get_final_response(), which
triggers all registered result hooks including after_run context providers.
This caused providers to fire incorrectly on error paths.
Guard against this by checking result_stream._consumed: True only after
StopAsyncIteration (normal completion), False when an exception was raised.
The fix applies to both the chat client and agent telemetry layers.
Closes#5231
* Python: Expose consumed/stream_error on ResponseStream and capture error in OTel span
Address Copilot review feedback on #5232:
- Add `_stream_error: Exception | None` to ResponseStream, set in __anext__'s
except branch so cleanup hooks can inspect the failure.
- Expose public `consumed` and `stream_error` properties to avoid coupling
observability.py to private stream internals.
- Update both _finalize_stream closures (chat and agent layers) to use the
public properties and call capture_exception() with the stream error before
returning early, ensuring the OTel span records the failure rather than
closing silently.
* Python: Address Copilot review feedback on stream error handling
- Use stream_error is not None as the guard in _finalize_stream instead of
not consumed, so the early-return path is keyed precisely to actual errors
rather than any non-normal completion state.
- Clear _stream_error after _run_cleanup_hooks() completes to avoid retaining
the exception traceback (and any large object graphs it references) on the
stream instance beyond the cleanup phase.
* Python: Remove consumed/stream_error properties, use private attrs directly
Per review feedback: since observability.py and _types.py are in the same
package, accessing _stream_error directly is fine and the public properties
are unnecessary.
* Python: Fix Pyright reportPrivateUsage via inline ignore comments
Keep _stream_error private (consistent with rest of ResponseStream), and
suppress reportPrivateUsage at the call sites in observability.py with
inline pyright: ignore comments — access is intentional within the package.
* AG-UI deterministic state updates from tool results
* fix(ag-ui): address PR #5201 review comments
1. Add missing AGUIEventConverter, AGUIHttpService, __version__ to
_IMPORTS in core ag_ui lazy-export list to match the .pyi stub.
2. Coalesce predictive and deterministic state snapshots into a single
StateSnapshotEvent when both mechanisms are active on the same tool
result, reducing redundant snapshot traffic.
3. Update state_update() docstring to clarify that a predictive snapshot
may be emitted before the deterministic one when predict_state_config
is active.
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 HandoffBuilder dropping function-level middleware when cloning agents (#5173)
_clone_chat_agent() was using agent.agent_middleware (agent-level only)
instead of agent.middleware (all types), which silently dropped any
function middleware registered on the original agent.
Changed to use agent.middleware to preserve all middleware types
(agent, function, and chat) during cloning.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix HandoffBuilder dropping function-level middleware when cloning agents
Fixes#5173
* Fix false-positive middleware regression test (#5173)
The test used isinstance(m, FunctionMiddleware) which matched
_AutoHandoffMiddleware (always appended during build) instead of the
user's @function_middleware decorator. Assert directly that
tracking_middleware is present in the cloned agent's middleware list.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #5173: Python: [Bug]: HandoffBuilder drops function-level middleware when cloning agents
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Add allowed_checkpoint_types support to CosmosCheckpointStorage (#5200)
Add allowed_checkpoint_types parameter to CosmosCheckpointStorage for
parity with FileCheckpointStorage. This ensures both providers use the
same restricted pickle deserialization by default.
Changes:
- Accept allowed_checkpoint_types kwarg in __init__, stored as frozenset
- Convert _document_to_checkpoint from @staticmethod to instance method
- Forward allowed_types to decode_checkpoint_value on all load paths
- Update class docstring to describe the new parameter
- Add tests covering built-in safe types, app type opt-in/blocking,
and all load paths (load, list_checkpoints, get_latest)
- Add changelog entry noting the breaking behavior change
BREAKING CHANGE: CosmosCheckpointStorage now uses restricted pickle
deserialization by default. Checkpoints containing application-defined
types will require passing those types via allowed_checkpoint_types.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Add `allowed_checkpoint_types` support to `CosmosCheckpointStorage` for parity with `FileCheckpointStorage`
Fixes#5200
* Address PR review: add pickle security warning and fix docstring examples
- Reintroduce explicit security warning about pickle deserialization risks
- Convert Example:: block to .. code-block:: python with imports for
consistency with other docstring examples
- Note: PR title should be updated to include [BREAKING] prefix per
changelog convention (comment #3, requires GitHub UI change)
Fixes#5200
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 python-feature-lifecycle skill YAML frontmatter
Remove copyright comment that preceded the YAML frontmatter delimiter,
which prevented the skill from loading. The --- block must be the very
first line of SKILL.md.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: update broken eslint-react plugin links in devui README
The upstream eslint-react repo moved plugins from packages/plugins/
to the top-level plugins/ directory, causing 404 errors detected by
linkspector CI.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: Refactor Handoff Orchestration and add HITL support
* Change HandoffAgentExecutor to use factory-based instantiation
* Extract shared request collection logic in AIAgentUnservicedRequestsCollector
* Refactor HandoffAgentExecutor to use the "ContinueTurn" pattern as in AIAgentHostExecutor
* fix: Remove '$' from exception strings
Rename authored identifiers, XML docs, log messages, and comments
from 'folder' to 'directory' across the file skills codebase for
consistency with the agentskills.io specification and .NET conventions.
Public API changes (experimental):
- ScriptFolders → ScriptDirectories
- ResourceFolders → ResourceDirectories
.NET BCL API calls (Directory.Exists, Path.GetDirectoryName, etc.)
were already using 'directory' and are unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* support reflection for discovery of resources and scripts in class-based skills
* fix format issues
* refactor samples to use reflection
* Validate resource member signatures during discovery
Add discovery-time validation in AgentClassSkill.DiscoverResources() to
fail fast when [AgentSkillResource] is applied to members with incompatible
signatures:
- Reject indexer properties (getter has parameters)
- Reject methods with parameters other than IServiceProvider or
CancellationToken
Throws InvalidOperationException with actionable error messages instead of
allowing silent runtime failures when ReadAsync invokes the AIFunction with
no named arguments.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* prevent duplicates
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Bump Python version to 1.1.0 for a release
* Fix changelog
* 1.0.1 instead of 1.1.0
* Update CHANGELOG.md
* update version and changelog
* Bump lower bounds
* Python: Migrate GitHub Copilot package to SDK 0.2.x
Replace all imports from the non-existent copilot.types module with
correct SDK 0.2.x module paths (copilot.session, copilot.client,
copilot.tools, copilot.generated.session_events). Fix PermissionRequest
attribute access from dict-style .get() to dataclass attribute access.
Add OTel telemetry support to Copilot samples via configure_otel_providers
and document new telemetry environment variables in samples README.
* Python: Fix remaining copilot.types import in sample validation script
* Python: Include model in default_options for telemetry span attributes
* Python: Address review feedback on log_level and session kwargs typing
* Python: Scope PR to SDK 0.2.x migration only, remove net-new OTel features
- Remove RawGitHubCopilotAgent split and AgentTelemetryLayer inheritance
- Remove TelemetryConfig plumbing and OTLP/file telemetry settings
- Remove configure_otel_providers() calls from samples
- Remove telemetry env var rows from samples README
- Retain only: import path fixes, PermissionRequest attribute access fix,
log_level default fix, session kwargs typed fix, dependency pin
* Python: Update tests for SDK 0.2.x API changes
- SubprocessConfig replaces CopilotClientOptions dict
- create_session and resume_session now use keyword args
- send and send_and_wait take plain string prompt instead of MessageOptions
- on_permission_request is always required; deny-all fallback replaces omission
* Python: Pin github-copilot-sdk to >=0.2.0,<=0.2.0
Tighten the upper bound from <0.3.0 to <=0.2.0 to avoid pulling in 0.2.1+
which has breaking API changes relative to 0.2.0. The lower bound stays at
>=0.2.0 since this migration requires the 0.2.x import paths; 0.1.x would
fail at import time.
* Python: Pin github-copilot-sdk to >=0.2.1,<=0.2.1
---------
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
* Harden Python checkpoint persistence defaults
Add RestrictedUnpickler to _checkpoint_encoding.py that limits which
types may be instantiated during pickle deserialization. By default
FileCheckpointStorage now uses the restricted unpickler, allowing only:
- Built-in Python value types (primitives, datetime, uuid, decimal,
collections, etc.)
- All agent_framework.* internal types
- Additional types specified via the new allowed_checkpoint_types
parameter on FileCheckpointStorage
This narrows the default type surface area for persisted checkpoints
while keeping framework-owned scenarios working without extra
configuration. Developers can extend the allowed set by passing
"module:qualname" strings to allowed_checkpoint_types.
The decode_checkpoint_value function retains backward-compatible
unrestricted behavior when called without the new allowed_types kwarg.
Fixes#4894
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: resolve mypy no-any-return error in checkpoint encoding
Add explicit type annotation for super().find_class() return value
to satisfy mypy's no-any-return check.
Fixes#4894
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Simplify find_class return in _RestrictedUnpickler (#4894)
Remove unnecessary intermediate variable and apply # noqa: S301 # nosec
directly on the super().find_class() call, matching the established
pattern used on the pickle.loads() call in the same file.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #4894: Python: Harden Python checkpoint persistence defaults
* Restore # noqa: S301 on line 102 of _checkpoint_encoding.py (#4894)
The review feedback correctly identified that removing the # noqa: S301
suppression from the find_class return statement would cause a ruff S301
lint failure, since the project enables bandit ("S") rules. This
restores consistency with lines 82 and 246 in the same file.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #4894: Python: Harden Python checkpoint persistence defaults
* Address PR review comments on checkpoint encoding (#4894)
- Move module docstring to proper position after __future__ import
- Fix find_class return type annotation to type[Any]
- Add missing # noqa: S301 pragma on find_class return
- Improve error message to reference both allowed_types param and
FileCheckpointStorage.allowed_checkpoint_types
- Add -> None return annotation to FileCheckpointStorage.__init__
- Replace tempfile.mktemp with TemporaryDirectory in test
- Replace contextlib.suppress with pytest.raises for precise assertion
- Remove unused contextlib import
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR #4941 review comments: fix docstring position and return type
- Move module docstring before 'from __future__' import so it populates
__doc__ (comment #4)
- Change find_class return annotation from type[Any] to type to avoid
misleading callers about non-type returns like copyreg._reconstructor
(comment #2)
Comments #1, #3, #5, #6, #7, #8 were already addressed in the current code.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #4894: review comment fixes
* fix: use pickle.UnpicklingError in RestrictedUnpickler and improve docstring (#4894)
- Change _RestrictedUnpickler.find_class to raise pickle.UnpicklingError
instead of WorkflowCheckpointException, since it is pickle-level concern
that gets wrapped by the caller in _base64_to_unpickle.
- Remove now-unnecessary WorkflowCheckpointException re-raise in
_base64_to_unpickle (pickle.UnpicklingError is caught by the generic
except Exception handler and wrapped).
- Expand decode_checkpoint_value docstring to show a concrete example of
the module:qualname format with a user-defined class.
- Add regression test verifying find_class raises pickle.UnpicklingError.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: address PR #4941 review comments for checkpoint encoding
- Comment 1 (line 103): Already resolved in prior commit — _RestrictedUnpickler
now raises pickle.UnpicklingError instead of WorkflowCheckpointException.
- Comment 2 (line 140): Add concrete usage examples to decode_checkpoint_value
docstring showing both direct allowed_types usage and FileCheckpointStorage
allowed_checkpoint_types usage. Rename 'SafeState' to 'MyState' across all
docstrings for consistency, making it clear this is a user-defined class name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: replace deprecated 'builtin' repo with pre-commit-hooks in pre-commit config
pre-commit 4.x no longer supports 'repo: builtin'. Merge those hooks into
the existing pre-commit-hooks repo entry.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* style: apply pyupgrade formatting to docstring example
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: resolve pre-commit hook paths for monorepo git root
The poe-check and bandit hooks referenced paths relative to python/
but pre-commit runs hooks from the git root (monorepo root). Fix
poe-check entry to cd into python/ first, and update bandit config
path to python/pyproject.toml.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix pre-commit config paths for prek --cd python execution
Revert bandit config path from 'python/pyproject.toml' to 'pyproject.toml'
and poe-check entry from explicit 'cd python' wrapper to direct invocation,
since prek --cd python already sets the working directory to python/.
Also apply ruff formatting fixes to cosmos checkpoint storage files.
Fixes#4894
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: add builtins:getattr to checkpoint deserialization allowlist
Pickle uses builtins:getattr to reconstruct enum members (e.g.,
WorkflowMessage.type which is a MessageType enum). Without it in the
allowlist, checkpoint roundtrip tests fail with
WorkflowCheckpointException.
Fixes#4894
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #4894: review comment fixes
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@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>
* .NET: Add JsonSerializerOptions support to programmatic skill APIs
Allow callers to pass custom JsonSerializerOptions when creating inline
resources and scripts via AgentInlineSkill, AgentClassSkill,
AgentInlineSkillResource, and AgentInlineSkillScript. A skill-level
default can be set on AgentInlineSkill and overridden per-resource/
script call.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/TestSkillTypes.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
_prepare_options() now removes tools, tool_choice, and parallel_tool_calls
from run_options after injecting agent_reference. The Foundry API rejects
requests containing both fields. FunctionTools are still invoked client-side
by the function invocation layer.
Fixes#5087
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
* Guard against empty text in _parse_structured_response_value (#5145)
When using response_format with background=True (Responses API), polling
an in-progress response produces empty text. _parse_structured_response_value
unconditionally passed this to model_validate_json/json.loads, causing
ValidationError or JSONDecodeError.
Add an early return of None when text is empty, matching the existing
guard for response_format=None. This allows .value to safely return None
for in-progress background responses.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix `response_format` crash on background polling with empty text
Fixes#5145
---------
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>
* fix: Concurrent Workflow Sample
* Switch to using Azure AI Projects APIs
* Remove agent streaming outputs by changing emitEvents to false on TurnToken
* Disable forwarding input from agent host executors
* Make output format more legible
* refactor: Update Concurrent sample to use message delivery event callback
Adds a public CreateSessionAsync(string conversationId, CancellationToken)
method to FoundryAgent that delegates to the inner ChatClientAgent,
allowing users to create sessions with existing server-side conversation IDs.
Fixes#5138
* add class-based skills
* address formating issues
* Remove generated filtered-unit.slnx and add to .gitignore
The filtered solution file is generated dynamically by
eng/scripts/New-FilteredSolution.ps1 during CI. Checking it in
risks it becoming stale and out-of-sync with the real solution.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove generated filtered-unit.slnx and add to .gitignore
The filtered solution file is generated dynamically by
eng/scripts/New-FilteredSolution.ps1 during CI. Checking it in
risks it becoming stale and out-of-sync with the real solution.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* discover scripts and resource from folders defined in spec
* Remove Step05 and Step06 DI skill samples
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* address review comments
* fix build error
* Fix mixed path separators in skill folder discovery on .NET Framework
Path.Combine with forward-slash folder names (e.g. "scripts/f1") produces
mixed separators on Windows, causing the StartsWith containment check to
fail against Path.GetFullPath-resolved file paths. Wrap in Path.GetFullPath
to canonicalize separators before the containment comparison.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* address comment
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Improve workflow unit tests
* Update test name prefix for clarity.
* Update tests to surface any errors.
* fix check-point restore-time race in off-thread workflow event stream
Previously, unhandled exceptions from agent execution would bubble up
to the SDK orchestrator, which emits a generic 'An internal server
error occurred.' message — hiding the actual cause (e.g., 401 auth
failures, model not found, etc.).
Now AgentFrameworkResponseHandler catches non-cancellation exceptions
and emits a proper response.failed event containing the real error
message, making it visible to clients and in logs.
OperationCanceledException still propagates for proper cancellation
handling by the SDK.
Also bumps package version to 0.9.0-hosted.260403.2.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- InputConverter: stop propagating request.Model to ChatOptions.ModelId
Hosted agents use their own model; client-provided model values like
'hosted-agent' were being passed through and causing server errors.
- Add FoundryResponsesRepl sample: interactive CLI client that connects
to a Foundry Responses endpoint using ResponsesClient.AsAIAgent()
- Bump package version to 0.9.0-hosted.260403.1
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move source and test files from the standalone Hosting.AzureAIResponses project
into the Foundry package under a Hosting/ subfolder. This consolidates the
Foundry-specific hosting adapter into the main Foundry package.
- Source: Microsoft.Agents.AI.Foundry.Hosting namespace
- Tests: merged into Foundry.UnitTests/Hosting/
- Conditionally compiled for .NETCoreApp TFMs only (net8.0+)
- Deleted standalone Hosting.AzureAIResponses project and test project
- Updated sample and solution references
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement Microsoft.Agents.AI.Hosting.AzureAIResponses to host agent-framework
AIAgents and workflows within Azure Foundry as hosted agents via the
Azure.AI.AgentServer.Responses SDK.
- AgentFrameworkResponseHandler: bridges ResponseHandler to AIAgent execution
- InputConverter: converts Responses API inputs/history to MEAI ChatMessage
- OutputConverter: converts agent response updates to SSE event stream
- ServiceCollectionExtensions: DI registration helpers
- 336 unit tests across net8.0/net9.0/net10.0 (112 per TFM)
- ResponseStreamValidator: SSE protocol validation tool for samples
- FoundryResponsesHosting sample app
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* add class-based skills
* address formating issues
* Remove generated filtered-unit.slnx and add to .gitignore
The filtered solution file is generated dynamically by
eng/scripts/New-FilteredSolution.ps1 during CI. Checking it in
risks it becoming stale and out-of-sync with the real solution.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove generated filtered-unit.slnx and add to .gitignore
The filtered solution file is generated dynamically by
eng/scripts/New-FilteredSolution.ps1 during CI. Checking it in
risks it becoming stale and out-of-sync with the real solution.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* consolidate DI samples into one
* fix file encoding
* suppress compatibility warning
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add github actions workflow for verify-samples
* Make workflow run as part of PR (for now)
* Update workflow to remove pr trigger
* Address PR comments
* fix: Remove Timeout from InputWait in StreamingRunEventStream
* fix: Race condition when the workflow executes to halt before TakeEventStream
* test: Make the OffThread Delay test more nimble
* fix: Remove slight window where runStatus could be stale
* Fix GitHubCopilotAgent not calling context provider hooks (#3984)
GitHubCopilotAgent accepted context_providers in its constructor but
never called before_run()/after_run() on them in _run_impl() or
_stream_updates(), silently ignoring all context providers.
Add _run_before_providers() helper to create SessionContext and invoke
before_run on each provider. Both _run_impl() and _stream_updates() now
run the full provider lifecycle: before_run before sending the prompt
(with provider instructions prepended) and after_run after receiving the
response. This follows the same pattern used by A2AAgent.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix GitHubCopilotAgent to invoke context provider before_run/after_run hooks
Fixes#3984
* fix(#3984): address review feedback for context provider integration
- Build prompt from session_context.get_messages(include_input=True) so
provider-injected context_messages are included in both non-streaming
and streaming paths (review comments #1, #2)
- Preserve timeout in opts (use get instead of pop) so providers can
observe it via context.options (review comment #3)
- Eliminate streaming double-buffer: move after_run invocation to a
ResponseStream result_hook (matching Agent class pattern) instead of
maintaining a separate updates list in the generator (review comment #4)
- Improve _run_before_providers docstring
Add tests for:
- Context messages included in prompt (non-streaming + streaming)
- Error path: after_run NOT called when send_and_wait/streaming raises
- Multiple providers: forward before_run, reverse after_run ordering
- BaseHistoryProvider with load_messages=False is skipped
- Streaming after_run response contains aggregated updates
- Streaming with no updates still sets empty response
- Timeout preserved in session context options for providers
Note: _run_before_providers remains on GitHubCopilotAgent for now. A
follow-up PR should extract it to BaseAgent so subclasses can reuse it
without duplicating the provider iteration logic.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #3984: Python: [Bug]: GitHubCopilotAgent Memory Example
* refactor(#3984): promote _run_before_providers to BaseAgent
Move _run_before_providers from GitHubCopilotAgent into BaseAgent,
mirroring the existing _run_after_providers helper. Agent's
_prepare_session_and_messages now delegates to the shared base method,
eliminating the near-duplicate provider iteration logic that could
drift as the provider contract evolves.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #3984: Python: [Bug]: GitHubCopilotAgent Memory Example
* revert: keep _run_before_providers in GitHubCopilotAgent only
Undo the promotion of _run_before_providers to BaseAgent. The method
stays in GitHubCopilotAgent where it is needed, and _agents.py
retains its original inline provider iteration in RawAgent.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: replace deprecated BaseContextProvider/BaseHistoryProvider with ContextProvider/HistoryProvider
Update imports and usages in GitHubCopilotAgent and its tests to use
the new non-deprecated class names from the core package.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: address review feedback - reorder providers before session, wrap streaming after_run in try/except, assert after_run on skipped HistoryProvider
- Move _run_before_providers before _get_or_create_session so provider
contributions can affect session configuration
- Wrap _run_after_providers in try/except in streaming _after_run_hook
to prevent provider errors from replacing successful responses
- Add after_run assertion to test_history_provider_skip_when_load_messages_false
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>
Add deduplication to `prepend_instructions_to_messages()` to skip
instructions that are already present as leading messages with the
same role and text. This prevents duplicate system messages when
instructions are injected by multiple layers (e.g. Agent + chat client).
Fixes#5049
* Update Foundry Responses as ChatClientAgent
* Migrate obsolete AzureAI integration tests to versioned agent pattern
Replace obsolete CreateAIAgentAsync/GetAIAgentAsync calls with
Agents.CreateAgentVersionAsync() + AsAIAgent(AgentVersion) in all
AzureAI integration tests.
- Rename AIProjectClient* test files to FoundryVersionedAgent*
- Register AIFunction tools in PromptAgentDefinition.Tools for
server-side visibility via AsOpenAIResponseTool()
- Skip structured output tests (AzureAIProjectChatClient clears
ResponseFormat for versioned agents)
- Remove all [Obsolete] attributes and #pragma warning disable CS0618
* Merge FoundryMemory package into AzureAI under Memory/ folder
Move all FoundryMemory source, unit tests, and integration tests into
the Microsoft.Agents.AI.AzureAI package. Change namespace from
Microsoft.Agents.AI.FoundryMemory to Microsoft.Agents.AI.AzureAI.
- Add [Experimental] to FoundryMemoryProviderOptions and Scope
- Rename internal AIProjectClientExtensions to MemoryStoreExtensions
- Update AzureAI .csproj with Compliance.Abstractions, Redaction
- Remove FoundryMemory from solution and release filter
- Update sample to reference AzureAI instead of FoundryMemory
- Delete old Microsoft.Agents.AI.FoundryMemory project and tests
* Add EnsureMemoryStoreCreatedAsync and memory existence checks to integration tests
- Ensure memory store is created before testing memory operations
- Add AZURE_AI_EMBEDDING_DEPLOYMENT_NAME config setting
- Assert memories exist in store via SearchMemoriesAsync before cleanup
- Verify scope isolation with direct memory store queries
* Fix and rename AzureAI unit tests for RAPI vs Versioned clarity
- Rename AsAIAgentAsync_* to AsAIAgent_* (drop Async from method group)
- Add _Rapi_ prefix to non-versioned (Responses API) tests
- Add _Versioned_ prefix to versioned agent tests where needed
- Fix RAPI tests: assert GetService<AIProjectClient>() is null
- Fix Versioned tests: assert IsType<FoundryAgent> and
GetService<AIProjectClient>() returns the client instance
- Fix UserAgent header tests: proper HTTP handler routing
- Fix ChatClient_UsesDefaultConversationIdAsync test setup
- All 153 unit tests pass with 0 failures
* Rename Microsoft.Agents.AI.AzureAI to Microsoft.Agents.AI.Foundry
Rename the project, namespace, folder, and all references from
Microsoft.Agents.AI.AzureAI to Microsoft.Agents.AI.Foundry.
Also rename Workflows.Declarative.AzureAI to .Foundry.
- Rename src, unit test, integration test, and workflow folders
- Update namespaces in all source and test .cs files
- Update ProjectReferences in ~47 sample and test .csproj files
- Update solution files (.slnx, .slnf)
- Update sample using statements
- Update READMEs, SKILL.md, ADRs in docs/
- Disable package validation baseline for renamed packages
- Fix UTF-8 BOM encoding on all affected .cs files
- AzureAI.Persistent left completely unchanged
* Fix format: remove ImplicitUsings, add explicit usings, fix BOM encoding
- Remove ImplicitUsings=enable from Foundry csproj to resolve IDE0005
on shared ReplacingRedactor.cs
- Add explicit System usings to all source files that relied on them
- Sort usings alphabetically per editorconfig rules
- Fix UTF-8 BOM on 12 sample Program.cs files
- Rename Azure AI Foundry Agents to Microsoft Foundry Agents in docs
* Python: Fix broken samples and add missing READMEs
- simple_context_provider: move instructions kwarg into options dict
- suspend_resume_session: use OpenAIChatCompletionClient for in-memory demo
- foundry_chat_client_with_hosted_mcp: move store kwarg into options dict
- Add README.md for context_providers and conversations sample folders
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix additional sample issues in context_providers
- mem0_basic: send preferences query before sleep so Mem0 can learn them,
print result from new session recall
- mem0_sessions: add session for multi-turn conversation in agent-scoped
example, remove user_id from agent-scoped provider (Mem0 API stores
memories without user_id when agent_id is provided), use single message
for storing preferences
- redis_basics: print retrieved context messages instead of raw object
- redis_sessions: add missing load_dotenv() call
- redis_basics/redis_sessions: fix docstrings referencing wrong client type
- azure_redis_conversation: replace duplicate copyright with load_dotenv()
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix broken link in declarative README
openai_responses_agent.py was renamed to openai_agent.py
Co-authored-by: Copilot <223556219+Copilot@users.noreply.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>
* Fix RequestInfoEvent lost when resuming workflow from checkpoint
* Fix streaming run double disposal in tests and lockstep republishing before Started event is emitted.
* Fix bug to remove messages after sending to avoid losing messages on send failure.
* Fix declarative test harness
* Fix agent_with_hosted_mcp sample to use AzureOpenAIResponsesClient (#4861)
The agent_with_hosted_mcp sample used AzureOpenAIChatClient with an MCP tool
dict, but the Chat Completions API only supports 'function' and 'custom' tool
types, not 'mcp'. This caused a 400 error at runtime.
Switch the sample to AzureOpenAIResponsesClient which natively supports MCP
tools via the Responses API. Use get_mcp_tool() to construct the tool config.
Changes:
- main.py: Replace AzureOpenAIChatClient with AzureOpenAIResponsesClient
- requirements.txt: Update azure-ai-agentserver-agentframework to 1.0.0b16
and use agent-framework-azure-ai package
- agent.yaml: Use AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME env var
- Add regression test documenting chat client MCP tool passthrough behavior
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix agent_with_hosted_mcp sample to use Responses API client for MCP tools
Fixes#4861
* Remove REPRODUCTION_REPORT.md investigation artifact (#4861)
Remove the reproduction report markdown file from the test directory.
Investigation notes belong in the GitHub issue or PR description,
not as committed files in the source tree. The regression test in
test_openai_chat_client.py already provides automated verification.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add MCP tool API rejection regression test (#4861)
Add test_mcp_tool_dict_causes_api_rejection to verify that MCP tool
dicts passed through to the Chat Completions API result in a clear
ChatClientException rather than being silently dropped. This completes
the regression test coverage requested in code review.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* small fix
* Revert deletion of dotnet local.settings.json files
Restore the two local.settings.json files that were accidentally deleted in this PR.
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 _add_text_reasoning_content dropping id during coalescing (#4852)
Preserve the id field (rs_* identifier) when coalescing text_reasoning
Content objects by passing id=self.id or other.id to the Content
constructor. This fixes the encrypted reasoning round-trip where the
missing id prevented _prepare_content_for_openai from including it in
the serialized reasoning item.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix `_add_text_reasoning_content` to preserve `id` during coalescing
Fixes#4852
* Raise AdditionItemMismatch on conflicting text_reasoning ids (#4852)
Detect when both operands have different non-empty ids during
text_reasoning Content coalescing and raise AdditionItemMismatch
instead of silently keeping one. This prevents mis-associating
encrypted_content during round-trips.
Also adds tests for conflicting ids and the neither-has-id edge case.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #4852: Python: [Bug]: Content._add_text_reasoning_content drops id during coalescing, breaking encrypted reasoning round-trip
* test fix
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Remove unsupported memory scoping params from samples and docs
Fixes#4353
The `Mem0ContextProvider` and `RedisContextProvider` no longer support
`thread_id` or `scope_to_per_operation_thread_id` parameters. This commit
updates the affected samples and READMEs to use only the currently
supported API (`user_id`, `agent_id`, `application_id`).
Changes:
- mem0_sessions.py: Remove `thread_id` and
`scope_to_per_operation_thread_id` from examples 1 and 2, rewrite to
demonstrate user-scoped and agent-scoped memory patterns
- redis_sessions.py: Update module docstring to remove references to
removed thread scoping params
- mem0/README.md: Update Memory Scoping docs to reflect current API
- redis/README.md: Remove `thread_id` and
`scope_to_per_operation_thread_id` references from docs
* Address Copilot review: rename thread_scope functions, fix docstring
- Rename `example_global_thread_scope` -> `example_global_memory_scope`
- Rename `example_per_operation_thread_scope` -> `example_agent_scoped_memory`
- Update example 2 docstring to mention `application_id` alongside
`user_id` and `agent_id` since it's set in the provider config
- Update module docstring scenario 2 to include `application_id`
* fix: rebase onto main, address giles17 review feedback
- Resolve merge conflicts by rebasing all 4 original files onto current main
- Address giles17's agent review suggestions:
- mem0_basic.py: update comment to remove thread_id from scoping list
- mem0_oss.py: update comment to remove thread_id from scoping list
- redis_sessions.py: rename Example 2 from "Agent-Scoped Memory" to
"Hybrid Vector Search" to accurately describe what it demonstrates
- redis/README.md: update Example 2 description to match renamed example
---------
Co-authored-by: Tao Chen <taochen@microsoft.com>
Co-authored-by: Giles Odigwe <79032838+giles17@users.noreply.github.com>
* Add Python A2A agent-as-function-tools sample
Port of the .NET A2AAgent_AsFunctionTools sample to Python.
Resolves a remote A2A agent card, converts each skill to a
FunctionTool via as_tool(), and registers them with a host agent
using AzureOpenAIResponsesClient.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Sanitize A2A skill names before passing to as_tool()
as_tool() only auto-sanitizes when name is omitted. Since we pass
skill.name explicitly, we need to strip special characters ourselves.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Add header_provider to MCPStreamableHTTPTool (#4808)
Add a header_provider callback parameter to MCPStreamableHTTPTool that
enables injecting dynamic per-request HTTP headers from runtime kwargs
(originating from FunctionInvocationContext.kwargs set in agent middleware).
The implementation uses contextvars and httpx event hooks to ensure headers
are task-local and safe for concurrent tool calls:
- header_provider receives the runtime kwargs dict and returns headers
- call_tool sets a ContextVar before delegating to MCPTool.call_tool
- An httpx request event hook reads from the ContextVar and injects headers
Example usage:
mcp_tool = MCPStreamableHTTPTool(
name="web-api",
url="https://api.example.com/mcp",
header_provider=lambda kwargs: {
"X-Auth-Token": kwargs.get("auth_token", ""),
},
)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #4808: Python: [Bug]: Unable to pass AgentContext to MCPStreamableHTTPTool
* Add test for header_provider via FunctionTool.invoke with FunctionInvocationContext
Addresses PR review comment: exercises the full pipeline from
FunctionInvocationContext.kwargs through FunctionTool.invoke to
MCPStreamableHTTPTool.call_tool and header_provider, rather than
testing call_tool in isolation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #4808: review comment fixes
* Fix streamable MCP transport defaults
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix Azure AI test client mocks
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix MCP runtime kwarg regressions
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Stabilize MCP tool runtime kwargs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Use context kwargs in MCP wrappers
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* updated mcp samples
* fix link
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix sample bugs: incorrect API params, wrong client types, and invalid options
- typed_options.py: Fix AnthropicClient model->model_id, wrap raw strings in Message objects for get_response(), fix reasoning_effort->reasoning dict, fix budget_tokens minimum (1024), use OpenAIChatClient not FoundryChatClient, remove unused import
- client_reasoning.py: Fix deprecated model_id to model param
- client_with_hosted_mcp.py: Remove invalid store=True kwarg from Agent.run()
- code_defined_skill.py: Fix precision kwarg to use function_invocation_kwargs
- Various other samples: Fix deprecated API usage and incorrect params
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review comments
- client_with_hosted_mcp.py: Fix remaining store=True kwarg on line 68 to use options dict
- client_with_session.py: Change store=True to store=False to match in-memory persistence demo intent
- typed_options.py: Remove non-existent import and model key from docstring example
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* new sample fixes
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Improve CONTRIBUTING.md with dev setup links and docs guidance
- Consolidate Development Scripts into a Development Setup section with
quick links to language-specific dev guides and coding standards
- Add Python build/test/lint commands alongside existing .NET commands
- Add Documentation Contributions section with link checker, writing
guidelines, and style guidance
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Use directory note for .NET commands, matching Python style
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Split test commands into unit vs. integration for both Python and .NET
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove Documentation Contributions section
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Foundry Evals integration for Python
Merged and refactored eval module per Eduard's PR review:
- Merge _eval.py + _local_eval.py into single _evaluation.py
- Convert EvalItem from dataclass to regular class
- Rename to_dict() to to_eval_data()
- Convert _AgentEvalData to TypedDict
- Simplify check system: unified async pattern with isawaitable
- Parallelize checks and evaluators with asyncio.gather
- Add all/any mode to tool_called_check
- Fix bool(passed) truthy bug in _coerce_result
- Remove deprecated function_evaluator/async_function_evaluator aliases
- Remove _MinimalAgent, tighten evaluate_agent signature
- Set self.name in __init__ (LocalEvaluator, FoundryEvals)
- Limit FoundryEvals to AsyncOpenAI only
- Type project_client as AIProjectClient
- Remove NotImplementedError continuous eval code
- Add evaluation samples in 02-agents/ and 03-workflows/
- Update all imports and tests (167 passing)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: resolve mypy redundant-cast errors while keeping pyright happy
Use cast(list[Any], x) with type: ignore[redundant-cast] comments to
satisfy both mypy (which considers casting Any redundant) and pyright
strict mode (which needs explicit casts to narrow Unknown types).
Also fix evaluator decorator check_name type annotation to be
explicitly str, resolving mypy str|Any|None mismatch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: CI failures — pyupgrade, evaluator overloads, sample API, reset attr
- Apply pyupgrade: Sequence from collections.abc, remove forward-ref quotes
- Add @overload signatures to evaluator() for proper @evaluator usage
- Fix evaluate_workflow sample to use WorkflowBuilder(start_executor=) API
- Fix _workflow.py executor.reset() to use getattr pattern for pyright
- Remove unused EvalResults forward-ref string in default_factory lambda
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: skip gRPC-dependent observability test
The test_configure_otel_providers_with_env_file_and_vs_code_port test
triggers gRPC OTLP exporter creation, but the grpc dependency is
optional and not installed by default. Add skipif decorator matching
the pattern used by all other gRPC exporter tests in the same file.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: add nosec B101 for bandit assert check
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* style: align eval samples with repo conventions
- Move module docstrings before imports (after copyright header)
- Add -> None return type to all main() and helper functions
- Fix line-too-long in multiturn sample conversation data
- Add Workflow import for typed return in all_patterns_sample
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review feedback: async fixes, sample bugs, deprecation warnings
- Simplify _ensure_async_result to direct await (async-only clients)
- Replace get_event_loop() with get_running_loop()
- Narrow _fetch_output_items exception handling to specific types
- Add warning log when _filter_tool_evaluators falls back to defaults
- Add DeprecationWarning to options alias in Agent.__init__
- Add DeprecationWarning to evaluate_response()
- Rename raw key to _raw_arguments in convert_message fallback
- Fix evaluate_agent_sample.py: replace evals.select() with FoundryEvals()
- Fix evaluate_multiturn_sample.py: use Message/Content/FunctionTool types
- Fix evaluate_workflow_sample.py: replace evals.select() with FoundryEvals()
- Update test mocks to use AsyncMock for awaited API calls
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add test coverage for review feedback items
- Add num_repetitions=2 positive test verifying 2×items and 4 agent calls
- Add _poll_eval_run tests: timeout, failed, and canceled paths
- Add evaluate_traces tests: validation error, response_ids path, trace_ids path
- Add evaluate_foundry_target happy-path test with target/query verification
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix ruff ISC004 lint error and apply formatter
- Wrap implicit string concatenation in parens in evaluate_multiturn_sample.py
- Apply ruff formatter to 6 other files with minor formatting drift
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove core type changes (extracted to fix/workflow-stale-session branch)
Reverts changes to _agents.py, _agent_executor.py, and _workflow.py
back to upstream/main. These fixes are now in a separate PR.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review round 2: bugs, tests, and architecture
Code fixes:
- Fix _normalize_queries inverted condition (single query now replicates
to match expected_count)
- Fix substring match bug: 'end' in 'backend' matched; use exact set
lookup for executor ID filtering
- Fix used_available_tools sample: tool_definitions→tools param, use
FunctionTool attribute access instead of dict .get()
- Add None-check in _resolve_openai_client for misconfigured project
- Add Returns section to evaluate_workflow docstring
- Cache inspect.signature in @evaluator wrapper (avoid per-item reflection)
Architecture:
- Extract _evaluate_via_responses as module-level helper; evaluate_traces
now calls it directly instead of creating a FoundryEvals instance
- Move Foundry-specific typed-content conversion out of core to_eval_data;
core now returns plain role/content dicts, FoundryEvals applies
AgentEvalConverter in _evaluate_via_dataset
Tests:
- evaluate_response() deprecation warning emission and delegation
- num_repetitions > 1 with expected_output and expected_tool_calls
- Mock output_items.list in test_evaluate_calls_evals_api
- Update to_eval_data assertions for plain-dict format
- Unknown param error now raised at @evaluator decoration time
Skipped (separate PR): executor reset loop, xfail removal, options alias
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix CI: revert test_full_conversation, fix pyright errors
- Revert test_full_conversation.py to upstream/main (the session
preservation test was incorrectly changed to assert clearing)
- Fix pyright reportUnnecessaryComparison on get_openai_client() None
check by adding ignore comment
- Fix pyright reportPrivateUsage: add public EvalItem.split_messages()
method and use it in FoundryEvals._evaluate_via_dataset instead of
accessing private _split_conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review round 3: reliability, test gaps, cleanup
- Add try/except guard for non-numeric score in _coerce_result
- Add poll_interval minimum bound (0.1s) to prevent tight loops
- Add runtime async client check in _resolve_openai_client
- Remove _ensure_async_result wrapper (10 call sites → direct await)
- Better error message when queries provided without agent
- Import-time asserts for evaluator set consistency
- Remove 28 redundant @pytest.mark.asyncio decorators
- Add doc note about _raw_arguments sensitive data
- Tests: tool_called_check mode=any, _normalize_queries branches,
_extract_result_counts paths, _extract_per_evaluator, bare check
via evaluate_agent, output_items assertion, modulo wrapping,
async client check, queries-without-agent error
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix CI: ruff S101 assert, pyright and mypy arg-type errors
- Replace module-level assert with if/raise for evaluator set
consistency checks (ruff S101 disallows bare assert)
- Add type: ignore[arg-type] and pyright: ignore[reportArgumentType]
on OpenAI SDK evals API calls that pass dicts where typed params
are expected (SDK accepts dicts at runtime)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review round 4: bugs, reliability, test fixes
- Fix all_passed ignoring parent result_counts when sub_results present
- Fix _extract_tool_calls: parse string arguments via json.loads before
falling back to None (real LLM responses use string arguments)
- Sanitize _raw_arguments to '[unparseable]' to avoid leaking sensitive
tool-call data to external evaluation services
- Add NOTE comment on to_eval_data message serialization dropping
non-text content (tool calls, results)
- Eliminate double conversation split in _evaluate_via_dataset: build
JSONL dicts directly from split_messages + AgentEvalConverter
- Raise poll_interval floor from 0.1s to 1.0s to prevent rate-limit
exhaustion
- Fix MagicMock(name=...) bug in test: sets display name not .name attr
- Fix mock_output_item.sample: use MagicMock object instead of dict so
_fetch_output_items exercises error/usage/input/output extraction
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review round 5: reliability, docs, test coverage
Code fixes:
- Move import-time RuntimeError checks to unit tests (avoids breaking
imports for all users on developer set-drift mistake)
- _filter_tool_evaluators now raises ValueError when all evaluators
require tools but no items have tools (was silently substituting)
- Add poll_interval upper bound (60s) to prevent single-iteration sleep
- Log exc_info=True in _fetch_output_items for debugging API changes
- Fix evaluate() docstring: remove claim about Responses API optimization
- Validate target dict has 'type' key in evaluate_foundry_target
- Document to_eval_data() limitation: non-text content is omitted
Tests:
- TestEvaluatorSetConsistency: verify _AGENT/_TOOL subsets of _BUILTIN
- TestEvaluateTracesAgentId: agent_id-only path with lookback_hours
- TestFilterToolEvaluatorsRaises: ValueError on all-tool no-items
- TestEvaluateFoundryTargetValidation: target without 'type' key
- Assert items==[] on failed/canceled poll results
- Mock output_items.list in response_ids test for full flow
- TestAllPassedSubResults: result_counts=None + sub_results delegation
and parent failures override sub_results
- TestBuildOverallItemEmpty: empty workflow outputs returns None
Skipped r5-07 (_raw_arguments length hint): marginal debugging value,
could leak content size information.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix error message: evaluate_responses() → evaluate_traces(response_ids=...)
The referenced function doesn't exist; the correct API is
evaluate_traces(response_ids=...) from the azure-ai package.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove dead to_eval_data() method, fix docstring claims
- Remove to_eval_data() from EvalItem (dead code after r4-05 JSONL refactor)
- Migrate 15 tests from to_eval_data() to split_messages()
- Update sample to use split_messages() + Message properties
- Remove unimplemented Responses API optimization docstring claim
- Update split_messages() docstring to not reference removed method
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Reduce default eval timeout from 600s to 180s (3 minutes)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove dead _evaluate_via_responses method from FoundryEvals
The method was never called — evaluate() uses _evaluate_via_dataset,
and evaluate_traces() calls _evaluate_via_responses_impl directly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Revert unrelated formatting changes to get-started samples
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix pyright: remove phantom FoundryMemoryProvider import, apply ruff format
- Remove import of non-existent _foundry_memory_provider module
(incorrectly kept during rebase conflict resolution)
- Apply ruff formatter to test_local_eval.py and get-started samples
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix eval samples: use FoundryChatClient for Agent()
The upstream provider-leading client refactor (#4818) made client=
a required parameter on Agent(). Update the three getting-started
eval samples to use FoundryChatClient with FOUNDRY_PROJECT_ENDPOINT,
matching the standard pattern from 01-get-started samples.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Simplify self-reflection sample using FoundryEvals
Replace ~80 lines of manual OpenAI evals API code (create_eval,
run_eval, manual polling, raw JSONL params) with FoundryEvals:
- evaluate_groundedness() uses FoundryEvals.evaluate() with EvalItem
- Remove create_openai_client(), create_eval(), run_eval() functions
- Remove openai SDK type imports (DataSourceConfigCustom, etc.)
- run_self_reflection_batch creates FoundryEvals instance once,
reuses it for all iterations across all prompts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update eval samples to FoundryChatClient and FOUNDRY_PROJECT_ENDPOINT
- Migrate all foundry_evals samples from AzureOpenAIResponsesClient to FoundryChatClient
- Update env var from AZURE_AI_PROJECT_ENDPOINT to FOUNDRY_PROJECT_ENDPOINT
- Use AzureCliCredential consistently across all samples
- Fix README.md: correct function names (evaluate_dataset -> FoundryEvals.evaluate, evaluate_responses -> evaluate_traces)
- Update self_reflection .env.example and README.md
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix lint errors in eval samples (E501, ASYNC240, formatting)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove evaluate_all_patterns_sample.py (redundant with focused samples)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix async credential mismatch: use azure.identity.aio for async AIProjectClient
AIProjectClient from azure.ai.projects.aio requires an async credential.
Switch all foundry_evals samples from azure.identity.AzureCliCredential
to azure.identity.aio.AzureCliCredential. Also pass project_client to
FoundryChatClient instead of duplicating endpoint+credential.
Close credential in self_reflection sample to avoid resource leak.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Revert test_observability.py to upstream/main (not our test)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address moonbox3 review: sphinx docstrings, pagination, isinstance check
- Convert all Example:: / Typical usage:: code blocks to .. code-block:: python
format matching codebase convention (both _evaluation.py and _foundry_evals.py)
- Add async pagination in _fetch_output_items via async for (handles large result sets)
- Replace hasattr(__aenter__) with isinstance(client, AsyncOpenAI) in _resolve_openai_client
- Move AsyncOpenAI import from TYPE_CHECKING to runtime (needed for isinstance)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix test failures and address remaining moonbox3 review comments
- Fix tests: use MagicMock(spec=AsyncOpenAI) for project_client mocks
(isinstance check now requires proper type, not duck-typing)
- Fix tests: replace mock_page.__iter__ with _AsyncPage helper for async for
- Fix evaluate_response: auto-extract queries from response messages when
query is not provided (previously always raised ValueError)
- Add debug logging when skipping internal _-prefixed executor IDs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address Tao's PR review comments on Foundry Evals
- T1: Add comment explaining builtin.* pass-through in _resolve_evaluator
- T2: Add comment referencing OpenAI evals API for testing_criteria dict
- T3: Document Mustache-style {{item.*}} template placeholders
- T4: Document poll loop 60s sleep upper bound rationale
- T5: Narrow run type to RunRetrieveResponse, use typed field access
instead of vars()/getattr dance in _extract_result_counts and
_extract_per_evaluator; use run.error and run.report_url directly
- T6: Clarify openai_client docstring re: Azure Foundry endpoint
- T8: Remove misleading empty expected_tool_calls from sample
- Update tests to match real SDK PerTestingCriteriaResult shape
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove unnecessary Any union from run type annotations
RunRetrieveResponse is the correct type — no backward compat needed
for a brand new feature.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Accept FoundryChatClient instead of raw AsyncOpenAI
FoundryEvals now takes client: FoundryChatClient as its primary
parameter instead of openai_client: AsyncOpenAI. The builtin.*
evaluators require a Foundry endpoint, so the type should reflect that.
- FoundryEvals.__init__: client: FoundryChatClient replaces openai_client
- evaluate_traces / evaluate_foundry_target: same change
- _resolve_openai_client: extracts .client from FoundryChatClient
- project_client fallback retained for standalone functions
- All samples updated to construct FoundryChatClient and pass as client=
- Tests updated (openai_client= → client=)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove implicit 60s upper bound on poll interval
If a developer sets a higher poll_interval, respect it. Only clamp
to remaining time and enforce a 1s minimum for rate-limit protection.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove 1s floor on poll interval — let the developer control it
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update python/samples/05-end-to-end/evaluation/foundry_evals/.env.example
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Update python/samples/02-agents/evaluation/evaluate_agent.py
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Address eavanvalkenburg review (round 2) on Python eval PR
- Rename model_deployment -> model across FoundryEvals and all samples
- Make model param optional, resolves from client.model
- Convert EvalResults from dataclass to regular class
- Remove deprecated evaluate_response() function
- Refactor splitters: BUILT_IN_SPLITTERS dict + standalone functions
- Change per_turn_items from classmethod to staticmethod
- Simplify EvalCheck type alias to use Awaitable[CheckResult]
- Remove errored property from EvalResults
- Remove default value from Evaluator protocol eval_name
- Rename assert_passed -> raise_for_status, add EvalNotPassedError
- Type agent param as SupportsAgentRun | None
- Fix Arguments docstring
- Update __init__.py exports
- Update all tests and samples
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Move FoundryEvals to foundry package, split tool eval sample
- Move _foundry_evals.py from azure-ai to foundry package
- Move test_foundry_evals.py to foundry/tests/
- Update lazy re-exports in agent_framework.foundry namespace
- Update .pyi type stubs
- All samples now import from agent_framework.foundry
- Split tool-call evaluation into evaluate_tool_calls_sample.py
- Fix all_passed to check errored count from result_counts
- Fix raise_for_status to include errored item details
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Auto-create FoundryChatClient from env vars when no client provided
FoundryEvals() now works zero-config when FOUNDRY_PROJECT_ENDPOINT and
FOUNDRY_MODEL environment variables are set. Auto-creates a FoundryChatClient
under the hood, matching the established env var pattern.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix pyright errors: remove dead _normalize_queries, suppress EvalAPIError check
- Remove unused _normalize_queries function and its tests
- Add pyright ignore for EvalAPIError None check (defensive guard)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Support multimodal image content in eval pipeline
Add image (data/uri) content handling to AgentEvalConverter.convert_message()
so that Content.from_data() and Content.from_uri() image payloads are
preserved as input_image parts in the Foundry evaluator format.
- Handle Content type='data' and type='uri' → emit input_image parts
- Add 6 unit tests for image content through convert_message/convert_messages
- Add integration test verifying images flow through EvalItem → JSONL path
- Add evaluate_multimodal.py sample demonstrating local image eval
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address remaining review comments
- Fix project_client docstring to say async-only (not sync/async)
- Add builtin evaluator name validation warning in _resolve_evaluator
- Replace getattr with typed attribute access in _poll_eval_run,
_extract_result_counts, _extract_per_evaluator, _fetch_output_items
- Remove cast import from _foundry_evals (no longer needed)
- Tighten _coerce_result: honour explicit 'passed' when both 'score'
and 'passed' are present; remove performative cast
- Fix self_reflection sample: add env file existence check
- Fix traces sample: correct Pattern 2 section label
- Update all Foundry eval samples to FoundryChatClient + FOUNDRY_MODEL
(remove AIProjectClient + AZURE_AI_MODEL_DEPLOYMENT_NAME pattern)
- Add eval_name and OpenAI client docs to FoundryEvals docstring
- Update test mocks to match typed SDK objects (_MockResultCounts)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix ruff lint errors (E501, SIM108, SIM102)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix pyright errors: type-narrow dict to dict[str, Any], add ignore comments
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Replace ConversationSplitter type alias with Protocol
ConversationSplitter is now a runtime-checkable Protocol with a named
'conversation' parameter, making the expected signature self-documenting.
ConversationSplit enum members gain a __call__ method so they satisfy
the protocol directly -- ConversationSplit.LAST_TURN(conversation) works.
This simplifies _split_conversation from an isinstance dispatch to a
single split(conversation) call.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Standardize on AZURE_AI_MODEL_DEPLOYMENT_NAME and fix Unicode in samples
- Replace FOUNDRY_MODEL with AZURE_AI_MODEL_DEPLOYMENT_NAME in all
eval samples to match repo convention
- Replace Unicode symbols with ASCII equivalents in all eval sample
print statements to avoid cp1252 encoding errors on Windows
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update python/samples/03-workflows/evaluation/evaluate_workflow.py
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Apply suggestions from code review
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Rename ADR 0020 to 0023 (foundry evals integration)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: alliscode <bentho@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Add API breaking change validation for RC packages
Enable .NET Package Validation for release candidate packages to detect
API breaking changes in CI. This follows the same pattern used by
Semantic Kernel, centralized through nuget-package.props.
Changes:
- Enable EnablePackageValidation for IsReleaseCandidate packages
- Update PackageValidationBaselineVersion to 1.0.0-rc4 (latest published)
- Generate CompatibilitySuppressions.xml for existing known API changes
in 5 packages (AI, AzureAI, OpenAI, Workflows, Workflows.Declarative.AzureAI)
- Opt out Workflows.Declarative.Mcp (not yet published to NuGet)
- Add breaking changes guidance to CONTRIBUTING.md
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review feedback
- Remove unnecessary empty PackageValidationBaselineVersion override
in Workflows.Declarative.Mcp.csproj (EnablePackageValidation=false
is sufficient)
- Tighten CONTRIBUTING.md wording to clarify opt-out possibility
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Enable package validation for GA packages (no VersionSuffix)
Expand the EnablePackageValidation condition to also cover future GA
packages that have no VersionSuffix, not just RC packages.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix EnablePackageValidation GA condition to check PackageVersion
The previous condition VersionSuffix=='' matched all packages (preview
included) since VersionSuffix defaults to empty. Now uses two separate
conditions: one for RC, one for true GA (PackageVersion == VersionPrefix).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add IsGeneralAvailable flag for package validation
Replace fragile PackageVersion condition with explicit IsGeneralAvailable
property, following the same per-project self-declaration pattern as
IsReleaseCandidate.
- Directory.Build.props: Add IsGeneralAvailable=false default
- nuget-package.props: EnablePackageValidation on RC OR GA
- CONTRIBUTING.md: Update docs to mention both flags
When packages go GA, they set IsGeneralAvailable=true in their .csproj.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename IsGeneralAvailable to IsGenerallyAvailable
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* add inline skills
* Fix IDE1006 and IDE0004 formatting errors in test files
- Add 'Async' suffix to async test methods in FilteringAgentSkillsSourceTests,
DeduplicatingAgentSkillsSourceTests, and AgentInMemorySkillsSourceTests
- Use pragma to suppress false-positive IDE0004 on casts needed for overload
disambiguation in AgentInlineSkillTests and AgentInlineSkillResourceTests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* address issues
* address comments
* make inline skills script and resource model classes internal
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Stage
* Add FoundryAgentClient, model param, chatClientFactory, and RAPI samples
- Add model parameter to FoundryAgentClient simple constructor
- Add chatClientFactory parameter to both constructors
- Switch to OpenAI.GetProjectResponsesClientForModel for direct Responses API usage
- Add FoundryAgents-RAPI samples (Step01 Basics, Step02 Multiturn, Step03 FunctionTools)
- Add solution folder entry for FoundryAgents-RAPI samples
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add auto-discovery constructor and simplify RAPI samples
- Add FoundryAgentClient constructor that reads AZURE_AI_PROJECT_ENDPOINT and
AZURE_AI_MODEL_DEPLOYMENT_NAME from environment variables with DefaultAzureCredential
- Simplify RAPI samples to use auto-discovery (no env var or credential code)
- Remove Azure.Identity direct references from sample csproj files
- Update READMEs to document environment variable requirements
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add remaining RAPI samples (Step04-Step12)
- Step04: Function tools with human-in-the-loop approvals
- Step05: Structured output with typed responses
- Step06: Persisted conversations with session serialization
- Step07: Observability with OpenTelemetry
- Step08: Dependency injection with hosted service
- Step10: Image multi-modality
- Step11: Agent as function tool (agent composition)
- Step12: Middleware (PII, guardrails, function logging, HITL approval)
- Update solution file and folder README with all new samples
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add all RAPI samples (Step09-Step23) and switch to AzureCliCredential
- Step09: MCP client as tools (GitHub server via stdio)
- Step13: Plugins with dependency injection
- Step14: Code Interpreter tool
- Step15: Computer Use tool with screenshot simulation
- Step16: File Search with vector stores
- Step17: OpenAPI tools (REST Countries API)
- Step18: Bing Custom Search
- Step19: SharePoint grounding
- Step20: Microsoft Fabric
- Step21: Web Search with citations
- Step22: Memory Search with multi-turn conversations
- Step23: Local MCP via HTTP (Microsoft Learn)
- Switch all samples (Step04-Step12) to use AzureCliCredential with env vars
- Update solution file and README with all 23 samples
- All 23 samples build successfully, tested Step05/06/11/13/21
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Switch Step01-03 samples to AzureCliCredential for consistency
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Clarify connection ID format in SharePoint and Fabric READMEs
Document that SHAREPOINT_PROJECT_CONNECTION_ID and FABRIC_PROJECT_CONNECTION_ID
should use the connection name (e.g., 'SharepointTestTool'), not the full ARM
resource URI.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Normalize env vars, fix structured output, update READMEs with connection ID formats
- Normalize AZURE_FOUNDRY_PROJECT_* env vars to AZURE_AI_PROJECT_ENDPOINT / AZURE_AI_MODEL_DEPLOYMENT_NAME across all samples (Steps 18-22 READMEs + Steps 19-20 Program.cs)
- Fix RAPI Step05 StructuredOutput to use full constructor with ResponseFormat for streaming JSON
- Update Deep Research sample to use AzureCliCredential
- Enrich Bing Grounding README with full ARM resource URI format
- Fix Bing Custom Search README env var mismatch (BING_CUSTOM_SEARCH_* -> AZURE_AI_CUSTOM_SEARCH_*)
- Add finding instructions for connection ID and instance name in Bing Custom Search READMEs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Refactor memory samples and switch to DefaultAzureCredential
- Refactor RAPI Step22 MemorySearch: extract store setup to EnsureMemoryStoreAsync local function
- Refactor non-RAPI Step22 MemorySearch: same pattern with explicit memory lifecycle
- Set UpdateDelay=0 on MemoryUpdateOptions and MemorySearchPreviewTool for faster ingestion
- Use WaitForMemoriesUpdateAsync with 500ms polling interval
- Switch Step19 SharePoint, Step20 Fabric, Step22 MemorySearch (both) to DefaultAzureCredential
- Remove SearchOptions from MemorySearchPreviewTool (causes unknown parameter error)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Switch all RAPI samples to DefaultAzureCredential and format
- Replace AzureCliCredential with DefaultAzureCredential across all 20 RAPI samples
- Run dotnet format on all RAPI and non-RAPI Foundry samples
- AzureAI unit tests: 341 passed (net10.0 + net472)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename to Microsoft Foundry, add metadata, rename RAPI folder
- Replace 'Azure AI Foundry' / 'Azure Foundry' with 'Microsoft Foundry' in all docs, comments, and XML docs
- Update FoundryAgentClient metadata provider name to 'microsoft.foundry'
- Rename FoundryAgents-RAPI folder to FoundryResponseAgents
- Rewrite FoundryResponseAgents README with comparison table vs Foundry Agents
- Update slnx and parent README with new folder references
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review: simplify sample comments and fix DeepResearch credential
- Remove 'no server-side agent' and 'Responses API directly' phrasing from comments
- Simplify to 'Create a FoundryAgentClient' per review feedback
- Switch Agent_Step15_DeepResearch to DefaultAzureCredential
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Restore full DefaultAzureCredential warning comment in DeepResearch sample
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add ADR 0020: Foundry agent type naming convention
Proposes naming options for a new MAF type wrapping versioned
Foundry agents (Prompt, ContainerApp, Hosted, Workflow) to
distinguish from the existing FoundryResponsesAgent (RAPI path).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Simplify FoundryResponsesAgent samples with env-var constructors and rename folders
- Add env-var constructors to FoundryResponsesAgent (simple + options-based)
- Fix Constructor 1 model optionality (no longer throws on missing AZURE_AI_MODEL_DEPLOYMENT_NAME)
- Add ApplyModelDeploymentFallback helper for options-based constructor
- Update all 23 FoundryResponseAgents samples to remove Environment.GetEnvironmentVariable boilerplate
- Condense 6 simple samples to one-liner constructor calls
- Add XML doc remarks about auto-resolved parameters on all constructors
- Rename FoundryAgents -> FoundryVersionedAgents (server-side, versioned)
- Rename FoundryResponseAgents -> FoundryAgents (now the default path forward)
- Update .slnx and README cross-references for new folder names
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add FoundryAITool factory, rename RAPI folders, and clean up references
- Create FoundryAITool static factory class with 17 methods wrapping AgentTool.Create* and ResponseTool.Create* into AITool returns
- Rename 23 FoundryAgentsRAPI_* subfolders to FoundryAgents_* (drop RAPI prefix)
- Rename .csproj files and update .slnx references accordingly
- Update 12 samples (6 FoundryAgents + 6 FoundryVersionedAgents) to use FoundryAITool
- Replace all FoundryResponsesAgent references with FoundryAgent in comments and READMEs
- Update sample READMEs to reference FoundryAITool methods
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename FoundryVersionedAgents subfolders from FoundryAgents_* to FoundryVersionedAgents_*
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add FoundryVersionedAgent class and refactor extension method internals
- Create FoundryVersionedAgent with private ctor and async static factory methods
(CreateAIAgentAsync/GetAIAgentAsync) with env-var and explicit endpoint tiers
- Extract shared internal helpers from AzureAIProjectChatClientExtensions:
CreateChatClientAgent, CreateAgentVersionFromOptionsAsync,
CreateAgentVersionWithProtocolAsync (tools overload),
CreateChatClientAgentOptions, GetAgentRecordByNameAsync, ThrowIfInvalidAgentName
- Extension methods now delegate to shared internal helpers
- All 49 existing samples continue to build successfully
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add CreateConversationSessionAsync, DeleteAIAgentAsync, auto-resolve model, simplify samples
- Add CreateConversationSessionAsync to FoundryAgent and FoundryVersionedAgent
(returns ChatClientAgentSession, creates server-side conversation + session in one call)
- Add DeleteAIAgentAsync static method to FoundryVersionedAgent
- Make model parameter optional in env-var factory overloads (auto-resolves from
AZURE_AI_MODEL_DEPLOYMENT_NAME)
- Update all FoundryVersionedAgents samples to use DeleteAIAgentAsync
- Remove deploymentName env var from samples where only used for model parameter
- Use CreateConversationSessionAsync in Step02_MultiturnConversation
- Use explicit types instead of var for agent/session variables
- All 49 samples build successfully
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove manual AIProjectClient construction from FoundryVersionedAgents samples
- Replace manual AIProjectClient construction with GetService<AIProjectClient>()
from the FoundryVersionedAgent in all dual-option and tool-specific samples
- Remove AZURE_AI_PROJECT_ENDPOINT env var reads from updated samples
- Remove Azure.Identity usings where no longer needed
- Only Step01.1, Step01.2, Eval_Step01 retain manual construction (pedagogical samples)
- All 49 samples build successfully
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Replace aiProjectClient extension calls with FoundryVersionedAgent factories in all samples
- Replace aiProjectClient.CreateAIAgentAsync with FoundryVersionedAgent.CreateAIAgentAsync
in Option 2 (Native SDK) paths across Steps 14-21
- Replace aiProjectClient.Agents.DeleteAgentAsync with FoundryVersionedAgent.DeleteAIAgentAsync
- Remove unused AIProjectClient variables and using directives
- Only Step01.1, Step01.2, Eval_Step01 retain direct AIProjectClient usage (pedagogical)
- Step16, Step22 use GetService<AIProjectClient>() for file/memory operations
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove unused using directives from Step01.2, Step09, Eval_Step02
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update ADR 0020 with accepted decision: Option 6
- Add Option 6 detailing FoundryAgent, FoundryVersionedAgent, FoundryAITool,
env-var auto-discovery, and self-contained factory patterns
- Mark decision as accepted with rationale
- Update current state and metadata sections
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update Step01 basics samples to use FoundryVersionedAgent factories
- Step01.1: Replace manual AIProjectClient/AsAIAgent with FoundryVersionedAgent.CreateAIAgentAsync/GetAIAgentAsync/DeleteAIAgentAsync
- Step01.2: Replace manual AIProjectClient with FoundryVersionedAgent.CreateAIAgentAsync/DeleteAIAgentAsync
- Remove env var boilerplate and Azure.Identity dependency
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add DeleteAIAgentVersionAsync to FoundryVersionedAgent
- DeleteAIAgentAsync: deletes the agent and all its versions (existing)
- DeleteAIAgentVersionAsync: deletes only the specific version associated with the agent instance
- Internally delegates to Agents.DeleteAgentAsync vs Agents.DeleteAgentVersionAsync
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix cleanup comments: DeleteAIAgentAsync deletes the agent and all its versions
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update all FoundryVersionedAgents READMEs for FoundryVersionedAgent and auto-discovery
- Rewrite main README with FoundryVersionedAgent usage, auto-discovery table, code example
- Fix sample table links from FoundryAgents_Step* to FoundryVersionedAgents_Step*
- Add FoundryAITool references in tool-specific sample descriptions
- Update individual READMEs: fix stale paths, add auto-discovery note after env var blocks
- Update tool references: AgentTool/ResponseTool -> FoundryAITool
- Update parent 02-agents/README.md with FoundryVersionedAgent description
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Revert unrelated AGUI and Hosting.OpenAI formatting changes to main
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove env-var auto-discovery, add AsAIAgent, mark extensions Obsolete
- Remove 2 env-var constructors from FoundryAgent (keep explicit endpoint ctors)
- Remove 5 env-var factory methods from FoundryVersionedAgent (keep explicit ones)
- Add 3 AsAIAgent static methods to FoundryVersionedAgent (AgentVersion/AgentRecord/AgentReference)
- Mark all 8 AIProjectClient extension methods as [Obsolete] pointing to FoundryVersionedAgent
- Remove ApplyModelDeploymentFallback, env var constants, Azure.Identity usings from source
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update all samples to use explicit endpoint, credential, and model parameters
- Add explicit Environment.GetEnvironmentVariable reads for AZURE_AI_PROJECT_ENDPOINT
and AZURE_AI_MODEL_DEPLOYMENT_NAME to all 48 sample files
- Pass new Uri(endpoint), new DefaultAzureCredential(), deploymentName to
FoundryAgent constructors and FoundryVersionedAgent factory methods
- Add using Azure.Identity where missing
- Matches repo-wide pattern used by other non-Foundry samples
- All 49 samples build successfully
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Migrate remaining samples and source from obsoleted extension methods
- Migrate AgentProviders, AgentWithRAG, AgentWithMemory, HostedWorkflow samples to FoundryVersionedAgent
- Migrate AzureAgentProvider.cs to FoundryVersionedAgent.AsAIAgent
- Migrate AzureAIProjectChatClientTests.cs to FoundryVersionedAgent.GetAIAgentAsync
- Remove pragma suppressions from migrated files
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add unit tests for FoundryAgent and FoundryVersionedAgent
- FoundryAgentTests.cs: 14 tests covering constructors, validation,
properties, metadata, GetService, chat client factory, user-agent header
- FoundryVersionedAgentTests.cs: 31 tests covering CreateAIAgentAsync,
GetAIAgentAsync, AsAIAgent (3 overloads), DeleteAIAgentAsync,
DeleteAIAgentVersionAsync, validation, invalid names, metadata, GetService
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Finalize Foundry agent migration
Align FoundryAgent and FoundryVersionedAgent samples, docs, and tests with the explicit configuration model, clean up stale README guidance, and fix AzureAI unit test validation/build issues.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Apply formatter cleanup after validation
Capture the dotnet format follow-up changes produced during branch validation so the committed state matches the successfully built and tested branch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add integration tests for FoundryAgent and FoundryVersionedAgent
Mark old AIProjectClient extension-method integration tests as obsolete and add new integration test suites for both FoundryAgent (Responses API) and FoundryVersionedAgent (versioned agents). All 71 non-skipped tests pass against the live Foundry service.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update ADR 0020 with test coverage details
Add integration test coverage note to the Current State section of ADR 0020.
* Simplify Foundry agents and validate moved samples
* Rename FoundryAgent integration tests to ResponsesAgent
The test classes exercise the non-versioned Responses path via
AIProjectClient.AsAIAgent(), not the removed FoundryAgent wrapper type.
Rename files and class names to reflect the actual test surface.
* Update documentation for ChatClientAgent usage
Added example usage of ChatClientAgent with JokerAgent.
* Refactor ChatClientAgent instantiation for clarity
* Revise agent type naming and usage examples
Updated documentation to reflect changes in agent creation methods and added examples for using `ChatClientAgent`.
* Fix Azure SDK namespace migration after rebase
Update Azure.AI.Projects.OpenAI references to Azure.AI.Projects.Agents
and Azure.AI.Extensions.OpenAI to match Azure.AI.Projects 2.0.0-beta.2.
- Replace deprecated namespace across samples, tests, and src
- Fix renamed types: OpenAPIFunctionDefinition -> OpenApiFunctionDefinition,
BingCustomSearchToolParameters -> BingCustomSearchToolOptions,
BrowserAutomationToolParameters -> BrowserAutomationToolOptions
- Fix API changes: AgentRecord.Versions -> GetLatestVersion(),
ResponsesClient constructor, FunctionApprovalRequestContent ->
ToolApprovalRequestContent
- Apply dotnet format
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address merge markers
* Replace obsolete GetAIAgentAsync with AsAIAgent in samples
Switch Agent_Step07_AsMcpTool and A2AServer to use the non-obsolete
PersistentAgentsClient.AsAIAgent(PersistentAgent) extension instead
of the deprecated GetAIAgentAsync, fixing CS0618 build errors.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix broken markdown links in Responses sample READMEs
Replace stale ChatClientAgents_Step* folder references with the
correct Agent_Step* names across all Responses sample READMEs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix format errors and address PR review comments
- Fix charset and remove unused using in AzureAIProjectResponsesChatClient
- Fix doc comment tags (code -> c) in FoundryAITool
- Fix stray period in LocalMCP sample comment
- Fix grammar in FoundryMemoryProvider xmldoc
- Fix AIProjectClientAgentRunStreamingConversationTests base class
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Apply dotnet format fixes to PR-changed files
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix build errors from format pass and apply naming conventions
- Fix static call to CreateSessionAsync in Step02 samples and extension tests
- Use expression-bodied lambda in FoundryMemoryProvider (RCS1021)
- Apply PascalCase naming to const fields in ResponsesAgentExtensionCreateTests (IDE1006)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Introduce FoundryAgent sealed type and update AsAIAgent extensions
- Add FoundryAgent sealed class wrapping ChatClientAgent with:
- Public ctors: (projectEndpoint, credential, model, instructions) and (agentEndpoint, credential)
- Internal ctor: (AIProjectClient, ChatClientAgent) for extension use
- CreateConversationSessionAsync() for server-side conversations
- GetService<ChatClientAgent>() and GetService<AIProjectClient>()
- MEAI user-agent policy on internally-created AIProjectClient
- Change all AsAIAgent extension return types from ChatClientAgent to FoundryAgent
- Update all samples and tests to use FoundryAgent type
- Add 16 FoundryAgentTests covering ctors, GetService, UserAgent, RunAsync
- Fix pre-existing Agent_Step12_Plugins build error
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Collapse sample folders and add FoundryAgent_Step01 sample
- Move all Responses/* samples up to AgentsWithFoundry/ (flat structure)
- Remove entire Versioned/ folder (26 samples)
- Add FoundryAgent_Step01 sample showing direct FoundryAgent ctor usage
- Update slnx to reflect flat folder structure
- Fix csproj ProjectReference paths for new depth
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update READMEs for flat AgentsWithFoundry structure
- Rewrite AgentsWithFoundry/README.md with FoundryAgent quick start
- Fix cd commands and paths in 11 sample READMEs
- Update 02-agents/README.md to single Foundry link
- Update AGENTS.md tree to flat structure
- Fix AgentWithMemory cross-reference
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix FoundryAgent_Step01 sample with full create/run/delete lifecycle
Show the complete server-side agent lifecycle: create version with
native SDK, wrap as FoundryAgent via AsAIAgent, run, then delete.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Revert RAPI samples to use AIAgent instead of FoundryAgent
RAPI samples should not reference FoundryAgent directly. Restored
original sample code with only ChatClientAgent -> AIAgent type change
to accommodate the AsAIAgent return type.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Convert versioned-pattern samples to pure RAPI
Step09, Step13, Step17, Step22 were using CreateAgentVersionAsync +
PromptAgentDefinition which is the versioned pattern. Converted to
use AsAIAgent(model, instructions, tools) which is the RAPI path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix format issues from Docker CI check
- FoundryAgent_Step01: CRLF -> LF
- Agent_Step09: missing final newline
- Agent_Step11_Middleware: add internal modifier, final newline
- Agent_Step02: remove redundant cast (IDE0004)
- Agent_Step08: simplify name (IDE0001)
- FoundryAgentTests: s_ prefix, Async suffix naming conventions
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Switch Step09 MCP sample to Microsoft Learn HTTP endpoint
Replace npx stdio GitHub MCP server with the public Microsoft Learn
MCP endpoint (https://learn.microsoft.com/api/mcp) using HTTP transport.
No external tooling required to run.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix missing final newline in Step09 MCP sample
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review: use DelegatingAIAgent, clean up Step01 sample
- FoundryAgent now inherits DelegatingAIAgent instead of AIAgent,
removing manual delegation boilerplate (westey-m feedback)
- Simplified Agent_Step01_Basics to single agent creation path,
moved composable IChatClient approach to README (westey-m feedback)
- Fixed FoundryAgentTests param name assertion
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update sample using Project specialized type instead
* Address PR review feedback: DefaultAzureCredential warnings, sample simplifications, format fixes
- Add DefaultAzureCredential production warning comments to ~25 samples
- Simplify Anthropic and OpenAI Step01 samples to single agent
- Convert Step11 Middleware regex patterns to [GeneratedRegex]
- Remove unnecessary cleanup comment from Step06
- Fix Step09 README MCP transport description
- Enhance FoundryAgent xmldoc with non-persistent agent comparison
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Split Step02, simplify RAG Step04, sharpen Step23 differentiation
- Split Step02 into 02.1 (simple multi-turn via sessions) and 02.2 (server-side conversations via CreateConversationSessionAsync)
- RAG Step04: replace HostedFileSearchTool + MEAI wrapping with native OpenAI FileSearchTool
- Step23: clarify DelegatingAIFunction wrapping pattern vs Step09 basic MCP
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix Hosted MCP sample: use ResponseTool.CreateMcpTool and move tool to PromptAgentDefinition
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix broken README link after Step02 split
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address Sergey round 3 feedback: branding, README nav, sample rename
- Replace 'Azure AI Foundry' with 'Microsoft Foundry' in ADR 0020
- Fix 3 READMEs: 'ChatClientAgents' → 'AgentsWithFoundry' sample directory
- Rename FoundryAgent_Step01 → Agent_Step00_FoundryAgentLifecycle for naming consistency
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Changes
* Fix ChatClientAgent streaming responses missing MessageId
Generate fallback MessageId in ChatClientAgent.RunCoreStreamingAsync when
the underlying LLM provider does not set ChatResponseUpdate.MessageId.
Without a MessageId the AGUI converter's null==null check silently drops
all text content, causing CopilotKit Zod validation errors.
Changes:
- ChatClientAgent: generate msg_{Guid} fallback via ??= in streaming loop
- AgentResponseExtensions: sync wrapper MessageId back to RawRepresentation
in AsChatResponseUpdate() so downstream consumers see the value
- Add unit tests for both fixes and AGUI streaming MessageId scenarios
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR #4615 review comments
- Fix MessageId seeding: use first-seen provider MessageId (or generate
fallback) and apply consistently to all chunks in the stream, preventing
message splitting when providers set MessageId only on the first chunk
- Add test for mixed MessageId scenario (first chunk only)
- Fix skipped TextStreaming test: assert Empty (not NotEmpty) to match
actual null==null behavior
- Fix skipped ToolCalls test: assert empty ParentMessageId to match
actual empty-string passthrough behavior
* Handle empty MessageId in AsChatResponseUpdate sync
Treat empty/whitespace MessageId the same as null when syncing from
the AgentResponseUpdate wrapper back to RawRepresentation. Providers
that return empty string MessageId (e.g. tool call responses) now get
the wrapper value recovered correctly.
Add test for empty string MessageId recovery scenario.
* Move MessageId fallback generation to AGUI layer
Move fallback MessageId generation from ChatClientAgent to
AsAGUIEventStreamAsync, addressing the architectural concern that
MessageId is nullable in the AIAgent abstraction and the requirement
for non-null values is specific to the AGUI protocol.
The AGUI layer now generates a fallback MessageId for null or
empty/whitespace values, covering all agent types (not just
ChatClientAgent) including external implementations.
Changes:
- Revert MessageId generation from ChatClientAgent.RunCoreStreamingAsync
- Add fallback MessageId generation in AsAGUIEventStreamAsync for
null/empty MessageId values (handles both null and whitespace)
- Unskip and update AGUI tests to verify fallback generation
- Update ChatClientAgent tests to reflect passthrough behavior
* Revert AsChatResponseUpdate MessageId sync-back
Remove the MessageId sync-back logic from AsChatResponseUpdate() as it
is no longer needed. With fallback generation moved to the AGUI layer,
the abstraction layer should not mutate the RawRepresentation object.
Revert to the original passthrough behavior for AsChatResponseUpdate()
and update tests accordingly.
---------
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>
* feat: Implement return-to-previous routing in handoff workflow
- Also obsoletes HandoffsWorkflowBuilder => HandoffWorkflowBuilder (no "s")
* refactor: Remove instance-shared current agent tracking in handoffs
Because the tracker was instance-shared between the start and end executors, it would be shared between all sessions, resulting in incorrect behaviour.
The corect way to do this is to keep the data in a shared executor scope, which is per-session.
* fix: Fix test logic for Handoff to correctly use checkpointing for multiturn
* Move ag_ui_workflow_handoff demo to 05-end-to-end (#4895)
Move the AG-UI workflow handoff demo from python/samples/demos/ to
python/samples/05-end-to-end/ to follow the current folder structure
convention. Update README paths accordingly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix review feedback: remove build artifacts, fix README paths (#4895)
- Add .gitignore to frontend/ to exclude *.tsbuildinfo, vite.config.js,
and vite.config.d.ts build artifacts from version control
- Remove the 4 tracked build artifact files from the tree
- Fix step 2 cd path in README to be relative after 'cd python'
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Clarify working directory context in README Step 2 (#4895)
Step 2 uses a python/-relative path (samples/...) which assumes the
user is still in the python/ directory from Step 1. Add a brief note
making this explicit.
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>
* Use actual message role when creating ChatMessage
Replace hard-coded ChatRole.User with a ChatRole constructed from the message's Role. The change ensures ToChatMessage and FunctionMessage use the original role (new ChatRole(this.Role)) for both text and contents branches, fixing incorrect role assignment when constructing ChatMessage instances.
* Update changes
* Fix formatting in ToChatMessage tests
---------
Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
* .NET: Add integration test for OpenAPI tools with AsAIAgent(agentVersion)
Validates end-to-end flow creating a Foundry agent with an OpenAPI tool
definition via native Azure.AI.Projects SDK types and wrapping it with
AsAIAgent(agentVersion). The test confirms the server-side OpenAPI
function is invoked correctly through RunAsync.
Addresses #4883
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review: RetryFact, PascalCase naming, stronger tool assertion
- Use RetryFact with Skip for manual testing (flaky due to external API)
- Fix agentName -> AgentName to match PascalCase convention in file
- Strengthen tool invocation assertion: require >= 3 Eurozone countries
- Add comment explaining server-side OpenAPI tools don't surface as
FunctionCallContent in the MEAI abstraction
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add AsIChatClientWithStoredOutputDisabled for ProjectResponsesClient
Add extension method on ProjectResponsesClient in Microsoft.Agents.AI.AzureAI
package (Azure.AI.Extensions.OpenAI namespace) mirroring the existing extension
on ResponsesClient in the OpenAI package. This enables Azure AI consumers to
disable server-side response storage without depending on the OpenAI package.
- New ProjectResponsesClientExtensions class with AsIChatClientWithStoredOutputDisabled
- Optional deploymentName parameter (model is no longer required)
- Updated OpenAI counterpart doc to remove 'Required' wording for model param
- Added unit tests covering null guard, inner client accessibility,
StoredOutputEnabled=false, and reasoning encrypted content inclusion/exclusion
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Preserve existing RawRepresentationFactory when disabling stored output
Address PR review feedback: wrap/chain the existing factory instead of
replacing it, so upstream configuration (e.g., deploymentName/model defaults
from AsIChatClient) is preserved.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* add adr suggesting a new design to support a multi-source architecture for agent skills
* add deciders
* move the adr to the decisions folder
* remove unnecessary section
* describe adding a custom skill source
* update
* address comments
* add constructor overloads to inline skill resource and script
* consider ai-function as an alternative for skill script and skill resource model classes
* update decision outcome section and sync adr with latest changes in the code
* Add ADR to decide consitency of Chat History Persistence
* Add example
* Update ADR with review results
* Remove unecessary clarification
* Rename ADR to no 22
* 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>
* Include reasoning messages in MESSAGES_SNAPSHOT (#4843)
FlowState now tracks reasoning messages emitted during a run.
_emit_text_reasoning() persists reasoning (including encrypted_value)
into flow.reasoning_messages, and _build_messages_snapshot() appends
them to the final MESSAGES_SNAPSHOT event.
Changes:
- Add reasoning_messages field to FlowState
- Update _emit_text_reasoning() to accept optional flow parameter
- Include reasoning_messages in _build_messages_snapshot()
- Add 'reasoning' to ALLOWED_AGUI_ROLES so normalize_agui_role()
preserves the role through snapshot round-trips
- Skip reasoning messages in agui_messages_to_agent_framework() since
they are UI-only state and should not be forwarded to LLM providers
- Add regression tests for snapshot emission, encrypted value
preservation, and multi-turn round-trip with reasoning
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Include reasoning messages in MESSAGES_SNAPSHOT events
Fixes#4843
* Fix PR review feedback for reasoning persistence (#4843)
- Accumulate reasoning text per message_id (append deltas) instead of
storing only the current chunk, matching flow.accumulated_text pattern
- Use camelCase encryptedValue in snapshot JSON to match AG-UI protocol
conventions (toolCallId, encryptedValue)
- Normalize snake_case encrypted_value to encryptedValue in
agui_messages_to_snapshot_format for input compatibility
- Update normalize_agui_role docstring to include reasoning role
- Add tests for incremental reasoning accumulation and key normalization
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #4843: Python: agent-framework-ag-ui: include reasoning messages in MESSAGES_SNAPSHOT
---------
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>
* Fix A2AAgent dropping message content from in-progress TaskStatusUpdateEvents (#4783)
_updates_from_task() returned [] for working-state tasks when
background=False, silently discarding all intermediate message content
from task.status.message. Now extracts and yields message parts from
in-progress status updates during streaming.
Also fixed MockA2AClient.send_message to yield all queued responses
(enabling multi-event streaming tests) and added text parameter to
add_in_progress_task_response for tests that need status messages.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix: gate intermediate status updates behind emit_intermediate flag and add missing test coverage
- Add emit_intermediate parameter to _updates_from_task and _map_a2a_stream
- Thread stream flag from run() so only streaming callers see intermediate updates
- Add IN_PROGRESS_TASK_STATES guard to emit_intermediate condition
- Add role parameter to test helper add_in_progress_task_response
- Add clarifying comment on MockA2AClient.send_message batch semantics
- Add tests for user role mapping, background precedence, non-streaming behavior,
terminal task with no artifacts, and empty parts edge case
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: HandoffAgentExecutor does not output any reponse when non-streaming
* fix: Ensure Workflow outputs persisted in chat history when hosted AsAgent
* fix: Remove duplicate history entry creation and ad test
* test: Add streaming tests for AsAgent to smoke tests
* feat: Add output configurability to Handoffs
* refactor: [BREAKING] Config => ExecutorConfig
Make the Config name less likely to collide with other classes by renaming to ExecutorConfig. Makes Configured and related classes internal as they do not need to be part of the public surface.
* fix: Make RouteBuilder explicit in SourceGen to avoid conflicts
* Handle external input request and response conversion for workflow as agent scenario
* Remove unnecessary test comment
* Fix PR comments
* Updated to fix edge cases, and add more tests.
* Update pending requests to use typed properties instead of relying on StateBag. replying to PR feedback.
* Fixed external response de-dup and updated possible brittle test.
* Address PR comments on sending turn token for normal messages and handle contentId collision by source agent
* Remove unnecessary serialization element and address pr comment on intercepted outgoing requests
* Updated MEAI changes for UserInput request and response abstractions.
* Expose workflow as MCP Tool
* Expose workflow as MCP Tool
* Cleanup
* PR feedback fixes
* update changelog to include PR numner
* Improvements to error handling.
* Adding a sample project demonstrating how to setup Agents and Workflows together.
* Ensure duplicate agent registrations are properly handled.
The `sessionId`, an optional parameter when starting a new session when
running a workflow is an arbitrary string. This allows consumers to
support whatever ids are needed by other systems, but can result in
errors when an OS special or forbidden character is included.
The fix is to escape the paths, in a 1:1 manner. We rely on
EncodeDataString to do this.
* Also modifies the index file to make it easier to determine what the
name of the file on disk is for a given `sessionId`.
* Persist messages during the Function Call Loop
* Revert version reset
* Fix bugs and improve sample
* Fix formatting issues
* Also updating conversation id during run
* Update based on ADR feedback
instructions="You are an upbeat assistant that writes beautifully.",
)
print(awaitagent.run("Write a haiku about Microsoft Agent Framework."))
@@ -120,41 +120,38 @@ if __name__ == "__main__":
```
### Basic Agent - .NET
Create a simple Agent, using Microsoft Foundry with token-based auth, that writes a haiku about the Microsoft Agent Framework
```c#
// dotnet add package Microsoft.Agents.AI.Foundry
// Use `az login` to authenticate with Azure CLI
using Azure.AI.Projects;
using Azure.Identity;
using System;
using Azure.AI.Projects;
using Azure.Identity;
var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
var agent = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential())
.AsAIAgent(model: deploymentName, name: "HaikuBot", instructions: "You are an upbeat assistant that writes beautifully.");
Console.WriteLine(await agent.RunAsync("Write a haiku about Microsoft Agent Framework."));
```
Create a simple Agent, using OpenAI Responses, that writes a haiku about the Microsoft Agent Framework
- [End-to-End](./dotnet/samples/05-end-to-end): full applications and demos
## Troubleshooting
### Authentication
| Problem | Cause | Fix |
|---------|-------|-----|
| Authentication errors when using Azure credentials | Not signed in to Azure CLI | Run `az login` before starting your app |
| API key errors | Wrong or missing API key | Verify the key and ensure it's for the correct resource/provider |
> **Tip:** `DefaultAzureCredential` is convenient for development but in production, consider using a specific credential (e.g., `ManagedIdentityCredential`) to avoid latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
### Environment Variables
The samples typically read configuration from environment variables. Common required variables:
| `AZURE_OPENAI_DEPLOYMENT_NAME` | Azure OpenAI samples | Model deployment name (e.g. `gpt-4o-mini`) |
| `AZURE_AI_PROJECT_ENDPOINT` | Microsoft Foundry samples | Your Microsoft Foundry project endpoint |
| `AZURE_AI_MODEL_DEPLOYMENT_NAME` | Microsoft Foundry samples | Model deployment name |
| `OPENAI_API_KEY` | OpenAI (non-Azure) samples | Your OpenAI platform API key |
## Contributor Resources
@@ -182,4 +207,9 @@ Console.WriteLine(await agent.RunAsync("Write a haiku about Microsoft Agent Fram
## Important Notes
If you use the Microsoft Agent Framework to build applications that operate with third-party servers or agents, you do so at your own risk. We recommend reviewing all data being shared with third-party servers or agents and being cognizant of third-party practices for retention and location of data. It is your responsibility to manage whether your data will flow outside of your organization's Azure compliance and geographic boundaries and any related implications.
> [!IMPORTANT]
> If you use Microsoft Agent Framework to build applications that operate with any third-party servers, agents, code, or non-Azure Direct models (“Third-Party Systems”), you do so at your own risk. Third-Party Systems are Non-Microsoft Products under the Microsoft Product Terms and are governed by their own third-party license terms. You are responsible for any usage and associated costs.
>
>We recommend reviewing all data being shared with and received from Third-Party Systems and being cognizant of third-party practices for handling, sharing, retention and location of data. It is your responsibility to manage whether your data will flow outside of your organization’s Azure compliance and geographic boundaries and any related implications, and that appropriate permissions, boundaries and approvals are provisioned.
>
>You are responsible for carefully reviewing and testing applications you build using Microsoft Agent Framework in the context of your specific use cases, and making all appropriate decisions and customizations. This includes implementing your own responsible AI mitigations such as metaprompt, content filters, or other safety systems, and ensuring your applications meet appropriate quality, reliability, security, and trustworthiness standards. See also: [Transparency FAQ](./TRANSPARENCY_FAQ.md)
This folder contains sample agent definitions that can be run using the declarative agent support, for python see the [declarative agent python sample folder](../python/samples/02-agents/declarative/).
This folder contains sample agent definitions that can be run using the declarative agent support, for python see the [declarative agent python sample folder](../../python/samples/02-agents/declarative/).
# Foundry agent surface stays centered on `ChatClientAgent`
## Context
The Microsoft Foundry integration exposes two distinct usage patterns:
1. Direct Responses usage, where callers provide model, instructions, and tools at runtime.
2. Server-side versioned agents, where callers create and manage `AgentVersion` resources through `AIProjectClient.Agents`.
We briefly explored adding public wrapper types such as `FoundryAgent`, `FoundryVersionedAgent`, and `FoundryResponsesChatClient` to make those paths feel more specialized. That direction created extra public types, duplicated existing `ChatClientAgent` behavior, and pushed samples toward compatibility helpers instead of the native Azure SDK flow.
## Decision
Keep the public surface centered on `ChatClientAgent`.
- Direct Responses scenarios use `AIProjectClient.AsAIAgent(...)`.
- Server-side versioned scenarios use native `AIProjectClient.Agents` APIs to create or retrieve agent resources, then wrap `AgentRecord` or `AgentVersion` with `AIProjectClient.AsAIAgent(...)`.
- Compatibility helpers such as `AIProjectClient.CreateAIAgentAsync(...)` and `AIProjectClient.GetAIAgentAsync(...)` remain only as obsolete migration shims.
- Public wrapper types `FoundryAgent`, `FoundryVersionedAgent`, `FoundryResponsesChatClient`, and `FoundryResponsesChatClientAgent` are not part of the chosen direction.
## Why
-`ChatClientAgent` is already the framework abstraction used everywhere else.
-`AIProjectClient` is the native Azure SDK entry point for versioned agent lifecycle operations.
- A single agent abstraction avoids parallel type hierarchies for the same backend.
- Samples become clearer when they show either:
- direct Responses construction via `AIProjectClient.AsAIAgent(...)`, or
- native Foundry resource management via `AIProjectClient.Agents`.
## Consequences
### Direct Responses path
Use the convenience overloads on `AIProjectClient`:
-`FoundryAgents/` samples show the direct Responses path with `AIProjectClient.AsAIAgent(...)`.
-`FoundryVersionedAgents/` samples should show native `AIProjectClient.Agents` create/get/delete flows plus `AsAIAgent(...)`.
### Compatibility APIs
Obsolete helper extensions remain only to ease migration of existing code. New samples and new guidance should not be written against them.
## Rejected direction
Do not introduce or preserve separate public wrapper types whose main purpose is to forward to `ChatClientAgent` while carrying Foundry-specific naming.
That approach:
- duplicates lifecycle concepts already present on `AIProjectClient`,
- fragments the public API,
- complicates samples and docs,
- and makes migration harder by encouraging wrapper-specific affordances.
The Agent Framework needs a skills system that lets agents discover and use domain-specific knowledge, reference documents, and executable scripts. Skills can originate from different sources — filesystem directories (SKILL.md files), inline C# code, or reusable class libraries — and the framework must support all three uniformly while allowing extensibility, composition, and filtering.
## Decision Drivers
- Skills must be definable from multiple sources: filesystem, inline code, reusable classes, etc
- Common abstractions are needed so the provider and builder work uniformly regardless of skill origin
- File-based scripts must support user-defined executors, enabling custom runtimes and languages; code/class-based scripts execute in-process as C# delegates
- Skills must be filterable so consumers can include or exclude specific skills based on defined criteria
- Multiple skill sources must be composable into a single provider
- It must be possible to add custom skill sources (e.g., databases, REST APIs, package registries) by implementing a common abstraction
## Architecture
### Model-Facing Tools
Skills are presented to the model as up to three tools that progressively disclose skill content. The system prompt lists available skill names and descriptions; the model then calls these tools on demand:
- **`load_skill(skillName)`** — returns the full skill body (instructions, listed resources, listed scripts)
- **`read_skill_resource(skillName, resourceName)`** — reads a supplementary resource (file-based or code-defined) associated with a skill
- **`run_skill_script(skillName, scriptName, arguments?)`** — executes a script associated with a skill; only registered when at least one skill contains scripts
Each tool delegates to the corresponding method on the resolved `AgentSkill` — calling `Resource.ReadAsync()` or `Script.RunAsync()` respectively.
If skills have no scripts defined, the `run_skill_script` tool is **not advertised** to the model and instructions related to script execution are **not included** in the default skills instructions.
### Abstract Base Types
The architecture defines four abstract base types that all skill variants implement:
1.**File-Based Skills** — discovered from `SKILL.md` files on the filesystem. Resources and scripts are files in subdirectories.
2.**Programmatic Skills** — defined in C# code. These are further divided into:
- **Inline Skills** — built at runtime via the `AgentInlineSkill` class and its fluent API. Ideal for quick, agent-specific skill definitions.
- **Class-Based Skills** — defined as reusable C# classes that subclass `AgentClassSkill`. Ideal for packaging skills as shared libraries or NuGet packages.
Both programmatic skill types use `AgentInlineSkillResource` and `AgentInlineSkillScript` for their resources and scripts. They are typically served by `AgentInMemorySkillsSource`, which accepts any `AgentSkill` and is not limited to programmatic skills.
### File-Based Skills
File-based skills are authored as `SKILL.md` files on disk. Resources and scripts are discovered from corresponding subfolders within the skill directory.
**`AgentFileSkill`** — A filesystem-based skill discovered from a directory containing a `SKILL.md` file. Parsed from YAML frontmatter; content is the raw markdown body. Resources and scripts are discovered from files in corresponding subfolders:
**`AgentFileSkillScript`** — A file-based skill script that represents a script file on disk. Delegates execution to an external `AgentFileSkillScriptRunner` callback (e.g., runs Python/shell via `Process.Start`). Throws `NotSupportedException` if no executor is configured:
The executor can be provided at the **provider level** via `AgentSkillsProviderBuilder.UseFileScriptRunner(executor)` and optionally overridden for a **particular file skill** or for a **set of skills** at the file skill source level, giving fine-grained control over how different scripts are executed.
**`AgentFileSkillsSource`** — A skill source that discovers skills from filesystem directories containing `SKILL.md` files. Recursively scans directories (max 2 levels), validates frontmatter, and enforces path traversal and symlink security checks:
**`AgentFileSkillsSourceOptions`** — Configuration options for `AgentFileSkillsSource`. Allows customizing the allowed file extensions for resources and scripts without adding constructor parameters:
Programmatic skills are defined in C# code rather than discovered from the filesystem. There are two kinds: **inline** and **class-based**. Both use `AgentInlineSkillResource` and `AgentInlineSkillScript` for resources and scripts, and are held by a single `AgentInMemorySkillsSource`.
**`AgentInMemorySkillsSource`** — A general-purpose skill source that holds any `AgentSkill` instances in memory. Although commonly used for programmatic skills (`AgentInlineSkill` and `AgentClassSkill`), it accepts any `AgentSkill` subclass and is not restricted to code-defined skills:
Inline skills are built at runtime via the `AgentInlineSkill` class and its fluent API. They are ideal for quick, agent-specific skill definitions where a full class hierarchy would be overkill.
**`AgentInlineSkill`** — A skill defined entirely in code. Resources can be static values or functions; scripts are always functions. Constructed with name, description, and instructions, then extended with resources and scripts:
**`AgentInlineSkillResource`** — A skill resource backed by a delegate. The delegate is invoked via an `AIFunction` each time `ReadAsync` is called, producing a dynamic (computed) value:
Class-based skills are designed for packaging skills as reusable libraries. Users subclass `AgentClassSkill` and override properties. Unlike inline skills, class-based skills are self-contained, can live in shared libraries or NuGet packages, and are well-suited for dependency injection.
**`AgentClassSkill`** — An abstract base class for defining skills as reusable C# classes that bundle all skill components (frontmatter, instructions, resources, scripts) together. Designed for packaging skills as distributable libraries:
```csharp
publicabstractclassAgentClassSkill:AgentSkill
{
publicabstractstringInstructions{get;}
// Content is auto-synthesized from Frontmatter + Instructions + Resources + Scripts
The following subsections present alternative approaches for handling filtering, caching, and deduplication of skills across multiple sources.
### Via Composition
In this approach, the `AgentSkillsProvider` accepts a **single**`AgentSkillsSource`. Multiple sources are composed externally via an aggregate source, and cross-cutting concerns like filtering, caching, and deduplication are implemented as **source decorators** — subclasses of `DelegatingAgentSkillsSource` that intercept `GetSkillsAsync()`.
**`FilteringAgentSkillsSource`** — A decorator that applies filter logic before returning results. The decorator pattern keeps filtering orthogonal to source implementations and allows composing multiple filters:
**`CachingAgentSkillsSource`** — A decorator that caches skills after the first load, keeping the provider stateless and giving consumers control over caching granularity per source. For example, file-based skills (expensive to discover) can be cached while code-defined skills remain uncached:
**Deduplication** is similarly implemented as a decorator (`DeduplicatingAgentSkillsSource`) that deduplicates by name (case-insensitive, first-one-wins) and logs a warning for skipped duplicates.
**Example** — Combining file-based and code-defined sources with filtering and caching:
- Clean single-responsibility: the provider serves skills, sources provide them.
- Caching, filtering, and deduplication are composable as source decorators — each concern is a separate, testable wrapper.
**Cons:**
- DI is less flexible: multiple `AgentSkillsSource` implementations registered in the container cannot be auto-injected into the provider. The consumer must manually compose them via an aggregate source.
- Increased public API surface: requires additional public classes (aggregate source, caching decorators, filtering decorators) that consumers need to learn and use.
### Via AgentSkillsProvider
In this approach, the `AgentSkillsProvider` accepts **`IEnumerable<AgentSkillsSource>`** and handles aggregation, filtering, caching, and deduplication internally.
The provider aggregates skills from all registered sources, deduplicates by name (case-insensitive, first-one-wins), caches the result after the first load, and optionally applies filtering via a predicate on `AgentSkillsProviderOptions`. Duplicate skill names are logged as warnings.
**Example** — Registering multiple sources directly with the provider:
```csharp
// Conceptual example — in practice, use AgentSkillsProviderBuilder
- DI-friendly: register multiple `AgentSkillsSource` implementations in the container, and they are all auto-injected into `AgentSkillsProvider` via `IEnumerable<AgentSkillsSource>`.
- Smaller public API surface: no need for aggregate source, caching decorators, or filtering decorator classes — these concerns are handled internally by the provider.
**Cons:**
- The provider takes on multiple responsibilities — aggregation, caching, deduplication, and filtering.
- Less granular caching control: caching is all-or-nothing across sources rather than per-source as with decorators.
- Less extensible: new behaviors (e.g., ordering, TTL expiration) require modifying the provider rather than adding a decorator.
### Builder Pattern
**`AgentSkillsProviderBuilder`** provides a fluent API for composing skills from multiple sources. The builder centralizes configuration — script executors, approval callbacks, prompt templates, and filtering — so consumers don't need to know the underlying source types.
The builder internally decides how to wire up the object graph: it creates the appropriate source instances, applies caching and filtering, and returns a fully configured `AgentSkillsProvider`. This keeps the setup code concise while still allowing fine-grained control when needed.
**Example** — Using the builder to combine multiple source types with configuration:
- **Explicit skill context at execution time.** `RunAsync` receives the owning `AgentSkill`, so any script can access skill metadata or resources during execution without requiring construction-time wiring.
- **Self-contained abstraction.** A dedicated type communicates clearly that scripts are a skills-framework concept, separate from general-purpose AI functions.
- **Easier extensibility for custom script types.** Third-party implementations can subclass `AgentSkillScript` and access the owning skill in `RunAsync` without special setup.
**Cons:**
- **Wrapper overhead.** `AgentInlineSkillScript` is a thin pass-through around `AIFunction` — it adds a class, a constructor, and an indirection layer for no behavioral difference.
- **Parallel abstraction.** `AgentSkillScript` and `AIFunction` serve overlapping purposes (named callable with arguments), creating two parallel hierarchies for the same concept.
- **Friction for consumers.** Users who already have `AIFunction` instances must wrap them in `AgentInlineSkillScript` to use them as scripts, adding ceremony.
### Option B — Reuse `AIFunction` directly
Scripts are represented as `AIFunction` (from `Microsoft.Extensions.AI`). `AgentSkill.Scripts` returns
`IReadOnlyList<AIFunction>?`. `AgentInlineSkillScript` is eliminated entirely — callers use
`AIFunctionFactory.Create(delegate, name: ...)` or pass `AIFunction` instances directly.
`AgentFileSkillScript` becomes an `AIFunction` subclass that captures its owning `AgentFileSkill` via
an internal back-reference set during construction.
```csharp
// AgentSkill exposes scripts as AIFunction directly:
- **Fewer types.** Eliminates `AgentSkillScript` and `AgentInlineSkillScript`, reducing the public API surface by two classes.
- **Seamless interop.** Any `AIFunction` — whether from `AIFunctionFactory`, a custom subclass, or an external library — can be used as a skill script with zero wrapping.
- **Consistent with `Microsoft.Extensions.AI` ecosystem.** Scripts share the same type as tool functions used by `IChatClient` and `FunctionInvokingChatClient`, reducing conceptual overhead for developers already familiar with the ecosystem.
**Cons:**
- **No owning-skill context in invocation signature.** `AIFunction.InvokeAsync` does not accept an `AgentSkill` parameter, so `AgentFileSkillScript` must capture its owning skill via an internal setter during construction. This adds a construction-order dependency: the skill must set the back-reference on its scripts.
- **Custom script types lose automatic skill access.** Third-party `AIFunction` subclasses that need the owning skill must implement their own mechanism (e.g., constructor injection, closure capture) instead of receiving it as a method parameter.
- **Semantic overloading.** `AIFunction` now means both "a tool the model can call" and "a script within a skill", which could blur the distinction for framework users.
## Resource Representation: `AgentSkillResource` vs `AIFunction`
Two approaches were considered for representing skill resources (supplementary content such as references, assets, or dynamic data):
### Option A — Custom `AgentSkillResource` abstract base class (original design)
Resources are modeled as a custom `AgentSkillResource` abstract class with `Name`, `Description`, and
- **Clear semantic distinction.** A dedicated `AgentSkillResource` type distinguishes resources (data providers) from scripts (executable actions), making the API self-documenting.
- **Purpose-built API.** `ReadAsync` communicates intent better than `InvokeAsync` for a data-access operation.
**Cons:**
- **Wrapper overhead.** `AgentInlineSkillResource` wraps `AIFunction` internally for delegate/function cases — adding a class and indirection for no behavioral difference.
- **Parallel abstraction.** `AgentSkillResource` and `AIFunction` serve overlapping purposes (named callable that returns data), creating two parallel hierarchies.
- **Friction for consumers.** Users who already have `AIFunction` instances must wrap them in `AgentInlineSkillResource`, adding ceremony.
### Option B — Reuse `AIFunction` directly
Resources are represented as `AIFunction`. `AgentSkill.Resources` returns `IReadOnlyList<AIFunction>?`.
`AgentInlineSkillResource` becomes an `AIFunction` subclass (retained as a convenience for the static-value
pattern: `new AgentInlineSkillResource("data", "name")`). `AgentFileSkillResource` becomes an `AIFunction`
subclass that reads file content.
```csharp
// AgentSkill exposes resources as AIFunction directly:
- **Fewer base types.** Eliminates the `AgentSkillResource` abstract class, reducing the public API surface.
- **Seamless interop.** Any `AIFunction` can be used as a skill resource with zero wrapping.
**Cons:**
- **Loss of semantic distinction.** Resources and scripts are now both `AIFunction`, which could make it less obvious which list a function belongs to when reading code.
- **Static values require a wrapper.** Unlike the original `ReadAsync` which could return a stored value directly, `AIFunction.InvokeAsync` implies invocation. `AgentInlineSkillResource` is retained as a convenience subclass to handle the static-value case, so this is not eliminated — just moved to a different class.
## Decision Outcome
### 1. Keep `AgentSkillResource` and `AgentSkillScript` (Option A for both sections)
We are staying with the custom `AgentSkillResource` and `AgentSkillScript` model classes instead of reusing `AIFunction`:
- **Resources have no parameters.** If a consumer provides an `AIFunction` with parameters, those parameters will never be advertised to the LLM, and the resulting call will fail.
- **Approval breaks for `AIFunction`-based representations.** When a resource or script represented by an `AIFunction` is configured with approval, the second approval invocation will not work correctly.
- **Injecting the owning skill into an `AIFunction`-based script is problematic.** Constructor injection would introduce a circular reference between the skill and the script. An internal property setter is possible but adds coupling.
### 2. Make all agent skill classes internal
All agent-skill-related classes are made `internal` to minimize the public API surface while the feature matures. We can reconsider and promote types to `public` later based on community signal.
This leaves two public entry points:
- **`AgentSkillsProvider`** — use directly when all skills come from a single source and filtering is not needed.
- **`AgentSkillsProviderBuilder`** — use when mixing skill types or when filtering support is required.
### 3. Caching at provider level
Caching of tools and instructions is implemented inside `AgentSkillsProvider` rather than as an external decorator. Recreating tools and instructions on every provider call is wasteful, and a caching decorator sitting outside the provider would not have the information needed to cache them effectively.
The `agent-framework-core` package currently bundles OpenAI and Azure OpenAI client implementations along with their dependencies (`openai`, `azure-identity`, `azure-ai-projects`, `packaging`). This makes core heavier than necessary for users who don't use OpenAI, and it conflates the core abstractions with a specific provider implementation. Additionally, the current class naming (`OpenAIResponsesClient`, `OpenAIChatClient`) is based on the underlying OpenAI API names rather than what users actually want to do, making discoverability harder for newcomers.
## Decision Drivers
- **Lightweight core**: Core should only contain abstractions, middleware infrastructure, and telemetry — no provider-specific code or dependencies.
- **Discoverability-first**: Import namespaces should guide users to the right client. `from agent_framework.openai import ...` should surface all OpenAI-related clients; `from agent_framework.azure import ...` should surface Foundry, Azure AI, and other Azure-specific classes.
- **Provider-leading naming**: The primary client name should reflect the provider, not the underlying API. The Responses API is now the recommended default for OpenAI, so its client should be called `OpenAIChatClient` (not `OpenAIResponsesClient`).
- **Clean separation of concerns**: Azure-specific deprecated wrappers belong in the azure-ai package, not in the OpenAI package.
## Considered Options
- **Keep OpenAI in core**: Simpler but keeps core heavy; doesn't help discoverability.
- **Extract OpenAI with Azure wrappers in the OpenAI package**: Keeps Azure OpenAI wrappers alongside OpenAI code, but pollutes the OpenAI package with Azure concerns.
- **Extract OpenAI, place Azure wrappers in azure-ai**: Clean separation; the OpenAI package has zero Azure dependencies; deprecated Azure wrappers live in a single file in azure-ai for easy future deletion.
## Decision Outcome
Chosen option: "Extract OpenAI, place Azure wrappers in azure-ai", because it achieves the lightest core, cleanest OpenAI package, and the most maintainable deprecation path.
Key changes:
1.**New `agent-framework-openai` package** with dependencies on `agent-framework-core`, `openai`, and `packaging` only.
2.**Class renames**: `OpenAIResponsesClient` → `OpenAIChatClient` (Responses API), `OpenAIChatClient` → `OpenAIChatCompletionClient` (Chat Completions API). Old names remain as deprecated aliases.
3.**Deprecated classes**: `OpenAIAssistantsClient`, all `AzureOpenAI*Client` classes, `AzureAIClient`, `AzureAIAgentClient`, and `AzureAIProjectAgentProvider` are marked deprecated.
4.**New `FoundryChatClient`** in azure-ai for Azure AI Foundry Responses API access, built on `RawFoundryChatClient(RawOpenAIChatClient)`.
5.**All deprecated `AzureOpenAI*` classes** consolidated into a single file (`_deprecated_azure_openai.py`) in the azure-ai package for clean future deletion.
6.**Core's `agent_framework.openai` and `agent_framework.azure` namespaces** become lazy-loading gateways, preserving backward-compatible import paths while removing hard dependencies.
7.**Unified `model` parameter** replaces `model_id` (OpenAI), `deployment_name` (Azure OpenAI), and `model_deployment_name` (Azure AI) across all client constructors. The term `model` is intentionally generic: it naturally maps to an OpenAI model name *and* to an Azure OpenAI deployment name, making it straightforward to use `OpenAIChatClient` with either OpenAI or Azure OpenAI backends (via `AsyncAzureOpenAI`). Environment variables are similarly unified (e.g., `OPENAI_MODEL` instead of separate `OPENAI_CHAT_MODEL_ID` / `OPENAI_CHAT_COMPLETION_MODEL_ID`).
8.**`FoundryAgent`** replaces the pattern of `Agent(client=AzureAIClient(...))` for connecting to pre-configured agents in Azure AI Foundry (PromptAgents and HostedAgents). The underlying `RawFoundryAgentChatClient` is an implementation detail — most users interact only with `FoundryAgent`. `AzureAIAgentClient` is separately deprecated as it refers to the V1 Agents Service API. See below for design rationale.
### Foundry Agent Design: `FoundryAgentClient` vs `FoundryAgent`
The existing `AzureAIClient` combines two concerns: CRUD lifecycle management (creating/deleting agents on the service) and runtime communication (sending messages via the Responses API). The new design removes CRUD entirely — users connect to agents that already exist in Foundry.
**Two approaches were considered:**
**Option A — `FoundryAgentClient` only (public ChatClient):**
Users compose `Agent(client=FoundryAgentClient(...), tools=[...])`. This follows the universal `Agent(client=X)` pattern used by every other provider. However, a "client" that wraps a named remote agent (with `agent_name` as a constructor param) is semantically odd — clients typically wrap a model endpoint, not a specific agent.
**Option B — `FoundryAgent` (Agent subclass) + private `_FoundryAgentChatClient` and public `RawFoundryAgentChatClient`:**
Users write `FoundryAgent(agent_name="my-agent", ...)` for the common case. Internally, `FoundryAgent` creates a `_FoundryAgentChatClient` and passes it to the standard `Agent` base class. For advanced customization, users pass `client_type=RawFoundryAgentChatClient` (or a custom subclass) to control the client middleware layers. The `Agent(client=RawFoundryAgentChatClient(...))` composition pattern still works for users who prefer it.
**Chosen option: Option B**, because:
- The common case (`FoundryAgent(...)`) is a single object with no boilerplate.
-`client_type=` gives full control over client middleware without parameter duplication — the agent forwards connection params to the client internally.
-`RawFoundryAgent(RawAgent)` and `FoundryAgent(Agent)` mirror the established `RawAgent`/`Agent` pattern.
- Runtime validation (only `FunctionTool` allowed) lives in `RawFoundryAgentChatClient._prepare_options`, ensuring it applies regardless of how the client is used — through `FoundryAgent`, `Agent(client=...)`, or any custom composition.
**Public classes:**
-`RawFoundryAgentChatClient(RawOpenAIChatClient)` — Responses API client that injects agent reference and validates tools. Extension point for custom client middleware.
-`RawFoundryAgent(RawAgent)` — Agent without agent-level middleware/telemetry.
-`FoundryAgent(AgentTelemetryLayer, AgentMiddlewareLayer, RawFoundryAgent)` — Recommended production agent.
**Internal (private):**
-`_FoundryAgentChatClient` — Full client with function invocation, chat middleware, and telemetry layers. Created automatically by `FoundryAgent`; users customize via `client_type=RawFoundryAgentChatClient` or a custom subclass.
**Deprecated:**
-`AzureAIClient` — replaced by `FoundryAgent` (which uses `FoundryAgentClient` internally).
-`AzureAIAgentClient` — refers to V1 Agents Service API, no direct replacement.
-`AzureAIProjectAgentProvider` — replaced by `FoundryAgent`.
When using `ChatClientAgent` with tools, the `FunctionInvokingChatClient` (FIC) loops multiple times — service call → tool execution → service call → … — before producing a final response. There are two points of discrepancy between how chat history is stored by the framework's `ChatHistoryProvider` and how the underlying AI service stores chat history (e.g., OpenAI Responses with `store=true`):
1.**Persistence timing**: The AI service persists messages after *each* service call within the FIC loop. The `ChatHistoryProvider` currently persists messages only once, at the *end* of the full agent run (after all FIC loop iterations complete).
2.**Trailing `FunctionResultContent` storage**: When tool calling is terminated mid-loop (e.g., via `FunctionInvokingChatClient` termination filters), the final response from the agent may contain `FunctionResultContent` that was never sent to a subsequent service call. The AI service never stores this trailing `FunctionResultContent`, but the `ChatHistoryProvider` currently stores all response content, including the trailing `FunctionResultContent`.
These discrepancies mean that a `ChatHistoryProvider`-managed conversation and a service-managed conversation can diverge in content and structure, even when processing the same interactions.
### Practical Impact: Resuming After Tool-Call Termination
Today, users of `AIAgent` get different behaviors depending on whether chat history is stored service-side or in a `ChatHistoryProvider`. This creates concrete challenges — for example, when the function call loop is terminated and the user wants to resume the conversation in a subsequent run. With service-stored history, the trailing `FunctionResultContent` is never persisted, so the last stored message is the `FunctionCallContent` from the service. With `ChatHistoryProvider`-stored history, the trailing `FunctionResultContent`*is* persisted. The user cannot know whether the last `FunctionResultContent` is in the chat history or not without inspecting the storage mechanism, making it difficult to write resumption logic that works correctly regardless of the storage backend.
### Relationship Between the Two Discrepancies
The persistence timing and `FunctionResultContent` trimming behaviors are interrelated:
- **Per-service-call persistence**: When messages are persisted after each individual service call, trailing `FunctionResultContent` trimming is unnecessary. If tool calling is terminated, the `FunctionResultContent` from the terminated call was never sent to a subsequent service call, so it is never persisted. The per-service-call approach naturally matches the service's behavior.
- **Per-run persistence**: When messages are batched and persisted at the end of the full run, trailing `FunctionResultContent` trimming becomes necessary to match the service's behavior. Without trimming, the stored history contains `FunctionResultContent` that the service would never have stored.
## Decision Drivers
- **A. Consistency**: The default behavior of `ChatHistoryProvider` should produce stored history that closely matches what the underlying AI service would store, minimizing surprise when switching between framework-managed and service-managed chat history.
- **B. Atomicity**: A run that fails mid-way through a multi-step tool-calling loop should not leave chat history in a partially-updated state, unless the user explicitly opts into that behavior.
- **C. Recoverability**: For long-running tool-calling loops, it should be possible to recover intermediate progress if the process is interrupted, rather than losing all work from the current run.
- **D. Simplicity**: The default behavior should be easy to understand and predict for most users, without requiring knowledge of the FIC loop internals.
- **E. Flexibility**: Regardless of the chosen default, users should be able to opt into the alternative behavior.
## Considered Options
- Option 1: Per-run persistence with opt-in FRC (FunctionResultContent) trimming
### Option 1: Per-run persistence with opt-in FRC trimming
Keep the current default behavior of persisting chat history only at the end of the full agent run. Add `FunctionResultContent` trimming as an opt-in behavior to improve consistency with service storage.
- Good, because runs are atomic — chat history is only updated when the full run succeeds, satisfying driver B.
- Good, because the mental model is simple: one run = one history update, satisfying driver D.
- Good, because trimming trailing `FunctionResultContent` improves consistency with service storage, partially satisfying driver A.
- Bad, because the default persistence timing still differs from the service's behavior (per-run vs. per-service-call), only partially satisfying driver A.
- Bad, because if the process crashes mid-loop, all intermediate progress from the current run is lost, not satisfying driver C.
- Bad, because this option alone does not provide a way for users to opt into per-service-call persistence, not satisfying driver E.
Introduce an optional RequirePerServiceCallChatHistoryPersistence setting to persist chat history after each individual service call within the FIC loop, matching the AI service's behavior. Trailing `FunctionResultContent` trimming is unnecessary with this approach (it is naturally handled).
- Good, because the stored history matches the service's behavior when opting in for both timing and content, fully satisfying driver A.
- Good, because intermediate progress is preserved if the process is interrupted, satisfying driver C.
- Good, because no separate `FunctionResultContent` trimming logic is needed, reducing complexity.
- Bad, because chat history may be left in an incomplete state if the run fails mid-loop (e.g., `FunctionCallContent` stored without corresponding `FunctionResultContent`), not satisfying driver B. A subsequent run cannot proceed without manually providing the missing `FunctionResultContent`.
- Bad, because the mental model is more complex: a single run may produce multiple history updates, partially failing driver D.
- Neutral, because users can opt out to per-run persistence if they prefer atomicity, satisfying driver E.
## Decision Outcome
Chosen option: **Option 2: Opt-in per-service-call persistence (via `RequirePerServiceCallChatHistoryPersistence`)**. The existing per-run persistence behavior is retained as-is, requiring no changes from users. Per-service-call persistence is available as an opt-in feature via the `RequirePerServiceCallChatHistoryPersistence` setting. This satisfies drivers B (atomicity) and D (simplicity) for the common case, while fully satisfying driver A (consistency) for users who opt into simulated service-stored behavior. Users who need per-service-call persistence for recoverability (driver C) can enable it explicitly.
### Configuration Matrix
The behavior depends on the combination of `UseProvidedChatClientAsIs` and `RequirePerServiceCallChatHistoryPersistence`:
| `false` (default) | `false` (default) | **Per-run persistence.** Messages are persisted at the end of the full agent run via the `ChatHistoryProvider`. |
| `false` | `true` | **Per-service-call persistence (simulated).** A `PerServiceCallChatHistoryPersistingChatClient` middleware is automatically injected into the chat client pipeline between `FunctionInvokingChatClient` and the leaf `IChatClient`. Messages are persisted after each service call. A sentinel `ConversationId` causes FIC to treat the conversation as service-managed. |
| `true` | `false` | **Per-run persistence.** No middleware is injected because the user has provided a custom chat client stack. Messages are persisted at the end of the run. |
| `true` | `true` | **User responsibility.** The system checks whether the custom chat client stack includes a `PerServiceCallChatHistoryPersistingChatClient`. If not, a warning is emitted — the user is expected to have added their own per-service-call persistence mechanism. End-of-run persistence is skipped. |
### Consequences
- Good, because per-run persistence is atomic by default — chat history is only updated when the full run succeeds, satisfying driver B.
- Good, because the default mental model is simple: one run = one history update, satisfying driver D.
- Good, because users who opt into `RequirePerServiceCallChatHistoryPersistence` get stored history that matches the service's behavior for both timing and content, fully satisfying driver A.
- Good, because per-service-call persistence preserves intermediate progress if the process is interrupted, satisfying driver C when opted in.
- Good, because no separate `FunctionResultContent` trimming logic is needed when per-service-call persistence is active — it is naturally handled.
- Good, because conflict detection (configurable via `ThrowOnChatHistoryProviderConflict`, `WarnOnChatHistoryProviderConflict`, `ClearOnChatHistoryProviderConflict`) prevents misconfiguration when a service returns a `ConversationId` alongside a configured `ChatHistoryProvider`.
- Bad, because per-service-call persistence (when opted in) may leave chat history in an incomplete state if the run fails mid-loop (e.g., `FunctionCallContent` stored without corresponding `FunctionResultContent`), requiring manual recovery in rare cases.
- Neutral, because users who want per-service-call consistency can opt in via `RequirePerServiceCallChatHistoryPersistence = true`, satisfying driver E.
- Neutral, because increased write frequency from per-service-call persistence may impact performance for some storage backends; this can be mitigated with a caching decorator.
### Implementation Notes
#### Conversation ID Consistency
When `RequirePerServiceCallChatHistoryPersistence` is enabled, the `PerServiceCallChatHistoryPersistingChatClient`
decorator also updates `session.ConversationId` after each service call. This handles two scenarios:
1.**Framework-managed chat history** — the decorator sets a sentinel `ConversationId` on the response
so that `FunctionInvokingChatClient` treats the conversation as service-managed (clearing accumulated
history between iterations and not injecting duplicate `FunctionCallContent` during approval processing).
2.**Service-stored chat history** — when the service returns a real `ConversationId`, the decorator
updates `session.ConversationId` immediately after each service call, rather than deferring the update
to the end of the run. This ensures intermediate ConversationId changes are captured even if the
process is interrupted mid-loop.
For some service-stored scenarios (e.g., the Conversations API with the Responses API), there is only
one thread with one ID, so every service call returns the same ConversationId and this per-call update
makes no practical difference. Enabling `RequirePerServiceCallChatHistoryPersistence` ensures consistent
per-service-call behavior across all service types regardless of how they manage ConversationIds.
description:How to use the verify-samples tool to run, verify, and manage sample definitions in the Agent Framework repository. Use this when adding, updating, or running sample verification.
---
# verify-samples Tool
The `verify-samples` project (`dotnet/eng/verify-samples/`) is an automated tool that runs sample projects and verifies their output using deterministic checks and AI-powered verification.
## Running verify-samples
**Important:** By default, samples must be pre-built before running verify-samples. Build the solution first, or pass `--build` to build samples during the run:
-`AZURE_OPENAI_ENDPOINT` — for the AI verification agent
-`AZURE_OPENAI_DEPLOYMENT_NAME` (optional, defaults to `gpt-5-mini`)
Individual samples require their own env vars (e.g., `AZURE_AI_PROJECT_ENDPOINT`). The tool automatically checks and skips samples with missing env vars.
### Output Files
-`--log results.log` — detailed per-sample log with stdout/stderr, AI reasoning, and a summary
-`--csv results.csv` — tabular summary with Sample, ProjectPath, Status, FailedChecks, and Failures columns
-`--md results.md` — Markdown summary with results table and collapsible failure details (suitable for GitHub PR comments)
## Sample Categories
Definitions are in the `dotnet/eng/verify-samples/` directory:
// Skip this sample with a reason (for structural issues only)
SkipReason=null,// or "Requires external service X."
// Deterministic checks: substrings that must appear in stdout
MustContain=["=== Section Header ==="],
// Substrings that must NOT appear in stdout
MustNotContain=[],
// If true, only MustContain checks are used (no AI verification)
IsDeterministic=false,
// AI verification: natural-language descriptions of expected output
// Each entry describes one aspect to verify independently
ExpectedOutputDescription=
[
"The output should show structured person information with Name, Age, and Occupation fields.",
"The output should not contain error messages or stack traces.",
],
// Stdin inputs to feed to the sample (for interactive samples)
Inputs=["Y","Y","Y"],
// Delay between stdin inputs in ms (default 2000, increase for LLM calls between inputs)
InputDelayMs=3000,
}
```
## How to Add a New Sample Definition
1.**Check the sample's Program.cs** to understand:
- What environment variables it reads (look for `GetEnvironmentVariable`)
- Whether it needs stdin input (look for `Console.ReadLine`, `Application.GetInput`)
- Whether it has an external loop (look for `EXIT` patterns in YAML workflows)
- What output it produces (section headers, markers, expected behavior)
- Whether it exits on its own or runs as a server
2.**Choose the right verification strategy:**
- **Deterministic** (`IsDeterministic = true`): Use `MustContain` for samples with fixed output strings. No AI verification.
- **AI-verified** (default): Use `ExpectedOutputDescription` with semantic descriptions. Write expectations that are flexible enough for non-deterministic LLM output.
- **Both**: Use `MustContain` for fixed markers AND `ExpectedOutputDescription` for LLM-generated content.
3.**Set `SkipReason` only for structural issues:**
@@ -29,13 +29,14 @@ using types like `IChatClient`, `FunctionInvokingChatClient`, `AITool`, `AIFunct
## Key Conventions
- **Encoding**: All new files must be saved with UTF-8 encoding with BOM (Byte Order Mark). This is required for `dotnet format` to work correctly.
- **Command output capture**: When running `dotnet build`, `dotnet test`, `dotnet format`, or similar commands, redirect output to a temp file first (e.g., `dotnet build --tl:off 2>&1 | Out-File $env:TEMP\build.log`), then analyze the file as needed. This avoids re-running expensive commands when the initial analysis misses something.
- **Encoding**: All new files must be saved with UTF-8 encoding with BOM (Byte Order Mark). This is required for `dotnet format` to work correctly. When using PowerShell `Set-Content`, always pass `-Encoding UTF8BOM` to preserve the BOM (e.g., `Set-Content $file $content -NoNewline -Encoding UTF8BOM`).
- **Copyright header**: `// Copyright (c) Microsoft. All rights reserved.` at top of all `.cs` files
- **XML docs**: Required for all public methods and classes
- **Async**: Use `Async` suffix for methods returning `Task`/`ValueTask`
- **Private classes**: Should be `sealed` unless subclassed
- **Config**: Read from environment variables with `UPPER_SNAKE_CASE` naming
- **Tests**: Add Arrange/Act/Assert comments; use Moq for mocking
- **Tests**: Add Arrange/Act/Assert comments; use Moq for mocking; test methods returning `Task`/`ValueTask` must use the `Async` suffix.
">> Use deserialized session with previously created memories",
">> Read memories using memory component",
"MEMORY - User Name:",
"MEMORY - User Age:",
">> Use new session with previously created memories",
],
ExpectedOutputDescription=
[
"In the 'Use session with blank memory' section, the agent should respond to the user's messages. It may ask for the user's name or age if not yet known.",
"In the 'Use deserialized session with previously created memories' section, the agent should correctly recall that the user's name is Ruaidhrí and age is 20.",
"The 'MEMORY - User Name:' line should show 'Ruaidhrí' (or a close transliteration).",
"The 'MEMORY - User Age:' line should show '20'.",
"In the 'Use new session with previously created memories' section, the agent should know the user's name and age from the transferred memory.",
"The output should not contain error messages or stack traces.",
ExpectedOutputDescription=["The output should show a customer support workflow processing a laptop issue, with agent responses providing troubleshooting or support."],
ExpectedOutputDescription=["The output should show a declarative workflow executing generated code, processing a math question and producing a result."],
ExpectedOutputDescription=["The output should show a workflow calling function tools (e.g. a menu plugin) to answer a question about restaurant specials."],
ExpectedOutputDescription=["The output should show a workflow invoking a function tool (e.g. a menu plugin) to answer a question about the soup of the day."],
Inputs=["Search for .NET tutorials on Microsoft Learn"],
InputDelayMs=3000,
ExpectedOutputDescription=["The output should show a workflow using MCP tools to search Microsoft Learn documentation and provide a summary of results."],
ExpectedOutputDescription=["The output should show a student-teacher workflow where a student asks a math question and a teacher provides the answer."],
ExpectedOutputDescription=["The output should show a workflow using an MCP tool with approval to search Microsoft Learn, followed by an exit from the input loop."],
<!-- Override the repo-wide IsPackable=false default from Directory.Build.props. Projects that want to stay non-packable (e.g. Mem0) set IsPackable=false AFTER importing this file. -->
<IsPackable>true</IsPackable>
<!-- Package validation. Baseline Version should be the latest version available on NuGet. -->
@@ -121,7 +109,7 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent
// Track approval ID to original call ID mapping
_=newDictionary<string,string>();
#pragmawarningdisableMEAI001// Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
varendpoint=Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")??thrownewInvalidOperationException("AZURE_OPENAI_ENDPOINT environment variable is not set.");
$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini"# Optional, defaults to gpt-4o-mini
$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-5.4-mini"# Optional, defaults to gpt-5.4-mini
```
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure OpenAI resource.
@@ -5,8 +5,8 @@ This sample demonstrates how to create an AIAgent using Anthropic Claude models
The sample supports three deployment scenarios:
1.**Anthropic Public API** - Direct connection to Anthropic's public API
2.**Azure Foundry with API Key** - Anthropic models deployed through Azure Foundry using API key authentication
3.**Azure Foundry with Azure CLI** - Anthropic models deployed through Azure Foundry using Azure CLI credentials
2.**Microsoft Foundry with API Key** - Anthropic models deployed through Microsoft Foundry using API key authentication
3.**Microsoft Foundry with Azure CLI** - Anthropic models deployed through Microsoft Foundry using Azure CLI credentials
## Prerequisites
@@ -25,29 +25,29 @@ $env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic A
$env:ANTHROPIC_CHAT_MODEL_NAME="claude-haiku-4-5"# Optional, defaults to claude-haiku-4-5
```
### For Azure Foundry with API Key
### For Microsoft Foundry with API Key
-Azure Foundry service endpoint and deployment configured
-Microsoft Foundry service endpoint and deployment configured
- Anthropic API key
Set the following environment variables:
```powershell
$env:ANTHROPIC_RESOURCE="your-foundry-resource-name"# Replace with your Azure Foundry resource name (subdomain before .services.ai.azure.com)
$env:ANTHROPIC_RESOURCE="your-foundry-resource-name"# Replace with your Microsoft Foundry resource name (subdomain before .services.ai.azure.com)
$env:ANTHROPIC_API_KEY="your-anthropic-api-key"# Replace with your Anthropic API key
$env:ANTHROPIC_CHAT_MODEL_NAME="claude-haiku-4-5"# Optional, defaults to claude-haiku-4-5
```
### For Azure Foundry with Azure CLI
### For Microsoft Foundry with Azure CLI
-Azure Foundry service endpoint and deployment configured
-Microsoft Foundry service endpoint and deployment configured
- Azure CLI installed and authenticated (for Azure credential authentication)
Set the following environment variables:
```powershell
$env:ANTHROPIC_RESOURCE="your-foundry-resource-name"# Replace with your Azure Foundry resource name (subdomain before .services.ai.azure.com)
$env:ANTHROPIC_RESOURCE="your-foundry-resource-name"# Replace with your Microsoft Foundry resource name (subdomain before .services.ai.azure.com)
$env:ANTHROPIC_CHAT_MODEL_NAME="claude-haiku-4-5"# Optional, defaults to claude-haiku-4-5
```
**Note**: When using Azure Foundry with Azure CLI, make sure you're logged in with `az login` and have access to the Azure Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
**Note**: When using Microsoft Foundry with Azure CLI, make sure you're logged in with `az login` and have access to the Microsoft Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
#pragmawarningdisableCS0618// Type or member is obsolete - sample uses deprecated PersistentAgentsClientExtensions
// This sample shows how to create and use a simple AI agent with Azure Foundry Agents as the backend.
// This sample shows how to create and use a simple AI agent with Microsoft Foundry Agents as the backend.
usingAzure.AI.Agents.Persistent;
usingAzure.Identity;
usingMicrosoft.Agents.AI;
varendpoint=Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT")??thrownewInvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
@@ -13,14 +13,14 @@ Below is a comparison between the classic and new Foundry Agents approaches:
Before you begin, ensure you have the following prerequisites:
- .NET 10 SDK or later
-Azure Foundry service endpoint and deployment configured
-Microsoft Foundry service endpoint and deployment configured
- Azure CLI installed and authenticated (for Azure credential authentication)
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Microsoft Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
Set the following environment variables:
```powershell
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"# Replace with your Azure Foundry resource endpoint
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"# Optional, defaults to gpt-4o-mini
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"# Replace with your Microsoft Foundry resource endpoint
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini"# Optional, defaults to gpt-5.4-mini
// This sample shows how to create and use a AI agents with Azure Foundry Agents as the backend.
// This sample shows how to create and use AI agents with Microsoft Foundry Agents as the backend.
usingAzure.AI.Projects;
usingAzure.AI.Projects.Agents;
usingAzure.Identity;
usingMicrosoft.Agents.AI;
usingMicrosoft.Agents.AI.Foundry;
varendpoint=Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT")??thrownewInvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
// Define the agent you want to create. (Prompt Agent in this case)
varagentVersionCreationOptions=newAgentVersionCreationOptions(newPromptAgentDefinition(model:deploymentName){Instructions="You are good at telling jokes."});
varagentVersionCreationOptions=newProjectsAgentVersionCreationOptions(newDeclarativeAgentDefinition(model:deploymentName){Instructions="You are good at telling jokes."});
// Azure.AI.Agents SDK creates and manages agent by name and versions.
// You can create a server side agent version with the Azure.AI.Agents SDK client below.
// You can also create another AIAgent version by providing the same name with a different definition.
AIAgentnewJokerAgent=awaitaiProjectClient.CreateAIAgentAsync(name:JokerName,model:deploymentName,instructions:"You are extremely hilarious at telling jokes.");
newProjectsAgentVersionCreationOptions(newDeclarativeAgentDefinition(model:deploymentName){Instructions="You are extremely hilarious at telling jokes."}));
@@ -13,14 +13,14 @@ Below is a comparison between the classic and new Foundry Agents approaches:
Before you begin, ensure you have the following prerequisites:
- .NET 10 SDK or later
-Azure Foundry service endpoint and deployment configured
-Microsoft Foundry service endpoint and deployment configured
- Azure CLI installed and authenticated (for Azure credential authentication)
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Microsoft Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
Set the following environment variables:
```powershell
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"# Replace with your Azure Foundry resource endpoint
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"# Optional, defaults to gpt-4o-mini
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"# Replace with your Microsoft Foundry resource endpoint
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini"# Optional, defaults to gpt-5.4-mini
// This sample shows how to use the OpenAI SDK to create and use a simple AI agent with any model hosted in Azure AI Foundry.
// You could use models from Microsoft, OpenAI, DeepSeek, Hugging Face, Meta, xAI or any other model you have deployed in your Azure AI Foundry resource.
// This sample shows how to use the OpenAI SDK to create and use a simple AI agent with any model hosted in Microsoft Foundry.
// You could use models from Microsoft, OpenAI, DeepSeek, Hugging Face, Meta, xAI or any other model you have deployed in your Microsoft Foundry resource.
// Note: Ensure that you pick a model that suits your needs. For example, if you want to use function calling, ensure that the model you pick supports function calling.
usingSystem.ClientModel;
@@ -15,7 +15,7 @@ var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? th
This sample shows how to use the OpenAI SDK to create and use a simple AI agent with any model hosted in Azure AI Foundry.
This sample shows how to use the OpenAI SDK to create and use a simple AI agent with any model hosted in Microsoft Foundry.
You could use models from Microsoft, OpenAI, DeepSeek, Hugging Face, Meta, xAI or any other model you have deployed in Azure AI Foundry.
You could use models from Microsoft, OpenAI, DeepSeek, Hugging Face, Meta, xAI or any other model you have deployed in Microsoft Foundry.
**Note**: Ensure that you pick a model that suits your needs. For example, if you want to use function calling, ensure that the model you pick supports function calling.
@@ -11,19 +11,19 @@ You could use models from Microsoft, OpenAI, DeepSeek, Hugging Face, Meta, xAI o
Before you begin, ensure you have the following prerequisites:
- .NET 10 SDK or later
-Azure AI Foundry resource
- A model deployment in your Azure AI Foundry resource. This example defaults to using the `Phi-4-mini-instruct` model,
-Microsoft Foundry resource
- A model deployment in your Microsoft Foundry resource. This example defaults to using the `Phi-4-mini-instruct` model,
so if you want to use a different model, ensure that you set your `AZURE_AI_MODEL_DEPLOYMENT_NAME` environment
variable to the name of your deployed model.
- An API key or role based authentication to access the Azure AI Foundry resource
- An API key or role based authentication to access the Microsoft Foundry resource
See [here](https://learn.microsoft.com/en-us/azure/ai-foundry/quickstarts/get-started-code?tabs=csharp) for more info on setting up these prerequisites
Set the following environment variables:
```powershell
# Replace with your Azure AI Foundry resource endpoint
# Ensure that you have the "/openai/v1/" path in the URL, since this is required when using the OpenAI SDK to access Azure Foundry models.
# Replace with your Microsoft Foundry resource endpoint
# Ensure that you have the "/openai/v1/" path in the URL, since this is required when using the OpenAI SDK to access Microsoft Foundry models.
@@ -18,14 +18,13 @@ See the README.md for each sample for the prerequisites for that sample.
|[Creating an AIAgent with Anthropic](./Agent_With_Anthropic/)|This sample demonstrates how to create an AIAgent using Anthropic Claude models as the underlying inference service|
|[Creating an AIAgent with Foundry Agents using Azure.AI.Agents.Persistent](./Agent_With_AzureAIAgentsPersistent/)|This sample demonstrates how to create a Foundry Persistent agent and expose it as an AIAgent using the Azure.AI.Agents.Persistent SDK|
|[Creating an AIAgent with Foundry Agents using Azure.AI.Project](./Agent_With_AzureAIProject/)|This sample demonstrates how to create an Foundry Project agent and expose it as an AIAgent using the Azure.AI.Project SDK|
|[Creating an AIAgent with AzureFoundry Model](./Agent_With_AzureFoundryModel/)|This sample demonstrates how to use any model deployed to Azure Foundry to create an AIAgent|
|[Creating an AIAgent with Foundry Model](./Agent_With_AzureFoundryModel/)|This sample demonstrates how to use any model deployed to Microsoft Foundry to create an AIAgent|
|[Creating an AIAgent with Azure OpenAI ChatCompletion](./Agent_With_AzureOpenAIChatCompletion/)|This sample demonstrates how to create an AIAgent using Azure OpenAI ChatCompletion as the underlying inference service|
|[Creating an AIAgent with Azure OpenAI Responses](./Agent_With_AzureOpenAIResponses/)|This sample demonstrates how to create an AIAgent using Azure OpenAI Responses as the underlying inference service|
|[Creating an AIAgent with a custom implementation](./Agent_With_CustomImplementation/)|This sample demonstrates how to create an AIAgent with a custom implementation|
|[Creating an AIAgent with GitHub Copilot](./Agent_With_GitHubCopilot/)|This sample demonstrates how to create an AIAgent using GitHub Copilot SDK as the underlying inference service|
|[Creating an AIAgent with Ollama](./Agent_With_Ollama/)|This sample demonstrates how to create an AIAgent using Ollama as the underlying inference service|
|[Creating an AIAgent with ONNX](./Agent_With_ONNX/)|This sample demonstrates how to create an AIAgent using ONNX as the underlying inference service|
|[Creating an AIAgent with OpenAI Assistants](./Agent_With_OpenAIAssistants/)|This sample demonstrates how to create an AIAgent using OpenAI Assistants as the underlying inference service.</br>WARNING: The Assistants API is deprecated and will be shut down. For more information see the OpenAI documentation: https://platform.openai.com/docs/assistants/migration|
|[Creating an AIAgent with OpenAI ChatCompletion](./Agent_With_OpenAIChatCompletion/)|This sample demonstrates how to create an AIAgent using OpenAI ChatCompletion as the underlying inference service|
|[Creating an AIAgent with OpenAI Responses](./Agent_With_OpenAIResponses/)|This sample demonstrates how to create an AIAgent using OpenAI Responses as the underlying inference service|
AgentResponseresponse2=awaitagent.RunAsync("I had 3 client dinners and a $1,200 flight last week. Return a draft expense report and ask about any missing details.",
This sample demonstrates how to use **Agent Skills** with a `ChatClientAgent` in the Microsoft Agent Framework.
## What are Agent Skills?
Agent Skills are modular packages of instructions and resources that enable AI agents to perform specialized tasks. They follow the [Agent Skills specification](https://agentskills.io/) and implement the progressive disclosure pattern:
1.**Advertise**: Skills are advertised with name + description (~100 tokens per skill)
2.**Load**: Full instructions are loaded on-demand via `load_skill` tool
3.**Resources**: References and other files loaded via `read_skill_resource` tool
## Skills Included
### expense-report
Policy-based expense filing with spending limits, receipt requirements, and approval workflows.
description:File and validate employee expense reports according to Contoso company policy. Use when asked about expense submissions, reimbursement rules, receipt requirements, spending limits, or expense categories.
metadata:
author:contoso-finance
version:"2.1"
---
# Expense Report
## Categories and Limits
| Category | Limit | Receipt | Approval |
|---|---|---|---|
| Meals — solo | $50/day | >$25 | No |
| Meals — team/client | $75/person | Always | Manager if >$200 total |
1. Collect receipts — must show vendor, date, amount, payment method.
2. Categorize per table above.
3. Use template: [assets/expense-report-template.md](assets/expense-report-template.md).
4. For client/team meals: list attendee names and business purpose.
5. Submit — auto-approved if <$500; manager if $500–$2,000; VP if >$2,000.
6. Reimbursement: 10 business days via direct deposit.
## Policy Rules
- Submit within 30 days of transaction.
- Alcohol is never reimbursable.
- Foreign currency: convert to USD at transaction-date rate; note original currency and amount.
- Mixed personal/business travel: only business portion reimbursable; provide comparison quotes.
- Lost receipts (>$25): file Lost Receipt Affidavit from Finance. Max 2 per quarter.
- For policy questions not covered above, consult the FAQ: [references/POLICY_FAQ.md](references/POLICY_FAQ.md). Answers should be based on what this document and the FAQ state.
**Q: Can I expense coffee or snacks during the workday?**
A: Daily coffee/snacks under $10 are not reimbursable (considered personal). Coffee purchased during a client meeting or team working session is reimbursable as a team meal.
**Q: What if a team dinner exceeds the per-person limit?**
A: The $75/person limit applies as a guideline. Overages up to 20% are accepted with a written justification (e.g., "client dinner at venue chosen by client"). Overages beyond 20% require pre-approval from your VP.
**Q: Do I need to list every attendee?**
A: Yes. For client meals, list the client's name and company. For team meals, list all employee names. For groups over 10, you may attach a separate attendee list.
## Travel
**Q: Can I book a premium economy or business class flight?**
A: Economy class is the standard. Premium economy is allowed for flights over 6 hours. Business class requires VP pre-approval and is generally reserved for flights over 10 hours or medical accommodation.
**Q: What about ride-sharing (Uber/Lyft) vs. rental cars?**
A: Use ride-sharing for trips under 30 miles round-trip. Rent a car for multi-day travel or when ride-sharing would exceed $100/day. Always choose the compact/standard category unless traveling with 3+ people.
**Q: Are tips reimbursable?**
A: Tips up to 20% are reimbursable for meals, taxi/ride-share, and hotel housekeeping. Tips above 20% require justification.
## Lodging
**Q: What if the $250/night limit isn't enough for the city I'm visiting?**
A: For high-cost cities (New York, San Francisco, London, Tokyo, Sydney), the limit is automatically increased to $350/night. No additional approval is needed. For other locations where rates are unusually high (e.g., during a major conference), request a per-trip exception from your manager before booking.
**Q: Can I stay with friends/family instead and get a per-diem?**
A: No. Contoso reimburses actual lodging costs only, not per-diems.
## Subscriptions and Software
**Q: Can I expense a personal productivity tool?**
A: Software must be directly related to your job function. Tools like IDE licenses, design software, or project management apps are reimbursable. General productivity apps (note-taking, personal calendar) are not, unless your manager confirms a business need in writing.
**Q: What about annual subscriptions?**
A: Annual subscriptions over $200 require manager approval before purchase. Submit the approval email with your expense report.
## Receipts and Documentation
**Q: My receipt is faded/damaged. What do I do?**
A: Try to obtain a duplicate from the vendor. If not possible, submit a Lost Receipt Affidavit (available from the Finance SharePoint site). You're limited to 2 affidavits per quarter.
**Q: Do I need a receipt for parking meters or tolls?**
A: For amounts under $15, no receipt is required — just note the date, location, and amount. For $15 and above, a receipt or bank/credit card statement excerpt is required.
## Approval and Reimbursement
**Q: My manager is on leave. Who approves my report?**
A: Expense reports can be approved by your skip-level manager or any manager designated as an alternate approver in the expense system.
**Q: Can I submit expenses from a previous quarter?**
A: The standard 30-day window applies. Expenses older than 30 days require a written explanation and VP approval. Expenses older than 90 days are not reimbursable except in extraordinary circumstances (extended leave, medical emergency) with CFO approval.
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.