Skip to content

Commit a6f3364

Browse files
authored
summary: AWS Bedrock instrumentation (#2314)
feat: Add auto-instrumentation for AWS Bedrock feat: New configuration options are available specific to [AI monitoring](https://docs.newrelic.com/docs/apm/agents/net-agent/configuration/net-agent-configuration/#ai_monitoring). feat: A new AI monitoring related public API method has been added - [SetLlmTokenCountingCallback](https://docs.newrelic.com/docs/apm/agents/net-agent/net-agent-api/set-llm-token-counting-callback/) notice: New Relic AI monitoring is the industry’s first APM solution that provides end-to-end visibility for AI Large Language Model (LLM) applications. It enables end-to-end visibility into the key components of an AI LLM application. With AI monitoring, users can monitor, alert, and debug AI-powered applications for reliability, latency, performance, security and cost. AI monitoring also enables AI/LLM specific insights (metrics, events, logs and traces) which can easily integrate to build advanced guardrails for enterprise security, privacy and compliance. notice: AI monitoring offers custom-built insights and tracing for the complete lifecycle of an LLM’s prompts and responses, from raw user input to repaired/polished responses. AI monitoring provides built-in integrations with popular LLMs and components of the AI development stack. This release provides instrumentation for AWS Bedrock. notice: When AI monitoring is enabled, the agent will now capture AI LLM related data. This data will be visible under a new APM tab called AI Responses. See our [AI Monitoring documentation](https://docs.newrelic.com/docs/ai-monitoring/intro-to-ai-monitoring/) for more details.
1 parent 453d15e commit a6f3364

File tree

72 files changed

+3566
-17
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+3566
-17
lines changed

.github/workflows/all_solutions.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ jobs:
247247
DistributedTracing,
248248
Errors,
249249
HttpClientInstrumentation,
250-
InfiniteTracing,
250+
InfiniteTracing,
251+
LLM,
251252
Logging.ContextData,
252253
Logging.HsmAndCsp,
253254
Logging.LocalDecoration,

.github/workflows/run_integration_tests.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
if [ "${{ inputs.integration-test-namespaces }}" == "ALL" ] ; then
5454
# Use the full list of namespaces
5555
namespaces="[ 'AgentFeatures', 'AgentLogs', 'AgentMetrics', 'Api', 'AppDomainCaching', 'AspNetCore', 'BasicInstrumentation', 'CatInbound', 'CatOutbound', 'CodeLevelMetrics', 'Configuration', \
56-
'CSP', 'CustomAttributes', 'CustomInstrumentation', 'DataTransmission', 'DistributedTracing', 'Errors', 'HttpClientInstrumentation', 'InfiniteTracing', 'Logging.ContextData', \
56+
'CSP', 'CustomAttributes', 'CustomInstrumentation', 'DataTransmission', 'DistributedTracing', 'Errors', 'HttpClientInstrumentation', 'InfiniteTracing', 'LLM', 'Logging.ContextData', \
5757
'Logging.HsmAndCsp', 'Logging.LocalDecoration', 'Logging.LogLevelDetection', 'Logging.MaxSamplesStored', 'Logging.MetricsAndForwarding', 'Logging.ZeroMaxSamplesStored', \
5858
'Owin', 'MassTransit', 'ReJit.NetCore', 'ReJit.NetFramework', 'RequestHandling', 'RequestHeadersCapture.AspNet', 'RequestHeadersCapture.AspNetCore', 'RequestHeadersCapture.EnvironmentVariables', \
5959
'RequestHeadersCapture.Owin', 'RequestHeadersCapture.WCF', 'RestSharp', 'WCF.Client.IIS.ASPDisabled', 'WCF.Client.IIS.ASPEnabled', 'WCF.Client.Self', \

FullAgent.sln

+8
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Home", "src\Agent\NewRelic\
178178
{87EF7419-8390-49F2-8C51-4DF9B886E834} = {87EF7419-8390-49F2-8C51-4DF9B886E834}
179179
{8D2B52DD-D45C-481D-92F0-28990F168820} = {8D2B52DD-D45C-481D-92F0-28990F168820}
180180
{9784EAEA-C32F-4DC4-BD84-4075BBA541AB} = {9784EAEA-C32F-4DC4-BD84-4075BBA541AB}
181+
{9C20BC4E-7A9F-4518-B3E7-C1FFD6C0EC8A} = {9C20BC4E-7A9F-4518-B3E7-C1FFD6C0EC8A}
181182
{9E9E1367-E09E-45F9-9A01-7D4489FBCB23} = {9E9E1367-E09E-45F9-9A01-7D4489FBCB23}
182183
{A4B357E3-9CEF-492C-A0E2-2397D15F1CD6} = {A4B357E3-9CEF-492C-A0E2-2397D15F1CD6}
183184
{AA683341-1FF5-4D45-A831-1BAF3C100A5C} = {AA683341-1FF5-4D45-A831-1BAF3C100A5C}
@@ -218,6 +219,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kafka", "src\Agent\NewRelic
218219
EndProject
219220
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore6Plus", "src\Agent\NewRelic\Agent\Extensions\Providers\Wrapper\AspNetCore6Plus\AspNetCore6Plus.csproj", "{D4F48A7F-F3D3-4303-921D-BF7FE34B7118}"
220221
EndProject
222+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bedrock", "src\Agent\NewRelic\Agent\Extensions\Providers\Wrapper\Bedrock\Bedrock.csproj", "{9C20BC4E-7A9F-4518-B3E7-C1FFD6C0EC8A}"
223+
EndProject
221224
Global
222225
GlobalSection(SolutionConfigurationPlatforms) = preSolution
223226
Debug|Any CPU = Debug|Any CPU
@@ -456,6 +459,10 @@ Global
456459
{D4F48A7F-F3D3-4303-921D-BF7FE34B7118}.Debug|Any CPU.Build.0 = Debug|Any CPU
457460
{D4F48A7F-F3D3-4303-921D-BF7FE34B7118}.Release|Any CPU.ActiveCfg = Release|Any CPU
458461
{D4F48A7F-F3D3-4303-921D-BF7FE34B7118}.Release|Any CPU.Build.0 = Release|Any CPU
462+
{9C20BC4E-7A9F-4518-B3E7-C1FFD6C0EC8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
463+
{9C20BC4E-7A9F-4518-B3E7-C1FFD6C0EC8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
464+
{9C20BC4E-7A9F-4518-B3E7-C1FFD6C0EC8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
465+
{9C20BC4E-7A9F-4518-B3E7-C1FFD6C0EC8A}.Release|Any CPU.Build.0 = Release|Any CPU
459466
EndGlobalSection
460467
GlobalSection(SolutionProperties) = preSolution
461468
HideSolutionNode = FALSE
@@ -526,6 +533,7 @@ Global
526533
{F921A316-A8D2-4992-B894-69A3B5CA34E3} = {5E86E10A-C38F-48CB-ADE9-67B22BB2F50A}
527534
{270A9CC8-8031-49F4-A380-1389E7517DB7} = {5E86E10A-C38F-48CB-ADE9-67B22BB2F50A}
528535
{D4F48A7F-F3D3-4303-921D-BF7FE34B7118} = {5E86E10A-C38F-48CB-ADE9-67B22BB2F50A}
536+
{9C20BC4E-7A9F-4518-B3E7-C1FFD6C0EC8A} = {5E86E10A-C38F-48CB-ADE9-67B22BB2F50A}
529537
EndGlobalSection
530538
GlobalSection(ExtensibilityGlobals) = postSolution
531539
EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.2\lib\NET35

build/ArtifactBuilder/CoreAgentComponents.cs

+2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ protected override void CreateAgentComponents()
5959
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.MassTransitLegacy.dll",
6060
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.Kafka.dll",
6161
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.AspNetCore6Plus.dll",
62+
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.Bedrock.dll",
6263
};
6364

6465
var wrapperXmls = new[]
@@ -82,6 +83,7 @@ protected override void CreateAgentComponents()
8283
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.MassTransitLegacy.Instrumentation.xml",
8384
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.Kafka.Instrumentation.xml",
8485
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.AspNetCore6Plus.Instrumentation.xml",
86+
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.Bedrock.Instrumentation.xml",
8587
};
8688

8789
ExtensionXsd = $@"{SourceHomeBuilderPath}\extensions\extension.xsd";

build/ArtifactBuilder/FrameworkAgentComponents.cs

+2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ protected override void CreateAgentComponents()
6767
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.MassTransit.dll",
6868
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.MassTransitLegacy.dll",
6969
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.Kafka.dll",
70+
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.Bedrock.dll",
7071
};
7172

7273
var wrapperXmls = new[]
@@ -105,6 +106,7 @@ protected override void CreateAgentComponents()
105106
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.MassTransit.Instrumentation.xml",
106107
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.MassTransitLegacy.Instrumentation.xml",
107108
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.Kafka.Instrumentation.xml",
109+
$@"{SourceHomeBuilderPath}\extensions\NewRelic.Providers.Wrapper.Bedrock.Instrumentation.xml",
108110
};
109111

110112
ExtensionXsd = $@"{SourceHomeBuilderPath}\extensions\extension.xsd";

src/Agent/MsiInstaller/Installer/Product.wxs

+12
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,9 @@ SPDX-License-Identifier: Apache-2.0
474474
<Component Id="KafkaWrapperComponent" Guid="{BE97F5F5-A392-48A0-8888-B2973EA09490}">
475475
<File Id="KafkaWrapperFile" Name="NewRelic.Providers.Wrapper.Kafka.dll" KeyPath="yes" Source="$(var.HomeFolderPath)\extensions\NewRelic.Providers.Wrapper.Kafka.dll"/>
476476
</Component>
477+
<Component Id="BedrockWrapperComponent" Guid="{C913E912-97D1-4042-8E11-A35E04C6A6E5}">
478+
<File Id="BedrockWrapperFile" Name="NewRelic.Providers.Wrapper.Bedrock.dll" KeyPath="yes" Source="$(var.HomeFolderPath)\extensions\NewRelic.Providers.Wrapper.Bedrock.dll"/>
479+
</Component>
477480

478481
<!-- Reference libraries -->
479482
<Component Id="NewRelicCoreReferenceComponent" Guid="{C196ED1E-0FBA-4D36-9C34-E969B0C9E8C9}">
@@ -545,6 +548,9 @@ SPDX-License-Identifier: Apache-2.0
545548
<Component Id="CoreAspNetCore6PlusWrapperComponent" Guid="{3B7C5E60-BA9D-4B1C-8B16-B16025B075C0}">
546549
<File Id="CoreAspNetCore6PlusWrapperFile" Name="NewRelic.Providers.Wrapper.AspNetCore6Plus.dll" KeyPath="yes" Source="$(var.HomeFolderPath)_coreclr\extensions\NewRelic.Providers.Wrapper.AspNetCore6Plus.dll"/>
547550
</Component>
551+
<Component Id="CoreBedrockWrapperComponent" Guid="{7DA522F5-38D4-4E7E-AA2B-857D01812152}">
552+
<File Id="CoreBedrockWrapperFile" Name="NewRelic.Providers.Wrapper.Bedrock.dll" KeyPath="yes" Source="$(var.HomeFolderPath)_coreclr\extensions\NewRelic.Providers.Wrapper.Bedrock.dll"/>
553+
</Component>
548554

549555
<!-- Reference libraries -->
550556
<Component Id="CoreNewRelicCoreReferenceComponent" Guid="{DD2BE979-7D4B-47EA-9FBE-F6B381D70E0B}">
@@ -659,6 +665,9 @@ SPDX-License-Identifier: Apache-2.0
659665
<Component Id="KafkaInstrumentationComponent" Guid="{9FC86A0E-CACD-4DA8-84F8-20997C904913}">
660666
<File Id="KafkaInstrumentationFile" Name="NewRelic.Providers.Wrapper.Kafka.Instrumentation.xml" KeyPath="yes" Source="$(var.HomeFolderPath)\extensions\NewRelic.Providers.Wrapper.Kafka.Instrumentation.xml"/>
661667
</Component>
668+
<Component Id="BedrockInstrumentationComponent" Guid="{D6F0A2D6-D8D5-4C61-8056-1C9AA4D07132}">
669+
<File Id="BedrockInstrumentationFile" Name="NewRelic.Providers.Wrapper.Bedrock.Instrumentation.xml" KeyPath="yes" Source="$(var.HomeFolderPath)\extensions\NewRelic.Providers.Wrapper.Bedrock.Instrumentation.xml"/>
670+
</Component>
662671
</ComponentGroup>
663672

664673
<ComponentGroup Id="CoreNewRelic.Agent.Extensions.Instrumentation" Directory="CoreExtensionsFolder">
@@ -719,6 +728,9 @@ SPDX-License-Identifier: Apache-2.0
719728
<Component Id="CoreAspNetCore6PlusInstrumentationComponent" Guid="{1CC1E672-5AA5-4F6F-A736-748EA556653A}">
720729
<File Id="CoreAspNetCore6PlusInstrumentationFile" Name="NewRelic.Providers.Wrapper.AspNetCore6Plus.Instrumentation.xml" KeyPath="yes" Source="$(var.HomeFolderPath)_coreclr\extensions\NewRelic.Providers.Wrapper.AspNetCore6Plus.Instrumentation.xml"/>
721730
</Component>
731+
<Component Id="CoreBedrockInstrumentationComponent" Guid="{CF3A17F1-3C5C-4B72-8E3B-10E53EB94913}">
732+
<File Id="CoreBedrockInstrumentationFile" Name="NewRelic.Providers.Wrapper.Bedrock.Instrumentation.xml" KeyPath="yes" Source="$(var.HomeFolderPath)_coreclr\extensions\NewRelic.Providers.Wrapper.Bedrock.Instrumentation.xml"/>
733+
</Component>
722734
</ComponentGroup>
723735

724736
<!-- Extensions XSD-->

src/Agent/NewRelic.Api.Agent/NewRelic.cs

+39
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,45 @@ public static void SetErrorGroupCallback(Func<IReadOnlyDictionary<string, object
849849
}
850850
}
851851

