Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,18 @@ public abstract class AgentClassSkill<
private readonly Lazy<IReadOnlyList<AgentSkillResource>?> _resources;
private readonly Lazy<IReadOnlyList<AgentSkillScript>?> _scripts;
private readonly Lazy<string> _content;
private readonly Func<JsonElement?, AIFunctionArguments>? _argumentMarshaler;

/// <summary>
/// Initializes a new instance of the <see cref="AgentClassSkill{TSelf}"/> class.
/// </summary>
protected AgentClassSkill()
/// <param name="argumentMarshaler">
/// Optional argument marshaler applied to all scripts in this skill.
/// When <see langword="null"/>, the default marshaler is used which expects arguments as a JSON object.
/// </param>
protected AgentClassSkill(Func<JsonElement?, AIFunctionArguments>? argumentMarshaler = null)
{
this._argumentMarshaler = argumentMarshaler;
this._resources = new Lazy<IReadOnlyList<AgentSkillResource>?>(this.DiscoverResources);
this._scripts = new Lazy<IReadOnlyList<AgentSkillScript>?>(this.DiscoverScripts);
this._content = new Lazy<string>(() => AgentInlineSkillContentBuilder.Build(
Expand Down Expand Up @@ -240,7 +246,7 @@ protected AgentSkillResource CreateResource(string name, Delegate method, string
/// </param>
/// <returns>A new <see cref="AgentSkillScript"/> instance.</returns>
protected AgentSkillScript CreateScript(string name, Delegate method, string? description = null, JsonSerializerOptions? serializerOptions = null)
=> new AgentInlineSkillScript(name, method, description, serializerOptions ?? this.SerializerOptions);
=> new AgentInlineSkillScript(name, method, description, serializerOptions ?? this.SerializerOptions, this._argumentMarshaler);

private List<AgentSkillResource>? DiscoverResources()
{
Expand Down Expand Up @@ -356,7 +362,8 @@ private static void ValidateResourceMethodParameters(MethodInfo method, Type ski
method: method,
target: method.IsStatic ? null : this,
description: method.GetCustomAttribute<DescriptionAttribute>()?.Description,
serializerOptions: this.SerializerOptions));
serializerOptions: this.SerializerOptions,
argumentMarshaler: this._argumentMarshaler));
}

return scripts;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public sealed class AgentInlineSkill : AgentSkill
{
private readonly string _instructions;
private readonly JsonSerializerOptions? _serializerOptions;
private readonly Func<JsonElement?, AIFunctionArguments>? _argumentMarshaler;
private List<AgentInlineSkillResource>? _resources;
private List<AgentInlineSkillScript>? _scripts;
private string? _cachedContent;
Expand All @@ -45,11 +46,16 @@ public sealed class AgentInlineSkill : AgentSkill
/// added to this skill. Individual <see cref="AddScript"/> and <see cref="AddResource(string, Delegate, string?, JsonSerializerOptions?)"/>
/// calls can override this default. When <see langword="null"/>, <see cref="AIJsonUtilities.DefaultOptions"/> is used.
/// </param>
public AgentInlineSkill(AgentSkillFrontmatter frontmatter, string instructions, JsonSerializerOptions? serializerOptions = null)
/// <param name="argumentMarshaler">
/// Optional argument marshaler applied by default to all scripts added to this skill.
/// When <see langword="null"/>, the default marshaler is used which expects arguments as a JSON object.
/// </param>
public AgentInlineSkill(AgentSkillFrontmatter frontmatter, string instructions, JsonSerializerOptions? serializerOptions = null, Func<JsonElement?, AIFunctionArguments>? argumentMarshaler = null)
{
this.Frontmatter = Throw.IfNull(frontmatter);
this._instructions = Throw.IfNullOrWhitespace(instructions);
this._serializerOptions = serializerOptions;
this._argumentMarshaler = argumentMarshaler;
}

/// <summary>
Expand All @@ -68,6 +74,10 @@ public AgentInlineSkill(AgentSkillFrontmatter frontmatter, string instructions,
/// added to this skill. Individual <see cref="AddScript"/> and <see cref="AddResource(string, Delegate, string?, JsonSerializerOptions?)"/>
/// calls can override this default. When <see langword="null"/>, <see cref="AIJsonUtilities.DefaultOptions"/> is used.
/// </param>
/// <param name="argumentMarshaler">
/// Optional argument marshaler applied by default to all scripts added to this skill.
/// When <see langword="null"/>, the default marshaler is used which expects arguments as a JSON object.
/// </param>
public AgentInlineSkill(
string name,
string description,
Expand All @@ -76,7 +86,8 @@ public AgentInlineSkill(
string? compatibility = null,
string? allowedTools = null,
AdditionalPropertiesDictionary? metadata = null,
JsonSerializerOptions? serializerOptions = null)
JsonSerializerOptions? serializerOptions = null,
Func<JsonElement?, AIFunctionArguments>? argumentMarshaler = null)
: this(
new AgentSkillFrontmatter(name, description, compatibility)
{
Expand All @@ -85,7 +96,8 @@ public AgentInlineSkill(
Metadata = metadata,
},
instructions,
serializerOptions)
serializerOptions,
argumentMarshaler)
{
}
Comment thread
SergeyMenshykh marked this conversation as resolved.

Expand Down Expand Up @@ -169,7 +181,7 @@ public AgentInlineSkill AddResource(string name, Delegate method, string? descri
/// <returns>This instance, for chaining.</returns>
public AgentInlineSkill AddScript(string name, Delegate method, string? description = null, JsonSerializerOptions? serializerOptions = null)
{
(this._scripts ??= []).Add(new AgentInlineSkillScript(name, method, description, serializerOptions ?? this._serializerOptions));
(this._scripts ??= []).Add(new AgentInlineSkillScript(name, method, description, serializerOptions ?? this._serializerOptions, this._argumentMarshaler));
return this;
}
Comment thread
SergeyMenshykh marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ namespace Microsoft.Agents.AI;
internal sealed class AgentInlineSkillScript : AgentSkillScript
{
private readonly AIFunction _function;
private readonly Func<JsonElement?, AIFunctionArguments> _argumentMarshaler;

/// <summary>
/// Initializes a new instance of the <see cref="AgentInlineSkillScript"/> class from a delegate.
Expand All @@ -32,13 +33,18 @@ internal sealed class AgentInlineSkillScript : AgentSkillScript
/// Optional <see cref="JsonSerializerOptions"/> used to marshal the delegate's parameters and return value.
/// When <see langword="null"/>, <see cref="AIJsonUtilities.DefaultOptions"/> is used.
/// </param>
public AgentInlineSkillScript(string name, Delegate method, string? description = null, JsonSerializerOptions? serializerOptions = null)
/// <param name="argumentMarshaler">
/// Optional function for converting raw JSON arguments into <see cref="AIFunctionArguments"/>.
/// When <see langword="null"/>, the default marshaler is used which expects arguments as a JSON object.
/// </param>
public AgentInlineSkillScript(string name, Delegate method, string? description = null, JsonSerializerOptions? serializerOptions = null, Func<JsonElement?, AIFunctionArguments>? argumentMarshaler = null)
: base(Throw.IfNullOrWhitespace(name), description)
{
Throw.IfNull(method);

var options = new AIFunctionFactoryOptions { Name = this.Name, SerializerOptions = serializerOptions };
this._function = AIFunctionFactory.Create(method, options);
this._argumentMarshaler = argumentMarshaler ?? ConvertToFunctionArguments;
}

/// <summary>
Expand All @@ -53,13 +59,18 @@ public AgentInlineSkillScript(string name, Delegate method, string? description
/// Optional <see cref="JsonSerializerOptions"/> used to marshal the method's parameters and return value.
/// When <see langword="null"/>, <see cref="AIJsonUtilities.DefaultOptions"/> is used.
/// </param>
public AgentInlineSkillScript(string name, MethodInfo method, object? target, string? description = null, JsonSerializerOptions? serializerOptions = null)
/// <param name="argumentMarshaler">
/// Optional function for converting raw JSON arguments into <see cref="AIFunctionArguments"/>.
/// When <see langword="null"/>, the default marshaler is used which expects arguments as a JSON object.
/// </param>
public AgentInlineSkillScript(string name, MethodInfo method, object? target, string? description = null, JsonSerializerOptions? serializerOptions = null, Func<JsonElement?, AIFunctionArguments>? argumentMarshaler = null)
: base(Throw.IfNullOrWhitespace(name), description)
{
Throw.IfNull(method);

var options = new AIFunctionFactoryOptions { Name = this.Name, SerializerOptions = serializerOptions };
this._function = AIFunctionFactory.Create(method, target, options);
this._argumentMarshaler = argumentMarshaler ?? ConvertToFunctionArguments;
}

/// <summary>
Expand All @@ -70,19 +81,15 @@ public AgentInlineSkillScript(string name, MethodInfo method, object? target, st
/// <inheritdoc/>
public override async Task<object?> RunAsync(AgentSkill skill, JsonElement? arguments, IServiceProvider? serviceProvider, CancellationToken cancellationToken = default)
{
var funcArgs = ConvertToFunctionArguments(arguments);
var funcArgs = this._argumentMarshaler(arguments);
funcArgs.Services = serviceProvider;

return await this._function.InvokeAsync(funcArgs, cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Converts a raw <see cref="JsonElement"/> to <see cref="AIFunctionArguments"/> for delegate invocation.
/// Default argument marshaling: expects arguments as a JSON object whose properties map to the delegate's parameters.
/// </summary>
/// <exception cref="InvalidOperationException">
/// Thrown when <paramref name="arguments"/> is provided but is not a JSON object.
/// Inline skill scripts expect arguments as a JSON object whose properties map to the delegate's parameters.
/// </exception>
private static AIFunctionArguments ConvertToFunctionArguments(JsonElement? arguments)
{
if (arguments is null ||
Expand Down
Loading
Loading