Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
0e922de
Declarative support for AzureAI Persistent agents
markwallace-microsoft Jan 9, 2026
394e43e
Merge branch 'main' into users/markwallace/decl_azureai_persistent
markwallace-microsoft Jan 9, 2026
2ba1a25
Start to fix compile errors due to refactoring
markwallace-microsoft Jan 12, 2026
ccfe855
Merge branch 'main' into users/markwallace/decl_azureai_persistent
markwallace-microsoft Jan 13, 2026
42549eb
Merge branch 'users/markwallace/decl_azureai_persistent' of https://g…
markwallace-microsoft Jan 13, 2026
374b55c
Merge branch 'main' into users/markwallace/decl_azureai_persistent
markwallace-microsoft Jan 21, 2026
65b1d54
Merge branch 'main' into users/markwallace/decl_azureai_persistent
markwallace-microsoft Jan 29, 2026
6ae3bda
Remove declarative support for persistent agents
markwallace-microsoft Jan 30, 2026
bae9fc1
Merge branch 'main' into users/markwallace/decl_azureai_persistent
markwallace-microsoft Jan 30, 2026
a1d5a35
Add unit test project for Microsoft.Agents.AI.Declarative.AzureAI
markwallace-microsoft Jan 30, 2026
668bc7d
Add unit tests for Microsoft.Agents.AI.Declarative.AzureAI
markwallace-microsoft Jan 30, 2026
59682da
Merge branch 'main' into users/markwallace/decl_azureai_persistent
markwallace-microsoft Jan 30, 2026
14401f7
Fix formatting
markwallace-microsoft Jan 30, 2026
e92425e
More formatting
markwallace-microsoft Jan 30, 2026
6e51685
Merge branch 'main' into users/markwallace/decl_azureai_persistent
markwallace-microsoft Jan 30, 2026
6de8bf1
Merge branch 'main' into users/markwallace/decl_azureai_persistent
markwallace-microsoft Feb 3, 2026
1d19b69
New sample for declarative OpenAI
markwallace-microsoft Feb 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 0 additions & 28 deletions agent-samples/openai/OpenAIAssistants.yaml

This file was deleted.

3 changes: 3 additions & 0 deletions dotnet/agent-framework-dotnet.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
</Folder>
<Folder Name="/Samples/GettingStarted/DeclarativeAgents/">
<Project Path="samples/GettingStarted/DeclarativeAgents/ChatClient/DeclarativeChatClientAgents.csproj" />
<Project Path="samples/GettingStarted/DeclarativeAgents/OpenAI/DeclarativeOpenAIAgents.csproj" />
</Folder>
<Folder Name="/Samples/GettingStarted/AGUI/">
<File Path="samples/GettingStarted/AGUI/README.md" />
Expand Down Expand Up @@ -402,6 +403,7 @@
<Project Path="src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj" />
<Project Path="src/Microsoft.Agents.AI.CopilotStudio/Microsoft.Agents.AI.CopilotStudio.csproj" />
<Project Path="src/Microsoft.Agents.AI.CosmosNoSql/Microsoft.Agents.AI.CosmosNoSql.csproj" />
<Project Path="src/Microsoft.Agents.AI.Declarative.AzureAI/Microsoft.Agents.AI.Declarative.AzureAI.csproj" />
<Project Path="src/Microsoft.Agents.AI.Declarative/Microsoft.Agents.AI.Declarative.csproj" />
<Project Path="src/Microsoft.Agents.AI.DevUI/Microsoft.Agents.AI.DevUI.csproj" />
<Project Path="src/Microsoft.Agents.AI.DurableTask/Microsoft.Agents.AI.DurableTask.csproj" />
Expand Down Expand Up @@ -446,6 +448,7 @@
<Project Path="tests/Microsoft.Agents.AI.AzureAI.Persistent.UnitTests/Microsoft.Agents.AI.AzureAI.Persistent.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.AzureAI.UnitTests/Microsoft.Agents.AI.AzureAI.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/Microsoft.Agents.AI.CosmosNoSql.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Declarative.AzureAI.UnitTests/Microsoft.Agents.AI.Declarative.AzureAI.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Declarative.UnitTests/Microsoft.Agents.AI.Declarative.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.DevUI.UnitTests/Microsoft.Agents.AI.DevUI.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.DurableTask.UnitTests/Microsoft.Agents.AI.DurableTask.UnitTests.csproj" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>

