Functions/GenXdev.AI.LMStudio/ConvertTo-LMStudioFunctionDefinition.ps1

################################################################################
<#
.SYNOPSIS
Converts PowerShell functions to LMStudio function definitions.
 
.DESCRIPTION
Takes PowerShell functions and generates LMStudio compatible function definitions
including parameter information and callback handlers.
 
.PARAMETER Functions
One or more PowerShell function info objects to convert to LMStudio definitions.
 
.EXAMPLE
Get-Command Get-Process | ConvertTo-LMStudioFunctionDefinition
#>

function ConvertTo-LMStudioFunctionDefinition {

    [CmdletBinding()]
    [OutputType([System.Collections.Generic.List[hashtable]])]
    param(
        ########################################################################
        [Parameter(
            Mandatory = $false,
            Position = 0,
            ValueFromPipeline = $true,
            HelpMessage = "PowerShell commands to convert to tool functions"
        )]
        [GenXdev.Helpers.ExposedCmdletDefinition[]] $ExposedCmdLets = @()
    )

    begin {

        # create result collection to store function definitions
        [System.Collections.Generic.List[hashtable]] $result = New-Object "System.Collections.Generic.List[System.Collections.Hashtable]"

        Write-Verbose "Starting conversion of PowerShell functions to LMStudio format"
    }

    process {

        if ($ExposedCmdLets) {

            foreach ($currentCommand in $ExposedCmdLets) {

                $commandInfo = Get-Command -Name ($currentCommand.Name) -ErrorAction SilentlyContinue | Select-Object -First 1

                if ($null -eq $commandInfo) {

                    Write-Warning "Command $($currentCommand.Name) not found. Skipping."
                    continue
                }

                $allowedParams = @($currentCommand.AllowedParams);

                Write-Verbose "Processing command: $($currentCommand.Name)"

                # Handle both function and cmdlet types
                $callback = $commandInfo

                # initialize collections for parameter processing
                [System.Collections.Generic.List[string]]$requiredParams = @()
                $propertiesTable = @{}

                @($commandInfo.Parameters.GetEnumerator()).Value | ForEach-Object {
                    $parameter = $_

                    [System.Management.Automation.ParameterMetadata]$parameter = $_

                    $found = $false
                    $typeStr = ""
                    foreach ($allowedParam in $allowedParams) {

                        $parts = "$allowedParam".Split("=");
                        $name = $parts[0].Trim()

                        if ($parameter.Name -like $name) {

                            $found = $true
                            if ($parts.Length -gt 1) {

                                $typeStr = $parts[1].Trim()
                            }
                            break
                        }
                    }

                    if (-not $found) {

                        return
                    }

                    $returnType = "";
                    $powershell_returnType = "";

                    # track required parameters
                    $parameter.Attributes | ForEach-Object {
                        if ($_.TypeId -eq "System.Management.Automation.ParameterAttribute") {

                            if ($_.Mandatory) {

                                $null = $requiredParams.Add($parameter.Name)
                            }
                        }
                        if ($_.TypeId -eq "System.Management.Automation.OutputTypeAttribute") {

                            [System.Management.Automation.OutputTypeAttribute] $p = $_

                            $powershell_returnType = $p.Type
                            $returnType = Convert-DotNetTypeToLLMType -DotNetType $powershell_returnType.FullName
                        }
                    }

                    # build parameter properties
                    $helpMessage = $null;
                    if ([string]::IsNullOrWhiteSpace($helpMessage)) {

                        try {
                            $moduleName = $commandInfo.ModuleName
                            if ($moduleName -like "GenXdev.*") {

                                $moduleName = (($commandInfo.ModuleName.Split(".") | Select-Object -First 2) -Join ".")
                            }

                            if ([string]::IsNullOrWhiteSpace($moduleName)) {

                                $moduleName = ""
                            }
                            else {

                                $moduleName = "$moduleName\"
                            }

                            $help = (Get-Help -Name "$ModuleName$($commandInfo.Name)" -Parameter "$($parameter.name)")
                            $paramHelp = $help ? "$(($help.description | Out-String).trim())" : $null
                            if (-not [string]::IsNullOrWhiteSpace($paramHelp)) {

                                $helpMessage = $paramHelp
                            }
                        }
                        catch {

                            $helpMessage = $null
                            Write-Verbose "Could not get help message for parameter $($parameter.Name)"
                        }
                    }

                    if ([string]::IsNullOrWhiteSpace($helpMessage )) {

                        $propertiesTable."$($parameter.Name)" = @{

                            type            = [string]::IsNullOrWhiteSpace($typeStr) ? (Convert-DotNetTypeToLLMType -DotNetType $parameter.ParameterType.FullName) : $typeStr
                            powershell_type = $parameter.ParameterType.FullName
                        }
                    }
                    else {
                        $propertiesTable."$($parameter.Name)" = @{

                            type            = [string]::IsNullOrWhiteSpace($typeStr) ? (Convert-DotNetTypeToLLMType -DotNetType $parameter.ParameterType.FullName) : $typeStr
                            powershell_type = $parameter.ParameterType.FullName
                            description     = $helpMessage
                        }
                    }
                }

                # get command help message
                $functionHelpMessage = $commandInfo.Description

                $moduleName = $commandInfo.ModuleName

                if ($moduleName -like "GenXdev.*") {

                    $moduleName = (($commandInfo.ModuleName.Split(".") | Select-Object -First 2) -Join ".")
                }

                if ([string]::IsNullOrWhiteSpace($moduleName)) {

                    $moduleName = ""
                }
                else {
                    $moduleName = "$moduleName\"
                }
                if ([string]::IsNullOrWhiteSpace($functionHelpMessage)) {
                    try {

                        $functionHelpMessage = "$((Get-Help ("$ModuleName$($commandInfo.Name)")).description.Text)"
                    }
                    catch {
                        $functionHelpMessage = "No description available."
                    }
                }

                # build and add function definition
                $name = $commandInfo.Name
                $found = $false;
                $allcmdLetNames = @($name.ToLowerInvariant(), ($moduleName.ToLowerInvariant() + $name.ToLowerInvariant()))
                $NoConfirmationToolFunctionNames = @($ExposedCmdLets | Where-Object -Property Confirm -EQ $false | Select-Object -ExpandProperty Name)

                foreach ($AllowedCmdLet in $NoConfirmationToolFunctionNames) {

                    if ($AllowedCmdLet.ToLowerInvariant() -in $allcmdLetNames) {

                        $found = $true
                        break;
                    }
                }

                $newFunctionDefinition = @{
                    type     = "function"
                    function = @{
                        name                       = "$name"
                        description                = "$functionHelpMessage"
                        user_confirmation_required = (-not $found)
                        parameters                 = @{
                            type       = 'object'
                            properties = $propertiesTable
                            required   = $requiredParams
                        }
                        callback                   = $callback
                    }
                }

                if (-not [string]::IsNullOrWhiteSpace($powershell_returnType)) {

                    $newFunctionDefinition.function.powershell_returnType = $powershell_returnType
                }

                if (-not [string]::IsNullOrWhiteSpace($returnType)) {

                    $newFunctionDefinition.function.returnType = $returnType
                }

                $null = $result.Add($newFunctionDefinition)
            }
        }
    }

    end {

        Write-Verbose "Completed conversion with $($result.Count) function definitions"
        Write-Output $result
    }
}
################################################################################