Files
agent-framework/dotnet/src/Microsoft.Agents.AI/Skills/Programmatic/AgentInlineSkill.cs
T
bb9ed63a34 .NET: Restructure skill script schemas XML and remove resources from body (#6343)
* Restore UTF-8 BOMs and fix BuildScriptSchemasBlock doc comment

- Restore UTF-8 BOM on all changed files to match repo convention
- Fix XML doc: <schema name=...> -> <schema script=...> to match emitted output

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR review comments: fix doc remarks and rename tests

- Update script doc remarks to clarify only parameter schemas are included
- Fix grammar: 'arguments format' -> 'argument format'
- Rename misleading test methods to match actual assertions
- Clarify comment about removed wrapper element

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 21:15:29 +01:00

176 lines
8.6 KiB
C#

// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI;
/// <summary>
/// A skill defined entirely in code with resources (static values or delegates) and scripts (delegates).
/// </summary>
/// <remarks>
/// All calls to <see cref="AddResource(string, object, string?)"/>,
/// <see cref="AddResource(string, Delegate, string?, JsonSerializerOptions?)"/>, and <see cref="AddScript"/>
/// must be made before the skill's <see cref="GetContentAsync"/> is first called.
/// Calls made after that point will not be reflected in the generated
/// content. In typical usage, this means configuring all
/// resources and scripts before registering the skill with an
/// <see cref="AgentSkillsProvider"/> or <see cref="AgentSkillsProviderBuilder"/>.
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public sealed class AgentInlineSkill : AgentSkill
{
private readonly string _instructions;
private readonly JsonSerializerOptions? _serializerOptions;
private List<AgentInlineSkillResource>? _resources;
private List<AgentInlineSkillScript>? _scripts;
private string? _cachedContent;
/// <summary>
/// Initializes a new instance of the <see cref="AgentInlineSkill"/> class
/// with a pre-built <see cref="AgentSkillFrontmatter"/>.
/// </summary>
/// <param name="frontmatter">The skill frontmatter containing name, description, and other metadata.</param>
/// <param name="instructions">Skill instructions text.</param>
/// <param name="serializerOptions">
/// Optional <see cref="JsonSerializerOptions"/> applied by default to all scripts and delegate resources
/// 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)
{
this.Frontmatter = Throw.IfNull(frontmatter);
this._instructions = Throw.IfNullOrWhitespace(instructions);
this._serializerOptions = serializerOptions;
}
/// <summary>
/// Initializes a new instance of the <see cref="AgentInlineSkill"/> class
/// with all frontmatter properties specified individually.
/// </summary>
/// <param name="name">Skill name in kebab-case.</param>
/// <param name="description">Skill description for discovery.</param>
/// <param name="instructions">Skill instructions text.</param>
/// <param name="license">Optional license name or reference.</param>
/// <param name="compatibility">Optional compatibility information (max 500 chars).</param>
/// <param name="allowedTools">Optional space-delimited list of pre-approved tools.</param>
/// <param name="metadata">Optional arbitrary key-value metadata.</param>
/// <param name="serializerOptions">
/// Optional <see cref="JsonSerializerOptions"/> applied by default to all scripts and delegate resources
/// 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(
string name,
string description,
string instructions,
string? license = null,
string? compatibility = null,
string? allowedTools = null,
AdditionalPropertiesDictionary? metadata = null,
JsonSerializerOptions? serializerOptions = null)
: this(
new AgentSkillFrontmatter(name, description, compatibility)
{
License = license,
AllowedTools = allowedTools,
Metadata = metadata,
},
instructions,
serializerOptions)
{
}
/// <inheritdoc/>
public override AgentSkillFrontmatter Frontmatter { get; }
/// <inheritdoc/>
public override ValueTask<string> GetContentAsync(CancellationToken cancellationToken = default)
{
return new(this._cachedContent ??= AgentInlineSkillContentBuilder.Build(this.Frontmatter.Name, this.Frontmatter.Description, this._instructions, this._scripts));
}
/// <inheritdoc/>
public override ValueTask<AgentSkillResource?> GetResourceAsync(string name, CancellationToken cancellationToken = default)
{
var resource = this._resources?.FirstOrDefault(r => r.Name == name);
return new(resource);
}
/// <inheritdoc/>
public override ValueTask<AgentSkillScript?> GetScriptAsync(string name, CancellationToken cancellationToken = default)
{
var script = this._scripts?.FirstOrDefault(s => s.Name == name);
return new(script);
}
/// <summary>
/// Registers a static resource with this skill.
/// </summary>
/// <remarks>
/// Resources are not automatically included in the skill body.
/// To enable discovery, reference the resource by name in the skill's instructions or in another resource.
/// </remarks>
/// <param name="name">The resource name.</param>
/// <param name="value">The static resource value.</param>
/// <param name="description">An optional description of the resource.</param>
/// <returns>This instance, for chaining.</returns>
public AgentInlineSkill AddResource(string name, object value, string? description = null)
{
(this._resources ??= []).Add(new AgentInlineSkillResource(name, value, description));
return this;
}
/// <summary>
/// Registers a dynamic resource with this skill, backed by a C# delegate.
/// The delegate's parameters and return type are automatically marshaled via <c>AIFunctionFactory</c>.
/// </summary>
/// <remarks>
/// Resources are not automatically included in the skill body.
/// To enable discovery, reference the resource by name in the skill's instructions or in another resource.
/// </remarks>
/// <param name="name">The resource name.</param>
/// <param name="method">A method that produces the resource value when requested.</param>
/// <param name="description">An optional description of the resource.</param>
/// <param name="serializerOptions">
/// Optional <see cref="JsonSerializerOptions"/> for this resource's delegate marshaling.
/// When <see langword="null"/>, the skill-level default (if any) is used; otherwise <see cref="AIJsonUtilities.DefaultOptions"/> is used.
/// </param>
/// <returns>This instance, for chaining.</returns>
public AgentInlineSkill AddResource(string name, Delegate method, string? description = null, JsonSerializerOptions? serializerOptions = null)
{
(this._resources ??= []).Add(new AgentInlineSkillResource(name, method, description, serializerOptions ?? this._serializerOptions));
return this;
}
/// <summary>
/// Registers a script with this skill, backed by a C# delegate.
/// The delegate's parameters and return type are automatically marshaled via <c>AIFunctionFactory</c>.
/// </summary>
/// <remarks>
/// Only the script's parameter schema is included in the skill body (as a <c>&lt;script_schemas&gt;</c> block).
/// To enable discovery, reference the script by name in the skill's instructions or in a resource.
/// </remarks>
/// <param name="name">The script name.</param>
/// <param name="method">A method to execute when the script is invoked.</param>
/// <param name="description">An optional description of the script.</param>
/// <param name="serializerOptions">
/// Optional <see cref="JsonSerializerOptions"/> for this script's delegate marshaling.
/// When <see langword="null"/>, the skill-level default (if any) is used; otherwise <see cref="AIJsonUtilities.DefaultOptions"/> is used.
/// </param>
/// <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));
return this;
}
}