<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" />
<PackageReference Include="Azure.Identity" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
<PackageReference Include="Microsoft.Extensions.Configuration" />
<PackageReference Include="Microsoft.Agents.ObjectModel" />
<PackageReference Include="Microsoft.Agents.ObjectModel.Json" />
<PackageReference Include="Microsoft.Agents.ObjectModel.PowerFx" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Declarative.AzureAI\Microsoft.Agents.AI.Declarative.AzureAI.csproj" />
</ItemGroup>

</Project>
61 changes: 61 additions & 0 deletions dotnet/samples/GettingStarted/DeclarativeAgents/OpenAI/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) Microsoft. All rights reserved.

// This sample shows how to load an AI agent from a YAML file and process a prompt using Azure OpenAI as the backend.
// Unlike the ChatClient sample, this uses the OpenAIPromptAgentFactory which can create a ChatClient from the YAML model definition.

using System.ComponentModel;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;

string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");

// Read command-line arguments
if (args.Length < 2)
{
Console.WriteLine("Usage: DeclarativeOpenAIAgents <yaml-file-path> <prompt>");
Console.WriteLine(" <yaml-file-path>: The path to the YAML file containing the agent definition");
Console.WriteLine(" <prompt>: The prompt to send to the agent");
return;
}

string yamlFilePath = args[0];
string prompt = args[1];

// Verify the YAML file exists
if (!File.Exists(yamlFilePath))
{
Console.WriteLine($"Error: File not found: {yamlFilePath}");
return;
}

// Read the YAML content from the file
string text = await File.ReadAllTextAsync(yamlFilePath);

// Example function tool that can be used by the agent.
[Description("Get the weather for a given location.")]
static string GetWeather(
[Description("The city and state, e.g. San Francisco, CA")] string location,
[Description("The unit of temperature. Possible values are 'celsius' and 'fahrenheit'.")] string unit)
=> $"The weather in {location} is cloudy with a high of {(unit.Equals("celsius", StringComparison.Ordinal) ? "15°C" : "59°F")}.";

// Create the configuration with the Azure OpenAI endpoint
IConfiguration configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["AzureOpenAI:Endpoint"] = endpoint,
})
.Build();

// Create the agent from the YAML definition.
// OpenAIPromptAgentFactory can create a ChatClient based on the model defined in the YAML file.
OpenAIPromptAgentFactory agentFactory = new(
new Uri(endpoint),
new AzureCliCredential(),
[AIFunctionFactory.Create(GetWeather, "GetWeather")],
configuration: configuration);
AIAgent? agent = await agentFactory.CreateFromYamlAsync(text);

// Invoke the agent and output the text result.
Console.WriteLine(await agent!.RunAsync(prompt));
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"profiles": {
"OpenAI": {
"commandName": "Project",
"commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\openai\\OpenAI.yaml \"What is the weather in Cambridge, MA in °C?\""
},
"OpenAIChat": {
"commandName": "Project",
"commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\openai\\OpenAIChat.yaml \"What is the weather in Cambridge, MA in °C?\""
},
"OpenAIResponses": {
"commandName": "Project",
"commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\openai\\OpenAIResponses.yaml \"What is the weather in Cambridge, MA in °C?\""
}
}
}
63 changes: 63 additions & 0 deletions dotnet/samples/GettingStarted/DeclarativeAgents/OpenAI/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Declarative OpenAI Agents

This sample demonstrates how to use the `OpenAIPromptAgentFactory` to create AI agents from YAML definitions.

## Overview

Unlike the `ChatClientPromptAgentFactory` which requires you to create an `IChatClient` upfront, the `OpenAIPromptAgentFactory` can create the chat client based on the model definition in the YAML file. This is useful when:

- You want the model to be defined declaratively in the YAML file
- You need to support multiple models without changing code
- You want to use Azure OpenAI endpoints with token-based authentication

## Prerequisites

- .NET 10.0 SDK
- Azure OpenAI endpoint access
- Azure CLI installed and authenticated (`az login`)

## Configuration

Set the following environment variable:

```bash
# Required: Azure OpenAI endpoint URL
set AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com/
```

## Running the Sample

```bash
dotnet run -- <yaml-file-path> <prompt>
```

### Example

```bash
dotnet run -- agent.yaml "What is the weather in Seattle?"
```

## Sample YAML Agent Definition

Create a file named `agent.yaml` with the following content:

```yaml
name: WeatherAgent
description: An agent that can provide weather information
model:
api: chat
configuration:
azure_deployment: gpt-4o-mini
instructions: |
You are a helpful assistant that provides weather information.
Use the GetWeather function when asked about weather conditions.
```

