Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
41 changes: 41 additions & 0 deletions src/Observability/Runtime/DTOs/ApplyGuardrailData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.Agents.A365.Observability.Runtime.Tracing.Scopes;
using System;
using System.Collections.Generic;

namespace Microsoft.Agents.A365.Observability.Runtime.DTOs
{
/// <summary>
/// Encapsulates all telemetry data for an apply_guardrail operation.
/// </summary>
public class ApplyGuardrailData : BaseData
{
/// <summary>
/// Initializes a new instance of the <see cref="ApplyGuardrailData"/> class.
/// </summary>
/// <param name="parentSpanId">The parent span ID for distributed tracing.</param>
/// <param name="attributes">The telemetry attributes (tags).</param>
/// <param name="startTime">Optional custom start time for the operation.</param>
/// <param name="endTime">Optional custom end time for the operation.</param>
/// <param name="spanId">Optional span ID for the operation. If not provided one will be created.</param>
/// <param name="spanKind">Optional span kind override. Defaults to <c>null</c> (unset). Use <see cref="SpanKindConstants.Internal"/> as appropriate.</param>
/// <param name="traceId">Optional trace ID for distributed tracing.</param>
public ApplyGuardrailData(
string parentSpanId,
IDictionary<string, object?>? attributes = null,
DateTimeOffset? startTime = null,
DateTimeOffset? endTime = null,
string? spanId = null,
string? spanKind = null,
string? traceId = null)
: base(attributes, startTime, endTime, spanId, parentSpanId, spanKind, traceId)
{ }

/// <summary>
/// Gets the name of the operation.
/// </summary>
public override string Name => OpenTelemetryConstants.OperationNames.ApplyGuardrail.ToString();
}
}
126 changes: 126 additions & 0 deletions src/Observability/Runtime/DTOs/Builders/ApplyGuardrailDataBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.Agents.A365.Observability.Runtime.Tracing.Contracts;
using Microsoft.Agents.A365.Observability.Runtime.Tracing.Scopes;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace Microsoft.Agents.A365.Observability.Runtime.DTOs.Builders
{
/// <summary>
/// Builds an ApplyGuardrailData instance.
/// </summary>
public class ApplyGuardrailDataBuilder : BaseDataBuilder<ApplyGuardrailData>
{
/// <summary>
/// Builds complete data for an apply_guardrail operation.
/// </summary>
/// <param name="guardrailDetails">The details of the guardrail evaluation.</param>
/// <param name="agentDetails">The details of the agent (includes tenant ID).</param>
/// <param name="conversationId">The conversation id.</param>
/// <param name="parentSpanId">The parent span ID for distributed tracing.</param>
/// <param name="startTime">Optional custom start time for the operation.</param>
/// <param name="endTime">Optional custom end time for the operation.</param>
/// <param name="spanId">Optional span ID for the operation.</param>
/// <param name="channel">Optional channel information for the operation.</param>
/// <param name="callerDetails">Optional details about the caller.</param>
/// <param name="extraAttributes">Optional dictionary of extra attributes.</param>
/// <param name="spanKind">Optional span kind override.</param>
/// <param name="traceId">Optional trace ID for distributed tracing.</param>
/// <returns>An ApplyGuardrailData object containing all telemetry data.</returns>
public static ApplyGuardrailData Build(
GuardrailDetails guardrailDetails,
AgentDetails agentDetails,
string conversationId,
string parentSpanId,
DateTimeOffset? startTime = null,
DateTimeOffset? endTime = null,
string? spanId = null,
Channel? channel = null,
CallerDetails? callerDetails = null,
IDictionary<string, object?>? extraAttributes = null,
string? spanKind = null,
string? traceId = null)
{
var attributes = BuildAttributes(guardrailDetails, agentDetails, conversationId, channel, callerDetails, extraAttributes);

return new ApplyGuardrailData(parentSpanId, attributes, startTime, endTime, spanId, spanKind, traceId);
}

private static Dictionary<string, object?> BuildAttributes(
GuardrailDetails guardrailDetails,
AgentDetails agentDetails,
string conversationId,
Channel? channel,
CallerDetails? callerDetails,
IDictionary<string, object?>? extraAttributes = null)
{
var attributes = new Dictionary<string, object?>();

// SDK attributes
AddSdkAttributes(attributes);

// Operation name
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiOperationNameKey, OpenTelemetryConstants.ApplyGuardrailOperationName);

AddAgentDetails(attributes, agentDetails);

// Guardrail details
AddGuardrailDetails(attributes, guardrailDetails);

// Conversation
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiConversationIdKey, conversationId);

// Channel
AddChannelAttributes(attributes, channel);

// Caller details
AddCallerDetails(attributes, callerDetails);

// Extra attributes
AddExtraAttributes(attributes, extraAttributes);

return attributes;
}

