From 71a0bf22eb935719c0b2e0a7825d41b5f235f524 Mon Sep 17 00:00:00 2001
From: westey <164392973+westey-m@users.noreply.github.com>
Date: Fri, 22 Aug 2025 15:58:10 +0100
Subject: [PATCH] .NET: Enhance console samples while preserving copyability
(#475)
* Enhance console samples while preserving copyability
* Add readme for minimal console demo
* Suppress pre-release version warning
* Address PR comments
* Change showing settings to opt-in
* Update comment
---
dotnet/agent-framework-dotnet.slnx | 8 +
.../AgentOpenTelemetry.csproj | 6 +-
dotnet/demos/AgentOpenTelemetry/Program.cs | 2 +-
dotnet/demos/Directory.Build.props | 20 ++-
.../MinimalConsole/MinimalConsole.csproj | 7 +-
dotnet/demos/MinimalConsole/Program.cs | 7 +-
dotnet/demos/MinimalConsole/README.md | 30 ++++
dotnet/src/Shared/Demos/README.md | 20 +++
dotnet/src/Shared/Demos/SampleEnvironment.cs | 142 ++++++++++++++++++
9 files changed, 229 insertions(+), 13 deletions(-)
create mode 100644 dotnet/demos/MinimalConsole/README.md
create mode 100644 dotnet/src/Shared/Demos/README.md
create mode 100644 dotnet/src/Shared/Demos/SampleEnvironment.cs
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index 59cd9f847c..033eceebb0 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -31,6 +31,10 @@
+
+
+
+
@@ -88,6 +92,10 @@
+
+
+
+
diff --git a/dotnet/demos/AgentOpenTelemetry/AgentOpenTelemetry.csproj b/dotnet/demos/AgentOpenTelemetry/AgentOpenTelemetry.csproj
index 2312d1d87f..16b7d163a8 100644
--- a/dotnet/demos/AgentOpenTelemetry/AgentOpenTelemetry.csproj
+++ b/dotnet/demos/AgentOpenTelemetry/AgentOpenTelemetry.csproj
@@ -2,9 +2,11 @@
Exe
+ net9.0
+ 12
+
+ enable
enable
- $(ProjectsTargetFrameworks)
- $(ProjectsDebugTargetFrameworks)
diff --git a/dotnet/demos/AgentOpenTelemetry/Program.cs b/dotnet/demos/AgentOpenTelemetry/Program.cs
index 228b4f1be9..cfc9434a6a 100644
--- a/dotnet/demos/AgentOpenTelemetry/Program.cs
+++ b/dotnet/demos/AgentOpenTelemetry/Program.cs
@@ -97,7 +97,7 @@ logger.LogInformation("OTLP endpoint configured: {OtlpEndpoint}", otlpEndpoint);
logger.LogDebug("Service name: {ServiceName}, Source name: {SourceName}", ServiceName, SourceName);
// Create the chat client
-var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT", EnvironmentVariableTarget.Machine) ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT environment variable is not set.");
+var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT environment variable is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
logger.LogInformation("Initializing Azure OpenAI client with endpoint: {Endpoint}", endpoint);
diff --git a/dotnet/demos/Directory.Build.props b/dotnet/demos/Directory.Build.props
index cf76e03054..d69fd22796 100644
--- a/dotnet/demos/Directory.Build.props
+++ b/dotnet/demos/Directory.Build.props
@@ -1,12 +1,20 @@
-
-
- false
- false
- net472;net9.0
- 5ee045b0-aea3-4f08-8d31-32d1a6f8fed0
+ true
+ AllEnabledByDefault
+ latest
+ true
+ Debug;Release;Publish
+ $(NoWarn);NU5104
+
+
+
+
+
+
+
+
diff --git a/dotnet/demos/MinimalConsole/MinimalConsole.csproj b/dotnet/demos/MinimalConsole/MinimalConsole.csproj
index 7020df82e5..078b334181 100644
--- a/dotnet/demos/MinimalConsole/MinimalConsole.csproj
+++ b/dotnet/demos/MinimalConsole/MinimalConsole.csproj
@@ -2,8 +2,11 @@
Exe
- $(ProjectsTargetFrameworks)
- $(ProjectsDebugTargetFrameworks)
+ net9.0
+ 12
+
+ enable
+ disable
diff --git a/dotnet/demos/MinimalConsole/Program.cs b/dotnet/demos/MinimalConsole/Program.cs
index a4c21ec673..0ebd6a3833 100644
--- a/dotnet/demos/MinimalConsole/Program.cs
+++ b/dotnet/demos/MinimalConsole/Program.cs
@@ -8,14 +8,17 @@ using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using OpenAI;
+var azureOpenAIEndpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
+var azureOpenAIDeploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_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.";
AIAgent agent = new AzureOpenAIClient(
- new Uri(Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!),
+ new Uri(azureOpenAIEndpoint),
new AzureCliCredential())
- .GetChatClient("gpt-4o-mini")
+ .GetChatClient(azureOpenAIDeploymentName)
.CreateAIAgent(
instructions: "You are a helpful assistant, you can help the user with weather information.",
tools: [AIFunctionFactory.Create(GetWeather)]);
diff --git a/dotnet/demos/MinimalConsole/README.md b/dotnet/demos/MinimalConsole/README.md
new file mode 100644
index 0000000000..dfba693f6c
--- /dev/null
+++ b/dotnet/demos/MinimalConsole/README.md
@@ -0,0 +1,30 @@
+# Minimal Console Application
+
+This demo shows a very basic console application, that uses the agent framework with Azure OpenAI and function calling.
+
+## Overview
+
+## Prerequisites
+
+- .NET 8.0 SDK or later
+- Azure OpenAI service endpoint and deployment configured
+- Azure CLI installed and authenticated (for Azure credential authentication)
+
+## Configuration
+
+### Azure OpenAI Setup
+Set the following environment variables:
+```powershell
+$env:AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/"
+$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini
+```
+
+**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure OpenAI resource.
+
+## Running the Demo
+
+```powershell
+cd dotnet/demos/MinimalConsole
+dotnet build
+dotnet run --no-build
+```
diff --git a/dotnet/src/Shared/Demos/README.md b/dotnet/src/Shared/Demos/README.md
new file mode 100644
index 0000000000..31380fab72
--- /dev/null
+++ b/dotnet/src/Shared/Demos/README.md
@@ -0,0 +1,20 @@
+# Demos
+
+Contains a helper that adds an override `System.Environment` class to a project.
+This override version has an enhanced `GetEnvironmentVariable` method that prompts the user
+to enter a value if the environment variable is not set.
+
+The code is still fully copyable to another project. These sample projects just allow for a simplified user experience
+for users who are new and just getting started.
+
+To use this in your project, add the following to your `.csproj` file:
+
+```xml
+
+
+
+
+
+
+
+```
diff --git a/dotnet/src/Shared/Demos/SampleEnvironment.cs b/dotnet/src/Shared/Demos/SampleEnvironment.cs
new file mode 100644
index 0000000000..80668651ce
--- /dev/null
+++ b/dotnet/src/Shared/Demos/SampleEnvironment.cs
@@ -0,0 +1,142 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+#pragma warning disable IDE0005 // Using directive is unnecessary. - need to suppress this, since this file is used in both projects with implicit usings and without.
+
+using System;
+using System.Collections;
+using SystemEnvironment = System.Environment;
+
+namespace SampleHelpers;
+
+internal static class SampleEnvironment
+{
+ public static string? GetEnvironmentVariable(string key)
+ {
+ // Allows for opting into showing all setting values in the console output, so that it is easy to troubleshoot sample setup issues.
+ var showAllSampleValues = SystemEnvironment.GetEnvironmentVariable("AF_SHOW_ALL_DEMO_SETTING_VALUES");
+ var shouldShowValue = showAllSampleValues?.ToUpperInvariant() == "Y";
+
+ var value = SystemEnvironment.GetEnvironmentVariable(key);
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ var color = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.Write("Setting '");
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.Write(key);
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine("' is not set in environment variables.");
+
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.Write("Please provide the setting for '");
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.Write(key);
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.Write("'> ");
+ Console.ForegroundColor = color;
+ value = Console.ReadLine();
+ value = string.IsNullOrWhiteSpace(value) ? null : value.Trim();
+
+ Console.WriteLine();
+ }
+ else if (shouldShowValue)
+ {
+ var color = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.Write("Using setting: Source=");
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.Write("EnvironmentVariables");
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.Write(", Key='");
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.Write(key);
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.Write("', Value='");
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.Write(value);
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine("'");
+ Console.ForegroundColor = color;
+
+ Console.WriteLine();
+ }
+
+ return value;
+ }
+
+ // Methods that directly call System.Environment
+
+ public static string? GetEnvironmentVariable(string variable, EnvironmentVariableTarget target)
+ => System.Environment.GetEnvironmentVariable(variable, target);
+
+ public static IDictionary GetEnvironmentVariables()
+ => System.Environment.GetEnvironmentVariables();
+
+ public static IDictionary GetEnvironmentVariables(EnvironmentVariableTarget target)
+ => System.Environment.GetEnvironmentVariables(target);
+
+ public static void SetEnvironmentVariable(string variable, string? value)
+ => System.Environment.SetEnvironmentVariable(variable, value);
+
+ public static void SetEnvironmentVariable(string variable, string? value, EnvironmentVariableTarget target)
+ => System.Environment.SetEnvironmentVariable(variable, value, target);
+
+ public static string[] GetCommandLineArgs()
+ => System.Environment.GetCommandLineArgs();
+
+ public static string CommandLine
+ => System.Environment.CommandLine;
+
+ public static string CurrentDirectory
+ {
+ get => System.Environment.CurrentDirectory;
+ set => System.Environment.CurrentDirectory = value;
+ }
+
+ public static string ExpandEnvironmentVariables(string name)
+ => System.Environment.ExpandEnvironmentVariables(name);
+
+ public static string GetFolderPath(System.Environment.SpecialFolder folder)
+ => System.Environment.GetFolderPath(folder);
+
+ public static string GetFolderPath(System.Environment.SpecialFolder folder, System.Environment.SpecialFolderOption option)
+ => System.Environment.GetFolderPath(folder, option);
+
+ public static int ProcessorCount
+ => System.Environment.ProcessorCount;
+
+ public static bool Is64BitProcess
+ => System.Environment.Is64BitProcess;
+
+ public static bool Is64BitOperatingSystem
+ => System.Environment.Is64BitOperatingSystem;
+
+ public static string MachineName
+ => System.Environment.MachineName;
+
+ public static string NewLine
+ => System.Environment.NewLine;
+
+ public static OperatingSystem OSVersion
+ => System.Environment.OSVersion;
+
+ public static string StackTrace
+ => System.Environment.StackTrace;
+
+ public static int SystemPageSize
+ => System.Environment.SystemPageSize;
+
+ public static bool HasShutdownStarted
+ => System.Environment.HasShutdownStarted;
+
+#if NET9_0_OR_GREATER
+ public static int ProcessId
+ => System.Environment.ProcessId;
+
+ public static string? ProcessPath
+ => System.Environment.ProcessPath;
+
+ public static bool IsPrivilegedProcess
+ => System.Environment.IsPrivilegedProcess;
+#endif
+}