## Key Differences from ChatClient Sample

| Feature | ChatClient Sample | OpenAI Sample |
|---------|------------------|---------------|
| Chat client creation | Manual (in code) | Automatic (from YAML model definition) |
| Model selection | Code-specified | YAML-specified |
| Factory class | `ChatClientPromptAgentFactory` | `OpenAIPromptAgentFactory` |
| Authentication | Passed to `AzureOpenAIClient` | Passed to factory constructor |
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Threading;
using System.Threading.Tasks;
using Azure.AI.Projects;
using Azure.Core;
using Microsoft.Agents.ObjectModel;
using Microsoft.Extensions.Configuration;
using Microsoft.PowerFx;
using Microsoft.Shared.Diagnostics;

namespace Microsoft.Agents.AI;

/// <summary>
/// Provides an <see cref="PromptAgentFactory"/> which creates instances of <see cref="AIAgent"/> using a <see cref="AIProjectClient"/>.
/// </summary>
public sealed class AzureAIPromptAgentFactory : PromptAgentFactory
{
private readonly AIProjectClient? _projectClient;
private readonly TokenCredential? _tokenCredential;

/// <summary>
/// Creates a new instance of the <see cref="AzureAIPromptAgentFactory"/> class with an associated <see cref="AIProjectClient"/>.
/// </summary>
/// <param name="projectClient">The <see cref="AIProjectClient"/> instance to use for creating agents.</param>
/// <param name="engine">Optional <see cref="RecalcEngine"/>, if none is provided a default instance will be created.</param>
/// <param name="configuration">The <see cref="IConfiguration"/> instance to use for configuration.</param>
public AzureAIPromptAgentFactory(AIProjectClient projectClient, RecalcEngine? engine = null, IConfiguration? configuration = null) : base(engine, configuration)
{
Throw.IfNull(projectClient);

this._projectClient = projectClient;
}

/// <summary>
/// Creates a new instance of the <see cref="AzureAIPromptAgentFactory"/> class with an associated <see cref="TokenCredential"/>.
/// </summary>
/// <param name="tokenCredential">The <see cref="TokenCredential"/> to use for authenticating requests.</param>
/// <param name="engine">Optional <see cref="RecalcEngine"/>, if none is provided a default instance will be created.</param>
/// <param name="configuration">The <see cref="IConfiguration"/> instance to use for configuration.</param>
public AzureAIPromptAgentFactory(TokenCredential tokenCredential, RecalcEngine? engine = null, IConfiguration? configuration = null) : base(engine, configuration)
{
Throw.IfNull(tokenCredential);

this._tokenCredential = tokenCredential;
}

/// <inheritdoc/>
public override async Task<AIAgent?> TryCreateAsync(GptComponentMetadata promptAgent, CancellationToken cancellationToken = default)
{
Throw.IfNull(promptAgent);
Throw.IfNullOrEmpty(promptAgent.Name);

var projectClient = this._projectClient ?? this.CreateAIProjectClient(promptAgent);

var modelId = promptAgent.Model?.ModelNameHint;
if (string.IsNullOrEmpty(modelId))
{
throw new InvalidOperationException("The model id must be specified in the agent definition model to create a foundry agent.");
}

return await projectClient.CreateAIAgentAsync(
name: promptAgent.Name,
model: modelId,
instructions: promptAgent.Instructions?.ToTemplateString() ?? string.Empty,
description: promptAgent.Description,
tools: promptAgent.GetAITools(),
cancellationToken: cancellationToken).ConfigureAwait(false);
}

private AIProjectClient CreateAIProjectClient(GptComponentMetadata promptAgent)
{
var externalModel = promptAgent.Model as CurrentModels;
var connection = externalModel?.Connection as RemoteConnection;
if (connection is not null)
{
var endpoint = connection.Endpoint?.Eval(this.Engine);
if (string.IsNullOrEmpty(endpoint))
{
throw new InvalidOperationException("The endpoint must be specified in the agent definition model connection to create an AIProjectClient.");
}
if (this._tokenCredential is null)
{
throw new InvalidOperationException("A TokenCredential must be registered in the service provider to create an AIProjectClient.");
}
return new AIProjectClient(new Uri(endpoint), this._tokenCredential);
}

throw new InvalidOperationException("A AIProjectClient must be registered in the service provider or a FoundryConnection must be specified in the agent definition model connection to create an AIProjectClient.");
}
}
Loading
Loading