Files
Chris 904a5b843e Python / .NET Samples - Restructure and Improve Samples (Feature Branc… (#4092)
* Python: .NET Samples - Restructure and Improve Samples (Feature Branch) (#4091)

* Moved by agent (#4094)

* Fix readme links

* .NET Samples - Create `04-hosting` learning path step (#4098)

* Agent move

* Agent reorderd

* Remove A2A section from README 

Removed A2A section from the Getting Started README.

* Agent fixed links

* Fix broken sample links in durable-agents README (#4101)

* Initial plan

* Fix broken internal links in documentation

Co-authored-by: crickman <66376200+crickman@users.noreply.github.com>

* Revert template link changes; keep only durable-agents README fix

Co-authored-by: crickman <66376200+crickman@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: crickman <66376200+crickman@users.noreply.github.com>

* .NET Samples - Create `03-workflows` learning path step (#4102)

* Fix solution project path

* Python: Fix broken markdown links to repo resources (outside /docs) (#4105)

* Initial plan

* Fix broken markdown links to repo resources

Co-authored-by: crickman <66376200+crickman@users.noreply.github.com>

* Update README to rename .NET Workflows Samples section

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: crickman <66376200+crickman@users.noreply.github.com>

* .NET Samples - Create `02-agents` learning path step (#4107)

* .NET: Fix broken relative link in GroupChatToolApproval README (#4108)

* Initial plan

* Fix broken link in GroupChatToolApproval README

Co-authored-by: crickman <66376200+crickman@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: crickman <66376200+crickman@users.noreply.github.com>

* Update labeler configuration for workflow samples

* .NET - Reorder Agents samples to start from Step01 instead of Step04 (#4110)

* Fix solution

* Resolve new sample paths

* Move new AgentSkills and AgentWithMemory_Step04 samples

* Fix link

* Fix readme path

* fix: update stale dotnet/samples/Durable path reference in AGENTS.md

Co-authored-by: crickman <66376200+crickman@users.noreply.github.com>

* Moved new sample

* Update solution

* Resolve merge (new sample)

* Sync to new sample - FoundryAgents_Step21_BingCustomSearch

* Updated README

* .NET Samples - Configuration Naming Update (#4149)

* .NET: Restore AzureFunctions index parity with ConsoleApps under DurableAgents samples (#4221)

* Clean-up `05_host_your_agent`

* Config setting consistency

* Refine samples

* AGENTS.md

* Move new samples

* Re-order samples

* Move new project and fixup solution

* Fixup model config

* Fix up new UT project

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
2026-02-26 00:56:10 +00:00

267 lines
9.8 KiB
C#

// Copyright (c) Microsoft. All rights reserved.
namespace AgentWebChat.AppHost;
public static class ModelExtensions
{
public static IResourceBuilder<AIModel> AddAIModel(this IDistributedApplicationBuilder builder, string name)
{
var model = new AIModel(name);
return builder.CreateResourceBuilder(model);
}
public static IResourceBuilder<AIModel> RunAsOpenAI(this IResourceBuilder<AIModel> builder, string modelName, IResourceBuilder<ParameterResource> apiKey)
{
if (builder.ApplicationBuilder.ExecutionContext.IsRunMode)
{
return builder.AsOpenAI(modelName, apiKey);
}
return builder;
}
public static IResourceBuilder<AIModel> PublishAsOpenAI(this IResourceBuilder<AIModel> builder, string modelName, IResourceBuilder<ParameterResource> apiKey)
{
if (builder.ApplicationBuilder.ExecutionContext.IsPublishMode)
{
return builder.AsOpenAI(modelName, apiKey);
}
return builder;
}
public static IResourceBuilder<AIModel> RunAsAzureOpenAI(this IResourceBuilder<AIModel> builder, string modelName, Action<IResourceBuilder<AzureOpenAIResource>>? configure)
{
if (builder.ApplicationBuilder.ExecutionContext.IsRunMode)
{
return builder.AsAzureOpenAI(modelName, configure);
}
return builder;
}
public static IResourceBuilder<AIModel> PublishAsAzureOpenAI(this IResourceBuilder<AIModel> builder, string modelName, Action<IResourceBuilder<AzureOpenAIResource>>? configure)
{
if (builder.ApplicationBuilder.ExecutionContext.IsPublishMode)
{
return builder.AsAzureOpenAI(modelName, configure);
}
return builder;
}
public static IResourceBuilder<AIModel> AsAzureOpenAI(this IResourceBuilder<AIModel> builder, string modelName, Action<IResourceBuilder<AzureOpenAIResource>>? configure)
{
builder.Reset();
var openAIModel = builder.ApplicationBuilder.AddAzureOpenAI(builder.Resource.Name);
configure?.Invoke(openAIModel);
builder.Resource.UnderlyingResource = openAIModel.Resource;
// Add the model name to the connection string
builder.Resource.ConnectionString = ReferenceExpression.Create($"{openAIModel.Resource.ConnectionStringExpression};Model={modelName}");
builder.Resource.Provider = "AzureOpenAI";
return builder;
}
public static IResourceBuilder<AIModel> RunAsAzureAIInference(this IResourceBuilder<AIModel> builder, string modelName, IResourceBuilder<ParameterResource> endpoint, IResourceBuilder<ParameterResource> apiKey)
{
if (builder.ApplicationBuilder.ExecutionContext.IsRunMode)
{
return builder.AsAzureAIInference(modelName, endpoint, apiKey);
}
return builder;
}
public static IResourceBuilder<AIModel> PublishAsAzureAIInference(this IResourceBuilder<AIModel> builder, string modelName, IResourceBuilder<ParameterResource> endpoint, IResourceBuilder<ParameterResource> apiKey)
{
if (builder.ApplicationBuilder.ExecutionContext.IsPublishMode)
{
return builder.AsAzureAIInference(modelName, endpoint, apiKey);
}
return builder;
}
public static IResourceBuilder<AIModel> AsAzureAIInference(this IResourceBuilder<AIModel> builder, string modelName, IResourceBuilder<ParameterResource> endpoint, IResourceBuilder<ParameterResource> apiKey)
{
builder.Reset();
// See: https://github.com/dotnet/aspire/issues/7641
var csb = new ReferenceExpressionBuilder();
csb.Append($"Endpoint={endpoint.Resource};");
csb.Append($"AccessKey={apiKey.Resource};");
csb.Append($"Model={modelName}");
var cs = csb.Build();
builder.ApplicationBuilder.AddResource(builder.Resource);
if (builder.ApplicationBuilder.ExecutionContext.IsRunMode)
{
var csTask = cs.GetValueAsync(default).AsTask();
if (!csTask.IsCompletedSuccessfully)
{
throw new InvalidOperationException("Connection string could not be resolved!");
}
#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits
builder.WithInitialState(new CustomResourceSnapshot
{
ResourceType = "Azure AI Inference Model",
State = KnownResourceStates.Running,
Properties = [
new("ConnectionString", csTask.Result ) { IsSensitive = true }
]
});
#pragma warning restore VSTHRD002
}
builder.Resource.UnderlyingResource = builder.Resource;
builder.Resource.ConnectionString = cs;
builder.Resource.Provider = "AzureAIInference";
return builder;
}
public static IResourceBuilder<AIModel> RunAsAzureAIInference(this IResourceBuilder<AIModel> builder, string modelName, string endpoint, IResourceBuilder<ParameterResource> apiKey)
{
if (builder.ApplicationBuilder.ExecutionContext.IsRunMode)
{
return builder.AsAzureAIInference(modelName, endpoint, apiKey);
}
return builder;
}
public static IResourceBuilder<AIModel> PublishAsAzureAIInference(this IResourceBuilder<AIModel> builder, string modelName, string endpoint, IResourceBuilder<ParameterResource> apiKey)
{
if (builder.ApplicationBuilder.ExecutionContext.IsPublishMode)
{
return builder.AsAzureAIInference(modelName, endpoint, apiKey);
}
return builder;
}
public static IResourceBuilder<AIModel> AsAzureAIInference(this IResourceBuilder<AIModel> builder, string modelName, string endpoint, IResourceBuilder<ParameterResource> apiKey)
{
builder.Reset();
// See: https://github.com/dotnet/aspire/issues/7641
var csb = new ReferenceExpressionBuilder();
csb.Append($"Endpoint={endpoint};");
csb.Append($"AccessKey={apiKey.Resource};");
csb.Append($"Model={modelName}");
var cs = csb.Build();
builder.ApplicationBuilder.AddResource(builder.Resource);
if (builder.ApplicationBuilder.ExecutionContext.IsRunMode)
{
var csTask = cs.GetValueAsync(default).AsTask();
if (!csTask.IsCompletedSuccessfully)
{
throw new InvalidOperationException("Connection string could not be resolved!");
}
#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits
builder.WithInitialState(new CustomResourceSnapshot
{
ResourceType = "Azure AI Inference Model",
State = KnownResourceStates.Running,
Properties = [
new("ConnectionString", csTask.Result ) { IsSensitive = true }
]
});
#pragma warning restore VSTHRD002
}
builder.Resource.UnderlyingResource = builder.Resource;
builder.Resource.ConnectionString = cs;
builder.Resource.Provider = "AzureAIInference";
return builder;
}
public static IResourceBuilder<AIModel> AsOpenAI(this IResourceBuilder<AIModel> builder, string modelName, IResourceBuilder<ParameterResource> apiKey)
{
builder.Reset();
// See: https://github.com/dotnet/aspire/issues/7641
var csb = new ReferenceExpressionBuilder();
csb.Append($"AccessKey={apiKey.Resource};");
csb.Append($"Model={modelName}");
var cs = csb.Build();
builder.ApplicationBuilder.AddResource(builder.Resource);
if (builder.ApplicationBuilder.ExecutionContext.IsRunMode)
{
var csTask = cs.GetValueAsync(default).AsTask();
if (!csTask.IsCompletedSuccessfully)
{
throw new InvalidOperationException("Connection string could not be resolved!");
}
#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits
builder.WithInitialState(new CustomResourceSnapshot
{
ResourceType = "OpenAI Model",
State = KnownResourceStates.Running,
Properties = [
new("ConnectionString", csTask.Result ) { IsSensitive = true }
]
});
#pragma warning restore VSTHRD002
}
builder.Resource.UnderlyingResource = builder.Resource;
builder.Resource.ConnectionString = cs;
builder.Resource.Provider = "OpenAI";
return builder;
}
private static void Reset(this IResourceBuilder<AIModel> builder)
{
// Reset the properties of the AIModel resource
if (builder.Resource.UnderlyingResource is { } underlyingResource)
{
builder.ApplicationBuilder.Resources.Remove(underlyingResource);
if (underlyingResource is IResourceWithParent resourceWithParent)
{
builder.ApplicationBuilder.Resources.Remove(resourceWithParent.Parent);
}
}
builder.Resource.ConnectionString = null;
builder.Resource.Provider = null;
}
}
// A resource representing an AI model.
public class AIModel(string name) : Resource(name), IResourceWithConnectionString
{
internal string? Provider { get; set; }
internal IResourceWithConnectionString? UnderlyingResource { get; set; }
internal ReferenceExpression? ConnectionString { get; set; }
public ReferenceExpression ConnectionStringExpression =>
this.Build();
public ReferenceExpression Build()
{
var connectionString = this.ConnectionString ?? throw new InvalidOperationException("No connection string available.");
if (this.Provider is null)
{
throw new InvalidOperationException("No provider configured.");
}
return ReferenceExpression.Create($"{connectionString};Provider={this.Provider}");
}
}