diff --git a/.github/actions/sample-validation-setup/action.yml b/.github/actions/sample-validation-setup/action.yml
index 2920aaa5bd..14c92694ff 100644
--- a/.github/actions/sample-validation-setup/action.yml
+++ b/.github/actions/sample-validation-setup/action.yml
@@ -34,7 +34,7 @@ runs:
- name: Test Copilot CLI
shell: bash
- run: copilot -p "What can you do in one sentence?"
+ run: copilot --version && copilot -p "What can you do in one sentence?"
- name: Azure CLI Login
uses: azure/login@v2
diff --git a/.github/workflows/python-sample-validation.yml b/.github/workflows/python-sample-validation.yml
index 63f95a78c3..7ce2219573 100644
--- a/.github/workflows/python-sample-validation.yml
+++ b/.github/workflows/python-sample-validation.yml
@@ -67,11 +67,13 @@ jobs:
# Azure AI configuration
AZURE_AI_PROJECT_ENDPOINT: ${{ vars.AZURE_AI_PROJECT_ENDPOINT }}
AZURE_AI_MODEL_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }}
+ FOUNDRY_PROJECT_ENDPOINT: ${{ vars.FOUNDRY_PROJECT_ENDPOINT }}
+ FOUNDRY_MODEL: ${{ vars.FOUNDRY_MODEL }}
# Azure OpenAI configuration
AZURE_OPENAI_ENDPOINT: ${{ vars.AZUREOPENAI__ENDPOINT }}
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }}
AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }}
- AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__EMBEDDINGDEPLOYMENTNAME }}
+ AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME }}
# OpenAI configuration
OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }}
OPENAI_CHAT_MODEL_ID: ${{ vars.OPENAI__CHATMODELID }}
@@ -97,6 +99,8 @@ jobs:
- name: Create .env for samples
run: |
+ echo "FOUNDRY_PROJECT_ENDPOINT=$FOUNDRY_PROJECT_ENDPOINT" >> .env
+ echo "FOUNDRY_MODEL=$FOUNDRY_MODEL" >> .env
echo "AZURE_AI_PROJECT_ENDPOINT=$AZURE_AI_PROJECT_ENDPOINT" >> .env
echo "AZURE_AI_MODEL_DEPLOYMENT_NAME=$AZURE_AI_MODEL_DEPLOYMENT_NAME" >> .env
echo "AZURE_OPENAI_ENDPOINT=$AZURE_OPENAI_ENDPOINT" >> .env
@@ -125,6 +129,7 @@ jobs:
environment: integration
env:
OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }}
+ OPENAI_MODEL: ${{ vars.OPENAI__CHATMODELID }}
OPENAI_CHAT_MODEL_ID: ${{ vars.OPENAI__CHATMODELID }}
OPENAI_RESPONSES_MODEL_ID: ${{ vars.OPENAI__RESPONSESMODELID }}
defaults:
@@ -144,6 +149,7 @@ jobs:
- name: Create .env for samples
run: |
echo "OPENAI_API_KEY=$OPENAI_API_KEY" >> .env
+ echo "OPENAI_MODEL=$OPENAI_MODEL" >> .env
echo "OPENAI_CHAT_MODEL_ID=$OPENAI_CHAT_MODEL_ID" >> .env
echo "OPENAI_RESPONSES_MODEL_ID=$OPENAI_RESPONSES_MODEL_ID" >> .env
@@ -158,8 +164,8 @@ jobs:
name: validation-report-02-agents-openai
path: python/samples/sample_validation/reports/
- validate-02-agents-azure-openai:
- name: Validate 02-agents/providers/azure_openai
+ validate-02-agents-azure:
+ name: Validate 02-agents/providers/azure
runs-on: ubuntu-latest
environment: integration
env:
@@ -190,93 +196,13 @@ jobs:
- name: Run sample validation
run: |
- cd scripts && uv run python -m sample_validation --subdir 02-agents/providers/azure_openai --save-report --report-name 02-agents-azure-openai
+ cd scripts && uv run python -m sample_validation --subdir 02-agents/providers/azure --save-report --report-name 02-agents-azure
- name: Upload validation report
uses: actions/upload-artifact@v7
if: always()
with:
- name: validation-report-02-agents-azure-openai
- path: python/samples/sample_validation/reports/
-
- validate-02-agents-azure-ai:
- name: Validate 02-agents/providers/azure_ai
- runs-on: ubuntu-latest
- environment: integration
- env:
- AZURE_AI_PROJECT_ENDPOINT: ${{ vars.AZURE_AI_PROJECT_ENDPOINT }}
- AZURE_AI_MODEL_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }}
- AZURE_AI_CHAT_MODEL_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }}
- AZURE_AI_EMBEDDING_MODEL_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__EMBEDDINGDEPLOYMENTNAME }}
- BING_CONNECTION_ID: ${{ secrets.BING_CONNECTION_ID }}
- defaults:
- run:
- working-directory: python
- steps:
- - uses: actions/checkout@v6
-
- - name: Setup environment
- uses: ./.github/actions/sample-validation-setup
- with:
- azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
- azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
- azure-subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- os: ${{ runner.os }}
-
- - name: Create .env for samples
- run: |
- echo "AZURE_AI_PROJECT_ENDPOINT=$AZURE_AI_PROJECT_ENDPOINT" >> .env
- echo "AZURE_AI_MODEL_DEPLOYMENT_NAME=$AZURE_AI_MODEL_DEPLOYMENT_NAME" >> .env
- echo "AZURE_AI_CHAT_MODEL_DEPLOYMENT_NAME=$AZURE_AI_CHAT_MODEL_DEPLOYMENT_NAME" >> .env
- echo "AZURE_AI_EMBEDDING_MODEL_DEPLOYMENT_NAME=$AZURE_AI_EMBEDDING_MODEL_DEPLOYMENT_NAME" >> .env
- echo "BING_CONNECTION_ID=$BING_CONNECTION_ID" >> .env
-
- - name: Run sample validation
- run: |
- cd scripts && uv run python -m sample_validation --subdir 02-agents/providers/azure_ai --save-report --report-name 02-agents-azure-ai
-
- - name: Upload validation report
- uses: actions/upload-artifact@v7
- if: always()
- with:
- name: validation-report-02-agents-azure-ai
- path: python/samples/sample_validation/reports/
-
- validate-02-agents-azure-ai-agent:
- name: Validate 02-agents/providers/azure_ai_agent
- runs-on: ubuntu-latest
- environment: integration
- env:
- AZURE_AI_PROJECT_ENDPOINT: ${{ vars.AZURE_AI_PROJECT_ENDPOINT }}
- AZURE_AI_MODEL_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }}
- defaults:
- run:
- working-directory: python
- steps:
- - uses: actions/checkout@v6
-
- - name: Setup environment
- uses: ./.github/actions/sample-validation-setup
- with:
- azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
- azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
- azure-subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- os: ${{ runner.os }}
-
- - name: Create .env for samples
- run: |
- echo "AZURE_AI_PROJECT_ENDPOINT=$AZURE_AI_PROJECT_ENDPOINT" >> .env
- echo "AZURE_AI_MODEL_DEPLOYMENT_NAME=$AZURE_AI_MODEL_DEPLOYMENT_NAME" >> .env
-
- - name: Run sample validation
- run: |
- cd scripts && uv run python -m sample_validation --subdir 02-agents/providers/azure_ai_agent --save-report --report-name 02-agents-azure-ai-agent
-
- - name: Upload validation report
- uses: actions/upload-artifact@v7
- if: always()
- with:
- name: validation-report-02-agents-azure-ai-agent
+ name: validation-report-02-agents-azure
path: python/samples/sample_validation/reports/
validate-02-agents-anthropic:
@@ -409,11 +335,13 @@ jobs:
name: validation-report-02-agents-ollama
path: python/samples/sample_validation/reports/
- validate-02-agents-foundry-local:
- name: Validate 02-agents/providers/foundry_local
- if: false # Temporarily disabled - requires local Foundry setup
+ validate-02-agents-foundry:
+ name: Validate 02-agents/providers/foundry
runs-on: ubuntu-latest
environment: integration
+ env:
+ FOUNDRY_PROJECT_ENDPOINT: ${{ vars.FOUNDRY_PROJECT_ENDPOINT }}
+ FOUNDRY_MODEL: ${{ vars.FOUNDRY_MODEL }}
defaults:
run:
working-directory: python
@@ -428,15 +356,20 @@ jobs:
azure-subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
os: ${{ runner.os }}
+ - name: Create .env for samples
+ run: |
+ echo "FOUNDRY_PROJECT_ENDPOINT=$FOUNDRY_PROJECT_ENDPOINT" >> .env
+ echo "FOUNDRY_MODEL=$FOUNDRY_MODEL" >> .env
+
- name: Run sample validation
run: |
- cd scripts && uv run python -m sample_validation --subdir 02-agents/providers/foundry_local --save-report --report-name 02-agents-foundry-local
+ cd scripts && uv run python -m sample_validation --subdir 02-agents/providers/foundry --save-report --report-name 02-agents-foundry
- name: Upload validation report
uses: actions/upload-artifact@v7
if: always()
with:
- name: validation-report-02-agents-foundry-local
+ name: validation-report-02-agents-foundry
path: python/samples/sample_validation/reports/
validate-02-agents-copilotstudio:
@@ -516,6 +449,8 @@ jobs:
environment: integration
env:
# Azure AI configuration
+ FOUNDRY_PROJECT_ENDPOINT: ${{ vars.FOUNDRY_PROJECT_ENDPOINT }}
+ FOUNDRY_MODEL: ${{ vars.FOUNDRY_MODEL }}
AZURE_AI_PROJECT_ENDPOINT: ${{ vars.AZURE_AI_PROJECT_ENDPOINT }}
AZURE_AI_MODEL_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }}
# Azure OpenAI configuration
@@ -538,6 +473,8 @@ jobs:
- name: Create .env for samples
run: |
+ echo "FOUNDRY_PROJECT_ENDPOINT=$FOUNDRY_PROJECT_ENDPOINT" >> .env
+ echo "FOUNDRY_MODEL=$FOUNDRY_MODEL" >> .env
echo "AZURE_AI_PROJECT_ENDPOINT=$AZURE_AI_PROJECT_ENDPOINT" >> .env
echo "AZURE_AI_MODEL_DEPLOYMENT_NAME=$AZURE_AI_MODEL_DEPLOYMENT_NAME" >> .env
echo "AZURE_OPENAI_ENDPOINT=$AZURE_OPENAI_ENDPOINT" >> .env
@@ -759,14 +696,12 @@ jobs:
- validate-01-get-started
- validate-02-agents
- validate-02-agents-openai
- - validate-02-agents-azure-openai
- - validate-02-agents-azure-ai
- - validate-02-agents-azure-ai-agent
+ - validate-02-agents-azure
- validate-02-agents-anthropic
- validate-02-agents-github-copilot
- validate-02-agents-amazon
- validate-02-agents-ollama
- - validate-02-agents-foundry-local
+ - validate-02-agents-foundry
- validate-02-agents-copilotstudio
- validate-02-agents-custom
- validate-03-workflows
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3c0e6dcf13..4999145e81 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -74,6 +74,37 @@ Contributions must maintain API signature and behavioral compatibility. Contribu
that include breaking changes will be rejected. Please file an issue to discuss
your idea or change if you believe that a breaking change is warranted.
+#### Automated API Compatibility Validation
+
+The .NET projects use [Package Validation](https://learn.microsoft.com/dotnet/fundamentals/package-validation/overview)
+to automatically detect API breaking changes. This validation runs during `dotnet build`
+(Release configuration) and `dotnet pack`, comparing the current API surface against the
+latest published NuGet baseline version.
+
+**What gets validated:** By default, packable RC packages (`IsReleaseCandidate=true`) and
+GA packages (`IsGenerallyAvailable=true`) that have a published NuGet baseline and do not
+override validation settings are automatically validated. The shared baseline version and
+default validation settings are defined in `dotnet/nuget/nuget-package.props`, but
+individual projects may opt out (for example by setting `EnablePackageValidation=false`).
+
+**If the build fails with CP errors (e.g., CP0001, CP0002):**
+
+1. **Unintentional breaking change** — Refactor your code to maintain backward compatibility.
+2. **Intentional breaking change** (approved by maintainers) — Generate a suppression file:
+ ```bash
+ dotnet build .csproj -c Release /p:ApiCompatGenerateSuppressionFile=true
+ ```
+ This creates or updates a `CompatibilitySuppressions.xml` in the project directory.
+ Include this file in your PR with justification for the breaking change.
+
+**After each release:**
+
+1. Delete all `CompatibilitySuppressions.xml` files from validated projects.
+2. Update `PackageValidationBaselineVersion` in `dotnet/nuget/nuget-package.props` to the
+ newly published version.
+
+For more details, see the [Package Validation diagnostic IDs](https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids).
+
### Suggested Workflow
We use and recommend the following workflow:
diff --git a/docs/decisions/0020-foundry-agent-type-naming.md b/docs/decisions/0020-foundry-agent-type-naming.md
new file mode 100644
index 0000000000..03d43f64ac
--- /dev/null
+++ b/docs/decisions/0020-foundry-agent-type-naming.md
@@ -0,0 +1,125 @@
+---
+status: accepted
+contact: rogerbarreto
+date: 2026-03-06
+deciders: rogerbarreto, alliscode
+consulted: ""
+informed: ""
+---
+
+# 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`:
+
+```csharp
+AIProjectClient aiProjectClient = new(new Uri(endpoint), credential);
+
+ChatClientAgent agent = aiProjectClient.AsAIAgent(
+ model: deploymentName,
+ instructions: "You are good at telling jokes.",
+ name: "JokerAgent");
+```
+
+Or use composed `ChatClientAgent`
+
+```csharp
+ProjectResponsesClient projectResponsesClient = new(new Uri(endpoint), new DefaultAzureCredential(), new AgentReference($"model:{deploymentName}"));
+
+ChatClientAgent agent = new(
+ chatClient: projectResponsesClient.AsIChatClient(),
+ instructions: "You are good at telling jokes.",
+ name: "JokerAgent");
+```
+
+This path is code-first and does not create a persistent server-side agent.
+
+### Versioned agent path
+
+Use the convenience overloads on `AIProjectClient`:
+
+```csharp
+AIProjectClient aiProjectClient = new(new Uri(endpoint), credential);
+
+AgentVersion version = await aiProjectClient.Agents.CreateAgentVersionAsync(
+ "JokerAgent",
+ new AgentVersionCreationOptions(
+ new PromptAgentDefinition(deploymentName)
+ {
+ Instructions = "You are good at telling jokes."
+ }));
+
+ChatClientAgent agent = aiProjectClient.AsAIAgent(version);
+```
+
+Or use composed `ChatClientAgent`
+
+```csharp
+AIProjectClient aiProjectClient = new(new Uri(endpoint), credential);
+
+AgentVersion version = await aiProjectClient.Agents.CreateAgentVersionAsync(
+ "JokerAgent",
+ new AgentVersionCreationOptions(
+ new PromptAgentDefinition(deploymentName)
+ {
+ Instructions = "You are good at telling jokes."
+ }));
+
+ProjectResponsesClient projectResponsesClient = aiProjectClient
+ .GetProjectOpenAIClient()
+ .GetProjectResponsesClientForAgent(new AgentReference(version.Name, version.Version));
+
+ChatClientAgent agent = new(
+ chatClient: projectResponsesClient.AsIChatClient(),
+ name: "JokerAgent");
+```
+
+### Samples
+
+- `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.
diff --git a/docs/decisions/0022-chat-history-persistence-consistency.md b/docs/decisions/0022-chat-history-persistence-consistency.md
index 2c760e6614..2784934817 100644
--- a/docs/decisions/0022-chat-history-persistence-consistency.md
+++ b/docs/decisions/0022-chat-history-persistence-consistency.md
@@ -31,8 +31,6 @@ The persistence timing and `FunctionResultContent` trimming behaviors are interr
- **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.
-This means the trimming feature (introduced in [PR #4792](https://github.com/microsoft/agent-framework/pull/4792)) is primarily needed as a complement to per-run persistence. The `PersistChatHistoryAtEndOfRun` setting (introduced in [PR #4762](https://github.com/microsoft/agent-framework/pull/4762)) inverts the default so that per-service-call persistence is the standard behavior, and per-run persistence is opt-in.
-
## 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.
@@ -43,33 +41,30 @@ This means the trimming feature (introduced in [PR #4792](https://github.com/mic
## Considered Options
-- Option 1: Default to per-run persistence with `FunctionResultContent` trimming (opt-in to per-service-call)
-- Option 2: Default to per-service-call persistence (opt-in to per-run)
+- Option 1: Per-run persistence with opt-in FRC (FunctionResultContent) trimming
+- Option 2: Opt-in per-service-call persistence (via `SimulateServiceStoredChatHistory`)
## Pros and Cons of the Options
-### Option 1: Default to per-run persistence with `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 the default to improve consistency with service storage. Provide an opt-in setting for users who want per-service-call persistence.
-
-Settings:
-- `PersistChatHistoryAtEndOfRun` = `true`
+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.
-- Good, because users can opt in to per-service-call persistence for checkpointing/recovery scenarios, satisfying drivers C and E.
- 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 by default.
+- 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.
-### Option 2: Default to per-service-call persistence
+### Option 2: Opt-in per-service-call persistence (via `SimulateServiceStoredChatHistory`)
-Change the default 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). Provide an opt-in setting for users who want per-run atomicity with trimming.
+Introduce an optional SimulateServiceStoredChatHistory 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).
Settings:
-- `PersistChatHistoryAtEndOfRun` = `false` (default)
+- `SimulateServiceStoredChatHistory` = `true`
-- Good, because the stored history matches the service's behavior by default for both timing and content, fully satisfying driver A.
+- 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`.
@@ -78,39 +73,36 @@ Settings:
## Decision Outcome
-Chosen option: **Option 2 — Default to per-service-call persistence**, because it fully satisfies the consistency driver (A), naturally handles `FunctionResultContent` trimming without additional logic, and provides better recoverability for long-running tool-calling loops. Per-run persistence remains available via the `PersistChatHistoryAtEndOfRun` setting for users who prefer atomic run semantics.
+Chosen option: **Option 2: Opt-in per-service-call persistence (via `SimulateServiceStoredChatHistory`)**. 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 `SimulateServiceStoredChatHistory` 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 `PersistChatHistoryAtEndOfRun`:
+The behavior depends on the combination of `UseProvidedChatClientAsIs` and `SimulateServiceStoredChatHistory`:
-| `UseProvidedChatClientAsIs` | `PersistChatHistoryAtEndOfRun` | Behavior |
+| `UseProvidedChatClientAsIs` | `SimulateServiceStoredChatHistory` | Behavior |
|---|---|---|
-| `false` (default) | `false` (default) | **Per-service-call persistence.** A `ChatHistoryPersistingChatClient` middleware is automatically injected into the chat client pipeline between `FunctionInvokingChatClient` and the leaf `IChatClient`. Messages are persisted after each service call. |
-| `true` | `false` | **User responsibility.** No middleware is injected because the user has provided a custom chat client stack. The user is responsible for ensuring correct persistence behavior (e.g., by including their own persisting middleware). |
-| `false` | `true` | **Per-run persistence with marking.** A `ChatHistoryPersistingChatClient` middleware is injected, but configured to *mark* messages with metadata rather than store them immediately. At the end of the run, marked messages are stored. Trailing `FunctionResultContent` is trimmed. |
-| `true` | `true` | **Per-run persistence with warning.** The system checks whether the custom chat client stack includes a `ChatHistoryPersistingChatClient`. If not, a warning is emitted (particularly relevant for workflow handoff scenarios where trimming cannot be guaranteed). If no `ChatHistoryPersistingChatClient` is preset, all messages are stored at the end of the run, otherwise marked messages are stored. |
+| `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 `ServiceStoredSimulatingChatClient` 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 `ServiceStoredSimulatingChatClient`. 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 the stored history matches the service's behavior by default for both timing and content, fully satisfying consistency (driver A).
-- Good, because intermediate progress is preserved if the process is interrupted, satisfying recoverability (driver C).
-- Good, because no separate `FunctionResultContent` trimming logic is needed in the default path, reducing complexity.
-- Good, because marking persisted messages with metadata enables deduplication and aids debugging.
-- Good, because warnings for custom chat client configurations without the persisting middleware help prevent silent failures in workflow handoff scenarios.
-- Bad, because chat history may be left in an incomplete state if the run fails mid-loop (e.g., `FunctionCallContent` stored without corresponding `FunctionResultContent`), requiring manual recovery in rare cases.
-- Bad, because the mental model is more complex for the default path: a single run may produce multiple history updates.
-- Neutral, because users who prefer atomic run semantics can opt in to per-run persistence via `PersistChatHistoryAtEndOfRun = true`.
+- 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 `SimulateServiceStoredChatHistory` 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 `SimulateServiceStoredChatHistory = 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
-The `ChatHistoryPersistingChatClient` middleware must also update the session's `ConversationId` consistently for both response-based and conversation-based service interactions, ensuring the session always reflects the latest service-provided identifier.
+We should introduce a separate `ConversationIdPersistingChatClient`, middleware which allows us to
+persist response `ConversationIds` during the FICC loop. This could be used with or without
+`ServiceStoredSimulatingChatClient`.
-## More Information
-
-- [PR #4762: Persist messages during function call loop](https://github.com/microsoft/agent-framework/pull/4762) — introduces `PersistChatHistoryAfterEachServiceCall` option and `ChatHistoryPersistingChatClient` decorator
-- [PR #4792: Trim final FRC to match service storage](https://github.com/microsoft/agent-framework/pull/4792) — introduces `StoreFinalFunctionResultContent` option and `FilterFinalFunctionResultContent` logic
-- [Issue #2889](https://github.com/microsoft/agent-framework/issues/2889) — original issue tracking chat history persistence during function call loops
diff --git a/docs/decisions/0020-foundry-evals-integration.md b/docs/decisions/0023-foundry-evals-integration.md
similarity index 99%
rename from docs/decisions/0020-foundry-evals-integration.md
rename to docs/decisions/0023-foundry-evals-integration.md
index f5b5db4db5..ea9d2f3c69 100644
--- a/docs/decisions/0020-foundry-evals-integration.md
+++ b/docs/decisions/0023-foundry-evals-integration.md
@@ -462,7 +462,7 @@ class FoundryEvals:
### Azure AI: FoundryEvals Constants
```python
-from agent_framework_azure_ai import FoundryEvals
+from agent_framework.foundry import FoundryEvals
evaluators = [FoundryEvals.RELEVANCE, FoundryEvals.TOOL_CALL_ACCURACY]
```
diff --git a/dotnet/Directory.Build.props b/dotnet/Directory.Build.props
index 2482c43013..16b69a355f 100644
--- a/dotnet/Directory.Build.props
+++ b/dotnet/Directory.Build.props
@@ -17,6 +17,7 @@
false
+ false
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index 8975fb072e..978cb22ad0 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -1,4 +1,4 @@
-
+
@@ -115,6 +115,7 @@
+
@@ -131,6 +132,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -153,35 +182,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -328,8 +328,8 @@
-
+
diff --git a/dotnet/nuget/nuget-package.props b/dotnet/nuget/nuget-package.props
index 7b241e9d56..f2f4b59fc0 100644
--- a/dotnet/nuget/nuget-package.props
+++ b/dotnet/nuget/nuget-package.props
@@ -2,17 +2,19 @@
1.0.0
- 4
+ 5$(VersionPrefix)-rc$(RCNumber)
- $(VersionPrefix)-$(VersionSuffix).260311.1
- $(VersionPrefix)-preview.260311.1
- 1.0.0-rc4
+ $(VersionPrefix)-$(VersionSuffix).260330.1
+ $(VersionPrefix)-preview.260330.1
+ 1.0.0-rc5Debug;Release;Publishtrue
- 0.0.1
+ 1.0.0-rc4
+
+ true$(NoWarn);CP0003
diff --git a/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/Program.cs b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/Program.cs
index 5d770ff3fd..0c3e75cf96 100644
--- a/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/Program.cs
+++ b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/Program.cs
@@ -70,7 +70,7 @@ while ((input = Console.ReadLine()) != null && !input.Equals("exit", StringCompa
if (approvalRequest.AdditionalProperties != null)
{
- approvalResponse.AdditionalProperties = new AdditionalPropertiesDictionary();
+ approvalResponse.AdditionalProperties = [];
foreach (var kvp in approvalRequest.AdditionalProperties)
{
approvalResponse.AdditionalProperties[kvp.Key] = kvp.Value;
diff --git a/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs
index 866bbfad31..135d87bffd 100644
--- a/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs
+++ b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs
@@ -131,9 +131,9 @@ internal sealed class ServerFunctionApprovalClientAgent : DelegatingAIAgent
transformedContents ??= CopyContentsUpToIndex(message.Contents, contentIndex);
approvalCalls.Remove(functionResult.CallId);
}
- else if (transformedContents != null)
+ else
{
- transformedContents.Add(content);
+ transformedContents?.Add(content);
}
}
@@ -155,10 +155,10 @@ internal sealed class ServerFunctionApprovalClientAgent : DelegatingAIAgent
result ??= CopyMessagesUpToIndex(messages, messageIndex);
result.Add(newMessage);
}
- else if (result != null)
+ else
{
// We're already copying messages, so copy this unchanged message too
- result.Add(message);
+ result?.Add(message);
}
// If result is null, we haven't made any changes yet, so keep processing
}
diff --git a/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs
index ff3e6ffbb1..8c1f27eea9 100644
--- a/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs
+++ b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs
@@ -57,16 +57,10 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent
throw new InvalidOperationException("Invalid request_approval tool call");
}
- var request = toolCall.Arguments.TryGetValue("request", out var reqObj) &&
+ var request = (toolCall.Arguments.TryGetValue("request", out var reqObj) &&
reqObj is JsonElement argsElement &&
argsElement.Deserialize(jsonSerializerOptions.GetTypeInfo(typeof(ApprovalRequest))) is ApprovalRequest approvalRequest &&
- approvalRequest != null ? approvalRequest : null;
-
- if (request == null)
- {
- throw new InvalidOperationException("Failed to deserialize approval request from tool call");
- }
-
+ approvalRequest != null ? approvalRequest : null) ?? throw new InvalidOperationException("Failed to deserialize approval request from tool call");
return new ToolApprovalRequestContent(
requestId: request.ApprovalId,
new FunctionCallContent(
@@ -77,17 +71,11 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent
private static ToolApprovalResponseContent ConvertToolResultToApprovalResponse(FunctionResultContent result, ToolApprovalRequestContent approval, JsonSerializerOptions jsonSerializerOptions)
{
- var approvalResponse = result.Result is JsonElement je ?
+ var approvalResponse = (result.Result is JsonElement je ?
(ApprovalResponse?)je.Deserialize(jsonSerializerOptions.GetTypeInfo(typeof(ApprovalResponse))) :
result.Result is string str ?
(ApprovalResponse?)JsonSerializer.Deserialize(str, jsonSerializerOptions.GetTypeInfo(typeof(ApprovalResponse))) :
- result.Result as ApprovalResponse;
-
- if (approvalResponse == null)
- {
- throw new InvalidOperationException("Failed to deserialize approval response from tool result");
- }
-
+ result.Result as ApprovalResponse) ?? throw new InvalidOperationException("Failed to deserialize approval response from tool result");
return approval.CreateResponse(approvalResponse.Approved);
}
#pragma warning restore MEAI001
@@ -121,7 +109,7 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent
// Track approval ID to original call ID mapping
_ = new Dictionary();
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
- Dictionary trackedRequestApprovalToolCalls = new(); // Remote approvals
+ Dictionary trackedRequestApprovalToolCalls = []; // Remote approvals
for (int messageIndex = 0; messageIndex < messages.Count; messageIndex++)
{
var message = messages[messageIndex];
@@ -146,7 +134,7 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent
});
}
else if (content is FunctionResultContent toolResult &&
- trackedRequestApprovalToolCalls.TryGetValue(toolResult.CallId, out var approval) == true)
+ trackedRequestApprovalToolCalls.TryGetValue(toolResult.CallId, out var approval))
{
result ??= CopyMessagesUpToIndex(messages, messageIndex);
transformedContents ??= CopyContentsUpToIndex(message.Contents, j);
@@ -161,9 +149,9 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent
AdditionalProperties = message.AdditionalProperties
});
}
- else if (result != null)
+ else
{
- result.Add(message);
+ result?.Add(message);
}
}
}
diff --git a/dotnet/samples/02-agents/AGUI/Step05_StateManagement/Client/StatefulAgent.cs b/dotnet/samples/02-agents/AGUI/Step05_StateManagement/Client/StatefulAgent.cs
index 41c94d5686..8a8062befe 100644
--- a/dotnet/samples/02-agents/AGUI/Step05_StateManagement/Client/StatefulAgent.cs
+++ b/dotnet/samples/02-agents/AGUI/Step05_StateManagement/Client/StatefulAgent.cs
@@ -72,10 +72,9 @@ internal sealed class StatefulAgent : DelegatingAIAgent
if (content is DataContent dataContent && dataContent.MediaType == "application/json")
{
// Deserialize the state
- TState? newState = JsonSerializer.Deserialize(
+ if (JsonSerializer.Deserialize(
dataContent.Data.Span,
- this._jsonSerializerOptions.GetTypeInfo(typeof(TState))) as TState;
- if (newState != null)
+ this._jsonSerializerOptions.GetTypeInfo(typeof(TState))) is TState newState)
{
this.State = newState;
}
diff --git a/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Program.cs
index aab95d5b38..b2cd14f68a 100644
--- a/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Program.cs
+++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Program.cs
@@ -6,6 +6,7 @@ using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.AzureAI;
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-4o-mini";
@@ -30,14 +31,18 @@ var createdAgentVersion = aiProjectClient.Agents.CreateAgentVersion(agentName: J
// agentVersion.Name =
// You can use an AIAgent with an already created server side agent version.
-AIAgent existingJokerAgent = aiProjectClient.AsAIAgent(createdAgentVersion);
+FoundryAgent existingJokerAgent = aiProjectClient.AsAIAgent(createdAgentVersion);
// You can also create another AIAgent version by providing the same name with a different definition.
-AIAgent newJokerAgent = await aiProjectClient.CreateAIAgentAsync(name: JokerName, model: deploymentName, instructions: "You are extremely hilarious at telling jokes.");
+AgentVersion newJokerAgentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
+ JokerName,
+ new AgentVersionCreationOptions(new PromptAgentDefinition(model: deploymentName) { Instructions = "You are extremely hilarious at telling jokes." }));
+FoundryAgent newJokerAgent = aiProjectClient.AsAIAgent(newJokerAgentVersion);
// You can also get the AIAgent latest version just providing its name.
-AIAgent jokerAgentLatest = await aiProjectClient.GetAIAgentAsync(name: JokerName);
-var latestAgentVersion = jokerAgentLatest.GetService()!;
+AgentRecord jokerAgentRecord = await aiProjectClient.Agents.GetAgentAsync(JokerName);
+FoundryAgent jokerAgentLatest = aiProjectClient.AsAIAgent(jokerAgentRecord);
+AgentVersion latestAgentVersion = jokerAgentRecord.GetLatestVersion();
// The AIAgent version can be accessed via the GetService method.
Console.WriteLine($"Latest agent version id: {latestAgentVersion.Id}");
diff --git a/dotnet/samples/02-agents/AgentSkills/Agent_Step02_CodeDefinedSkills/Agent_Step02_CodeDefinedSkills.csproj b/dotnet/samples/02-agents/AgentSkills/Agent_Step02_CodeDefinedSkills/Agent_Step02_CodeDefinedSkills.csproj
new file mode 100644
index 0000000000..fd3d71fe7e
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentSkills/Agent_Step02_CodeDefinedSkills/Agent_Step02_CodeDefinedSkills.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net10.0
+
+ enable
+ enable
+ $(NoWarn);MAAI001
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/AgentSkills/Agent_Step02_CodeDefinedSkills/Program.cs b/dotnet/samples/02-agents/AgentSkills/Agent_Step02_CodeDefinedSkills/Program.cs
new file mode 100644
index 0000000000..46c58985fb
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentSkills/Agent_Step02_CodeDefinedSkills/Program.cs
@@ -0,0 +1,90 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample demonstrates how to define Agent Skills entirely in code using AgentInlineSkill.
+// No SKILL.md files are needed — skills, resources, and scripts are all defined programmatically.
+//
+// Three approaches are shown using a unit-converter skill:
+// 1. Static resources — inline content provided via AddResource
+// 2. Dynamic resources — computed at runtime via a factory delegate
+// 3. Code scripts — executable delegates the agent can invoke directly
+
+using System.Text.Json;
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using OpenAI.Responses;
+
+// --- Configuration ---
+string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+// --- Build the code-defined skill ---
+var unitConverterSkill = new AgentInlineSkill(
+ name: "unit-converter",
+ description: "Convert between common units using a multiplication factor. Use when asked to convert miles, kilometers, pounds, or kilograms.",
+ instructions: """
+ Use this skill when the user asks to convert between units.
+
+ 1. Review the conversion-table resource to find the factor for the requested conversion.
+ 2. Check the conversion-policy resource for rounding and formatting rules.
+ 3. Use the convert script, passing the value and factor from the table.
+ """)
+ // 1. Static Resource: conversion tables
+ .AddResource(
+ "conversion-table",
+ """
+ # Conversion Tables
+
+ Formula: **result = value × factor**
+
+ | From | To | Factor |
+ |-------------|-------------|----------|
+ | miles | kilometers | 1.60934 |
+ | kilometers | miles | 0.621371 |
+ | pounds | kilograms | 0.453592 |
+ | kilograms | pounds | 2.20462 |
+ """)
+ // 2. Dynamic Resource: conversion policy (computed at runtime)
+ .AddResource("conversion-policy", () =>
+ {
+ const int Precision = 4;
+ return $"""
+ # Conversion Policy
+
+ **Decimal places:** {Precision}
+ **Format:** Always show both the original and converted values with units
+ **Generated at:** {DateTime.UtcNow:O}
+ """;
+ })
+ // 3. Code Script: convert
+ .AddScript("convert", (double value, double factor) =>
+ {
+ double result = Math.Round(value * factor, 4);
+ return JsonSerializer.Serialize(new { value, factor, result });
+ });
+
+// --- Skills Provider ---
+var skillsProvider = new AgentSkillsProvider(unitConverterSkill);
+
+// --- Agent Setup ---
+AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
+ .GetResponsesClient()
+ .AsAIAgent(new ChatClientAgentOptions
+ {
+ Name = "UnitConverterAgent",
+ ChatOptions = new()
+ {
+ Instructions = "You are a helpful assistant that can convert units.",
+ },
+ AIContextProviders = [skillsProvider],
+ },
+ model: deploymentName);
+
+// --- Example: Unit conversion ---
+Console.WriteLine("Converting units with code-defined skills");
+Console.WriteLine(new string('-', 60));
+
+AgentResponse response = await agent.RunAsync(
+ "How many kilometers is a marathon (26.2 miles)? And how many pounds is 75 kilograms?");
+
+Console.WriteLine($"Agent: {response.Text}");
diff --git a/dotnet/samples/02-agents/AgentSkills/Agent_Step02_CodeDefinedSkills/README.md b/dotnet/samples/02-agents/AgentSkills/Agent_Step02_CodeDefinedSkills/README.md
new file mode 100644
index 0000000000..5b7c8747dd
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentSkills/Agent_Step02_CodeDefinedSkills/README.md
@@ -0,0 +1,52 @@
+# Code-Defined Agent Skills Sample
+
+This sample demonstrates how to define **Agent Skills entirely in code** using `AgentInlineSkill`.
+
+## What it demonstrates
+
+- Creating skills programmatically with `AgentInlineSkill` — no SKILL.md files needed
+- **Static resources** via `AddResource` with inline content
+- **Dynamic resources** via `AddResource` with a factory delegate (computed at runtime)
+- **Code scripts** via `AddScript` with a delegate handler
+- Using the `AgentSkillsProvider` constructor with inline skills
+
+## Skills Included
+
+### unit-converter (code-defined)
+
+Converts between common units using multiplication factors. Defined entirely in C# code:
+
+- `conversion-table` — Static resource with factor table
+- `conversion-policy` — Dynamic resource with formatting rules (generated at runtime)
+- `convert` — Script that performs `value × factor` conversion
+
+## Running the Sample
+
+### Prerequisites
+
+- .NET 10.0 SDK
+- Azure OpenAI endpoint with a deployed model
+
+### Setup
+
+```bash
+export AZURE_OPENAI_ENDPOINT="https://your-endpoint.openai.azure.com/"
+export AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+### Run
+
+```bash
+dotnet run
+```
+
+### Expected Output
+
+```
+Converting units with code-defined skills
+------------------------------------------------------------
+Agent: Here are your conversions:
+
+1. **26.2 miles → 42.16 km** (a marathon distance)
+2. **75 kg → 165.35 lbs**
+```
diff --git a/dotnet/samples/02-agents/AgentSkills/README.md b/dotnet/samples/02-agents/AgentSkills/README.md
index 75b850f077..6011384997 100644
--- a/dotnet/samples/02-agents/AgentSkills/README.md
+++ b/dotnet/samples/02-agents/AgentSkills/README.md
@@ -1,7 +1,24 @@
# AgentSkills Samples
-Samples demonstrating Agent Skills capabilities.
+Samples demonstrating Agent Skills capabilities. Each sample shows a different way to define and use skills.
| Sample | Description |
|--------|-------------|
| [Agent_Step01_FileBasedSkills](Agent_Step01_FileBasedSkills/) | Define skills as `SKILL.md` files on disk with reference documents. Uses a unit-converter skill. |
+| [Agent_Step02_CodeDefinedSkills](Agent_Step02_CodeDefinedSkills/) | Define skills entirely in C# code using `AgentInlineSkill`, with static/dynamic resources and scripts. |
+
+## Key Concepts
+
+### File-Based vs Code-Defined Skills
+
+| Aspect | File-Based | Code-Defined |
+|--------|-----------|--------------|
+| Definition | `SKILL.md` files on disk | `AgentInlineSkill` instances in C# |
+| Resources | All files in skill directory (filtered by extension) | `AddResource` (static value or delegate-backed) |
+| Scripts | Supported via script executor delegate | `AddScript` delegates |
+| Discovery | Automatic from directory path | Explicit via constructor |
+| Dynamic content | No (static files only) | Yes (factory delegates) |
+| Reusability | Copy skill directory | Inline or shared instances |
+
+For single-source scenarios, use the `AgentSkillsProvider` constructors directly. To combine multiple skill types, use the `AgentSkillsProviderBuilder`.
+
diff --git a/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Program.cs b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Program.cs
index 3d9c715588..04df345cd6 100644
--- a/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Program.cs
+++ b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Program.cs
@@ -5,20 +5,13 @@
using Anthropic;
using Anthropic.Core;
using Microsoft.Agents.AI;
-using Microsoft.Extensions.AI;
var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY") ?? throw new InvalidOperationException("ANTHROPIC_API_KEY is not set.");
var model = Environment.GetEnvironmentVariable("ANTHROPIC_CHAT_MODEL_NAME") ?? "claude-haiku-4-5";
-AIAgent agent = new AnthropicClient(new ClientOptions { ApiKey = apiKey })
+AIAgent agent =
+ new AnthropicClient(new ClientOptions { ApiKey = apiKey })
.AsAIAgent(model: model, instructions: "You are good at telling jokes.", name: "Joker");
// Invoke the agent and output the text result.
-var response = await agent.RunAsync("Tell me a joke about a pirate.");
-Console.WriteLine(response);
-
-// Invoke the agent with streaming support.
-await foreach (var update in agent.RunStreamingAsync("Tell me a joke about a pirate."))
-{
- Console.WriteLine(update);
-}
+Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
diff --git a/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/Program.cs b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/Program.cs
index 914eda330a..d6410d3308 100644
--- a/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/Program.cs
+++ b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/Program.cs
@@ -11,6 +11,7 @@ using System.Text.Json;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.AzureAI;
using Microsoft.Agents.AI.FoundryMemory;
string foundryEndpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
@@ -19,6 +20,9 @@ string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLO
string embeddingModelName = Environment.GetEnvironmentVariable("AZURE_AI_EMBEDDING_DEPLOYMENT_NAME") ?? "text-embedding-ada-002";
// Create an AIProjectClient for Foundry with Azure Identity authentication.
+// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
+// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
+// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
DefaultAzureCredential credential = new();
AIProjectClient projectClient = new(new Uri(foundryEndpoint), credential);
@@ -33,11 +37,15 @@ FoundryMemoryProvider memoryProvider = new(
memoryStoreName,
stateInitializer: _ => new(new FoundryMemoryProviderScope("sample-user-123")));
-AIAgent agent = await projectClient.CreateAIAgentAsync(deploymentName,
- options: new ChatClientAgentOptions()
+FoundryAgent agent = projectClient.AsAIAgent(
+ new ChatClientAgentOptions()
{
Name = "TravelAssistantWithFoundryMemory",
- ChatOptions = new() { Instructions = "You are a friendly travel assistant. Use known memories about the user when responding, and do not invent details." },
+ ChatOptions = new()
+ {
+ ModelId = deploymentName,
+ Instructions = "You are a friendly travel assistant. Use known memories about the user when responding, and do not invent details."
+ },
AIContextProviders = [memoryProvider]
});
diff --git a/dotnet/samples/02-agents/AgentWithMemory/README.md b/dotnet/samples/02-agents/AgentWithMemory/README.md
index 87818c77d6..aa95012f68 100644
--- a/dotnet/samples/02-agents/AgentWithMemory/README.md
+++ b/dotnet/samples/02-agents/AgentWithMemory/README.md
@@ -1,4 +1,4 @@
-# Agent Framework Retrieval Augmented Generation (RAG)
+# Agent Framework Retrieval Augmented Generation (RAG)
These samples show how to create an agent with the Agent Framework that uses Memory to remember previous conversations or facts from previous conversations.
@@ -10,4 +10,4 @@ These samples show how to create an agent with the Agent Framework that uses Mem
|[Memory with Azure AI Foundry](./AgentWithMemory_Step04_MemoryUsingFoundry/)|This sample demonstrates how to create and run an agent that uses Azure AI Foundry's managed memory service to extract and retrieve individual memories.|
|[Bounded Chat History with Overflow](./AgentWithMemory_Step05_BoundedChatHistory/)|This sample demonstrates how to create a bounded chat history provider that overflows older messages to a vector store and recalls them as memories.|
-> **See also**: [Memory Search with Foundry Agents](../FoundryAgents/FoundryAgents_Step22_MemorySearch/) - demonstrates using the built-in Memory Search tool with Azure Foundry Agents.
+> **See also**: [Memory Search with Foundry Agents](../AgentsWithFoundry/Agent_Step22_MemorySearch/) - demonstrates using the built-in Memory Search tool with Azure Foundry agents.
diff --git a/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Program.cs b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Program.cs
index e2bd31055a..e82f420105 100644
--- a/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Program.cs
+++ b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Program.cs
@@ -4,28 +4,14 @@
using System.ClientModel;
using Microsoft.Agents.AI;
-using OpenAI;
-using OpenAI.Chat;
+using OpenAI.Responses;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
var model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt-4o-mini";
-AIAgent agent = new OpenAIClient(apiKey)
- .GetChatClient(model)
- .AsAIAgent(instructions: "You are good at telling jokes.", name: "Joker");
+AIAgent agent =
+ new ResponsesClient(new ApiKeyCredential(apiKey))
+ .AsAIAgent(model: model, instructions: "You are good at telling jokes.", name: "Joker");
-UserChatMessage chatMessage = new("Tell me a joke about a pirate.");
-
-// Invoke the agent and output the text result.
-ChatCompletion chatCompletion = await agent.RunAsync([chatMessage]);
-Console.WriteLine(chatCompletion.Content.Last().Text);
-
-// Invoke the agent with streaming support.
-AsyncCollectionResult completionUpdates = agent.RunStreamingAsync([chatMessage]);
-await foreach (StreamingChatCompletionUpdate completionUpdate in completionUpdates)
-{
- if (completionUpdate.ContentUpdate.Count > 0)
- {
- Console.WriteLine(completionUpdate.ContentUpdate[0].Text);
- }
-}
+// Once you have the agent, you can invoke it like any other AIAgent.
+Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
diff --git a/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs
index 0f65121c04..2d6e30ae14 100644
--- a/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs
+++ b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs
@@ -16,7 +16,7 @@ using Qdrant.Client;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
var embeddingDeploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME") ?? "text-embedding-3-large";
-var afOverviewUrl = "https://github.com/MicrosoftDocs/semantic-kernel-docs/blob/main/agent-framework/overview/agent-framework-overview.md";
+var afOverviewUrl = "https://raw.githubusercontent.com/MicrosoftDocs/semantic-kernel-docs/refs/heads/main/agent-framework/overview/index.md";
var afMigrationUrl = "https://raw.githubusercontent.com/MicrosoftDocs/semantic-kernel-docs/refs/heads/main/agent-framework/migration-guide/from-semantic-kernel/index.md";
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
diff --git a/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs
index c356bccbd9..e049618a72 100644
--- a/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs
+++ b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs
@@ -4,11 +4,13 @@
using System.ClientModel;
using Azure.AI.Projects;
+using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
-using Microsoft.Extensions.AI;
+using Microsoft.Agents.AI.AzureAI;
using OpenAI;
using OpenAI.Files;
+using OpenAI.Responses;
using OpenAI.VectorStores;
var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
@@ -37,14 +39,20 @@ ClientResult vectorStoreCreate = await vectorStoreClient.CreateVect
FileIds = { uploadResult.Value.Id }
});
-var fileSearchTool = new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreCreate.Value.Id)] };
+// Use the native OpenAI SDK FileSearchTool directly with the vector store ID.
+#pragma warning disable OPENAI001
+FileSearchTool fileSearchTool = new([vectorStoreCreate.Value.Id]);
+#pragma warning restore OPENAI001
-AIAgent agent = await aiProjectClient
- .CreateAIAgentAsync(
- model: deploymentName,
- name: "AskContoso",
- instructions: "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.",
- tools: [fileSearchTool]);
+AgentVersion agentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
+ "AskContoso",
+ new AgentVersionCreationOptions(
+ new PromptAgentDefinition(model: deploymentName)
+ {
+ Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.",
+ Tools = { fileSearchTool }
+ }));
+FoundryAgent agent = aiProjectClient.AsAIAgent(agentVersion);
AgentSession session = await agent.CreateSessionAsync();
diff --git a/dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/Program.cs
index 7bc6478968..a63063b6a5 100644
--- a/dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/Program.cs
+++ b/dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/Program.cs
@@ -3,6 +3,7 @@
// This sample shows how to expose an AI agent as an MCP tool.
using Azure.AI.Projects;
+using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.DependencyInjection;
@@ -18,11 +19,17 @@ var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYME
var aiProjectClient = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential());
// Create a server side agent and expose it as an AIAgent.
-AIAgent agent = await aiProjectClient.CreateAIAgentAsync(
- model: deploymentName,
- instructions: "You are good at telling jokes, and you always start each joke with 'Aye aye, captain!'.",
- name: "Joker",
- description: "An agent that tells jokes.");
+AgentVersion agentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
+ "Joker",
+ new AgentVersionCreationOptions(
+ new PromptAgentDefinition(model: deploymentName)
+ {
+ Instructions = "You are good at telling jokes, and you always start each joke with 'Aye aye, captain!'.",
+ })
+ {
+ Description = "An agent that tells jokes.",
+ });
+AIAgent agent = aiProjectClient.AsAIAgent(agentVersion);
// Convert the agent to an AIFunction and then to an MCP tool.
// The agent name and description will be used as the mcp tool name and description.
diff --git a/dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/Program.cs
index 18969ed66e..bab09bc886 100644
--- a/dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/Program.cs
+++ b/dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/Program.cs
@@ -189,9 +189,9 @@ async Task PIIMiddleware(IEnumerable messages, Agent
// Regex patterns for PII detection (simplified for demonstration)
Regex[] piiPatterns =
[
- new(@"\b\d{3}-\d{3}-\d{4}\b", RegexOptions.Compiled), // Phone number (e.g., 123-456-7890)
- new(@"\b[\w\.-]+@[\w\.-]+\.\w+\b", RegexOptions.Compiled), // Email address
- new(@"\b[A-Z][a-z]+\s[A-Z][a-z]+\b", RegexOptions.Compiled) // Full name (e.g., John Doe)
+ MyRegex(), // Phone number (e.g., 123-456-7890)
+ EmailRegex(), // Email address
+ FullNameRegex() // Full name (e.g., John Doe)
];
foreach (var pattern in piiPatterns)
@@ -309,3 +309,15 @@ internal sealed class DateTimeContextProvider : MessageAIContextProvider
]);
}
}
+
+internal partial class Program
+{
+ [GeneratedRegex(@"\b\d{3}-\d{3}-\d{4}\b", RegexOptions.Compiled)]
+ private static partial Regex MyRegex();
+
+ [GeneratedRegex(@"\b[\w\.-]+@[\w\.-]+\.\w+\b", RegexOptions.Compiled)]
+ private static partial Regex EmailRegex();
+
+ [GeneratedRegex(@"\b[A-Z][a-z]+\s[A-Z][a-z]+\b", RegexOptions.Compiled)]
+ private static partial Regex FullNameRegex();
+}
diff --git a/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/Program.cs
index 7a76f73455..11d3f561f4 100644
--- a/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/Program.cs
+++ b/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/Program.cs
@@ -17,10 +17,10 @@ var bingConnectionId = Environment.GetEnvironmentVariable("AZURE_AI_BING_CONNECT
PersistentAgentsAdministrationClientOptions persistentAgentsClientOptions = new();
persistentAgentsClientOptions.Retry.NetworkTimeout = TimeSpan.FromMinutes(20);
-// Get a client to create/retrieve server side agents with.
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
+// Get a client to create/retrieve server side agents with.
PersistentAgentsClient persistentAgentsClient = new(endpoint, new DefaultAzureCredential(), persistentAgentsClientOptions);
// Define and configure the Deep Research tool.
diff --git a/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/README.md b/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/README.md
index dc24ba4554..1848b10826 100644
--- a/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/README.md
+++ b/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/README.md
@@ -23,12 +23,14 @@ Before running this sample, ensure you have:
Pay special attention to the purple `Note` boxes in the Azure documentation.
-**Note**: The Bing Connection ID must be from the **project**, not the resource. It has the following format:
+**Note**: The Bing Grounding Connection ID must be the **full ARM resource URI** from the project, not just the connection name. It has the following format:
```
-/subscriptions//resourceGroups//providers//accounts//projects//connections/
+/subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts//projects//connections/
```
+You can find this in the Azure AI Foundry portal under **Management > Connected resources**, or retrieve it programmatically via the connections API (`.id` property).
+
## Environment Variables
Set the following environment variables:
@@ -37,8 +39,8 @@ Set the following environment variables:
# Replace with your Azure AI Foundry project endpoint
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/"
-# Replace with your Bing connection ID from the project
-$env:AZURE_AI_BING_CONNECTION_ID="/subscriptions/.../connections/your-bing-connection"
+# Replace with your Bing Grounding connection ID (full ARM resource URI)
+$env:AZURE_AI_BING_CONNECTION_ID="/subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts//projects//connections/"
# Optional, defaults to o3-deep-research
$env:AZURE_AI_REASONING_DEPLOYMENT_NAME="o3-deep-research"
diff --git a/dotnet/samples/02-agents/Agents/Agent_Step17_AdditionalAIContext/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step17_AdditionalAIContext/Program.cs
index e3913c9f0e..0fd21833e1 100644
--- a/dotnet/samples/02-agents/Agents/Agent_Step17_AdditionalAIContext/Program.cs
+++ b/dotnet/samples/02-agents/Agents/Agent_Step17_AdditionalAIContext/Program.cs
@@ -24,12 +24,12 @@ var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT
Func> loadNextThreeCalendarEvents = async () =>
{
// In a real implementation, this method would connect to a calendar service
- return new string[]
- {
+ return
+ [
"Doctor's appointment today at 15:00",
"Team meeting today at 17:00",
"Birthday party today at 20:00"
- };
+ ];
};
// Create an agent with an AI context provider attached that aggregates two other providers:
@@ -87,7 +87,7 @@ namespace SampleApp
internal sealed class TodoListAIContextProvider : AIContextProvider
{
private static List GetTodoItems(AgentSession? session)
- => session?.StateBag.GetValue>(nameof(TodoListAIContextProvider)) ?? new List();
+ => session?.StateBag.GetValue>(nameof(TodoListAIContextProvider)) ?? [];
private static void SetTodoItems(AgentSession? session, List items)
=> session?.StateBag.SetValue(nameof(TodoListAIContextProvider), items);
diff --git a/dotnet/samples/02-agents/Agents/Agent_Step19_InFunctionLoopCheckpointing/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step19_InFunctionLoopCheckpointing/Program.cs
index 07382e6417..0c74ad86b1 100644
--- a/dotnet/samples/02-agents/Agents/Agent_Step19_InFunctionLoopCheckpointing/Program.cs
+++ b/dotnet/samples/02-agents/Agents/Agent_Step19_InFunctionLoopCheckpointing/Program.cs
@@ -1,15 +1,16 @@
// Copyright (c) Microsoft. All rights reserved.
// This sample demonstrates how the ChatClientAgent persists chat history after each individual
-// call to the AI service.
+// call to the AI service, using the SimulateServiceStoredChatHistory option.
// When an agent uses tools, FunctionInvokingChatClient may loop multiple times
// (service call → tool execution → service call), and intermediate messages (tool calls and
// results) are persisted after each service call. This allows you to inspect or recover them
// even if the process is interrupted mid-loop, but may also result in chat history that is not
// yet finalized (e.g., tool calls without results) being persisted, which may be undesirable in some cases.
//
-// To opt into end-of-run persistence instead (atomic run semantics), set
-// PersistChatHistoryAtEndOfRun = true on ChatClientAgentOptions.
+// To use end-of-run persistence instead (atomic run semantics), remove the
+// SimulateServiceStoredChatHistory = true setting (or set it to false). End-of-run
+// persistence is the default behavior.
//
// The sample runs two multi-turn conversations: one using non-streaming (RunAsync) and one
// using streaming (RunStreamingAsync), to demonstrate correct behavior in both modes.
@@ -53,7 +54,7 @@ static string GetTime([Description("The city name.")] string city) =>
_ => $"{city}: time data not available."
};
-// Create the agent — per-service-call persistence is the default behavior.
+// Create the agent — per-service-call persistence is enabled via SimulateServiceStoredChatHistory.
// The in-memory ChatHistoryProvider is used by default when the service does not require service stored chat
// history, so for those cases, we can inspect the chat history via session.TryGetInMemoryChatHistory().
IChatClient chatClient = string.Equals(store, "TRUE", StringComparison.OrdinalIgnoreCase) ?
@@ -63,6 +64,7 @@ AIAgent agent = chatClient.AsAIAgent(
new ChatClientAgentOptions
{
Name = "WeatherAssistant",
+ SimulateServiceStoredChatHistory = true,
ChatOptions = new()
{
Instructions = "You are a helpful assistant. When asked about multiple cities, call the appropriate tool for each city.",
diff --git a/dotnet/samples/02-agents/Agents/Agent_Step19_InFunctionLoopCheckpointing/README.md b/dotnet/samples/02-agents/Agents/Agent_Step19_InFunctionLoopCheckpointing/README.md
index d6157586f0..461f76aaf4 100644
--- a/dotnet/samples/02-agents/Agents/Agent_Step19_InFunctionLoopCheckpointing/README.md
+++ b/dotnet/samples/02-agents/Agents/Agent_Step19_InFunctionLoopCheckpointing/README.md
@@ -1,16 +1,19 @@
# In-Function-Loop Checkpointing
-This sample demonstrates how `ChatClientAgent` persists chat history after each individual call to the AI service by default. This per-service-call persistence ensures intermediate progress is saved during the function invocation loop.
+This sample demonstrates how `ChatClientAgent` can persist chat history after each individual call to the AI service using the `SimulateServiceStoredChatHistory` option. This per-service-call persistence ensures intermediate progress is saved during the function invocation loop.
## What This Sample Shows
-When an agent uses tools, the `FunctionInvokingChatClient` loops multiple times (service call → tool execution → service call → …). By default, chat history is persisted after each service call via the `ChatHistoryPersistingChatClient` decorator:
+When an agent uses tools, the `FunctionInvokingChatClient` loops multiple times (service call → tool execution → service call → …). By enabling `SimulateServiceStoredChatHistory = true`, chat history is persisted after each service call via the `ServiceStoredSimulatingChatClient` decorator:
-- A `ChatHistoryPersistingChatClient` decorator is automatically inserted into the chat client pipeline
+- A `ServiceStoredSimulatingChatClient` decorator is inserted into the chat client pipeline
+- Before each service call, the decorator loads history from the `ChatHistoryProvider` and prepends it to the request
- After each service call, the decorator notifies the `ChatHistoryProvider` (and any `AIContextProvider` instances) with the new messages
- Only **new** messages are sent to providers on each notification — messages that were already persisted in an earlier call within the same run are deduplicated automatically
-To opt into end-of-run persistence instead (atomic run semantics), set `PersistChatHistoryAtEndOfRun = true` on `ChatClientAgentOptions`. In that mode, the decorator marks messages with metadata rather than persisting them immediately, and `ChatClientAgent` persists only the marked messages at the end of the run.
+By default (without `SimulateServiceStoredChatHistory`), chat history is persisted at the end of the full agent run instead. To use per-service-call persistence, set `SimulateServiceStoredChatHistory = true` on `ChatClientAgentOptions`.
+
+With `SimulateServiceStoredChatHistory` = true, the behavior matches that of chat history stored in the underlying AI service exactly.
Per-service-call persistence is useful for:
- **Crash recovery** — if the process is interrupted mid-loop, the intermediate tool calls and results are already persisted
@@ -26,7 +29,7 @@ The sample asks the agent about the weather and time in three cities. The model
```
ChatClientAgent
└─ FunctionInvokingChatClient (handles tool call loop)
- └─ ChatHistoryPersistingChatClient (persists after each service call)
+ └─ ServiceStoredSimulatingChatClient (persists after each service call)
└─ Leaf IChatClient (Azure OpenAI)
```
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/FoundryAgents_Step02_MultiturnConversation.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/Agent_Step00_FoundryAgentLifecycle.csproj
similarity index 92%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/FoundryAgents_Step02_MultiturnConversation.csproj
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/Agent_Step00_FoundryAgentLifecycle.csproj
index daf7e24494..d861331d9f 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/FoundryAgents_Step02_MultiturnConversation.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/Agent_Step00_FoundryAgentLifecycle.csproj
@@ -1,4 +1,4 @@
-
+Exe
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/Program.cs
new file mode 100644
index 0000000000..c6b2d5c764
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/Program.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to create, use, and clean up a FoundryAgent backed by a server-side
+// versioned agent in Azure AI Foundry. It demonstrates the full lifecycle:
+// create agent version -> wrap as FoundryAgent -> run -> delete.
+
+using Azure.AI.Projects;
+using Azure.AI.Projects.Agents;
+using Azure.Identity;
+using Microsoft.Agents.AI.AzureAI;
+
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+const string JokerName = "JokerAgent";
+
+// Create the AIProjectClient to manage server-side agents.
+AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential());
+
+// Create a server-side agent version using the native SDK.
+AgentVersion agentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
+ JokerName,
+ new AgentVersionCreationOptions(
+ new PromptAgentDefinition(model: deploymentName)
+ {
+ Instructions = "You are good at telling jokes.",
+ }));
+
+// Wrap the agent version as a FoundryAgent using the AsAIAgent extension.
+FoundryAgent agent = aiProjectClient.AsAIAgent(agentVersion);
+
+// Once you have the agent, you can invoke it like any other AIAgent.
+Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
+
+// Cleanup: deletes the agent and all its versions.
+await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/README.md
new file mode 100644
index 0000000000..738a1d2e42
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/README.md
@@ -0,0 +1,23 @@
+# Agent Step 00 - FoundryAgent Lifecycle
+
+This sample demonstrates the full lifecycle of a `FoundryAgent` backed by a server-side versioned agent in Microsoft Foundry: create → run → delete.
+
+## Prerequisites
+
+- A Microsoft Foundry project endpoint
+- A model deployment name (defaults to `gpt-4o-mini`)
+- Azure CLI installed and authenticated
+
+## Environment Variables
+
+| Variable | Description | Required |
+| --- | --- | --- |
+| `AZURE_AI_PROJECT_ENDPOINT` | Microsoft Foundry project endpoint | Yes |
+| `AZURE_AI_MODEL_DEPLOYMENT_NAME` | Model deployment name | No (defaults to `gpt-4o-mini`) |
+
+## Running the sample
+
+```powershell
+cd dotnet/samples/02-agents/AgentsWithFoundry
+dotnet run --project .\Agent_Step00_FoundryAgentLifecycle
+```
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step01_Basics/Agent_Step01_Basics.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step01_Basics/Agent_Step01_Basics.csproj
new file mode 100644
index 0000000000..7367c1d2f8
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step01_Basics/Agent_Step01_Basics.csproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ net10.0
+
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step01_Basics/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step01_Basics/Program.cs
new file mode 100644
index 0000000000..cd89116db7
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step01_Basics/Program.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to create and run a basic agent with AIProjectClient.AsAIAgent(...).
+
+using Azure.AI.Projects;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
+// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
+// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
+AIAgent agent =
+ new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential())
+ .AsAIAgent(model: deploymentName, instructions: "You are good at telling jokes.", name: "JokerAgent");
+
+// Once you have the agent, you can invoke it like any other AIAgent.
+Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step01_Basics/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step01_Basics/README.md
new file mode 100644
index 0000000000..88eebb2a82
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step01_Basics/README.md
@@ -0,0 +1,55 @@
+# Creating and Running a Basic Agent with the Responses API
+
+This sample demonstrates how to create and run a basic AI agent using the `ChatClientAgent`, which uses the Microsoft Foundry Responses API directly without creating server-side agent definitions.
+
+## What this sample demonstrates
+
+- Creating a `ChatClientAgent` with instructions and a model
+- Running a simple single-turn conversation
+- No server-side agent creation or cleanup required
+
+## Prerequisites
+
+Before you begin, ensure you have the following prerequisites:
+
+- .NET 10 SDK or later
+- 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 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"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+Navigate to the AgentsWithFoundry sample directory and run:
+
+```powershell
+cd dotnet/samples/02-agents/AgentsWithFoundry
+dotnet run --project .\Agent_Step01_Basics
+```
+
+## Alternative: Composable approach
+
+You can also create the same agent by composing the underlying `IChatClient` directly. This gives you full control over the chat client pipeline:
+
+```csharp
+using Azure.AI.Projects;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+
+AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
+
+AIAgent agent = new ChatClientAgent(
+ chatClient: aiProjectClient.GetProjectOpenAIClient().GetProjectResponsesClient().AsIChatClient(deploymentName),
+ instructions: "You are good at telling jokes.",
+ name: "JokerAgent");
+```
+
+This approach is useful when you need to customize the chat client pipeline or swap providers (e.g., Anthropic, OpenAI) while keeping the same agent code.
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/FoundryAgents_Evaluations_Step01_RedTeaming.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.1_MultiturnConversation/Agent_Step02.1_MultiturnConversation.csproj
similarity index 70%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/FoundryAgents_Evaluations_Step01_RedTeaming.csproj
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.1_MultiturnConversation/Agent_Step02.1_MultiturnConversation.csproj
index d77c0bb0d3..5e73fd236a 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/FoundryAgents_Evaluations_Step01_RedTeaming.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.1_MultiturnConversation/Agent_Step02.1_MultiturnConversation.csproj
@@ -9,8 +9,7 @@
-
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.1_MultiturnConversation/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.1_MultiturnConversation/Program.cs
new file mode 100644
index 0000000000..15b880102f
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.1_MultiturnConversation/Program.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to create a multi-turn conversation agent using sessions.
+// Context is preserved across multiple runs via response ID chaining in the session.
+
+using Azure.AI.Projects;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
+// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
+// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
+AIAgent agent = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential())
+ .AsAIAgent(deploymentName, instructions: "You are good at telling jokes.", name: "JokerAgent");
+
+// Create a session to maintain context across multiple runs.
+AgentSession session = await agent.CreateSessionAsync();
+
+// First turn
+Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session));
+
+// Second turn — the agent remembers the first turn via the session.
+Console.WriteLine(await agent.RunAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", session));
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.1_MultiturnConversation/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.1_MultiturnConversation/README.md
new file mode 100644
index 0000000000..a895981952
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.1_MultiturnConversation/README.md
@@ -0,0 +1,36 @@
+# Multi-turn Conversation
+
+This sample demonstrates how to implement multi-turn conversations where context is preserved across multiple agent runs using sessions and response ID chaining.
+
+## What this sample demonstrates
+
+- Creating an agent with instructions
+- Using sessions to maintain conversation context across multiple runs
+- Response ID chaining for multi-turn conversations
+- No server-side conversation creation required
+
+## Prerequisites
+
+Before you begin, ensure you have the following prerequisites:
+
+- .NET 10 SDK or later
+- 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 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"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+Navigate to the AgentsWithFoundry sample directory and run:
+
+```powershell
+cd dotnet/samples/02-agents/AgentsWithFoundry
+dotnet run --project .\Agent_Step02.1_MultiturnConversation
+```
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/Agent_Step02.2_MultiturnWithServerConversations.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/Agent_Step02.2_MultiturnWithServerConversations.csproj
new file mode 100644
index 0000000000..7367c1d2f8
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/Agent_Step02.2_MultiturnWithServerConversations.csproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ net10.0
+
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/Program.cs
new file mode 100644
index 0000000000..7a66555c18
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/Program.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to use server-side conversations with a FoundryAgent.
+// Server-side conversations persist on the Foundry service and are visible in the Foundry Project UI.
+// Use this when you need conversation history to be stored and accessible server-side.
+
+using Azure.AI.Projects;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.AzureAI;
+
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
+// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
+// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
+FoundryAgent agent = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential())
+ .AsAIAgent(deploymentName, instructions: "You are good at telling jokes.", name: "JokerAgent");
+
+// CreateConversationSessionAsync creates a server-side ProjectConversation
+// that persists on the Foundry service and is visible in the Foundry Project UI.
+AgentSession session = await agent.CreateConversationSessionAsync();
+
+Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session));
+Console.WriteLine(await agent.RunAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", session));
+
+// Streaming with server-side conversation context.
+await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("Tell me another joke, but about a ninja this time.", session))
+{
+ Console.Write(update);
+}
+
+Console.WriteLine();
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/README.md
new file mode 100644
index 0000000000..9d35133b39
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/README.md
@@ -0,0 +1,36 @@
+# Multi-turn Conversation with Server-Side Conversations
+
+This sample demonstrates how to use server-side conversations with a `FoundryAgent`. Server-side conversations persist on the Foundry service and are visible in the Foundry Project UI, making them ideal when you need conversation history to be stored and accessible server-side.
+
+## What this sample demonstrates
+
+- Creating a `FoundryAgent` with instructions
+- Using `CreateConversationSessionAsync` to create a server-side `ProjectConversation`
+- Multi-turn conversations with both text and streaming output
+- Server-side conversation persistence visible in the Foundry Project UI
+
+## Prerequisites
+
+Before you begin, ensure you have the following prerequisites:
+
+- .NET 10 SDK or later
+- 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 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"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+Navigate to the AgentsWithFoundry sample directory and run:
+
+```powershell
+cd dotnet/samples/02-agents/AgentsWithFoundry
+dotnet run --project .\Agent_Step02.2_MultiturnWithServerConversations
+```
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step03_UsingFunctionTools/Agent_Step03_UsingFunctionTools.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step03_UsingFunctionTools/Agent_Step03_UsingFunctionTools.csproj
new file mode 100644
index 0000000000..7367c1d2f8
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step03_UsingFunctionTools/Agent_Step03_UsingFunctionTools.csproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ net10.0
+
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step03_UsingFunctionTools/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step03_UsingFunctionTools/Program.cs
new file mode 100644
index 0000000000..7935835a24
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step03_UsingFunctionTools/Program.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample demonstrates how to use function tools.
+
+using System.ComponentModel;
+using Azure.AI.Projects;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+
+[Description("Get the weather for a given location.")]
+static string GetWeather([Description("The location to get the weather for.")] string location)
+ => $"The weather in {location} is cloudy with a high of 15°C.";
+
+// Define the function tool.
+AITool tool = AIFunctionFactory.Create(GetWeather);
+
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
+// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
+// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
+AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
+
+// Create a AIAgent with function tools.
+AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: "You are a helpful assistant that can get weather information.",
+ name: "WeatherAssistant",
+ tools: [tool]);
+
+// Non-streaming agent interaction with function tools.
+AgentSession session = await agent.CreateSessionAsync();
+Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?", session));
+
+// Streaming agent interaction with function tools.
+session = await agent.CreateSessionAsync();
+await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("What is the weather like in Amsterdam?", session))
+{
+ Console.Write(update);
+}
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step03_UsingFunctionTools/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step03_UsingFunctionTools/README.md
new file mode 100644
index 0000000000..9aa2a4ee69
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step03_UsingFunctionTools/README.md
@@ -0,0 +1,37 @@
+# Using Function Tools with the Responses API
+
+This sample demonstrates how to use function tools with the `ChatClientAgent`, allowing the agent to call custom functions to retrieve information.
+
+## What this sample demonstrates
+
+- Creating function tools using `AIFunctionFactory`
+- Passing function tools to a `ChatClientAgent`
+- Running agents with function tools (text output)
+- Running agents with function tools (streaming output)
+- No server-side agent creation or cleanup required
+
+## Prerequisites
+
+Before you begin, ensure you have the following prerequisites:
+
+- .NET 10 SDK or later
+- 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 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"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+Navigate to the AgentsWithFoundry sample directory and run:
+
+```powershell
+cd dotnet/samples/02-agents/AgentsWithFoundry
+dotnet run --project .\Agent_Step03_UsingFunctionTools
+```
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj
new file mode 100644
index 0000000000..7367c1d2f8
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ net10.0
+
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step04_UsingFunctionToolsWithApprovals/Program.cs
similarity index 69%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step04_UsingFunctionToolsWithApprovals/Program.cs
index 08051a500e..9a85dba83b 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step04_UsingFunctionToolsWithApprovals/Program.cs
@@ -1,9 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
// This sample demonstrates how to use an agent with function tools that require a human in the loop for approvals.
-// It shows both non-streaming and streaming agent interactions using weather-related tools.
-// If the agent is hosted in a service, with a remote user, combine this sample with the Persisted Conversations sample to persist the chat history
-// while the agent is waiting for user input.
using System.ComponentModel;
using Azure.AI.Projects;
@@ -11,18 +8,13 @@ using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
-string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-
-// Create a sample function tool that the agent can use.
[Description("Get the weather for a given location.")]
static string GetWeather([Description("The location to get the weather for.")] string location)
=> $"The weather in {location} is cloudy with a high of 15°C.";
-const string AssistantInstructions = "You are a helpful assistant that can get weather information.";
-const string AssistantName = "WeatherAssistant";
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
@@ -30,16 +22,16 @@ AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredent
ApprovalRequiredAIFunction approvalTool = new(AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather)));
-// Create AIAgent directly
-AIAgent agent = await aiProjectClient.CreateAIAgentAsync(name: AssistantName, model: deploymentName, instructions: AssistantInstructions, tools: [approvalTool]);
+AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: "You are a helpful assistant that can get weather information.",
+ name: "WeatherAssistant",
+ tools: [approvalTool]);
// Call the agent with approval-required function tools.
-// The agent will request approval before invoking the function.
AgentSession session = await agent.CreateSessionAsync();
AgentResponse response = await agent.RunAsync("What is the weather like in Amsterdam?", session);
// Check if there are any approval requests.
-// For simplicity, we are assuming here that only function approvals are pending.
List approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList();
while (approvalRequests.Count > 0)
@@ -53,13 +45,8 @@ while (approvalRequests.Count > 0)
return new ChatMessage(ChatRole.User, [functionApprovalRequest.CreateResponse(approved)]);
});
- // Pass the user input responses back to the agent for further processing.
response = await agent.RunAsync(userInputMessages, session);
-
approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList();
}
Console.WriteLine($"\nAgent: {response}");
-
-// Cleanup by agent name removes the agent version created.
-await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step04_UsingFunctionToolsWithApprovals/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step04_UsingFunctionToolsWithApprovals/README.md
new file mode 100644
index 0000000000..430c57548d
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step04_UsingFunctionToolsWithApprovals/README.md
@@ -0,0 +1,30 @@
+# Using Function Tools with Approvals via the Responses API
+
+This sample demonstrates how to use function tools that require human-in-the-loop approval before execution.
+
+## What this sample demonstrates
+
+- Creating function tools that require approval using `ApprovalRequiredAIFunction`
+- Handling approval requests from the agent
+- Passing approval responses back to the agent
+- No server-side agent creation or cleanup required
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+```powershell
+cd dotnet/samples/02-agents/AgentsWithFoundry
+dotnet run --project .\Agent_Step04_UsingFunctionToolsWithApprovals
+```
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj
new file mode 100644
index 0000000000..7367c1d2f8
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ net10.0
+
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step05_StructuredOutput/Program.cs
similarity index 53%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step05_StructuredOutput/Program.cs
index 3c02a4cec2..28cd4cf6bc 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step05_StructuredOutput/Program.cs
@@ -15,29 +15,23 @@ using SampleApp;
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-const string AssistantInstructions = "You are a helpful assistant that extracts structured information about people.";
-const string AssistantName = "StructuredOutputAssistant";
-
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-// Create ChatClientAgent directly
-ChatClientAgent agent = await aiProjectClient.CreateAIAgentAsync(
- model: deploymentName,
- new ChatClientAgentOptions()
+AIAgent agent = aiProjectClient.AsAIAgent(new ChatClientAgentOptions
+{
+ Name = "StructuredOutputAssistant",
+ ChatOptions = new()
{
- Name = AssistantName,
- ChatOptions = new()
- {
- Instructions = AssistantInstructions,
- ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema()
- }
- });
+ ModelId = deploymentName,
+ Instructions = "You are a helpful assistant that extracts structured information about people.",
+ ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema()
+ }
+});
-// Set PersonInfo as the type parameter of RunAsync method to specify the expected structured output from the agent and invoke the agent with some unstructured input.
+// Set PersonInfo as the type parameter of RunAsync method to specify the expected structured output.
AgentResponse response = await agent.RunAsync("Please provide information about John Smith, who is a 35-year-old software engineer.");
// Access the structured output via the Result property of the agent response.
@@ -46,39 +40,21 @@ Console.WriteLine($"Name: {response.Result.Name}");
Console.WriteLine($"Age: {response.Result.Age}");
Console.WriteLine($"Occupation: {response.Result.Occupation}");
-// Create the ChatClientAgent with the specified name, instructions, and expected structured output the agent should produce.
-ChatClientAgent agentWithPersonInfo = await aiProjectClient.CreateAIAgentAsync(
- model: deploymentName,
- new ChatClientAgentOptions()
- {
- Name = AssistantName,
- ChatOptions = new()
- {
- Instructions = AssistantInstructions,
- ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema()
- }
- });
+// Invoke the agent with streaming support, then deserialize the assembled response.
+IAsyncEnumerable updates = agent.RunStreamingAsync("Please provide information about Jane Doe, who is a 28-year-old data scientist.");
-// Invoke the agent with some unstructured input while streaming, to extract the structured information from.
-IAsyncEnumerable updates = agentWithPersonInfo.RunStreamingAsync("Please provide information about John Smith, who is a 35-year-old software engineer.");
-
-// Assemble all the parts of the streamed output, since we can only deserialize once we have the full json,
-// then deserialize the response into the PersonInfo class.
PersonInfo personInfo = JsonSerializer.Deserialize((await updates.ToAgentResponseAsync()).Text, JsonSerializerOptions.Web)
?? throw new InvalidOperationException("Failed to deserialize the streamed response into PersonInfo.");
-Console.WriteLine("Assistant Output:");
+Console.WriteLine("\nStreaming Assistant Output:");
Console.WriteLine($"Name: {personInfo.Name}");
Console.WriteLine($"Age: {personInfo.Age}");
Console.WriteLine($"Occupation: {personInfo.Occupation}");
-// Cleanup by agent name removes the agent version created.
-await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
-
namespace SampleApp
{
///
- /// Represents information about a person, including their name, age, and occupation, matched to the JSON schema used in the agent.
+ /// Represents information about a person.
///
[Description("Information about a person including their name, age, and occupation")]
public class PersonInfo
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step05_StructuredOutput/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step05_StructuredOutput/README.md
new file mode 100644
index 0000000000..f5421b3f64
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step05_StructuredOutput/README.md
@@ -0,0 +1,29 @@
+# Structured Output with the Responses API
+
+This sample demonstrates how to configure an agent to produce structured output using JSON schema.
+
+## What this sample demonstrates
+
+- Using `RunAsync()` to get typed structured output from the agent
+- Deserializing streamed responses into structured types
+- No server-side agent creation or cleanup required
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+```powershell
+cd dotnet/samples/02-agents/AgentsWithFoundry
+dotnet run --project .\Agent_Step05_StructuredOutput
+```
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj
new file mode 100644
index 0000000000..7367c1d2f8
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ net10.0
+
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step06_PersistedConversations/Program.cs
similarity index 78%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/Program.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step06_PersistedConversations/Program.cs
index d8a5a7cd35..c9774cb1bf 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step06_PersistedConversations/Program.cs
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
-// This sample shows how to create and use a simple AI agent with a conversation that can be persisted to disk.
+// This sample shows how to persist and resume conversations.
using System.Text.Json;
using Azure.AI.Projects;
@@ -10,16 +10,14 @@ using Microsoft.Agents.AI;
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-const string JokerInstructions = "You are good at telling jokes.";
-const string JokerName = "JokerAgent";
-
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-AIAgent agent = await aiProjectClient.CreateAIAgentAsync(name: JokerName, model: deploymentName, instructions: JokerInstructions);
+AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: "You are good at telling jokes.",
+ name: "JokerAgent");
// Start a new session for the agent conversation.
AgentSession session = await agent.CreateSessionAsync();
@@ -42,6 +40,3 @@ AgentSession resumedSession = await agent.DeserializeSessionAsync(reloadedSerial
// Run the agent again with the resumed session.
Console.WriteLine(await agent.RunAsync("Now tell the same joke in the voice of a pirate, and add some emojis to the joke.", resumedSession));
-
-// Cleanup by agent name removes the agent version created.
-await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step06_PersistedConversations/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step06_PersistedConversations/README.md
new file mode 100644
index 0000000000..a8cfda07ca
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step06_PersistedConversations/README.md
@@ -0,0 +1,30 @@
+# Persisted Conversations with the Responses API
+
+This sample demonstrates how to persist and resume agent conversations using session serialization.
+
+## What this sample demonstrates
+
+- Serializing agent sessions to JSON for persistence
+- Saving and loading sessions from disk
+- Resuming conversations with preserved context
+- No server-side agent creation or cleanup required
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+```powershell
+cd dotnet/samples/02-agents/AgentsWithFoundry
+dotnet run --project .\Agent_Step06_PersistedConversations
+```
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/FoundryAgents_Step07_Observability.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step07_Observability/Agent_Step07_Observability.csproj
similarity index 85%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/FoundryAgents_Step07_Observability.csproj
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step07_Observability/Agent_Step07_Observability.csproj
index 5ceeabb204..1189939bc0 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/FoundryAgents_Step07_Observability.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step07_Observability/Agent_Step07_Observability.csproj
@@ -9,8 +9,6 @@
-
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step07_Observability/Program.cs
similarity index 71%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/Program.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step07_Observability/Program.cs
index 257e24859f..68bfb91af0 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step07_Observability/Program.cs
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
-// This sample shows how to create and use a simple AI agent with Azure Foundry Agents as the backend that logs telemetry using OpenTelemetry.
+// This sample shows how to add OpenTelemetry observability to an agent.
using Azure.AI.Projects;
using Azure.Identity;
@@ -9,15 +9,11 @@ using Microsoft.Agents.AI;
using OpenTelemetry;
using OpenTelemetry.Trace;
+string? applicationInsightsConnectionString = Environment.GetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING");
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-string? applicationInsightsConnectionString = Environment.GetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING");
-const string JokerInstructions = "You are good at telling jokes.";
-const string JokerName = "JokerAgent";
-
-// Create TracerProvider with console exporter
-// This will output the telemetry data to the console.
+// Create TracerProvider with console exporter.
string sourceName = Guid.NewGuid().ToString("N");
TracerProviderBuilder tracerProviderBuilder = Sdk.CreateTracerProviderBuilder()
.AddSource(sourceName)
@@ -28,14 +24,16 @@ if (!string.IsNullOrWhiteSpace(applicationInsightsConnectionString))
}
using var tracerProvider = tracerProviderBuilder.Build();
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-// Define the agent you want to create. (Prompt Agent in this case)
-AIAgent agent = (await aiProjectClient.CreateAIAgentAsync(name: JokerName, model: deploymentName, instructions: JokerInstructions))
+AIAgent agent = aiProjectClient
+ .AsAIAgent(
+ deploymentName,
+ instructions: "You are good at telling jokes.",
+ name: "JokerAgent")
.AsBuilder()
.UseOpenTelemetry(sourceName: sourceName)
.Build();
@@ -48,8 +46,7 @@ Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session
session = await agent.CreateSessionAsync();
await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("Tell me a joke about a pirate.", session))
{
- Console.WriteLine(update);
+ Console.Write(update);
}
-// Cleanup by agent name removes the agent version created.
-await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
+Console.WriteLine();
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step07_Observability/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step07_Observability/README.md
new file mode 100644
index 0000000000..cb3bb729ff
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step07_Observability/README.md
@@ -0,0 +1,31 @@
+# Observability with the Responses API
+
+This sample demonstrates how to add OpenTelemetry observability to an agent using console and Azure Monitor exporters.
+
+## What this sample demonstrates
+
+- Configuring OpenTelemetry tracing with console exporter
+- Optional Azure Application Insights integration
+- Using `.AsBuilder().UseOpenTelemetry()` to add telemetry to the agent
+- No server-side agent creation or cleanup required
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+$env:APPLICATIONINSIGHTS_CONNECTION_STRING="..." # Optional
+```
+
+## Run the sample
+
+```powershell
+cd dotnet/samples/02-agents/AgentsWithFoundry
+dotnet run --project .\Agent_Step07_Observability
+```
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/FoundryAgents_Step08_DependencyInjection.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step08_DependencyInjection/Agent_Step08_DependencyInjection.csproj
similarity index 83%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/FoundryAgents_Step08_DependencyInjection.csproj
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step08_DependencyInjection/Agent_Step08_DependencyInjection.csproj
index f1812befeb..72af634725 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/FoundryAgents_Step08_DependencyInjection.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step08_DependencyInjection/Agent_Step08_DependencyInjection.csproj
@@ -11,8 +11,6 @@
-
-
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step08_DependencyInjection/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step08_DependencyInjection/Program.cs
new file mode 100644
index 0000000000..52a7d73132
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step08_DependencyInjection/Program.cs
@@ -0,0 +1,83 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to use dependency injection to register a AIAgent and use it from a hosted service.
+
+using Azure.AI.Projects;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using SampleApp;
+
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
+// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
+// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
+AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
+
+AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: "You are good at telling jokes.",
+ name: "JokerAgent");
+
+// Create a host builder that we will register services with and then run.
+HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
+
+// Add the AI agent to the service collection.
+builder.Services.AddSingleton(agent);
+
+// Add a sample service that will use the agent to respond to user input.
+builder.Services.AddHostedService();
+
+// Build and run the host.
+using IHost host = builder.Build();
+await host.RunAsync().ConfigureAwait(false);
+
+namespace SampleApp
+{
+ ///
+ /// A sample service that uses an AI agent to respond to user input.
+ ///
+ internal sealed class SampleService(AIAgent agent, IHostApplicationLifetime appLifetime) : IHostedService
+ {
+ private AgentSession? _session;
+
+ public async Task StartAsync(CancellationToken cancellationToken)
+ {
+ this._session = await agent.CreateSessionAsync(cancellationToken);
+ _ = this.RunAsync(appLifetime.ApplicationStopping);
+ }
+
+ public async Task RunAsync(CancellationToken cancellationToken)
+ {
+ await Task.Delay(100, cancellationToken);
+
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ Console.WriteLine("\nAgent: Ask me to tell you a joke about a specific topic. To exit just press Ctrl+C or enter without any input.\n");
+ Console.Write("> ");
+ string? input = Console.ReadLine();
+
+ if (string.IsNullOrWhiteSpace(input))
+ {
+ appLifetime.StopApplication();
+ break;
+ }
+
+ await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(input, this._session, cancellationToken: cancellationToken))
+ {
+ Console.Write(update);
+ }
+
+ Console.WriteLine();
+ }
+ }
+
+ public Task StopAsync(CancellationToken cancellationToken)
+ {
+ Console.WriteLine("\nShutting down...");
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step08_DependencyInjection/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step08_DependencyInjection/README.md
new file mode 100644
index 0000000000..c9ad936fd6
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step08_DependencyInjection/README.md
@@ -0,0 +1,30 @@
+# Dependency Injection with the Responses API
+
+This sample demonstrates how to register a `ChatClientAgent` in a dependency injection container and use it from a hosted service.
+
+## What this sample demonstrates
+
+- Registering `ChatClientAgent` as an `AIAgent` in the service collection
+- Using the agent from a `IHostedService` with an interactive chat loop
+- Streaming responses in a hosted service context
+- No server-side agent creation or cleanup required
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+```powershell
+cd dotnet/samples/02-agents/AgentsWithFoundry
+dotnet run --project .\Agent_Step08_DependencyInjection
+```
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/FoundryAgents_Step23_LocalMCP.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step09_UsingMcpClientAsTools/Agent_Step09_UsingMcpClientAsTools.csproj
similarity index 84%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/FoundryAgents_Step23_LocalMCP.csproj
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step09_UsingMcpClientAsTools/Agent_Step09_UsingMcpClientAsTools.csproj
index 1e3e6f57e3..96cdf948fe 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/FoundryAgents_Step23_LocalMCP.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step09_UsingMcpClientAsTools/Agent_Step09_UsingMcpClientAsTools.csproj
@@ -6,16 +6,15 @@
enableenable
- $(NoWarn);CA1812
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step09_UsingMcpClientAsTools/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step09_UsingMcpClientAsTools/Program.cs
new file mode 100644
index 0000000000..87f80297af
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step09_UsingMcpClientAsTools/Program.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to use MCP client tools with an agent.
+// It connects to the Microsoft Learn MCP server via HTTP and uses its tools.
+
+using Azure.AI.Projects;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+using ModelContextProtocol.Client;
+
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+// Connect to the Microsoft Learn MCP server via HTTP (Streamable HTTP transport).
+Console.WriteLine("Connecting to MCP server at https://learn.microsoft.com/api/mcp ...");
+
+await using McpClient mcpClient = await McpClient.CreateAsync(new HttpClientTransport(new()
+{
+ Endpoint = new Uri("https://learn.microsoft.com/api/mcp"),
+ Name = "Microsoft Learn MCP",
+}));
+
+// Retrieve the list of tools available on the MCP server.
+IList mcpTools = await mcpClient.ListToolsAsync();
+Console.WriteLine($"MCP tools available: {string.Join(", ", mcpTools.Select(t => t.Name))}");
+
+List agentTools = [.. mcpTools.Cast()];
+
+// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
+// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
+// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
+AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
+
+AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: "You are a helpful assistant that can help with Microsoft documentation questions. Use the Microsoft Learn MCP tool to search for documentation.",
+ name: "DocsAgent",
+ tools: agentTools);
+
+Console.WriteLine($"Agent '{agent.Name}' created. Asking a question...\n");
+
+const string Prompt = "How does one create an Azure storage account using az cli?";
+Console.WriteLine($"User: {Prompt}\n");
+Console.WriteLine($"Agent: {await agent.RunAsync(Prompt)}");
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step09_UsingMcpClientAsTools/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step09_UsingMcpClientAsTools/README.md
new file mode 100644
index 0000000000..72437e0802
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step09_UsingMcpClientAsTools/README.md
@@ -0,0 +1,29 @@
+# Using MCP Client as Tools with the Responses API
+
+This sample shows how to use MCP (Model Context Protocol) client tools with a `ChatClientAgent` using the Responses API directly.
+
+## What this sample demonstrates
+
+- Connecting to an MCP server via HTTP client transport
+- Retrieving MCP tools and passing them to a `ChatClientAgent`
+- Using MCP tools for agent interactions without server-side agent creation
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+- Node.js installed (for npx/MCP server)
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+```powershell
+dotnet run
+```
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/Agent_Step10_UsingImages.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/Agent_Step10_UsingImages.csproj
new file mode 100644
index 0000000000..6064cf9334
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/Agent_Step10_UsingImages.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net10.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/Program.cs
similarity index 61%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/Program.cs
index d810c8046a..2fdc150be9 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/Program.cs
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
-// This sample shows how to use Image Multi-Modality with an AI agent.
+// This sample shows how to use image multi-modality with an agent.
using Azure.AI.Projects;
using Azure.Identity;
@@ -10,29 +10,25 @@ using Microsoft.Extensions.AI;
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o";
-const string VisionInstructions = "You are a helpful agent that can analyze images";
-const string VisionName = "VisionAgent";
-
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-// Define the agent you want to create. (Prompt Agent in this case)
-AIAgent agent = await aiProjectClient.CreateAIAgentAsync(name: VisionName, model: deploymentName, instructions: VisionInstructions);
+AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: "You are a helpful agent that can analyze images.",
+ name: "VisionAgent");
ChatMessage message = new(ChatRole.User, [
new TextContent("What do you see in this image?"),
- await DataContent.LoadFromAsync("Assets/walkway.jpg"),
+ await DataContent.LoadFromAsync("assets/walkway.jpg"),
]);
AgentSession session = await agent.CreateSessionAsync();
await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(message, session))
{
- Console.WriteLine(update);
+ Console.Write(update);
}
-// Cleanup by agent name removes the agent version created.
-await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
+Console.WriteLine();
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/README.md
new file mode 100644
index 0000000000..370bf896cf
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/README.md
@@ -0,0 +1,30 @@
+# Using Images with the Responses API
+
+This sample demonstrates how to use image multi-modality with an agent.
+
+## What this sample demonstrates
+
+- Loading images using `DataContent.LoadFromAsync`
+- Sending images alongside text to the agent
+- Streaming the agent's image analysis response
+- No server-side agent creation or cleanup required
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and a vision-capable model deployment (e.g., `gpt-4o`)
+- Azure CLI installed and authenticated (`az login`)
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o"
+```
+
+## Run the sample
+
+```powershell
+cd dotnet/samples/02-agents/AgentsWithFoundry
+dotnet run --project .\Agent_Step10_UsingImages
+```
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/Assets/walkway.jpg b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/assets/walkway.jpg
similarity index 100%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/Assets/walkway.jpg
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/assets/walkway.jpg
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step11_AsFunctionTool/Agent_Step11_AsFunctionTool.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step11_AsFunctionTool/Agent_Step11_AsFunctionTool.csproj
new file mode 100644
index 0000000000..7367c1d2f8
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step11_AsFunctionTool/Agent_Step11_AsFunctionTool.csproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ net10.0
+
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step11_AsFunctionTool/Program.cs
similarity index 57%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/Program.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step11_AsFunctionTool/Program.cs
index 585725322e..3715ab194f 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step11_AsFunctionTool/Program.cs
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
-// This sample shows how to create and use an Azure Foundry Agents AI agent as a function tool.
+// This sample shows how to use one agent as a function tool for another agent.
using System.ComponentModel;
using Azure.AI.Projects;
@@ -8,43 +8,29 @@ using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
-string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-
-const string WeatherInstructions = "You answer questions about the weather.";
-const string WeatherName = "WeatherAgent";
-const string MainInstructions = "You are a helpful assistant who responds in French.";
-const string MainName = "MainAgent";
-
[Description("Get the weather for a given location.")]
static string GetWeather([Description("The location to get the weather for.")] string location)
=> $"The weather in {location} is cloudy with a high of 15°C.";
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-// Create the weather agent with function tools.
AITool weatherTool = AIFunctionFactory.Create(GetWeather);
-AIAgent weatherAgent = await aiProjectClient.CreateAIAgentAsync(
- name: WeatherName,
- model: deploymentName,
- instructions: WeatherInstructions,
+AIAgent weatherAgent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: "You answer questions about the weather.",
+ name: "WeatherAgent",
tools: [weatherTool]);
-// Create the main agent, and provide the weather agent as a function tool.
-AIAgent agent = await aiProjectClient.CreateAIAgentAsync(
- name: MainName,
- model: deploymentName,
- instructions: MainInstructions,
+AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: "You are a helpful assistant who responds in French.",
+ name: "MainAgent",
tools: [weatherAgent.AsAIFunction()]);
// Invoke the agent and output the text result.
AgentSession session = await agent.CreateSessionAsync();
Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?", session));
-
-// Cleanup by agent name removes the agent versions created.
-await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
-await aiProjectClient.Agents.DeleteAgentAsync(weatherAgent.Name);
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step11_AsFunctionTool/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step11_AsFunctionTool/README.md
new file mode 100644
index 0000000000..7d361305b9
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step11_AsFunctionTool/README.md
@@ -0,0 +1,30 @@
+# Agent as a Function Tool with the Responses API
+
+This sample demonstrates how to use one agent as a function tool for another agent.
+
+## What this sample demonstrates
+
+- Creating a specialized agent (weather) with function tools
+- Exposing an agent as a function tool using `.AsAIFunction()`
+- Composing agents where one agent delegates to another
+- No server-side agent creation or cleanup required
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+```powershell
+cd dotnet/samples/02-agents/AgentsWithFoundry
+dotnet run --project .\Agent_Step11_AsFunctionTool
+```
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/FoundryAgents_Step12_Middleware.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step12_Middleware/Agent_Step12_Middleware.csproj
similarity index 81%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/FoundryAgents_Step12_Middleware.csproj
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step12_Middleware/Agent_Step12_Middleware.csproj
index 9f29a8d7e6..b30baccd54 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/FoundryAgents_Step12_Middleware.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step12_Middleware/Agent_Step12_Middleware.csproj
@@ -3,15 +3,13 @@
Exenet10.0
-
+
enableenable
-
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step12_Middleware/Program.cs
similarity index 70%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step12_Middleware/Program.cs
index 824e1507b3..e37bf89639 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step12_Middleware/Program.cs
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
-// This sample shows multiple middleware layers working together with Azure Foundry Agents:
+// This sample shows multiple middleware layers working together with a ChatClientAgent:
// agent run (PII filtering and guardrails),
// function invocation (logging and result overrides), and human-in-the-loop
// approval workflows for sensitive function calls.
@@ -12,19 +12,6 @@ using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
-// Get Azure AI Foundry configuration from environment variables
-string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
-string deploymentName = System.Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o";
-
-const string AssistantInstructions = "You are an AI assistant that helps people find information.";
-const string AssistantName = "InformationAssistant";
-
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
-// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
-// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
-// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
-AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-
[Description("Get the weather for a given location.")]
static string GetWeather([Description("The location to get the weather for.")] string location)
=> $"The weather in {location} is cloudy with a high of 15°C.";
@@ -33,14 +20,20 @@ static string GetWeather([Description("The location to get the weather for.")] s
static string GetDateTime()
=> DateTimeOffset.Now.ToString();
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
+// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
+// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
+AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
+
AITool dateTimeTool = AIFunctionFactory.Create(GetDateTime, name: nameof(GetDateTime));
AITool getWeatherTool = AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather));
-// Define the agent you want to create. (Prompt Agent in this case)
-AIAgent originalAgent = await aiProjectClient.CreateAIAgentAsync(
- name: AssistantName,
- model: deploymentName,
- instructions: AssistantInstructions,
+AIAgent originalAgent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: "You are an AI assistant that helps people find information.",
+ name: "InformationAssistant",
tools: [getWeatherTool, dateTimeTool]);
// Adding middleware to the agent level
@@ -63,24 +56,17 @@ AgentResponse piiResponse = await middlewareEnabledAgent.RunAsync("My name is Jo
Console.WriteLine($"Pii filtered response: {piiResponse}");
Console.WriteLine("\n\n=== Example 3: Agent function middleware ===");
-
-// Agent function middleware support is limited to agents that wraps a upstream ChatClientAgent or derived from it.
-
AgentResponse functionCallResponse = await middlewareEnabledAgent.RunAsync("What's the current time and the weather in Seattle?", session);
Console.WriteLine($"Function calling response: {functionCallResponse}");
// Special per-request middleware agent.
Console.WriteLine("\n\n=== Example 4: Middleware with human in the loop function approval ===");
-AIAgent humanInTheLoopAgent = await aiProjectClient.CreateAIAgentAsync(
+AIAgent humanInTheLoopAgent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: "You are a Human in the loop testing AI assistant that helps people find information.",
name: "HumanInTheLoopAgent",
- model: deploymentName,
- instructions: "You are an Human in the loop testing AI assistant that helps people find information.",
-
- // Adding a function with approval required
tools: [new ApprovalRequiredAIFunction(AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather)))]);
-// Using the ConsolePromptingApprovalMiddleware for a specific request to handle user approval during function calls.
AgentResponse response = await humanInTheLoopAgent
.AsBuilder()
.Use(ConsolePromptingApprovalMiddleware, null)
@@ -108,7 +94,6 @@ async ValueTask
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step13_Plugins/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step13_Plugins/Program.cs
new file mode 100644
index 0000000000..3af63090c5
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step13_Plugins/Program.cs
@@ -0,0 +1,153 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to use plugins with an AI agent. Plugin classes can
+// depend on other services that need to be injected. In this sample, the
+// AgentPlugin class uses the WeatherProvider and CurrentTimeProvider classes
+// to get weather and current time information. Both services are registered
+// in the service collection and injected into the plugin.
+// Plugin classes may have many methods, but only some are intended to be used
+// as AI functions. The AsAITools method of the plugin class shows how to specify
+// which methods should be exposed to the AI agent.
+
+using Azure.AI.Projects;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.DependencyInjection;
+using SampleApp;
+
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+const string AssistantInstructions = "You are a helpful assistant that helps people find information.";
+const string AssistantName = "PluginAssistant";
+
+// Create a service collection to hold the agent plugin and its dependencies.
+ServiceCollection services = new();
+services.AddSingleton();
+services.AddSingleton();
+services.AddSingleton(); // The plugin depends on WeatherProvider and CurrentTimeProvider registered above.
+
+IServiceProvider serviceProvider = services.BuildServiceProvider();
+
+// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
+// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
+// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
+AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
+
+// Create a ChatClientAgent with the options-based constructor to pass services.
+AIAgent agent = aiProjectClient.AsAIAgent(new ChatClientAgentOptions
+{
+ Name = AssistantName,
+ ChatOptions = new() { ModelId = deploymentName, Instructions = AssistantInstructions, Tools = serviceProvider.GetRequiredService().AsAITools().ToList() }
+},
+ services: serviceProvider);
+
+// Invoke the agent and output the text result.
+AgentSession session = await agent.CreateSessionAsync();
+Console.WriteLine(await agent.RunAsync("Tell me current time and weather in Seattle.", session));
+
+namespace SampleApp
+{
+ ///
+ /// The agent plugin that provides weather and current time information.
+ ///
+ internal sealed class AgentPlugin
+ {
+ private readonly WeatherProvider _weatherProvider;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The weather provider to get weather information.
+ public AgentPlugin(WeatherProvider weatherProvider)
+ {
+ this._weatherProvider = weatherProvider;
+ }
+
+ ///
+ /// Gets the weather information for the specified location.
+ ///
+ ///
+ /// This method demonstrates how to use the dependency that was injected into the plugin class.
+ ///
+ /// The location to get the weather for.
+ /// The weather information for the specified location.
+ public string GetWeather(string location)
+ {
+ return this._weatherProvider.GetWeather(location);
+ }
+
+ ///
+ /// Gets the current date and time for the specified location.
+ ///
+ ///
+ /// This method demonstrates how to resolve a dependency using the service provider passed to the method.
+ ///
+ /// The service provider to resolve the .
+ /// The location to get the current time for.
+ /// The current date and time as a .
+ public DateTimeOffset GetCurrentTime(IServiceProvider sp, string location)
+ {
+ CurrentTimeProvider currentTimeProvider = sp.GetRequiredService();
+ return currentTimeProvider.GetCurrentTime(location);
+ }
+
+ ///
+ /// Returns the functions provided by this plugin.
+ ///
+ ///
+ /// In real world scenarios, a class may have many methods and only a subset of them may be intended to be exposed as AI functions.
+ /// This method demonstrates how to explicitly specify which methods should be exposed to the AI agent.
+ ///
+ /// The functions provided by this plugin.
+ public IEnumerable AsAITools()
+ {
+ yield return AIFunctionFactory.Create(this.GetWeather);
+ yield return AIFunctionFactory.Create(this.GetCurrentTime);
+ }
+ }
+
+ internal sealed class WeatherProvider
+ {
+ private readonly string _weatherSummary = "cloudy with a high of 15°C";
+
+ ///
+ /// The weather provider that returns weather information.
+ ///
+ ///
+ /// Gets the weather information for the specified location.
+ ///
+ ///
+ /// The weather information is hardcoded for demonstration purposes.
+ /// In a real application, this could call a weather API to get actual weather data.
+ ///
+ /// The location to get the weather for.
+ /// The weather information for the specified location.
+ public string GetWeather(string location)
+ {
+ return $"The weather in {location} is {this._weatherSummary}.";
+ }
+ }
+
+ internal sealed class CurrentTimeProvider
+ {
+ private readonly TimeProvider _timeProvider = TimeProvider.System;
+
+ ///
+ /// Provides the current date and time.
+ ///
+ ///
+ /// This class returns the current date and time using the system's clock.
+ ///
+ ///
+ /// Gets the current date and time.
+ ///
+ /// The location to get the current time for (not used in this implementation).
+ /// The current date and time as a .
+ public DateTimeOffset GetCurrentTime(string location)
+ {
+ return this._timeProvider.GetLocalNow();
+ }
+ }
+}
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step13_Plugins/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step13_Plugins/README.md
new file mode 100644
index 0000000000..8cc2f59116
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step13_Plugins/README.md
@@ -0,0 +1,29 @@
+# Using Plugins with the Responses API
+
+This sample shows how to use plugins with a `ChatClientAgent` using the Responses API directly, with dependency injection for plugin services.
+
+## What this sample demonstrates
+
+- Creating plugin classes with injected dependencies
+- Registering services and building a service provider
+- Passing `services` to the `ChatClientAgent` via the options-based constructor
+- Using `AIFunctionFactory` to expose plugin methods as AI tools
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+```powershell
+dotnet run
+```
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/FoundryAgents_Step03_UsingFunctionTools.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/Agent_Step14_CodeInterpreter.csproj
similarity index 89%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/FoundryAgents_Step03_UsingFunctionTools.csproj
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/Agent_Step14_CodeInterpreter.csproj
index daf7e24494..e11688b6ba 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/FoundryAgents_Step03_UsingFunctionTools.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/Agent_Step14_CodeInterpreter.csproj
@@ -9,7 +9,6 @@
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/Program.cs
similarity index 59%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/Program.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/Program.cs
index 5a27daed12..8d7f598a4a 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/Program.cs
@@ -1,61 +1,31 @@
// Copyright (c) Microsoft. All rights reserved.
-// This sample shows how to use Code Interpreter Tool with AI Agents.
+// This sample shows how to use Code Interpreter Tool with AIProjectClient.AsAIAgent(...).
using System.Text;
using Azure.AI.Projects;
-using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI.Assistants;
-using OpenAI.Responses;
+
+const string AgentInstructions = "You are a personal math tutor. When asked a math question, write and run code using the python tool to answer the question.";
+const string AgentName = "CoderAgent-RAPI";
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-const string AgentInstructions = "You are a personal math tutor. When asked a math question, write and run code using the python tool to answer the question.";
-const string AgentNameMEAI = "CoderAgent-MEAI";
-const string AgentNameNative = "CoderAgent-NATIVE";
-
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-
-// Option 1 - Using HostedCodeInterpreterTool + AgentOptions (MEAI + AgentFramework)
-// Create the server side agent version
-AIAgent agentOption1 = await aiProjectClient.CreateAIAgentAsync(
- model: deploymentName,
- name: AgentNameMEAI,
+AIAgent agent = aiProjectClient.AsAIAgent(
+ deploymentName,
instructions: AgentInstructions,
+ name: AgentName,
tools: [new HostedCodeInterpreterTool() { Inputs = [] }]);
-// Option 2 - Using PromptAgentDefinition SDK native type
-// Create the server side agent version
-AIAgent agentOption2 = await aiProjectClient.CreateAIAgentAsync(
- name: AgentNameNative,
- creationOptions: new AgentVersionCreationOptions(
- new PromptAgentDefinition(model: deploymentName)
- {
- Instructions = AgentInstructions,
- Tools = {
- ResponseTool.CreateCodeInterpreterTool(
- new CodeInterpreterToolContainer(
- CodeInterpreterToolContainerConfiguration.CreateAutomaticContainerConfiguration(fileIds: [])
- )
- ),
- }
- })
-);
-
-// Either invoke option1 or option2 agent, should have same result
-// Option 1
-AgentResponse response = await agentOption1.RunAsync("I need to solve the equation sin(x) + x^2 = 42");
-
-// Option 2
-// AgentResponse response = await agentOption2.RunAsync("I need to solve the equation sin(x) + x^2 = 42");
+AgentResponse response = await agent.RunAsync("I need to solve the equation sin(x) + x^2 = 42");
// Get the CodeInterpreterToolCallContent
CodeInterpreterToolCallContent? toolCallContent = response.Messages.SelectMany(m => m.Contents).OfType().FirstOrDefault();
@@ -87,7 +57,3 @@ foreach (AIAnnotation annotation in response.Messages.SelectMany(m => m.Contents
""");
}
}
-
-// Cleanup by agent name removes the agent version created.
-await aiProjectClient.Agents.DeleteAgentAsync(agentOption1.Name);
-await aiProjectClient.Agents.DeleteAgentAsync(agentOption2.Name);
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/README.md
new file mode 100644
index 0000000000..1a8cfc8aae
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/README.md
@@ -0,0 +1,28 @@
+# Code Interpreter with the Responses API
+
+This sample shows how to use the Code Interpreter tool with a `ChatClientAgent` using the Responses API directly.
+
+## What this sample demonstrates
+
+- Using `HostedCodeInterpreterTool` with `ChatClientAgent`
+- Extracting code input and output from agent responses
+- Handling code interpreter annotations and file citations
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+```powershell
+dotnet run
+```
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/FoundryAgents_Step15_ComputerUse.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Agent_Step15_ComputerUse.csproj
similarity index 99%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/FoundryAgents_Step15_ComputerUse.csproj
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Agent_Step15_ComputerUse.csproj
index 041c72c43e..f739f56123 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/FoundryAgents_Step15_ComputerUse.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Agent_Step15_ComputerUse.csproj
@@ -29,5 +29,5 @@
Always
-
+
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_browser_search.png b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Assets/cua_browser_search.png
similarity index 100%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_browser_search.png
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Assets/cua_browser_search.png
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_search_results.png b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Assets/cua_search_results.png
similarity index 100%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_search_results.png
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Assets/cua_search_results.png
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_search_typed.png b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Assets/cua_search_typed.png
similarity index 100%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_search_typed.png
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Assets/cua_search_typed.png
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/ComputerUseUtil.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/ComputerUseUtil.cs
similarity index 100%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/ComputerUseUtil.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/ComputerUseUtil.cs
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Program.cs
similarity index 61%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Program.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Program.cs
index 7f6382d085..22f03e27b3 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Program.cs
@@ -1,11 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.
-// This sample shows how to use Computer Use Tool with AI Agents.
+// This sample shows how to use Computer Use Tool with a ChatClientAgent.
using Azure.AI.Projects;
-using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.AzureAI;
using Microsoft.Extensions.AI;
using OpenAI.Responses;
@@ -15,59 +15,32 @@ internal sealed class Program
{
private static async Task Main(string[] args)
{
- string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
- string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "computer-use-preview";
-
- // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
- // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
- // latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
- // Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
- AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
const string AgentInstructions = @"
You are a computer automation assistant.
Be direct and efficient. When you reach the search results page, read and describe the actual search result titles and descriptions you can see.
";
- const string AgentNameMEAI = "ComputerAgent-MEAI";
- const string AgentNameNative = "ComputerAgent-NATIVE";
+ const string AgentName = "ComputerAgent-RAPI";
- // Option 1 - Using ComputerUseTool + AgentOptions (MEAI + AgentFramework)
- // Create AIAgent directly
- AIAgent agentOption1 = await aiProjectClient.CreateAIAgentAsync(
- name: AgentNameMEAI,
- model: deploymentName,
+ string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+ string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "computer-use-preview";
+
+ // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
+ // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
+ // latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
+ AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
+
+ // Create a AIAgent with ComputerUseTool.
+ AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
instructions: AgentInstructions,
+ name: AgentName,
description: "Computer automation agent with screen interaction capabilities.",
tools: [
- ResponseTool.CreateComputerTool(ComputerToolEnvironment.Browser, 1026, 769).AsAITool(),
+ FoundryAITool.CreateComputerTool(ComputerToolEnvironment.Browser, 1026, 769),
]);
- // Option 2 - Using PromptAgentDefinition SDK native type
- // Create the server side agent version
- AIAgent agentOption2 = await aiProjectClient.CreateAIAgentAsync(
- name: AgentNameNative,
- creationOptions: new AgentVersionCreationOptions(
- new PromptAgentDefinition(model: deploymentName)
- {
- Instructions = AgentInstructions,
- Tools = { ResponseTool.CreateComputerTool(
- environment: new ComputerToolEnvironment("windows"),
- displayWidth: 1026,
- displayHeight: 769) }
- })
- );
-
- // Either invoke option1 or option2 agent, should have same result
- // Option 1
- await InvokeComputerUseAgentAsync(agentOption1);
-
- // Option 2
- //await InvokeComputerUseAgentAsync(agentOption2);
-
- // Cleanup by agent name removes the agent version created.
- await aiProjectClient.Agents.DeleteAgentAsync(agentOption1.Name);
- await aiProjectClient.Agents.DeleteAgentAsync(agentOption2.Name);
+ await InvokeComputerUseAgentAsync(agent);
}
private static async Task InvokeComputerUseAgentAsync(AIAgent agent)
@@ -94,10 +67,8 @@ internal sealed class Program
// Initial request with screenshot - start with Bing search page
Console.WriteLine("Starting computer automation session (initial screenshot: cua_browser_search.png)...");
- // IMPORTANT: Computer-use with the Azure Agents API differs from the vanilla OpenAI Responses API.
- // The Azure Agents API rejects requests that include previous_response_id alongside
- // computer_call_output items. To work around this, each call uses a fresh session (avoiding
- // previous_response_id) and re-sends the full conversation context as input items instead.
+ // We use PreviousResponseId to chain calls, sending only the new computer_call_output items
+ // instead of re-sending the full context.
AgentSession session = await agent.CreateSessionAsync();
AgentResponse response = await agent.RunAsync(message, session: session, options: runOptions);
@@ -161,31 +132,15 @@ internal sealed class Program
Console.WriteLine("Sending action result back to agent...");
- // Build the follow-up messages with full conversation context.
- // The Azure Agents API rejects previous_response_id when computer_call_output items are
- // present, so we must re-send all prior output items (reasoning, computer_call, etc.)
- // as input items alongside the computer_call_output to maintain conversation continuity.
- List followUpMessages = [];
-
- // Re-send all response output items as an assistant message so the API has full context
- List priorOutputContents = response.Messages
- .SelectMany(m => m.Contents)
- .ToList();
- followUpMessages.Add(new ChatMessage(ChatRole.Assistant, priorOutputContents));
-
- // Add the computer_call_output as a user message
+ // Send only the computer_call_output — the session carries PreviousResponseId for context continuity.
AIContent callOutput = new()
{
RawRepresentation = new ComputerCallOutputResponseItem(
currentCallId,
output: ComputerCallOutput.CreateScreenshotOutput(new BinaryData(screenInfo.ImageBytes), "image/png"))
};
- followUpMessages.Add(new ChatMessage(ChatRole.User, [callOutput]));
- // Create a fresh session so ConversationId does not carry over a previous_response_id.
- // Without this, the Azure Agents API returns an error when computer_call_output is present.
- session = await agent.CreateSessionAsync();
- response = await agent.RunAsync(followUpMessages, session: session, options: runOptions);
+ response = await agent.RunAsync([new ChatMessage(ChatRole.User, [callOutput])], session: session, options: runOptions);
}
}
}
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/README.md
new file mode 100644
index 0000000000..ecaa18e10f
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/README.md
@@ -0,0 +1,29 @@
+# Computer Use with the Responses API
+
+This sample shows how to use the Computer Use tool with a `ChatClientAgent` using the Responses API directly.
+
+## What this sample demonstrates
+
+- Using `FoundryAITool.CreateComputerTool()` with `ChatClientAgent`
+- Processing computer call actions (click, type, key press)
+- Managing the computer use interaction loop with screenshots
+- Handling the Azure Agents API workaround for `previous_response_id` with `computer_call_output`
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="computer-use-preview"
+```
+
+## Run the sample
+
+```powershell
+dotnet run
+```
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/FoundryAgents_Step04_UsingFunctionToolsWithApprovals.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step16_FileSearch/Agent_Step16_FileSearch.csproj
similarity index 89%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/FoundryAgents_Step04_UsingFunctionToolsWithApprovals.csproj
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step16_FileSearch/Agent_Step16_FileSearch.csproj
index daf7e24494..e11688b6ba 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/FoundryAgents_Step04_UsingFunctionToolsWithApprovals.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step16_FileSearch/Agent_Step16_FileSearch.csproj
@@ -9,7 +9,6 @@
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step16_FileSearch/Program.cs
similarity index 69%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/Program.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step16_FileSearch/Program.cs
index 5371903a9f..c01a951df4 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step16_FileSearch/Program.cs
@@ -1,22 +1,20 @@
// Copyright (c) Microsoft. All rights reserved.
-// This sample shows how to use File Search Tool with AI Agents.
+// This sample shows how to use File Search Tool with a ChatClientAgent.
using Azure.AI.Projects;
-using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI.Assistants;
using OpenAI.Files;
-using OpenAI.Responses;
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
const string AgentInstructions = "You are a helpful assistant that can search through uploaded files to answer questions.";
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
+// We need the AIProjectClient to upload files and create vector stores.
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
@@ -52,8 +50,11 @@ var vectorStoreResult = await vectorStoresClient.CreateVectorStoreAsync(
string vectorStoreId = vectorStoreResult.Value.Id;
Console.WriteLine($"Created vector store, vector store ID: {vectorStoreId}");
-AIAgent agent = await CreateAgentWithMEAI();
-// AIAgent agent = await CreateAgentWithNativeSDK();
+// Create a AIAgent with HostedFileSearchTool.
+AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: AgentInstructions,
+ name: "FileSearchAgent-RAPI",
+ tools: [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreId)] }]);
// Run the agent
Console.WriteLine("\n--- Running File Search Agent ---");
@@ -73,39 +74,9 @@ foreach (AIAnnotation annotation in response.Messages.SelectMany(m => m.Contents
}
}
-// Cleanup.
+// Cleanup file resources.
Console.WriteLine("\n--- Cleanup ---");
-await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
await vectorStoresClient.DeleteVectorStoreAsync(vectorStoreId);
await filesClient.DeleteFileAsync(uploadedFile.Id);
File.Delete(searchFilePath);
Console.WriteLine("Cleanup completed successfully.");
-
-// --- Agent Creation Options ---
-
-#pragma warning disable CS8321 // Local function is declared but never used
-// Option 1 - Using HostedFileSearchTool (MEAI + AgentFramework)
-async Task CreateAgentWithMEAI()
-{
- return await aiProjectClient.CreateAIAgentAsync(
- model: deploymentName,
- name: "FileSearchAgent-MEAI",
- instructions: AgentInstructions,
- tools: [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreId)] }]);
-}
-
-// Option 2 - Using PromptAgentDefinition with ResponseTool.CreateFileSearchTool (Native SDK)
-async Task CreateAgentWithNativeSDK()
-{
- return await aiProjectClient.CreateAIAgentAsync(
- name: "FileSearchAgent-NATIVE",
- creationOptions: new AgentVersionCreationOptions(
- new PromptAgentDefinition(model: deploymentName)
- {
- Instructions = AgentInstructions,
- Tools = {
- ResponseTool.CreateFileSearchTool(vectorStoreIds: [vectorStoreId])
- }
- })
- );
-}
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step16_FileSearch/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step16_FileSearch/README.md
new file mode 100644
index 0000000000..5ce7f11cda
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step16_FileSearch/README.md
@@ -0,0 +1,29 @@
+# File Search with the Responses API
+
+This sample shows how to use the File Search tool with a `ChatClientAgent` using the Responses API directly.
+
+## What this sample demonstrates
+
+- Uploading files and creating vector stores via `AIProjectClient`
+- Using `HostedFileSearchTool` with `ChatClientAgent`
+- Handling file citation annotations in agent responses
+- Cleaning up file resources after use
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+```powershell
+dotnet run
+```
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/FoundryAgents_Step01.1_Basics.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/Agent_Step17_OpenAPITools.csproj
similarity index 82%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/FoundryAgents_Step01.1_Basics.csproj
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/Agent_Step17_OpenAPITools.csproj
index 89b9d8ddc0..4602e9c9e0 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/FoundryAgents_Step01.1_Basics.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/Agent_Step17_OpenAPITools.csproj
@@ -6,15 +6,14 @@
enableenable
- $(NoWarn);IDE0059
-
+
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/Program.cs
similarity index 55%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/Program.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/Program.cs
index ebf66e6c2c..5cc2720dd9 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/Program.cs
@@ -6,16 +6,32 @@ using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
-using OpenAI.Responses;
+using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Extensions.AI;
-// Warning: DefaultAzureCredential is intended for simplicity in development. For production scenarios, consider using a more specific credential.
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
const string AgentInstructions = "You are a helpful assistant that can use the countries API to retrieve information about countries by their currency code.";
+// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
+// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
+// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
+AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-// A simple OpenAPI specification for the REST Countries API
-const string CountriesOpenApiSpec = """
+AITool openApiTool = FoundryAITool.CreateOpenApiTool(CreateOpenAPIFunctionDefinition());
+
+AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: AgentInstructions,
+ name: "OpenAPIToolsAgent",
+ tools: [openApiTool]);
+
+// Run the agent with a question about countries
+Console.WriteLine(await agent.RunAsync("What countries use the Euro (EUR) as their currency? Please list them."));
+
+OpenApiFunctionDefinition CreateOpenAPIFunctionDefinition()
+{
+ // A simple OpenAPI specification for the REST Countries API
+ const string CountriesOpenApiSpec = """
{
"openapi": "3.1.0",
"info": {
@@ -68,49 +84,12 @@ const string CountriesOpenApiSpec = """
}
""";
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
-AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-
-// Create the OpenAPI function definition
-var openApiFunction = new OpenApiFunctionDefinition(
- "get_countries",
- BinaryData.FromString(CountriesOpenApiSpec),
- new OpenAPIAnonymousAuthenticationDetails())
-{
- Description = "Retrieve information about countries by currency code"
-};
-
-AIAgent agent = await CreateAgentWithMEAI();
-// AIAgent agent = await CreateAgentWithNativeSDK();
-
-// Run the agent with a question about countries
-Console.WriteLine(await agent.RunAsync("What countries use the Euro (EUR) as their currency? Please list them."));
-
-// Cleanup by deleting the agent
-await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
-
-// --- Agent Creation Options ---
-
-// Option 1 - Using AsAITool wrapping for OpenApiTool (MEAI + AgentFramework)
-async Task CreateAgentWithMEAI()
-{
- return await aiProjectClient.CreateAIAgentAsync(
- model: deploymentName,
- name: "OpenAPIToolsAgent-MEAI",
- instructions: AgentInstructions,
- tools: [((ResponseTool)AgentTool.CreateOpenApiTool(openApiFunction)).AsAITool()]);
-}
-
-// Option 2 - Using PromptAgentDefinition with AgentTool.CreateOpenApiTool (Native SDK)
-async Task CreateAgentWithNativeSDK()
-{
- return await aiProjectClient.CreateAIAgentAsync(
- name: "OpenAPIToolsAgent-NATIVE",
- creationOptions: new AgentVersionCreationOptions(
- new PromptAgentDefinition(model: deploymentName)
- {
- Instructions = AgentInstructions,
- Tools = { (ResponseTool)AgentTool.CreateOpenApiTool(openApiFunction) }
- })
- );
+ // Create the OpenAPI function definition
+ return new(
+ "get_countries",
+ BinaryData.FromString(CountriesOpenApiSpec),
+ new OpenAPIAnonymousAuthenticationDetails())
+ {
+ Description = "Retrieve information about countries by currency code"
+ };
}
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/README.md
new file mode 100644
index 0000000000..52a11ec1dd
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/README.md
@@ -0,0 +1,29 @@
+# OpenAPI Tools with the Responses API
+
+This sample shows how to use OpenAPI tools with a `ChatClientAgent` using the Responses API directly.
+
+## What this sample demonstrates
+
+- Defining an OpenAPI specification inline
+- Creating an `OpenAPIFunctionDefinition` for the REST Countries API
+- Using `FoundryAITool.CreateOpenApiTool()` with `ChatClientAgent`
+- Server-side execution of OpenAPI tool calls
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+```powershell
+dotnet run
+```
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/FoundryAgents_Step01.2_Running.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/Agent_Step18_BingCustomSearch.csproj
similarity index 89%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/FoundryAgents_Step01.2_Running.csproj
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/Agent_Step18_BingCustomSearch.csproj
index daf7e24494..e11688b6ba 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/FoundryAgents_Step01.2_Running.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/Agent_Step18_BingCustomSearch.csproj
@@ -9,7 +9,6 @@
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/Program.cs
similarity index 54%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/Program.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/Program.cs
index 98ea576226..4ab548403d 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/Program.cs
@@ -1,15 +1,13 @@
// Copyright (c) Microsoft. All rights reserved.
-// This sample shows how to use Bing Custom Search Tool with AI Agents.
+// This sample shows how to use Bing Custom Search Tool with a ChatClientAgent.
using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
-using OpenAI.Responses;
+using Microsoft.Agents.AI.AzureAI;
-string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
string connectionId = Environment.GetEnvironmentVariable("AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID") ?? throw new InvalidOperationException("AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID is not set.");
string instanceName = Environment.GetEnvironmentVariable("AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME") ?? throw new InvalidOperationException("AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME is not set.");
@@ -18,19 +16,24 @@ const string AgentInstructions = """
Use the available Bing Custom Search tools to answer questions and perform tasks.
""";
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
+// Bing Custom Search tool parameters
+BingCustomSearchToolOptions bingCustomSearchToolParameters = new([
+ new BingCustomSearchConfiguration(connectionId, instanceName)
+]);
+
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-// Bing Custom Search tool parameters shared by both options
-BingCustomSearchToolOptions bingCustomSearchToolParameters = new([
- new BingCustomSearchConfiguration(connectionId, instanceName)
-]);
-
-AIAgent agent = await CreateAgentWithMEAIAsync();
-// AIAgent agent = await CreateAgentWithNativeSDKAsync();
+// Create a AIAgent with Bing Custom Search tool.
+AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: AgentInstructions,
+ name: "BingCustomSearchAgent-RAPI",
+ tools: [FoundryAITool.CreateBingCustomSearchTool(bingCustomSearchToolParameters)]);
Console.WriteLine($"Created agent: {agent.Name}");
@@ -42,35 +45,3 @@ foreach (var message in response.Messages)
{
Console.WriteLine(message.Text);
}
-
-// Cleanup by deleting the agent
-await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
-Console.WriteLine($"\nDeleted agent: {agent.Name}");
-
-// --- Agent Creation Options ---
-
-// Option 1 - Using AsAITool wrapping for the ResponseTool returned by AgentTool.CreateBingCustomSearchTool (MEAI + AgentFramework)
-async Task CreateAgentWithMEAIAsync()
-{
- return await aiProjectClient.CreateAIAgentAsync(
- model: deploymentName,
- name: "BingCustomSearchAgent-MEAI",
- instructions: AgentInstructions,
- tools: [((ResponseTool)AgentTool.CreateBingCustomSearchTool(bingCustomSearchToolParameters)).AsAITool()]);
-}
-
-// Option 2 - Using PromptAgentDefinition with AgentTool.CreateBingCustomSearchTool (Native SDK)
-async Task CreateAgentWithNativeSDKAsync()
-{
- return await aiProjectClient.CreateAIAgentAsync(
- name: "BingCustomSearchAgent-NATIVE",
- creationOptions: new AgentVersionCreationOptions(
- new PromptAgentDefinition(model: deploymentName)
- {
- Instructions = AgentInstructions,
- Tools = {
- (ResponseTool)AgentTool.CreateBingCustomSearchTool(bingCustomSearchToolParameters),
- }
- })
- );
-}
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/README.md
new file mode 100644
index 0000000000..fc48fd8744
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/README.md
@@ -0,0 +1,36 @@
+# Bing Custom Search with the Responses API
+
+This sample shows how to use the Bing Custom Search tool with a `ChatClientAgent` using the Responses API directly.
+
+## What this sample demonstrates
+
+- Configuring `BingCustomSearchToolParameters` with connection ID and instance name
+- Using `FoundryAITool.CreateBingCustomSearchTool()` with `ChatClientAgent`
+- Processing search results from agent responses
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+- Bing Custom Search resource configured with a connection ID
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+$env:AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID="your-connection-id" # The full ARM resource URI, e.g., "/subscriptions/.../connections/your-bing-connection"
+$env:AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME="your-instance-name" # The Bing Custom Search configuration name (from Azure portal)
+```
+
+### Finding the connection ID and instance name
+
+- **Connection ID** (`AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID`): The full ARM resource URI including the `/projects//connections/` segment. Find the connection name in your Foundry project under **Management center** → **Connected resources**.
+- **Instance Name** (`AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME`): The **configuration name** from your Bing Custom Search resource (Azure portal → your Bing Custom Search resource → **Configurations**). This is _not_ the Azure resource name or the connection name — it's the name of the specific search configuration that defines which domains/sites to search against.
+
+## Run the sample
+
+```powershell
+dotnet run
+```
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/Agent_Step19_SharePoint.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/Agent_Step19_SharePoint.csproj
new file mode 100644
index 0000000000..e11688b6ba
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/Agent_Step19_SharePoint.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net10.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/Program.cs
similarity index 53%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/Program.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/Program.cs
index ad6a08abaa..aafca6b8bd 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/Program.cs
@@ -1,15 +1,13 @@
// Copyright (c) Microsoft. All rights reserved.
-// This sample shows how to use SharePoint Grounding Tool with AI Agents.
+// This sample shows how to use SharePoint Grounding Tool with a ChatClientAgent.
using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
-using OpenAI.Responses;
+using Microsoft.Agents.AI.AzureAI;
-string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
string sharepointConnectionId = Environment.GetEnvironmentVariable("SHAREPOINT_PROJECT_CONNECTION_ID") ?? throw new InvalidOperationException("SHAREPOINT_PROJECT_CONNECTION_ID is not set.");
const string AgentInstructions = """
@@ -17,18 +15,23 @@ const string AgentInstructions = """
Use the available SharePoint tools to answer questions and perform tasks.
""";
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
+// Create SharePoint tool options with project connection
+var sharepointOptions = new SharePointGroundingToolOptions();
+sharepointOptions.ProjectConnections.Add(new ToolProjectConnection(sharepointConnectionId));
+
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-// Create SharePoint tool options with project connection
-var sharepointOptions = new SharePointGroundingToolOptions();
-sharepointOptions.ProjectConnections.Add(new ToolProjectConnection(sharepointConnectionId));
-
-AIAgent agent = await CreateAgentWithMEAIAsync();
-// AIAgent agent = await CreateAgentWithNativeSDKAsync();
+// Create a AIAgent with SharePoint tool.
+AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: AgentInstructions,
+ name: "SharePointAgent-RAPI",
+ tools: [FoundryAITool.CreateSharepointTool(sharepointOptions)]);
Console.WriteLine($"Created agent: {agent.Name}");
@@ -52,33 +55,3 @@ foreach (var message in response.Messages)
}
}
}
-
-// Cleanup by agent name removes the agent version created.
-await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
-Console.WriteLine($"\nDeleted agent: {agent.Name}");
-
-// --- Agent Creation Options ---
-
-// Option 1 - Using AgentTool.CreateSharepointTool + AsAITool() (MEAI + AgentFramework)
-async Task CreateAgentWithMEAIAsync()
-{
- return await aiProjectClient.CreateAIAgentAsync(
- model: deploymentName,
- name: "SharePointAgent-MEAI",
- instructions: AgentInstructions,
- tools: [((ResponseTool)AgentTool.CreateSharepointTool(sharepointOptions)).AsAITool()]);
-}
-
-// Option 2 - Using PromptAgentDefinition SDK native type
-async Task CreateAgentWithNativeSDKAsync()
-{
- return await aiProjectClient.CreateAIAgentAsync(
- name: "SharePointAgent-NATIVE",
- creationOptions: new AgentVersionCreationOptions(
- new PromptAgentDefinition(model: deploymentName)
- {
- Instructions = AgentInstructions,
- Tools = { AgentTool.CreateSharepointTool(sharepointOptions) }
- })
- );
-}
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/README.md
new file mode 100644
index 0000000000..bfaacee860
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/README.md
@@ -0,0 +1,30 @@
+# SharePoint Grounding with the Responses API
+
+This sample shows how to use the SharePoint Grounding tool with a `ChatClientAgent` using the Responses API directly.
+
+## What this sample demonstrates
+
+- Configuring `SharePointGroundingToolOptions` with project connections
+- Using `FoundryAITool.CreateSharepointTool()` with `ChatClientAgent`
+- Displaying grounding annotations from agent responses
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+- SharePoint connection configured in your Microsoft Foundry project
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+$env:SHAREPOINT_PROJECT_CONNECTION_ID="your-sharepoint-connection-id" # The full ARM resource URI, e.g., "/subscriptions/.../connections/SharepointTestTool"
+```
+
+## Run the sample
+
+```powershell
+dotnet run
+```
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/Agent_Step20_MicrosoftFabric.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/Agent_Step20_MicrosoftFabric.csproj
new file mode 100644
index 0000000000..e11688b6ba
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/Agent_Step20_MicrosoftFabric.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net10.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/Program.cs
new file mode 100644
index 0000000000..7c49afa7ee
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/Program.cs
@@ -0,0 +1,42 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to use Microsoft Fabric Tool with a ChatClientAgent.
+
+using Azure.AI.Projects;
+using Azure.AI.Projects.Agents;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.AzureAI;
+
+string fabricConnectionId = Environment.GetEnvironmentVariable("FABRIC_PROJECT_CONNECTION_ID") ?? throw new InvalidOperationException("FABRIC_PROJECT_CONNECTION_ID is not set.");
+
+const string AgentInstructions = "You are a helpful assistant with access to Microsoft Fabric data. Answer questions based on data available through your Fabric connection.";
+
+// Configure Microsoft Fabric tool options with project connection
+var fabricToolOptions = new FabricDataAgentToolOptions();
+fabricToolOptions.ProjectConnections.Add(new ToolProjectConnection(fabricConnectionId));
+
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
+// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
+// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
+AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
+
+// Create a AIAgent with Microsoft Fabric tool.
+AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: AgentInstructions,
+ name: "FabricAgent-RAPI",
+ tools: [FoundryAITool.CreateMicrosoftFabricTool(fabricToolOptions)]);
+
+Console.WriteLine($"Created agent: {agent.Name}");
+
+// Run the agent with a sample query
+AgentResponse response = await agent.RunAsync("What data is available in the connected Fabric workspace?");
+
+Console.WriteLine("\n=== Agent Response ===");
+foreach (var message in response.Messages)
+{
+ Console.WriteLine(message.Text);
+}
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/README.md
new file mode 100644
index 0000000000..4a4d31e151
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/README.md
@@ -0,0 +1,30 @@
+# Microsoft Fabric with the Responses API
+
+This sample shows how to use the Microsoft Fabric tool with a `ChatClientAgent` using the Responses API directly.
+
+## What this sample demonstrates
+
+- Configuring `FabricDataAgentToolOptions` with project connections
+- Using `FoundryAITool.CreateMicrosoftFabricTool()` with `ChatClientAgent`
+- Querying data available through a Fabric connection
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+- Microsoft Fabric connection configured in your Microsoft Foundry project
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+$env:FABRIC_PROJECT_CONNECTION_ID="your-fabric-connection-id" # The full ARM resource URI, e.g., "/subscriptions/.../connections/FabricTestTool"
+```
+
+## Run the sample
+
+```powershell
+dotnet run
+```
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/Agent_Step21_WebSearch.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/Agent_Step21_WebSearch.csproj
new file mode 100644
index 0000000000..e11688b6ba
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/Agent_Step21_WebSearch.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net10.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/Program.cs
new file mode 100644
index 0000000000..20d74b0401
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/Program.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to use the Web Search Tool with a ChatClientAgent.
+
+using Azure.AI.Projects;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+using OpenAI.Responses;
+
+const string AgentInstructions = "You are a helpful assistant that can search the web to find current information and answer questions accurately.";
+const string AgentName = "WebSearchAgent-RAPI";
+
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
+// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
+// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
+AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
+
+// Create a AIAgent with HostedWebSearchTool.
+AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: AgentInstructions,
+ name: AgentName,
+ tools: [new HostedWebSearchTool()]);
+
+AgentResponse response = await agent.RunAsync("What's the weather today in Seattle?");
+
+// Get the text response
+Console.WriteLine($"Response: {response.Text}");
+
+// Getting any annotations/citations generated by the web search tool
+foreach (AIAnnotation annotation in response.Messages.SelectMany(m => m.Contents).SelectMany(c => c.Annotations ?? []))
+{
+ Console.WriteLine($"Annotation: {annotation}");
+ if (annotation.RawRepresentation is UriCitationMessageAnnotation urlCitation)
+ {
+ Console.WriteLine($$"""
+ Title: {{urlCitation.Title}}
+ URL: {{urlCitation.Uri}}
+ """);
+ }
+}
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/README.md
new file mode 100644
index 0000000000..1a9d80106d
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/README.md
@@ -0,0 +1,28 @@
+# Web Search with the Responses API
+
+This sample shows how to use the Web Search tool with a `ChatClientAgent` using the Responses API directly.
+
+## What this sample demonstrates
+
+- Using `HostedWebSearchTool` with `ChatClientAgent`
+- Processing web search citations and annotations
+- Extracting URL citation details (title, URL) from responses
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+```powershell
+dotnet run
+```
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Agent_Step22_MemorySearch.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Agent_Step22_MemorySearch.csproj
new file mode 100644
index 0000000000..4602e9c9e0
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Agent_Step22_MemorySearch.csproj
@@ -0,0 +1,20 @@
+
+
+
+ Exe
+ net10.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Program.cs
similarity index 70%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/Program.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Program.cs
index 60452b7d19..9e1a902f29 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Program.cs
@@ -9,6 +9,8 @@ using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Extensions.AI;
using OpenAI.Responses;
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
@@ -22,27 +24,25 @@ const string AgentInstructions = """
When a user shares personal details or preferences, remember them for future conversations.
""";
-const string AgentNameMEAI = "MemorySearchAgent-MEAI";
-const string AgentNameNative = "MemorySearchAgent-NATIVE";
+const string AgentName = "MemorySearchAgent";
-// Scope identifies the user or context for memory isolation.
-// Using a unique user identifier ensures memories are private to that user.
string userScope = $"user_{Environment.MachineName}";
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
-DefaultAzureCredential credential = new();
-AIProjectClient aiProjectClient = new(new Uri(endpoint), credential);
+MemorySearchPreviewTool memorySearchTool = new(memoryStoreName, userScope) { UpdateDelayInSecs = 0 };
+// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
+// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
+// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
+AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
+
+// Create agent using the RAPI path with the MemorySearch tool
+AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
+ instructions: AgentInstructions,
+ name: AgentName,
+ tools: [FoundryAITool.FromResponseTool(memorySearchTool)]);
// Ensure the memory store exists and has memories to retrieve.
await EnsureMemoryStoreAsync();
-// Create the Memory Search tool configuration
-MemorySearchPreviewTool memorySearchTool = new(memoryStoreName, userScope) { UpdateDelayInSecs = 0 };
-
-// Create agent using Option 1 (MEAI) or Option 2 (Native SDK)
-AIAgent agent = await CreateAgentWithMEAI();
-// AIAgent agent = await CreateAgentWithNativeSDK();
-
try
{
Console.WriteLine("Agent created with Memory Search tool. Starting conversation...\n");
@@ -73,41 +73,14 @@ try
}
finally
{
- // Cleanup: Delete the agent and memory store.
+ // Cleanup: Delete the memory store (no server-side agent to clean up in RAPI path).
Console.WriteLine("\nCleaning up...");
- await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
- Console.WriteLine("Agent deleted.");
await aiProjectClient.MemoryStores.DeleteMemoryStoreAsync(memoryStoreName);
Console.WriteLine("Memory store deleted.");
}
-#pragma warning disable CS8321 // Local function is declared but never used
-
-// Option 1 - Using MemorySearchTool wrapped as MEAI AITool
-async Task CreateAgentWithMEAI()
-{
- return await aiProjectClient.CreateAIAgentAsync(
- model: deploymentName,
- name: AgentNameMEAI,
- instructions: AgentInstructions,
- tools: [((ResponseTool)memorySearchTool).AsAITool()]);
-}
-
-// Option 2 - Using PromptAgentDefinition with MemorySearchTool (Native SDK)
-async Task CreateAgentWithNativeSDK()
-{
- return await aiProjectClient.CreateAIAgentAsync(
- name: AgentNameNative,
- creationOptions: new AgentVersionCreationOptions(
- new PromptAgentDefinition(model: deploymentName)
- {
- Instructions = AgentInstructions,
- Tools = { memorySearchTool }
- })
- );
-}
-
// Helpers — kept at the bottom so the main agent flow above stays clean.
+
async Task EnsureMemoryStoreAsync()
{
Console.WriteLine($"Creating memory store '{memoryStoreName}'...");
@@ -123,19 +96,37 @@ async Task EnsureMemoryStoreAsync()
Console.WriteLine("Memory store created.");
}
+ // Explicitly add memories from a simulated prior conversation.
Console.WriteLine("Storing memories from a prior conversation...");
MemoryUpdateOptions memoryOptions = new(userScope) { UpdateDelay = 0 };
- memoryOptions.Items.Add(ResponseItem.CreateUserMessageItem("My name is Alice and I love programming in C#."));
+ memoryOptions.Items.Add(ResponseItem.CreateUserMessageItem("My name is Alice and I prefer C#."));
MemoryUpdateResult updateResult = await aiProjectClient.MemoryStores.WaitForMemoriesUpdateAsync(
memoryStoreName: memoryStoreName,
- pollingInterval: 500,
- options: memoryOptions);
+ options: memoryOptions,
+ pollingInterval: 500);
if (updateResult.Status == MemoryStoreUpdateStatus.Failed)
{
throw new InvalidOperationException($"Memory update failed: {updateResult.ErrorDetails}");
}
- Console.WriteLine($"Memory update completed (status: {updateResult.Status}).\n");
+ Console.WriteLine($"Memory update completed (status: {updateResult.Status}).");
+
+ // Quick verification that memories are searchable.
+ Console.WriteLine("Verifying stored memories...");
+ MemorySearchOptions searchOptions = new(userScope)
+ {
+ Items = { ResponseItem.CreateUserMessageItem("What are Alice's preferences?") }
+ };
+ MemoryStoreSearchResponse searchResult = await aiProjectClient.MemoryStores.SearchMemoriesAsync(
+ memoryStoreName: memoryStoreName,
+ options: searchOptions);
+
+ foreach (var memory in searchResult.Memories)
+ {
+ Console.WriteLine($" - {memory.MemoryItem.Content}");
+ }
+
+ Console.WriteLine();
}
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/README.md
new file mode 100644
index 0000000000..b8020553af
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/README.md
@@ -0,0 +1,31 @@
+# Memory Search with the Responses API
+
+This sample demonstrates how to use the Memory Search tool with a `ChatClientAgent` using the Responses API directly.
+
+## What this sample demonstrates
+
+- Configuring `MemorySearchPreviewTool` with a memory store and user scope
+- Using memory search for cross-conversation recall
+- Inspecting `MemorySearchToolCallResponseItem` results
+- User profile persistence across conversations
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+- A memory store created beforehand via Azure Portal or Python SDK
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+$env:AZURE_AI_MEMORY_STORE_ID="your-memory-store-name"
+```
+
+## Run the sample
+
+```powershell
+dotnet run
+```
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/Agent_Step23_LocalMCP.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/Agent_Step23_LocalMCP.csproj
new file mode 100644
index 0000000000..e51f57c439
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/Agent_Step23_LocalMCP.csproj
@@ -0,0 +1,20 @@
+
+
+
+ Exe
+ net10.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/Program.cs
similarity index 50%
rename from dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/Program.cs
rename to dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/Program.cs
index d41771ef37..c9f8c0060b 100644
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/Program.cs
@@ -1,24 +1,20 @@
// Copyright (c) Microsoft. All rights reserved.
-// This sample demonstrates how to use a local MCP (Model Context Protocol) client with Azure Foundry Agents.
-// The MCP tools are resolved locally by connecting directly to the MCP server via HTTP,
-// and then passed to the Foundry agent as client-side tools.
-// This sample uses the Microsoft Learn MCP endpoint to search documentation.
+// This sample demonstrates how to wrap MCP tools with a DelegatingAIFunction to add custom behavior (e.g., logging).
+// Compare with Step09 which shows basic MCP tool usage without wrapping.
+// The LoggingMcpTool pattern is useful for diagnostics, metering, or adding approval logic around tool calls.
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using ModelContextProtocol.Client;
-
-string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+using SampleApp;
const string AgentInstructions = "You are a helpful assistant that can help with Microsoft documentation questions. Use the Microsoft Learn MCP tool to search for documentation.";
-const string AgentName = "DocsAgent";
+const string AgentName = "DocsAgent-RAPI";
// Connect to the MCP server locally via HTTP (Streamable HTTP transport).
-// The MCP server is hosted at Microsoft Learn and provides documentation search capabilities.
Console.WriteLine("Connecting to MCP server at https://learn.microsoft.com/api/mcp ...");
await using McpClient mcpClient = await McpClient.CreateAsync(new HttpClientTransport(new()
@@ -34,53 +30,48 @@ Console.WriteLine($"MCP tools available: {string.Join(", ", mcpTools.Select(t =>
// Wrap each MCP tool with a DelegatingAIFunction to log local invocations.
List wrappedTools = mcpTools.Select(tool => (AITool)new LoggingMcpTool(tool)).ToList();
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-// Create the agent with the locally-resolved MCP tools.
-AIAgent agent = await aiProjectClient.CreateAIAgentAsync(
- model: deploymentName,
- name: AgentName,
+// Create a AIAgent with the locally-resolved MCP tools.
+AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
instructions: AgentInstructions,
+ name: AgentName,
tools: wrappedTools);
Console.WriteLine($"Agent '{agent.Name}' created successfully.");
-try
-{
- // First query
- const string Prompt1 = "How does one create an Azure storage account using az cli?";
- Console.WriteLine($"\nUser: {Prompt1}\n");
- AgentResponse response1 = await agent.RunAsync(Prompt1);
- Console.WriteLine($"Agent: {response1}");
+// First query
+const string Prompt1 = "How does one create an Azure storage account using az cli?";
+Console.WriteLine($"\nUser: {Prompt1}\n");
+AgentResponse response1 = await agent.RunAsync(Prompt1);
+Console.WriteLine($"Agent: {response1}");
- Console.WriteLine("\n=======================================\n");
+Console.WriteLine("\n=======================================\n");
- // Second query
- const string Prompt2 = "What is Microsoft Agent Framework?";
- Console.WriteLine($"User: {Prompt2}\n");
- AgentResponse response2 = await agent.RunAsync(Prompt2);
- Console.WriteLine($"Agent: {response2}");
-}
-finally
-{
- // Cleanup by removing the agent when done
- await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
- Console.WriteLine($"\nAgent '{agent.Name}' deleted.");
-}
+// Second query
+const string Prompt2 = "What is Microsoft Agent Framework?";
+Console.WriteLine($"User: {Prompt2}\n");
+AgentResponse response2 = await agent.RunAsync(Prompt2);
+Console.WriteLine($"Agent: {response2}");
-///
-/// Wraps an MCP tool to log when it is invoked locally,
-/// confirming that the MCP call is happening client-side.
-///
-internal sealed class LoggingMcpTool(AIFunction innerFunction) : DelegatingAIFunction(innerFunction)
+namespace SampleApp
{
- protected override ValueTask InvokeCoreAsync(AIFunctionArguments arguments, CancellationToken cancellationToken)
+ ///
+ /// Wraps an MCP tool to log when it is invoked locally,
+ /// confirming that the MCP call is happening client-side.
+ ///
+ internal sealed class LoggingMcpTool(AIFunction innerFunction) : DelegatingAIFunction(innerFunction)
{
- Console.WriteLine($" >> [LOCAL MCP] Invoking tool '{this.Name}' locally...");
- return base.InvokeCoreAsync(arguments, cancellationToken);
+ protected override ValueTask InvokeCoreAsync(AIFunctionArguments arguments, CancellationToken cancellationToken)
+ {
+ Console.WriteLine($" >> [LOCAL MCP] Invoking tool '{this.Name}' locally...");
+ return base.InvokeCoreAsync(arguments, cancellationToken);
+ }
}
}
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/README.md
new file mode 100644
index 0000000000..84456df2b7
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/README.md
@@ -0,0 +1,29 @@
+# Local MCP with the Responses API
+
+This sample demonstrates how to use a local MCP (Model Context Protocol) client with a `ChatClientAgent` using the Responses API directly.
+
+## What this sample demonstrates
+
+- Connecting to an MCP server via HTTP (Streamable HTTP transport)
+- Resolving MCP tools locally and wrapping them with logging
+- Using `DelegatingAIFunction` to add custom behavior to MCP tools
+- Passing locally-resolved MCP tools to `ChatClientAgent`
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Microsoft Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (`az login`)
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## Run the sample
+
+```powershell
+dotnet run
+```
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/README.md
new file mode 100644
index 0000000000..b5802053b3
--- /dev/null
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/README.md
@@ -0,0 +1,81 @@
+# Getting started with Foundry Agents
+
+These samples demonstrate how to use Azure AI Foundry with Agent Framework.
+
+## Quick start
+
+The simplest way to create a Foundry agent is using the `FoundryAgent` type directly:
+
+```csharp
+FoundryAgent agent = new(
+ new Uri(endpoint),
+ new AzureCliCredential(),
+ model: "gpt-4o-mini",
+ instructions: "You are good at telling jokes.",
+ name: "JokerAgent");
+
+Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
+```
+
+Or using the `AIProjectClient.AsAIAgent(...)` extensions:
+
+```csharp
+AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
+
+FoundryAgent agent = aiProjectClient.AsAIAgent(
+ model: deploymentName,
+ instructions: "You are good at telling jokes.",
+ name: "JokerAgent");
+```
+
+## Prerequisites
+
+- .NET 10 SDK or later
+- Foundry project endpoint
+- Azure CLI installed and authenticated
+
+Set:
+
+```powershell
+$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
+$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+Some samples require extra tool-specific environment variables. See each sample for details.
+
+## Samples
+
+| Sample | Description |
+| --- | --- |
+| [FoundryAgent lifecycle](./Agent_Step00_FoundryAgentLifecycle/) | Create a FoundryAgent directly with endpoint and credentials |
+| [Basics (Responses API)](./Agent_Step01_Basics/) | Create and run an agent using AsAIAgent extensions |
+| [Multi-turn conversation](./Agent_Step02.1_MultiturnConversation/) | Multi-turn using sessions and response ID chaining |
+| [Multi-turn with server conversations](./Agent_Step02.2_MultiturnWithServerConversations/) | Server-side conversations visible in Foundry UI |
+| [Using function tools](./Agent_Step03_UsingFunctionTools/) | Function tools |
+| [Function tools with approvals](./Agent_Step04_UsingFunctionToolsWithApprovals/) | Human-in-the-loop approval |
+| [Structured output](./Agent_Step05_StructuredOutput/) | Structured output with JSON schema |
+| [Persisted conversations](./Agent_Step06_PersistedConversations/) | Persisting and resuming conversations |
+| [Observability](./Agent_Step07_Observability/) | OpenTelemetry observability |
+| [Dependency injection](./Agent_Step08_DependencyInjection/) | DI with a hosted service |
+| [Using MCP client as tools](./Agent_Step09_UsingMcpClientAsTools/) | MCP client tools |
+| [Using images](./Agent_Step10_UsingImages/) | Image multi-modality |
+| [Agent as function tool](./Agent_Step11_AsFunctionTool/) | Agent as a function tool for another |
+| [Middleware](./Agent_Step12_Middleware/) | Multiple middleware layers |
+| [Plugins](./Agent_Step13_Plugins/) | Plugins with dependency injection |
+| [Code interpreter](./Agent_Step14_CodeInterpreter/) | Code interpreter tool |
+| [Computer use](./Agent_Step15_ComputerUse/) | Computer use tool |
+| [File search](./Agent_Step16_FileSearch/) | File search tool |
+| [OpenAPI tools](./Agent_Step17_OpenAPITools/) | OpenAPI tools |
+| [Bing custom search](./Agent_Step18_BingCustomSearch/) | Bing Custom Search tool |
+| [SharePoint](./Agent_Step19_SharePoint/) | SharePoint grounding tool |
+| [Microsoft Fabric](./Agent_Step20_MicrosoftFabric/) | Microsoft Fabric tool |
+| [Web search](./Agent_Step21_WebSearch/) | Web search tool |
+| [Memory search](./Agent_Step22_MemorySearch/) | Memory search tool |
+| [Local MCP](./Agent_Step23_LocalMCP/) | Local MCP client with HTTP transport |
+
+## Running the samples
+
+```powershell
+cd dotnet/samples/02-agents/AgentsWithFoundry
+dotnet run --project .\FoundryAgent_Step01
+```
\ No newline at end of file
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/Program.cs
deleted file mode 100644
index 1e1e48d54b..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/Program.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-// This sample demonstrates how to use Azure AI Foundry's Red Teaming service to assess
-// the safety and resilience of an AI model against adversarial attacks.
-//
-// It uses the RedTeam API from Azure.AI.Projects to run automated attack simulations
-// with various attack strategies (encoding, obfuscation, jailbreaks) across multiple
-// risk categories (Violence, HateUnfairness, Sexual, SelfHarm).
-//
-// For more details, see:
-// https://learn.microsoft.com/azure/ai-foundry/concepts/ai-red-teaming-agent
-
-using Azure.AI.Projects;
-using Azure.Identity;
-
-string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-
-Console.WriteLine("=" + new string('=', 79));
-Console.WriteLine("RED TEAMING EVALUATION SAMPLE");
-Console.WriteLine("=" + new string('=', 79));
-Console.WriteLine();
-
-// Initialize Azure credentials and clients
-// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
-// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
-// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
-DefaultAzureCredential credential = new();
-AIProjectClient aiProjectClient = new(new Uri(endpoint), credential);
-
-// Configure the target model for red teaming
-AzureOpenAIModelConfiguration targetConfig = new(deploymentName);
-
-// Create the red team run configuration
-RedTeam redTeamConfig = new(targetConfig)
-{
- DisplayName = "FinancialAdvisor-RedTeam",
- ApplicationScenario = "A financial advisor assistant that provides general financial advice and information.",
- NumTurns = 3,
- RiskCategories =
- {
- RiskCategory.Violence,
- RiskCategory.HateUnfairness,
- RiskCategory.Sexual,
- RiskCategory.SelfHarm,
- },
- AttackStrategies =
- {
- AttackStrategy.Easy,
- AttackStrategy.Moderate,
- AttackStrategy.Jailbreak,
- },
-};
-
-Console.WriteLine($"Target model: {deploymentName}");
-Console.WriteLine("Risk categories: Violence, HateUnfairness, Sexual, SelfHarm");
-Console.WriteLine("Attack strategies: Easy, Moderate, Jailbreak");
-Console.WriteLine($"Simulation turns: {redTeamConfig.NumTurns}");
-Console.WriteLine();
-
-// Submit the red team run to the service
-Console.WriteLine("Submitting red team run...");
-RedTeam redTeamRun = await aiProjectClient.RedTeams.CreateAsync(redTeamConfig, options: null);
-
-Console.WriteLine($"Red team run created: {redTeamRun.Name}");
-Console.WriteLine($"Status: {redTeamRun.Status}");
-Console.WriteLine();
-
-// Poll for completion
-Console.WriteLine("Waiting for red team run to complete (this may take several minutes)...");
-while (redTeamRun.Status != "Completed" && redTeamRun.Status != "Failed" && redTeamRun.Status != "Canceled")
-{
- await Task.Delay(TimeSpan.FromSeconds(15));
- redTeamRun = await aiProjectClient.RedTeams.GetAsync(redTeamRun.Name);
- Console.WriteLine($" Status: {redTeamRun.Status}");
-}
-
-Console.WriteLine();
-
-if (redTeamRun.Status == "Completed")
-{
- Console.WriteLine("Red team run completed successfully!");
- Console.WriteLine();
- Console.WriteLine("Results:");
- Console.WriteLine(new string('-', 80));
- Console.WriteLine($" Run name: {redTeamRun.Name}");
- Console.WriteLine($" Display name: {redTeamRun.DisplayName}");
- Console.WriteLine($" Status: {redTeamRun.Status}");
-
- Console.WriteLine();
- Console.WriteLine("Review the detailed results in the Azure AI Foundry portal:");
- Console.WriteLine($" {endpoint}");
-}
-else
-{
- Console.WriteLine($"Red team run ended with status: {redTeamRun.Status}");
-}
-
-Console.WriteLine();
-Console.WriteLine(new string('=', 80));
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/README.md
deleted file mode 100644
index 24e4a62b35..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/README.md
+++ /dev/null
@@ -1,101 +0,0 @@
-# Red Teaming with Azure AI Foundry (Classic)
-
-> [!IMPORTANT]
-> This sample uses the **classic Azure AI Foundry** red teaming API (`/redTeams/runs`) via `Azure.AI.Projects`. Results are viewable in the classic Foundry portal experience. The **new Foundry** portal's red teaming feature uses a different evaluation-based API that is not yet available in the .NET SDK.
-
-This sample demonstrates how to use Azure AI Foundry's Red Teaming service to assess the safety and resilience of an AI model against adversarial attacks.
-
-## What this sample demonstrates
-
-- Configuring a red team run targeting an Azure OpenAI model deployment
-- Using multiple `AttackStrategy` options (Easy, Moderate, Jailbreak)
-- Evaluating across `RiskCategory` categories (Violence, HateUnfairness, Sexual, SelfHarm)
-- Submitting a red team scan and polling for completion
-- Reviewing results in the Azure AI Foundry portal
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure AI Foundry project (hub and project created)
-- Azure OpenAI deployment (e.g., gpt-4o or gpt-4o-mini)
-- Azure CLI installed and authenticated (for Azure credential authentication)
-
-### Regional Requirements
-
-Red teaming is only available in regions that support risk and safety evaluators:
-- **East US 2**, **Sweden Central**, **US North Central**, **France Central**, **Switzerland West**
-
-### Environment Variables
-
-Set the following environment variables:
-
-```powershell
-$env:AZURE_AI_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/api/projects/your-project" # Replace with your Azure Foundry project endpoint
-$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini
-```
-
-## Run the sample
-
-Navigate to the sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming
-dotnet run
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Configure a `RedTeam` run targeting the specified model deployment
-2. Define risk categories and attack strategies
-3. Submit the scan to Azure AI Foundry's Red Teaming service
-4. Poll for completion (this may take several minutes)
-5. Display the run status and direct you to the Azure AI Foundry portal for detailed results
-
-## Understanding Red Teaming
-
-### Attack Strategies
-
-| Strategy | Description |
-|----------|-------------|
-| Easy | Simple encoding/obfuscation attacks (ROT13, Leetspeak, etc.) |
-| Moderate | Moderate complexity attacks requiring an LLM for orchestration |
-| Jailbreak | Crafted prompts designed to bypass AI safeguards (UPIA) |
-
-### Risk Categories
-
-| Category | Description |
-|----------|-------------|
-| Violence | Content related to violence |
-| HateUnfairness | Hate speech or unfair content |
-| Sexual | Sexual content |
-| SelfHarm | Self-harm related content |
-
-### Interpreting Results
-
-- Results are available in the Azure AI Foundry portal (**classic view** — toggle at top-right) under the red teaming section
-- Lower Attack Success Rate (ASR) is better — target ASR < 5% for production
-- Review individual attack conversations to understand vulnerabilities
-
-### Current Limitations
-
-> [!NOTE]
-> - The .NET Red Teaming API (`Azure.AI.Projects`) currently supports targeting **model deployments only** via `AzureOpenAIModelConfiguration`. The `AzureAIAgentTarget` type exists in the SDK but is consumed by the **Evaluation Taxonomy** API (`/evaluationtaxonomies`), not by the Red Teaming API (`/redTeams/runs`).
-> - Agent-targeted red teaming with agent-specific risk categories (Prohibited actions, Sensitive data leakage, Task adherence) is documented in the [concept docs](https://learn.microsoft.com/azure/ai-foundry/concepts/ai-red-teaming-agent) but is not yet available via the public REST API or .NET SDK.
-> - Results from this API appear in the **classic** Azure AI Foundry portal view. The new Foundry portal uses a separate evaluation-based system with `eval_*` identifiers.
-
-## Related Resources
-
-- [Azure AI Red Teaming Agent](https://learn.microsoft.com/azure/ai-foundry/concepts/ai-red-teaming-agent)
-- [RedTeam .NET API Reference](https://learn.microsoft.com/dotnet/api/azure.ai.projects.redteam?view=azure-dotnet-preview)
-- [Risk and Safety Evaluations](https://learn.microsoft.com/azure/ai-foundry/concepts/evaluation-metrics-built-in#risk-and-safety-evaluators)
-
-## Next Steps
-
-After running red teaming:
-1. Review attack results and strengthen agent guardrails
-2. Explore the Self-Reflection sample (FoundryAgents_Evaluations_Step02_SelfReflection) for quality assessment
-3. Set up continuous red teaming in your CI/CD pipeline
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/FoundryAgents_Evaluations_Step02_SelfReflection.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/FoundryAgents_Evaluations_Step02_SelfReflection.csproj
deleted file mode 100644
index 646cd75532..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/FoundryAgents_Evaluations_Step02_SelfReflection.csproj
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
- Exe
- net10.0
-
- enable
- enable
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/Program.cs
deleted file mode 100644
index 8f8c9fa4ee..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/Program.cs
+++ /dev/null
@@ -1,292 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-// This sample demonstrates how to use Microsoft.Extensions.AI.Evaluation.Quality to evaluate
-// an Agent Framework agent's response quality with a self-reflection loop.
-//
-// It uses GroundednessEvaluator, RelevanceEvaluator, and CoherenceEvaluator to score responses,
-// then iteratively asks the agent to improve based on evaluation feedback.
-//
-// Based on: Reflexion: Language Agents with Verbal Reinforcement Learning (NeurIPS 2023)
-// Reference: https://arxiv.org/abs/2303.11366
-//
-// For more details, see:
-// https://learn.microsoft.com/dotnet/ai/evaluation/libraries
-
-using Azure.AI.OpenAI;
-using Azure.AI.Projects;
-using Azure.Identity;
-using Microsoft.Agents.AI;
-using Microsoft.Extensions.AI;
-using Microsoft.Extensions.AI.Evaluation;
-using Microsoft.Extensions.AI.Evaluation.Quality;
-using Microsoft.Extensions.AI.Evaluation.Safety;
-
-using ChatMessage = Microsoft.Extensions.AI.ChatMessage;
-using ChatRole = Microsoft.Extensions.AI.ChatRole;
-
-string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-string openAiEndpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
-string evaluatorDeploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? deploymentName;
-
-Console.WriteLine("=" + new string('=', 79));
-Console.WriteLine("SELF-REFLECTION EVALUATION SAMPLE");
-Console.WriteLine("=" + new string('=', 79));
-Console.WriteLine();
-
-// Initialize Azure credentials and client
-// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
-// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
-// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
-DefaultAzureCredential credential = new();
-AIProjectClient aiProjectClient = new(new Uri(endpoint), credential);
-
-// Set up the LLM-based chat client for quality evaluators
-IChatClient chatClient = new AzureOpenAIClient(new Uri(openAiEndpoint), credential)
- .GetChatClient(evaluatorDeploymentName)
- .AsIChatClient();
-
-// Configure evaluation: quality evaluators use the LLM, safety evaluators use Azure AI Foundry
-ContentSafetyServiceConfiguration safetyConfig = new(
- credential: credential,
- endpoint: new Uri(endpoint));
-
-ChatConfiguration chatConfiguration = safetyConfig.ToChatConfiguration(
- originalChatConfiguration: new ChatConfiguration(chatClient));
-
-// Create a test agent
-AIAgent agent = await aiProjectClient.CreateAIAgentAsync(
- name: "KnowledgeAgent",
- model: deploymentName,
- instructions: "You are a helpful assistant. Answer questions accurately based on the provided context.");
-Console.WriteLine($"Created agent: {agent.Name}");
-Console.WriteLine();
-
-// Example question and grounding context
-const string Question = """
- What are the main benefits of using Azure AI Foundry for building AI applications?
- """;
-
-const string Context = """
- Azure AI Foundry is a comprehensive platform for building, deploying, and managing AI applications.
- Key benefits include:
- 1. Unified development environment with support for multiple AI frameworks and models
- 2. Built-in safety and security features including content filtering and red teaming tools
- 3. Scalable infrastructure that handles deployment and monitoring automatically
- 4. Integration with Azure services like Azure OpenAI, Cognitive Services, and Machine Learning
- 5. Evaluation tools for assessing model quality, safety, and performance
- 6. Support for RAG (Retrieval-Augmented Generation) patterns with vector search
- 7. Enterprise-grade compliance and governance features
- """;
-
-Console.WriteLine("Question:");
-Console.WriteLine(Question);
-Console.WriteLine();
-
-// Run evaluations
-try
-{
- await RunSelfReflectionWithGroundedness(agent, Question, Context, chatConfiguration);
- await RunQualityEvaluation(agent, Question, Context, chatConfiguration);
- await RunCombinedQualityAndSafetyEvaluation(agent, Question, chatConfiguration);
-}
-finally
-{
- // Cleanup
- await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
- Console.WriteLine();
- Console.WriteLine("Cleanup: Agent deleted.");
-}
-
-// ============================================================================
-// Implementation Functions
-// ============================================================================
-
-static async Task RunSelfReflectionWithGroundedness(
- AIAgent agent, string question, string context, ChatConfiguration chatConfiguration)
-{
- Console.WriteLine("Running Self-Reflection with Groundedness Evaluation...");
- Console.WriteLine();
-
- GroundednessEvaluator groundednessEvaluator = new();
- GroundednessEvaluatorContext groundingContext = new(context);
-
- const int MaxReflections = 3;
- double bestScore = 0;
-
- string currentPrompt = $"Context: {context}\n\nQuestion: {question}";
-
- for (int i = 0; i < MaxReflections; i++)
- {
- Console.WriteLine($"Iteration {i + 1}/{MaxReflections}:");
- Console.WriteLine(new string('-', 40));
-
- // Create a new session for each reflection iteration so that
- // conversation context does not carry over between runs. This keeps
- // each evaluation independent and avoids biasing groundedness scores.
- AgentSession session = await agent.CreateSessionAsync();
- AgentResponse agentResponse = await agent.RunAsync(currentPrompt, session);
- string responseText = agentResponse.Text;
-
- Console.WriteLine($"Response: {responseText[..Math.Min(150, responseText.Length)]}...");
-
- List messages =
- [
- new(ChatRole.User, currentPrompt),
- ];
- ChatResponse chatResponse = new(new ChatMessage(ChatRole.Assistant, responseText));
-
- EvaluationResult result = await groundednessEvaluator.EvaluateAsync(
- messages,
- chatResponse,
- chatConfiguration,
- additionalContext: [groundingContext]);
-
- NumericMetric groundedness = result.Get(GroundednessEvaluator.GroundednessMetricName);
- double score = groundedness.Value ?? 0;
- string rating = groundedness.Interpretation?.Rating.ToString() ?? "N/A";
-
- Console.WriteLine($"Groundedness score: {score:F1}/5 (Rating: {rating})");
- Console.WriteLine();
-
- if (score > bestScore)
- {
- bestScore = score;
- }
-
- if (score >= 4.0 || i == MaxReflections - 1)
- {
- if (score >= 4.0)
- {
- Console.WriteLine("Good groundedness achieved!");
- }
-
- break;
- }
-
- // Ask for improvement in the next iteration, including the previous response
- // so the LLM knows what to improve on (each iteration uses a new session).
- currentPrompt = $"""
- Context: {context}
-
- Your previous answer scored {score}/5 on groundedness.
- Your previous answer was:
- {responseText}
-
- Please improve your answer to be more grounded in the provided context.
- Only include information that is directly supported by the context.
-
- Question: {question}
- """;
- Console.WriteLine("Requesting improvement...");
- Console.WriteLine();
- }
-
- Console.WriteLine($"Best groundedness score: {bestScore:F1}/5");
- Console.WriteLine(new string('=', 80));
- Console.WriteLine();
-}
-
-static async Task RunQualityEvaluation(
- AIAgent agent, string question, string context, ChatConfiguration chatConfiguration)
-{
- Console.WriteLine("Running Quality Evaluation (Relevance, Coherence, Groundedness)...");
- Console.WriteLine();
-
- IEvaluator[] evaluators =
- [
- new RelevanceEvaluator(),
- new CoherenceEvaluator(),
- new GroundednessEvaluator(),
- ];
-
- CompositeEvaluator compositeEvaluator = new(evaluators);
- GroundednessEvaluatorContext groundingContext = new(context);
-
- string prompt = $"Context: {context}\n\nQuestion: {question}";
-
- AgentSession session = await agent.CreateSessionAsync();
- AgentResponse agentResponse = await agent.RunAsync(prompt, session);
- string responseText = agentResponse.Text;
-
- Console.WriteLine($"Response: {responseText[..Math.Min(150, responseText.Length)]}...");
- Console.WriteLine();
-
- List messages =
- [
- new(ChatRole.User, prompt),
- ];
- ChatResponse chatResponse = new(new ChatMessage(ChatRole.Assistant, responseText));
-
- EvaluationResult result = await compositeEvaluator.EvaluateAsync(
- messages,
- chatResponse,
- chatConfiguration,
- additionalContext: [groundingContext]);
-
- foreach (EvaluationMetric metric in result.Metrics.Values)
- {
- if (metric is NumericMetric n)
- {
- string rating = n.Interpretation?.Rating.ToString() ?? "N/A";
- Console.WriteLine($" {n.Name,-20} Score: {n.Value:F1}/5 Rating: {rating}");
- }
- }
-
- Console.WriteLine(new string('=', 80));
- Console.WriteLine();
-}
-
-static async Task RunCombinedQualityAndSafetyEvaluation(
- AIAgent agent, string question, ChatConfiguration chatConfiguration)
-{
- Console.WriteLine("Running Combined Quality + Safety Evaluation...");
- Console.WriteLine();
-
- IEvaluator[] evaluators =
- [
- new RelevanceEvaluator(),
- new CoherenceEvaluator(),
- new ContentHarmEvaluator(),
- new ProtectedMaterialEvaluator(),
- ];
-
- CompositeEvaluator compositeEvaluator = new(evaluators);
-
- AgentSession session = await agent.CreateSessionAsync();
- AgentResponse agentResponse = await agent.RunAsync(question, session);
- string responseText = agentResponse.Text;
-
- Console.WriteLine($"Response: {responseText[..Math.Min(150, responseText.Length)]}...");
- Console.WriteLine();
-
- List messages =
- [
- new(ChatRole.User, question), // No context in this evaluation — testing quality and safety on raw question
- ];
- ChatResponse chatResponse = new(new ChatMessage(ChatRole.Assistant, responseText));
-
- EvaluationResult result = await compositeEvaluator.EvaluateAsync(
- messages,
- chatResponse,
- chatConfiguration);
-
- Console.WriteLine("Quality Metrics:");
- foreach (EvaluationMetric metric in result.Metrics.Values)
- {
- if (metric is NumericMetric n)
- {
- string rating = n.Interpretation?.Rating.ToString() ?? "N/A";
- bool failed = n.Interpretation?.Failed ?? false;
- Console.WriteLine($" {n.Name,-25} Score: {n.Value:F1,-6} Rating: {rating,-15} Failed: {failed}");
- }
- else if (metric is BooleanMetric b)
- {
- string rating = b.Interpretation?.Rating.ToString() ?? "N/A";
- bool failed = b.Interpretation?.Failed ?? false;
- Console.WriteLine($" {b.Name,-25} Value: {b.Value,-6} Rating: {rating,-15} Failed: {failed}");
- }
- }
-
- Console.WriteLine(new string('=', 80));
-}
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/README.md
deleted file mode 100644
index d71eeca6af..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/README.md
+++ /dev/null
@@ -1,118 +0,0 @@
-# Self-Reflection Evaluation with Groundedness Assessment
-
-This sample demonstrates the self-reflection pattern using Agent Framework with `Microsoft.Extensions.AI.Evaluation.Quality` evaluators. The agent iteratively improves its responses based on real groundedness evaluation scores.
-
-For details on the self-reflection approach, see [Reflexion: Language Agents with Verbal Reinforcement Learning](https://arxiv.org/abs/2303.11366) (NeurIPS 2023).
-
-## What this sample demonstrates
-
-- Self-reflection loop that improves responses using real `GroundednessEvaluator` scores
-- Using `RelevanceEvaluator` and `CoherenceEvaluator` for multi-metric quality assessment
-- Combining quality and safety evaluators with `CompositeEvaluator`
-- Configuring `ContentSafetyServiceConfiguration` for safety evaluators alongside LLM-based quality evaluators
-- Tracking improvement across iterations
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure AI Foundry project (hub and project created)
-- Azure OpenAI deployment (e.g., gpt-4o or gpt-4o-mini)
-- 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).
-
-### Azure Resources Required
-
-1. **Azure AI Hub and Project**: Create these in the Azure Portal
- - Follow: https://learn.microsoft.com/azure/ai-foundry/how-to/create-projects
-2. **Azure OpenAI Deployment**: Deploy a model (e.g., gpt-4o or gpt-4o-mini)
- - Agent model: Used to generate responses
- - Evaluator model: Quality evaluators use an LLM; best results with GPT-4o
-3. **Azure CLI**: Install and authenticate with `az login`
-
-### Environment Variables
-
-Set the following environment variables:
-
-```powershell
-$env:AZURE_AI_PROJECT_ENDPOINT="https://your-project.api.azureml.ms" # Azure Foundry project endpoint
-$env:AZURE_OPENAI_ENDPOINT="https://your-openai.openai.azure.com/" # Azure OpenAI endpoint (for quality evaluators)
-$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Model deployment name
-```
-
-**Note**: For best evaluation results, use GPT-4o or GPT-4o-mini as the evaluator model. The groundedness evaluator has been tested and tuned for these models.
-
-## Run the sample
-
-Navigate to the sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection
-dotnet run
-```
-
-## Expected behavior
-
-The sample runs three evaluation scenarios:
-
-### 1. Self-Reflection with Groundedness
-- Asks a question with grounding context
-- Evaluates response groundedness using `GroundednessEvaluator`
-- If score is below 4/5, asks the agent to improve with feedback
-- Repeats up to 3 iterations
-- Tracks and reports the best score achieved
-
-### 2. Quality Evaluation
-- Evaluates a single response with multiple quality evaluators:
- - `RelevanceEvaluator` — is the response relevant to the question?
- - `CoherenceEvaluator` — is the response logically coherent?
- - `GroundednessEvaluator` — is the response grounded in the provided context?
-
-### 3. Combined Quality + Safety Evaluation
-- Runs both quality and safety evaluators together:
- - `RelevanceEvaluator`, `CoherenceEvaluator` (quality)
- - `ContentHarmEvaluator` (safety — violence, hate, sexual, self-harm)
- - `ProtectedMaterialEvaluator` (safety — copyrighted content detection)
-
-## Understanding the Evaluation
-
-### Groundedness Score (1-5 scale)
-
-The `GroundednessEvaluator` measures how well the agent's response is grounded in the provided context:
-
-- **5** = Excellent - Response is fully grounded in context
-- **4** = Good - Mostly grounded with minor deviations
-- **3** = Fair - Partially grounded but includes unsupported claims
-- **2** = Poor - Significant amount of ungrounded content
-- **1** = Very Poor - Response is largely unsupported by context
-
-### Self-Reflection Process
-
-1. **Initial Response**: Agent generates answer based on question + context
-2. **Evaluation**: `GroundednessEvaluator` scores the response (1-5)
-3. **Feedback**: If score < 4, agent receives the score and is asked to improve
-4. **Iteration**: Process repeats until good score or max iterations
-
-## Best Practices
-
-1. **Provide Complete Context**: Ensure grounding context contains all information needed to answer the question
-2. **Clear Instructions**: Give the agent clear instructions about staying grounded in context
-3. **Use Quality Models**: GPT-4o recommended for evaluation tasks
-4. **Multiple Evaluators**: Use combination of evaluators (groundedness + relevance + coherence)
-5. **Batch Processing**: For production, process multiple questions in batch
-
-## Related Resources
-
-- [Reflexion Paper (NeurIPS 2023)](https://arxiv.org/abs/2303.11366)
-- [Microsoft.Extensions.AI.Evaluation Libraries](https://learn.microsoft.com/dotnet/ai/evaluation/libraries)
-- [GroundednessEvaluator API Reference](https://learn.microsoft.com/dotnet/api/microsoft.extensions.ai.evaluation.quality.groundednessevaluator)
-- [Azure AI Foundry Evaluation Service](https://learn.microsoft.com/azure/ai-foundry/how-to/develop/evaluate-sdk)
-
-## Next Steps
-
-After running self-reflection evaluation:
-1. Implement similar patterns for other quality metrics (relevance, coherence, fluency)
-2. Integrate into CI/CD pipeline for continuous quality assurance
-3. Explore the Safety Evaluation sample (FoundryAgents_Evaluations_Step01_RedTeaming) for content safety assessment
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/Program.cs
deleted file mode 100644
index f4521d8898..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/Program.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-// This sample shows how to create and use AI agents with Azure Foundry Agents as the backend.
-
-using Azure.AI.Projects;
-using Azure.AI.Projects.Agents;
-using Azure.Identity;
-using Microsoft.Agents.AI;
-
-string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-
-const string JokerName = "JokerAgent";
-
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
-// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
-// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
-// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
-AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-
-// Define the agent you want to create. (Prompt Agent in this case)
-AgentVersionCreationOptions options = new(new PromptAgentDefinition(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.
-AgentVersion createdAgentVersion = aiProjectClient.Agents.CreateAgentVersion(agentName: JokerName, options);
-
-// Note:
-// agentVersion.Id = ":",
-// agentVersion.Version = ,
-// agentVersion.Name =
-
-// You can use an AIAgent with an already created server side agent version.
-AIAgent existingJokerAgent = aiProjectClient.AsAIAgent(createdAgentVersion);
-
-// You can also create another AIAgent version by providing the same name with a different definition/instruction.
-AIAgent newJokerAgent = await aiProjectClient.CreateAIAgentAsync(name: JokerName, model: deploymentName, instructions: "You are extremely hilarious at telling jokes.");
-
-// You can also get the AIAgent latest version by just providing its name.
-AIAgent jokerAgentLatest = await aiProjectClient.GetAIAgentAsync(name: JokerName);
-AgentVersion latestAgentVersion = jokerAgentLatest.GetService()!;
-
-// The AIAgent version can be accessed via the GetService method.
-Console.WriteLine($"Latest agent version id: {latestAgentVersion.Id}");
-
-// Once you have the AIAgent, you can invoke it like any other AIAgent.
-Console.WriteLine(await jokerAgentLatest.RunAsync("Tell me a joke about a pirate."));
-
-// Cleanup by agent name removes both agent versions created.
-await aiProjectClient.Agents.DeleteAgentAsync(existingJokerAgent.Name);
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/README.md
deleted file mode 100644
index ce5eca8277..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/README.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# Creating and Managing AI Agents with Versioning
-
-This sample demonstrates how to create and manage AI agents with Azure Foundry Agents, including:
-- Creating agents with different versions
-- Retrieving agents by version or latest version
-- Running multi-turn conversations with agents
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure 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).
-
-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
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step01.1_Basics
-```
-
-## What this sample demonstrates
-
-1. **Creating agents with versions**: Shows how to create multiple versions of the same agent with different instructions
-2. **Retrieving agents**: Demonstrates retrieving agents by specific version or getting the latest version
-3. **Multi-turn conversations**: Shows how to use threads to maintain conversation context across multiple agent runs
-4. **Agent cleanup**: Demonstrates proper resource cleanup by deleting agents
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/Program.cs
deleted file mode 100644
index 0bc17aff0a..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/Program.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-// This sample shows how to create and use a simple AI agent with Azure Foundry Agents as the backend.
-
-using Azure.AI.Projects;
-using Azure.AI.Projects.Agents;
-using Azure.Identity;
-using Microsoft.Agents.AI;
-
-string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-
-const string JokerInstructions = "You are good at telling jokes.";
-const string JokerName = "JokerAgent";
-
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
-// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
-// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
-// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
-AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-
-// Define the agent you want to create. (Prompt Agent in this case)
-AgentVersionCreationOptions options = new(new PromptAgentDefinition(model: deploymentName) { Instructions = JokerInstructions });
-
-// 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.
-AgentVersion agentVersion = aiProjectClient.Agents.CreateAgentVersion(agentName: JokerName, options);
-
-// You can use an AIAgent with an already created server side agent version.
-AIAgent jokerAgent = aiProjectClient.AsAIAgent(agentVersion);
-
-// Invoke the agent with streaming support.
-await foreach (AgentResponseUpdate update in jokerAgent.RunStreamingAsync("Tell me a joke about a pirate."))
-{
- Console.WriteLine(update);
-}
-
-// Cleanup by agent name removes the agent version created.
-await aiProjectClient.Agents.DeleteAgentAsync(jokerAgent.Name);
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/README.md
deleted file mode 100644
index 40cb5e107d..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/README.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Running a Simple AI Agent with Streaming
-
-This sample demonstrates how to create and run a simple AI agent with Azure Foundry Agents, including both text and streaming responses.
-
-## What this sample demonstrates
-
-- Creating a simple AI agent with instructions
-- Running an agent with text output
-- Running an agent with streaming output
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure 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).
-
-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
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step01.2_Running
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create an agent named "JokerAgent" with instructions to tell jokes
-2. Run the agent with a text prompt and display the response
-3. Run the agent again with streaming to display the response as it's generated
-4. Clean up resources by deleting the agent
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs
deleted file mode 100644
index 7bf12094fc..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-// This sample shows how to create and use a simple AI agent with a multi-turn conversation.
-
-using Azure.AI.Extensions.OpenAI;
-using Azure.AI.Projects;
-using Azure.AI.Projects.Agents;
-using Azure.Identity;
-using Microsoft.Agents.AI;
-
-string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-
-const string JokerInstructions = "You are good at telling jokes.";
-const string JokerName = "JokerAgent";
-
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
-// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
-// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
-// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
-AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-
-// Define the agent you want to create. (Prompt Agent in this case)
-AgentVersionCreationOptions options = new(new PromptAgentDefinition(model: deploymentName) { Instructions = JokerInstructions });
-
-// Retrieve an AIAgent for the created server side agent version.
-ChatClientAgent jokerAgent = await aiProjectClient.CreateAIAgentAsync(name: JokerName, options);
-
-// Invoke the agent with a multi-turn conversation, where the context is preserved in the session object.
-// Create a conversation in the server
-ProjectConversationsClient conversationsClient = aiProjectClient.GetProjectOpenAIClient().GetProjectConversationsClient();
-ProjectConversation conversation = await conversationsClient.CreateProjectConversationAsync();
-
-// Providing the conversation Id is not strictly necessary, but by not providing it no information will show up in the Foundry Project UI as conversations.
-// Sessions that don't have a conversation Id will work based on the `PreviousResponseId`.
-AgentSession session = await jokerAgent.CreateSessionAsync(conversation.Id);
-
-Console.WriteLine(await jokerAgent.RunAsync("Tell me a joke about a pirate.", session));
-Console.WriteLine(await jokerAgent.RunAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", session));
-
-// Invoke the agent with a multi-turn conversation and streaming, where the context is preserved in the session object.
-session = await jokerAgent.CreateSessionAsync(conversation.Id);
-await foreach (AgentResponseUpdate update in jokerAgent.RunStreamingAsync("Tell me a joke about a pirate.", session))
-{
- Console.WriteLine(update);
-}
-await foreach (AgentResponseUpdate update in jokerAgent.RunStreamingAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", session))
-{
- Console.WriteLine(update);
-}
-
-// Cleanup by agent name removes the agent version created.
-await aiProjectClient.Agents.DeleteAgentAsync(jokerAgent.Name);
-
-// Cleanup the conversation created.
-await conversationsClient.DeleteConversationAsync(conversation.Id);
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/README.md
deleted file mode 100644
index 86721bf960..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/README.md
+++ /dev/null
@@ -1,59 +0,0 @@
-# Multi-turn Conversation with AI Agents
-
-This sample demonstrates how to implement multi-turn conversations with AI agents, where context is preserved across multiple agent runs using threads and conversation IDs.
-
-## What this sample demonstrates
-
-- Creating an AI agent with instructions
-- Creating a project conversation to track conversations in the Foundry UI
-- Using threads with conversation IDs to maintain conversation context
-- Running multi-turn conversations with text output
-- Running multi-turn conversations with streaming output
-- Managing agent and conversation lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure 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).
-
-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
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step02_MultiturnConversation
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create an agent named "JokerAgent" with instructions to tell jokes
-2. Create a project conversation to enable visibility in the Azure Foundry UI
-3. Create a thread linked to the conversation ID for context tracking
-4. Run the agent with a text prompt and display the response
-5. Send a follow-up message to the same thread, demonstrating context preservation
-6. Create a new thread sharing the same conversation ID and run the agent with streaming
-7. Send a follow-up streaming message to demonstrate multi-turn streaming
-8. Clean up resources by deleting the agent and conversation
-
-## Conversation ID vs PreviousResponseId
-
-When working with multi-turn conversations, there are two approaches:
-
-- **With Conversation ID**: By passing a `conversation.Id` to `CreateSessionAsync()`, the conversation will be visible in the Azure Foundry Project UI. This is useful for tracking and debugging conversations.
-- **Without Conversation ID**: Sessions created without a conversation ID still work correctly, maintaining context via `PreviousResponseId`. However, these conversations may not appear in the Foundry UI.
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/Program.cs
deleted file mode 100644
index cfd74000a6..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/Program.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-// This sample demonstrates how to use an agent with function tools.
-// It shows both non-streaming and streaming agent interactions using weather-related tools.
-
-using System.ComponentModel;
-using Azure.AI.Projects;
-using Azure.Identity;
-using Microsoft.Agents.AI;
-using Microsoft.Extensions.AI;
-
-string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-
-[Description("Get the weather for a given location.")]
-static string GetWeather([Description("The location to get the weather for.")] string location)
- => $"The weather in {location} is cloudy with a high of 15°C.";
-
-const string AssistantInstructions = "You are a helpful assistant that can get weather information.";
-const string AssistantName = "WeatherAssistant";
-
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
-// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
-// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
-// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
-AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-
-// Define the agent with function tools.
-AITool tool = AIFunctionFactory.Create(GetWeather);
-
-// Create AIAgent directly
-var newAgent = await aiProjectClient.CreateAIAgentAsync(name: AssistantName, model: deploymentName, instructions: AssistantInstructions, tools: [tool]);
-
-// Getting an already existing agent by name with tools.
-/*
- * IMPORTANT: Since agents that are stored in the server only know the definition of the function tools (JSON Schema),
- * you need to provided all invocable function tools when retrieving the agent so it can invoke them automatically.
- * If no invocable tools are provided, the function calling needs to handled manually.
- */
-var existingAgent = await aiProjectClient.GetAIAgentAsync(name: AssistantName, tools: [tool]);
-
-// Non-streaming agent interaction with function tools.
-AgentSession session = await existingAgent.CreateSessionAsync();
-Console.WriteLine(await existingAgent.RunAsync("What is the weather like in Amsterdam?", session));
-
-// Streaming agent interaction with function tools.
-session = await existingAgent.CreateSessionAsync();
-await foreach (AgentResponseUpdate update in existingAgent.RunStreamingAsync("What is the weather like in Amsterdam?", session))
-{
- Console.WriteLine(update);
-}
-
-// Cleanup by agent name removes the agent version created.
-await aiProjectClient.Agents.DeleteAgentAsync(existingAgent.Name);
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/README.md
deleted file mode 100644
index fa9b5baf21..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/README.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Using Function Tools with AI Agents
-
-This sample demonstrates how to use function tools with AI agents, allowing agents to call custom functions to retrieve information.
-
-## What this sample demonstrates
-
-- Creating function tools using AIFunctionFactory
-- Passing function tools to an AI agent
-- Running agents with function tools (text output)
-- Running agents with function tools (streaming output)
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure 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).
-
-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
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step03.1_UsingFunctionTools
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create an agent named "WeatherAssistant" with a GetWeather function tool
-2. Run the agent with a text prompt asking about weather
-3. The agent will invoke the GetWeather function tool to retrieve weather information
-4. Run the agent again with streaming to display the response as it's generated
-5. Clean up resources by deleting the agent
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/README.md
deleted file mode 100644
index 42cbd6ba32..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/README.md
+++ /dev/null
@@ -1,51 +0,0 @@
-# Using Function Tools with Approvals (Human-in-the-Loop)
-
-This sample demonstrates how to use function tools that require human approval before execution, implementing a human-in-the-loop workflow.
-
-## What this sample demonstrates
-
-- Creating approval-required function tools using ApprovalRequiredAIFunction
-- Handling user input requests for function approvals
-- Implementing human-in-the-loop approval workflows
-- Processing agent responses with pending approvals
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure 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).
-
-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
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step04_UsingFunctionToolsWithApprovals
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create an agent named "WeatherAssistant" with an approval-required GetWeather function tool
-2. Run the agent with a prompt asking about weather
-3. The agent will request approval before invoking the GetWeather function
-4. The sample will prompt the user to approve or deny the function call (enter 'Y' to approve)
-5. After approval, the function will be executed and the result returned to the agent
-6. Clean up resources by deleting the agent
-
-**Note**: For hosted agents with remote users, combine this sample with the Persisted Conversations sample to persist chat history while waiting for user approval.
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/FoundryAgents_Step05_StructuredOutput.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/FoundryAgents_Step05_StructuredOutput.csproj
deleted file mode 100644
index daf7e24494..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/FoundryAgents_Step05_StructuredOutput.csproj
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
- Exe
- net10.0
-
- enable
- enable
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/README.md
deleted file mode 100644
index 4c44230e18..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/README.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Structured Output with AI Agents
-
-This sample demonstrates how to configure AI agents to produce structured output in JSON format using JSON schemas.
-
-## What this sample demonstrates
-
-- Configuring agents with JSON schema response formats
-- Using generic RunAsync method for structured output
-- Deserializing structured responses into typed objects
-- Running agents with streaming and structured output
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure 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).
-
-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
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step05_StructuredOutput
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create an agent named "StructuredOutputAssistant" configured to produce JSON output
-2. Run the agent with a prompt to extract person information
-3. Deserialize the JSON response into a PersonInfo object
-4. Display the structured data (Name, Age, Occupation)
-5. Run the agent again with streaming and deserialize the streamed JSON response
-6. Clean up resources by deleting the agent
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/FoundryAgents_Step06_PersistedConversations.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/FoundryAgents_Step06_PersistedConversations.csproj
deleted file mode 100644
index daf7e24494..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/FoundryAgents_Step06_PersistedConversations.csproj
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
- Exe
- net10.0
-
- enable
- enable
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/README.md
deleted file mode 100644
index 57a032e9ec..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/README.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# Persisted Conversations with AI Agents
-
-This sample demonstrates how to serialize and persist agent conversation threads to storage, allowing conversations to be resumed later.
-
-## What this sample demonstrates
-
-- Serializing agent threads to JSON
-- Persisting thread state to disk
-- Loading and deserializing thread state from storage
-- Resuming conversations with persisted threads
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure 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).
-
-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
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step06_PersistedConversations
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create an agent named "JokerAgent" with instructions to tell jokes
-2. Create a thread and run the agent with an initial prompt
-3. Serialize the thread state to JSON
-4. Save the serialized thread to a temporary file
-5. Load the thread from the file and deserialize it
-6. Resume the conversation with the same thread using a follow-up prompt
-7. Clean up resources by deleting the agent
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/README.md
deleted file mode 100644
index 459434bce2..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/README.md
+++ /dev/null
@@ -1,51 +0,0 @@
-# Observability with OpenTelemetry
-
-This sample demonstrates how to add observability to AI agents using OpenTelemetry for tracing and monitoring.
-
-## What this sample demonstrates
-
-- Setting up OpenTelemetry TracerProvider
-- Configuring console exporter for telemetry output
-- Configuring Azure Monitor exporter for Application Insights
-- Adding OpenTelemetry middleware to agents
-- Running agents with telemetry collection (text and streaming)
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure Foundry service endpoint and deployment configured
-- Azure CLI installed and authenticated (for Azure credential authentication)
-- (Optional) Application Insights connection string for Azure Monitor integration
-
-**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).
-
-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:APPLICATIONINSIGHTS_CONNECTION_STRING="your-connection-string" # Optional, for Azure Monitor integration
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step07_Observability
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create a TracerProvider with console exporter (and optionally Azure Monitor exporter)
-2. Create an agent named "JokerAgent" with OpenTelemetry middleware
-3. Run the agent with a text prompt and display telemetry traces to console
-4. Run the agent again with streaming and display telemetry traces
-5. Clean up resources by deleting the agent
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/Program.cs
deleted file mode 100644
index b7a9874e7b..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/Program.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-// This sample shows how to use dependency injection to register an AIAgent and use it from a hosted service with a user input chat loop.
-
-using System.ClientModel;
-using Azure.AI.Projects;
-using Azure.Identity;
-using Microsoft.Agents.AI;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-
-string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-
-const string JokerInstructions = "You are good at telling jokes.";
-const string JokerName = "JokerAgent";
-
-// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
-// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
-// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
-AIProjectClient aIProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-
-// Create a new agent if one doesn't exist already.
-ChatClientAgent agent;
-try
-{
- agent = await aIProjectClient.GetAIAgentAsync(name: JokerName);
-}
-catch (ClientResultException ex) when (ex.Status == 404)
-{
- agent = await aIProjectClient.CreateAIAgentAsync(name: JokerName, model: deploymentName, instructions: JokerInstructions);
-}
-
-// Create a host builder that we will register services with and then run.
-HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
-
-// Add the agents client to the service collection.
-builder.Services.AddSingleton((sp) => aIProjectClient);
-
-// Add the AI agent to the service collection.
-builder.Services.AddSingleton((sp) => agent);
-
-// Add a sample service that will use the agent to respond to user input.
-builder.Services.AddHostedService();
-
-// Build and run the host.
-using IHost host = builder.Build();
-await host.RunAsync().ConfigureAwait(false);
-
-///
-/// A sample service that uses an AI agent to respond to user input.
-///
-internal sealed class SampleService(AIProjectClient client, AIAgent agent, IHostApplicationLifetime appLifetime) : IHostedService
-{
- private AgentSession? _session;
-
- public async Task StartAsync(CancellationToken cancellationToken)
- {
- // Create a session that will be used for the entirety of the service lifetime so that the user can ask follow up questions.
- this._session = await agent.CreateSessionAsync(cancellationToken);
- _ = this.RunAsync(appLifetime.ApplicationStopping);
- }
-
- public async Task RunAsync(CancellationToken cancellationToken)
- {
- // Delay a little to allow the service to finish starting.
- await Task.Delay(100, cancellationToken);
-
- while (!cancellationToken.IsCancellationRequested)
- {
- Console.WriteLine("\nAgent: Ask me to tell you a joke about a specific topic. To exit just press Ctrl+C or enter without any input.\n");
- Console.Write("> ");
- string? input = Console.ReadLine();
-
- // If the user enters no input, signal the application to shut down.
- if (string.IsNullOrWhiteSpace(input))
- {
- appLifetime.StopApplication();
- break;
- }
-
- // Stream the output to the console as it is generated.
- await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(input, this._session, cancellationToken: cancellationToken))
- {
- Console.Write(update);
- }
-
- Console.WriteLine();
- }
- }
-
- public async Task StopAsync(CancellationToken cancellationToken)
- {
- Console.WriteLine("\nDeleting agent ...");
- await client.Agents.DeleteAgentAsync(agent.Name, cancellationToken).ConfigureAwait(false);
- }
-}
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/README.md
deleted file mode 100644
index 12760e736f..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/README.md
+++ /dev/null
@@ -1,51 +0,0 @@
-# Dependency Injection with AI Agents
-
-This sample demonstrates how to use dependency injection to register and manage AI agents within a hosted service application.
-
-## What this sample demonstrates
-
-- Setting up dependency injection with HostApplicationBuilder
-- Registering AIProjectClient as a singleton service
-- Registering AIAgent as a singleton service
-- Using agents in hosted services
-- Interactive chat loop with streaming responses
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure 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).
-
-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
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step08_DependencyInjection
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create a host with dependency injection configured
-2. Register AIProjectClient and AIAgent as services
-3. Create an agent named "JokerAgent" with instructions to tell jokes
-4. Start an interactive chat loop where you can ask the agent questions
-5. The agent will respond with streaming output
-6. Enter an empty line or press Ctrl+C to exit
-7. Clean up resources by deleting the agent
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/FoundryAgents_Step09_UsingMcpClientAsTools.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/FoundryAgents_Step09_UsingMcpClientAsTools.csproj
deleted file mode 100644
index a6d96cb3db..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/FoundryAgents_Step09_UsingMcpClientAsTools.csproj
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
- Exe
- net10.0
-
- enable
- enable
- 3afc9b74-af74-4d8e-ae96-fa1c511d11ac
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/Program.cs
deleted file mode 100644
index e1968122a4..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/Program.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-// This sample shows how to expose an AI agent as an MCP tool.
-
-using Azure.AI.Projects;
-using Azure.Identity;
-using Microsoft.Agents.AI;
-using Microsoft.Extensions.AI;
-using ModelContextProtocol.Client;
-
-string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-
-Console.WriteLine("Starting MCP Stdio for @modelcontextprotocol/server-github ... ");
-
-// Create an MCPClient for the GitHub server
-await using var mcpClient = await McpClient.CreateAsync(new StdioClientTransport(new()
-{
- Name = "MCPServer",
- Command = "npx",
- Arguments = ["-y", "--verbose", "@modelcontextprotocol/server-github"],
-}));
-
-// Retrieve the list of tools available on the GitHub server
-IList mcpTools = await mcpClient.ListToolsAsync();
-string agentName = "AgentWithMCP";
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
-// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
-// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
-// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
-AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-
-Console.WriteLine($"Creating the agent '{agentName}' ...");
-
-// Define the agent you want to create. (Prompt Agent in this case)
-AIAgent agent = await aiProjectClient.CreateAIAgentAsync(
- name: agentName,
- model: deploymentName,
- instructions: "You answer questions related to GitHub repositories only.",
- tools: [.. mcpTools.Cast()]);
-
-string prompt = "Summarize the last four commits to the microsoft/semantic-kernel repository?";
-
-Console.WriteLine($"Invoking agent '{agent.Name}' with prompt: {prompt} ...");
-
-// Invoke the agent and output the text result.
-Console.WriteLine(await agent.RunAsync(prompt));
-
-// Clean up the agent after use.
-await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/README.md
deleted file mode 100644
index e4e3fe537a..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/README.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# Using MCP Client Tools with AI Agents
-
-This sample demonstrates how to use Model Context Protocol (MCP) client tools with AI agents, allowing agents to access tools provided by MCP servers. This sample uses the GitHub MCP server to provide tools for querying GitHub repositories.
-
-## What this sample demonstrates
-
-- Creating MCP clients to connect to MCP servers (GitHub server)
-- Retrieving tools from MCP servers
-- Using MCP tools with AI agents
-- Running agents with MCP-provided function tools
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure Foundry service endpoint and deployment configured
-- Azure CLI installed and authenticated (for Azure credential authentication)
-- Node.js and npm installed (for running the GitHub MCP server)
-
-**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).
-
-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
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step09_UsingMcpClientAsTools
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Start the GitHub MCP server using `@modelcontextprotocol/server-github`
-2. Create an MCP client to connect to the GitHub server
-3. Retrieve the available tools from the GitHub MCP server
-4. Create an agent named "AgentWithMCP" with the GitHub tools
-5. Run the agent with a prompt to summarize the last four commits to the microsoft/semantic-kernel repository
-6. The agent will use the GitHub MCP tools to query the repository information
-7. Clean up resources by deleting the agent
\ No newline at end of file
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/FoundryAgents_Step10_UsingImages.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/FoundryAgents_Step10_UsingImages.csproj
deleted file mode 100644
index 53661ff199..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/FoundryAgents_Step10_UsingImages.csproj
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
- Exe
- net10.0
-
- enable
- enable
-
-
-
-
-
-
-
-
-
-
-
-
-
- Always
-
-
-
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/README.md
deleted file mode 100644
index 220104a291..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/README.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# Using Images with AI Agents
-
-This sample demonstrates how to use image multi-modality with an AI agent. It shows how to create a vision-enabled agent that can analyze and describe images using Azure Foundry Agents.
-
-## What this sample demonstrates
-
-- Creating a vision-enabled AI agent with image analysis capabilities
-- Sending both text and image content to an agent in a single message
-- Using `UriContent` for URI-referenced images
-- Processing multimodal input (text + image) with an AI agent
-- Managing agent lifecycle (creation and deletion)
-
-## Key features
-
-- **Vision Agent**: Creates an agent specifically instructed to analyze images
-- **Multimodal Input**: Combines text questions with image URI in a single message
-- **Azure Foundry Agents Integration**: Uses Azure Foundry Agents with vision capabilities
-
-## Prerequisites
-
-Before running this sample, ensure you have:
-
-1. An Azure OpenAI project set up
-2. A compatible model deployment (e.g., gpt-4o)
-3. Azure CLI installed and authenticated
-
-## Environment Variables
-
-Set the following environment variables:
-
-```powershell
-$env:AZURE_AI_PROJECT_ENDPOINT="https://your-resource.openai.azure.com/" # Replace with your Azure Foundry Project endpoint
-$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o" # Replace with your model deployment name (optional, defaults to gpt-4o)
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step10_UsingImages
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create a vision-enabled agent named "VisionAgent"
-2. Send a message containing both text ("What do you see in this image?") and a URI-referenced image of a green walkway (nature boardwalk)
-3. The agent will analyze the image and provide a description
-4. Clean up resources by deleting the agent
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/FoundryAgents_Step11_AsFunctionTool.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/FoundryAgents_Step11_AsFunctionTool.csproj
deleted file mode 100644
index 54f37f1aa6..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/FoundryAgents_Step11_AsFunctionTool.csproj
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- Exe
- net10.0
-
- enable
- enable
- 3afc9b74-af74-4d8e-ae96-fa1c511d11ac
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/README.md
deleted file mode 100644
index 5da59b6edb..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/README.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Using AI Agents as Function Tools (Nested Agents)
-
-This sample demonstrates how to expose an AI agent as a function tool, enabling nested agent scenarios where one agent can invoke another agent as a tool.
-
-## What this sample demonstrates
-
-- Creating an AI agent that can be used as a function tool
-- Wrapping an agent as an AIFunction
-- Using nested agents where one agent calls another
-- Managing multiple agent instances
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure 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).
-
-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
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step11_AsFunctionTool
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create a "JokerAgent" that tells jokes
-2. Wrap the JokerAgent as a function tool
-3. Create a "CoordinatorAgent" that has the JokerAgent as a function tool
-4. Run the CoordinatorAgent with a prompt that triggers it to call the JokerAgent
-5. The CoordinatorAgent will invoke the JokerAgent as a function tool
-6. Clean up resources by deleting both agents
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/README.md
deleted file mode 100644
index 96d12d9828..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/README.md
+++ /dev/null
@@ -1,58 +0,0 @@
-# Agent Middleware
-
-This sample demonstrates how to add middleware to intercept agent runs and function calls to implement cross-cutting concerns like logging, validation, and guardrails.
-
-## What This Sample Shows
-
-1. Azure Foundry Agents integration via `AIProjectClient` and `DefaultAzureCredential`
-2. Agent run middleware (logging and monitoring)
-3. Function invocation middleware (logging and overriding tool results)
-4. Per-request agent run middleware
-5. Per-request function pipeline with approval
-6. Combining agent-level and per-request middleware
-
-## Function Invocation Middleware
-
-Not all agents support function invocation middleware.
-
-Attempting to use function middleware on agents that do not wrap a ChatClientAgent or derives from it will throw an InvalidOperationException.
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure 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).
-
-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
-```
-
-## Running the Sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step12_Middleware
-```
-
-## Expected Behavior
-
-When you run this sample, you will see the following demonstrations:
-
-1. **Example 1: Wording Guardrail** - The agent receives a request for harmful content. The guardrail middleware intercepts the request and prevents the agent from responding to harmful prompts, returning a safe response instead.
-
-2. **Example 2: PII Detection** - The agent receives a message containing personally identifiable information (name, phone number, email). The PII middleware detects and filters this sensitive information before processing.
-
-3. **Example 3: Agent Function Middleware** - The agent uses function tools (GetDateTime and GetWeather) to answer a question about the current time and weather in Seattle. The function middleware logs the function calls and can override results if needed.
-
-4. **Example 4: Human-in-the-Loop Function Approval** - The agent attempts to call a weather function, but the approval middleware intercepts the call and prompts the user to approve or deny the function invocation before it executes. The user can respond with "Y" to approve or any other input to deny.
-
-Each example demonstrates how middleware can be used to implement cross-cutting concerns and control agent behavior at different levels (agent-level and per-request).
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/FoundryAgents_Step13_Plugins.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/FoundryAgents_Step13_Plugins.csproj
deleted file mode 100644
index 4a34560946..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/FoundryAgents_Step13_Plugins.csproj
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
- Exe
- net10.0
-
- enable
- enable
- $(NoWarn);CA1812
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/Program.cs
deleted file mode 100644
index 244d83d632..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/Program.cs
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-// This sample shows how to use plugins with an AI agent. Plugin classes can
-// depend on other services that need to be injected. In this sample, the
-// AgentPlugin class uses the WeatherProvider and CurrentTimeProvider classes
-// to get weather and current time information. Both services are registered
-// in the service collection and injected into the plugin.
-// Plugin classes may have many methods, but only some are intended to be used
-// as AI functions. The AsAITools method of the plugin class shows how to specify
-// which methods should be exposed to the AI agent.
-
-using Azure.AI.Projects;
-using Azure.Identity;
-using Microsoft.Agents.AI;
-using Microsoft.Extensions.AI;
-using Microsoft.Extensions.DependencyInjection;
-
-string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-
-const string AssistantInstructions = "You are a helpful assistant that helps people find information.";
-const string AssistantName = "PluginAssistant";
-
-// Create a service collection to hold the agent plugin and its dependencies.
-ServiceCollection services = new();
-services.AddSingleton();
-services.AddSingleton();
-services.AddSingleton(); // The plugin depends on WeatherProvider and CurrentTimeProvider registered above.
-
-IServiceProvider serviceProvider = services.BuildServiceProvider();
-
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
-// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
-// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
-// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
-AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-
-// Define the agent with plugin tools
-// Define the agent you want to create. (Prompt Agent in this case)
-AIAgent agent = await aiProjectClient.CreateAIAgentAsync(
- name: AssistantName,
- model: deploymentName,
- instructions: AssistantInstructions,
- tools: serviceProvider.GetRequiredService().AsAITools().ToList(),
- services: serviceProvider);
-
-// Invoke the agent and output the text result.
-AgentSession session = await agent.CreateSessionAsync();
-Console.WriteLine(await agent.RunAsync("Tell me current time and weather in Seattle.", session));
-
-// Cleanup by agent name removes the agent version created.
-await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
-
-///
-/// The agent plugin that provides weather and current time information.
-///
-/// The weather provider to get weather information.
-internal sealed class AgentPlugin(WeatherProvider weatherProvider)
-{
- ///
- /// Gets the weather information for the specified location.
- ///
- ///
- /// This method demonstrates how to use the dependency that was injected into the plugin class.
- ///
- /// The location to get the weather for.
- /// The weather information for the specified location.
- public string GetWeather(string location)
- {
- return weatherProvider.GetWeather(location);
- }
-
- ///
- /// Gets the current date and time for the specified location.
- ///
- ///
- /// This method demonstrates how to resolve a dependency using the service provider passed to the method.
- ///
- /// The service provider to resolve the .
- /// The location to get the current time for.
- /// The current date and time as a .
- public DateTimeOffset GetCurrentTime(IServiceProvider sp, string location)
- {
- // Resolve the CurrentTimeProvider from the service provider
- CurrentTimeProvider currentTimeProvider = sp.GetRequiredService();
-
- return currentTimeProvider.GetCurrentTime(location);
- }
-
- ///
- /// Returns the functions provided by this plugin.
- ///
- ///
- /// In real world scenarios, a class may have many methods and only a subset of them may be intended to be exposed as AI functions.
- /// This method demonstrates how to explicitly specify which methods should be exposed to the AI agent.
- ///
- /// The functions provided by this plugin.
- public IEnumerable AsAITools()
- {
- yield return AIFunctionFactory.Create(this.GetWeather);
- yield return AIFunctionFactory.Create(this.GetCurrentTime);
- }
-}
-
-///
-/// The weather provider that returns weather information.
-///
-internal sealed class WeatherProvider
-{
- ///
- /// Gets the weather information for the specified location.
- ///
- ///
- /// The weather information is hardcoded for demonstration purposes.
- /// In a real application, this could call a weather API to get actual weather data.
- ///
- /// The location to get the weather for.
- /// The weather information for the specified location.
- public string GetWeather(string location)
- {
- return $"The weather in {location} is cloudy with a high of 15°C.";
- }
-}
-
-///
-/// Provides the current date and time.
-///
-///
-/// This class returns the current date and time using the system's clock.
-///
-internal sealed class CurrentTimeProvider
-{
- ///
- /// Gets the current date and time.
- ///
- /// The location to get the current time for (not used in this implementation).
- /// The current date and time as a .
- public DateTimeOffset GetCurrentTime(string location)
- {
- return DateTimeOffset.Now;
- }
-}
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/README.md
deleted file mode 100644
index 5c52ffcd1c..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/README.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Using Plugins with AI Agents
-
-This sample demonstrates how to use plugins with AI agents, where plugins are services registered in dependency injection that expose methods as AI function tools.
-
-## What this sample demonstrates
-
-- Creating plugin services with methods to expose as tools
-- Using AsAITools() to selectively expose plugin methods
-- Registering plugins in dependency injection
-- Using plugins with AI agents
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure 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).
-
-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
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step13_Plugins
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create a plugin service with methods to expose as tools
-2. Register the plugin in dependency injection
-3. Create an agent named "PluginAgent" with the plugin methods as function tools
-4. Run the agent with a prompt that triggers it to call plugin methods
-5. The agent will invoke the plugin methods to retrieve information
-6. Clean up resources by deleting the agent
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/FoundryAgents_Step14_CodeInterpreter.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/FoundryAgents_Step14_CodeInterpreter.csproj
deleted file mode 100644
index 4a34560946..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/FoundryAgents_Step14_CodeInterpreter.csproj
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
- Exe
- net10.0
-
- enable
- enable
- $(NoWarn);CA1812
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/README.md
deleted file mode 100644
index 34fa18c94c..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/README.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# Using Code Interpreter with AI Agents
-
-This sample demonstrates how to use the code interpreter tool with AI agents. The code interpreter allows agents to write and execute Python code to solve problems, perform calculations, and analyze data.
-
-## What this sample demonstrates
-
-- Creating agents with code interpreter capabilities
-- Using HostedCodeInterpreterTool (MEAI abstraction)
-- Using native SDK code interpreter tools (ResponseTool.CreateCodeInterpreterTool)
-- Extracting code inputs and results from agent responses
-- Handling code interpreter annotations
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure 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).
-
-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
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step14_CodeInterpreter
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create two agents with code interpreter capabilities:
- - Option 1: Using HostedCodeInterpreterTool (MEAI abstraction)
- - Option 2: Using native SDK code interpreter tools
-2. Run the agent with a mathematical problem: "I need to solve the equation sin(x) + x^2 = 42"
-3. The agent will use the code interpreter to write and execute Python code to solve the equation
-4. Extract and display the code that was executed
-5. Display the results from the code execution
-6. Display any annotations generated by the code interpreter tool
-7. Clean up resources by deleting both agents
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/README.md
deleted file mode 100644
index 092f2bd1cf..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/README.md
+++ /dev/null
@@ -1,66 +0,0 @@
-# Using Computer Use Tool with AI Agents
-
-This sample demonstrates how to use the computer use tool with AI agents. The computer use tool allows agents to interact with a computer environment by viewing the screen, controlling the mouse and keyboard, and performing various actions to help complete tasks.
-
-> [!NOTE]
-> **Azure Agents API vs. vanilla OpenAI Responses API behavior:**
-> The Azure Agents API rejects requests that include `previous_response_id` alongside
-> `computer_call_output` items — unlike the vanilla OpenAI Responses API, which accepts them.
-> This sample works around the limitation by creating a **fresh session for each follow-up call**
-> (so no `previous_response_id` is carried over) and re-sending all prior response output items
-> (reasoning, computer_call, etc.) as input items to preserve full conversation context.
-> Additionally, the sample uses the **current** `CallId` from each computer call response
-> (not the initial one) and clears the `ContinuationToken` after polling completes to prevent
-> stale tokens from affecting subsequent requests.
-
-## What this sample demonstrates
-
-- Creating agents with computer use capabilities
-- Using HostedComputerTool (MEAI abstraction)
-- Using native SDK computer use tools (ResponseTool.CreateComputerTool)
-- Extracting computer action information from agent responses
-- Handling computer tool results (text output and screenshots)
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure 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).
-
-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="computer-use-preview" # Optional, defaults to computer-use-preview
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step15_ComputerUse
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create two agents with computer use capabilities:
- - Option 1: Using HostedComputerTool (MEAI abstraction)
- - Option 2: Using native SDK computer use tools
-2. Run the agent with a task: "I need you to help me search for 'OpenAI news'. Please type 'OpenAI news' and submit the search. Once you see search results, the task is complete."
-3. The agent will use the computer use tool to:
- - Interpret the screenshots
- - Issue action requests based on the task
- - Analyze the search results for "OpenAI news" from the screenshots.
-4. Extract and display the computer actions performed
-5. Display the results from the computer tool execution
-6. Display the final response from the agent
-7. Clean up resources by deleting both agents
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/README.md
deleted file mode 100644
index db74868d3d..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/README.md
+++ /dev/null
@@ -1,52 +0,0 @@
-# Using File Search with AI Agents
-
-This sample demonstrates how to use the file search tool with AI agents. The file search tool allows agents to search through uploaded files stored in vector stores to answer user questions.
-
-## What this sample demonstrates
-
-- Uploading files and creating vector stores
-- Creating agents with file search capabilities
-- Using HostedFileSearchTool (MEAI abstraction)
-- Using native SDK file search tools (ResponseTool.CreateFileSearchTool)
-- Handling file citation annotations
-- Managing agent and resource lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure Foundry service endpoint and deployment configured
-- Azure CLI installed and authenticated (for Azure credential authentication)
-
-**Note**: This demo uses `DefaultAzureCredential` for authentication. For local development, make sure you're logged in with `az login` and have access to the Azure Foundry resource. For more information, see the [Azure Identity documentation](https://learn.microsoft.com/dotnet/api/azure.identity.defaultazurecredential).
-
-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
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step16_FileSearch
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create a temporary text file with employee directory information
-2. Upload the file to Azure Foundry
-3. Create a vector store with the uploaded file
-4. Create an agent with file search capabilities using one of:
- - Option 1: Using HostedFileSearchTool (MEAI abstraction)
- - Option 2: Using native SDK file search tools
-5. Run a query against the agent to search through the uploaded file
-6. Display file citation annotations from responses
-7. Clean up resources (agent, vector store, and uploaded file)
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/FoundryAgents_Step17_OpenAPITools.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/FoundryAgents_Step17_OpenAPITools.csproj
deleted file mode 100644
index 77b76acfa0..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/FoundryAgents_Step17_OpenAPITools.csproj
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
- Exe
- net10.0
-
- enable
- enable
- $(NoWarn);CA1812;CS8321
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/README.md
deleted file mode 100644
index a859f6b963..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/README.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# Using OpenAPI Tools with AI Agents
-
-This sample demonstrates how to use OpenAPI tools with AI agents. OpenAPI tools allow agents to call external REST APIs defined by OpenAPI specifications.
-
-## What this sample demonstrates
-
-- Creating agents with OpenAPI tool capabilities
-- Using AgentTool.CreateOpenApiTool with an embedded OpenAPI specification
-- Anonymous authentication for public APIs
-- Running an agent that can call external REST APIs
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure Foundry service endpoint and deployment configured
-- Azure CLI installed and authenticated (for Azure credential authentication)
-
-**Note**: This demo uses `DefaultAzureCredential` for authentication, which supports multiple authentication methods including Azure CLI, managed identity, and more. Make sure you're logged in with `az login` and have access to the Azure Foundry resource. For more information, see the [Azure Identity documentation](https://learn.microsoft.com/dotnet/api/azure.identity.defaultazurecredential).
-
-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
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step17_OpenAPITools
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create an agent with an OpenAPI tool configured to call the REST Countries API
-2. Ask the agent: "What countries use the Euro (EUR) as their currency?"
-3. The agent will use the OpenAPI tool to call the REST Countries API
-4. Display the response containing the list of countries that use EUR
-5. Clean up resources by deleting the agent
\ No newline at end of file
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/FoundryAgents_Step18_BingCustomSearch.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/FoundryAgents_Step18_BingCustomSearch.csproj
deleted file mode 100644
index 730d284bd9..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/FoundryAgents_Step18_BingCustomSearch.csproj
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
- Exe
- net10.0
-
- enable
- enable
- $(NoWarn);CA1812;CS8321
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/README.md
deleted file mode 100644
index ccc1873a04..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/README.md
+++ /dev/null
@@ -1,63 +0,0 @@
-# Using Bing Custom Search with AI Agents
-
-This sample demonstrates how to use the Bing Custom Search tool with AI agents to perform customized web searches.
-
-## What this sample demonstrates
-
-- Creating agents with Bing Custom Search capabilities
-- Configuring custom search instances via connection ID and instance name
-- Two agent creation approaches: MEAI abstraction (Option 1) and Native SDK (Option 2)
-- Running search queries through the agent
-- Managing agent lifecycle (creation and deletion)
-
-## Agent creation options
-
-This sample provides two approaches for creating agents with Bing Custom Search:
-
-- **Option 1 - MEAI + AgentFramework**: Uses the Agent Framework `ResponseTool` wrapped with `AsAITool()` to call the `CreateAIAgentAsync` overload that accepts `tools:[]`, while still relying on the same underlying Azure AI Projects SDK types as Option 2.
-- **Option 2 - Native SDK**: Uses `PromptAgentDefinition` with `AgentVersionCreationOptions` to create the agent directly with the Azure AI Projects SDK types.
-
-Both options produce the same result. Toggle between them by commenting/uncommenting the corresponding `CreateAgentWith*Async` call in `Program.cs`.
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure Foundry service endpoint and deployment configured
-- Azure CLI installed and authenticated (for Azure credential authentication)
-- A Bing Custom Search resource configured in Azure and connected to your Foundry project
-
-**Note**: This demo uses Azure Default credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure Foundry resource.
-
-Set the following environment variables:
-
-```powershell
-$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
-$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini
-$env:BING_CUSTOM_SEARCH_PROJECT_CONNECTION_ID="/subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts//projects//connections/"
-$env:BING_CUSTOM_SEARCH_INSTANCE_NAME="your-configuration-name"
-```
-
-### Finding the connection ID and instance name
-
-- **Connection ID**: The full ARM resource path including the `/projects//connections/` segment. Find the connection name in your Foundry project under **Management center** → **Connected resources**.
-- **Instance Name**: The **configuration name** from the Bing Custom Search resource (Azure portal → your Bing Custom Search resource → **Configurations**). This is _not_ the Azure resource name.
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step18_BingCustomSearch
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create an agent with Bing Custom Search tool capabilities
-2. Run the agent with a search query about Microsoft AI
-3. Display the search results returned by the agent
-4. Clean up resources by deleting the agent
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/FoundryAgents_Step19_SharePoint.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/FoundryAgents_Step19_SharePoint.csproj
deleted file mode 100644
index 4d17fe06bb..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/FoundryAgents_Step19_SharePoint.csproj
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
- Exe
- net10.0
-
- enable
- enable
- $(NoWarn);CA1812;CS8321
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/README.md
deleted file mode 100644
index ccbd699011..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/README.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# Using SharePoint Grounding with AI Agents
-
-This sample demonstrates how to use the SharePoint grounding tool with AI agents. The SharePoint grounding tool enables agents to search and retrieve information from SharePoint sites.
-
-## What this sample demonstrates
-
-- Creating agents with SharePoint grounding capabilities
-- Using AgentTool.CreateSharepointTool (MEAI abstraction)
-- Using native SDK SharePoint tools (PromptAgentDefinition)
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure Foundry service endpoint and deployment configured
-- Azure authentication configured for `DefaultAzureCredential` (for example, Azure CLI logged in with `az login`, environment variables, managed identity, or IDE sign-in)
-- A SharePoint project connection configured in Azure Foundry
-
-**Note**: This demo uses `DefaultAzureCredential` for authentication. This credential will try multiple authentication mechanisms in order (such as environment variables, managed identity, Azure CLI login, and IDE sign-in) and use the first one that works. A common option for local development is to sign in with the Azure CLI using `az login` and ensure you 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) and the [DefaultAzureCredential documentation](https://learn.microsoft.com/dotnet/api/azure.identity.defaultazurecredential).
-
-Set the following environment variables:
-
-```powershell
-$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint
-$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini
-$env:SHAREPOINT_PROJECT_CONNECTION_ID="your-sharepoint-connection-id" # Required: SharePoint project connection ID
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step19_SharePoint
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create two agents with SharePoint grounding capabilities:
- - Option 1: Using AgentTool.CreateSharepointTool (MEAI abstraction)
- - Option 2: Using native SDK SharePoint tools
-2. Run the agent with a query: "List the documents available in SharePoint"
-3. The agent will use SharePoint grounding to search and retrieve relevant documents
-4. Display the response and any grounding annotations
-5. Clean up resources by deleting both agents
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/FoundryAgents_Step20_MicrosoftFabric.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/FoundryAgents_Step20_MicrosoftFabric.csproj
deleted file mode 100644
index 4d17fe06bb..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/FoundryAgents_Step20_MicrosoftFabric.csproj
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
- Exe
- net10.0
-
- enable
- enable
- $(NoWarn);CA1812;CS8321
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/Program.cs
deleted file mode 100644
index e5ab205f68..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/Program.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-// This sample shows how to use Microsoft Fabric Tool with AI Agents.
-
-using Azure.AI.Projects;
-using Azure.AI.Projects.Agents;
-using Azure.Identity;
-using Microsoft.Agents.AI;
-using OpenAI.Responses;
-
-string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-string fabricConnectionId = Environment.GetEnvironmentVariable("FABRIC_PROJECT_CONNECTION_ID") ?? throw new InvalidOperationException("FABRIC_PROJECT_CONNECTION_ID is not set.");
-
-const string AgentInstructions = "You are a helpful assistant with access to Microsoft Fabric data. Answer questions based on data available through your Fabric connection.";
-
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
-// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
-// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
-// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
-AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-
-// Configure Microsoft Fabric tool options with project connection
-var fabricToolOptions = new FabricDataAgentToolOptions();
-fabricToolOptions.ProjectConnections.Add(new ToolProjectConnection(fabricConnectionId));
-
-AIAgent agent = await CreateAgentWithMEAIAsync();
-// AIAgent agent = await CreateAgentWithNativeSDKAsync();
-
-Console.WriteLine($"Created agent: {agent.Name}");
-
-// Run the agent with a sample query
-AgentResponse response = await agent.RunAsync("What data is available in the connected Fabric workspace?");
-
-Console.WriteLine("\n=== Agent Response ===");
-foreach (var message in response.Messages)
-{
- Console.WriteLine(message.Text);
-}
-
-// Cleanup by deleting the agent
-await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
-Console.WriteLine($"\nDeleted agent: {agent.Name}");
-
-// --- Agent Creation Options ---
-
-// Option 1 - Using AsAITool wrapping for the ResponseTool returned by AgentTool.CreateMicrosoftFabricTool (MEAI + AgentFramework)
-async Task CreateAgentWithMEAIAsync()
-{
- return await aiProjectClient.CreateAIAgentAsync(
- model: deploymentName,
- name: "FabricAgent-MEAI",
- instructions: AgentInstructions,
- tools: [((ResponseTool)AgentTool.CreateMicrosoftFabricTool(fabricToolOptions)).AsAITool()]);
-}
-
-// Option 2 - Using PromptAgentDefinition with AgentTool.CreateMicrosoftFabricTool (Native SDK)
-async Task CreateAgentWithNativeSDKAsync()
-{
- return await aiProjectClient.CreateAIAgentAsync(
- name: "FabricAgent-NATIVE",
- creationOptions: new AgentVersionCreationOptions(
- new PromptAgentDefinition(model: deploymentName)
- {
- Instructions = AgentInstructions,
- Tools =
- {
- AgentTool.CreateMicrosoftFabricTool(fabricToolOptions),
- }
- })
- );
-}
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/README.md
deleted file mode 100644
index a5faf79d9d..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/README.md
+++ /dev/null
@@ -1,57 +0,0 @@
-# Using Microsoft Fabric Tool with AI Agents
-
-This sample demonstrates how to use the Microsoft Fabric tool with AI Agents, allowing agents to query and interact with data in Microsoft Fabric workspaces.
-
-## What this sample demonstrates
-
-- Creating agents with Microsoft Fabric data access capabilities
-- Using FabricDataAgentToolOptions to configure Fabric connections
-- Two agent creation approaches: MEAI abstraction (Option 1) and Native SDK (Option 2)
-- Managing agent lifecycle (creation and deletion)
-
-## Agent creation options
-
-This sample provides two approaches for creating agents with Microsoft Fabric:
-
-- **Option 1 - MEAI + AgentFramework**: Uses the Agent Framework `ResponseTool` wrapped with `AsAITool()` to call the `CreateAIAgentAsync` overload that accepts `tools:[]`, while still relying on the same underlying Azure AI Projects SDK types as Option 2.
-- **Option 2 - Native SDK**: Uses `PromptAgentDefinition` with `AgentVersionCreationOptions` to create the agent directly with the Azure AI Projects SDK types.
-
-Both options produce the same result. Toggle between them by commenting/uncommenting the corresponding `CreateAgentWith*Async` call in `Program.cs`.
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure Foundry service endpoint and deployment configured
-- Azure CLI installed and authenticated (for Azure credential authentication)
-- A Microsoft Fabric workspace with a configured project connection in Azure Foundry
-
-**Note**: This demo uses Azure Default credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure Foundry resource.
-
-Set the following environment variables:
-
-```powershell
-$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
-$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini
-$env:FABRIC_PROJECT_CONNECTION_ID="your-fabric-connection-id" # The Fabric project connection ID from Azure Foundry
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step20_MicrosoftFabric
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create an agent with Microsoft Fabric tool capabilities
-2. Configure the agent with a Fabric project connection
-3. Run the agent with a query about available Fabric data
-4. Display the agent's response
-5. Clean up resources by deleting the agent
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/FoundryAgents_Step21_WebSearch.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/FoundryAgents_Step21_WebSearch.csproj
deleted file mode 100644
index 4d17fe06bb..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/FoundryAgents_Step21_WebSearch.csproj
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
- Exe
- net10.0
-
- enable
- enable
- $(NoWarn);CA1812;CS8321
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/Program.cs
deleted file mode 100644
index c116a975e1..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/Program.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-// This sample shows how to use the Responses API Web Search Tool with AI Agents.
-
-using Azure.AI.Projects;
-using Azure.AI.Projects.Agents;
-using Azure.Identity;
-using Microsoft.Agents.AI;
-using Microsoft.Extensions.AI;
-using OpenAI.Responses;
-
-string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-
-const string AgentInstructions = "You are a helpful assistant that can search the web to find current information and answer questions accurately.";
-const string AgentName = "WebSearchAgent";
-
-// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
-AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
-
-// Option 1 - Using HostedWebSearchTool (MEAI + AgentFramework)
-AIAgent agent = await CreateAgentWithMEAIAsync();
-
-// Option 2 - Using PromptAgentDefinition with the Responses API native type
-// AIAgent agent = await CreateAgentWithNativeSDKAsync();
-
-AgentResponse response = await agent.RunAsync("What's the weather today in Seattle?");
-
-// Get the text response
-Console.WriteLine($"Response: {response.Text}");
-
-// Getting any annotations/citations generated by the web search tool
-foreach (AIAnnotation annotation in response.Messages.SelectMany(m => m.Contents).SelectMany(c => c.Annotations ?? []))
-{
- Console.WriteLine($"Annotation: {annotation}");
- if (annotation.RawRepresentation is UriCitationMessageAnnotation urlCitation)
- {
- Console.WriteLine($$"""
- Title: {{urlCitation.Title}}
- URL: {{urlCitation.Uri}}
- """);
- }
-}
-
-// Cleanup by agent name removes the agent version created.
-await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
-
-// Creates the agent using the HostedWebSearchTool MEAI abstraction that maps to the built-in Responses API web search tool.
-async Task CreateAgentWithMEAIAsync()
- => await aiProjectClient.CreateAIAgentAsync(
- name: AgentName,
- model: deploymentName,
- instructions: AgentInstructions,
- tools: [new HostedWebSearchTool()]);
-
-// Creates the agent using the PromptAgentDefinition with the Responses API native ResponseTool.CreateWebSearchTool().
-async Task CreateAgentWithNativeSDKAsync()
- => await aiProjectClient.CreateAIAgentAsync(
- AgentName,
- new AgentVersionCreationOptions(
- new PromptAgentDefinition(model: deploymentName)
- {
- Instructions = AgentInstructions,
- Tools = { ResponseTool.CreateWebSearchTool() }
- }));
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/README.md
deleted file mode 100644
index 8da390878c..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/README.md
+++ /dev/null
@@ -1,52 +0,0 @@
-# Using Web Search with AI Agents
-
-This sample demonstrates how to use the Responses API web search tool with AI agents. The web search tool allows agents to search the web for current information to answer questions accurately.
-
-## What this sample demonstrates
-
-- Creating agents with web search capabilities
-- Using HostedWebSearchTool (MEAI abstraction)
-- Using native SDK web search tools (ResponseTool.CreateWebSearchTool)
-- Extracting text responses and URL citations from agent responses
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure Foundry service endpoint and deployment configured
-- Azure authentication configured for `DefaultAzureCredential` (for example, Azure CLI logged in with `az login`, environment variables, managed identity, or IDE sign-in)
-
-**Note**: This sample authenticates using `DefaultAzureCredential` from the Azure Identity library, which will try several credential sources (including Azure CLI, environment variables, managed identity, and IDE sign-in). Ensure at least one supported credential source is available. For more information, see the [Azure Identity documentation](https://learn.microsoft.com/dotnet/api/overview/azure/identity-readme).
-
-**Note**: The web search tool uses the built-in web search capability from the OpenAI Responses API.
-
-Set the following environment variables:
-
-```powershell
-$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint
-$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step21_WebSearch
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create an agent with web search capabilities using HostedWebSearchTool (MEAI abstraction)
- - Alternative: Using native SDK web search tools (commented out in code)
- - Alternative: Retrieving an existing agent by name (commented out in code)
-2. Run the agent with a query: "What's the weather today in Seattle?"
-3. The agent will use the web search tool to find current information
-4. Display the text response from the agent
-5. Display any URL citations from web search results
-6. Clean up resources by deleting the agent
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/FoundryAgents_Step22_MemorySearch.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/FoundryAgents_Step22_MemorySearch.csproj
deleted file mode 100644
index d83a9d9202..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/FoundryAgents_Step22_MemorySearch.csproj
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
- Exe
- net10.0
-
- enable
- enable
- $(NoWarn);CA1812
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/README.md
deleted file mode 100644
index 9e6d79d579..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/README.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# Using Memory Search with AI Agents
-
-This sample demonstrates how to use the Memory Search tool with AI agents. The Memory Search tool enables agents to recall information from previous conversations, supporting user profile persistence and chat summaries across sessions.
-
-## What this sample demonstrates
-
-- Creating an agent with Memory Search tool capabilities
-- Configuring memory scope for user isolation
-- Having conversations where the agent remembers past information
-- Inspecting memory search results from agent responses
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure Foundry service endpoint and deployment configured
-- Azure CLI installed and authenticated (for Azure credential authentication)
-- **A pre-created Memory Store** (see below)
-
-**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).
-
-### Creating a Memory Store
-
-Memory stores must be created before running this sample. The .NET SDK currently only supports **using** existing memory stores with agents. To create a memory store, use one of these methods:
-
-**Option 1: Azure Portal**
-1. Navigate to your Azure AI Foundry project
-2. Go to the Memory section
-3. Create a new memory store with your desired settings
-
-**Option 2: Python SDK**
-```python
-from azure.ai.projects import AIProjectClient
-from azure.ai.projects.models import MemoryStoreDefaultDefinition, MemoryStoreDefaultOptions
-from azure.identity import DefaultAzureCredential
-
-project_client = AIProjectClient(
- endpoint="https://your-endpoint.openai.azure.com/",
- credential=DefaultAzureCredential()
-)
-
-memory_store = await project_client.memory_stores.create(
- name="my-memory-store",
- description="Memory store for Agent Framework conversations",
- definition=MemoryStoreDefaultDefinition(
- chat_model=os.environ["AZURE_AI_CHAT_MODEL_DEPLOYMENT_NAME"],
- embedding_model=os.environ["AZURE_AI_EMBEDDING_MODEL_DEPLOYMENT_NAME"],
- options=MemoryStoreDefaultOptions(
- user_profile_enabled=True,
- chat_summary_enabled=True
- )
- )
-)
-```
-
-## Environment Variables
-
-Set the following environment variables:
-
-```powershell
-$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
-$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini
-$env:AZURE_AI_MEMORY_STORE_NAME="your-memory-store-name" # Required - name of pre-created memory store
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step22_MemorySearch
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Create an agent with Memory Search tool configured
-2. Send a message with personal information ("My name is Alice and I love programming in C#")
-3. Wait for memory indexing
-4. Ask the agent to recall the previously shared information
-5. Display memory search results if available in the response
-6. Clean up by deleting the agent (note: memory store persists)
-
-## Important notes
-
-- **Memory Store Lifecycle**: Memory stores are long-lived resources and are NOT deleted when the agent is deleted. Clean them up separately via Azure Portal or Python SDK.
-- **Scope**: The `scope` parameter isolates memories per user/context. Use unique identifiers for different users.
-- **Update Delay**: The `UpdateDelay` parameter controls how quickly new memories are indexed.
diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/README.md
deleted file mode 100644
index 8651108987..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/README.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Using Local MCP Client with Azure Foundry Agents
-
-This sample demonstrates how to use a local MCP (Model Context Protocol) client with Azure Foundry Agents. Unlike the hosted MCP approach where Azure Foundry invokes the MCP server on the service side, this sample connects to the MCP server directly from the client via HTTP (Streamable HTTP transport) and passes the resolved tools to the agent.
-
-## What this sample demonstrates
-
-- Connecting to an MCP server locally using `HttpClientTransport`
-- Discovering available tools from the MCP server client-side
-- Passing locally-resolved MCP tools to a Foundry agent
-- Using the Microsoft Learn MCP endpoint for documentation search
-- Managing agent lifecycle (creation and deletion)
-
-## Prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure 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).
-
-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
-```
-
-## Run the sample
-
-Navigate to the FoundryAgents sample directory and run:
-
-```powershell
-cd dotnet/samples/02-agents/FoundryAgents
-dotnet run --project .\FoundryAgents_Step23_LocalMCP
-```
-
-## Expected behavior
-
-The sample will:
-
-1. Connect to the Microsoft Learn MCP server via HTTP and list available tools
-2. Create an agent with the locally-resolved MCP tools
-3. Ask two questions about Microsoft documentation
-4. The agent will use the MCP tools (invoked locally) to search Microsoft Learn documentation
-5. Display the agent's responses with information from the documentation
-6. Clean up resources by deleting the agent
diff --git a/dotnet/samples/02-agents/FoundryAgents/README.md b/dotnet/samples/02-agents/FoundryAgents/README.md
deleted file mode 100644
index 426a8cdad5..0000000000
--- a/dotnet/samples/02-agents/FoundryAgents/README.md
+++ /dev/null
@@ -1,121 +0,0 @@
-# Getting started with Foundry Agents
-
-The getting started with Foundry Agents samples demonstrate the fundamental concepts and functionalities
-of Azure Foundry Agents and can be used with Azure Foundry as the AI provider.
-
-These samples showcase how to work with agents managed through Azure Foundry, including agent creation,
-versioning, multi-turn conversations, and advanced features like code interpretation and computer use.
-
-## Classic vs New Foundry Agents
-
-> [!NOTE]
-> Recently, Azure Foundry introduced a new and improved experience for creating and managing AI agents, which is the target of these samples.
-
-For more information about the previous classic agents and for what's new in Foundry Agents, see the [Foundry Agents migration documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/how-to/migrate?view=foundry).
-
-For a sample demonstrating how to use classic Foundry Agents, see the following: [Agent with Azure AI Persistent](../AgentProviders/Agent_With_AzureAIAgentsPersistent/README.md).
-
-## Agent Versioning and Static Definitions
-
-One of the key architectural changes in the new Foundry Agents compared to the classic experience is how agent definitions are handled. In the new architecture, agents have **versions** and their definitions are established at creation time. This means that the agent's configuration—including instructions, tools, and options—is fixed when the agent version is created.
-
-> [!IMPORTANT]
-> Agent versions are static and strictly adhere to their original definition. Any attempt to provide or override tools, instructions, or options during an agent run or request will be ignored by the agent, as the API does not support runtime configuration changes. All agent behavior must be defined at agent creation time.
-
-This design ensures consistency and predictability in agent behavior across all interactions with a specific agent version.
-
-The Agent Framework intentionally ignores unsupported runtime parameters rather than throwing exceptions. This abstraction-first approach ensures that code written against the unified agent abstraction remains portable across providers (OpenAI, Azure OpenAI, Foundry Agents). It removes the need for provider-specific conditional logic. Teams can adopt Foundry Agents without rewriting existing orchestration code. Configurations that work with other providers will gracefully degrade, rather than fail, when the underlying API does not support them.
-
-## Getting started with Foundry Agents prerequisites
-
-Before you begin, ensure you have the following prerequisites:
-
-- .NET 10 SDK or later
-- Azure Foundry service endpoint and project configured
-- Azure CLI installed and authenticated (for Azure credential authentication)
-
-**Note**: These samples use Azure Foundry Agents. For more information, see [Azure AI Foundry documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/).
-
-**Note**: These samples use 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).
-
-## Samples
-
-|Sample|Description|
-|---|---|
-|[Basics](./FoundryAgents_Step01.1_Basics/)|This sample demonstrates how to create and manage AI agents with versioning|
-|[Running a simple agent](./FoundryAgents_Step01.2_Running/)|This sample demonstrates how to create and run a basic Foundry agent|
-|[Multi-turn conversation](./FoundryAgents_Step02_MultiturnConversation/)|This sample demonstrates how to implement a multi-turn conversation with a Foundry agent|
-|[Using function tools](./FoundryAgents_Step03_UsingFunctionTools/)|This sample demonstrates how to use function tools with a Foundry agent|
-|[Using function tools with approvals](./FoundryAgents_Step04_UsingFunctionToolsWithApprovals/)|This sample demonstrates how to use function tools where approvals require human in the loop approvals before execution|
-|[Structured output](./FoundryAgents_Step05_StructuredOutput/)|This sample demonstrates how to use structured output with a Foundry agent|
-|[Persisted conversations](./FoundryAgents_Step06_PersistedConversations/)|This sample demonstrates how to persist conversations and reload them later|
-|[Observability](./FoundryAgents_Step07_Observability/)|This sample demonstrates how to add telemetry to a Foundry agent|
-|[Dependency injection](./FoundryAgents_Step08_DependencyInjection/)|This sample demonstrates how to add and resolve a Foundry agent with a dependency injection container|
-|[Using MCP client as tools](./FoundryAgents_Step09_UsingMcpClientAsTools/)|This sample demonstrates how to use MCP clients as tools with a Foundry agent|
-|[Using images](./FoundryAgents_Step10_UsingImages/)|This sample demonstrates how to use image multi-modality with a Foundry agent|
-|[Exposing as a function tool](./FoundryAgents_Step11_AsFunctionTool/)|This sample demonstrates how to expose a Foundry agent as a function tool|
-|[Using middleware](./FoundryAgents_Step12_Middleware/)|This sample demonstrates how to use middleware with a Foundry agent|
-|[Using plugins](./FoundryAgents_Step13_Plugins/)|This sample demonstrates how to use plugins with a Foundry agent|
-|[Code interpreter](./FoundryAgents_Step14_CodeInterpreter/)|This sample demonstrates how to use the code interpreter tool with a Foundry agent|
-|[Computer use](./FoundryAgents_Step15_ComputerUse/)|This sample demonstrates how to use computer use capabilities with a Foundry agent|
-|[File search](./FoundryAgents_Step16_FileSearch/)|This sample demonstrates how to use the file search tool with a Foundry agent|
-|[OpenAPI tools](./FoundryAgents_Step17_OpenAPITools/)|This sample demonstrates how to use OpenAPI tools with a Foundry agent|
-|[Bing Custom Search](./FoundryAgents_Step18_BingCustomSearch/)|This sample demonstrates how to use Bing Custom Search tool with a Foundry agent|
-|[SharePoint grounding](./FoundryAgents_Step19_SharePoint/)|This sample demonstrates how to use the SharePoint grounding tool with a Foundry agent|
-|[Microsoft Fabric](./FoundryAgents_Step20_MicrosoftFabric/)|This sample demonstrates how to use Microsoft Fabric tool with a Foundry agent|
-|[Web search](./FoundryAgents_Step21_WebSearch/)|This sample demonstrates how to use the Responses API web search tool with a Foundry agent|
-|[Memory search](./FoundryAgents_Step22_MemorySearch/)|This sample demonstrates how to use memory search tool with a Foundry agent|
-|[Local MCP](./FoundryAgents_Step23_LocalMCP/)|This sample demonstrates how to use a local MCP client with a Foundry agent|
-
-## Evaluation Samples
-
-Evaluation is critical for building trustworthy and high-quality AI applications. The evaluation samples demonstrate how to assess agent safety, quality, and performance using Azure AI Foundry's evaluation capabilities.
-
-|Sample|Description|
-|---|---|
-|[Red Team Evaluation](./FoundryAgents_Evaluations_Step01_RedTeaming/)|This sample demonstrates how to use Azure AI Foundry's Red Teaming service to assess model safety against adversarial attacks|
-|[Self-Reflection with Groundedness](./FoundryAgents_Evaluations_Step02_SelfReflection/)|This sample demonstrates the self-reflection pattern where agents iteratively improve responses based on groundedness evaluation|
-
-For details on safety evaluation, see the [Red Team Evaluation README](./FoundryAgents_Evaluations_Step01_RedTeaming/README.md).
-
-## Running the samples from the console
-
-To run the samples, navigate to the desired sample directory, e.g.
-
-```powershell
-cd FoundryAgents_Step01.2_Running
-```
-
-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
-```
-
-If the variables are not set, you will be prompted for the values when running the samples.
-
-Execute the following command to build the sample:
-
-```powershell
-dotnet build
-```
-
-Execute the following command to run the sample:
-
-```powershell
-dotnet run --no-build
-```
-
-Or just build and run in one step:
-
-```powershell
-dotnet run
-```
-
-## Running the samples from Visual Studio
-
-Open the solution in Visual Studio and set the desired sample project as the startup project. Then, run the project using the built-in debugger or by pressing `F5`.
-
-You will be prompted for any required environment variables if they are not already set.
-
diff --git a/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs b/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs
index e91ed4d15a..bedf87b8c5 100644
--- a/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs
+++ b/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs
@@ -5,9 +5,11 @@
// The sample first shows how to use MCP tools with auto approval, and then how to set up a tool that requires approval before it can be invoked and how to approve such a tool.
using Azure.AI.Projects;
+using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
+using OpenAI.Responses;
var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
var model = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4.1-mini";
@@ -23,26 +25,22 @@ var aiProjectClient = new AIProjectClient(new Uri(endpoint), new DefaultAzureCre
// Create an MCP tool definition that the agent can use.
// In this case we allow the tool to always be called without approval.
-var mcpTool = new HostedMcpServerTool(
- serverName: "microsoft_learn",
- serverAddress: "https://learn.microsoft.com/api/mcp")
-{
- AllowedTools = ["microsoft_docs_search"],
- ApprovalMode = HostedMcpServerToolApprovalMode.NeverRequire
-};
+var mcpTool = ResponseTool.CreateMcpTool(
+ serverLabel: "microsoft_learn",
+ serverUri: new Uri("https://learn.microsoft.com/api/mcp"),
+ toolCallApprovalPolicy: new McpToolCallApprovalPolicy(GlobalMcpToolCallApprovalPolicy.NeverRequireApproval));
// Create a server side agent with the mcp tool, and expose it as an AIAgent.
-AIAgent agent = await aiProjectClient.CreateAIAgentAsync(
- model: model,
- options: new()
- {
- Name = "MicrosoftLearnAgent",
- ChatOptions = new()
+AgentVersion agentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
+ "MicrosoftLearnAgent",
+ new AgentVersionCreationOptions(
+ new PromptAgentDefinition(model: model)
{
Instructions = "You answer questions by searching the Microsoft Learn content only.",
- Tools = [mcpTool]
- },
- });
+ Tools = { mcpTool }
+ }));
+
+AIAgent agent = aiProjectClient.AsAIAgent(agentVersion);
// You can then invoke the agent like any other AIAgent.
AgentSession session = await agent.CreateSessionAsync();
@@ -56,26 +54,23 @@ aiProjectClient.Agents.DeleteAgent(agent.Name);
// Create an MCP tool definition that the agent can use.
// In this case we require approval before the tool can be called.
-var mcpToolWithApproval = new HostedMcpServerTool(
- serverName: "microsoft_learn",
- serverAddress: "https://learn.microsoft.com/api/mcp")
-{
- AllowedTools = ["microsoft_docs_search"],
- ApprovalMode = HostedMcpServerToolApprovalMode.AlwaysRequire
-};
+var mcpToolWithApproval = ResponseTool.CreateMcpTool(
+ serverLabel: "microsoft_learn",
+ serverUri: new Uri("https://learn.microsoft.com/api/mcp"),
+ allowedTools: new McpToolFilter() { ToolNames = { "microsoft_docs_search" } },
+ toolCallApprovalPolicy: new McpToolCallApprovalPolicy(GlobalMcpToolCallApprovalPolicy.AlwaysRequireApproval));
// Create an agent with the MCP tool that requires approval.
-AIAgent agentWithRequiredApproval = await aiProjectClient.CreateAIAgentAsync(
- model: model,
- options: new()
- {
- Name = "MicrosoftLearnAgentWithApproval",
- ChatOptions = new()
+AgentVersion agentVersionWithApproval = await aiProjectClient.Agents.CreateAgentVersionAsync(
+ "MicrosoftLearnAgentWithApproval",
+ new AgentVersionCreationOptions(
+ new PromptAgentDefinition(model: model)
{
Instructions = "You answer questions by searching the Microsoft Learn content only.",
- Tools = [mcpToolWithApproval]
- },
- });
+ Tools = { mcpToolWithApproval }
+ }));
+
+AIAgent agentWithRequiredApproval = aiProjectClient.AsAIAgent(agentVersionWithApproval);
// You can then invoke the agent like any other AIAgent.
// For simplicity, we are assuming here that only mcp tool approvals are pending.
diff --git a/dotnet/samples/02-agents/README.md b/dotnet/samples/02-agents/README.md
index b901645f88..5ff0db416d 100644
--- a/dotnet/samples/02-agents/README.md
+++ b/dotnet/samples/02-agents/README.md
@@ -1,22 +1,21 @@
-# Getting started
+# Getting started
-The getting started samples demonstrate the fundamental concepts and functionalities
-of the agent framework.
+The getting started samples demonstrate the fundamental concepts and functionality of the agent framework.
## Samples
-|Sample|Description|
-|---|---|
-|[Agents](./Agents/README.md)|Step by step instructions for getting started with agents|
-|[Foundry Agents](./FoundryAgents/README.md)|Getting started with Azure Foundry Agents|
-|[Agent Providers](./AgentProviders/README.md)|Getting started with creating agents using various providers|
-|[Agents With Retrieval Augmented Generation (RAG)](./AgentWithRAG/README.md)|Adding Retrieval Augmented Generation (RAG) capabilities to your agents.|
-|[Agents With Memory](./AgentWithMemory/README.md)|Adding Memory capabilities to your agents.|
-|[Agent Open Telemetry](./AgentOpenTelemetry/README.md)|Getting started with OpenTelemetry for agents|
-|[Agent With OpenAI exchange types](./AgentWithOpenAI/README.md)|Using OpenAI exchange types with agents|
-|[Agent With Anthropic](./AgentWithAnthropic/README.md)|Getting started with agents using Anthropic Claude|
-|[Model Context Protocol](./ModelContextProtocol/README.md)|Getting started with Model Context Protocol|
-|[Agent Skills](./AgentSkills/README.md)|Getting started with Agent Skills|
-|[Declarative Agents](./DeclarativeAgents)|Loading and executing AI agents from YAML configuration files| │
-|[AG-UI](./AGUI/README.md)|Getting started with AG-UI (Agent UI Protocol) servers and clients| │
-|[Dev UI](./DevUI/README.md)|Interactive web interface for testing and debugging AI agents during development|
\ No newline at end of file
+| Sample | Description |
+| --- | --- |
+| [Agents](./Agents/README.md) | Step-by-step instructions for getting started with agents |
+| [Agents with Foundry](./AgentsWithFoundry/README.md) | Foundry agent samples using `FoundryAgent` and `AIProjectClient.AsAIAgent(...)` |
+| [Agent Providers](./AgentProviders/README.md) | Getting started with creating agents using various providers |
+| [Agents With Retrieval Augmented Generation (RAG)](./AgentWithRAG/README.md) | Adding Retrieval Augmented Generation (RAG) capabilities to your agents |
+| [Agents With Memory](./AgentWithMemory/README.md) | Adding memory capabilities to your agents |
+| [Agent Open Telemetry](./AgentOpenTelemetry/README.md) | Getting started with OpenTelemetry for agents |
+| [Agent With OpenAI exchange types](./AgentWithOpenAI/README.md) | Using OpenAI exchange types with agents |
+| [Agent With Anthropic](./AgentWithAnthropic/README.md) | Getting started with agents using Anthropic Claude |
+| [Model Context Protocol](./ModelContextProtocol/README.md) | Getting started with Model Context Protocol |
+| [Agent Skills](./AgentSkills/README.md) | Getting started with Agent Skills |
+| [Declarative Agents](./DeclarativeAgents) | Loading and executing AI agents from YAML configuration files |
+| [AG-UI](./AGUI/README.md) | Getting started with AG-UI (Agent UI Protocol) servers and clients |
+| [Dev UI](./DevUI/README.md) | Interactive web interface for testing and debugging AI agents during development |
diff --git a/dotnet/samples/03-workflows/Agents/FoundryAgent/Program.cs b/dotnet/samples/03-workflows/Agents/FoundryAgent/Program.cs
index 589eca2bbc..5b1f59ac62 100644
--- a/dotnet/samples/03-workflows/Agents/FoundryAgent/Program.cs
+++ b/dotnet/samples/03-workflows/Agents/FoundryAgent/Program.cs
@@ -1,8 +1,10 @@
// Copyright (c) Microsoft. All rights reserved.
using Azure.AI.Projects;
+using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.AzureAI;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.AI;
@@ -68,15 +70,19 @@ public static class Program
/// The target language for translation
/// The to create the agent with.
/// The model to use for the agent
- /// A ChatClientAgent configured for the specified language
- private static async Task CreateTranslationAgentAsync(
+ /// A FoundryAgent configured for the specified language
+ private static async Task CreateTranslationAgentAsync(
string targetLanguage,
AIProjectClient aiProjectClient,
string model)
{
- return await aiProjectClient.CreateAIAgentAsync(
- name: $"{targetLanguage} Translator",
- model: model,
- instructions: $"You are a translation assistant that translates the provided text to {targetLanguage}.");
+ AgentVersion agentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
+ $"{targetLanguage} Translator",
+ new AgentVersionCreationOptions(
+ new PromptAgentDefinition(model: model)
+ {
+ Instructions = $"You are a translation assistant that translates the provided text to {targetLanguage}.",
+ }));
+ return aiProjectClient.AsAIAgent(agentVersion);
}
}
diff --git a/dotnet/samples/03-workflows/Declarative/HostedWorkflow/Program.cs b/dotnet/samples/03-workflows/Declarative/HostedWorkflow/Program.cs
index 5936aaf82f..7852a8730f 100644
--- a/dotnet/samples/03-workflows/Declarative/HostedWorkflow/Program.cs
+++ b/dotnet/samples/03-workflows/Declarative/HostedWorkflow/Program.cs
@@ -8,6 +8,7 @@ using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.AzureAI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using Shared.Foundry;
@@ -49,7 +50,7 @@ internal sealed class Program
string workflowInput = GetWorkflowInput(args);
- AIAgent agent = aiProjectClient.AsAIAgent(agentVersion);
+ FoundryAgent agent = aiProjectClient.AsAIAgent(agentVersion);
AgentSession session = await agent.CreateSessionAsync();
diff --git a/dotnet/samples/03-workflows/README.md b/dotnet/samples/03-workflows/README.md
index 2b8d375654..1ab52106ec 100644
--- a/dotnet/samples/03-workflows/README.md
+++ b/dotnet/samples/03-workflows/README.md
@@ -1,6 +1,6 @@
-# Workflow Getting Started Samples
+# Workflow Getting Started Samples
-The getting started with workflow samples demonstrate the fundamental concepts and functionalities of workflows in Agent Framework.
+The workflow samples demonstrate the fundamental concepts and functionality of workflows in Agent Framework.
## Samples Overview
@@ -20,15 +20,13 @@ Please begin with the [Start Here](./_StartHere) samples in order. These three s
| [Mixed Workflow with Agents and Executors](./_StartHere/06_MixedWorkflowAgentsAndExecutors) | Shows how to mix agents and executors with adapter pattern for type conversion and protocol handling |
| [Writer-Critic Workflow](./_StartHere/07_WriterCriticWorkflow) | Demonstrates iterative refinement with quality gates, max iteration safety, multiple message handlers, and conditional routing for feedback loops |
-Once completed, please proceed to other samples listed below.
-
-> Note that you don't need to follow a strict order after the foundational samples. However, some samples build upon concepts from previous ones, so it's beneficial to be aware of the dependencies.
+Once completed, please proceed to the other samples listed below.
### Agents
| Sample | Concepts |
|--------|----------|
-| [Foundry Agents in Workflows](./Agents/FoundryAgent) | Demonstrates using Azure Foundry Agents within a workflow |
+| [Foundry Agents in Workflows](./Agents/FoundryAgent) | Demonstrates using Azure Foundry agents in a workflow through `ChatClientAgent` |
| [Custom Agent Executors](./Agents/CustomAgentExecutors) | Shows how to create a custom agent executor for more complex scenarios |
| [Workflow as an Agent](./Agents/WorkflowAsAnAgent) | Illustrates how to encapsulate a workflow as an agent |
| [Group Chat with Tool Approval](./Agents/GroupChatToolApproval) | Shows multi-agent group chat with tool approval requests and human-in-the-loop interaction |
@@ -58,25 +56,3 @@ Once completed, please proceed to other samples listed below.
| [Edge Conditions](./ConditionalEdges/01_EdgeCondition) | Introduces conditional edges for dynamic routing based on executor outputs |
| [Switch-Case Routing](./ConditionalEdges/02_SwitchCase) | Extends conditional edges with switch-case routing for multiple paths |
| [Multi-Selection Routing](./ConditionalEdges/03_MultiSelection) | Demonstrates multi-selection routing where one executor can trigger multiple downstream executors |
-
-> These 3 samples build upon each other. It's recommended to explore them in sequence to fully grasp the concepts.
-
-### Declarative Workflows
-
-| Sample | Concepts |
-|--------|----------|
-| [Declarative](./Declarative) | Demonstrates execution of declartive workflows. |
-
-### Checkpointing
-
-| Sample | Concepts |
-|--------|----------|
-| [Checkpoint and Resume](./Checkpoint/CheckpointAndResume) | Introduces checkpoints for saving and restoring workflow state for time travel purposes |
-| [Checkpoint and Rehydrate](./Checkpoint/CheckpointAndRehydrate) | Demonstrates hydrating a new workflow instance from a saved checkpoint |
-| [Checkpoint with Human-in-the-Loop](./Checkpoint/CheckpointWithHumanInTheLoop) | Combines checkpointing with human-in-the-loop interactions |
-
-### Human-in-the-Loop
-
-| Sample | Concepts |
-|--------|----------|
-| [Basic Human-in-the-Loop](./HumanInTheLoop/HumanInTheLoopBasic) | Introduces human-in-the-loop interaction using input ports and external requests |
diff --git a/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/HostAgentFactory.cs b/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/HostAgentFactory.cs
index 1149f9a293..13c01be156 100644
--- a/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/HostAgentFactory.cs
+++ b/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/HostAgentFactory.cs
@@ -2,6 +2,7 @@
using A2A;
using Azure.AI.Projects;
+using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
@@ -19,8 +20,8 @@ internal static class HostAgentFactory
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
var aiProjectClient = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential());
- AIAgent agent = await aiProjectClient
- .GetAIAgentAsync(agentName, tools: tools);
+ AgentRecord agentRecord = await aiProjectClient.Agents.GetAgentAsync(agentName);
+ AIAgent agent = aiProjectClient.AsAIAgent(agentRecord, tools: tools);
AgentCard agentCard = agentType.ToUpperInvariant() switch
{
diff --git a/dotnet/samples/AGENTS.md b/dotnet/samples/AGENTS.md
index 1578b39a26..f515f531eb 100644
--- a/dotnet/samples/AGENTS.md
+++ b/dotnet/samples/AGENTS.md
@@ -28,7 +28,7 @@ dotnet/samples/
│ ├── AGUI/ # AG-UI protocol samples
│ ├── DeclarativeAgents/ # Declarative agent definitions
│ ├── DevUI/ # DevUI samples
-│ ├── FoundryAgents/ # Azure AI Foundry agent samples
+│ ├── AgentsWithFoundry/ # Azure AI Foundry samples (FoundryAgent + AsAIAgent extensions)
│ └── ModelContextProtocol/ # MCP server/client patterns
├── 03-workflows/ # Workflow patterns
│ ├── _StartHere/ # Introductory workflow samples
diff --git a/dotnet/src/Microsoft.Agents.AI.AGUI/Shared/ChatResponseUpdateAGUIExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AGUI/Shared/ChatResponseUpdateAGUIExtensions.cs
index f5fb103bd4..b11475ddfc 100644
--- a/dotnet/src/Microsoft.Agents.AI.AGUI/Shared/ChatResponseUpdateAGUIExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AGUI/Shared/ChatResponseUpdateAGUIExtensions.cs
@@ -341,8 +341,17 @@ internal static class ChatResponseUpdateAGUIExtensions
};
string? currentMessageId = null;
+ string? streamingMessageId = null;
await foreach (var chatResponse in updates.WithCancellation(cancellationToken).ConfigureAwait(false))
{
+ // Generate a fallback MessageId when the provider doesn't supply one.
+ // This ensures all AGUI events have a valid messageId regardless of agent type.
+ if (string.IsNullOrWhiteSpace(chatResponse.MessageId))
+ {
+ streamingMessageId ??= Guid.NewGuid().ToString("N");
+ chatResponse.MessageId = streamingMessageId;
+ }
+
if (chatResponse is { Contents.Count: > 0 } &&
chatResponse.Contents[0] is TextContent &&
!string.Equals(currentMessageId, chatResponse.MessageId, StringComparison.Ordinal))
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClient.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClient.cs
index 32bb08674b..ec788233ed 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClient.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClient.cs
@@ -44,7 +44,7 @@ internal sealed class AzureAIProjectChatClient : DelegatingChatClient
{
this._agentClient = aiProjectClient;
this._agentReference = Throw.IfNull(agentReference);
- this._metadata = new ChatClientMetadata("azure.ai.agents", defaultModelId: defaultModelId);
+ this._metadata = new ChatClientMetadata("microsoft.foundry", defaultModelId: defaultModelId);
this._chatOptions = chatOptions;
}
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs
index b129f4b1f2..479ab894ae 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs
@@ -14,6 +14,7 @@ using Azure.AI.Projects.Agents;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.AzureAI;
using Microsoft.Extensions.AI;
+using Microsoft.Extensions.Logging;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
using OpenAI;
@@ -42,7 +43,7 @@ public static partial class AzureAIProjectChatClientExtensions
/// When instantiating a by using an , minimal information will be available about the agent in the instance level, and any logic that relies
/// on to retrieve information about the agent like will receive as the result.
///
- public static ChatClientAgent AsAIAgent(
+ public static FoundryAgent AsAIAgent(
this AIProjectClient aiProjectClient,
AgentReference agentReference,
IList? tools = null,
@@ -53,7 +54,7 @@ public static partial class AzureAIProjectChatClientExtensions
Throw.IfNull(agentReference);
ThrowIfInvalidAgentName(agentReference.Name);
- return AsChatClientAgent(
+ var innerAgent = AsChatClientAgent(
aiProjectClient,
agentReference,
new ChatClientAgentOptions()
@@ -64,6 +65,8 @@ public static partial class AzureAIProjectChatClientExtensions
},
clientFactory,
services);
+
+ return new FoundryAgent(aiProjectClient, innerAgent);
}
///
@@ -79,7 +82,8 @@ public static partial class AzureAIProjectChatClientExtensions
/// Thrown when or is .
/// Thrown when is empty or whitespace, or when the agent with the specified name was not found.
/// The agent with the specified name was not found.
- public static async Task GetAIAgentAsync(
+ [Obsolete("Use native AIProjectClient agent APIs and AsAIAgent(AgentRecord/AgentVersion) instead.")]
+ public static async Task GetAIAgentAsync(
this AIProjectClient aiProjectClient,
string name,
IList? tools = null,
@@ -110,7 +114,7 @@ public static partial class AzureAIProjectChatClientExtensions
/// An optional to use for resolving services required by the instances being invoked.
/// A instance that can be used to perform operations based on the latest version of the Azure AI Agent.
/// Thrown when or is .
- public static ChatClientAgent AsAIAgent(
+ public static FoundryAgent AsAIAgent(
this AIProjectClient aiProjectClient,
AgentRecord agentRecord,
IList? tools = null,
@@ -122,13 +126,15 @@ public static partial class AzureAIProjectChatClientExtensions
var allowDeclarativeMode = tools is not { Count: > 0 };
- return AsChatClientAgent(
+ var innerAgent = AsChatClientAgent(
aiProjectClient,
agentRecord,
tools,
clientFactory,
!allowDeclarativeMode,
services);
+
+ return new FoundryAgent(aiProjectClient, innerAgent);
}
///
@@ -141,7 +147,7 @@ public static partial class AzureAIProjectChatClientExtensions
/// An optional to use for resolving services required by the instances being invoked.
/// A instance that can be used to perform operations based on the provided version of the Azure AI Agent.
/// Thrown when or is .
- public static ChatClientAgent AsAIAgent(
+ public static FoundryAgent AsAIAgent(
this AIProjectClient aiProjectClient,
AgentVersion agentVersion,
IList? tools = null,
@@ -153,13 +159,15 @@ public static partial class AzureAIProjectChatClientExtensions
var allowDeclarativeMode = tools is not { Count: > 0 };
- return AsChatClientAgent(
+ var innerAgent = AsChatClientAgent(
aiProjectClient,
agentVersion,
tools,
clientFactory,
!allowDeclarativeMode,
services);
+
+ return new FoundryAgent(aiProjectClient, innerAgent);
}
///
@@ -172,7 +180,8 @@ public static partial class AzureAIProjectChatClientExtensions
/// A to cancel the operation if needed.
/// A instance that can be used to perform operations on the newly created agent.
/// Thrown when or is .
- public static async Task GetAIAgentAsync(
+ [Obsolete("Use native AIProjectClient agent APIs and AsAIAgent(AgentRecord/AgentVersion) instead.")]
+ public static async Task GetAIAgentAsync(
this AIProjectClient aiProjectClient,
ChatClientAgentOptions options,
Func? clientFactory = null,
@@ -194,12 +203,9 @@ public static partial class AzureAIProjectChatClientExtensions
var agentOptions = CreateChatClientAgentOptions(agentVersion, options, requireInvocableTools: !options.UseProvidedChatClientAsIs);
- return AsChatClientAgent(
+ return new FoundryAgent(
aiProjectClient,
- agentVersion,
- agentOptions,
- clientFactory,
- services);
+ AsChatClientAgent(aiProjectClient, agentVersion, agentOptions, clientFactory, services));
}
///
@@ -218,7 +224,8 @@ public static partial class AzureAIProjectChatClientExtensions
/// Thrown when , , or is .
/// Thrown when or is empty or whitespace.
/// When using prompt agent definitions with tools the parameter needs to be provided.
- public static Task CreateAIAgentAsync(
+ [Obsolete("Use native AIProjectClient.Agents APIs instead.")]
+ public static Task CreateAIAgentAsync(
this AIProjectClient aiProjectClient,
string name,
string model,
@@ -256,7 +263,8 @@ public static partial class AzureAIProjectChatClientExtensions
/// A instance that can be used to perform operations on the newly created agent.
/// Thrown when or is .
/// Thrown when is empty or whitespace, or when the agent name is not provided in the options.
- public static async Task CreateAIAgentAsync(
+ [Obsolete("Use native AIProjectClient.Agents APIs instead.")]
+ public static async Task CreateAIAgentAsync(
this AIProjectClient aiProjectClient,
string model,
ChatClientAgentOptions options,
@@ -267,7 +275,6 @@ public static partial class AzureAIProjectChatClientExtensions
Throw.IfNull(aiProjectClient);
Throw.IfNull(options);
Throw.IfNullOrWhitespace(model);
- const bool RequireInvocableTools = true;
if (string.IsNullOrWhiteSpace(options.Name))
{
@@ -276,43 +283,13 @@ public static partial class AzureAIProjectChatClientExtensions
ThrowIfInvalidAgentName(options.Name);
- PromptAgentDefinition agentDefinition = new(model)
- {
- Instructions = options.ChatOptions?.Instructions,
- Temperature = options.ChatOptions?.Temperature,
- TopP = options.ChatOptions?.TopP,
- TextOptions = new() { TextFormat = ToOpenAIResponseTextFormat(options.ChatOptions?.ResponseFormat, options.ChatOptions) }
- };
+ AgentVersion agentVersion = await CreateAgentVersionFromOptionsAsync(aiProjectClient, model, options, cancellationToken).ConfigureAwait(false);
- // Map reasoning options from the abstraction-level ChatOptions.Reasoning,
- // falling back to extracting from the raw representation factory for breaking glass scenarios.
- if (options.ChatOptions?.Reasoning is { } reasoning)
- {
- agentDefinition.ReasoningOptions = ToResponseReasoningOptions(reasoning);
- }
- else if (options.ChatOptions?.RawRepresentationFactory?.Invoke(new NoOpChatClient()) is CreateResponseOptions respCreationOptions)
- {
- agentDefinition.ReasoningOptions = respCreationOptions.ReasoningOptions;
- }
+ var agentOptions = CreateChatClientAgentOptions(agentVersion, options, requireInvocableTools: true);
- ApplyToolsToAgentDefinition(agentDefinition, options.ChatOptions?.Tools);
-
- AgentVersionCreationOptions? creationOptions = new(agentDefinition);
- if (!string.IsNullOrWhiteSpace(options.Description))
- {
- creationOptions.Description = options.Description;
- }
-
- AgentVersion agentVersion = await CreateAgentVersionWithProtocolAsync(aiProjectClient, options.Name, creationOptions, cancellationToken).ConfigureAwait(false);
-
- var agentOptions = CreateChatClientAgentOptions(agentVersion, options, RequireInvocableTools);
-
- return AsChatClientAgent(
+ return new FoundryAgent(
aiProjectClient,
- agentVersion,
- agentOptions,
- clientFactory,
- services);
+ AsChatClientAgent(aiProjectClient, agentVersion, agentOptions, clientFactory, services));
}
///
@@ -330,7 +307,8 @@ public static partial class AzureAIProjectChatClientExtensions
/// When using this extension method with a the tools are only declarative and not invocable.
/// Invocation of any in-process tools will need to be handled manually.
///
- public static Task CreateAIAgentAsync(
+ [Obsolete("Use native AIProjectClient.Agents APIs instead.")]
+ public static Task CreateAIAgentAsync(
this AIProjectClient aiProjectClient,
string name,
AgentVersionCreationOptions creationOptions,
@@ -351,6 +329,75 @@ public static partial class AzureAIProjectChatClientExtensions
cancellationToken);
}
+ ///
+ /// Creates a non-versioned backed by the project's Responses API using the specified model and instructions.
+ ///
+ /// The to use for Responses API calls. Cannot be .
+ /// The model deployment name to use for the agent. Cannot be or whitespace.
+ /// The instructions that guide the agent's behavior. Cannot be or whitespace.
+ /// Optional name for the agent.
+ /// Optional human-readable description for the agent.
+ /// Optional collection of tools that the agent can invoke during conversations.
+ /// Provides a way to customize the creation of the underlying used by the agent.
+ /// Optional logger factory for creating loggers used by the agent.
+ /// An optional to use for resolving services required by the instances being invoked.
+ /// A backed by the project's Responses API.
+ /// Thrown when is .
+ /// Thrown when or is empty or whitespace.
+ public static FoundryAgent AsAIAgent(
+ this AIProjectClient aiProjectClient,
+ string model,
+ string instructions,
+ string? name = null,
+ string? description = null,
+ IList? tools = null,
+ Func? clientFactory = null,
+ ILoggerFactory? loggerFactory = null,
+ IServiceProvider? services = null)
+ {
+ Throw.IfNull(aiProjectClient);
+ Throw.IfNullOrWhitespace(model);
+ Throw.IfNullOrWhitespace(instructions);
+
+ ChatClientAgentOptions options = new()
+ {
+ Name = name,
+ Description = description,
+ ChatOptions = new ChatOptions
+ {
+ ModelId = model,
+ Instructions = instructions,
+ Tools = tools,
+ },
+ };
+
+ return new FoundryAgent(aiProjectClient, CreateResponsesChatClientAgent(aiProjectClient, options, clientFactory, loggerFactory, services));
+ }
+
+ ///
+ /// Creates a non-versioned backed by the project's Responses API using the specified options.
+ ///
+ /// The to use for Responses API calls. Cannot be .
+ /// Configuration options that control the agent's behavior. is required.
+ /// Provides a way to customize the creation of the underlying used by the agent.
+ /// Optional logger factory for creating loggers used by the agent.
+ /// An optional to use for resolving services required by the instances being invoked.
+ /// A backed by the project's Responses API.
+ /// Thrown when or is .
+ /// Thrown when does not specify .
+ public static FoundryAgent AsAIAgent(
+ this AIProjectClient aiProjectClient,
+ ChatClientAgentOptions options,
+ Func? clientFactory = null,
+ ILoggerFactory? loggerFactory = null,
+ IServiceProvider? services = null)
+ {
+ Throw.IfNull(aiProjectClient);
+ Throw.IfNull(options);
+
+ return new FoundryAgent(aiProjectClient, CreateResponsesChatClientAgent(aiProjectClient, options, clientFactory, loggerFactory, services));
+ }
+
#region Private
private static readonly ModelReaderWriterOptions s_modelWriterOptionsWire = new("W");
@@ -358,7 +405,7 @@ public static partial class AzureAIProjectChatClientExtensions
///
/// Asynchronously retrieves an agent record by name using the protocol method to inject user-agent headers.
///
- private static async Task GetAgentRecordByNameAsync(AIProjectClient aiProjectClient, string agentName, CancellationToken cancellationToken)
+ internal static async Task GetAgentRecordByNameAsync(AIProjectClient aiProjectClient, string agentName, CancellationToken cancellationToken)
{
ClientResult protocolResponse = await aiProjectClient.Agents.GetAgentAsync(agentName, cancellationToken.ToRequestOptions(false)).ConfigureAwait(false);
var rawResponse = protocolResponse.GetRawResponse();
@@ -369,7 +416,7 @@ public static partial class AzureAIProjectChatClientExtensions
///
/// Asynchronously creates an agent version using the protocol method to inject user-agent headers.
///
- private static async Task CreateAgentVersionWithProtocolAsync(AIProjectClient aiProjectClient, string agentName, AgentVersionCreationOptions creationOptions, CancellationToken cancellationToken)
+ internal static async Task CreateAgentVersionWithProtocolAsync(AIProjectClient aiProjectClient, string agentName, AgentVersionCreationOptions creationOptions, CancellationToken cancellationToken)
{
BinaryData serializedOptions = ModelReaderWriter.Write(creationOptions, s_modelWriterOptionsWire, AzureAIProjectsAgentsContext.Default);
BinaryContent content = BinaryContent.Create(serializedOptions);
@@ -379,7 +426,7 @@ public static partial class AzureAIProjectChatClientExtensions
return result ?? throw new InvalidOperationException($"Failed to create agent version for agent '{agentName}'.");
}
- private static async Task CreateAIAgentAsync(
+ private static async Task CreateAIAgentAsync(
this AIProjectClient aiProjectClient,
string name,
IList? tools,
@@ -397,17 +444,61 @@ public static partial class AzureAIProjectChatClientExtensions
AgentVersion agentVersion = await CreateAgentVersionWithProtocolAsync(aiProjectClient, name, creationOptions, cancellationToken).ConfigureAwait(false);
- return AsChatClientAgent(
- aiProjectClient,
- agentVersion,
- tools,
- clientFactory,
- !allowDeclarativeMode,
- services);
+ return new FoundryAgent(aiProjectClient, AsChatClientAgent(aiProjectClient, agentVersion, tools, clientFactory, !allowDeclarativeMode, services));
}
- /// This method creates an with the specified ChatClientAgentOptions.
- private static ChatClientAgent AsChatClientAgent(
+ ///
+ /// Creates an agent version with optional tool application, using the protocol method to inject user-agent headers.
+ ///
+ internal static async Task CreateAgentVersionWithProtocolAsync(AIProjectClient aiProjectClient, string agentName, AgentVersionCreationOptions creationOptions, IList? tools, CancellationToken cancellationToken)
+ {
+ if (tools is { Count: > 0 })
+ {
+ ApplyToolsToAgentDefinition(creationOptions.Definition, tools);
+ }
+
+ return await CreateAgentVersionWithProtocolAsync(aiProjectClient, agentName, creationOptions, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Creates an agent version from , mapping options to a .
+ ///
+ internal static async Task CreateAgentVersionFromOptionsAsync(
+ AIProjectClient aiProjectClient,
+ string model,
+ ChatClientAgentOptions options,
+ CancellationToken cancellationToken)
+ {
+ PromptAgentDefinition agentDefinition = new(model)
+ {
+ Instructions = options.ChatOptions?.Instructions,
+ Temperature = options.ChatOptions?.Temperature,
+ TopP = options.ChatOptions?.TopP,
+ TextOptions = new() { TextFormat = ToOpenAIResponseTextFormat(options.ChatOptions?.ResponseFormat, options.ChatOptions) }
+ };
+
+ if (options.ChatOptions?.Reasoning is { } reasoning)
+ {
+ agentDefinition.ReasoningOptions = ToResponseReasoningOptions(reasoning);
+ }
+ else if (options.ChatOptions?.RawRepresentationFactory?.Invoke(new NoOpChatClient()) is CreateResponseOptions respCreationOptions)
+ {
+ agentDefinition.ReasoningOptions = respCreationOptions.ReasoningOptions;
+ }
+
+ ApplyToolsToAgentDefinition(agentDefinition, options.ChatOptions?.Tools);
+
+ AgentVersionCreationOptions creationOptions = new(agentDefinition);
+ if (!string.IsNullOrWhiteSpace(options.Description))
+ {
+ creationOptions.Description = options.Description;
+ }
+
+ return await CreateAgentVersionWithProtocolAsync(aiProjectClient, options.Name!, creationOptions, cancellationToken).ConfigureAwait(false);
+ }
+
+ /// Creates a with the specified options.
+ internal static ChatClientAgent CreateChatClientAgent(
AIProjectClient aiProjectClient,
AgentVersion agentVersion,
ChatClientAgentOptions agentOptions,
@@ -424,6 +515,37 @@ public static partial class AzureAIProjectChatClientExtensions
return new ChatClientAgent(chatClient, agentOptions, services: services);
}
+ internal static ChatClientAgent CreateResponsesChatClientAgent(
+ AIProjectClient aiProjectClient,
+ ChatClientAgentOptions agentOptions,
+ Func? clientFactory,
+ ILoggerFactory? loggerFactory,
+ IServiceProvider? services)
+ {
+ Throw.IfNull(aiProjectClient);
+ Throw.IfNull(agentOptions);
+ Throw.IfNull(agentOptions.ChatOptions);
+ Throw.IfNullOrWhitespace(agentOptions.ChatOptions.ModelId);
+
+ IChatClient chatClient = new AzureAIProjectResponsesChatClient(aiProjectClient, agentOptions.ChatOptions.ModelId);
+
+ if (clientFactory is not null)
+ {
+ chatClient = clientFactory(chatClient);
+ }
+
+ return new ChatClientAgent(chatClient, agentOptions, loggerFactory, services);
+ }
+
+ /// This method creates an with the specified ChatClientAgentOptions.
+ private static ChatClientAgent AsChatClientAgent(
+ AIProjectClient aiProjectClient,
+ AgentVersion agentVersion,
+ ChatClientAgentOptions agentOptions,
+ Func? clientFactory,
+ IServiceProvider? services)
+ => CreateChatClientAgent(aiProjectClient, agentVersion, agentOptions, clientFactory, services);
+
/// This method creates an with the specified ChatClientAgentOptions.
private static ChatClientAgent AsChatClientAgent(
AIProjectClient aiProjectClient,
@@ -503,7 +625,7 @@ public static partial class AzureAIProjectChatClientExtensions
/// This method rebuilds the agent options from the agent definition returned by the version and combine with the in-proc tools when provided
/// this ensures that all required tools are provided and the definition of the agent options are consistent with the agent definition coming from the server.
///
- private static ChatClientAgentOptions CreateChatClientAgentOptions(AgentVersion agentVersion, ChatOptions? chatOptions, bool requireInvocableTools)
+ internal static ChatClientAgentOptions CreateChatClientAgentOptions(AgentVersion agentVersion, ChatOptions? chatOptions, bool requireInvocableTools)
{
var agentDefinition = agentVersion.Definition;
@@ -591,7 +713,7 @@ public static partial class AzureAIProjectChatClientExtensions
/// Specifies whether the returned options must include invocable tools. Set to to require
/// invocable tools; otherwise, .
/// A instance configured according to the specified parameters.
- private static ChatClientAgentOptions CreateChatClientAgentOptions(AgentVersion agentVersion, ChatClientAgentOptions? options, bool requireInvocableTools)
+ internal static ChatClientAgentOptions CreateChatClientAgentOptions(AgentVersion agentVersion, ChatClientAgentOptions? options, bool requireInvocableTools)
{
var agentOptions = CreateChatClientAgentOptions(agentVersion, options?.ChatOptions, requireInvocableTools);
if (options is not null)
@@ -768,7 +890,7 @@ public static partial class AzureAIProjectChatClientExtensions
private static Regex AgentNameValidationRegex() => new("^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$");
#endif
- private static string ThrowIfInvalidAgentName(string? name)
+ internal static string ThrowIfInvalidAgentName(string? name)
{
Throw.IfNullOrWhitespace(name);
if (!AgentNameValidationRegex().IsMatch(name))
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectResponsesChatClient.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectResponsesChatClient.cs
new file mode 100644
index 0000000000..48bb20d766
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectResponsesChatClient.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Azure.AI.Projects;
+using Microsoft.Extensions.AI;
+using Microsoft.Shared.Diagnostics;
+
+namespace Microsoft.Agents.AI.AzureAI;
+
+#pragma warning disable OPENAI001
+internal sealed class AzureAIProjectResponsesChatClient : DelegatingChatClient
+{
+ private readonly ChatClientMetadata _metadata;
+ private readonly AIProjectClient _aiProjectClient;
+
+ internal AzureAIProjectResponsesChatClient(AIProjectClient aiProjectClient, string defaultModelId)
+ : base(Throw.IfNull(aiProjectClient)
+ .GetProjectOpenAIClient()
+ .GetProjectResponsesClientForModel(Throw.IfNullOrWhitespace(defaultModelId))
+ .AsIChatClient())
+ {
+ this._aiProjectClient = aiProjectClient;
+ this._metadata = new ChatClientMetadata("microsoft.foundry", defaultModelId: defaultModelId);
+ }
+
+ public override object? GetService(Type serviceType, object? serviceKey = null)
+ {
+ return (serviceKey is null && serviceType == typeof(ChatClientMetadata))
+ ? this._metadata
+ : (serviceKey is null && serviceType == typeof(AIProjectClient))
+ ? this._aiProjectClient
+ : base.GetService(serviceType, serviceKey);
+ }
+}
+#pragma warning restore OPENAI001
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/CompatibilitySuppressions.xml b/dotnet/src/Microsoft.Agents.AI.AzureAI/CompatibilitySuppressions.xml
new file mode 100644
index 0000000000..79cd4b890d
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/CompatibilitySuppressions.xml
@@ -0,0 +1,284 @@
+
+
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.AsAIAgent(Azure.AI.Projects.AIProjectClient,Azure.AI.Projects.OpenAI.AgentRecord,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider)
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.AsAIAgent(Azure.AI.Projects.AIProjectClient,Azure.AI.Projects.OpenAI.AgentReference,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider)
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.AsAIAgent(Azure.AI.Projects.AIProjectClient,Azure.AI.Projects.OpenAI.AgentVersion,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider)
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.CreateAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,Azure.AI.Projects.AgentVersionCreationOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.Threading.CancellationToken)
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.CreateAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.CreateAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,System.String,System.String,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.GetAIAgentAsync(Azure.AI.Projects.AIProjectClient,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.GetAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net10.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.AsAIAgent(Azure.AI.Projects.AIProjectClient,Azure.AI.Projects.OpenAI.AgentRecord,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider)
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.AsAIAgent(Azure.AI.Projects.AIProjectClient,Azure.AI.Projects.OpenAI.AgentReference,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider)
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.AsAIAgent(Azure.AI.Projects.AIProjectClient,Azure.AI.Projects.OpenAI.AgentVersion,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider)
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.CreateAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,Azure.AI.Projects.AgentVersionCreationOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.Threading.CancellationToken)
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.CreateAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.CreateAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,System.String,System.String,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.GetAIAgentAsync(Azure.AI.Projects.AIProjectClient,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.GetAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ lib/net472/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.AsAIAgent(Azure.AI.Projects.AIProjectClient,Azure.AI.Projects.OpenAI.AgentRecord,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider)
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.AsAIAgent(Azure.AI.Projects.AIProjectClient,Azure.AI.Projects.OpenAI.AgentReference,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider)
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.AsAIAgent(Azure.AI.Projects.AIProjectClient,Azure.AI.Projects.OpenAI.AgentVersion,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider)
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.CreateAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,Azure.AI.Projects.AgentVersionCreationOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.Threading.CancellationToken)
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.CreateAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.CreateAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,System.String,System.String,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.GetAIAgentAsync(Azure.AI.Projects.AIProjectClient,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.GetAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net8.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.AsAIAgent(Azure.AI.Projects.AIProjectClient,Azure.AI.Projects.OpenAI.AgentRecord,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider)
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.AsAIAgent(Azure.AI.Projects.AIProjectClient,Azure.AI.Projects.OpenAI.AgentReference,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider)
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.AsAIAgent(Azure.AI.Projects.AIProjectClient,Azure.AI.Projects.OpenAI.AgentVersion,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider)
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.CreateAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,Azure.AI.Projects.AgentVersionCreationOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.Threading.CancellationToken)
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.CreateAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.CreateAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,System.String,System.String,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.GetAIAgentAsync(Azure.AI.Projects.AIProjectClient,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.GetAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/net9.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.AsAIAgent(Azure.AI.Projects.AIProjectClient,Azure.AI.Projects.OpenAI.AgentRecord,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider)
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.AsAIAgent(Azure.AI.Projects.AIProjectClient,Azure.AI.Projects.OpenAI.AgentReference,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider)
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.AsAIAgent(Azure.AI.Projects.AIProjectClient,Azure.AI.Projects.OpenAI.AgentVersion,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider)
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.CreateAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,Azure.AI.Projects.AgentVersionCreationOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.Threading.CancellationToken)
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.CreateAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.CreateAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,System.String,System.String,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.GetAIAgentAsync(Azure.AI.Projects.AIProjectClient,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Azure.AI.Projects.AzureAIProjectChatClientExtensions.GetAIAgentAsync(Azure.AI.Projects.AIProjectClient,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},System.IServiceProvider,System.Threading.CancellationToken)
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.AzureAI.dll
+ true
+
+
\ No newline at end of file
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/FoundryAITool.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/FoundryAITool.cs
new file mode 100644
index 0000000000..80ed48e1df
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/FoundryAITool.cs
@@ -0,0 +1,210 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Diagnostics.CodeAnalysis;
+using Azure.AI.Projects.Agents;
+using Microsoft.Extensions.AI;
+using Microsoft.Shared.DiagnosticIds;
+using OpenAI.Responses;
+
+#pragma warning disable OPENAI001
+
+namespace Microsoft.Agents.AI.AzureAI;
+
+///
+/// Provides factory methods for creating instances from Microsoft Foundry and OpenAI response tools.
+///
+///
+///
+/// This class wraps (Azure.AI.Projects.OpenAI) and (OpenAI SDK) factory methods,
+/// returning directly — eliminating the need for manual casting and .AsAITool() calls.
+///
+///
+/// Instead of writing:
+/// ((ResponseTool)AgentTool.CreateOpenApiTool(definition)).AsAITool()
+/// You can write:
+/// FoundryAITool.CreateOpenApiTool(definition)
+///
+///
+[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
+public static class FoundryAITool
+{
+ ///
+ /// Converts an existing into an .
+ ///
+ /// The response tool to convert.
+ /// An wrapping the provided response tool.
+ public static AITool FromResponseTool(ResponseTool responseTool) => responseTool.AsAITool();
+
+ // --- Azure.AI.Projects.OpenAI AgentTool factories ---
+
+ ///
+ /// Creates an for OpenAPI tool invocations.
+ ///
+ /// The OpenAPI function definition specifying the API endpoint, schema, and authentication.
+ /// An that calls the specified OpenAPI endpoint.
+ public static AITool CreateOpenApiTool(OpenApiFunctionDefinition definition)
+ => ((ResponseTool)AgentTool.CreateOpenApiTool(definition)).AsAITool();
+
+ ///
+ /// Creates an for Bing Grounding search.
+ ///
+ /// The Bing Grounding search configuration options.
+ /// An for Bing Grounding search.
+ public static AITool CreateBingGroundingTool(BingGroundingSearchToolOptions options)
+ => ((ResponseTool)AgentTool.CreateBingGroundingTool(options)).AsAITool();
+
+ ///
+ /// Creates an for Bing Custom Search.
+ ///
+ /// The Bing Custom Search configuration parameters.
+ /// An for Bing Custom Search.
+ public static AITool CreateBingCustomSearchTool(BingCustomSearchToolOptions parameters)
+ => ((ResponseTool)AgentTool.CreateBingCustomSearchTool(parameters)).AsAITool();
+
+ ///
+ /// Creates an for Microsoft Fabric data agent.
+ ///
+ /// The Fabric data agent configuration options.
+ /// An for Microsoft Fabric.
+ public static AITool CreateMicrosoftFabricTool(FabricDataAgentToolOptions options)
+ => ((ResponseTool)AgentTool.CreateMicrosoftFabricTool(options)).AsAITool();
+
+ ///
+ /// Creates an for SharePoint grounding.
+ ///
+ /// The SharePoint grounding configuration options.
+ /// An for SharePoint grounding.
+ public static AITool CreateSharepointTool(SharePointGroundingToolOptions options)
+ => ((ResponseTool)AgentTool.CreateSharepointTool(options)).AsAITool();
+
+ ///
+ /// Creates an for Azure AI Search.
+ ///
+ /// Optional Azure AI Search configuration options.
+ /// An for Azure AI Search.
+ public static AITool CreateAzureAISearchTool(AzureAISearchToolOptions? options = null)
+ => ((ResponseTool)AgentTool.CreateAzureAISearchTool(options)).AsAITool();
+
+ ///
+ /// Creates an for browser automation.
+ ///
+ /// The browser automation configuration parameters.
+ /// An for browser automation.
+ public static AITool CreateBrowserAutomationTool(BrowserAutomationToolOptions parameters)
+ => ((ResponseTool)AgentTool.CreateBrowserAutomationTool(parameters)).AsAITool();
+
+ ///
+ /// Creates an for structured output capture.
+ ///
+ /// The structured output definition.
+ /// An for structured output capture.
+ public static AITool CreateStructuredOutputsTool(StructuredOutputDefinition outputs)
+ => ((ResponseTool)AgentTool.CreateStructuredOutputsTool(outputs)).AsAITool();
+
+ ///
+ /// Creates an for Agent-to-Agent (A2A) communication.
+ ///
+ /// The base URI for the A2A agent.
+ /// Optional path to the agent card.
+ /// An for A2A communication.
+ public static AITool CreateA2ATool(Uri baseUri, string? agentCardPath = null)
+ => AgentTool.CreateA2ATool(baseUri, agentCardPath).AsAITool();
+
+ // --- OpenAI SDK ResponseTool factories ---
+
+ ///
+ /// Creates an for computer use (screen interaction).
+ ///
+ /// The computer tool environment type.
+ /// The display width in pixels.
+ /// The display height in pixels.
+ /// An for computer use.
+ [Experimental("OPENAICUA001")]
+ public static AITool CreateComputerTool(ComputerToolEnvironment environment, int displayWidth, int displayHeight)
+ => ResponseTool.CreateComputerTool(environment, displayWidth, displayHeight).AsAITool();
+
+ ///
+ /// Creates an for function tool invocations.
+ ///
+ /// The name of the function.
+ /// The function parameters schema as JSON.
+ /// Whether strict mode is enabled for parameter validation.
+ /// Optional description of the function.
+ /// An for function invocations.
+ public static AITool CreateFunctionTool(string functionName, BinaryData functionParameters, bool? strictModeEnabled, string? functionDescription = null)
+ => ResponseTool.CreateFunctionTool(functionName, functionParameters, strictModeEnabled, functionDescription).AsAITool();
+
+ ///
+ /// Creates an for file search over vector stores.
+ ///
+ /// The IDs of vector stores to search.
+ /// Optional maximum number of results to return.
+ /// Optional ranking options for search results.
+ /// Optional filters for search results.
+ /// An for file search.
+ public static AITool CreateFileSearchTool(IEnumerable vectorStoreIds, int? maxResultCount = null, FileSearchToolRankingOptions? rankingOptions = null, BinaryData? filters = null)
+ => ResponseTool.CreateFileSearchTool(vectorStoreIds, maxResultCount, rankingOptions, filters).AsAITool();
+
+ ///
+ /// Creates an for web search.
+ ///
+ /// Optional user location for search context.
+ /// Optional search context size.
+ /// Optional search filters.
+ /// An for web search.
+ public static AITool CreateWebSearchTool(WebSearchToolLocation? userLocation = null, WebSearchToolContextSize? searchContextSize = null, WebSearchToolFilters? filters = null)
+ => ResponseTool.CreateWebSearchTool(userLocation, searchContextSize, filters).AsAITool();
+
+ ///
+ /// Creates an for MCP (Model Context Protocol) server tools.
+ ///
+ /// The label for the MCP server.
+ /// The URI of the MCP server.
+ /// Optional authorization token.
+ /// Optional server description.
+ /// Optional custom headers.
+ /// Optional filter for allowed tools.
+ /// Optional tool call approval policy.
+ /// An for MCP server tools.
+ public static AITool CreateMcpTool(string serverLabel, Uri serverUri, string? authorizationToken = null, string? serverDescription = null, IDictionary? headers = null, McpToolFilter? allowedTools = null, McpToolCallApprovalPolicy? toolCallApprovalPolicy = null)
+ => ResponseTool.CreateMcpTool(serverLabel, serverUri, authorizationToken, serverDescription, headers, allowedTools, toolCallApprovalPolicy).AsAITool();
+
+ ///
+ /// Creates an for MCP (Model Context Protocol) server tools using a connector ID.
+ ///
+ /// The label for the MCP server.
+ /// The connector ID for the MCP server.
+ /// Optional authorization token.
+ /// Optional server description.
+ /// Optional custom headers.
+ /// Optional filter for allowed tools.
+ /// Optional tool call approval policy.
+ /// An for MCP server tools.
+ public static AITool CreateMcpTool(string serverLabel, McpToolConnectorId connectorId, string? authorizationToken = null, string? serverDescription = null, IDictionary? headers = null, McpToolFilter? allowedTools = null, McpToolCallApprovalPolicy? toolCallApprovalPolicy = null)
+ => ResponseTool.CreateMcpTool(serverLabel, connectorId, authorizationToken, serverDescription, headers, allowedTools, toolCallApprovalPolicy).AsAITool();
+
+ ///
+ /// Creates an for code interpreter.
+ ///
+ /// The container configuration for the code interpreter.
+ /// An for code interpreter.
+ public static AITool CreateCodeInterpreterTool(CodeInterpreterToolContainer container)
+ => ResponseTool.CreateCodeInterpreterTool(container).AsAITool();
+
+ ///
+ /// Creates an for image generation.
+ ///
+ /// The model to use for image generation.
+ /// Optional image quality setting.
+ /// Optional image size setting.
+ /// Optional output file format.
+ /// Optional output compression factor.
+ /// Optional moderation level.
+ /// Optional background setting.
+ /// Optional input fidelity setting.
+ /// Optional input image mask.
+ /// Optional partial image count.
+ /// An for image generation.
+ public static AITool CreateImageGenerationTool(string model, ImageGenerationToolQuality? quality = null, ImageGenerationToolSize? size = null, ImageGenerationToolOutputFileFormat? outputFileFormat = null, int? outputCompressionFactor = null, ImageGenerationToolModerationLevel? moderationLevel = null, ImageGenerationToolBackground? background = null, ImageGenerationToolInputFidelity? inputFidelity = null, ImageGenerationToolInputImageMask? inputImageMask = null, int? partialImageCount = null)
+ => ResponseTool.CreateImageGenerationTool(model, quality, size, outputFileFormat, outputCompressionFactor, moderationLevel, background, inputFidelity, inputImageMask, partialImageCount).AsAITool();
+}
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/FoundryAgent.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/FoundryAgent.cs
new file mode 100644
index 0000000000..66fa93e39f
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/FoundryAgent.cs
@@ -0,0 +1,208 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ClientModel;
+using System.Diagnostics.CodeAnalysis;
+using Azure.AI.Extensions.OpenAI;
+using Azure.AI.Projects;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.Logging;
+using Microsoft.Shared.DiagnosticIds;
+using Microsoft.Shared.Diagnostics;
+
+namespace Microsoft.Agents.AI.AzureAI;
+
+///
+/// Provides an that uses Microsoft Foundry for AI agent capabilities.
+///
+///
+///
+/// connects to a pre-configured server-side agent in Microsoft Foundry,
+/// wrapping it as an for use with Agent Framework. Unlike the direct
+/// AIProjectClient.AsAIAgent(model, instructions) approach (which creates a local agent
+/// backed by the Responses API without any server-side agent definition),
+/// works with agents that are managed and versioned in the Foundry service.
+///
+///
+/// This class provides convenient access to Foundry-specific features such as server-side
+/// conversation management via .
+///
+///
+/// Instances can be created directly via public constructors or through
+/// AsAIAgent extension methods on .
+///
+///
+[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
+public sealed class FoundryAgent : DelegatingAIAgent
+{
+ private readonly AIProjectClient _aiProjectClient;
+
+ ///
+ /// Initializes a new instance of the class using the direct Responses API path.
+ ///
+ /// The Microsoft Foundry project endpoint.
+ /// The authentication credential.
+ /// The model deployment name.
+ /// The instructions that guide the agent's behavior.
+ /// Optional configuration options for the .
+ /// Optional name for the agent.
+ /// Optional description for the agent.
+ /// Optional tools to use when interacting with the agent.
+ /// Provides a way to customize the creation of the underlying .
+ /// Optional logger factory for creating loggers used by the agent.
+ /// Optional service provider for resolving dependencies required by AI functions.
+ public FoundryAgent(
+ Uri projectEndpoint,
+ AuthenticationTokenProvider credential,
+ string model,
+ string instructions,
+ AIProjectClientOptions? clientOptions = null,
+ string? name = null,
+ string? description = null,
+ IList? tools = null,
+ Func? clientFactory = null,
+ ILoggerFactory? loggerFactory = null,
+ IServiceProvider? services = null)
+ : base(CreateInnerAgent(
+ CreateProjectClient(projectEndpoint, credential, clientOptions),
+ model, instructions, name, description, tools, clientFactory, loggerFactory, services,
+ out var aiProjectClient))
+ {
+ this._aiProjectClient = aiProjectClient;
+ }
+
+ ///
+ /// Initializes a new instance of the class from an agent-specific endpoint.
+ ///
+ /// The agent-specific endpoint URI (must contain the agent name in the path).
+ /// The authentication credential.
+ /// Optional configuration options for the .
+ /// Optional tools to use when interacting with the agent.
+ /// Provides a way to customize the creation of the underlying .
+ /// Optional service provider for resolving dependencies required by AI functions.
+ public FoundryAgent(
+ Uri agentEndpoint,
+ AuthenticationTokenProvider credential,
+ AIProjectClientOptions? clientOptions = null,
+ IList? tools = null,
+ Func? clientFactory = null,
+ IServiceProvider? services = null)
+ : base(CreateInnerAgentFromEndpoint(
+ CreateProjectClient(agentEndpoint, credential, clientOptions),
+ agentEndpoint, tools, clientFactory, services,
+ out var aiProjectClient))
+ {
+ this._aiProjectClient = aiProjectClient;
+ }
+
+ ///
+ /// Internal constructor used by AsAIAgent extension methods that already have an and a configured .
+ ///
+ internal FoundryAgent(AIProjectClient aiProjectClient, ChatClientAgent innerAgent)
+ : base(Throw.IfNull(innerAgent))
+ {
+ this._aiProjectClient = Throw.IfNull(aiProjectClient);
+ }
+
+ #region Convenience methods
+
+ ///
+ /// Creates a server-side conversation session that appears in the Foundry Project UI.
+ ///
+ /// A token to monitor for cancellation requests.
+ /// A linked to the newly created server-side conversation.
+ public async Task CreateConversationSessionAsync(CancellationToken cancellationToken = default)
+ {
+ var conversationsClient = this._aiProjectClient
+ .GetProjectOpenAIClient()
+ .GetProjectConversationsClient();
+
+ var conversation = (await conversationsClient.CreateProjectConversationAsync(options: null, cancellationToken).ConfigureAwait(false)).Value;
+
+ return (ChatClientAgentSession)await ((ChatClientAgent)this.InnerAgent).CreateSessionAsync(conversation.Id, cancellationToken).ConfigureAwait(false);
+ }
+
+ #endregion
+
+ ///
+ public override object? GetService(Type serviceType, object? serviceKey = null)
+ {
+ if (serviceKey is null && serviceType == typeof(AIProjectClient))
+ {
+ return this._aiProjectClient;
+ }
+
+ return base.GetService(serviceType, serviceKey);
+ }
+
+ #region Private helpers
+
+ private static ChatClientAgent CreateInnerAgent(
+ AIProjectClient aiProjectClient,
+ string model, string instructions,
+ string? name, string? description,
+ IList? tools,
+ Func? clientFactory,
+ ILoggerFactory? loggerFactory,
+ IServiceProvider? services,
+ out AIProjectClient outClient)
+ {
+ Throw.IfNullOrWhitespace(model);
+ Throw.IfNullOrWhitespace(instructions);
+
+ outClient = aiProjectClient;
+
+ ChatClientAgentOptions options = new()
+ {
+ Name = name,
+ Description = description,
+ ChatOptions = new ChatOptions
+ {
+ ModelId = model,
+ Instructions = instructions,
+ Tools = tools,
+ },
+ };
+
+ return AzureAIProjectChatClientExtensions.CreateResponsesChatClientAgent(aiProjectClient, options, clientFactory, loggerFactory, services);
+ }
+
+ private static ChatClientAgent CreateInnerAgentFromEndpoint(
+ AIProjectClient aiProjectClient,
+ Uri agentEndpoint,
+ IList? tools,
+ Func? clientFactory,
+ IServiceProvider? services,
+ out AIProjectClient outClient)
+ {
+ outClient = aiProjectClient;
+
+ AgentReference agentReference = agentEndpoint.Segments[^1].TrimEnd('/');
+
+ ChatClientAgentOptions agentOptions = new()
+ {
+ Name = agentReference.Name,
+ ChatOptions = new() { Tools = tools },
+ };
+
+ IChatClient chatClient = new AzureAIProjectChatClient(aiProjectClient, agentReference, defaultModelId: null, agentOptions.ChatOptions);
+
+ if (clientFactory is not null)
+ {
+ chatClient = clientFactory(chatClient);
+ }
+
+ return new ChatClientAgent(chatClient, agentOptions, services: services);
+ }
+
+ private static AIProjectClient CreateProjectClient(Uri endpoint, AuthenticationTokenProvider credential, AIProjectClientOptions? clientOptions = null)
+ {
+ Throw.IfNull(endpoint);
+ Throw.IfNull(credential);
+
+ clientOptions ??= new AIProjectClientOptions();
+ clientOptions.AddPolicy(RequestOptionsExtensions.UserAgentPolicy, System.ClientModel.Primitives.PipelinePosition.PerCall);
+ return new AIProjectClient(endpoint, credential, clientOptions);
+ }
+
+ #endregion
+}
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/RequestOptionsExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/RequestOptionsExtensions.cs
index 722d316330..2705611b57 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/RequestOptionsExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/RequestOptionsExtensions.cs
@@ -7,6 +7,9 @@ namespace Microsoft.Agents.AI;
internal static class RequestOptionsExtensions
{
+ /// Gets the singleton that adds a MEAI user-agent header.
+ internal static PipelinePolicy UserAgentPolicy => MeaiUserAgentPolicy.Instance;
+
/// Creates a configured for use with Foundry Agents.
public static RequestOptions ToRequestOptions(this CancellationToken cancellationToken, bool streaming)
{
diff --git a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProvider.cs b/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProvider.cs
index 6f9f37518e..93b343b2de 100644
--- a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProvider.cs
+++ b/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProvider.cs
@@ -19,7 +19,7 @@ using OpenAI.Responses;
namespace Microsoft.Agents.AI.FoundryMemory;
///
-/// Provides an Azure AI Foundry Memory backed that persists conversation messages as memories
+/// Provides a Microsoft Foundry Memory backed that persists conversation messages as memories
/// and retrieves related memories to augment the agent invocation context.
///
///
@@ -49,7 +49,7 @@ public sealed class FoundryMemoryProvider : AIContextProvider
/// Initializes a new instance of the class.
///
/// The Azure AI Project client configured for your Foundry project.
- /// The name of the memory store in Azure AI Foundry.
+ /// The name of the memory store in Microsoft Foundry.
/// A delegate that initializes the provider state on the first invocation, providing the scope for memory storage and retrieval.
/// Provider options.
/// Optional logger factory.
@@ -87,17 +87,7 @@ public sealed class FoundryMemoryProvider : AIContextProvider
public override IReadOnlyList StateKeys => this._stateKeys ??= [this._sessionState.StateKey];
private static Func ValidateStateInitializer(Func stateInitializer) =>
- session =>
- {
- State state = stateInitializer(session);
-
- if (state is null)
- {
- throw new InvalidOperationException("State initializer must return a non-null state.");
- }
-
- return state;
- };
+ session => stateInitializer(session) ?? throw new InvalidOperationException("State initializer must return a non-null state.");
///
protected override async ValueTask ProvideAIContextAsync(InvokingContext context, CancellationToken cancellationToken = default)
@@ -332,7 +322,7 @@ public sealed class FoundryMemoryProvider : AIContextProvider
/// Waits for all pending memory update operations to complete.
///
///
- /// Memory extraction in Azure AI Foundry is asynchronous. This method polls the latest pending update
+ /// Memory extraction in Microsoft Foundry is asynchronous. This method polls the latest pending update
/// and returns when it has completed, failed, or been superseded. Since updates are processed in order,
/// completion of the latest update implies all prior updates have also been processed.
///
diff --git a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderScope.cs b/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderScope.cs
index 717df1d12b..6646c482a3 100644
--- a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderScope.cs
+++ b/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderScope.cs
@@ -9,7 +9,7 @@ namespace Microsoft.Agents.AI.FoundryMemory;
/// Allows scoping of memories for the .
///
///
-/// Azure AI Foundry memories are scoped by a single string identifier that you control.
+/// Microsoft Foundry memories are scoped by a single string identifier that you control.
/// Common patterns include using a user ID, team ID, or other unique identifier
/// to partition memories across different contexts.
///
diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/CompatibilitySuppressions.xml b/dotnet/src/Microsoft.Agents.AI.OpenAI/CompatibilitySuppressions.xml
new file mode 100644
index 0000000000..fe04c26eca
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI.OpenAI/CompatibilitySuppressions.xml
@@ -0,0 +1,109 @@
+
+
+
+
+ CP0002
+ M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)
+ lib/net10.0/Microsoft.Agents.AI.OpenAI.dll
+ lib/net10.0/Microsoft.Agents.AI.OpenAI.dll
+ true
+
+
+ CP0002
+ M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,System.String,System.String,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)
+ lib/net10.0/Microsoft.Agents.AI.OpenAI.dll
+ lib/net10.0/Microsoft.Agents.AI.OpenAI.dll
+ true
+
+
+ CP0002
+ M:OpenAI.Responses.OpenAIResponseClientExtensions.AsIChatClientWithStoredOutputDisabled(OpenAI.Responses.ResponsesClient)
+ lib/net10.0/Microsoft.Agents.AI.OpenAI.dll
+ lib/net10.0/Microsoft.Agents.AI.OpenAI.dll
+ true
+
+
+ CP0002
+ M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)
+ lib/net472/Microsoft.Agents.AI.OpenAI.dll
+ lib/net472/Microsoft.Agents.AI.OpenAI.dll
+ true
+
+
+ CP0002
+ M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,System.String,System.String,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)
+ lib/net472/Microsoft.Agents.AI.OpenAI.dll
+ lib/net472/Microsoft.Agents.AI.OpenAI.dll
+ true
+
+
+ CP0002
+ M:OpenAI.Responses.OpenAIResponseClientExtensions.AsIChatClientWithStoredOutputDisabled(OpenAI.Responses.ResponsesClient)
+ lib/net472/Microsoft.Agents.AI.OpenAI.dll
+ lib/net472/Microsoft.Agents.AI.OpenAI.dll
+ true
+
+
+ CP0002
+ M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)
+ lib/net8.0/Microsoft.Agents.AI.OpenAI.dll
+ lib/net8.0/Microsoft.Agents.AI.OpenAI.dll
+ true
+
+
+ CP0002
+ M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,System.String,System.String,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)
+ lib/net8.0/Microsoft.Agents.AI.OpenAI.dll
+ lib/net8.0/Microsoft.Agents.AI.OpenAI.dll
+ true
+
+
+ CP0002
+ M:OpenAI.Responses.OpenAIResponseClientExtensions.AsIChatClientWithStoredOutputDisabled(OpenAI.Responses.ResponsesClient)
+ lib/net8.0/Microsoft.Agents.AI.OpenAI.dll
+ lib/net8.0/Microsoft.Agents.AI.OpenAI.dll
+ true
+
+
+ CP0002
+ M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)
+ lib/net9.0/Microsoft.Agents.AI.OpenAI.dll
+ lib/net9.0/Microsoft.Agents.AI.OpenAI.dll
+ true
+
+
+ CP0002
+ M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,System.String,System.String,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)
+ lib/net9.0/Microsoft.Agents.AI.OpenAI.dll
+ lib/net9.0/Microsoft.Agents.AI.OpenAI.dll
+ true
+
+
+ CP0002
+ M:OpenAI.Responses.OpenAIResponseClientExtensions.AsIChatClientWithStoredOutputDisabled(OpenAI.Responses.ResponsesClient)
+ lib/net9.0/Microsoft.Agents.AI.OpenAI.dll
+ lib/net9.0/Microsoft.Agents.AI.OpenAI.dll
+ true
+
+
+ CP0002
+ M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)
+ lib/netstandard2.0/Microsoft.Agents.AI.OpenAI.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.OpenAI.dll
+ true
+
+
+ CP0002
+ M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,System.String,System.String,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)
+ lib/netstandard2.0/Microsoft.Agents.AI.OpenAI.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.OpenAI.dll
+ true
+
+
+ CP0002
+ M:OpenAI.Responses.OpenAIResponseClientExtensions.AsIChatClientWithStoredOutputDisabled(OpenAI.Responses.ResponsesClient)
+ lib/netstandard2.0/Microsoft.Agents.AI.OpenAI.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.OpenAI.dll
+ true
+
+
\ No newline at end of file
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/AzureAgentProvider.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/AzureAgentProvider.cs
index bfc7bd36ff..6db870f8ec 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/AzureAgentProvider.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/AzureAgentProvider.cs
@@ -175,7 +175,7 @@ public sealed class AzureAgentProvider(Uri projectEndpoint, TokenCredential proj
AIProjectClient client = this.GetAgentClient();
- agent = client.AsAIAgent(agentVersion, tools: null, clientFactory: null, services: null);
+ agent = client.AsAIAgent(agentVersion);
FunctionInvokingChatClient? functionInvokingClient = agent.GetService();
if (functionInvokingClient is not null)
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/CompatibilitySuppressions.xml b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/CompatibilitySuppressions.xml
new file mode 100644
index 0000000000..3454984fae
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/CompatibilitySuppressions.xml
@@ -0,0 +1,39 @@
+
+
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.Declarative.AzureAgentProvider.get_OpenAIClientOptions
+ lib/net10.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
+ lib/net10.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.Declarative.AzureAgentProvider.get_OpenAIClientOptions
+ lib/net472/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
+ lib/net472/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.Declarative.AzureAgentProvider.get_OpenAIClientOptions
+ lib/net8.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
+ lib/net8.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.Declarative.AzureAgentProvider.get_OpenAIClientOptions
+ lib/net9.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
+ lib/net9.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.Declarative.AzureAgentProvider.get_OpenAIClientOptions
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
+ true
+
+
\ No newline at end of file
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/Microsoft.Agents.AI.Workflows.Declarative.Mcp.csproj b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/Microsoft.Agents.AI.Workflows.Declarative.Mcp.csproj
index f9bf706669..bca32e93fc 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/Microsoft.Agents.AI.Workflows.Declarative.Mcp.csproj
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/Microsoft.Agents.AI.Workflows.Declarative.Mcp.csproj
@@ -13,6 +13,11 @@
+
+
+ false
+
+
Microsoft Agent Framework Declarative Workflows MCP
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/CompatibilitySuppressions.xml b/dotnet/src/Microsoft.Agents.AI.Workflows/CompatibilitySuppressions.xml
new file mode 100644
index 0000000000..f227141706
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows/CompatibilitySuppressions.xml
@@ -0,0 +1,319 @@
+
+
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Config
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Config`1
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.ConfigurationExtensions
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Configured
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Configured`1
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Configured`2
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Config
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Config`1
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.ConfigurationExtensions
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Configured
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Configured`1
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Configured`2
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Config
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Config`1
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.ConfigurationExtensions
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Configured
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Configured`1
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Configured`2
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Config
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Config`1
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.ConfigurationExtensions
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Configured
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Configured`1
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Configured`2
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Config
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Config`1
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.ConfigurationExtensions
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Configured
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Configured`1
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.Workflows.Configured`2
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.AgentWorkflowBuilder.CreateHandoffBuilderWith(Microsoft.Agents.AI.AIAgent)
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.BindExecutor``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.ConfigureFactory``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net10.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.AgentWorkflowBuilder.CreateHandoffBuilderWith(Microsoft.Agents.AI.AIAgent)
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.BindExecutor``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.ConfigureFactory``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ lib/net472/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.AgentWorkflowBuilder.CreateHandoffBuilderWith(Microsoft.Agents.AI.AIAgent)
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.BindExecutor``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.ConfigureFactory``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net8.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.AgentWorkflowBuilder.CreateHandoffBuilderWith(Microsoft.Agents.AI.AIAgent)
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.BindExecutor``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.ConfigureFactory``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ lib/net9.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.AgentWorkflowBuilder.CreateHandoffBuilderWith(Microsoft.Agents.AI.AIAgent)
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.BindExecutor``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
+ CP0002
+ M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.ConfigureFactory``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll
+ true
+
+
\ No newline at end of file
diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs
index 05caff4d83..713361d9be 100644
--- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs
+++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs
@@ -139,8 +139,8 @@ public sealed partial class ChatClientAgent : AIAgent
this._logger = (loggerFactory ?? chatClient.GetService() ?? NullLoggerFactory.Instance).CreateLogger();
- // Warn if using a custom chat client stack with end-of-run persistence but no ChatHistoryPersistingChatClient.
- this.WarnOnMissingPersistingClient();
+ // Warn if using a custom chat client stack with simulated service stored persistence but no ServiceStoredSimulatingChatClient.
+ this.WarnOnMissingServiceStoredSimulatingClient();
}
///
@@ -454,7 +454,7 @@ public sealed partial class ChatClientAgent : AIAgent
/// Notifies the and all of successfully completed messages.
///
///
- /// This method is also called by to persist messages per-service-call.
+ /// This method is also called by to persist messages per-service-call.
///
internal async Task NotifyProvidersOfNewMessagesAsync(
ChatClientAgentSession session,
@@ -463,7 +463,7 @@ public sealed partial class ChatClientAgent : AIAgent
ChatOptions? chatOptions,
CancellationToken cancellationToken)
{
- ChatHistoryProvider? chatHistoryProvider = this.ResolveChatHistoryProvider(chatOptions, session);
+ ChatHistoryProvider? chatHistoryProvider = this.ResolveChatHistoryProvider(chatOptions);
if (chatHistoryProvider is not null)
{
@@ -486,7 +486,7 @@ public sealed partial class ChatClientAgent : AIAgent
/// Notifies the and all of a failure during a service call.
///
///
- /// This method is also called by to report failures per-service-call.
+ /// This method is also called by to report failures per-service-call.
///
internal async Task NotifyProvidersOfFailureAsync(
ChatClientAgentSession session,
@@ -495,7 +495,7 @@ public sealed partial class ChatClientAgent : AIAgent
ChatOptions? chatOptions,
CancellationToken cancellationToken)
{
- ChatHistoryProvider? chatHistoryProvider = this.ResolveChatHistoryProvider(chatOptions, session);
+ ChatHistoryProvider? chatHistoryProvider = this.ResolveChatHistoryProvider(chatOptions);
if (chatHistoryProvider is not null)
{
@@ -701,7 +701,7 @@ public sealed partial class ChatClientAgent : AIAgent
throw new InvalidOperationException("A session must be provided when continuing a background response with a continuation token.");
}
- if ((continuationToken is not null || chatOptions?.AllowBackgroundResponses is true) && this.PersistsChatHistoryPerServiceCall && this._logger.IsEnabled(LogLevel.Warning))
+ if ((continuationToken is not null || chatOptions?.AllowBackgroundResponses is true) && this.SimulatesServiceStoredChatHistory && this._logger.IsEnabled(LogLevel.Warning))
{
var warningAgentName = this.GetLoggingAgentName();
this._logger.LogAgentChatClientBackgroundResponseFallback(this.Id, warningAgentName);
@@ -719,57 +719,6 @@ public sealed partial class ChatClientAgent : AIAgent
throw new InvalidOperationException("Input messages are not allowed when continuing a background response using a continuation token.");
}
- IEnumerable inputMessagesForChatClient = inputMessages;
-
- // Populate the session messages only if we are not continuing an existing response as it's not allowed
- if (chatOptions?.ContinuationToken is null)
- {
- ChatHistoryProvider? chatHistoryProvider = this.ResolveChatHistoryProvider(chatOptions, typedSession);
-
- // Add any existing messages from the session to the messages to be sent to the chat client.
- // The ChatHistoryProvider returns the merged result (history + input messages).
- if (chatHistoryProvider is not null)
- {
- var invokingContext = new ChatHistoryProvider.InvokingContext(this, typedSession, inputMessagesForChatClient);
- inputMessagesForChatClient = await chatHistoryProvider.InvokingAsync(invokingContext, cancellationToken).ConfigureAwait(false);
- }
-
- // If we have an AIContextProvider, we should get context from it, and update our
- // messages and options with the additional context.
- // The AIContextProvider returns the accumulated AIContext (original + new contributions).
- if (this.AIContextProviders is { Count: > 0 } aiContextProviders)
- {
- var aiContext = new AIContext
- {
- Instructions = chatOptions?.Instructions,
- Messages = inputMessagesForChatClient,
- Tools = chatOptions?.Tools
- };
-
- foreach (var aiContextProvider in aiContextProviders)
- {
- var invokingContext = new AIContextProvider.InvokingContext(this, typedSession, aiContext);
- aiContext = await aiContextProvider.InvokingAsync(invokingContext, cancellationToken).ConfigureAwait(false);
- }
-
- // Materialize the accumulated messages and tools once at the end of the provider pipeline.
- inputMessagesForChatClient = aiContext.Messages ?? [];
-
- var tools = aiContext.Tools as IList ?? aiContext.Tools?.ToList();
- if (chatOptions?.Tools is { Count: > 0 } || tools is { Count: > 0 })
- {
- chatOptions ??= new();
- chatOptions.Tools = tools;
- }
-
- if (chatOptions?.Instructions is not null || aiContext.Instructions is not null)
- {
- chatOptions ??= new();
- chatOptions.Instructions = aiContext.Instructions;
- }
- }
- }
-
// If a user provided two different session ids, via the session object and options, we should throw
// since we don't know which one to use.
if (!string.IsNullOrWhiteSpace(typedSession.ConversationId) && !string.IsNullOrWhiteSpace(chatOptions?.ConversationId) && typedSession.ConversationId != chatOptions!.ConversationId)
@@ -788,12 +737,53 @@ public sealed partial class ChatClientAgent : AIAgent
chatOptions.ConversationId = typedSession.ConversationId;
}
- // When per-service-call persistence is active, set a sentinel conversation ID so that
- // FunctionInvokingChatClient treats locally-persisted history the same as service-managed
- // history. This prevents it from adding duplicate FunctionCallContent messages into the
- // request when processing approval responses — the loaded history already contains them.
- // ChatHistoryPersistingChatClient strips the sentinel before forwarding to the inner client.
- chatOptions = this.SetLocalHistoryConversationIdIfNeeded(chatOptions);
+ IEnumerable inputMessagesForChatClient = inputMessages;
+
+ // Populate the session messages only if we are not continuing an existing response as it's not allowed.
+ // When SimulateServiceStoredChatHistory is active, the ServiceStoredSimulatingChatClient
+ // owns the chat history lifecycle — it loads history before each service call. The agent
+ // must not load history itself, as that would result in duplicate messages.
+ if (chatOptions?.ContinuationToken is null && !this.SimulatesServiceStoredChatHistory)
+ {
+ // Add any existing messages from the session to the messages to be sent to the chat client.
+ // The ChatHistoryProvider returns the merged result (history + input messages).
+ inputMessagesForChatClient = await this.LoadChatHistoryAsync(typedSession, inputMessagesForChatClient, chatOptions, cancellationToken).ConfigureAwait(false);
+ }
+
+ // AIContextProviders should always be invoked (unless continuing an existing response)
+ // to contribute additional messages, tools, and instructions — even when the decorator
+ // handles history loading.
+ if (chatOptions?.ContinuationToken is null && this.AIContextProviders is { Count: > 0 } aiContextProviders)
+ {
+ var aiContext = new AIContext
+ {
+ Instructions = chatOptions?.Instructions,
+ Messages = inputMessagesForChatClient,
+ Tools = chatOptions?.Tools
+ };
+
+ foreach (var aiContextProvider in aiContextProviders)
+ {
+ var invokingContext = new AIContextProvider.InvokingContext(this, typedSession, aiContext);
+ aiContext = await aiContextProvider.InvokingAsync(invokingContext, cancellationToken).ConfigureAwait(false);
+ }
+
+ // Materialize the accumulated messages and tools once at the end of the provider pipeline.
+ inputMessagesForChatClient = aiContext.Messages ?? [];
+
+ var tools = aiContext.Tools as IList ?? aiContext.Tools?.ToList();
+ if (chatOptions?.Tools is { Count: > 0 } || tools is { Count: > 0 })
+ {
+ chatOptions ??= new();
+ chatOptions.Tools = tools;
+ }
+
+ if (chatOptions?.Instructions is not null || aiContext.Instructions is not null)
+ {
+ chatOptions ??= new();
+ chatOptions.Instructions = aiContext.Instructions;
+ }
+ }
// Materialize the accumulated messages once at the end of the provider pipeline, reusing the existing list if possible.
List messagesList = inputMessagesForChatClient as List ?? inputMessagesForChatClient.ToList();
@@ -839,8 +829,6 @@ public sealed partial class ChatClientAgent : AIAgent
}
}
- // If we got a conversation id back from the chat client, it means that the service supports server side session storage
- // so we should update the session with the new id.
session.ConversationId = responseConversationId;
}
}
@@ -849,14 +837,14 @@ public sealed partial class ChatClientAgent : AIAgent
/// Updates the session conversation ID at the end of an agent run.
///
///
- /// When a in persist mode handles per-service-call
- /// conversation ID updates, this end-of-run update is skipped. When the decorator is in mark-only
- /// mode or absent, the update is performed here. When is
+ /// When a handles per-service-call
+ /// conversation ID updates, this end-of-run update is skipped. When the decorator is
+ /// absent, the update is performed here. When is
/// (continuation token scenarios), the update is always performed.
///
private void UpdateSessionConversationIdAtEndOfRun(ChatClientAgentSession session, string? responseConversationId, CancellationToken cancellationToken, bool forceUpdate = false)
{
- if (!forceUpdate && this.PersistsChatHistoryPerServiceCall)
+ if (!forceUpdate && this.SimulatesServiceStoredChatHistory)
{
return;
}
@@ -868,10 +856,9 @@ public sealed partial class ChatClientAgent : AIAgent
/// Notifies providers of successfully completed messages at the end of an agent run.
///
///
- /// When a in persist mode handles per-service-call
- /// notification, this end-of-run notification is skipped. When the decorator is in mark-only mode,
- /// only the marked messages are persisted. When no decorator is present (custom stack with
- /// ), all messages are persisted.
+ /// When a handles per-service-call
+ /// notification, this end-of-run notification is skipped. When no decorator is present,
+ /// all messages are persisted.
/// When is (continuation token or
/// background response scenarios), notification is always performed with all messages because
/// per-service-call persistence is unreliable in these scenarios.
@@ -884,19 +871,11 @@ public sealed partial class ChatClientAgent : AIAgent
CancellationToken cancellationToken,
bool forceNotify = false)
{
- if (!forceNotify && this.PersistsChatHistoryPerServiceCall)
+ if (!forceNotify && this.SimulatesServiceStoredChatHistory)
{
return Task.CompletedTask;
}
- if (!forceNotify && this.HasMarkOnlyChatHistoryPersistingClient)
- {
- // In mark-only mode, persist only messages that were marked by the decorator.
- var markedRequestMessages = GetMarkedMessages(requestMessages);
- var markedResponseMessages = GetMarkedMessages(responseMessages);
- return this.NotifyProvidersOfNewMessagesAsync(session, markedRequestMessages, markedResponseMessages, chatOptions, cancellationToken);
- }
-
return this.NotifyProvidersOfNewMessagesAsync(session, requestMessages, responseMessages, chatOptions, cancellationToken);
}
@@ -904,7 +883,7 @@ public sealed partial class ChatClientAgent : AIAgent
/// Notifies providers of a failure at the end of an agent run.
///
///
- /// When a in persist mode handles per-service-call
+ /// When a handles per-service-call
/// notification (including failure), this end-of-run notification is skipped to avoid
/// duplicate notification. In all other cases, failure is reported at the end of the run.
///
@@ -915,7 +894,7 @@ public sealed partial class ChatClientAgent : AIAgent
ChatOptions? chatOptions,
CancellationToken cancellationToken)
{
- if (this.PersistsChatHistoryPerServiceCall)
+ if (this.SimulatesServiceStoredChatHistory)
{
return Task.CompletedTask;
}
@@ -924,60 +903,19 @@ public sealed partial class ChatClientAgent : AIAgent
}
///
- /// Gets a value indicating whether the agent has a
- /// decorator in persist mode (not mark-only), which handles per-service-call persistence.
+ /// Gets a value indicating whether the agent is configured to simulate service-stored chat history.
+ /// When , end-of-run persistence and history loading are skipped because a
+ /// per-service-call decorator (such as or a
+ /// user-supplied equivalent) is expected to handle the history lifecycle.
///
- private bool PersistsChatHistoryPerServiceCall
+ private bool SimulatesServiceStoredChatHistory
{
get
{
- var persistingClient = this.ChatClient.GetService();
- return persistingClient?.MarkOnly == false;
+ return this._agentOptions?.SimulateServiceStoredChatHistory is true;
}
}
- ///
- /// Sets the sentinel on
- /// when per-service-call persistence is active and no real
- /// conversation ID is present.
- ///
- ///
- /// The (possibly new) with the sentinel set, or the original
- /// if no sentinel is needed.
- ///
- private ChatOptions? SetLocalHistoryConversationIdIfNeeded(ChatOptions? chatOptions)
- {
- if (this.PersistsChatHistoryPerServiceCall && string.IsNullOrWhiteSpace(chatOptions?.ConversationId))
- {
- chatOptions ??= new ChatOptions();
- chatOptions.ConversationId = ChatHistoryPersistingChatClient.LocalHistoryConversationId;
- }
-
- return chatOptions;
- }
-
- ///
- /// Gets a value indicating whether the agent has a
- /// decorator in mark-only mode, which marks messages for later persistence at the end of the run.
- ///
- private bool HasMarkOnlyChatHistoryPersistingClient
- {
- get
- {
- var persistingClient = this.ChatClient.GetService();
- return persistingClient?.MarkOnly == true;
- }
- }
-
- ///
- /// Returns only the messages that have been marked as persisted by a in mark-only mode.
- ///
- private static List GetMarkedMessages(IEnumerable messages)
- {
- return messages.Where(m =>
- m.AdditionalProperties?.TryGetValue(ChatHistoryPersistingChatClient.PersistedMarkerKey, out var value) == true && value is true).ToList();
- }
-
///
/// Ensures that contains the resolved session.
///
@@ -985,7 +923,7 @@ public sealed partial class ChatClientAgent : AIAgent
/// The base class sets with the raw session parameter
/// (which may be null) and restores it after each yield in streaming scenarios. After
/// resolves or creates a session, we update the
- /// context so the decorator always has a valid session.
+ /// context so the decorator always has a valid session.
/// The original agent from the context is preserved to maintain the top-of-stack agent in
/// decorated agent scenarios.
///
@@ -1001,36 +939,36 @@ public sealed partial class ChatClientAgent : AIAgent
///
/// Checks for potential misconfiguration when using a custom chat client stack and logs warnings.
///
- private void WarnOnMissingPersistingClient()
+ private void WarnOnMissingServiceStoredSimulatingClient()
{
if (this._agentOptions?.UseProvidedChatClientAsIs is not true)
{
return;
}
- if (this._agentOptions?.PersistChatHistoryAtEndOfRun is not true)
+ if (this._agentOptions?.SimulateServiceStoredChatHistory is not true)
{
return;
}
- var persistingClient = this.ChatClient.GetService();
+ var persistingClient = this.ChatClient.GetService();
if (persistingClient is null && this._logger.IsEnabled(LogLevel.Warning))
{
var loggingAgentName = this.GetLoggingAgentName();
this._logger.LogAgentChatClientMissingPersistingClient(
this.Id,
- loggingAgentName);
+ loggingAgentName); // CodeQL [CWE-359] False positive: Agent name is not personal information, but rather just the name of a code component (agent in this case).
}
}
- private ChatHistoryProvider? ResolveChatHistoryProvider(ChatOptions? chatOptions, ChatClientAgentSession session)
+ private ChatHistoryProvider? ResolveChatHistoryProvider(ChatOptions? chatOptions)
{
- ChatHistoryProvider? provider = session.ConversationId is null ? this.ChatHistoryProvider : null;
+ ChatHistoryProvider? provider = chatOptions?.ConversationId is null ? this.ChatHistoryProvider : null;
// If someone provided an override ChatHistoryProvider via AdditionalProperties, we should use that instead.
if (chatOptions?.AdditionalProperties?.TryGetValue(out ChatHistoryProvider? overrideProvider) is true)
{
- if (session.ConversationId is not null && overrideProvider is not null)
+ if (this._agentOptions?.ThrowOnChatHistoryProviderConflict is true && string.IsNullOrWhiteSpace(chatOptions?.ConversationId) is false)
{
throw new InvalidOperationException(
$"Only {nameof(ChatClientAgentSession.ConversationId)} or {nameof(this.ChatHistoryProvider)} may be used, but not both. The current {nameof(ChatClientAgentSession)} has a {nameof(ChatClientAgentSession.ConversationId)} indicating server-side chat history management, but an override {nameof(this.ChatHistoryProvider)} was provided via {nameof(AgentRunOptions.AdditionalProperties)}.");
@@ -1055,6 +993,29 @@ public sealed partial class ChatClientAgent : AIAgent
return provider;
}
+ ///
+ /// Loads chat history from the resolved and prepends it to the given messages.
+ ///
+ ///
+ /// This method is used by both the agent (during ) and by
+ /// to load history before each service call.
+ ///
+ internal async Task> LoadChatHistoryAsync(
+ ChatClientAgentSession session,
+ IEnumerable messages,
+ ChatOptions? chatOptions,
+ CancellationToken cancellationToken)
+ {
+ var chatHistoryProvider = this.ResolveChatHistoryProvider(chatOptions);
+ if (chatHistoryProvider is null)
+ {
+ return messages;
+ }
+
+ var invokingContext = new ChatHistoryProvider.InvokingContext(this, session, messages);
+ return await chatHistoryProvider.InvokingAsync(invokingContext, cancellationToken).ConfigureAwait(false);
+ }
+
private static ChatClientAgentContinuationToken? WrapContinuationToken(ResponseContinuationToken? continuationToken, IEnumerable? inputMessages = null, List? responseUpdates = null)
{
if (continuationToken is null)
diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentLogMessages.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentLogMessages.cs
index 2a324522a4..5899ef5cb2 100644
--- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentLogMessages.cs
+++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentLogMessages.cs
@@ -72,12 +72,12 @@ internal static partial class ChatClientAgentLogMessages
///
/// Logs a warning when is
- /// and is ,
- /// but no is found in the custom chat client stack.
+ /// and is ,
+ /// but no is found in the custom chat client stack.
///
[LoggerMessage(
Level = LogLevel.Warning,
- Message = "Agent {AgentId}/{AgentName}: PersistChatHistoryAtEndOfRun is enabled with a custom chat client stack (UseProvidedChatClientAsIs), but no ChatHistoryPersistingChatClient was found in the pipeline. All messages will be persisted at the end of the run without marking. This setup is not supported with some other features, e.g. handoffs. Consider adding a ChatHistoryPersistingChatClient to the pipeline using the UseChatHistoryPersisting extension method.")]
+ Message = "Agent {AgentId}/{AgentName}: SimulateServiceStoredChatHistory is enabled with a custom chat client stack (UseProvidedChatClientAsIs), but no ServiceStoredSimulatingChatClient was found in the pipeline. Chat history will not be persisted by ChatClientAgent. Consider adding a ServiceStoredSimulatingChatClient to the pipeline using the UseServiceStoredChatHistorySimulation extension method if you have not added your own persistence mechanism.")]
public static partial void LogAgentChatClientMissingPersistingClient(
this ILogger logger,
string agentId,
@@ -92,7 +92,7 @@ internal static partial class ChatClientAgentLogMessages
///
[LoggerMessage(
Level = LogLevel.Warning,
- Message = "Agent {AgentId}/{AgentName}: Per-service-call persistence is falling back to end-of-run persistence because the run involves background responses. Messages will be marked during the run and persisted at the end.")]
+ Message = "Agent {AgentId}/{AgentName}: SimulateServiceStoredChatHistory is enabled but we have to fall back to end-of-run persistence because the run involves background responses.")]
public static partial void LogAgentChatClientBackgroundResponseFallback(
this ILogger logger,
string agentId,
diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentOptions.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentOptions.cs
index 8df9112446..e3d4326ae6 100644
--- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentOptions.cs
+++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentOptions.cs
@@ -92,54 +92,46 @@ public sealed class ChatClientAgentOptions
public bool ThrowOnChatHistoryProviderConflict { get; set; } = true;
///
- /// Gets or sets a value indicating whether to persist chat history only at the end of the full agent run
- /// rather than after each individual service call.
+ /// Gets or sets a value indicating whether the should simulate
+ /// service-stored chat history behavior using its configured .
///
///
///
- /// By default, persists request and response messages either via
- /// a , or the underlying AI service's chat history storage.
- /// Persistence is done immediately after each call to the AI service within the function invocation loop.
- /// When storing in the underlying AI service, the session's
- /// is also updated after each service call, keeping it in sync with the service-side conversation state.
+ /// When set to , a decorator is
+ /// injected between the and the leaf
+ /// in the chat client pipeline. This decorator takes full ownership of the chat history lifecycle:
+ /// it loads history from the before each service call and persists
+ /// new messages after each service call. It also returns a sentinel
+ /// on the response, causing the to treat the conversation
+ /// as service-managed — clearing accumulated history and not injecting duplicate
+ /// during approval-response processing.
///
///
- /// Setting this property to causes messages to be marked during the function
- /// invocation loop but persisted only at the end of the full agent run, providing atomic run semantics.
- /// Updating the is likewise deferred and
- /// updated only at the end of the run, consistent with atomic run semantics.
- /// A decorator is inserted into the chat client pipeline
- /// in mark-only mode, and the persists only the marked messages at the
- /// end of the run.
+ /// This mode aligns the behavior of framework-managed chat history with service-stored chat history,
+ /// ensuring consistency in how messages are stored and loaded, including during function calling loops
+ /// and tool-call termination scenarios.
///
///
- /// When this option is (the default), the
- /// decorator persists messages and updates the
- /// immediately after each service call. This may leave chat history in a state where
- /// is required to start a new run if the last successful service
- /// call returned .
+ /// When set to (the default), the handles
+ /// chat history persistence at the end of the full agent run via the
+ /// pipeline.
///
///
- /// This option has no effect when is .
- /// When using a custom chat client stack, you can add a
- /// manually via the
+ /// When setting the setting to and
+ /// to , ensure that your custom chat client stack includes a
+ /// to enable per-service-call persistence.
+ /// If no is provided, and you are not storing chat history via other means,
+ /// no chat history may be stored.
+ /// When using a custom chat client stack, you can add a
+ /// manually via the
/// extension method.
///
- ///
- /// Note that when using single threaded service stored chat history, like OpenAI Conversations,
- /// there is only one id, so even if the conversation id is not updated after each service call,
- /// the chat history will still contain intermediate messages. Setting this property to
- /// in this case will therefore have no real effect. Setting this property to when using
- /// OpenAI Responses with response ids on the other hand, allows atomic run semantics, since
- /// each service request produces a new response id, and if the run fails mid-loop, the session will
- /// still contain the pre-run respnose id, allowing the next run to start with a clean slate.
- ///
///
///
/// Default is .
///
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
- public bool PersistChatHistoryAtEndOfRun { get; set; }
+ public bool SimulateServiceStoredChatHistory { get; set; }
///
/// Creates a new instance of with the same values as this instance.
@@ -157,6 +149,6 @@ public sealed class ChatClientAgentOptions
ClearOnChatHistoryProviderConflict = this.ClearOnChatHistoryProviderConflict,
WarnOnChatHistoryProviderConflict = this.WarnOnChatHistoryProviderConflict,
ThrowOnChatHistoryProviderConflict = this.ThrowOnChatHistoryProviderConflict,
- PersistChatHistoryAtEndOfRun = this.PersistChatHistoryAtEndOfRun,
+ SimulateServiceStoredChatHistory = this.SimulateServiceStoredChatHistory,
};
}
diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientBuilderExtensions.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientBuilderExtensions.cs
index a1e8b5f8a5..68e1307eeb 100644
--- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientBuilderExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientBuilderExtensions.cs
@@ -86,25 +86,21 @@ public static class ChatClientBuilderExtensions
services: services);
///
- /// Adds a to the chat client pipeline.
+ /// Adds a to the chat client pipeline.
///
///
///
/// This decorator should be positioned between the and the leaf
- /// in the pipeline. It intercepts service calls to either persist messages
- /// immediately or mark them for later persistence, depending on the parameter.
- ///
- ///
- /// If is set to , the
- /// should be configured with set to
- /// as without this combination, messages will never be persisted when using a for
- /// chat history persistence.
+ /// in the pipeline. It simulates service-stored chat history behavior by
+ /// loading history before each service call, persisting after each call, and returning a sentinel
+ /// on the response.
///
///
/// This extension method is intended for use with custom chat client stacks when
/// is .
/// When is (the default),
- /// the automatically injects this decorator.
+ /// the automatically injects this decorator when
+ /// is .
///
///
/// This decorator only works within the context of a running and will throw an
@@ -112,18 +108,10 @@ public static class ChatClientBuilderExtensions
///
///
/// The to add the decorator to.
- ///
- /// When , messages are marked with metadata but not persisted immediately,
- /// and the session's is not updated.
- /// The will persist only the marked messages and update the
- /// conversation ID at the end of the run.
- /// When (the default), messages are persisted and the conversation ID
- /// is updated immediately after each service call.
- ///
/// The for chaining.
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
- public static ChatClientBuilder UseChatHistoryPersisting(this ChatClientBuilder builder, bool markOnly = false)
+ public static ChatClientBuilder UseServiceStoredChatHistorySimulation(this ChatClientBuilder builder)
{
- return builder.Use(innerClient => new ChatHistoryPersistingChatClient(innerClient, markOnly));
+ return builder.Use(innerClient => new ServiceStoredSimulatingChatClient(innerClient));
}
}
diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientExtensions.cs
index fffac628a6..5251314766 100644
--- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientExtensions.cs
@@ -63,14 +63,17 @@ public static class ChatClientExtensions
});
}
- // ChatHistoryPersistingChatClient is registered after FunctionInvokingChatClient so that it sits
- // between FIC and the leaf client. ChatClientBuilder.Build applies factories in reverse order,
- // making the first Use() call outermost. By adding our decorator second, the resulting pipeline is:
- // FunctionInvokingChatClient → ChatHistoryPersistingChatClient → leaf IChatClient
- // This allows the decorator to persist messages after each individual service call within
- // FIC's function invocation loop, or to mark them for later persistence at the end of the run.
- bool markOnly = options?.PersistChatHistoryAtEndOfRun is true;
- chatBuilder.Use(innerClient => new ChatHistoryPersistingChatClient(innerClient, markOnly));
+ // ServiceStoredSimulatingChatClient is only injected when SimulateServiceStoredChatHistory is enabled.
+ // It is registered after FunctionInvokingChatClient so that it sits between FIC and the leaf client.
+ // ChatClientBuilder.Build applies factories in reverse order, making the first Use() call outermost.
+ // By adding our decorator second, the resulting pipeline is:
+ // FunctionInvokingChatClient → ServiceStoredSimulatingChatClient → leaf IChatClient
+ // This allows the decorator to simulate service-stored chat history by loading history before
+ // each service call, persisting after each call, and returning a sentinel ConversationId.
+ if (options?.SimulateServiceStoredChatHistory is true)
+ {
+ chatBuilder.Use(innerClient => new ServiceStoredSimulatingChatClient(innerClient));
+ }
var agentChatClient = chatBuilder.Build(services);
diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatHistoryPersistingChatClient.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatHistoryPersistingChatClient.cs
deleted file mode 100644
index e733b778eb..0000000000
--- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatHistoryPersistingChatClient.cs
+++ /dev/null
@@ -1,351 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.AI;
-
-namespace Microsoft.Agents.AI;
-
-///
-/// A delegating chat client that notifies and
-/// instances of request and response messages after each individual call to the inner chat client,
-/// or marks messages for later persistence depending on the configured mode.
-///
-///
-///
-/// This decorator is intended to operate between the and the leaf
-/// in a pipeline.
-///
-///
-/// In persist mode (the default), it ensures that providers are notified and the session's
-/// is updated after each service call, so that
-/// intermediate messages (e.g., tool calls and results) are saved even if the process is interrupted
-/// mid-loop.
-///
-///
-/// In mark-only mode ( is ), it marks messages with metadata
-/// but does not notify providers or update the .
-/// Both are deferred to the at the end of the run, providing atomic
-/// run semantics.
-///
-///
-/// This chat client must be used within the context of a running . It retrieves the
-/// current agent and session from , which is set automatically when an agent's
-/// or
-///
-/// method is called. The ensures the run context always contains a resolved session,
-/// even when the caller passes null. An is thrown if no run context is
-/// available or if the agent is not a .
-///
-///
-internal sealed class ChatHistoryPersistingChatClient : DelegatingChatClient
-{
- ///
- /// The key used in and
- /// to mark messages and their content as already persisted to chat history.
- ///
- internal const string PersistedMarkerKey = "_chatHistoryPersisted";
-
- ///
- /// A sentinel value set on by
- /// when per-service-call persistence is active and no real conversation ID exists.
- ///
- ///
- ///
- /// This signals to that the chat history is being managed
- /// externally (by this decorator), which prevents it from adding duplicate
- /// messages into the request during approval-response processing. Without this sentinel,
- /// would reconstruct function-call messages from approval
- /// responses and append them to the original messages — but the loaded history already contains
- /// those same function calls, causing duplicate tool-call entries that the model rejects.
- ///
- ///
- /// This decorator strips the sentinel before forwarding requests to the inner client, so the
- /// underlying model never sees it.
- ///
- ///
- internal const string LocalHistoryConversationId = "_agent_local_history";
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The underlying chat client that will handle the core operations.
- ///
- /// When , messages are marked with metadata but not persisted immediately,
- /// and the session's is not updated.
- /// The will persist only the marked messages and update the
- /// conversation ID at the end of the run.
- /// When (the default), messages are persisted and the conversation ID
- /// is updated immediately after each service call.
- ///
- public ChatHistoryPersistingChatClient(IChatClient innerClient, bool markOnly = false)
- : base(innerClient)
- {
- this.MarkOnly = markOnly;
- }
-
- ///
- /// Gets a value indicating whether this decorator is in mark-only mode.
- ///
- ///
- /// When , messages are marked with metadata but not persisted immediately,
- /// and the session's is not updated.
- /// Both are deferred to the at the end of the run.
- /// When , messages are persisted and the conversation ID is updated
- /// after each service call.
- ///
- public bool MarkOnly { get; }
-
- ///
- public override async Task GetResponseAsync(
- IEnumerable messages,
- ChatOptions? options = null,
- CancellationToken cancellationToken = default)
- {
- var (agent, session) = GetRequiredAgentAndSession();
- options = StripLocalHistoryConversationId(options);
-
- ChatResponse response;
- try
- {
- response = await base.GetResponseAsync(messages, options, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- var newRequestMessagesOnFailure = GetNewRequestMessages(messages);
- await agent.NotifyProvidersOfFailureAsync(session, ex, newRequestMessagesOnFailure, options, cancellationToken).ConfigureAwait(false);
- throw;
- }
-
- var newRequestMessages = GetNewRequestMessages(messages);
-
- if (this.ShouldDeferPersistence(options))
- {
- // In mark-only mode or when resuming from a continuation token, just mark messages
- // for later persistence by ChatClientAgent. Conversation ID and provider notification
- // are deferred to end-of-run. For continuation tokens, the end-of-run handler needs
- // to send the combined data from both the previous and current runs.
- MarkAsPersisted(newRequestMessages);
- MarkAsPersisted(response.Messages);
- }
- else
- {
- // In persist mode, persist immediately and update conversation ID.
- agent.UpdateSessionConversationId(session, response.ConversationId, cancellationToken);
- await agent.NotifyProvidersOfNewMessagesAsync(session, newRequestMessages, response.Messages, options, cancellationToken).ConfigureAwait(false);
- MarkAsPersisted(newRequestMessages);
- MarkAsPersisted(response.Messages);
- }
-
- return response;
- }
-
- ///
- public override async IAsyncEnumerable GetStreamingResponseAsync(
- IEnumerable messages,
- ChatOptions? options = null,
- [EnumeratorCancellation] CancellationToken cancellationToken = default)
- {
- var (agent, session) = GetRequiredAgentAndSession();
- options = StripLocalHistoryConversationId(options);
-
- List responseUpdates = [];
-
- IAsyncEnumerator enumerator;
- try
- {
- enumerator = base.GetStreamingResponseAsync(messages, options, cancellationToken).GetAsyncEnumerator(cancellationToken);
- }
- catch (Exception ex)
- {
- var newRequestMessagesOnFailure = GetNewRequestMessages(messages);
- await agent.NotifyProvidersOfFailureAsync(session, ex, newRequestMessagesOnFailure, options, cancellationToken).ConfigureAwait(false);
- throw;
- }
-
- bool hasUpdates;
- try
- {
- hasUpdates = await enumerator.MoveNextAsync().ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- var newRequestMessagesOnFailure = GetNewRequestMessages(messages);
- await agent.NotifyProvidersOfFailureAsync(session, ex, newRequestMessagesOnFailure, options, cancellationToken).ConfigureAwait(false);
- throw;
- }
-
- while (hasUpdates)
- {
- var update = enumerator.Current;
- responseUpdates.Add(update);
- yield return update;
-
- try
- {
- hasUpdates = await enumerator.MoveNextAsync().ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- var newRequestMessagesOnFailure = GetNewRequestMessages(messages);
- await agent.NotifyProvidersOfFailureAsync(session, ex, newRequestMessagesOnFailure, options, cancellationToken).ConfigureAwait(false);
- throw;
- }
- }
-
- var chatResponse = responseUpdates.ToChatResponse();
- var newRequestMessages = GetNewRequestMessages(messages);
-
- if (this.ShouldDeferPersistence(options))
- {
- // In mark-only mode or when resuming from a continuation token, just mark messages
- // for later persistence by ChatClientAgent. Conversation ID and provider notification
- // are deferred to end-of-run. For continuation tokens, the end-of-run handler needs
- // to send the combined data from both the previous and current runs.
- MarkAsPersisted(newRequestMessages);
- MarkAsPersisted(chatResponse.Messages);
- }
- else
- {
- // In persist mode, persist immediately and update conversation ID.
- agent.UpdateSessionConversationId(session, chatResponse.ConversationId, cancellationToken);
- await agent.NotifyProvidersOfNewMessagesAsync(session, newRequestMessages, chatResponse.Messages, options, cancellationToken).ConfigureAwait(false);
- MarkAsPersisted(newRequestMessages);
- MarkAsPersisted(chatResponse.Messages);
- }
- }
-
- ///
- /// Gets the current and from the run context.
- ///
- private static (ChatClientAgent Agent, ChatClientAgentSession Session) GetRequiredAgentAndSession()
- {
- var runContext = AIAgent.CurrentRunContext
- ?? throw new InvalidOperationException(
- $"{nameof(ChatHistoryPersistingChatClient)} can only be used within the context of a running AIAgent. " +
- "Ensure that the chat client is being invoked as part of an AIAgent.RunAsync or AIAgent.RunStreamingAsync call.");
-
- var chatClientAgent = runContext.Agent.GetService()
- ?? throw new InvalidOperationException(
- $"{nameof(ChatHistoryPersistingChatClient)} can only be used with a {nameof(ChatClientAgent)}. " +
- $"The current agent is of type '{runContext.Agent.GetType().Name}'.");
-
- if (runContext.Session is not ChatClientAgentSession chatClientAgentSession)
- {
- throw new InvalidOperationException(
- $"{nameof(ChatHistoryPersistingChatClient)} requires a {nameof(ChatClientAgentSession)}. " +
- $"The current session is of type '{runContext.Session?.GetType().Name ?? "null"}'.");
- }
-
- return (chatClientAgent, chatClientAgentSession);
- }
-
- ///
- /// Determines whether persistence should be deferred to end-of-run instead of happening immediately.
- ///
- ///
- /// when in mode, when the call is resuming from
- /// a continuation token (since the end-of-run handler needs to combine data from the previous
- /// and current runs), or when background responses are allowed (since the caller may stop
- /// consuming the stream mid-run, preventing the post-stream persistence code from executing).
- ///
- private bool ShouldDeferPersistence(ChatOptions? options)
- {
- return this.MarkOnly || options?.ContinuationToken is not null || options?.AllowBackgroundResponses is true;
- }
-
- ///
- /// Returns only the request messages that have not yet been persisted to chat history.
- ///
- ///
- /// A message is considered already persisted if any of the following is true:
- ///
- /// It has the in its .
- /// It has an of
- /// (indicating it was loaded from chat history and does not need to be re-persisted).
- /// It has and all of its items have the
- /// in their . This handles the
- /// streaming case where reconstructs objects
- /// independently via ToChatResponse(), producing different object references that share the same
- /// underlying instances.
- ///
- ///
- /// A list of request messages that have not yet been persisted.
- /// The full set of request messages to filter.
- private static List GetNewRequestMessages(IEnumerable messages)
- {
- return messages.Where(m => !IsAlreadyPersisted(m)).ToList();
- }
-
- ///
- /// Determines whether a message has already been persisted to chat history by this decorator.
- ///
- private static bool IsAlreadyPersisted(ChatMessage message)
- {
- if (message.AdditionalProperties?.TryGetValue(PersistedMarkerKey, out var value) == true && value is true)
- {
- return true;
- }
-
- if (message.GetAgentRequestMessageSourceType() == AgentRequestMessageSourceType.ChatHistory)
- {
- return true;
- }
-
- // In streaming mode, FunctionInvokingChatClient reconstructs ChatMessage objects via ToChatResponse()
- // independently, producing different ChatMessage instances. However, the underlying AIContent objects
- // (e.g., FunctionCallContent, FunctionResultContent) are shared references. Checking for markers on
- // AIContent handles dedup in this case.
- if (message.Contents.Count > 0 && message.Contents.All(c => c.AdditionalProperties?.TryGetValue(PersistedMarkerKey, out var value) == true && value is true))
- {
- return true;
- }
-
- return false;
- }
-
- ///
- /// Marks the given messages as persisted by setting a marker on both the
- /// and each of its items.
- ///
- ///
- /// Both levels are marked because may reconstruct
- /// objects in streaming mode (losing the message-level marker),
- /// but the references are shared and retain their markers.
- ///
- /// The messages to mark as persisted.
- private static void MarkAsPersisted(IEnumerable messages)
- {
- foreach (var message in messages)
- {
- message.AdditionalProperties ??= new();
- message.AdditionalProperties[PersistedMarkerKey] = true;
-
- foreach (var content in message.Contents)
- {
- content.AdditionalProperties ??= new();
- content.AdditionalProperties[PersistedMarkerKey] = true;
- }
- }
- }
-
- ///
- /// If the carry the sentinel,
- /// returns a clone with the conversation ID cleared so the inner client never sees it.
- /// Otherwise returns the original unchanged.
- ///
- private static ChatOptions? StripLocalHistoryConversationId(ChatOptions? options)
- {
- if (options?.ConversationId == LocalHistoryConversationId)
- {
- options = options.Clone();
- options.ConversationId = null;
- }
-
- return options;
- }
-}
diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ServiceStoredSimulatingChatClient.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ServiceStoredSimulatingChatClient.cs
new file mode 100644
index 0000000000..f7dafa303d
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ServiceStoredSimulatingChatClient.cs
@@ -0,0 +1,273 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.AI;
+
+namespace Microsoft.Agents.AI;
+
+///
+/// A delegating chat client that simulates service-stored chat history behavior using
+/// framework-managed instances.
+///
+///
+///
+/// This decorator is intended to operate between the and the leaf
+/// in a pipeline.
+///
+///
+/// Before each service call, it loads chat history from the agent's
+/// and prepends it to the request messages. After each successful service call, it persists
+/// new request and response messages to the provider. It also returns a sentinel
+/// on the response so that the
+/// treats the conversation as service-managed —
+/// clearing accumulated history between iterations and not injecting duplicate
+/// during approval-response processing.
+///
+///
+/// This chat client must be used within the context of a running . It retrieves the
+/// current agent and session from , which is set automatically when an agent's
+/// or
+///
+/// method is called. The ensures the run context always contains a resolved session,
+/// even when the caller passes null. An is thrown if no run context is
+/// available or if the agent is not a .
+///
+///
+internal sealed class ServiceStoredSimulatingChatClient : DelegatingChatClient
+{
+ ///
+ /// A sentinel value returned on to signal
+ /// that chat history is being managed downstream.
+ ///
+ ///
+ ///
+ /// When sees a non-null ,
+ /// it treats the conversation as service-managed: it clears accumulated history between
+ /// iterations (via FixupHistories) and does not inject
+ /// into the request during approval-response processing (via ProcessFunctionApprovalResponses).
+ ///
+ ///
+ /// This decorator strips the sentinel from on incoming
+ /// requests before forwarding to the inner client, so the underlying model never sees it.
+ ///
+ ///
+ internal const string LocalHistoryConversationId = "_agent_local_chat_history";
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The underlying chat client that will handle the core operations.
+ public ServiceStoredSimulatingChatClient(IChatClient innerClient)
+ : base(innerClient)
+ {
+ }
+
+ ///
+ public override async Task GetResponseAsync(
+ IEnumerable messages,
+ ChatOptions? options = null,
+ CancellationToken cancellationToken = default)
+ {
+ var (agent, session) = GetRequiredAgentAndSession();
+ options = StripLocalHistoryConversationId(options);
+
+ bool isServiceManaged = !string.IsNullOrEmpty(options?.ConversationId);
+ bool isContinuationOrBackground = options?.ContinuationToken is not null
+ || options?.AllowBackgroundResponses is true;
+ bool skipSimulation = isServiceManaged || isContinuationOrBackground;
+
+ var newMessages = messages as IList ?? messages.ToList();
+
+ // When simulating, load history and prepend it. When the service manages
+ // history (real ConversationId) or this is a continuation/background run,
+ // just forward the input messages as-is.
+ var messagesForService = skipSimulation
+ ? newMessages
+ : await agent.LoadChatHistoryAsync(session, newMessages, options, cancellationToken).ConfigureAwait(false);
+
+ ChatResponse response;
+ try
+ {
+ response = await base.GetResponseAsync(messagesForService, options, cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ await agent.NotifyProvidersOfFailureAsync(session, ex, newMessages, options, cancellationToken).ConfigureAwait(false);
+ throw;
+ }
+
+ await agent.NotifyProvidersOfNewMessagesAsync(session, newMessages, response.Messages, options, cancellationToken).ConfigureAwait(false);
+
+ if (isContinuationOrBackground)
+ {
+ // Continuation/background run — the agent's forced end-of-run handles
+ // session ConversationId and persistence; the decorator is a no-op.
+ }
+ else if (isServiceManaged || !string.IsNullOrEmpty(response.ConversationId))
+ {
+ // Service manages history — update session with the real ConversationId.
+ agent.UpdateSessionConversationId(session, response.ConversationId, cancellationToken);
+ }
+ else
+ {
+ // Normal simulated path — set sentinel so FICC treats this as service-managed.
+ SetSentinelConversationId(response, session);
+ }
+
+ return response;
+ }
+
+ ///
+ public override async IAsyncEnumerable GetStreamingResponseAsync(
+ IEnumerable messages,
+ ChatOptions? options = null,
+ [EnumeratorCancellation] CancellationToken cancellationToken = default)
+ {
+ var (agent, session) = GetRequiredAgentAndSession();
+ options = StripLocalHistoryConversationId(options);
+
+ bool isServiceManaged = !string.IsNullOrEmpty(options?.ConversationId);
+ bool isContinuationOrBackground = options?.ContinuationToken is not null
+ || options?.AllowBackgroundResponses is true;
+ bool skipSimulation = isServiceManaged || isContinuationOrBackground;
+
+ var newMessages = messages as IList ?? messages.ToList();
+
+ // When simulating, load history and prepend it. When the service manages
+ // history (real ConversationId) or this is a continuation/background run,
+ // just forward the input messages as-is.
+ var messagesForService = skipSimulation
+ ? newMessages
+ : await agent.LoadChatHistoryAsync(session, newMessages, options, cancellationToken).ConfigureAwait(false);
+
+ List responseUpdates = [];
+
+ IAsyncEnumerator enumerator;
+ try
+ {
+ enumerator = base.GetStreamingResponseAsync(messagesForService, options, cancellationToken).GetAsyncEnumerator(cancellationToken);
+ }
+ catch (Exception ex)
+ {
+ await agent.NotifyProvidersOfFailureAsync(session, ex, newMessages, options, cancellationToken).ConfigureAwait(false);
+ throw;
+ }
+
+ bool hasUpdates;
+ try
+ {
+ hasUpdates = await enumerator.MoveNextAsync().ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ await agent.NotifyProvidersOfFailureAsync(session, ex, newMessages, options, cancellationToken).ConfigureAwait(false);
+ throw;
+ }
+
+ while (hasUpdates)
+ {
+ var update = enumerator.Current;
+ responseUpdates.Add(update);
+
+ // If the service returned a real ConversationId on any update, remember that.
+ // Otherwise stamp our sentinel so FICC treats this as service-managed —
+ // unless this is a continuation/background run where the agent handles everything.
+ if (!string.IsNullOrEmpty(update.ConversationId))
+ {
+ isServiceManaged = true;
+ }
+ else if (!skipSimulation)
+ {
+ update.ConversationId = LocalHistoryConversationId;
+ }
+
+ yield return update;
+
+ try
+ {
+ hasUpdates = await enumerator.MoveNextAsync().ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ await agent.NotifyProvidersOfFailureAsync(session, ex, newMessages, options, cancellationToken).ConfigureAwait(false);
+ throw;
+ }
+ }
+
+ var chatResponse = responseUpdates.ToChatResponse();
+
+ await agent.NotifyProvidersOfNewMessagesAsync(session, newMessages, chatResponse.Messages, options, cancellationToken).ConfigureAwait(false);
+
+ if (isContinuationOrBackground)
+ {
+ // Continuation/background run — the agent's forced end-of-run handles
+ // session ConversationId and persistence; the decorator is a no-op.
+ }
+ else if (isServiceManaged)
+ {
+ // Service manages history — update session with the real ConversationId.
+ agent.UpdateSessionConversationId(session, chatResponse.ConversationId, cancellationToken);
+ }
+ else
+ {
+ // Normal simulated path — set sentinel on session.
+ session.ConversationId = LocalHistoryConversationId;
+ }
+ }
+
+ ///
+ /// Sets the sentinel on the response and session
+ /// so that treats the conversation as service-managed.
+ ///
+ private static void SetSentinelConversationId(ChatResponse response, ChatClientAgentSession session)
+ {
+ response.ConversationId = LocalHistoryConversationId;
+ session.ConversationId = LocalHistoryConversationId;
+ }
+
+ ///
+ /// Gets the current and from the run context.
+ ///
+ private static (ChatClientAgent Agent, ChatClientAgentSession Session) GetRequiredAgentAndSession()
+ {
+ var runContext = AIAgent.CurrentRunContext
+ ?? throw new InvalidOperationException(
+ $"{nameof(ServiceStoredSimulatingChatClient)} can only be used within the context of a running AIAgent. " +
+ "Ensure that the chat client is being invoked as part of an AIAgent.RunAsync or AIAgent.RunStreamingAsync call.");
+
+ var chatClientAgent = runContext.Agent.GetService()
+ ?? throw new InvalidOperationException(
+ $"{nameof(ServiceStoredSimulatingChatClient)} can only be used with a {nameof(ChatClientAgent)}. " +
+ $"The current agent is of type '{runContext.Agent.GetType().Name}'.");
+
+ if (runContext.Session is not ChatClientAgentSession chatClientAgentSession)
+ {
+ throw new InvalidOperationException(
+ $"{nameof(ServiceStoredSimulatingChatClient)} requires a {nameof(ChatClientAgentSession)}. " +
+ $"The current session is of type '{runContext.Session?.GetType().Name ?? "null"}'.");
+ }
+
+ return (chatClientAgent, chatClientAgentSession);
+ }
+
+ ///
+ /// If the carry the sentinel,
+ /// returns a clone with the conversation ID cleared so the inner client never sees it.
+ /// Otherwise returns the original unchanged.
+ ///
+ private static ChatOptions? StripLocalHistoryConversationId(ChatOptions? options)
+ {
+ if (options?.ConversationId == LocalHistoryConversationId)
+ {
+ options = options.Clone();
+ options.ConversationId = null;
+ }
+
+ return options;
+ }
+}
diff --git a/dotnet/src/Microsoft.Agents.AI/CompatibilitySuppressions.xml b/dotnet/src/Microsoft.Agents.AI/CompatibilitySuppressions.xml
new file mode 100644
index 0000000000..941346fbf1
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI/CompatibilitySuppressions.xml
@@ -0,0 +1,74 @@
+
+
+
+
+ CP0001
+ T:Microsoft.Agents.AI.FileAgentSkillsProvider
+ lib/net10.0/Microsoft.Agents.AI.dll
+ lib/net10.0/Microsoft.Agents.AI.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.FileAgentSkillsProviderOptions
+ lib/net10.0/Microsoft.Agents.AI.dll
+ lib/net10.0/Microsoft.Agents.AI.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.FileAgentSkillsProvider
+ lib/net472/Microsoft.Agents.AI.dll
+ lib/net472/Microsoft.Agents.AI.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.FileAgentSkillsProviderOptions
+ lib/net472/Microsoft.Agents.AI.dll
+ lib/net472/Microsoft.Agents.AI.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.FileAgentSkillsProvider
+ lib/net8.0/Microsoft.Agents.AI.dll
+ lib/net8.0/Microsoft.Agents.AI.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.FileAgentSkillsProviderOptions
+ lib/net8.0/Microsoft.Agents.AI.dll
+ lib/net8.0/Microsoft.Agents.AI.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.FileAgentSkillsProvider
+ lib/net9.0/Microsoft.Agents.AI.dll
+ lib/net9.0/Microsoft.Agents.AI.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.FileAgentSkillsProviderOptions
+ lib/net9.0/Microsoft.Agents.AI.dll
+ lib/net9.0/Microsoft.Agents.AI.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.FileAgentSkillsProvider
+ lib/netstandard2.0/Microsoft.Agents.AI.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.dll
+ true
+
+
+ CP0001
+ T:Microsoft.Agents.AI.FileAgentSkillsProviderOptions
+ lib/netstandard2.0/Microsoft.Agents.AI.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.dll
+ true
+
+
\ No newline at end of file
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/AgentInMemorySkillsSource.cs b/dotnet/src/Microsoft.Agents.AI/Skills/AgentInMemorySkillsSource.cs
new file mode 100644
index 0000000000..57c9295c24
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI/Skills/AgentInMemorySkillsSource.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Shared.DiagnosticIds;
+using Microsoft.Shared.Diagnostics;
+
+namespace Microsoft.Agents.AI;
+
+///
+/// A skill source that holds instances in memory.
+///
+[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
+internal sealed class AgentInMemorySkillsSource : AgentSkillsSource
+{
+ private readonly List _skills;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The skills to include in this source.
+ public AgentInMemorySkillsSource(IEnumerable skills)
+ {
+ this._skills = Throw.IfNull(skills).ToList();
+ }
+
+ ///
+ public override Task> GetSkillsAsync(CancellationToken cancellationToken = default)
+ {
+ return Task.FromResult>(this._skills);
+ }
+}
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkill.cs b/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkill.cs
index 5f0a66808d..4c5ca8dbc3 100644
--- a/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkill.cs
+++ b/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkill.cs
@@ -12,7 +12,8 @@ namespace Microsoft.Agents.AI;
///
///
/// A skill represents a domain-specific capability with instructions, resources, and scripts.
-/// Concrete implementations include (filesystem-backed).
+/// Concrete implementations include (filesystem-backed)
+/// and (code-defined).
///
///
/// Skill metadata follows the Agent Skills specification.
@@ -35,6 +36,8 @@ public abstract class AgentSkill
///
///
/// For file-based skills this is the raw SKILL.md file content.
+ /// For code-defined skills this is a synthesized XML document
+ /// containing name, description, and body (instructions, resources, scripts).
///
public abstract string Content { get; }
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs b/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs
index f2f87851c0..70d7939227 100644
--- a/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs
+++ b/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs
@@ -116,6 +116,38 @@ public sealed partial class AgentSkillsProvider : AIContextProvider
{
}
+ ///
+ /// Initializes a new instance of the class
+ /// with one or more inline (code-defined) skills.
+ /// Duplicate skill names are automatically deduplicated (first occurrence wins).
+ ///
+ /// The inline skills to include.
+ public AgentSkillsProvider(params AgentInlineSkill[] skills)
+ : this(skills as IEnumerable)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with inline (code-defined) skills.
+ /// Duplicate skill names are automatically deduplicated (first occurrence wins).
+ ///
+ /// The inline skills to include.
+ /// Optional provider configuration.
+ /// Optional logger factory.
+ public AgentSkillsProvider(
+ IEnumerable skills,
+ AgentSkillsProviderOptions? options = null,
+ ILoggerFactory? loggerFactory = null)
+ : this(
+ new DeduplicatingAgentSkillsSource(
+ new AgentInMemorySkillsSource(Throw.IfNull(skills)),
+ loggerFactory),
+ options,
+ loggerFactory)
+ {
+ }
+
///
/// Initializes a new instance of the class
/// from a custom . Unlike other constructors, this one does not
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProviderBuilder.cs b/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProviderBuilder.cs
index 17d7f2d6f3..8e52cc522e 100644
--- a/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProviderBuilder.cs
+++ b/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProviderBuilder.cs
@@ -13,9 +13,13 @@ namespace Microsoft.Agents.AI;
/// Fluent builder for constructing an backed by a composite source.
///
///
+///
+/// Use this builder to combine multiple skill sources into a single provider:
+///
///
/// var provider = new AgentSkillsProviderBuilder()
/// .UseFileSkills("/path/to/skills")
+/// .UseSkills(myInlineSkill1, myInlineSkill2)
/// .Build();
///
///
@@ -65,6 +69,40 @@ public sealed class AgentSkillsProviderBuilder
return this;
}
+ ///
+ /// Adds a single skill.
+ ///
+ /// The skill to add.
+ /// This builder instance for chaining.
+ public AgentSkillsProviderBuilder UseSkill(AgentSkill skill)
+ {
+ return this.UseSkills(skill);
+ }
+
+ ///
+ /// Adds one or more skills.
+ ///
+ /// The skills to add.
+ /// This builder instance for chaining.
+ public AgentSkillsProviderBuilder UseSkills(params AgentSkill[] skills)
+ {
+ var source = new AgentInMemorySkillsSource(skills);
+ this._sourceFactories.Add((_, _) => source);
+ return this;
+ }
+
+ ///
+ /// Adds skills from the specified collection.
+ ///
+ /// The skills to add.
+ /// This builder instance for chaining.
+ public AgentSkillsProviderBuilder UseSkills(IEnumerable skills)
+ {
+ var source = new AgentInMemorySkillsSource(skills);
+ this._sourceFactories.Add((_, _) => source);
+ return this;
+ }
+
///
/// Adds a custom skill source.
///
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/Programmatic/AgentInlineSkill.cs b/dotnet/src/Microsoft.Agents.AI/Skills/Programmatic/AgentInlineSkill.cs
new file mode 100644
index 0000000000..d326a47a59
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI/Skills/Programmatic/AgentInlineSkill.cs
@@ -0,0 +1,215 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Text;
+using System.Text.Json;
+using Microsoft.Extensions.AI;
+using Microsoft.Shared.DiagnosticIds;
+using Microsoft.Shared.Diagnostics;
+
+namespace Microsoft.Agents.AI;
+
+///
+/// A skill defined entirely in code with resources (static values or delegates) and scripts (delegates).
+///
+///
+/// All calls to ,
+/// , and
+/// must be made before the skill's is first accessed.
+/// Calls made after that point will not be reflected in the generated
+/// . In typical usage, this means configuring all
+/// resources and scripts before registering the skill with an
+/// or .
+///
+[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
+public sealed class AgentInlineSkill : AgentSkill
+{
+ private readonly string _instructions;
+ private List? _resources;
+ private List? _scripts;
+ private string? _cachedContent;
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a pre-built .
+ ///
+ /// The skill frontmatter containing name, description, and other metadata.
+ /// Skill instructions text.
+ public AgentInlineSkill(AgentSkillFrontmatter frontmatter, string instructions)
+ {
+ this.Frontmatter = Throw.IfNull(frontmatter);
+ this._instructions = Throw.IfNullOrWhitespace(instructions);
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with all frontmatter properties specified individually.
+ ///
+ /// Skill name in kebab-case.
+ /// Skill description for discovery.
+ /// Skill instructions text.
+ /// Optional license name or reference.
+ /// Optional compatibility information (max 500 chars).
+ /// Optional space-delimited list of pre-approved tools.
+ /// Optional arbitrary key-value metadata.
+ public AgentInlineSkill(
+ string name,
+ string description,
+ string instructions,
+ string? license = null,
+ string? compatibility = null,
+ string? allowedTools = null,
+ AdditionalPropertiesDictionary? metadata = null)
+ : this(
+ new AgentSkillFrontmatter(name, description, compatibility)
+ {
+ License = license,
+ AllowedTools = allowedTools,
+ Metadata = metadata,
+ },
+ instructions)
+ {
+ }
+
+ ///
+ public override AgentSkillFrontmatter Frontmatter { get; }
+
+ ///
+ public override string Content => this._cachedContent ??= this.BuildContent();
+
+ ///
+ public override IReadOnlyList? Resources => this._resources;
+
+ ///
+ public override IReadOnlyList? Scripts => this._scripts;
+
+ ///
+ /// Registers a static resource with this skill.
+ ///
+ /// The resource name.
+ /// The static resource value.
+ /// An optional description of the resource.
+ /// This instance, for chaining.
+ public AgentInlineSkill AddResource(string name, object value, string? description = null)
+ {
+ (this._resources ??= []).Add(new AgentInlineSkillResource(name, value, description));
+ return this;
+ }
+
+ ///
+ /// Registers a dynamic resource with this skill, backed by a C# delegate.
+ /// The delegate's parameters and return type are automatically marshaled via AIFunctionFactory.
+ ///
+ /// The resource name.
+ /// A method that produces the resource value when requested.
+ /// An optional description of the resource.
+ /// This instance, for chaining.
+ public AgentInlineSkill AddResource(string name, Delegate method, string? description = null)
+ {
+ (this._resources ??= []).Add(new AgentInlineSkillResource(name, method, description));
+ return this;
+ }
+
+ ///
+ /// Registers a script with this skill, backed by a C# delegate.
+ /// The delegate's parameters and return type are automatically marshaled via AIFunctionFactory.
+ ///
+ /// The script name.
+ /// A method to execute when the script is invoked.
+ /// An optional description of the script.
+ /// This instance, for chaining.
+ public AgentInlineSkill AddScript(string name, Delegate method, string? description = null)
+ {
+ (this._scripts ??= []).Add(new AgentInlineSkillScript(name, method, description));
+ return this;
+ }
+
+ private string BuildContent()
+ {
+ var sb = new StringBuilder();
+
+ sb.Append($"{EscapeXmlString(this.Frontmatter.Name)}\n")
+ .Append($"{EscapeXmlString(this.Frontmatter.Description)}\n\n")
+ .Append("\n")
+ .Append(EscapeXmlString(this._instructions))
+ .Append("\n");
+
+ if (this.Resources is { Count: > 0 })
+ {
+ sb.Append("\n\n\n");
+ foreach (var resource in this.Resources)
+ {
+ if (resource.Description is not null)
+ {
+ sb.Append($" \n");
+ }
+ else
+ {
+ sb.Append($" \n");
+ }
+ }
+
+ sb.Append("");
+ }
+
+ if (this.Scripts is { Count: > 0 })
+ {
+ sb.Append("\n\n\n");
+ foreach (var script in this.Scripts)
+ {
+ JsonElement? parametersSchema = ((AgentInlineSkillScript)script).ParametersSchema;
+
+ if (script.Description is null && parametersSchema is null)
+ {
+ sb.Append($" \n");
+ }
+ }
+
+ sb.Append("");
+ }
+
+ return sb.ToString();
+ }
+
+ ///
+ /// Escapes XML special characters: always escapes &, <, >,
+ /// ", and '. When is ,
+ /// quotes are left unescaped to preserve readability of embedded content such as JSON.
+ ///
+ /// The string to escape.
+ ///
+ /// When , leaves " and ' unescaped for use in XML element content (e.g., JSON).
+ /// When (default), escapes all XML special characters including quotes.
+ ///
+ private static string EscapeXmlString(string value, bool preserveQuotes = false)
+ {
+ var result = value
+ .Replace("&", "&")
+ .Replace("<", "<")
+ .Replace(">", ">");
+
+ if (!preserveQuotes)
+ {
+ result = result
+ .Replace("\"", """)
+ .Replace("'", "'");
+ }
+
+ return result;
+ }
+}
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/Programmatic/AgentInlineSkillResource.cs b/dotnet/src/Microsoft.Agents.AI/Skills/Programmatic/AgentInlineSkillResource.cs
new file mode 100644
index 0000000000..38791c3b21
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI/Skills/Programmatic/AgentInlineSkillResource.cs
@@ -0,0 +1,60 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.AI;
+using Microsoft.Shared.DiagnosticIds;
+using Microsoft.Shared.Diagnostics;
+
+namespace Microsoft.Agents.AI;
+
+///
+/// A skill resource defined in code, backed by either a static value or a delegate.
+///
+[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
+internal sealed class AgentInlineSkillResource : AgentSkillResource
+{
+ private readonly object? _value;
+ private readonly AIFunction? _function;
+
+ ///
+ /// Initializes a new instance of the class with a static value.
+ /// The value is returned as-is when is called.
+ ///
+ /// The resource name.
+ /// The static resource value.
+ /// An optional description of the resource.
+ public AgentInlineSkillResource(string name, object value, string? description = null)
+ : base(name, description)
+ {
+ this._value = Throw.IfNull(value);
+ }
+
+ ///
+ /// Initializes a new instance of the class with a delegate.
+ /// The delegate is invoked via an each time is called,
+ /// producing a dynamic (computed) value.
+ ///
+ /// The resource name.
+ /// A method that produces the resource value when requested.
+ /// An optional description of the resource.
+ public AgentInlineSkillResource(string name, Delegate method, string? description = null)
+ : base(name, description)
+ {
+ Throw.IfNull(method);
+ this._function = AIFunctionFactory.Create(method, name: this.Name);
+ }
+
+ ///
+ public override async Task ReadAsync(IServiceProvider? serviceProvider = null, CancellationToken cancellationToken = default)
+ {
+ if (this._function is not null)
+ {
+ return await this._function.InvokeAsync(new AIFunctionArguments() { Services = serviceProvider }, cancellationToken).ConfigureAwait(false);
+ }
+
+ return this._value;
+ }
+}
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/Programmatic/AgentInlineSkillScript.cs b/dotnet/src/Microsoft.Agents.AI/Skills/Programmatic/AgentInlineSkillScript.cs
new file mode 100644
index 0000000000..acb4f4780b
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI/Skills/Programmatic/AgentInlineSkillScript.cs
@@ -0,0 +1,46 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.AI;
+using Microsoft.Shared.DiagnosticIds;
+using Microsoft.Shared.Diagnostics;
+
+namespace Microsoft.Agents.AI;
+
+///
+/// A skill script backed by a delegate.
+///
+[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
+internal sealed class AgentInlineSkillScript : AgentSkillScript
+{
+ private readonly AIFunction _function;
+
+ ///
+ /// Initializes a new instance of the class from a delegate.
+ /// The delegate's parameters and return type are automatically marshaled via .
+ ///
+ /// The script name.
+ /// A method to execute when the script is invoked. Parameters are automatically deserialized from JSON.
+ /// An optional description of the script.
+ public AgentInlineSkillScript(string name, Delegate method, string? description = null)
+ : base(Throw.IfNullOrWhitespace(name), description)
+ {
+ Throw.IfNull(method);
+ this._function = AIFunctionFactory.Create(method, name: this.Name);
+ }
+
+ ///
+ /// Gets the JSON schema describing the parameters accepted by this script, or if not available.
+ ///
+ public JsonElement? ParametersSchema => this._function.JsonSchema;
+
+ ///
+ public override async Task RunAsync(AgentSkill skill, AIFunctionArguments arguments, CancellationToken cancellationToken = default)
+ {
+ return await this._function.InvokeAsync(arguments, cancellationToken).ConfigureAwait(false);
+ }
+}
diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunStreamingTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunStreamingTests.cs
index 870dda648c..0f2b123fd9 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunStreamingTests.cs
+++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunStreamingTests.cs
@@ -7,6 +7,8 @@ using Microsoft.Agents.AI;
namespace AzureAI.IntegrationTests;
+#pragma warning disable CS0618 // Tests intentionally exercise obsolete AIProjectClientFixture
+[Obsolete("Use FoundryVersionedAgentRunTests instead. These tests exercise obsolete AIProjectClient extension methods.")]
public class AIProjectClientAgentRunStreamingPreviousResponseTests() : RunStreamingTests(() => new())
{
public override Task RunWithNoMessageDoesNotFailAsync()
@@ -16,7 +18,8 @@ public class AIProjectClientAgentRunStreamingPreviousResponseTests() : RunStream
}
}
-public class AIProjectClientAgentRunStreamingConversationTests() : RunTests(() => new())
+[Obsolete("Use FoundryVersionedAgentRunTests instead. These tests exercise obsolete AIProjectClient extension methods.")]
+public class AIProjectClientAgentRunStreamingConversationTests() : RunStreamingTests(() => new())
{
public override Func> AgentRunOptionsFactory => async () =>
{
diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunTests.cs
index af4cee82e6..ad10ec5b7a 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunTests.cs
+++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunTests.cs
@@ -7,6 +7,8 @@ using Microsoft.Agents.AI;
namespace AzureAI.IntegrationTests;
+#pragma warning disable CS0618 // Tests intentionally exercise obsolete AIProjectClientFixture
+[Obsolete("Use FoundryVersionedAgentRunTests instead. These tests exercise obsolete AIProjectClient extension methods.")]
public class AIProjectClientAgentRunPreviousResponseTests() : RunTests(() => new())
{
public override Task RunWithNoMessageDoesNotFailAsync()
@@ -16,6 +18,7 @@ public class AIProjectClientAgentRunPreviousResponseTests() : RunTests(() => new())
{
public override Func> AgentRunOptionsFactory => async () =>
diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs
index 9db48f3832..b3782a6601 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs
+++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
+using System;
using System.Threading.Tasks;
using AgentConformance.IntegrationTests;
using AgentConformance.IntegrationTests.Support;
@@ -8,6 +9,8 @@ using Microsoft.Extensions.AI;
namespace AzureAI.IntegrationTests;
+#pragma warning disable CS0618 // Tests intentionally exercise obsolete AIProjectClientFixture
+[Obsolete("Use FoundryVersionedAgentStructuredOutputRunTests instead. These tests exercise obsolete AIProjectClient extension methods.")]
public class AIProjectClientAgentStructuredOutputRunTests() : StructuredOutputRunTests>(() => new AIProjectClientStructuredOutputFixture())
{
private const string NotSupported = "AIProjectClient does not support specifying structured output type at invocation time.";
@@ -87,6 +90,7 @@ public class AIProjectClientAgentStructuredOutputRunTests() : StructuredOutputRu
///
/// Represents a fixture for testing AIProjectClient with structured output of type provided at agent initialization.
///
+[Obsolete("Use FoundryVersionedAgentStructuredOutputFixture instead.")]
public class AIProjectClientStructuredOutputFixture : AIProjectClientFixture
{
public override async ValueTask InitializeAsync()
diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunStreamingTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunStreamingTests.cs
index 3b0c1c27b4..0e507e44c1 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunStreamingTests.cs
+++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunStreamingTests.cs
@@ -1,10 +1,13 @@
// Copyright (c) Microsoft. All rights reserved.
+using System;
using System.Threading.Tasks;
using AgentConformance.IntegrationTests;
namespace AzureAI.IntegrationTests;
+#pragma warning disable CS0618 // Tests intentionally exercise obsolete AIProjectClientFixture
+[Obsolete("Use FoundryVersionedAgentRunTests instead. These tests exercise obsolete AIProjectClient extension methods.")]
public class AIProjectClientChatClientAgentRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new())
{
public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync()
diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunTests.cs
index 1e47d0a970..a0ee72ebd4 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunTests.cs
+++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunTests.cs
@@ -1,10 +1,13 @@
// Copyright (c) Microsoft. All rights reserved.
+using System;
using System.Threading.Tasks;
using AgentConformance.IntegrationTests;
namespace AzureAI.IntegrationTests;
+#pragma warning disable CS0618 // Tests intentionally exercise obsolete AIProjectClientFixture
+[Obsolete("Use FoundryVersionedAgentRunTests instead. These tests exercise obsolete AIProjectClient extension methods.")]
public class AIProjectClientChatClientAgentRunTests() : ChatClientAgentRunTests(() => new())
{
public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync()
diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs
index 425197853e..bc5f38acf2 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs
+++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
+#pragma warning disable CS0618 // Tests intentionally exercise obsolete extension methods
+
using System;
using System.IO;
using System.Threading.Tasks;
@@ -7,6 +9,7 @@ using AgentConformance.IntegrationTests.Support;
using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.AzureAI;
using Microsoft.Extensions.AI;
using OpenAI.Files;
using OpenAI.Responses;
@@ -14,6 +17,7 @@ using Shared.IntegrationTests;
namespace AzureAI.IntegrationTests;
+[Obsolete("Use FoundryVersionedAgentCreateTests instead. These tests exercise obsolete AIProjectClient extension methods.")]
public class AIProjectClientCreateTests
{
private readonly AIProjectClient _client = new(new Uri(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint)), TestAzureCliCredentials.CreateAzureCliCredential());
@@ -51,7 +55,7 @@ public class AIProjectClientCreateTests
Assert.NotNull(agent);
Assert.Equal(AgentName, agent.Name);
Assert.Equal(AgentDescription, agent.Description);
- Assert.Equal(AgentInstructions, agent.Instructions);
+ Assert.Equal(AgentInstructions, agent.GetService()!.Instructions);
var agentRecord = await this._client.Agents.GetAgentAsync(agent.Name);
Assert.NotNull(agentRecord);
@@ -275,7 +279,7 @@ public class AIProjectClientCreateTests
try
{
// Step 2: Wrap the agent version using AsAIAgent extension.
- ChatClientAgent agent = this._client.AsAIAgent(agentVersion);
+ FoundryAgent agent = this._client.AsAIAgent(agentVersion);
// Assert the agent was created correctly and retains version metadata.
Assert.NotNull(agent);
@@ -327,7 +331,7 @@ public class AIProjectClientCreateTests
static string GetWeather(string location) => $"The weather in {location} is sunny with a high of 23C.";
var weatherFunction = AIFunctionFactory.Create(GetWeather);
- ChatClientAgent agent = createMechanism switch
+ FoundryAgent agent = createMechanism switch
{
"CreateWithChatClientAgentOptionsAsync" => await this._client.CreateAIAgentAsync(
model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName),
diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs
index 42892b99b3..112c76571b 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs
+++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
+#pragma warning disable CS0618 // Tests intentionally exercise obsolete extension methods
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -9,18 +11,20 @@ using AgentConformance.IntegrationTests.Support;
using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects;
using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.AzureAI;
using Microsoft.Extensions.AI;
using OpenAI.Responses;
using Shared.IntegrationTests;
namespace AzureAI.IntegrationTests;
+[Obsolete("Use FoundryVersionedAgentFixture instead. These tests exercise obsolete AIProjectClient extension methods.")]
public class AIProjectClientFixture : IChatClientAgentFixture
{
- private ChatClientAgent _agent = null!;
+ private FoundryAgent _agent = null!;
private AIProjectClient _client = null!;
- public IChatClient ChatClient => this._agent.ChatClient;
+ public IChatClient ChatClient => this._agent.GetService()!.ChatClient;
public AIAgent Agent => this._agent;
@@ -115,14 +119,14 @@ public class AIProjectClientFixture : IChatClientAgentFixture
string instructions = "You are a helpful assistant.",
IList? aiTools = null)
{
- return await this._client.CreateAIAgentAsync(GenerateUniqueAgentName(name), model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), instructions: instructions, tools: aiTools);
+ return (await this._client.CreateAIAgentAsync(GenerateUniqueAgentName(name), model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), instructions: instructions, tools: aiTools)).GetService()!;
}
public async Task CreateChatClientAgentAsync(ChatClientAgentOptions options)
{
options.Name ??= GenerateUniqueAgentName("HelpfulAssistant");
- return await this._client.CreateAIAgentAsync(model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), options);
+ return (await this._client.CreateAIAgentAsync(model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), options)).GetService()!;
}
public static string GenerateUniqueAgentName(string baseName) =>
@@ -170,12 +174,13 @@ public class AIProjectClientFixture : IChatClientAgentFixture
public virtual async ValueTask InitializeAsync()
{
this._client = new(new Uri(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint)), TestAzureCliCredentials.CreateAzureCliCredential());
- this._agent = await this.CreateChatClientAgentAsync();
+ this._agent = await this._client.CreateAIAgentAsync(GenerateUniqueAgentName("HelpfulAssistant"), model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), instructions: "You are a helpful assistant.");
}
public async Task InitializeAsync(ChatClientAgentOptions options)
{
this._client = new(new Uri(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint)), TestAzureCliCredentials.CreateAzureCliCredential());
- this._agent = await this.CreateChatClientAgentAsync(options);
+ options.Name ??= GenerateUniqueAgentName("HelpfulAssistant");
+ this._agent = await this._client.CreateAIAgentAsync(model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), options);
}
}
diff --git a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentChatClientRunStreamingTests.cs b/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentChatClientRunStreamingTests.cs
new file mode 100644
index 0000000000..5895ceb8b9
--- /dev/null
+++ b/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentChatClientRunStreamingTests.cs
@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Threading.Tasks;
+using AgentConformance.IntegrationTests;
+
+namespace AzureAI.IntegrationTests;
+
+public class ResponsesAgentChatClientRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new())
+{
+ public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync()
+ {
+ Assert.Skip("No messages is not supported");
+ return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync();
+ }
+}
diff --git a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentChatClientRunTests.cs b/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentChatClientRunTests.cs
new file mode 100644
index 0000000000..d80b25deb2
--- /dev/null
+++ b/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentChatClientRunTests.cs
@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Threading.Tasks;
+using AgentConformance.IntegrationTests;
+
+namespace AzureAI.IntegrationTests;
+
+public class ResponsesAgentChatClientRunTests() : ChatClientAgentRunTests(() => new())
+{
+ public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync()
+ {
+ Assert.Skip("No messages is not supported");
+ return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync();
+ }
+}
diff --git a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentExtensionCreateTests.cs b/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentExtensionCreateTests.cs
new file mode 100644
index 0000000000..cd8dc6cb03
--- /dev/null
+++ b/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentExtensionCreateTests.cs
@@ -0,0 +1,122 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Threading.Tasks;
+using AgentConformance.IntegrationTests.Support;
+using Azure.AI.Projects;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Extensions.AI;
+using Shared.IntegrationTests;
+
+namespace AzureAI.IntegrationTests;
+
+///
+/// Integration tests for non-versioned creation via extension methods.
+///
+public class ResponsesAgentExtensionCreateTests
+{
+ private static Uri Endpoint => new(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint));
+
+ private static string Model => TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName);
+
+ private readonly AIProjectClient _client = new(Endpoint, TestAzureCliCredentials.CreateAzureCliCredential());
+
+ [Fact]
+ public async Task AsAIAgent_WithModelAndInstructions_CreatesChatClientAgentAndRunsAsync()
+ {
+ // Arrange
+ const string AgentName = "ResponsesAgentExtensionSimple";
+ const string AgentDescription = "Integration test agent created from AIProjectClient.AsAIAgent(model, instructions).";
+ const string VerificationToken = "integration-extension-ok";
+
+ FoundryAgent agent = this._client.AsAIAgent(
+ model: Model,
+ instructions: $"You are a helpful assistant. When asked for verification, reply with exactly '{VerificationToken}'.",
+ name: AgentName,
+ description: AgentDescription);
+
+ AgentSession session = await agent.CreateSessionAsync();
+
+ try
+ {
+ // Act
+ AgentResponse response = await agent.RunAsync("Return the verification token.", session);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.Equal(AgentName, agent.Name);
+ Assert.Equal(AgentDescription, agent.Description);
+ Assert.Same(this._client, agent.GetService());
+ Assert.NotNull(agent.GetService());
+ Assert.Contains(VerificationToken, response.Text, StringComparison.OrdinalIgnoreCase);
+ }
+ finally
+ {
+ await DeleteSessionAsync(this._client, session);
+ }
+ }
+
+ [Fact]
+ public async Task AsAIAgent_WithOptions_CreatesChatClientAgentAndRunsAsync()
+ {
+ // Arrange
+ const string VerificationToken = "integration-options-ok";
+ ChatClientAgentOptions options = new()
+ {
+ Name = "ResponsesAgentExtensionOptions",
+ Description = "Integration test agent created from AIProjectClient.AsAIAgent(options).",
+ ChatOptions = new ChatOptions
+ {
+ ModelId = Model,
+ Instructions = $"You are a helpful assistant. When asked for verification, reply with exactly '{VerificationToken}'.",
+ },
+ };
+
+ FoundryAgent agent = this._client.AsAIAgent(options);
+ ChatClientAgentSession session = await agent.CreateConversationSessionAsync();
+
+ try
+ {
+ // Act
+ AgentResponse response = await agent.RunAsync("Return the verification token.", session);
+
+ // Assert
+ Assert.StartsWith("conv_", session.ConversationId, StringComparison.OrdinalIgnoreCase);
+ Assert.Equal(options.Name, agent.Name);
+ Assert.Equal(options.Description, agent.Description);
+ Assert.Same(this._client, agent.GetService());
+ Assert.Contains(VerificationToken, response.Text, StringComparison.OrdinalIgnoreCase);
+ }
+ finally
+ {
+ await DeleteSessionAsync(this._client, session);
+ }
+ }
+
+ private static async Task DeleteSessionAsync(AIProjectClient client, AgentSession session)
+ {
+ ChatClientAgentSession typedSession = (ChatClientAgentSession)session;
+
+ if (typedSession.ConversationId?.StartsWith("conv_", StringComparison.OrdinalIgnoreCase) == true)
+ {
+ await client.GetProjectOpenAIClient().GetProjectConversationsClient().DeleteConversationAsync(typedSession.ConversationId);
+ }
+ else if (typedSession.ConversationId?.StartsWith("resp_", StringComparison.OrdinalIgnoreCase) == true)
+ {
+ await DeleteResponseChainAsync(client, typedSession.ConversationId);
+ }
+ }
+
+ private static async Task DeleteResponseChainAsync(AIProjectClient client, string lastResponseId)
+ {
+ var responsesClient = client.GetProjectOpenAIClient().GetProjectResponsesClient();
+ var response = await responsesClient.GetResponseAsync(lastResponseId);
+ await responsesClient.DeleteResponseAsync(lastResponseId);
+
+ if (response.Value.PreviousResponseId is not null)
+ {
+ await DeleteResponseChainAsync(client, response.Value.PreviousResponseId);
+ }
+ }
+}
diff --git a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentFixture.cs b/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentFixture.cs
new file mode 100644
index 0000000000..ec678a00d9
--- /dev/null
+++ b/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentFixture.cs
@@ -0,0 +1,187 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using AgentConformance.IntegrationTests;
+using AgentConformance.IntegrationTests.Support;
+using Azure.AI.Extensions.OpenAI;
+using Azure.AI.Projects;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Extensions.AI;
+using OpenAI.Responses;
+using Shared.IntegrationTests;
+
+namespace AzureAI.IntegrationTests;
+
+///
+/// Integration test fixture that creates non-versioned Responses agents via the direct AIProjectClient.AsAIAgent(...) path.
+///
+public class ResponsesAgentFixture : IChatClientAgentFixture
+{
+ private FoundryAgent _agent = null!;
+ private AIProjectClient _client = null!;
+
+ public IChatClient ChatClient => this._agent.GetService()!.ChatClient;
+
+ public AIAgent Agent => this._agent;
+
+ public async Task CreateConversationAsync()
+ {
+ var response = await this._client.GetProjectOpenAIClient().GetProjectConversationsClient().CreateProjectConversationAsync();
+ return response.Value.Id;
+ }
+
+ public async Task> GetChatHistoryAsync(AIAgent agent, AgentSession session)
+ {
+ ChatClientAgentSession chatClientSession = (ChatClientAgentSession)session;
+
+ if (chatClientSession.ConversationId?.StartsWith("conv_", StringComparison.OrdinalIgnoreCase) == true)
+ {
+ return await this.GetChatHistoryFromConversationAsync(chatClientSession.ConversationId);
+ }
+
+ if (chatClientSession.ConversationId?.StartsWith("resp_", StringComparison.OrdinalIgnoreCase) == true)
+ {
+ return await this.GetChatHistoryFromResponsesChainAsync(chatClientSession.ConversationId);
+ }
+
+ ChatHistoryProvider? chatHistoryProvider = agent.GetService();
+
+ if (chatHistoryProvider is null)
+ {
+ return [];
+ }
+
+ return (await chatHistoryProvider.InvokingAsync(new(agent, session, []))).ToList();
+ }
+
+ private async Task> GetChatHistoryFromResponsesChainAsync(string conversationId)
+ {
+ var openAIResponseClient = this._client.GetProjectOpenAIClient().GetProjectResponsesClient();
+ var inputItems = await openAIResponseClient.GetResponseInputItemsAsync(conversationId).ToListAsync();
+ var response = await openAIResponseClient.GetResponseAsync(conversationId);
+ ResponseItem responseItem = response.Value.OutputItems.FirstOrDefault()!;
+
+ var previousMessages = inputItems
+ .Select(ConvertToChatMessage)
+ .Where(x => x.Text != "You are a helpful assistant.")
+ .Reverse();
+
+ ChatMessage responseMessage = ConvertToChatMessage(responseItem);
+
+ return [.. previousMessages, responseMessage];
+ }
+
+ private static ChatMessage ConvertToChatMessage(ResponseItem item)
+ {
+ if (item is MessageResponseItem messageResponseItem)
+ {
+ ChatRole role = messageResponseItem.Role == MessageRole.User ? ChatRole.User : ChatRole.Assistant;
+ return new ChatMessage(role, messageResponseItem.Content.FirstOrDefault()?.Text);
+ }
+
+ throw new NotSupportedException("This test currently only supports text messages");
+ }
+
+ private async Task> GetChatHistoryFromConversationAsync(string conversationId)
+ {
+ List messages = [];
+ await foreach (AgentResponseItem item in this._client.GetProjectOpenAIClient().GetProjectConversationsClient().GetProjectConversationItemsAsync(conversationId, order: "asc"))
+ {
+ var openAIItem = item.AsResponseResultItem();
+ if (openAIItem is MessageResponseItem messageItem)
+ {
+ messages.Add(new ChatMessage
+ {
+ Role = new ChatRole(messageItem.Role.ToString()),
+ Contents = messageItem.Content
+ .Where(c => c.Kind is ResponseContentPartKind.OutputText or ResponseContentPartKind.InputText)
+ .Select(c => new TextContent(c.Text))
+ .ToList()
+ });
+ }
+ }
+
+ return messages;
+ }
+
+ public Task CreateChatClientAgentAsync(
+ string name = "HelpfulAssistant",
+ string instructions = "You are a helpful assistant.",
+ IList? aiTools = null)
+ {
+ return Task.FromResult(this._client.AsAIAgent(
+ model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName),
+ instructions: instructions,
+ name: name,
+ tools: aiTools).GetService()!);
+ }
+
+ public Task CreateChatClientAgentAsync(ChatClientAgentOptions options)
+ {
+ return Task.FromResult(this._client.AsAIAgent(options).GetService()!);
+ }
+
+ // Non-versioned Responses agents have no server-side agent to delete.
+ public Task DeleteAgentAsync(ChatClientAgent agent) => Task.CompletedTask;
+
+ public async Task DeleteSessionAsync(AgentSession session)
+ {
+ ChatClientAgentSession typedSession = (ChatClientAgentSession)session;
+
+ if (typedSession.ConversationId?.StartsWith("conv_", StringComparison.OrdinalIgnoreCase) == true)
+ {
+ await this._client.GetProjectOpenAIClient().GetProjectConversationsClient().DeleteConversationAsync(typedSession.ConversationId);
+ }
+ else if (typedSession.ConversationId?.StartsWith("resp_", StringComparison.OrdinalIgnoreCase) == true)
+ {
+ await this.DeleteResponseChainAsync(typedSession.ConversationId!);
+ }
+ }
+
+ private async Task DeleteResponseChainAsync(string lastResponseId)
+ {
+ var response = await this._client.GetProjectOpenAIClient().GetProjectResponsesClient().GetResponseAsync(lastResponseId);
+ await this._client.GetProjectOpenAIClient().GetProjectResponsesClient().DeleteResponseAsync(lastResponseId);
+
+ if (response.Value.PreviousResponseId is not null)
+ {
+ await this.DeleteResponseChainAsync(response.Value.PreviousResponseId);
+ }
+ }
+
+ // Non-versioned Responses agents have no server-side agent to clean up on dispose.
+ public ValueTask DisposeAsync()
+ {
+ GC.SuppressFinalize(this);
+ return default;
+ }
+
+ public virtual ValueTask InitializeAsync()
+ {
+ this._client = new AIProjectClient(
+ new Uri(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint)),
+ TestAzureCliCredentials.CreateAzureCliCredential());
+
+ this._agent = this._client.AsAIAgent(
+ model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName),
+ instructions: "You are a helpful assistant.",
+ name: "HelpfulAssistant");
+
+ return default;
+ }
+
+ public ValueTask InitializeAsync(ChatClientAgentOptions options)
+ {
+ this._client = new AIProjectClient(
+ new Uri(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint)),
+ TestAzureCliCredentials.CreateAzureCliCredential());
+
+ this._agent = this._client.AsAIAgent(options);
+
+ return default;
+ }
+}
diff --git a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentRunStreamingTests.cs b/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentRunStreamingTests.cs
new file mode 100644
index 0000000000..5f21c316c4
--- /dev/null
+++ b/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentRunStreamingTests.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Threading.Tasks;
+using AgentConformance.IntegrationTests;
+using Microsoft.Agents.AI;
+
+namespace AzureAI.IntegrationTests;
+
+public class ResponsesAgentRunStreamingPreviousResponseTests() : RunStreamingTests(() => new())
+{
+ public override Task RunWithNoMessageDoesNotFailAsync()
+ {
+ Assert.Skip("No messages is not supported");
+ return base.RunWithNoMessageDoesNotFailAsync();
+ }
+}
+
+public class ResponsesAgentRunStreamingConversationTests() : RunStreamingTests(() => new())
+{
+ public override Func> AgentRunOptionsFactory => async () =>
+ {
+ var conversationId = await this.Fixture.CreateConversationAsync();
+ return new ChatClientAgentRunOptions(new() { ConversationId = conversationId });
+ };
+
+ public override Task RunWithNoMessageDoesNotFailAsync()
+ {
+ Assert.Skip("No messages is not supported");
+ return base.RunWithNoMessageDoesNotFailAsync();
+ }
+}
diff --git a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentRunTests.cs b/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentRunTests.cs
new file mode 100644
index 0000000000..71460f7737
--- /dev/null
+++ b/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentRunTests.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Threading.Tasks;
+using AgentConformance.IntegrationTests;
+using Microsoft.Agents.AI;
+
+namespace AzureAI.IntegrationTests;
+
+public class ResponsesAgentRunPreviousResponseTests() : RunTests(() => new())
+{
+ public override Task RunWithNoMessageDoesNotFailAsync()
+ {
+ Assert.Skip("No messages is not supported");
+ return base.RunWithNoMessageDoesNotFailAsync();
+ }
+}
+
+public class ResponsesAgentRunConversationTests() : RunTests(() => new())
+{
+ public override Func> AgentRunOptionsFactory => async () =>
+ {
+ var conversationId = await this.Fixture.CreateConversationAsync();
+ return new ChatClientAgentRunOptions(new() { ConversationId = conversationId });
+ };
+
+ public override Task RunWithNoMessageDoesNotFailAsync()
+ {
+ Assert.Skip("No messages is not supported");
+ return base.RunWithNoMessageDoesNotFailAsync();
+ }
+}
diff --git a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentStructuredOutputRunTests.cs b/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentStructuredOutputRunTests.cs
new file mode 100644
index 0000000000..19ebdb4e28
--- /dev/null
+++ b/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentStructuredOutputRunTests.cs
@@ -0,0 +1,100 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Threading.Tasks;
+using AgentConformance.IntegrationTests;
+using AgentConformance.IntegrationTests.Support;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+using Shared.IntegrationTests;
+
+namespace AzureAI.IntegrationTests;
+
+public class ResponsesAgentStructuredOutputRunTests() : StructuredOutputRunTests>(() => new())
+{
+ private const string NotSupported = "The direct Responses AsAIAgent path does not support specifying structured output type at invocation time.";
+
+ ///
+ /// Verifies that response format provided at agent initialization is used when invoking RunAsync.
+ ///
+ [RetryFact(Constants.RetryCount, Constants.RetryDelay)]
+ public async Task RunWithResponseFormatAtAgentInitializationReturnsExpectedResultAsync()
+ {
+ // Arrange
+ AIAgent agent = this.Fixture.Agent;
+ AgentSession session = await agent.CreateSessionAsync();
+ await using var cleanup = new SessionCleanup(session, this.Fixture);
+
+ // Act
+ AgentResponse response = await agent.RunAsync(new ChatMessage(ChatRole.User, "Provide information about the capital of France."), session);
+
+ // Assert
+ Assert.NotNull(response);
+ Assert.Single(response.Messages);
+ Assert.Contains("Paris", response.Text);
+ Assert.True(TryDeserialize(response.Text, AgentAbstractionsJsonUtilities.DefaultOptions, out CityInfo cityInfo));
+ Assert.Equal("Paris", cityInfo.Name);
+ }
+
+ ///
+ /// Verifies that generic RunAsync works when structured output is configured at agent initialization.
+ ///
+ [RetryFact(Constants.RetryCount, Constants.RetryDelay)]
+ public async Task RunGenericWithResponseFormatAtAgentInitializationReturnsExpectedResultAsync()
+ {
+ // Arrange
+ AIAgent agent = this.Fixture.Agent;
+ AgentSession session = await agent.CreateSessionAsync();
+ await using var cleanup = new SessionCleanup(session, this.Fixture);
+
+ // Act
+ AgentResponse response = await agent.RunAsync(
+ new ChatMessage(ChatRole.User, "Provide information about the capital of France."),
+ session);
+
+ // Assert
+ Assert.NotNull(response);
+ Assert.Single(response.Messages);
+ Assert.Contains("Paris", response.Text);
+
+ Assert.NotNull(response.Result);
+ Assert.Equal("Paris", response.Result.Name);
+ }
+
+ public override Task RunWithGenericTypeReturnsExpectedResultAsync()
+ {
+ Assert.Skip(NotSupported);
+ return base.RunWithGenericTypeReturnsExpectedResultAsync();
+ }
+
+ public override Task RunWithResponseFormatReturnsExpectedResultAsync()
+ {
+ Assert.Skip(NotSupported);
+ return base.RunWithResponseFormatReturnsExpectedResultAsync();
+ }
+
+ public override Task RunWithPrimitiveTypeReturnsExpectedResultAsync()
+ {
+ Assert.Skip(NotSupported);
+ return base.RunWithPrimitiveTypeReturnsExpectedResultAsync();
+ }
+}
+
+///
+/// Fixture for testing the direct Responses path with structured output of type provided at agent initialization.
+///
+public class ResponsesAgentStructuredOutputFixture : ResponsesAgentFixture
+{
+ public override ValueTask InitializeAsync()
+ {
+ ChatClientAgentOptions agentOptions = new()
+ {
+ ChatOptions = new ChatOptions()
+ {
+ ModelId = TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName),
+ ResponseFormat = ChatResponseFormat.ForJsonSchema(AgentAbstractionsJsonUtilities.DefaultOptions)
+ },
+ };
+
+ return this.InitializeAsync(agentOptions);
+ }
+}
diff --git a/dotnet/tests/Microsoft.Agents.AI.AGUI.UnitTests/AGUIStreamingMessageIdTests.cs b/dotnet/tests/Microsoft.Agents.AI.AGUI.UnitTests/AGUIStreamingMessageIdTests.cs
new file mode 100644
index 0000000000..5c55408ff8
--- /dev/null
+++ b/dotnet/tests/Microsoft.Agents.AI.AGUI.UnitTests/AGUIStreamingMessageIdTests.cs
@@ -0,0 +1,228 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Agents.AI.AGUI.Shared;
+using Microsoft.Extensions.AI;
+
+namespace Microsoft.Agents.AI.AGUI.UnitTests;
+
+///
+/// Tests for AGUI streaming behavior when MessageId is null or missing from
+/// ChatResponseUpdate objects (e.g., providers like Google GenAI/Vertex AI
+/// that don't supply MessageId on streaming chunks).
+///
+public sealed class AGUIStreamingMessageIdTests
+{
+ ///
+ /// When ChatResponseUpdate objects with null MessageId are fed directly to
+ /// AsAGUIEventStreamAsync, the AGUI layer generates a fallback MessageId so
+ /// that events are valid regardless of agent type or provider.
+ ///
+ [Fact]
+ public async Task TextStreaming_NullMessageId_GeneratesFallbackInAGUILayerAsync()
+ {
+ // Arrange - Simulate a provider that does NOT set MessageId
+ List providerUpdates =
+ [
+ new ChatResponseUpdate(ChatRole.Assistant, "Hello"),
+ new ChatResponseUpdate(ChatRole.Assistant, " world"),
+ new ChatResponseUpdate(ChatRole.Assistant, "!")
+ ];
+
+ // Act
+ List aguiEvents = [];
+ await foreach (BaseEvent evt in providerUpdates.ToAsyncEnumerableAsync()
+ .AsAGUIEventStreamAsync("thread-1", "run-1", AGUIJsonSerializerContext.Default.Options))
+ {
+ aguiEvents.Add(evt);
+ }
+
+ // Assert - AGUI layer should generate a fallback MessageId
+ List startEvents = aguiEvents.OfType().ToList();
+ List contentEvents = aguiEvents.OfType().ToList();
+
+ Assert.Single(startEvents);
+ Assert.False(string.IsNullOrEmpty(startEvents[0].MessageId));
+
+ Assert.Equal(3, contentEvents.Count);
+ Assert.All(contentEvents, e => Assert.False(string.IsNullOrEmpty(e.MessageId)));
+
+ // All events should share the same generated MessageId
+ string?[] distinctIds = contentEvents.Select(e => e.MessageId).Distinct().ToArray();
+ Assert.Single(distinctIds);
+ Assert.Equal(startEvents[0].MessageId, distinctIds[0]);
+ }
+
+ ///
+ /// Full pipeline: ChatClientAgent → AsChatResponseUpdatesAsync → AsAGUIEventStreamAsync
+ /// with a provider that returns null MessageId. Verifies that fallback MessageId
+ /// generation ensures valid AGUI events.
+ ///
+ [Fact]
+ public async Task FullPipeline_NullProviderMessageId_ProducesValidAGUIEventsAsync()
+ {
+ // Arrange - ChatClientAgent with a mock client that omits MessageId
+ IChatClient mockChatClient = new NullMessageIdChatClient();
+ ChatClientAgent agent = new(mockChatClient, name: "test-agent");
+
+ ChatMessage userMessage = new(ChatRole.User, "tell me about agents");
+
+ // Act - Run the full pipeline exactly as MapAGUI does
+ List aguiEvents = [];
+ await foreach (BaseEvent evt in agent
+ .RunStreamingAsync([userMessage])
+ .AsChatResponseUpdatesAsync()
+ .AsAGUIEventStreamAsync("thread-1", "run-1", AGUIJsonSerializerContext.Default.Options))
+ {
+ aguiEvents.Add(evt);
+ }
+
+ // Assert — The pipeline should produce AGUI events with valid messageId
+ List startEvents = aguiEvents.OfType().ToList();
+ List contentEvents = aguiEvents.OfType().ToList();
+
+ Assert.NotEmpty(startEvents);
+ Assert.NotEmpty(contentEvents);
+
+ foreach (TextMessageStartEvent startEvent in startEvents)
+ {
+ Assert.False(
+ string.IsNullOrEmpty(startEvent.MessageId),
+ "TextMessageStartEvent.MessageId should not be null/empty when provider omits it");
+ }
+
+ foreach (TextMessageContentEvent contentEvent in contentEvents)
+ {
+ Assert.False(
+ string.IsNullOrEmpty(contentEvent.MessageId),
+ "TextMessageContentEvent.MessageId should not be null/empty when provider omits it");
+ }
+
+ // All content events should share the same messageId
+ string?[] distinctMessageIds = contentEvents.Select(e => e.MessageId).Distinct().ToArray();
+ Assert.Single(distinctMessageIds);
+ }
+
+ ///
+ /// When ChatResponseUpdate has empty string MessageId, the AGUI layer generates
+ /// a fallback so ToolCallStartEvent.ParentMessageId is valid.
+ ///
+ [Fact]
+ public async Task ToolCalls_EmptyMessageId_GeneratesFallbackParentMessageIdAsync()
+ {
+ // Arrange - ChatResponseUpdate with a tool call but empty MessageId
+ FunctionCallContent functionCall = new("call_abc123", "GetWeather")
+ {
+ Arguments = new Dictionary { ["location"] = "San Francisco" }
+ };
+
+ List providerUpdates =
+ [
+ new ChatResponseUpdate
+ {
+ Role = ChatRole.Assistant,
+ MessageId = "",
+ Contents = [functionCall]
+ }
+ ];
+
+ // Act
+ List aguiEvents = [];
+ await foreach (BaseEvent evt in providerUpdates.ToAsyncEnumerableAsync()
+ .AsAGUIEventStreamAsync("thread-1", "run-1", AGUIJsonSerializerContext.Default.Options))
+ {
+ aguiEvents.Add(evt);
+ }
+
+ // Assert — ParentMessageId should have a generated fallback
+ ToolCallStartEvent? toolCallStart = aguiEvents.OfType().FirstOrDefault();
+ Assert.NotNull(toolCallStart);
+ Assert.Equal("call_abc123", toolCallStart.ToolCallId);
+ Assert.Equal("GetWeather", toolCallStart.ToolCallName);
+ Assert.False(
+ string.IsNullOrEmpty(toolCallStart.ParentMessageId),
+ "ParentMessageId should have a generated fallback for empty provider MessageId");
+ }
+
+ ///
+ /// When a provider properly sets MessageId (e.g., OpenAI), the AGUI pipeline
+ /// produces valid events with correct messageId values.
+ ///
+ [Fact]
+ public async Task TextStreaming_WithProviderMessageId_ProducesValidAGUIEventsAsync()
+ {
+ // Arrange — Provider that properly sets MessageId
+ List providerUpdates =
+ [
+ new ChatResponseUpdate(ChatRole.Assistant, "Hello")
+ {
+ MessageId = "chatcmpl-abc123"
+ },
+ new ChatResponseUpdate(ChatRole.Assistant, " world")
+ {
+ MessageId = "chatcmpl-abc123"
+ }
+ ];
+
+ // Act
+ List aguiEvents = [];
+ await foreach (BaseEvent evt in providerUpdates.ToAsyncEnumerableAsync()
+ .AsAGUIEventStreamAsync("thread-1", "run-1", AGUIJsonSerializerContext.Default.Options))
+ {
+ aguiEvents.Add(evt);
+ }
+
+ // Assert
+ List startEvents = aguiEvents.OfType().ToList();
+ List contentEvents = aguiEvents.OfType().ToList();
+
+ Assert.Single(startEvents);
+ Assert.Equal("chatcmpl-abc123", startEvents[0].MessageId);
+
+ Assert.Equal(2, contentEvents.Count);
+ Assert.All(contentEvents, e => Assert.Equal("chatcmpl-abc123", e.MessageId));
+ }
+}
+
+///
+/// Mock IChatClient that simulates a provider not setting MessageId on streaming chunks
+/// (e.g., Google GenAI / Vertex AI).
+///
+internal sealed class NullMessageIdChatClient : IChatClient
+{
+ public void Dispose()
+ {
+ }
+
+ public object? GetService(Type serviceType, object? serviceKey = null) => null;
+
+ public Task GetResponseAsync(
+ IEnumerable messages,
+ ChatOptions? options = null,
+ CancellationToken cancellationToken = default)
+ {
+ return Task.FromResult(new ChatResponse([new(ChatRole.Assistant, "response")]));
+ }
+
+ public async IAsyncEnumerable GetStreamingResponseAsync(
+ IEnumerable messages,
+ ChatOptions? options = null,
+ [EnumeratorCancellation] CancellationToken cancellationToken = default)
+ {
+ foreach (string chunk in (string[])["Agents", " are", " autonomous", " programs."])
+ {
+ yield return new ChatResponseUpdate
+ {
+ Role = ChatRole.Assistant,
+ Contents = [new TextContent(chunk)]
+ };
+
+ await Task.Yield();
+ }
+ }
+}
diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateExtensionsTests.cs
index 89cff04de8..5cf0b75eef 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateExtensionsTests.cs
@@ -384,6 +384,45 @@ public class AgentResponseUpdateExtensionsTests
Assert.Same(originalChatResponseUpdate, result);
}
+ [Fact]
+ public void AsChatResponseUpdate_WithRawRepresentationNullMessageId_ReturnsRawDirectly()
+ {
+ // Arrange - RawRepresentation has null MessageId
+ ChatResponseUpdate originalChatResponseUpdate = new()
+ {
+ ResponseId = "original-update",
+ Contents = [new TextContent("Hello")]
+ };
+ AgentResponseUpdate agentResponseUpdate = new(originalChatResponseUpdate);
+
+ // Act
+ ChatResponseUpdate result = agentResponseUpdate.AsChatResponseUpdate();
+
+ // Assert - Returns the raw representation directly without mutation
+ Assert.Same(originalChatResponseUpdate, result);
+ Assert.Null(result.MessageId);
+ }
+
+ [Fact]
+ public void AsChatResponseUpdate_WithRawRepresentationExistingMessageId_PreservesOriginal()
+ {
+ // Arrange - RawRepresentation already has MessageId set by provider
+ ChatResponseUpdate originalChatResponseUpdate = new()
+ {
+ ResponseId = "original-update",
+ MessageId = "provider-message-id",
+ Contents = [new TextContent("Hello")]
+ };
+ AgentResponseUpdate agentResponseUpdate = new(originalChatResponseUpdate);
+
+ // Act
+ ChatResponseUpdate result = agentResponseUpdate.AsChatResponseUpdate();
+
+ // Assert - Provider's original MessageId should be preserved
+ Assert.Same(originalChatResponseUpdate, result);
+ Assert.Equal("provider-message-id", result.MessageId);
+ }
+
[Fact]
public void AsChatResponseUpdate_WithoutRawRepresentation_CreatesNewChatResponseUpdate()
{
diff --git a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs
index 261faaded8..5c954d30e8 100644
--- a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs
@@ -21,11 +21,173 @@ using OpenAI.Responses;
namespace Microsoft.Agents.AI.AzureAI.UnitTests;
+#pragma warning disable CS0618
///
/// Unit tests for the class.
///
+[Obsolete("Includes coverage for obsolete AIProjectClient compatibility extension methods.")]
public sealed class AzureAIProjectChatClientExtensionsTests
{
+ #region AsAIAgent(AIProjectClient, model, instructions) Tests
+
+ ///
+ /// Verify that the non-versioned AsAIAgent overload throws ArgumentNullException when AIProjectClient is null.
+ ///
+ [Fact]
+ public void AsAIAgent_WithModelAndInstructions_WithNullClient_ThrowsArgumentNullException()
+ {
+ // Arrange
+ AIProjectClient? client = null;
+
+ // Act & Assert
+ ArgumentNullException exception = Assert.Throws(() =>
+ client!.AsAIAgent("gpt-4o-mini", "You are helpful."));
+
+ Assert.Equal("aiProjectClient", exception.ParamName);
+ }
+
+ ///
+ /// Verify that the non-versioned AsAIAgent overload creates a valid ChatClientAgent.
+ ///
+ [Fact]
+ public void AsAIAgent_WithModelAndInstructions_CreatesChatClientAgent()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ List tools =
+ [
+ AIFunctionFactory.Create(() => "test", "test_function", "A test function")
+ ];
+
+ // Act
+ FoundryAgent agent = client.AsAIAgent(
+ "gpt-4o-mini",
+ "You are helpful.",
+ name: "test-agent",
+ description: "A test agent",
+ tools: tools);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.Equal("test-agent", agent.Name);
+ Assert.Equal("A test agent", agent.Description);
+ Assert.Same(client, agent.GetService());
+ Assert.NotNull(agent.GetService());
+ }
+
+ ///
+ /// Verify that the non-versioned AsAIAgent overload applies the clientFactory.
+ ///
+ [Fact]
+ public void AsAIAgent_WithModelAndInstructions_WithClientFactory_AppliesFactoryCorrectly()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ TestChatClient? testChatClient = null;
+
+ // Act
+ FoundryAgent agent = client.AsAIAgent(
+ "gpt-4o-mini",
+ "You are helpful.",
+ clientFactory: innerClient => testChatClient = new TestChatClient(innerClient));
+
+ // Assert
+ Assert.NotNull(agent);
+ TestChatClient? retrievedTestClient = agent.GetService();
+ Assert.NotNull(retrievedTestClient);
+ Assert.Same(testChatClient, retrievedTestClient);
+ }
+
+ ///
+ /// Verify that the options-based non-versioned AsAIAgent overload creates a valid ChatClientAgent.
+ ///
+ [Fact]
+ public void AsAIAgent_WithOptions_CreatesChatClientAgent()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ ChatClientAgentOptions options = new()
+ {
+ Name = "options-agent",
+ Description = "Agent from options",
+ ChatOptions = new ChatOptions
+ {
+ ModelId = "gpt-4o-mini",
+ Instructions = "You are helpful.",
+ },
+ };
+
+ // Act
+ FoundryAgent agent = client.AsAIAgent(options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.Equal("options-agent", agent.Name);
+ Assert.Equal("Agent from options", agent.Description);
+ Assert.Same(client, agent.GetService());
+ }
+
+ ///
+ /// Verify that the non-versioned AsAIAgent overload adds the MEAI user-agent header to Responses API requests.
+ ///
+ [Fact]
+ public async Task AsAIAgent_WithModelAndInstructions_UserAgentHeaderAddedToResponsesRequestsAsync()
+ {
+ // Arrange
+ bool userAgentFound = false;
+ using HttpHandlerAssert httpHandler = new(request =>
+ {
+ if (request.Headers.TryGetValues("User-Agent", out IEnumerable? values))
+ {
+ foreach (string value in values)
+ {
+ if (value.Contains("MEAI"))
+ {
+ userAgentFound = true;
+ }
+ }
+ }
+
+ if (request.Method == HttpMethod.Post && request.RequestUri!.PathAndQuery.Contains("/responses"))
+ {
+ return new HttpResponseMessage(HttpStatusCode.OK)
+ {
+ Content = new StringContent(
+ TestDataUtil.GetOpenAIDefaultResponseJson(),
+ Encoding.UTF8,
+ "application/json")
+ };
+ }
+
+ return new HttpResponseMessage(HttpStatusCode.OK)
+ {
+ Content = new StringContent("{}", Encoding.UTF8, "application/json")
+ };
+ });
+
+#pragma warning disable CA5399
+ using HttpClient httpClient = new(httpHandler);
+#pragma warning restore CA5399
+
+ AIProjectClient aiProjectClient = new(
+ new Uri("https://test.openai.azure.com/"),
+ new FakeAuthenticationTokenProvider(),
+ new() { Transport = new HttpClientPipelineTransport(httpClient) });
+
+ FoundryAgent agent = aiProjectClient.AsAIAgent(
+ "gpt-4o-mini",
+ "You are helpful.");
+
+ // Act
+ AgentSession session = await agent.CreateSessionAsync();
+ await agent.RunAsync("Hello", session);
+
+ // Assert
+ Assert.True(userAgentFound, "MEAI user-agent header was not found in any request");
+ }
+
+ #endregion
+
#region AsAIAgent(AIProjectClient, AgentRecord) Tests
///
@@ -199,7 +361,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -217,7 +379,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
#endregion
@@ -409,7 +571,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
var chatClient = agent.GetService();
Assert.NotNull(chatClient);
var agentVersion = chatClient.GetService();
@@ -458,7 +620,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -481,7 +643,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
Assert.Equal("test-agent", agent.Name);
- Assert.Equal("Test instructions", agent.Instructions);
+ Assert.Equal("Test instructions", agent.GetService()!.Instructions);
}
///
@@ -570,7 +732,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -592,7 +754,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -612,7 +774,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -638,7 +800,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
var agentVersion = agent.GetService();
Assert.NotNull(agentVersion);
if (agentVersion.Definition is PromptAgentDefinition promptDef)
@@ -677,7 +839,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
var agentVersion = agent.GetService();
Assert.NotNull(agentVersion);
if (agentVersion.Definition is PromptAgentDefinition promptDef)
@@ -721,7 +883,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
var agentVersion = agent.GetService();
Assert.NotNull(agentVersion);
Assert.IsType(agentVersion.Definition);
@@ -783,7 +945,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
var chatClient = agent.GetService();
Assert.NotNull(chatClient);
var agentVersion = chatClient.GetService();
@@ -815,7 +977,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
var agentVersion = agent.GetService();
Assert.NotNull(agentVersion);
if (agentVersion.Definition is PromptAgentDefinition promptDef)
@@ -843,7 +1005,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -864,7 +1026,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
#endregion
@@ -912,7 +1074,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
var agentVersion = agent.GetService();
Assert.NotNull(agentVersion);
if (agentVersion.Definition is PromptAgentDefinition promptDef)
@@ -952,7 +1114,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -979,7 +1141,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -1011,7 +1173,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
#endregion
@@ -1064,7 +1226,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
Assert.Equal("test-agent", agent.Name);
- Assert.Equal("Custom instructions", agent.Instructions);
+ Assert.Equal("Custom instructions", agent.GetService()!.Instructions);
Assert.Equal("Custom description", agent.Description);
}
@@ -1353,7 +1515,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(agent);
Assert.Equal(AgentName, agent.Name);
- Assert.Equal(Instructions, agent.Instructions);
+ Assert.Equal(Instructions, agent.GetService()!.Instructions);
var wrappedClient = agent.GetService();
Assert.NotNull(wrappedClient);
}
@@ -1957,11 +2119,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
+ FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -1983,11 +2145,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
+ FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -2011,11 +2173,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
+ FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -2044,11 +2206,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
+ FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -2077,11 +2239,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
+ FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
#endregion
@@ -2107,11 +2269,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
+ FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -2133,11 +2295,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
+ FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -2159,11 +2321,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
+ FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
#endregion
@@ -2186,7 +2348,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
+ FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
// Assert
Assert.NotNull(agent);
@@ -2208,7 +2370,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
+ FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
// Assert
Assert.NotNull(agent);
@@ -2303,11 +2465,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await client.GetAIAgentAsync(options);
+ FoundryAgent agent = await client.GetAIAgentAsync(options);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
#endregion
@@ -2330,7 +2492,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await client.GetAIAgentAsync(options);
+ FoundryAgent agent = await client.GetAIAgentAsync(options);
// Assert
Assert.NotNull(agent);
@@ -2352,7 +2514,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await client.GetAIAgentAsync(options);
+ FoundryAgent agent = await client.GetAIAgentAsync(options);
// Assert
Assert.NotNull(agent);
@@ -2374,7 +2536,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await client.GetAIAgentAsync(options);
+ FoundryAgent agent = await client.GetAIAgentAsync(options);
// Assert
Assert.NotNull(agent);
@@ -2400,7 +2562,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act - should not throw even without tools when UseProvidedChatClientAsIs is true
- ChatClientAgent agent = await client.GetAIAgentAsync(options);
+ FoundryAgent agent = await client.GetAIAgentAsync(options);
// Assert
Assert.NotNull(agent);
@@ -2432,7 +2594,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act - UseProvidedChatClientAsIs is true, but provided AIFunctions should still be matched and preserved
- ChatClientAgent agent = await client.GetAIAgentAsync(options);
+ FoundryAgent agent = await client.GetAIAgentAsync(options);
// Assert
Assert.NotNull(agent);
@@ -2463,11 +2625,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await client.GetAIAgentAsync(options);
+ FoundryAgent agent = await client.GetAIAgentAsync(options);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
// Verify the agent ID is generated from server-returned name ("agent_abc123") and "latest"
Assert.Equal("agent_abc123:latest", agent.Id);
}
@@ -2525,11 +2687,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await client.GetAIAgentAsync(options);
+ FoundryAgent agent = await client.GetAIAgentAsync(options);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
// Verify the agent ID is generated from server-returned name ("agent_abc123") and "latest"
Assert.Equal("agent_abc123:latest", agent.Id);
}
@@ -2679,11 +2841,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await client.GetAIAgentAsync(options);
+ FoundryAgent agent = await client.GetAIAgentAsync(options);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -2707,11 +2869,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
+ FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -2745,11 +2907,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await client.GetAIAgentAsync(options);
+ FoundryAgent agent = await client.GetAIAgentAsync(options);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -2783,11 +2945,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
};
// Act
- ChatClientAgent agent = await client.GetAIAgentAsync(options);
+ FoundryAgent agent = await client.GetAIAgentAsync(options);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType(agent);
}
///
@@ -2841,11 +3003,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
AgentVersion agentVersion = ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJson(agentDefinition: definition)))!;
// Act - no tools provided, but requireInvocableTools is false when no tools param is passed
- ChatClientAgent agent = client.AsAIAgent(agentVersion);
+ FoundryAgent agent = client.AsAIAgent(agentVersion);
// Assert
Assert.NotNull(agent);
- Assert.IsType(agent);
+ Assert.IsType