private static void AddGuardrailDetails(
Dictionary<string, object?> attributes,
GuardrailDetails details)
{
// Required attributes
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiSecurityDecisionTypeKey, details.DecisionType.ToString().ToLowerInvariant());
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiSecurityTargetTypeKey, details.TargetType);

// Guardian attributes
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiGuardianIdKey, details.GuardianId);
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiGuardianNameKey, details.GuardianName);
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiGuardianProviderNameKey, details.GuardianProviderName);
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiGuardianVersionKey, details.GuardianVersion);

// Target attributes
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiSecurityTargetIdKey, details.TargetId);

// Decision attributes
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiSecurityDecisionReasonKey, details.DecisionReason);
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiSecurityDecisionCodeKey, details.DecisionCode);

// Policy attributes
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiSecurityPolicyIdKey, details.PolicyId);
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiSecurityPolicyNameKey, details.PolicyName);
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiSecurityPolicyVersionKey, details.PolicyVersion);

// Content attributes
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiSecurityContentInputHashKey, details.ContentInputHash);
if (details.ContentModified.HasValue)
{
attributes[OpenTelemetryConstants.GenAiSecurityContentModifiedKey] = details.ContentModified.Value;
}

// Correlation
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiSecurityExternalEventIdKey, details.ExternalEventId);
}

}
}
10 changes: 10 additions & 0 deletions src/Observability/Runtime/DTOs/Builders/BaseDataBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@ protected static void AddChannelAttributes(IDictionary<string, object?> attribut
AddIfNotNull(attributes, OpenTelemetryConstants.ChannelLinkKey, channel.Link);
}

/// <summary>
/// Adds telemetry SDK attributes (name, version, language) to the attributes dictionary.
/// </summary>
protected static void AddSdkAttributes(IDictionary<string, object?> attributes)
{
attributes[OpenTelemetryConstants.TelemetrySdkNameKey] = OpenTelemetryConstants.TelemetrySdkNameValue;
attributes[OpenTelemetryConstants.TelemetrySdkVersionKey] = OpenTelemetryConstants.TelemetrySdkVersionValue;
attributes[OpenTelemetryConstants.TelemetrySdkLanguageKey] = OpenTelemetryConstants.TelemetrySdkLanguageValue;
}

