Skip to content

Commit 75d49c6

Browse files
Copilotkzu
andcommitted
Surface AdditionalProperties in ChatClientAgentOptions, ChatClientAgent, and AIAgentMetadata
Co-authored-by: kzu <[email protected]>
1 parent 753a8d9 commit 75d49c6

File tree

5 files changed

+165
-2
lines changed

5 files changed

+165
-2
lines changed

dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgentMetadata.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

33
using System.Diagnostics;
4+
using Microsoft.Extensions.AI;
45

56
namespace Microsoft.Agents.AI;
67

@@ -21,9 +22,13 @@ public class AIAgentMetadata
2122
/// The name of the agent provider, if applicable. Where possible, this should map to the
2223
/// appropriate name defined in the OpenTelemetry Semantic Conventions for Generative AI systems.
2324
/// </param>
24-
public AIAgentMetadata(string? providerName = null)
25+
/// <param name="additionalProperties">
26+
/// Additional properties associated with the agent metadata.
27+
/// </param>
28+
public AIAgentMetadata(string? providerName = null, AdditionalPropertiesDictionary? additionalProperties = null)
2529
{
2630
this.ProviderName = providerName;
31+
this.AdditionalProperties = additionalProperties;
2732
}
2833

2934
/// <summary>
@@ -37,4 +42,17 @@ public AIAgentMetadata(string? providerName = null)
3742
/// OpenTelemetry Semantic Conventions for Generative AI systems.
3843
/// </remarks>
3944
public string? ProviderName { get; }
45+
46+
/// <summary>
47+
/// Gets additional properties associated with the agent metadata.
48+
/// </summary>
49+
/// <value>
50+
/// An <see cref="AdditionalPropertiesDictionary"/> containing custom properties,
51+
/// or <see langword="null"/> if no additional properties are present.
52+
/// </value>
53+
/// <remarks>
54+
/// Additional properties provide a way to include custom metadata or agent-specific
55+
/// information that doesn't fit into the standard agent schema.
56+
/// </remarks>
57+
public AdditionalPropertiesDictionary? AdditionalProperties { get; }
4058
}

dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public ChatClientAgent(IChatClient chatClient, ChatClientAgentOptions? options,
9797
// Options must be cloned since ChatClientAgentOptions is mutable.
9898
this._agentOptions = options?.Clone();
9999

100-
this._agentMetadata = new AIAgentMetadata(chatClient.GetService<ChatClientMetadata>()?.ProviderName);
100+
this._agentMetadata = new AIAgentMetadata(chatClient.GetService<ChatClientMetadata>()?.ProviderName, this._agentOptions?.AdditionalProperties);
101101

102102
// Get the type of the chat client before wrapping it as an agent invoking chat client.
103103
this._chatClientType = chatClient.GetType();
@@ -129,6 +129,9 @@ public ChatClientAgent(IChatClient chatClient, ChatClientAgentOptions? options,
129129
/// <inheritdoc/>
130130
public override string? Description => this._agentOptions?.Description;
131131

132+
/// <inheritdoc/>
133+
public override AdditionalPropertiesDictionary? AdditionalProperties => this._agentOptions?.AdditionalProperties;
134+
132135
/// <summary>
133136
/// Gets the system instructions that guide the agent's behavior during conversations.
134137
/// </summary>

dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentOptions.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,19 @@ public ChatClientAgentOptions(string? instructions, string? name = null, string?
9898
/// </remarks>
9999
public bool UseProvidedChatClientAsIs { get; set; }
100100

101+
/// <summary>
102+
/// Gets or sets additional properties associated with the agent options.
103+
/// </summary>
104+
/// <value>
105+
/// An <see cref="AdditionalPropertiesDictionary"/> containing custom properties,
106+
/// or <see langword="null"/> if no additional properties are present.
107+
/// </value>
108+
/// <remarks>
109+
/// Additional properties provide a way to include custom metadata or agent-specific
110+
/// information that doesn't fit into the standard agent schema.
111+
/// </remarks>
112+
public AdditionalPropertiesDictionary? AdditionalProperties { get; set; }
113+
101114
/// <summary>
102115
/// Creates a new instance of <see cref="ChatClientAgentOptions"/> with the same values as this instance.
103116
/// </summary>
@@ -111,6 +124,7 @@ public ChatClientAgentOptions Clone()
111124
ChatOptions = this.ChatOptions?.Clone(),
112125
ChatMessageStoreFactory = this.ChatMessageStoreFactory,
113126
AIContextProviderFactory = this.AIContextProviderFactory,
127+
AdditionalProperties = this.AdditionalProperties?.Clone(),
114128
};
115129

116130
/// <summary>

dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentOptionsTests.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,61 @@ public void Clone_WithNullChatOptions_ClonesCorrectly()
221221
Assert.Null(clone.ChatMessageStoreFactory);
222222
Assert.Null(clone.AIContextProviderFactory);
223223
}
224+
225+
[Fact]
226+
public void AdditionalProperties_DefaultValue_ReturnsNull()
227+
{
228+
// Arrange
229+
var options = new ChatClientAgentOptions();
230+
231+
// Act
232+
var result = options.AdditionalProperties;
233+
234+
// Assert
235+
Assert.Null(result);
236+
}
237+
238+
[Fact]
239+
public void AdditionalProperties_CanBeSet_ReturnsSetValue()
240+
{
241+
// Arrange
242+
var options = new ChatClientAgentOptions();
243+
var additionalProperties = new AdditionalPropertiesDictionary
244+
{
245+
["beta"] = true,
246+
["icon"] = "agent-icon.png"
247+
};
248+
249+
// Act
250+
options.AdditionalProperties = additionalProperties;
251+
252+
// Assert
253+
Assert.Same(additionalProperties, options.AdditionalProperties);
254+
}
255+
256+
[Fact]
257+
public void Clone_WithAdditionalProperties_ClonesAdditionalProperties()
258+
{
259+
// Arrange
260+
var original = new ChatClientAgentOptions
261+
{
262+
Id = "test-id",
263+
Name = "Test name",
264+
AdditionalProperties = new AdditionalPropertiesDictionary
265+
{
266+
["beta"] = true,
267+
["icon"] = "agent-icon.png"
268+
}
269+
};
270+
271+
// Act
272+
var clone = original.Clone();
273+
274+
// Assert
275+
Assert.NotSame(original, clone);
276+
Assert.NotSame(original.AdditionalProperties, clone.AdditionalProperties);
277+
Assert.Equal(original.AdditionalProperties?.Count, clone.AdditionalProperties?.Count);
278+
Assert.Equal(true, clone.AdditionalProperties?["beta"]);
279+
Assert.Equal("agent-icon.png", clone.AdditionalProperties?["icon"]);
280+
}
224281
}

dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2598,6 +2598,77 @@ await Assert.ThrowsAsync<InvalidOperationException>(async () =>
25982598

25992599
#endregion
26002600

2601+
#region AdditionalProperties Tests
2602+
2603+
[Fact]
2604+
public void AdditionalProperties_DefaultValue_ReturnsNull()
2605+
{
2606+
// Arrange
2607+
var chatClient = new Mock<IChatClient>().Object;
2608+
var agent = new ChatClientAgent(chatClient);
2609+
2610+
// Act
2611+
var result = agent.AdditionalProperties;
2612+
2613+
// Assert
2614+
Assert.Null(result);
2615+
}
2616+
2617+
[Fact]
2618+
public void AdditionalProperties_FromOptions_ReturnsOptionsValue()
2619+
{
2620+
// Arrange
2621+
var chatClient = new Mock<IChatClient>().Object;
2622+
var additionalProperties = new AdditionalPropertiesDictionary
2623+
{
2624+
["beta"] = true,
2625+
["icon"] = "agent-icon.png",
2626+
["requiresAuth"] = true
2627+
};
2628+
var options = new ChatClientAgentOptions
2629+
{
2630+
AdditionalProperties = additionalProperties
2631+
};
2632+
var agent = new ChatClientAgent(chatClient, options);
2633+
2634+
// Act
2635+
var result = agent.AdditionalProperties;
2636+
2637+
// Assert
2638+
Assert.NotNull(result);
2639+
Assert.Equal(3, result.Count);
2640+
Assert.Equal(true, result["beta"]);
2641+
Assert.Equal("agent-icon.png", result["icon"]);
2642+
Assert.Equal(true, result["requiresAuth"]);
2643+
}
2644+
2645+
[Fact]
2646+
public void GetService_AIAgentMetadata_ReturnsMetadataWithAdditionalProperties()
2647+
{
2648+
// Arrange
2649+
var chatClient = new Mock<IChatClient>().Object;
2650+
var additionalProperties = new AdditionalPropertiesDictionary
2651+
{
2652+
["beta"] = true
2653+
};
2654+
var options = new ChatClientAgentOptions
2655+
{
2656+
AdditionalProperties = additionalProperties
2657+
};
2658+
var agent = new ChatClientAgent(chatClient, options);
2659+
2660+
// Act
2661+
var metadata = agent.GetService<AIAgentMetadata>();
2662+
2663+
// Assert
2664+
Assert.NotNull(metadata);
2665+
Assert.NotNull(metadata.AdditionalProperties);
2666+
Assert.Single(metadata.AdditionalProperties);
2667+
Assert.Equal(true, metadata.AdditionalProperties["beta"]);
2668+
}
2669+
2670+
#endregion
2671+
26012672
private static async IAsyncEnumerable<T> ToAsyncEnumerableAsync<T>(IEnumerable<T> values)
26022673
{
26032674
await Task.Yield();

0 commit comments

Comments
 (0)