/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ using Microsoft.Extensions.AI; namespace GitHub.Copilot; /// /// Provides helpers for defining Copilot tools. /// public static class CopilotTool { /// The key used in to indicate that a tool intentionally overrides a built-in Copilot tool with the same name. internal const string OverridesBuiltInToolKey = "is_override"; /// The key used in to indicate that a tool can execute without a permission prompt. internal const string SkipPermissionKey = "skip_permission"; /// The key used in to carry the tool's deferral mode. internal const string DeferKey = "defer"; /// /// Defines a tool for use in a . /// /// The delegate to invoke when the tool is called. /// The Microsoft.Extensions.AI options used to create the function. /// Copilot-specific tool options. /// An that can be added to . /// /// This is a helper on top of that applies additional configuration to support /// Copilot tools, such as binding a parameter and adding Copilot-specific metadata properties based on the provided /// . Any may be used as a Copilot tool; this helper simply provides additional conveniences /// for tools that opt in to advanced features. /// public static AIFunction DefineTool( Delegate method, CopilotToolOptions? toolOptions = null, AIFunctionFactoryOptions? factoryOptions = null) { ArgumentNullException.ThrowIfNull(method); factoryOptions ??= new(); ApplyToolOptions(factoryOptions, toolOptions); ApplyToolInvocationBinding(factoryOptions); return AIFunctionFactory.Create(method, factoryOptions); static void ApplyToolInvocationBinding(AIFunctionFactoryOptions factoryOptions) { var configureParameterBinding = factoryOptions.ConfigureParameterBinding; factoryOptions.ConfigureParameterBinding = pi => { var bindingOptions = configureParameterBinding?.Invoke(pi) ?? default; if (bindingOptions.BindParameter is null && !bindingOptions.ExcludeFromSchema && pi.ParameterType == typeof(ToolInvocation)) { return new AIFunctionFactoryOptions.ParameterBindingOptions { ExcludeFromSchema = true, BindParameter = static (pi, arguments) => { // CopilotClient/CopilotSession attach this context object before invoking the AIFunction. if (arguments.Context is not null && arguments.Context.TryGetValue(typeof(ToolInvocation), out var invocation) && invocation is ToolInvocation toolInvocation) { return toolInvocation; } if (pi.HasDefaultValue) { return null; } throw new InvalidOperationException($"No {nameof(ToolInvocation)} was provided for the tool call."); } }; } return bindingOptions; }; } static void ApplyToolOptions(AIFunctionFactoryOptions factoryOptions, CopilotToolOptions? toolOptions) { if (toolOptions is not null && (toolOptions.OverridesBuiltInTool || toolOptions.SkipPermission || toolOptions.Defer is not null)) { Dictionary additionalProperties = new(StringComparer.Ordinal); if (factoryOptions.AdditionalProperties is not null) { foreach (var (key, value) in factoryOptions.AdditionalProperties) { additionalProperties[key] = value; } } if (toolOptions.OverridesBuiltInTool) { additionalProperties[OverridesBuiltInToolKey] = true; } if (toolOptions.SkipPermission) { additionalProperties[SkipPermissionKey] = true; } if (toolOptions.Defer is { } defer) { additionalProperties[DeferKey] = defer; } factoryOptions.AdditionalProperties = additionalProperties; } } } } /// /// Copilot-specific options for tools defined with . /// public sealed class CopilotToolOptions { /// /// Gets or sets a value indicating whether this tool intentionally overrides a built-in Copilot tool with the same name. /// /// /// When a with set to true is used to define a tool, /// the resulting will include "is_override": true in its . /// public bool OverridesBuiltInTool { get; set; } /// /// Gets or sets a value indicating whether this tool can execute without a permission prompt. /// /// /// When a with set to true is used to define a tool, /// the resulting will include "skip_permission": true in its . /// public bool SkipPermission { get; set; } /// /// Gets or sets a value controlling whether this tool may be deferred (loaded lazily via tool search) rather than always pre-loaded. /// /// /// When set, the resulting carries the value in its and the /// SDK forwards it to the CLI as the tool's defer mode. Defaults to "auto". /// public CopilotToolDefer? Defer { get; set; } } /// /// Controls whether a tool may be deferred (loaded lazily via tool search) rather than always pre-loaded. /// [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))] public enum CopilotToolDefer { /// The tool can be deferred and surfaced through tool search. [System.Text.Json.Serialization.JsonStringEnumMemberName("auto")] Auto, /// The tool is always pre-loaded. [System.Text.Json.Serialization.JsonStringEnumMemberName("never")] Never }