/// <summary>
/// Adds a key-value pair to the dictionary if the value is not null.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ public static ExecuteInferenceData Build(
{
var attributes = new Dictionary<string, object?>();

// SDK attributes
AddSdkAttributes(attributes);

// Agent details (includes tenant ID)
AddAgentDetails(attributes, agentDetails);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ public static ExecuteToolData Build(
{
var attributes = new Dictionary<string, object?>();

// SDK attributes
AddSdkAttributes(attributes);

// Operation name
AddIfNotNull(attributes, GenAiOperationNameKey, ExecuteToolDataBuilder.ExecuteToolOperationName);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ public static InvokeAgentData Build(
{
var attributes = new Dictionary<string, object?>();

// SDK attributes
AddSdkAttributes(attributes);

// Operation name
AddIfNotNull(attributes, GenAiOperationNameKey, InvokeAgentDataBuilder.InvokeAgentOperationName);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ public static OutputData Build(
{
var attributes = new Dictionary<string, object?>();

// SDK attributes
AddSdkAttributes(attributes);

// Operation name
AddIfNotNull(attributes, OpenTelemetryConstants.GenAiOperationNameKey, OutputMessagesOperationName);

Expand Down
36 changes: 36 additions & 0 deletions src/Observability/Runtime/Etw/A365EtwLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public class A365EtwLogger<T> : IA365EtwLogger<T>
private static readonly EventId ExecuteToolEventId = new EventId(1003, ExecuteToolEventName);
private const string OutputMessagesEventName = "OutputMessages";
private static readonly EventId OutputMessagesEventId = new EventId(1004, OutputMessagesEventName);
private const string ApplyGuardrailEventName = "ApplyGuardrail";
private static readonly EventId ApplyGuardrailEventId = new EventId(1005, ApplyGuardrailEventName);

/// <summary>
/// Initializes a new instance of the <see cref="A365EtwLogger{T}"/> class.
Expand Down Expand Up @@ -179,6 +181,40 @@ public void LogOutput(
);
}

/// <inheritdoc/>
public void LogApplyGuardrail(
GuardrailDetails guardrailDetails,
AgentDetails agentDetails,
string conversationId,
string parentSpanId,
DateTimeOffset? startTime = null,
DateTimeOffset? endTime = null,
string? spanId = null,
Channel? channel = null,
CallerDetails? callerDetails = null,
string? traceId = null)
{
var data = ApplyGuardrailDataBuilder.Build(
guardrailDetails,
agentDetails,
conversationId,
parentSpanId,
startTime,
endTime,
spanId,
channel,
callerDetails: callerDetails,
traceId: traceId);

logger.Log(
LogLevel.Information,
ApplyGuardrailEventId,
data.ToDictionary(),
null,
LogFormatter
);
}

private static string LogFormatter(Dictionary<string, object?> data, Exception? ex)
{
return $"Name: {data["Name"]}, SpanId: {data["SpanId"]}, ParentSpanId: {data["ParentSpanId"]}, TraceId: {data["TraceId"]}";
Expand Down
25 changes: 25 additions & 0 deletions src/Observability/Runtime/Etw/IA365EtwLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,5 +120,30 @@ public void LogOutput(
string? spanId = null,
string? parentSpanId = null,
string? traceId = null);

/// <summary>
/// Logs an apply_guardrail event.
/// </summary>
/// <param name="guardrailDetails">The details of the guardrail evaluation.</param>
/// <param name="agentDetails">The details of the agent (includes tenant ID).</param>
/// <param name="conversationId">The required conversation ID.</param>
/// <param name="parentSpanId">The required parent span ID for tracing.</param>
/// <param name="startTime">Optional start time of the guardrail evaluation.</param>
/// <param name="endTime">Optional end time of the guardrail evaluation.</param>
/// <param name="spanId">Optional span ID for tracing.</param>
/// <param name="channel">Optional channel information.</param>
/// <param name="callerDetails">Optional details of the caller.</param>
/// <param name="traceId">Optional trace ID for distributed tracing.</param>
public void LogApplyGuardrail(
GuardrailDetails guardrailDetails,
AgentDetails agentDetails,
string conversationId,
string parentSpanId,
DateTimeOffset? startTime = null,
DateTimeOffset? endTime = null,
string? spanId = null,
Channel? channel = null,
CallerDetails? callerDetails = null,
string? traceId = null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Runtime.Serialization;

namespace Microsoft.Agents.A365.Observability.Runtime.Tracing.Contracts
{
/// <summary>
/// The decision made by a security guardian during guardrail evaluation.
/// </summary>
[DataContract]
public enum GuardrailDecisionType
{
/// <summary>
/// Content or action is allowed to proceed.
/// </summary>
[EnumMember(Value = "allow")]
Allow,

/// <summary>
/// Content or action is logged for review but allowed to proceed.
/// </summary>
[EnumMember(Value = "audit")]
Audit,

/// <summary>
/// Content or action is denied/blocked.
/// </summary>
[EnumMember(Value = "deny")]
Deny,

/// <summary>
/// Content was modified (e.g., redacted, sanitized, rewritten).
/// </summary>
[EnumMember(Value = "modify")]
Modify,

/// <summary>
/// Content or action triggered a warning but is allowed to proceed.
/// </summary>
[EnumMember(Value = "warn")]
Warn
}
}
Loading
Loading