.NET: Organize the .Net samples (#578)

* Organize the .Net samples

* Organize the .Net samples

* Merge latest from main

* Update sample to also include function calling telemetry (#577)

* Move package installation instructions to user-guide (#572)

* Move package installation instructions to user-guide

* Update user-documentation-dotnet/getting-started/README.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update docs/docs-templates/getting-started/README.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* .NET: Add SK-AF Migration Samples for Responses API. (#575)

* Responses wip

* Adding OpenAI Responses Migration samples

* Address all samples and code for Azure and OpenAI Responses Migration code

* Update dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step02_ReasoningModel/Program.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Organize the .Net samples

* Organize the .Net samples

* Merge latest from main

* Use Agent rather than AIAgent

* Rename agents getting started samples

* Use singular Agent

---------

Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Mark Wallace
2025-09-02 10:52:07 +00:00
committed by GitHub
co-authored by Copilot Roger Barreto
parent 9b61c72e18
commit 7dee184ae4
78 changed files with 239 additions and 698 deletions
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>12</LangVersion>
<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.Logging" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
<PackageReference Include="OpenAI" />
<PackageReference Include="OpenTelemetry" />
<PackageReference Include="OpenTelemetry.Exporter.Console" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
<PackageReference Include="System.Diagnostics.DiagnosticSource" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Microsoft.Extensions.AI.Agents.OpenAI\Microsoft.Extensions.AI.Agents.OpenAI.csproj" />
<ProjectReference Include="..\..\..\src\Microsoft.Extensions.AI.Agents\Microsoft.Extensions.AI.Agents.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,216 @@
// Copyright (c) Microsoft. All rights reserved.
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Metrics;
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OpenTelemetry;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
#region Setup Telemetry
const string SourceName = "OpenTelemetryAspire.ConsoleApp";
const string ServiceName = "AgentOpenTelemetry";
// Enable telemetry for agents
AppContext.SetSwitch("Microsoft.Extensions.AI.Agents.EnableTelemetry", true);
// Configure OpenTelemetry for Aspire dashboard
var otlpEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318";
// Create a resource to identify this service
var resource = ResourceBuilder.CreateDefault()
.AddService(ServiceName, serviceVersion: "1.0.0")
.AddAttributes(new Dictionary<string, object>
{
["service.instance.id"] = Environment.MachineName,
["deployment.environment"] = "development"
})
.Build();
// Setup tracing with resource
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(ServiceName, serviceVersion: "1.0.0"))
.AddSource(SourceName) // Our custom activity source
.AddSource("Microsoft.Extensions.AI.Agents") // Agent Framework telemetry
.AddHttpClientInstrumentation() // Capture HTTP calls to OpenAI
.AddOtlpExporter(options => { options.Endpoint = new Uri(otlpEndpoint); })
.Build();
// Setup metrics with resource and instrument name filtering
using var meterProvider = Sdk.CreateMeterProviderBuilder()
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(ServiceName, serviceVersion: "1.0.0"))
.AddMeter(SourceName) // Our custom meter
.AddMeter("Microsoft.Extensions.AI.Agents") // Agent Framework metrics
.AddHttpClientInstrumentation() // HTTP client metrics
.AddRuntimeInstrumentation() // .NET runtime metrics
.AddOtlpExporter(options => { options.Endpoint = new Uri(otlpEndpoint); })
.Build();
// Setup structured logging with OpenTelemetry
var serviceCollection = new ServiceCollection();
serviceCollection.AddLogging(loggingBuilder => loggingBuilder
.SetMinimumLevel(LogLevel.Debug)
.AddOpenTelemetry(options =>
{
options.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(ServiceName, serviceVersion: "1.0.0"));
options.AddOtlpExporter(otlpOptions =>
{
otlpOptions.Endpoint = new Uri(otlpEndpoint);
});
options.IncludeScopes = true;
options.IncludeFormattedMessage = true;
}));
using var activitySource = new ActivitySource(SourceName);
using var meter = new Meter(SourceName);
// Create custom metrics
var interactionCounter = meter.CreateCounter<int>("agent_interactions_total", description: "Total number of agent interactions");
var responseTimeHistogram = meter.CreateHistogram<double>("agent_response_time_seconds", description: "Agent response time in seconds");
#endregion
var serviceProvider = serviceCollection.BuildServiceProvider();
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
var appLogger = loggerFactory.CreateLogger<Program>();
Console.WriteLine("""
=== OpenTelemetry Aspire Demo ===
This demo shows OpenTelemetry integration with the Agent Framework.
You can view the telemetry data in the Aspire Dashboard.
Type your message and press Enter. Type 'exit' or empty message to quit.
""");
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";
// Log application startup
appLogger.LogInformation("OpenTelemetry Aspire Demo application started");
[Description("Get the weather for a given location.")]
static async Task<string> GetWeather([Description("The location to get the weather for.")] string location)
{
await Task.Delay(2000);
return $"The weather in {location} is cloudy with a high of 15°C.";
}
// To ensure chat client's function calling is captured in the open telemetry, the chat client needs to have UseOpenTelemetry after UseFunctionInvocation
using var instrumentedChatClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
.GetChatClient(deploymentName)
.AsIChatClient() // Converts a native OpenAI SDK ChatClient into a Microsoft.Extensions.AI.IChatClient
.AsBuilder()
.UseFunctionInvocation()
.UseOpenTelemetry(loggerFactory: loggerFactory, sourceName: SourceName, (cfg) => { cfg.EnableSensitiveData = true; })
.Build();
appLogger.LogInformation("Creating Agent with OpenTelemetry instrumentation");
// Create the agent with the instrumented chat client
using var agent = new ChatClientAgent(instrumentedChatClient,
name: "OpenTelemetryDemoAgent",
instructions: "You are a helpful assistant that provides concise and informative responses.",
tools: [AIFunctionFactory.Create(GetWeather)])
.WithOpenTelemetry(loggerFactory, SourceName); // Enable telemetry on the agent
var thread = agent.GetNewThread();
appLogger.LogInformation("Agent created successfully with ID: {AgentId}", agent.Id);
// Create a parent span for the entire agent session
using var sessionActivity = activitySource.StartActivity("Agent Session");
var sessionId = thread.ConversationId ?? Guid.NewGuid().ToString();
sessionActivity?.SetTag("agent.name", "OpenTelemetryDemoAgent");
sessionActivity?.SetTag("session.id", sessionId);
sessionActivity?.SetTag("session.start_time", DateTimeOffset.UtcNow.ToString("O"));
appLogger.LogInformation("Starting agent session with ID: {SessionId}", sessionId);
using (appLogger.BeginScope(new Dictionary<string, object> { ["SessionId"] = sessionId, ["AgentName"] = "OpenTelemetryDemoAgent" }))
{
var interactionCount = 0;
while (true)
{
Console.Write("You: ");
var userInput = Console.ReadLine();
if (string.IsNullOrWhiteSpace(userInput) || userInput.Equals("exit", StringComparison.OrdinalIgnoreCase))
{
appLogger.LogInformation("User requested to exit the session");
break;
}
interactionCount++;
appLogger.LogInformation("Processing user interaction #{InteractionNumber}: {UserInput}", interactionCount, userInput);
// Create a child span for each individual interaction
using var activity = activitySource.StartActivity("Agent Interaction");
activity?.SetTag("user.input", userInput);
activity?.SetTag("agent.name", "OpenTelemetryDemoAgent");
activity?.SetTag("interaction.number", interactionCount);
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
try
{
appLogger.LogDebug("Starting agent execution for interaction #{InteractionNumber}", interactionCount);
Console.Write("Agent: ");
// Run the agent (this will create its own internal telemetry spans)
await foreach (var update in agent.RunStreamingAsync(userInput, thread))
{
Console.Write(update.Text);
}
Console.WriteLine();
stopwatch.Stop();
var responseTime = stopwatch.Elapsed.TotalSeconds;
// Record metrics (similar to Python example)
interactionCounter.Add(1, new KeyValuePair<string, object?>("status", "success"));
responseTimeHistogram.Record(responseTime,
new KeyValuePair<string, object?>("status", "success"));
activity?.SetTag("response.success", true);
appLogger.LogInformation("Agent interaction #{InteractionNumber} completed successfully in {ResponseTime:F2} seconds",
interactionCount, responseTime);
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine();
stopwatch.Stop();
var responseTime = stopwatch.Elapsed.TotalSeconds;
// Record error metrics
interactionCounter.Add(1, new KeyValuePair<string, object?>("status", "error"));
responseTimeHistogram.Record(responseTime,
new KeyValuePair<string, object?>("status", "error"));
activity?.SetTag("response.success", false);
activity?.SetTag("error.message", ex.Message);
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
appLogger.LogError(ex, "Agent interaction #{InteractionNumber} failed after {ResponseTime:F2} seconds: {ErrorMessage}",
interactionCount, responseTime, ex.Message);
}
}
// Add session summary to the parent span
sessionActivity?.SetTag("session.total_interactions", interactionCount);
sessionActivity?.SetTag("session.end_time", DateTimeOffset.UtcNow.ToString("O"));
appLogger.LogInformation("Agent session completed. Total interactions: {TotalInteractions}", interactionCount);
} // End of logging scope
appLogger.LogInformation("OpenTelemetry Aspire Demo application shutting down");
@@ -0,0 +1,210 @@
# OpenTelemetry Aspire Demo with Azure OpenAI
This demo showcases the integration of OpenTelemetry with the Microsoft Agent Framework using Azure OpenAI and .NET Aspire Dashboard for telemetry visualization.
## Overview
The demo consists of two main components:
1. **Aspire Dashboard** - Provides a web-based interface to visualize OpenTelemetry data
2. **Console Application** - An interactive console application that demonstrates agent interactions with proper OpenTelemetry instrumentation
## Architecture
```mermaid
graph TD
A["Console App<br/>(Interactive)"] --> B["Agent Framework<br/>with OpenTel<br/>Instrumentation"]
B --> C["Azure OpenAI<br/>Service"]
A --> D["Aspire Dashboard<br/>(OpenTelemetry Visualization)"]
B --> D
```
## Prerequisites
- .NET 8.0 SDK or later
- Azure OpenAI service endpoint and deployment configured
- Azure CLI installed and authenticated (for Azure credential authentication)
- Docker installed (for running Aspire Dashboard)
## 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
### Quick Start (Using Script)
The easiest way to run the demo is using the provided PowerShell script:
```powershell
.\start-demo.ps1
```
This script will automatically:
- ✅ Check prerequisites (Docker, Azure OpenAI configuration)
- 🔨 Build the console application
- 🐳 Start the Aspire Dashboard via Docker (with anonymous access)
- ⏳ Wait for dashboard to be ready (polls port until listening)
- 🌐 Open your browser with the dashboard
- 📊 Configure telemetry endpoints (http://localhost:4317)
- 🎯 Start the interactive console application
### Manual Setup (Step by Step)
If you prefer to run the components manually:
#### Step 1: Start the Aspire Dashboard via Docker
```powershell
docker run -d --name aspire-dashboard -p 4318:18888 -p 4317:18889 -e DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS=true mcr.microsoft.com/dotnet/aspire-dashboard:9.0
```
#### Step 2: Access the Dashboard
Open your browser to: http://localhost:4318
#### Step 3: Run the Console Application
```powershell
cd dotnet/demos/AgentOpenTelemetry
$env:OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317"
dotnet run
```
#### Interacting with the Console Application
You should see a welcome message like:
```
=== OpenTelemetry Aspire Demo ===
This demo shows OpenTelemetry integration with the Agent Framework.
You can view the telemetry data in the Aspire Dashboard.
Type your message and press Enter. Type 'exit' or empty message to quit.
You:
```
1. Type your message and press Enter to interact with the AI agent
2. The agent will respond, and you can continue the conversation
3. Type `exit` to stop the application
**Note**: Make sure the Aspire Dashboard is running before starting the console application, as the telemetry data will be sent to the dashboard.
#### Step 4: Test the Integration
1. **Start the Aspire Dashboard** (if not already running)
2. **Run the Console Application** in a separate terminal
3. **Send a test message** like "Hello, how are you?"
4. **Check the Aspire Dashboard** - you should see:
- New traces appearing in the **Traces** tab
- Each trace showing the complete agent interaction flow
- Metrics in the **Metrics** tab showing token usage and duration
- Logs in the **Structured Logs** tab with detailed information
## Viewing Telemetry Data
### Traces
1. In the Aspire Dashboard, navigate to the **Traces** tab
2. You'll see traces for each agent interaction
3. Each trace contains:
- An outer span for the entire agent interaction
- Inner spans from the Agent Framework's OpenTelemetry instrumentation
- Spans from HTTP calls to Azure OpenAI
### Metrics
1. Navigate to the **Metrics** tab
2. View metrics related to:
- Agent execution duration
- Token usage (input/output tokens)
- Request counts
### Logs
1. Navigate to the **Structured Logs** tab
2. Filter by the console application to see detailed logs
3. Logs include information about user inputs, agent responses, and any errors
## Key Features Demonstrated
### OpenTelemetry Integration
- **Automatic instrumentation** of Agent Framework operations
- **Custom spans** for user interactions
- **Proper span lifecycle management** (create → execute → close)
- **Telemetry correlation** across the entire request flow
### Agent Framework Features
- **ChatClientAgent** with Azure OpenAI integration
- **OpenTelemetry wrapper** using `.WithOpenTelemetry()`
- **Conversation threading** for multi-turn conversations
- **Error handling** with telemetry correlation
### Aspire Dashboard Features
- **Real-time telemetry visualization**
- **Distributed tracing** across services
- **Metrics and logging** integration
- **Resource management** and monitoring
## Available Script
The demo includes a PowerShell script to make running the demo easy:
### `start-demo.ps1`
Complete demo startup script that handles everything automatically.
**Usage:**
```powershell
.\start-demo.ps1 # Start the complete demo
```
**Features:**
- **Automatic configuration detection** - Checks for Azure OpenAI configuration
- **Project building** - Automatically builds projects before running
- **Error handling** - Provides clear error messages if something goes wrong
- **Multi-window support** - Opens dashboard in separate window for better experience
- **Browser auto-launch** - Automatically opens the Aspire Dashboard in your browser
- **Docker integration** - Uses Docker to run the Aspire Dashboard
**Docker Endpoints:**
- **Aspire Dashboard**: `http://localhost:4318`
- **OTLP Telemetry**: `http://localhost:4317`
## Troubleshooting
### Port Conflicts
If you encounter port binding errors, try:
1. Stop any existing Docker containers using the same ports (`docker stop aspire-dashboard`)
2. Or kill any processes using the conflicting ports
### Authentication Issues
- Ensure your Azure OpenAI endpoint is correctly configured
- Check that the environment variables are set in the correct terminal session
- Verify you're logged in with Azure CLI (`az login`) and have access to the Azure OpenAI resource
- Ensure the Azure OpenAI deployment name matches your actual deployment
### Build Issues
- Ensure you're using .NET 9.0 SDK
- Run `dotnet restore` if you encounter package restore issues
- Check that all project references are correctly resolved
## Project Structure
```
AgentOpenTelemetry/
├── AgentOpenTelemetry.csproj # Project file with dependencies
├── Program.cs # Main application with Azure OpenAI agent integration
├── start-demo.ps1 # PowerShell script to start the demo
└── README.md # This file
```
## Next Steps
- Experiment with different prompts to see various telemetry patterns
- Explore the Aspire Dashboard's filtering and search capabilities
- Try modifying the OpenTelemetry configuration to add custom metrics or spans
- Integrate additional services to see distributed tracing in action
@@ -0,0 +1,139 @@
# OpenTelemetry Console Demo with Aspire Dashboard (Docker)
# This script starts the Aspire Dashboard via Docker and the Console Application
Write-Host "Starting OpenTelemetry Console Demo..." -ForegroundColor Green
Write-Host ""
# Check if we're in the right directory
if (!(Test-Path "AgentOpenTelemetry.csproj")) {
Write-Host "Error: Please run this script from the AgentOpenTelemetry directory" -ForegroundColor Red
Write-Host "Expected to find AgentOpenTelemetry.csproj file" -ForegroundColor Red
exit 1
}
# Check if Docker is running
try {
docker version | Out-Null
Write-Host "Docker is running" -ForegroundColor Green
} catch {
Write-Host "Docker is not running or not installed" -ForegroundColor Red
Write-Host "Please start Docker Desktop and try again" -ForegroundColor Red
exit 1
}
# Check for Azure OpenAI configuration
if ($env:AZURE_OPENAI_ENDPOINT) {
Write-Host "Found Azure OpenAI endpoint: $($env:AZURE_OPENAI_ENDPOINT)" -ForegroundColor Green
if ($env:AZURE_OPENAI_DEPLOYMENT_NAME) {
Write-Host "Using deployment: $($env:AZURE_OPENAI_DEPLOYMENT_NAME)" -ForegroundColor Green
} else {
Write-Host "Using default deployment: gpt-4o-mini" -ForegroundColor Cyan
}
} else {
Write-Host "Warning: AZURE_OPENAI_ENDPOINT not found!" -ForegroundColor Yellow
Write-Host "Please set the AZURE_OPENAI_ENDPOINT environment variable" -ForegroundColor Yellow
Write-Host "Example: `$env:AZURE_OPENAI_ENDPOINT='https://your-resource.openai.azure.com/'" -ForegroundColor Yellow
Write-Host ""
}
# Build console application
Write-Host ""
Write-Host "Building console application..." -ForegroundColor Cyan
$buildResult = dotnet build --verbosity quiet
if ($LASTEXITCODE -ne 0) {
Write-Host "Failed to build Console App" -ForegroundColor Red
exit 1
}
Write-Host "Build completed successfully" -ForegroundColor Green
Write-Host ""
Write-Host "Starting Aspire Dashboard via Docker..." -ForegroundColor Cyan
# Stop any existing Aspire Dashboard container
Write-Host "Stopping any existing Aspire Dashboard container..." -ForegroundColor Gray
docker stop aspire-dashboard-afdemo 2>$null | Out-Null
docker rm aspire-dashboard-afdemo 2>$null | Out-Null
# Start Aspire Dashboard in Docker daemon mode with fixed token
Write-Host "Starting Aspire Dashboard container..." -ForegroundColor Green
$fixedToken = "demo-token-12345"
$dockerResult = docker run -d `
--name aspire-dashboard-afdemo `
-p 4318:18888 `
-p 4317:18889 `
-e DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS=true `
--restart unless-stopped `
mcr.microsoft.com/dotnet/aspire-dashboard:9.0
if ($LASTEXITCODE -ne 0) {
Write-Host "Failed to start Aspire Dashboard container" -ForegroundColor Red
Write-Host "Make sure Docker is running and try again" -ForegroundColor Red
exit 1
}
Write-Host "Aspire Dashboard started successfully!" -ForegroundColor Green
Write-Host "OTLP Endpoint: http://localhost:4318" -ForegroundColor Cyan
# Wait for dashboard to be ready by polling the port
Write-Host "Waiting for dashboard to be ready..." -ForegroundColor Gray
$maxWaitSeconds = 10
$waitCount = 0
$dashboardReady = $false
while ($waitCount -lt $maxWaitSeconds -and !$dashboardReady) {
try {
$tcpConnection = Test-NetConnection -ComputerName "localhost" -Port 4317 -InformationLevel Quiet -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
if ($tcpConnection) {
$dashboardReady = $true
Write-Host "Dashboard is ready! (took $waitCount seconds)" -ForegroundColor Green
} else {
Write-Host "." -NoNewline -ForegroundColor Gray
Start-Sleep -Seconds 1
$waitCount++
}
} catch {
Write-Host "." -NoNewline -ForegroundColor Gray
Start-Sleep -Seconds 1
$waitCount++
}
}
if (!$dashboardReady) {
Write-Host ""
Write-Host "Dashboard port 4317 not responding after $maxWaitSeconds seconds" -ForegroundColor Yellow
Write-Host " Continuing anyway - dashboard might still be starting..." -ForegroundColor Yellow
} else {
Write-Host ""
}
# Open the dashboard in browser (anonymous access enabled)
Write-Host "Opening dashboard in browser..." -ForegroundColor Green
Write-Host "Dashboard URL: http://localhost:4318" -ForegroundColor Cyan
Start-Process "http://localhost:4318"
Write-Host ""
Write-Host "Starting Console Application..." -ForegroundColor Cyan
Write-Host "You can now interact with the AI agent!" -ForegroundColor Green
Write-Host ""
# Set the OTLP endpoint for the console application (Docker Aspire Dashboard)
$otlpEndpoint = "http://localhost:4317"
Write-Host "Using OTLP endpoint: $otlpEndpoint" -ForegroundColor Cyan
$env:OTEL_EXPORTER_OTLP_ENDPOINT = $otlpEndpoint
# Start the console application in the current window
Write-Host ""
Write-Host "Starting the console application..." -ForegroundColor Green
Write-Host "Tip: The dashboard should now be open in your browser!" -ForegroundColor Cyan
Write-Host ""
dotnet run --no-build
Write-Host ""
Write-Host "Demo completed!" -ForegroundColor Green
Write-Host "The Aspire Dashboard is still running in Docker." -ForegroundColor Gray
Write-Host "You can view telemetry data in the browser tab that opened." -ForegroundColor Gray
Write-Host "To stop the dashboard: docker stop aspire-dashboard-afdemo" -ForegroundColor Gray