852+
/// <summary> Sets the method that will be invoked to define the token count of completion.
853+
///
854+
/// The callback takes the model name and input value, and returns an integer of the token count.
855+
/// A value returned from the callback that is less than or equal to 0 will be ignored.
856+
/// </summary>
857+
/// <param name="callback">The callback to invoke to generate the token count based on the model and input..</param>
858+
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
859+
public static void SetLlmTokenCountingCallback(Func<string, string, int> callback)
860+
{
861+
try
862+
{
863+
System.Diagnostics.Trace.WriteLine("NewRelic.SetLlmTokenCountingCallback()");
864+
}
865+
catch
866+
{
867+
// Swallow any exception thrown from here
868+
}
869+
}
870+
871+
/// <summary>
872+
/// Creates an event with the customer feedback on the LLM interaction.
873+
/// </summary>
874+
/// <param name="traceId">Required. ID of the trace where the chat completion(s) related to the feedback occurred</param>
875+
/// <param name="rating">Required. Rating provided by an end user. Must be string or int</param>
876+
/// <param name="category">Optional. Category of the feedback as provided by the end user</param>
877+
/// <param name="message">Optional. Freeform text feedback from an end user</param>
878+
/// <param name="metadata">Optional. Set of key-value pairs to store any other desired data to submit with the feedback event</param>
879+
public static void RecordLlmFeedbackEvent(string traceId, object rating, string category = "", string message = "", IDictionary<string, object>? metadata = null)
880+
{
881+
try
882+
{
883+
System.Diagnostics.Trace.WriteLine("NewRelic.RecordLlmFeedbackEvent()");
884+
}
885+
catch
886+
{
887+
// Swallow any exception thrown from here
888+
}
889+
}
890+
852891
#endregion
853892
}
854893

