SemanticPluginForge

Suppressing Functions and Parameters

The library supports suppressing functions and parameters in plugin metadata. This feature allows you to hide specific functions or parameters from the plugin’s consumers while maintaining functionality.

Suppressing Functions

To suppress a function, set the Suppress property to true in the FunctionMetadata.

Example: Hiding a Deprecated Function

public class CustomMetadataProvider : IPluginMetadataProvider
{
    public PluginMetadata? GetPluginMetadata(KernelPlugin plugin) => null;

    public FunctionMetadata? GetFunctionMetadata(KernelPlugin plugin, KernelFunctionMetadata metadata) =>
        plugin.Name == "SamplePlugin" && metadata.Name == "HiddenFunction"
            ? new FunctionMetadata(metadata.Name) { Suppress = true }
            : null;
}

Use Cases for Function Suppression

Suppressing Parameters

To suppress a parameter, set the Suppress property to true in the ParameterMetadata. If a default value is provided, it will be used when the function is called.

Example: Hiding a Parameter with Default Value

public class CustomMetadataProvider : IPluginMetadataProvider
{
    public PluginMetadata? GetPluginMetadata(KernelPlugin plugin) => null;

    public FunctionMetadata? GetFunctionMetadata(KernelPlugin plugin, KernelFunctionMetadata metadata) =>
        plugin.Name == "SamplePlugin" && metadata.Name == "FunctionWithHiddenParameter"
            ? new FunctionMetadata(metadata.Name)
            {
                Parameters = new List<ParameterMetadata>
                {
                    new ParameterMetadata("hiddenParam") 
                    { 
                        Suppress = true, 
                        DefaultValue = "default" 
                    }
                }
            }
            : null;
}

Important Rules for Parameter Suppression

Required Parameters Must Have Defaults

When a parameter is set to Suppress, it must either be optional or have a default value provided. This default value can be specified in the underlying implementation or through the metadata provider.

// This will throw an ArgumentException if the parameter is required and has no default
new ParameterMetadata("requiredParam") { Suppress = true } // ❌ Error!

// Correct approaches:
new ParameterMetadata("requiredParam") { Suppress = true, DefaultValue = "value" } // ✅ OK
new ParameterMetadata("optionalParam") { Suppress = true } // ✅ OK (if originally optional)

Default Value Precedence

If a default value is provided through the metadata provider, it takes precedence over the default value specified in the original plugin implementation.

// Original function signature:
public string MyFunction(string param = "original")

// Metadata override:
new ParameterMetadata("param") 
{ 
    Suppress = true, 
    DefaultValue = "overridden" // This value will be used
}

Practical Examples

Example 1: Context-Aware Parameters

Hide parameters that should be automatically resolved from context:

public class ContextAwareMetadataProvider : IPluginMetadataProvider
{
    public FunctionMetadata? GetFunctionMetadata(KernelPlugin plugin, KernelFunctionMetadata metadata)
    {
        if (plugin.Name == "WeatherPlugin" && metadata.Name == "GetWeather")
        {
            return new FunctionMetadata(metadata.Name)
            {
                Description = "Gets weather information for the user's location",
                Parameters = new List<ParameterMetadata>
                {
                    // Hide the location parameter and auto-resolve from user context
                    new ParameterMetadata("location") 
                    { 
                        Suppress = true, 
                        DefaultValue = "user_location_from_context",
                        Description = "User location automatically resolved from context"
                    }
                }
            };
        }
        return null;
    }
}

Example 2: Simplifying Complex APIs

Hide technical parameters from end users:

public class SimplifiedAPIMetadataProvider : IPluginMetadataProvider
{
    public FunctionMetadata? GetFunctionMetadata(KernelPlugin plugin, KernelFunctionMetadata metadata)
    {
        if (plugin.Name == "DatabasePlugin" && metadata.Name == "QueryData")
        {
            return new FunctionMetadata(metadata.Name)
            {
                Description = "Queries data from the database",
                Parameters = new List<ParameterMetadata>
                {
                    // Hide technical parameters
                    new ParameterMetadata("connectionString") 
                    { 
                        Suppress = true, 
                        DefaultValue = "server_default_connection"
                    },
                    new ParameterMetadata("timeout") 
                    { 
                        Suppress = true, 
                        DefaultValue = 30
                    },
                    new ParameterMetadata("retryCount") 
                    { 
                        Suppress = true, 
                        DefaultValue = 3
                    }
                    // Keep user-facing parameters visible
                }
            };
        }
        return null;
    }
}

