diff --git a/src/Agents/AgentExtensions.cs b/src/Agents/AgentExtensions.cs
index d7acf9a..997dc59 100644
--- a/src/Agents/AgentExtensions.cs
+++ b/src/Agents/AgentExtensions.cs
@@ -23,4 +23,14 @@ public static ChatResponse AsChatResponse(this AgentRunResponse response)
return chatResponse;
}
+
+ extension(AIAgent agent)
+ {
+ /// Gets the emoji associated with the agent, if any.
+ public string? Emoji => agent is not IHasAdditionalProperties additional
+ ? null
+ : additional.AdditionalProperties is null
+ ? null
+ : additional.AdditionalProperties.TryGetValue("Emoji", out var value) ? value as string : null;
+ }
}
\ No newline at end of file
diff --git a/src/Agents/ConfigurableAIAgent.cs b/src/Agents/ConfigurableAIAgent.cs
index 9f3a92e..0a69189 100644
--- a/src/Agents/ConfigurableAIAgent.cs
+++ b/src/Agents/ConfigurableAIAgent.cs
@@ -15,7 +15,7 @@ namespace Devlooped.Agents.AI;
/// A configuration-driven which monitors configuration changes and
/// re-applies them to the inner agent automatically.
///
-public sealed partial class ConfigurableAIAgent : AIAgent, IDisposable
+public sealed partial class ConfigurableAIAgent : AIAgent, IHasAdditionalProperties, IDisposable
{
readonly IServiceProvider services;
readonly IConfiguration configuration;
@@ -57,6 +57,9 @@ Type t when typeof(AIAgentMetadata).IsAssignableFrom(t) => metadata,
_ => agent.GetService(serviceType, serviceKey)
};
+ ///
+ public AdditionalPropertiesDictionary? AdditionalProperties { get; set; }
+
///
public override string Id => agent.Id;
///
@@ -89,6 +92,20 @@ public override IAsyncEnumerable RunStreamingAsync(IEnum
options?.Description = options?.Description?.Dedent();
options?.Instructions = options?.Instructions?.Dedent();
+ var properties = configSection.Get();
+ if (properties is not null)
+ {
+ properties?.Remove(nameof(AgentClientOptions.Name));
+ properties?.Remove(nameof(AgentClientOptions.Description));
+ properties?.Remove(nameof(AgentClientOptions.Instructions));
+ properties?.Remove(nameof(AgentClientOptions.Client));
+ properties?.Remove(nameof(AgentClientOptions.Model));
+ properties?.Remove(nameof(AgentClientOptions.Use));
+ properties?.Remove(nameof(AgentClientOptions.Tools));
+
+ AdditionalProperties = properties;
+ }
+
// If there was a custom id, we must validate it didn't change since that's not supported.
if (configuration[$"{section}:name"] is { } newname && newname != name)
throw new InvalidOperationException($"The name of a configured agent cannot be changed at runtime. Expected '{name}' but was '{newname}'.");
diff --git a/src/Agents/IHasAdditionalProperties.cs b/src/Agents/IHasAdditionalProperties.cs
new file mode 100644
index 0000000..4b30884
--- /dev/null
+++ b/src/Agents/IHasAdditionalProperties.cs
@@ -0,0 +1,10 @@
+using Microsoft.Extensions.AI;
+
+namespace Devlooped.Agents.AI;
+
+/// Indicates that the instance can have additional properties associated with it.
+public interface IHasAdditionalProperties
+{
+ /// Gets or sets any additional properties associated with the instance.
+ AdditionalPropertiesDictionary? AdditionalProperties { get; set; }
+}
diff --git a/src/Tests/ConfigurableAgentTests.cs b/src/Tests/ConfigurableAgentTests.cs
index 42e62e4..ae31366 100644
--- a/src/Tests/ConfigurableAgentTests.cs
+++ b/src/Tests/ConfigurableAgentTests.cs
@@ -25,6 +25,7 @@ public void CanConfigureAgent()
["ai:agents:bot:description"] = "Helpful chat agent",
["ai:agents:bot:instructions"] = "You are a helpful chat agent.",
["ai:agents:bot:options:temperature"] = "0.5",
+ ["ai:agents:bot:emoji"] = "🤖",
});
builder.AddAIAgents();
@@ -36,6 +37,10 @@ public void CanConfigureAgent()
Assert.Equal("chat", agent.Name);
Assert.Equal("chat", agent.DisplayName);
Assert.Equal("Helpful chat agent", agent.Description);
+
+ var additional = Assert.IsType(agent, exactMatch: false);
+ Assert.Equal("🤖", additional.AdditionalProperties?["emoji"]?.ToString());
+ Assert.Equal("🤖", agent.Emoji);
}
[Fact]