Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
dcb96cf
Refactor of SK Sample to add more OTEL and Refactor for behavior of T…
MattB-msft Nov 14, 2025
67f2e9c
Update devin agent sample (#48)
walterluna Nov 14, 2025
10f3bd1
Add streaming support and refactor Agent365 logic
MattB-msft Nov 14, 2025
2916688
Update LocalPackages path in nuget.config
MattB-msft Nov 15, 2025
8b508ae
Comment out `EnabledOtlpExporter` configuration
MattB-msft Nov 15, 2025
d8899e0
Add copyright headers and clean up unused directives
MattB-msft Nov 15, 2025
e826736
updating samples read me docs (#47)
abdulanu0 Nov 15, 2025
9751074
Update Node.js OpenAI sample (#50)
pontemonti Nov 17, 2025
c7451e9
Introducing thinking indicator in Perplexity sample agent (#37)
aubreyquinn Nov 17, 2025
38aa59c
updating auth handler (#53)
abdulanu0 Nov 18, 2025
caece1a
Update Python Agent Framework sample (#58)
pontemonti Nov 18, 2025
3e650a1
Update Python OpenAI sample agent (#57)
pontemonti Nov 18, 2025
c0ba1ea
Introducing observability in Perplexity agent (#44)
aubreyquinn Nov 18, 2025
a8cf16a
Add prompt injection defense to agent system prompt (#40)
efpiva Nov 18, 2025
a819085
Update Package References to Use npm Registry + Remove local preinsta…
JesuTerraz Nov 18, 2025
41cbe76
Add temporariy thumbnails to the related samples (#63)
Alive-Fish Nov 18, 2025
6c3516d
Update .NET Semantic Kernel Sample Agent (#61)
pontemonti Nov 19, 2025
df09bfd
Replace SemanticKernelSampleAgent with WeatherAgent
MattB-msft Nov 19, 2025
e9b1283
Update tooling manifest (#60)
pontemonti Nov 19, 2025
c1306c8
Introducing Microsoft Teams manifest file for Perplexity (#51)
aubreyquinn Nov 19, 2025
dad4562
Refactor auth handlers and improve observability logic
MattB-msft Nov 19, 2025
2f41b20
Add manifest template for n8n Sample (#49)
rbrighenti Nov 19, 2025
1006ab1
Potential fix for code scanning alert no. 1: Workflow does not contai…
pontemonti Nov 19, 2025
6206e14
Add CI workflow for Node.js OpenAI sample agent (#65)
pontemonti Nov 19, 2025
a228c22
Remove local reference in python samples (#62)
JesuTerraz Nov 19, 2025
1ecfaab
Google ADK Sample with Tooling (#70)
JesuTerraz Nov 20, 2025
a913033
Updated formatting on readme file (#71)
aubreyquinn Nov 20, 2025
21372bc
Perplexity: introducing the published agents-a365 packages (#72)
aubreyquinn Nov 20, 2025
1942933
Add Devin Agent's manifest sample (#52)
walterluna Nov 20, 2025
ed5456b
reference public package dependencies (#64)
walterluna Nov 20, 2025
40b5ced
Perplexity: added telemetry markers to all paths in the code (#73)
aubreyquinn Nov 20, 2025
e3a96e0
Remove `Assets` project and `nuget.config` from solutions
MattB-msft Nov 21, 2025
552c146
Refactor of SK Sample to add more OTEL and Refactor for behavior of T…
MattB-msft Nov 14, 2025
ec574fb
Add streaming support and refactor Agent365 logic
MattB-msft Nov 14, 2025
6f43e2b
Update LocalPackages path in nuget.config
MattB-msft Nov 15, 2025
7a8dbd4
Comment out `EnabledOtlpExporter` configuration
MattB-msft Nov 15, 2025
606af84
Add copyright headers and clean up unused directives
MattB-msft Nov 15, 2025
d721e21
Replace SemanticKernelSampleAgent with WeatherAgent
MattB-msft Nov 19, 2025
86c94fc
Refactor auth handlers and improve observability logic
MattB-msft Nov 19, 2025
a5b39f8
Remove `Assets` project and `nuget.config` from solutions
MattB-msft Nov 21, 2025
07159dc
Rebase
MattB-msft Nov 21, 2025
c328b68
Rebasing fixes.
MattB-msft Nov 21, 2025
824be09
fix path on build.
MattB-msft Nov 21, 2025
79d8fc7
Remove Warnings.
MattB-msft Nov 21, 2025
7afde0e
Renamed AgentFramework Sample. Project names
MattB-msft Nov 21, 2025
2377345
Renamed Weather agent to MyAgent
MattB-msft Nov 21, 2025
581e5c8
Updated Names
MattB-msft Nov 21, 2025
5413fa6
Update CI workflow for .NET Agent Framework solution
MattB-msft Nov 21, 2025
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
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,9 @@ coverage/

# OS-specific files
.DS_Store
Thumbs.db
Thumbs.db

# agents SDK Transcript logger.
agents_*/
*transcript.json
msteams*/
198 changes: 198 additions & 0 deletions dotnet/AspireOTelServiceDefaults/AgentOTELExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.ServiceDiscovery;
using OpenTelemetry;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

namespace Microsoft.Extensions.Hosting
{
// Adds common Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
// This can be used by ASP.NET Core apps, Azure Functions, and other .NET apps using the Generic Host.
// This allows you to use the local aspire desktop and monitor Agents SDK operations.
// To learn more about using the local aspire desktop, see https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/standalone?tabs=bash
public static class AgentOTELExtensions
{
private const string HealthEndpointPath = "/health";
private const string AlivenessEndpointPath = "/alive";

public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
{
builder.ConfigureOpenTelemetry();

builder.AddDefaultHealthChecks();

builder.Services.AddServiceDiscovery();

builder.Services.ConfigureHttpClientDefaults(http =>
{
// Turn on resilience by default
http.AddStandardResilienceHandler();

// Turn on service discovery by default
http.AddServiceDiscovery();
});

// Uncomment the following to restrict the allowed schemes for service discovery.
// builder.Services.Configure<ServiceDiscoveryOptions>(options =>
// {
// options.AllowedSchemes = ["https"];
// });

return builder;
}

public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
{
builder.Logging.AddOpenTelemetry(logging =>
{
logging.IncludeFormattedMessage = true;
logging.IncludeScopes = true;
});

builder.Services.AddOpenTelemetry()
.ConfigureResource(r => r
.Clear()
.AddService(
serviceName: "Agent365SemanticKernelSampleAgent",
serviceVersion: "1.0.0",
serviceInstanceId: Environment.MachineName)
.AddAttributes(new Dictionary<string, object>
{
["deployment.environment"] = builder.Environment.EnvironmentName,
["service.namespace"] = "Microsoft.Agents"
}))
.WithMetrics(metrics =>
{
metrics.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddRuntimeInstrumentation();
})
.WithTracing(tracing =>
{
tracing.AddSource(builder.Environment.ApplicationName)
.AddSource(
"Agent365SemanticKernelSampleAgent",
"Microsoft.Agents.Builder",
"Microsoft.Agents.Hosting",
"Agent365SemanticKernelSampleAgent.MyAgent",
"Microsoft.AspNetCore",
"System.Net.Http"
)
.AddAspNetCoreInstrumentation(tracing =>
{
// Exclude health check requests from tracing
tracing.Filter = context =>
!context.Request.Path.StartsWithSegments(HealthEndpointPath)
&& !context.Request.Path.StartsWithSegments(AlivenessEndpointPath);
tracing.RecordException = true;
tracing.EnrichWithHttpRequest = (activity, request) =>
{
activity.SetTag("http.request.body.size", request.ContentLength);
activity.SetTag("user_agent", request.Headers.UserAgent);
};
tracing.EnrichWithHttpResponse = (activity, response) =>
{
activity.SetTag("http.response.body.size", response.ContentLength);
};
})
// Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
//.AddGrpcClientInstrumentation()
.AddHttpClientInstrumentation(o =>
{
o.RecordException = true;
// Enrich outgoing request/response with extra tags
o.EnrichWithHttpRequestMessage = (activity, request) =>
{
activity.SetTag("http.request.method", request.Method);
activity.SetTag("http.request.host", request.RequestUri?.Host);
activity.SetTag("http.request.useragent", request.Headers?.UserAgent);
};
o.EnrichWithHttpResponseMessage = (activity, response) =>
{
activity.SetTag("http.response.status_code", (int)response.StatusCode);
//activity.SetTag("http.response.headers", response.Content.Headers);
// Convert response.Content.Headers to a string array: "HeaderName=val1,val2"
var headerList = response.Content?.Headers?
.Select(h => $"{h.Key}={string.Join(",", h.Value)}")
.ToArray();

if (headerList is { Length: > 0 })
{
// Set as an array tag (preferred for OTEL exporters supporting array-of-primitive attributes)
activity.SetTag("http.response.headers", headerList);

// (Optional) Also emit individual header tags (comment out if too high-cardinality)
// foreach (var h in response.Content.Headers)
// {
// activity.SetTag($"http.response.header.{h.Key.ToLowerInvariant()}", string.Join(",", h.Value));
// }
}

};
// Example filter: suppress telemetry for health checks
o.FilterHttpRequestMessage = request =>
!request.RequestUri?.AbsolutePath.Contains("health", StringComparison.OrdinalIgnoreCase) ?? true;
});
});

//builder.AddOpenTelemetryExporters();
return builder;
}

public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
{
builder.Services.AddHealthChecks()
// Add a default liveness check to ensure app is responsive
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);

return builder;
}

public static WebApplication MapDefaultEndpoints(this WebApplication app)
{
// Adding health checks endpoints to applications in non-development environments has security implications.
// See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments.
if (app.Environment.IsDevelopment())
{
// All health checks must pass for app to be considered ready to accept traffic after starting
app.MapHealthChecks(HealthEndpointPath);

// Only health checks tagged with the "live" tag must pass for app to be considered alive
app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("live")
});
}

return app;
}

private static TBuilder AddOpenTelemetryExporters<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
{
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);

if (useOtlpExporter)
{
builder.Services.AddOpenTelemetry().UseOtlpExporter();
}

// Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
//if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
//{
// builder.Services.AddOpenTelemetry()
// .UseAzureMonitor();
//}

return builder;
}

}
}
22 changes: 22 additions & 0 deletions dotnet/AspireOTelServiceDefaults/AspireOTelServiceDefaults.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireSharedProject>true</IsAspireSharedProject>
</PropertyGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />

<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="9.9.0" />
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="9.5.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.9.0" />
</ItemGroup>

</Project>
36 changes: 36 additions & 0 deletions dotnet/semantic-kernel/SemanticKernelSampleAgent.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36414.22
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SemanticKernelSampleAgent", "sample-agent\SemanticKernelSampleAgent.csproj", "{8CBB159F-2929-49A8-C300-E6F8194FB636}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspireOTelServiceDefaults", "..\AspireOTelServiceDefaults\AspireOTelServiceDefaults.csproj", "{AD051E44-BC52-C3A1-EDA8-A8CE3731A0CB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Assets", "Assets", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
ProjectSection(SolutionItems) = preProject
nuget.config = nuget.config
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8CBB159F-2929-49A8-C300-E6F8194FB636}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8CBB159F-2929-49A8-C300-E6F8194FB636}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8CBB159F-2929-49A8-C300-E6F8194FB636}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8CBB159F-2929-49A8-C300-E6F8194FB636}.Release|Any CPU.Build.0 = Release|Any CPU
{AD051E44-BC52-C3A1-EDA8-A8CE3731A0CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD051E44-BC52-C3A1-EDA8-A8CE3731A0CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD051E44-BC52-C3A1-EDA8-A8CE3731A0CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AD051E44-BC52-C3A1-EDA8-A8CE3731A0CB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4BB18351-B926-45B2-918B-D5E337BA126F}
EndGlobalSection
EndGlobal
6 changes: 6 additions & 0 deletions dotnet/semantic-kernel/nuget.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<configuration>
<packageSources>
<add key="Nuget" value="https://api.nuget.org/v3/index.json" />
<add key="LocalPackages" value="REPLACE PATH TO A365SDK PACKAGE DIRECTORY or REMOVE" />
</packageSources>
</configuration>
90 changes: 90 additions & 0 deletions dotnet/semantic-kernel/sample-agent/AgentMetrics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Diagnostics;
using System.Diagnostics.Metrics;

namespace SemanticKernelSampleAgent
{
public static class AgentMetrics
{
public static readonly string SourceName = "Agent365SemanticKernelSampleAgent";

public static readonly ActivitySource ActivitySource = new(SourceName);

private static readonly Meter Meter = new(SourceName);

public static readonly Counter<long> MessageProcessedCounter = Meter.CreateCounter<long>(
"agent.messages.processed",
"messages",
"Number of messages processed by the agent");

public static readonly Counter<long> RouteExecutedCounter = Meter.CreateCounter<long>(
"agent.routes.executed",
"routes",
"Number of routes executed by the agent");

public static readonly Histogram<double> MessageProcessingDuration = Meter.CreateHistogram<double>(
"agent.message.processing.duration",
"ms",
"Duration of message processing in milliseconds");

public static readonly Histogram<double> RouteExecutionDuration = Meter.CreateHistogram<double>(
"agent.route.execution.duration",
"ms",
"Duration of route execution in milliseconds");

public static readonly UpDownCounter<long> ActiveConversations = Meter.CreateUpDownCounter<long>(
"agent.conversations.active",
"conversations",
"Number of active conversations");


public static Activity InitializeMessageHandlingActivity(string HandlerName, ITurnContext context)
{
var activity = ActivitySource.StartActivity("AgentNotificationActivityAsync");
activity?.SetTag("conversation.id", context.Activity.Conversation?.Id);
activity?.SetTag("channel.id", context.Activity.ChannelId?.ToString());
activity?.SetTag("message.text.length", context.Activity.Text?.Length ?? 0);
activity?.SetTag("agent.isagentic", context.IsAgenticRequest());
activity?.SetTag("caller.id", context.Activity.From?.Id);

activity?.AddEvent(new ActivityEvent("message.received", DateTimeOffset.UtcNow, new()
{
["message.id"] = context.Activity.Id,
["message.text"] = context.Activity.Text,
["caller.id"] = context.Activity.From?.Id,
["agent.isagentic"] = context.IsAgenticRequest(),
["channel.id"] = context.Activity.ChannelId?.ToString()
}));
return activity!;
}

public static void FinalizeMessageHandlingActivity(Activity activity, ITurnContext context, long duration, bool success)
{
AssertionHelpers.ThrowIfNull(activity, nameof(activity));

MessageProcessingDuration.Record(duration,
new("conversation.id", context.Activity.Conversation?.Id ?? "unknown"),
new("channel.id", context.Activity.ChannelId?.ToString() ?? "unknown"));

RouteExecutedCounter.Add(1,
new("route.type", "message_handler"),
new("conversation.id", context.Activity.Conversation?.Id ?? "unknown"));

if (success)
{
activity?.SetStatus(ActivityStatusCode.Ok);
}
else
{
activity?.SetStatus(ActivityStatusCode.Error);
}
activity?.Stop();
activity?.Dispose();
}


}
}
Loading
Loading