chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
@@ -0,0 +1,539 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Azure.Identity;
using Azure.Monitor.OpenTelemetry.Exporter;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.AzureAIInference;
using Microsoft.SemanticKernel.Connectors.Google;
using Microsoft.SemanticKernel.Connectors.HuggingFace;
using Microsoft.SemanticKernel.Connectors.MistralAI;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Services;
using OpenTelemetry;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
/// <summary>
/// Example of telemetry in Semantic Kernel using Application Insights within console application.
/// </summary>
public sealed class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static async Task Main()
{
// Enable model diagnostics with sensitive data.
AppContext.SetSwitch("Microsoft.SemanticKernel.Experimental.GenAI.EnableOTelDiagnosticsSensitive", true);
// Load configuration from environment variables or user secrets.
LoadUserSecrets();
var connectionString = TestConfiguration.ApplicationInsights.ConnectionString;
var resourceBuilder = ResourceBuilder
.CreateDefault()
.AddService("TelemetryExample");
using var traceProvider = Sdk.CreateTracerProviderBuilder()
.SetResourceBuilder(resourceBuilder)
.AddSource("Microsoft.SemanticKernel*")
.AddSource("Telemetry.Example")
.AddAzureMonitorTraceExporter(options => options.ConnectionString = connectionString)
.Build();
using var meterProvider = Sdk.CreateMeterProviderBuilder()
.SetResourceBuilder(resourceBuilder)
.AddMeter("Microsoft.SemanticKernel*")
.AddAzureMonitorMetricExporter(options => options.ConnectionString = connectionString)
.Build();
using var loggerFactory = LoggerFactory.Create(builder =>
{
// Add OpenTelemetry as a logging provider
builder.AddOpenTelemetry(options =>
{
options.SetResourceBuilder(resourceBuilder);
options.AddAzureMonitorLogExporter(options => options.ConnectionString = connectionString);
// Format log messages. This is default to false.
options.IncludeFormattedMessage = true;
options.IncludeScopes = true;
});
builder.SetMinimumLevel(MinLogLevel);
});
var kernel = GetKernel(loggerFactory);
using var activity = s_activitySource.StartActivity("Main");
Console.WriteLine($"Operation/Trace ID: {Activity.Current?.TraceId}");
Console.WriteLine();
Console.WriteLine("Write a poem about John Doe and translate it to Italian.");
using (var _ = s_activitySource.StartActivity("Chat"))
{
await RunAzureAIInferenceChatAsync(kernel);
Console.WriteLine();
await RunAzureOpenAIChatAsync(kernel);
Console.WriteLine();
await RunGoogleAIChatAsync(kernel);
Console.WriteLine();
await RunHuggingFaceChatAsync(kernel);
Console.WriteLine();
await RunMistralAIChatAsync(kernel);
}
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("Get weather.");
using (var _ = s_activitySource.StartActivity("ToolCalls"))
{
await RunAzureOpenAIToolCallsAsync(kernel);
Console.WriteLine();
}
Console.WriteLine("Run ChatCompletion Agent.");
using (var _ = s_activitySource.StartActivity("Agent"))
{
await RunChatCompletionAgentAsync(kernel);
Console.WriteLine();
}
}
#region Private
/// <summary>
/// Log level to be used by <see cref="ILogger"/>.
/// </summary>
/// <remarks>
/// <see cref="LogLevel.Information"/> is set by default. <para />
/// <see cref="LogLevel.Trace"/> will enable logging with more detailed information, including sensitive data. Should not be used in production. <para />
/// </remarks>
private const LogLevel MinLogLevel = LogLevel.Information;
/// <summary>
/// Instance of <see cref="ActivitySource"/> for the application activities.
/// </summary>
private static readonly ActivitySource s_activitySource = new("Telemetry.Example");
private const string AzureOpenAIServiceKey = "AzureOpenAI";
private const string GoogleAIGeminiServiceKey = "GoogleAIGemini";
private const string HuggingFaceServiceKey = "HuggingFace";
private const string MistralAIServiceKey = "MistralAI";
private const string AzureAIInferenceServiceKey = "AzureAIInference";
#region chat completion
private static async Task RunAzureAIInferenceChatAsync(Kernel kernel)
{
Console.WriteLine("============= Azure AI Inference Chat Completion =============");
if (TestConfiguration.AzureAIInference is null)
{
Console.WriteLine("Azure AI Inference is not configured. Skipping.");
return;
}
using var activity = s_activitySource.StartActivity(AzureAIInferenceServiceKey);
SetTargetService(kernel, AzureAIInferenceServiceKey);
try
{
await RunChatAsync(kernel);
}
catch (Exception ex)
{
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
Console.WriteLine($"Error: {ex.Message}");
}
}
private static async Task RunAzureOpenAIChatAsync(Kernel kernel)
{
Console.WriteLine("============= Azure OpenAI Chat Completion =============");
if (TestConfiguration.AzureOpenAI is null)
{
Console.WriteLine("Azure OpenAI is not configured. Skipping.");
return;
}
using var activity = s_activitySource.StartActivity(AzureOpenAIServiceKey);
SetTargetService(kernel, AzureOpenAIServiceKey);
try
{
await RunChatAsync(kernel);
}
catch (Exception ex)
{
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
Console.WriteLine($"Error: {ex.Message}");
}
}
private static async Task RunGoogleAIChatAsync(Kernel kernel)
{
Console.WriteLine("============= Google Gemini Chat Completion =============");
if (TestConfiguration.GoogleAI is null)
{
Console.WriteLine("Google AI is not configured. Skipping.");
return;
}
using var activity = s_activitySource.StartActivity(GoogleAIGeminiServiceKey);
SetTargetService(kernel, GoogleAIGeminiServiceKey);
try
{
await RunChatAsync(kernel);
}
catch (Exception ex)
{
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
Console.WriteLine($"Error: {ex.Message}");
}
}
private static async Task RunHuggingFaceChatAsync(Kernel kernel)
{
Console.WriteLine("============= HuggingFace Chat Completion =============");
if (TestConfiguration.HuggingFace is null)
{
Console.WriteLine("Hugging Face is not configured. Skipping.");
return;
}
using var activity = s_activitySource.StartActivity(HuggingFaceServiceKey);
SetTargetService(kernel, HuggingFaceServiceKey);
try
{
await RunChatAsync(kernel);
}
catch (Exception ex)
{
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
Console.WriteLine($"Error: {ex.Message}");
}
}
private static async Task RunMistralAIChatAsync(Kernel kernel)
{
Console.WriteLine("============= MistralAI Chat Completion =============");
if (TestConfiguration.MistralAI is null)
{
Console.WriteLine("Mistral AI is not configured. Skipping.");
return;
}
using var activity = s_activitySource.StartActivity(MistralAIServiceKey);
SetTargetService(kernel, MistralAIServiceKey);
try
{
await RunChatAsync(kernel);
}
catch (Exception ex)
{
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
Console.WriteLine($"Error: {ex.Message}");
}
}
private static async Task RunChatAsync(Kernel kernel)
{
// Create the plugin from the sample plugins folder without registering it to the kernel.
// We do not advise registering plugins to the kernel and then invoking them directly,
// especially when the service supports function calling. Doing so will cause unexpected behavior,
// such as repeated calls to the same function.
var folder = RepoFiles.SamplePluginsPath();
var plugin = kernel.CreatePluginFromPromptDirectory(Path.Combine(folder, "WriterPlugin"));
// Using non-streaming to get the poem.
var poem = await kernel.InvokeAsync<string>(
plugin["ShortPoem"],
new KernelArguments { ["input"] = "Write a poem about John Doe." });
Console.WriteLine($"Poem:\n{poem}\n");
// Use streaming to translate the poem.
Console.WriteLine("Translated Poem:");
await foreach (var update in kernel.InvokeStreamingAsync<string>(
plugin["Translate"],
new KernelArguments
{
["input"] = poem,
["language"] = "Italian"
}))
{
Console.Write(update);
}
}
#endregion
#region tool calls
private static async Task RunAzureOpenAIToolCallsAsync(Kernel kernel)
{
Console.WriteLine("============= Azure OpenAI ToolCalls =============");
if (TestConfiguration.AzureOpenAI is null)
{
Console.WriteLine("Azure OpenAI is not configured. Skipping.");
return;
}
using var activity = s_activitySource.StartActivity(AzureOpenAIServiceKey);
SetTargetService(kernel, AzureOpenAIServiceKey);
try
{
await RunAutoToolCallAsync(kernel);
}
catch (Exception ex)
{
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
Console.WriteLine($"Error: {ex.Message}");
}
}
private static async Task RunAutoToolCallAsync(Kernel kernel)
{
var result = await kernel.InvokePromptAsync("What is the weather like in my location?");
Console.WriteLine(result);
}
#endregion
#region Agent
private static async Task RunChatCompletionAgentAsync(Kernel kernel)
{
Console.WriteLine("============= ChatCompletion Agent =============");
if (TestConfiguration.AzureOpenAI is null)
{
Console.WriteLine("Azure OpenAI is not configured. Skipping.");
return;
}
SetTargetService(kernel, AzureOpenAIServiceKey);
// Define the agent
ChatCompletionAgent agent =
new()
{
Name = "TestAgent",
Instructions = "You are a helpful assistant.",
Kernel = kernel
};
ChatMessageContent message = new(AuthorRole.User, "Write a poem about John Doe.");
Console.WriteLine($"User: {message.Content}");
await foreach (AgentResponseItem<ChatMessageContent> response in agent.InvokeAsync(message))
{
Console.WriteLine($"Agent: {response.Message.Content}");
}
}
#endregion
private static Kernel GetKernel(ILoggerFactory loggerFactory)
{
var folder = RepoFiles.SamplePluginsPath();
IKernelBuilder builder = Kernel.CreateBuilder();
builder.Services.AddSingleton(loggerFactory);
if (TestConfiguration.AzureOpenAI is not null)
{
if (TestConfiguration.AzureOpenAI.ApiKey is not null)
{
builder.AddAzureOpenAIChatCompletion(
deploymentName: TestConfiguration.AzureOpenAI.ChatDeploymentName,
modelId: TestConfiguration.AzureOpenAI.ChatModelId,
endpoint: TestConfiguration.AzureOpenAI.Endpoint,
apiKey: TestConfiguration.AzureOpenAI.ApiKey,
serviceId: AzureOpenAIServiceKey);
}
else
{
builder.AddAzureOpenAIChatCompletion(
deploymentName: TestConfiguration.AzureOpenAI.ChatDeploymentName,
modelId: TestConfiguration.AzureOpenAI.ChatModelId,
endpoint: TestConfiguration.AzureOpenAI.Endpoint,
credentials: new AzureCliCredential(),
serviceId: AzureOpenAIServiceKey);
}
}
if (TestConfiguration.GoogleAI is not null)
{
builder.AddGoogleAIGeminiChatCompletion(
modelId: TestConfiguration.GoogleAI.Gemini.ModelId,
apiKey: TestConfiguration.GoogleAI.ApiKey,
serviceId: GoogleAIGeminiServiceKey);
}
if (TestConfiguration.HuggingFace is not null)
{
builder.AddHuggingFaceChatCompletion(
model: TestConfiguration.HuggingFace.ModelId,
endpoint: new Uri("https://api-inference.huggingface.co"),
apiKey: TestConfiguration.HuggingFace.ApiKey,
serviceId: HuggingFaceServiceKey);
}
if (TestConfiguration.MistralAI is not null)
{
builder.AddMistralChatCompletion(
modelId: TestConfiguration.MistralAI.ChatModelId,
apiKey: TestConfiguration.MistralAI.ApiKey,
serviceId: MistralAIServiceKey);
}
if (TestConfiguration.AzureAIInference is not null)
{
if (string.IsNullOrEmpty(TestConfiguration.AzureAIInference.ApiKey))
{
builder.AddAzureAIInferenceChatCompletion(
modelId: TestConfiguration.AzureAIInference.ModelId,
credential: new DefaultAzureCredential(),
endpoint: TestConfiguration.AzureAIInference.Endpoint,
serviceId: AzureAIInferenceServiceKey,
openTelemetrySourceName: "Telemetry.Example",
openTelemetryConfig: c => c.EnableSensitiveData = true);
}
else
{
builder.AddAzureAIInferenceChatCompletion(
modelId: TestConfiguration.AzureAIInference.ModelId,
apiKey: TestConfiguration.AzureAIInference.ApiKey,
endpoint: TestConfiguration.AzureAIInference.Endpoint,
serviceId: AzureAIInferenceServiceKey,
openTelemetrySourceName: "Telemetry.Example",
openTelemetryConfig: c => c.EnableSensitiveData = true);
}
}
builder.Services.AddSingleton<IAIServiceSelector>(new AIServiceSelector());
builder.Plugins.AddFromType<WeatherPlugin>();
builder.Plugins.AddFromType<LocationPlugin>();
return builder.Build();
}
private static void SetTargetService(Kernel kernel, string targetServiceKey)
{
if (kernel.Data.ContainsKey("TargetService"))
{
kernel.Data["TargetService"] = targetServiceKey;
}
else
{
kernel.Data.Add("TargetService", targetServiceKey);
}
}
private static void LoadUserSecrets()
{
IConfigurationRoot configRoot = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddUserSecrets<Program>()
.Build();
TestConfiguration.Initialize(configRoot);
}
private sealed class AIServiceSelector : IAIServiceSelector
{
public bool TrySelectAIService<T>(
Kernel kernel, KernelFunction function, KernelArguments arguments,
[NotNullWhen(true)] out T? service, out PromptExecutionSettings? serviceSettings) where T : class, IAIService
{
var targetServiceKey = kernel.Data.TryGetValue("TargetService", out object? value) ? value : null;
if (targetServiceKey is not null)
{
var targetService = kernel.Services.GetKeyedServices<T>(targetServiceKey).FirstOrDefault();
if (targetService is not null)
{
service = targetService;
serviceSettings = targetServiceKey switch
{
AzureOpenAIServiceKey => new OpenAIPromptExecutionSettings()
{
Temperature = 0,
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
},
GoogleAIGeminiServiceKey => new GeminiPromptExecutionSettings()
{
Temperature = 0,
// Not show casing the AutoInvokeKernelFunctions behavior for Gemini due the following issue:
// https://github.com/microsoft/semantic-kernel/issues/6282
// ToolCallBehavior = GeminiToolCallBehavior.AutoInvokeKernelFunctions
},
HuggingFaceServiceKey => new HuggingFacePromptExecutionSettings()
{
Temperature = 0,
},
MistralAIServiceKey => new MistralAIPromptExecutionSettings()
{
Temperature = 0,
ToolCallBehavior = MistralAIToolCallBehavior.AutoInvokeKernelFunctions
},
AzureAIInferenceServiceKey => new AzureAIInferencePromptExecutionSettings()
{
Temperature = 0,
// Function/Tool calling enabled models in Azure AI Inference are listed in the below page as "Tool calling: Yes/No"
// https://learn.microsoft.com/en-us/azure/ai-foundry/model-inference/concepts/models,
// Ensure your model support tool calling before enabling the setting below.
// FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
},
_ => null,
};
return true;
}
}
service = null;
serviceSettings = null;
return false;
}
}
#endregion
#region Plugins
public sealed class WeatherPlugin
{
[KernelFunction]
public string GetWeather(string location) => $"Weather in {location} is 70°F.";
}
public sealed class LocationPlugin
{
[KernelFunction]
public string GetCurrentLocation()
{
return "Seattle";
}
}
#endregion
}
@@ -0,0 +1,188 @@
# Semantic Kernel Telemetry with AppInsights
This sample project shows how a .Net application can be configured to send Semantic Kernel telemetry to Application Insights.
> Note that it is also possible to use other Application Performance Management (APM) vendors. An example is [Prometheus](https://prometheus.io/docs/introduction/overview/). Please refer to this [link](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics-collection#configure-the-example-app-to-use-opentelemetrys-prometheus-exporter) on how to do it.
For more information, please refer to the following articles:
1. [Observability](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/observability-with-otel)
2. [OpenTelemetry](https://opentelemetry.io/docs/)
3. [Enable Azure Monitor OpenTelemetry for .Net](https://learn.microsoft.com/en-us/azure/azure-monitor/app/opentelemetry-enable?tabs=net)
4. [Configure Azure Monitor OpenTelemetry for .Net](https://learn.microsoft.com/en-us/azure/azure-monitor/app/opentelemetry-configuration?tabs=net)
5. [Add, modify, and filter Azure Monitor OpenTelemetry](https://learn.microsoft.com/en-us/azure/azure-monitor/app/opentelemetry-add-modify?tabs=net)
6. [Customizing OpenTelemetry .NET SDK for Metrics](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/docs/metrics/customizing-the-sdk/README.md)
7. [Customizing OpenTelemetry .NET SDK for Logs](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/docs/logs/customizing-the-sdk/README.md)
## What to expect
The Semantic Kernel .Net SDK is designed to efficiently generate comprehensive logs, traces, and metrics throughout the flow of function execution and model invocation. This allows you to effectively monitor your AI application's performance and accurately track token consumption.
> `ActivitySource.StartActivity` internally determines if there are any listeners recording the Activity. If there are no registered listeners or there are listeners that are not interested, StartActivity() will return null and avoid creating the Activity object. Read more [here](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/distributed-tracing-instrumentation-walkthroughs).
## OTel Semantic Conventions
Semantic Kernel is also committed to provide the best developer experience while complying with the industry standards for observability. For more information, please review [ADR](../../../../docs/decisions/0044-OTel-semantic-convention.md).
The OTel GenAI semantic conventions are experimental. There are two options to enable the feature:
1. AppContext switch:
- `Microsoft.SemanticKernel.Experimental.GenAI.EnableOTelDiagnostics`
- `Microsoft.SemanticKernel.Experimental.GenAI.EnableOTelDiagnosticsSensitive`
2. Environment variable
- `SEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICS`
- `SEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE`
> Enabling the collection of sensitive data including prompts and responses will implicitly enable the feature.
## Configuration
### Require resources
1. [Application Insights](https://learn.microsoft.com/en-us/azure/azure-monitor/app/create-workspace-resource)
2. [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/create-resource?pivots=web-portal)
### Secrets
This example will require secrets and credentials to access your Application Insights instance and Azure OpenAI.
We suggest using .NET [Secret Manager](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets)
to avoid the risk of leaking secrets into the repository, branches and pull requests.
You can also use environment variables if you prefer.
To set your secrets with Secret Manager:
```
cd dotnet/samples/TelemetryExample
dotnet user-secrets set "AzureOpenAI:ChatDeploymentName" "..."
dotnet user-secrets set "AzureOpenAI:ChatModelId" "..."
dotnet user-secrets set "AzureOpenAI:Endpoint" "https://... .openai.azure.com/"
dotnet user-secrets set "AzureOpenAI:ApiKey" "..."
dotnet user-secrets set "GoogleAI:Gemini:ModelId" "..."
dotnet user-secrets set "GoogleAI:ApiKey" "..."
dotnet user-secrets set "HuggingFace:ModelId" "..."
dotnet user-secrets set "HuggingFace:ApiKey" "..."
dotnet user-secrets set "MistralAI:ChatModelId" "mistral-large-latest"
dotnet user-secrets set "MistralAI:ApiKey" "..."
dotnet user-secrets set "ApplicationInsights:ConnectionString" "..."
```
## Running the sample
Simply run `dotnet run` under this directory if the command line interface is preferred. Otherwise, this example can also be run in Visual Studio.
> This will output the Operation/Trace ID, which can be used later in Application Insights for searching the operation.
## Application Insights/Azure Monitor
### Logs and traces
Go to your Application Insights instance, click on _Transaction search_ on the left menu. Use the operation id output by the program to search for the logs and traces associated with the operation. Click on any of the search result to view the end-to-end transaction details. Read more [here](https://learn.microsoft.com/en-us/azure/azure-monitor/app/transaction-search-and-diagnostics?tabs=transaction-search).
### Metrics
Running the application once will only generate one set of measurements (for each metrics). Run the application a couple times to generate more sets of measurements.
> Note: Make sure not to run the program too frequently. Otherwise, you may get throttled.
Please refer to here on how to analyze metrics in [Azure Monitor](https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/analyze-metrics).
### Log Analytics
It is also possible to use Log Analytics to query the telemetry items sent by the sample application. Please read more [here](https://learn.microsoft.com/en-us/azure/azure-monitor/logs/log-analytics-tutorial).
For example, to create a pie chart to summarize the Handlebars planner status:
```kql
dependencies
| where name == "Microsoft.SemanticKernel.Planning.Handlebars.HandlebarsPlanner"
| extend status = iff(success == True, "Success", "Failure")
| summarize count() by status
| render piechart
```
Or to create a bar chart to summarize the Handlebars planner status by date:
```kql
dependencies
| where name == "Microsoft.SemanticKernel.Planning.Handlebars.HandlebarsPlanner"
| extend status = iff(success == True, "Success", "Failure"), day = bin(timestamp, 1d)
| project day, status
| summarize
success = countif(status == "Success"),
failure = countif(status == "Failure") by day
| extend day = format_datetime(day, "MM/dd/yy")
| order by day
| render barchart
```
Or to see status and performance of each planner run:
```kql
dependencies
| where name == "Microsoft.SemanticKernel.Planning.Handlebars.HandlebarsPlanner"
| extend status = iff(success == True, "Success", "Failure")
| project timestamp, id, status, performance = performanceBucket
| order by timestamp
```
It is also possible to summarize the total token usage:
```kql
customMetrics
| where name == "semantic_kernel.connectors.openai.tokens.total"
| project value
| summarize sum(value)
| project Total = sum_value
```
Or track token usage by functions:
```kql
customMetrics
| where name == "semantic_kernel.function.invocation.token_usage.prompt" and customDimensions has "semantic_kernel.function.name"
| project customDimensions, value
| extend function = tostring(customDimensions["semantic_kernel.function.name"])
| project function, value
| summarize sum(value) by function
| render piechart
```
### Azure Dashboard
You can create an Azure Dashboard to visualize the custom telemetry items. You can read more here: [Create a new dashboard](https://learn.microsoft.com/en-us/azure/azure-monitor/app/overview-dashboard#create-a-new-dashboard).
## Aspire Dashboard
You can also use the [Aspire dashboard](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/overview) for local development.
### Steps
- Follow this [code sample](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/overview) to start an Aspire dashboard in a docker container.
- Add the package to the project: **`OpenTelemetry.Exporter.OpenTelemetryProtocol`**
- Replace all occurrences of
```c#
.AddAzureMonitorLogExporter(...)
```
with
```c#
.AddOtlpExporter(options => options.Endpoint = new Uri("http://localhost:4317"))
```
- Run the app and you can visual the traces in the Aspire dashboard.
## More information
- [Telemetry docs](../../../docs/TELEMETRY.md)
- [Planner telemetry improvement ADR](../../../../docs/decisions/0025-planner-telemetry-enhancement.md)
- [OTel Semantic Conventions ADR](../../../../docs/decisions/0044-OTel-semantic-convention.md)
@@ -0,0 +1,37 @@
// Copyright (c) Microsoft. All rights reserved.
using System.IO;
using System.Reflection;
internal static class RepoFiles
{
/// <summary>
/// Scan the local folders from the repo, looking for "prompt_template_samples" folder.
/// </summary>
/// <returns>The full path to prompt_template_samples</returns>
public static string SamplePluginsPath()
{
const string Folder = "prompt_template_samples";
static bool SearchPath(string pathToFind, out string result, int maxAttempts = 10)
{
var currDir = Path.GetFullPath(Assembly.GetExecutingAssembly().Location);
bool found;
do
{
result = Path.Join(currDir, pathToFind);
found = Directory.Exists(result);
currDir = Path.GetFullPath(Path.Combine(currDir, ".."));
} while (maxAttempts-- > 0 && !found);
return found;
}
if (!SearchPath(Folder, out var path))
{
throw new DirectoryNotFoundException("Plugins directory not found. The app needs the plugins from the repo to work.");
}
return path;
}
}
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<ImplicitUsings>disable</ImplicitUsings>
<IsPackable>false</IsPackable>
<!-- Suppress: "Declare types in namespaces", "Require ConfigureAwait" -->
<NoWarn>$(NoWarn);CA1024;CA1050;CA1707;CA2007;CS1591;VSTHRD111,SKEXP0050,SKEXP0060,SKEXP0001</NoWarn>
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Identity" />
<PackageReference Include="OpenTelemetry" />
<PackageReference Include="OpenTelemetry.Api" />
<PackageReference Include="OpenTelemetry.Api.ProviderBuilderExtensions" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
<PackageReference Include="Azure.Monitor.OpenTelemetry.Exporter" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Connectors\Connectors.AzureAIInference\Connectors.AzureAIInference.csproj" />
<ProjectReference Include="..\..\..\src\Connectors\Connectors.AzureOpenAI\Connectors.AzureOpenAI.csproj" />
<ProjectReference Include="..\..\..\src\Connectors\Connectors.MistralAI\Connectors.MistralAI.csproj" />
<ProjectReference Include="..\..\..\src\Connectors\Connectors.Google\Connectors.Google.csproj" />
<ProjectReference Include="..\..\..\src\Connectors\Connectors.HuggingFace\Connectors.HuggingFace.csproj" />
<ProjectReference Include="..\..\..\src\SemanticKernel.Core\SemanticKernel.Core.csproj" />
<ProjectReference Include="..\..\..\src\Agents\Core\Agents.Core.csproj" />
<ProjectReference Include="..\..\..\src\Plugins\Plugins.Core\Plugins.Core.csproj" />
<ProjectReference Include="..\..\..\src\Plugins\Plugins.Web\Plugins.Web.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,109 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Microsoft.Extensions.Configuration;
public sealed class TestConfiguration
{
private readonly IConfigurationRoot _configRoot;
private static TestConfiguration? s_instance;
private TestConfiguration(IConfigurationRoot configRoot)
{
this._configRoot = configRoot;
}
public static void Initialize(IConfigurationRoot configRoot)
{
s_instance = new TestConfiguration(configRoot);
}
public static AzureOpenAIConfig? AzureOpenAI => LoadSection<AzureOpenAIConfig>();
public static AzureAIInferenceConfig? AzureAIInference => LoadSection<AzureAIInferenceConfig>();
public static ApplicationInsightsConfig ApplicationInsights => LoadRequiredSection<ApplicationInsightsConfig>();
public static GoogleAIConfig? GoogleAI => LoadSection<GoogleAIConfig>();
public static HuggingFaceConfig? HuggingFace => LoadSection<HuggingFaceConfig>();
public static MistralAIConfig? MistralAI => LoadSection<MistralAIConfig>();
private static T? LoadSection<T>([CallerMemberName] string? caller = null)
{
if (s_instance is null)
{
throw new InvalidOperationException(
"TestConfiguration must be initialized with a call to Initialize(IConfigurationRoot) before accessing configuration values.");
}
if (string.IsNullOrEmpty(caller))
{
throw new ArgumentNullException(nameof(caller));
}
return s_instance._configRoot.GetSection(caller).Get<T>();
}
private static T LoadRequiredSection<T>([CallerMemberName] string? caller = null)
{
var section = LoadSection<T>(caller);
if (section is not null)
{
return section;
}
throw new KeyNotFoundException($"Could not find configuration section {caller}");
}
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor.
public class AzureOpenAIConfig
{
public string ChatDeploymentName { get; set; }
public string ChatModelId { get; set; }
public string Endpoint { get; set; }
public string ApiKey { get; set; }
}
public class ApplicationInsightsConfig
{
public string ConnectionString { get; set; }
}
public class GoogleAIConfig
{
public string ApiKey { get; set; }
public string EmbeddingModelId { get; set; }
public GeminiConfig Gemini { get; set; }
public class GeminiConfig
{
public string ModelId { get; set; }
}
}
public class HuggingFaceConfig
{
public string ApiKey { get; set; }
public string ModelId { get; set; }
public string EmbeddingModelId { get; set; }
}
public class MistralAIConfig
{
public string ApiKey { get; set; }
public string ChatModelId { get; set; }
}
public class AzureAIInferenceConfig
{
public Uri Endpoint { get; set; }
public string ApiKey { get; set; }
public string ModelId { get; set; }
}
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor.
}