src/Agent/NewRelic/Agent/Core/Agent.cs

+62-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using NewRelic.Agent.Core.Metrics;
1414
using NewRelic.Agent.Core.Segments;
1515
using NewRelic.Agent.Core.Transactions;
16+
using NewRelic.Agent.Core.Transformers;
1617
using NewRelic.Agent.Core.Transformers.TransactionTransformer;
1718
using NewRelic.Agent.Core.Utilities;
1819
using NewRelic.Agent.Core.WireModels;
@@ -65,14 +66,17 @@ public class Agent : IAgent // any changes to api, update the interface in exten
6566
private Extensions.Logging.ILogger _logger;
6667
private readonly ISimpleSchedulingService _simpleSchedulingService;
6768

69+
private readonly ICustomEventTransformer _customEventTransformer;
70+
6871
public Agent(ITransactionService transactionService, ITransactionTransformer transactionTransformer,
6972
IThreadPoolStatic threadPoolStatic, ITransactionMetricNameMaker transactionMetricNameMaker, IPathHashMaker pathHashMaker,
7073
ICatHeaderHandler catHeaderHandler, IDistributedTracePayloadHandler distributedTracePayloadHandler,
7174
ISyntheticsHeaderHandler syntheticsHeaderHandler, ITransactionFinalizer transactionFinalizer,
7275
IBrowserMonitoringPrereqChecker browserMonitoringPrereqChecker, IBrowserMonitoringScriptMaker browserMonitoringScriptMaker,
7376
IConfigurationService configurationService, IAgentHealthReporter agentHealthReporter, IAgentTimerService agentTimerService,
7477
IMetricNameService metricNameService, Api.ITraceMetadataFactory traceMetadataFactory, ICATSupportabilityMetricCounters catMetricCounters,
75-
ILogEventAggregator logEventAggregator, ILogContextDataFilter logContextDataFilter, ISimpleSchedulingService simpleSchedulingService)
78+
ILogEventAggregator logEventAggregator, ILogContextDataFilter logContextDataFilter, ISimpleSchedulingService simpleSchedulingService,
79+
ICustomEventTransformer customEventTransformer)
7680
{
7781
_transactionService = transactionService;
7882
_transactionTransformer = transactionTransformer;
@@ -95,6 +99,8 @@ public Agent(ITransactionService transactionService, ITransactionTransformer tra
9599
_logContextDataFilter = logContextDataFilter;
96100
_simpleSchedulingService = simpleSchedulingService;
97101

102+
_customEventTransformer = customEventTransformer;
103+
98104
Instance = this;
99105
}
100106

@@ -411,6 +417,61 @@ public Dictionary<string, string> GetLinkingMetadata()
411417

412418
#region ExperimentalApi
413419

420+
public void RecordLlmEvent(string eventType, IDictionary<string, object> attributes)
421+
{
422+
if (!_configurationService.Configuration.AiMonitoringEnabled)
423+
{
424+
return;
425+
}
426+
427+
// Record metric is streaming has been disabled
428+
if (!_configurationService.Configuration.AiMonitoringStreamingEnabled)
429+
{
430+
RecordSupportabilityMetric("Supportability/DotNet/ML/Streaming/Disabled");
431+
}
432+
433+
var transaction = _transactionService.GetCurrentInternalTransaction();
434+
transaction.SetLlmTransaction(true);
435+
436+
// Any custom attributes that are prefixed with "llm." must be added to the event
437+
var transactionAttributes = transaction.TransactionMetadata.UserAndRequestAttributes.GetAllAttributeValuesDic();
438+
foreach (var attribute in transactionAttributes)
439+
{
440+
if (attribute.Key.StartsWith("llm.", StringComparison.InvariantCultureIgnoreCase))
441+
{
442+
attributes.Add(attribute.Key, attribute.Value);
443+
}
444+
}
445+
446+
// Always use callback for token count if one is provided
447+
if ((eventType == "LlmChatCompletionMessage" || eventType == "LlmEmbedding")
448+
&& _configurationService.Configuration.LlmTokenCountingCallback != null)
449+
{
450+
// message and embedding events have different attribute names for the content of the message
451+
var content = eventType == "LlmChatCompletionMessage" ? (string)attributes["content"] : (string)attributes["input"];
452+
453+
// messages only have response models, embeddings have both
454+
var model = attributes.TryGetValue("request.model", out var requestModel) ? (string)requestModel : (string)attributes["response.model"];
455+
456+
// Use a nullable so that the CustomEvent code will automatically remove the attribute if the callback returns null
457+
var tokenCount = _configurationService.Configuration.LlmTokenCountingCallback?.Invoke(model, content);
458+
if (tokenCount.HasValue && tokenCount.Value > 0)
459+
{
460+
attributes["token_count"] = tokenCount;
461+
}
462+
}
463+
464+
// If record content is disabled, we need to remove the content and input attributes
465+
// We will still want the token counts so removal occurs after the attempt to get the token count
466+
if (!_configurationService.Configuration.AiMonitoringRecordContentEnabled)
467+
{
468+
attributes.Remove("content"); // ChatMessages
469+
attributes.Remove("input"); // Embeddings
470+
}
471+
472+
_customEventTransformer.Transform(eventType, attributes, transaction.Priority);
473+
}
474+
414475
public ISimpleSchedulingService SimpleSchedulingService
415476
{
416477
get { return _simpleSchedulingService; }

src/Agent/NewRelic/Agent/Core/Api/AgentApi.cs

+34
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,40 @@ void work()
642642
}
643643
TryInvoke(work, apiName, ApiMethod.SetErrorGroupCallback);
644644
}
645+
646+
/// <summary> Sets the method that will be invoked to define the token count of completion.
647+
///
648+
/// The callback takes the model name and input value, and returns an integer of the token count.
649+
/// A value returned from the callback that is less than or equal to 0 will be ignored.
650+
/// </summary>
651+
/// <param name="callback">The callback to invoke to generate the token count based on the model and input..</param>
652+
public static void SetLlmTokenCountingCallback(Func<string, string, int> callback)
653+
{
654+
const string apiName = nameof(SetLlmTokenCountingCallback);
655+
void work()
656+
{
657+
InternalApi.SetLlmTokenCountingCallback(callback);
658+
}
659+
TryInvoke(work, apiName, ApiMethod.SetLlmTokenCountingCallback);
660+
}
661+
662+
/// <summary>
663+
/// Creates an event with the customer feedback on the LLM interaction.
664+
/// </summary>
665+
/// <param name="traceId">Required. ID of the trace where the chat completion(s) related to the feedback occurred</param>
666+
/// <param name="rating">Required. Rating provided by an end user. Must be string or int</param>
667+
/// <param name="category">Optional. Category of the feedback as provided by the end user</param>
668+
/// <param name="message">Optional. Freeform text feedback from an end user</param>
669+
/// <param name="metadata">Optional. Set of key-value pairs to store any other desired data to submit with the feedback event</param>
670+
public static void RecordLlmFeedbackEvent(string traceId, object rating, string category = "", string message = "", IDictionary<string, object>? metadata = null)
671+
{
672+
const string apiName = nameof(RecordLlmFeedbackEvent);
673+
void work()
674+
{
675+
InternalApi.RecordLlmFeedbackEvent(traceId, rating, category, message, metadata);
676+
}
677+
TryInvoke(work, apiName, ApiMethod.RecordLlmFeedbackEvent);
678+
}
645679
}
646680
}
647681

0 commit comments

Comments
 (0)