Example 3: Environment-Specific Configuration

Use different defaults based on environment:

public class EnvironmentMetadataProvider : IPluginMetadataProvider
{
    private readonly IConfiguration _configuration;
    
    public EnvironmentMetadataProvider(IConfiguration configuration)
    {
        _configuration = configuration;
    }
    
    public FunctionMetadata? GetFunctionMetadata(KernelPlugin plugin, KernelFunctionMetadata metadata)
    {
        if (plugin.Name == "LoggingPlugin" && metadata.Name == "LogMessage")
        {
            var logLevel = _configuration.GetValue<string>("DefaultLogLevel", "Info");
            
            return new FunctionMetadata(metadata.Name)
            {
                Parameters = new List<ParameterMetadata>
                {
                    new ParameterMetadata("logLevel") 
                    { 
                        Suppress = true, 
                        DefaultValue = logLevel // Environment-specific default
                    }
                }
            };
        }
        return null;
    }
}

Testing Suppressed Elements

Testing Function Suppression

[Test]
public void SuppressedFunction_ShouldNotAppearInPlugin()
{
    var provider = new TestMetadataProvider();
    var builder = new PluginBuilder(provider);
    var originalPlugin = KernelPluginFactory.CreateFromObject(new TestPlugin());
    
    var enhancedPlugin = builder.PatchKernelPluginWithMetadata(originalPlugin);
    
    // Function should be suppressed
    Assert.False(enhancedPlugin.Any(f => f.Name == "SuppressedFunction"));
}

Testing Parameter Suppression

[Test]
public async Task SuppressedParameter_ShouldUseDefaultValue()
{
    var provider = new TestMetadataProvider();
    var builder = new PluginBuilder(provider);
    var originalPlugin = KernelPluginFactory.CreateFromObject(new TestPlugin());
    
    var enhancedPlugin = builder.PatchKernelPluginWithMetadata(originalPlugin);
    var function = enhancedPlugin["TestFunction"];
    
    // Call function without providing the suppressed parameter
    var result = await function.InvokeAsync(new Kernel(), new KernelArguments());
    
    // Should use the default value from metadata provider
    Assert.Equal("expected_default_value", result.GetValue<string>());
}

Best Practices

1. Document Suppressed Elements

Always document why elements are suppressed:

public FunctionMetadata? GetFunctionMetadata(KernelPlugin plugin, KernelFunctionMetadata metadata)
{
    if (plugin.Name == "AdminPlugin" && metadata.Name == "DeleteAllData")
    {
        // Suppress dangerous admin function in production for safety
        return new FunctionMetadata(metadata.Name) { Suppress = true };
    }
    return null;
}

2. Use Meaningful Default Values

Choose default values that make sense in context:

new ParameterMetadata("userId") 
{ 
    Suppress = true, 
    DefaultValue = "current_authenticated_user", // Clear intent
    Description = "Automatically resolved from current user session"
}

3. Consider Gradual Migration

Use suppression for gradual API changes:

public FunctionMetadata? GetFunctionMetadata(KernelPlugin plugin, KernelFunctionMetadata metadata)
{
    // Phase 1: Keep old parameter but suppress it
    if (plugin.Name == "APIv2" && metadata.Name == "ProcessData")
    {
        return new FunctionMetadata(metadata.Name)
        {
            Parameters = new List<ParameterMetadata>
            {
                // Gradually migrate from old parameter to new one
                new ParameterMetadata("oldFormat") 
                { 
                    Suppress = true, 
                    DefaultValue = false // Use new format by default
                }
            }
        };
    }
    return null;
}