Functions/GenXdev.AI/Get-AILLMSettings.ps1
|
<# .SYNOPSIS Gets the LLM settings for AI operations in GenXdev.AI. .DESCRIPTION This function retrieves the LLM (Large Language Model) settings used by the GenXdev.AI module for various AI operations. Settings are retrieved from session variables, persistent preferences, or default settings JSON file, in that order of precedence. If no settings are found for the requested LLMQueryType, the function automatically checks whether another LLMQueryType has been manually configured and falls back to those settings. If no type has ever been configured and the session is interactive, the user is prompted to select a default provider or enter settings manually. In non-interactive sessions (e.g., SSH remoting), an error is thrown with instructions for configuring settings. .LICENSE Copyright (C) 2026 René Vaessen / GenXdev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. .PARAMETER LLMQueryType The type of LLM query to get settings for. This determines which default settings to use when no custom settings are found. Valid values include SimpleIntelligence, Knowledge, Pictures, TextTranslation, Coding, and ToolUse. .PARAMETER Model The model identifier or pattern to use for AI operations. .PARAMETER ApiEndpoint The API endpoint URL for AI operations. .PARAMETER ApiKey The API key for authenticated AI operations. .PARAMETER NoSupportForJsonSchema When specified, indicates that the endpoint does not support json_schema response format. This enables fallback behavior using prompt-based instructions. .PARAMETER NoSupportForImageUpload When specified, indicates that the endpoint does not support image upload functionality. .PARAMETER NoSupportForToolCalls When specified, indicates that the endpoint does not support tool calling functionality. .PARAMETER PromptForSettings When specified, launches an interactive prompt to configure the LLM settings .PARAMETER PreferencesDatabasePath Database path for preference data files. .PARAMETER SessionOnly Use alternative settings stored in session for AI preferences like Language, Image collections, etc. .PARAMETER ClearSession Clear the session setting (Global variable) before retrieving. .PARAMETER SkipSession When specified, skips session settings and retrieves only from persistent preferences or defaults. This is useful when you want to ignore any temporary session-level overrides. .EXAMPLE Get-AILLMSettings Gets the LLM settings for SimpleIntelligence query type (default). .EXAMPLE Get-AILLMSettings -LLMQueryType "Coding" Gets the LLM settings for Coding query type. .EXAMPLE Get-AILLMSettings -SkipSession Gets the LLM settings from preferences or defaults only, ignoring session settings. .EXAMPLE Get-AILLMSettings "Knowledge" #> ################################################################################ function Get-AILLMSettings { [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] [OutputType([hashtable])] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] param( ############################################################################### [Parameter( Position = 0, Mandatory = $false, HelpMessage = 'The type of LLM query to get settings for' )] [ValidateSet( 'SimpleIntelligence', 'Knowledge', 'Pictures', 'TextTranslation', 'Coding', 'ToolUse' )] [string] $LLMQueryType = 'SimpleIntelligence', ############################################################################### [Parameter( Mandatory = $false, HelpMessage = 'The model identifier or pattern to use for AI operations' )] [string] $Model, ############################################################################### [Parameter( Mandatory = $false, HelpMessage = 'The API endpoint URL for AI operations' )] [string] $ApiEndpoint, ############################################################################### [Parameter( Mandatory = $false, HelpMessage = 'The API key for authenticated AI operations' )] [string] $ApiKey, ############################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Whether the endpoint does not support json_schema response format' )] [switch] $NoSupportForJsonSchema, ############################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Whether the endpoint does not support image upload functionality' )] [switch] $NoSupportForImageUpload, ############################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Whether the endpoint does not support tool calling functionality' )] [switch] $NoSupportForToolCalls, ############################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Use alternative settings stored in session for ' + 'preferences like Language, Image collections, etc') )] [switch] $SessionOnly, ############################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Clear the session setting (Global variable) before retrieving' )] [switch] $ClearSession, ############################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Database path for preference data files' )] [Alias('DatabasePath')] [string] $PreferencesDatabasePath, ############################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Skip session settings and get from preferences ' + 'or defaults only') )] [Alias('FromPreferences')] [switch] $SkipSession, ############################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Launch interactive prompt to configure LLM settings') )] [switch] $PromptForSettings, ############################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Suppress auto-prompt when ApiKey is missing. ' + 'Used internally to prevent recursion during display.') )] [switch] $SuppressAutoPrompt, ############################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Force non-interactive mode — forwards to ' + 'Invoke-AILLMSettingsPrompt when -PromptForSettings is used') )] [switch] $NonInteractive ############################################################################### ) begin { Microsoft.PowerShell.Utility\Write-Verbose '=== Get-AILLMSettings Started ===' Microsoft.PowerShell.Utility\Write-Verbose "LLMQueryType: $LLMQueryType" Microsoft.PowerShell.Utility\Write-Verbose "SessionOnly: $SessionOnly" Microsoft.PowerShell.Utility\Write-Verbose "SkipSession: $SkipSession" Microsoft.PowerShell.Utility\Write-Verbose "ClearSession: $ClearSession" if ($PromptForSettings) { Microsoft.PowerShell.Utility\Write-Verbose 'PromptForSettings: True' $promptParams = GenXdev\Copy-IdenticalParamValues -BoundParameters $PSBoundParameters -FunctionName 'GenXdev\Invoke-AILLMSettingsPrompt' GenXdev\Invoke-AILLMSettingsPrompt @promptParams } else { Microsoft.PowerShell.Utility\Write-Verbose 'PromptForSettings: False' } # log provided parameter values $providedParams = @() if ($PSBoundParameters.ContainsKey('Model')) { $providedParams += "Model=$Model" } if ($PSBoundParameters.ContainsKey('ApiEndpoint')) { $providedParams += "ApiEndpoint=$ApiEndpoint" } if ($PSBoundParameters.ContainsKey('ApiKey')) { $providedParams += 'ApiKey=(redacted)' } if ($PSBoundParameters.ContainsKey('NoSupportForJsonSchema')) { $providedParams += "NoSupportForJsonSchema=$NoSupportForJsonSchema" } if ($PSBoundParameters.ContainsKey('NoSupportForImageUpload')) { $providedParams += "NoSupportForImageUpload=$NoSupportForImageUpload" } if ($PSBoundParameters.ContainsKey('NoSupportForToolCalls')) { $providedParams += "NoSupportForToolCalls=$NoSupportForToolCalls" } if ($providedParams.Count -gt 0) { Microsoft.PowerShell.Utility\Write-Verbose "Provided parameters: $($providedParams -join ', ')" } else { Microsoft.PowerShell.Utility\Write-Verbose 'No explicit parameter values provided' } # handle clearing session variables first if requested if ($ClearSession) { # clear all global session variables for llm settings # These variables are used dynamically via Get-Variable, so suppress PSScriptAnalyzer warnings Microsoft.PowerShell.Utility\Set-Variable -Name ('AILLMSettings_' + $LLMQueryType + '_Model') -Value $null -Scope Global Microsoft.PowerShell.Utility\Set-Variable -Name ('AILLMSettings_' + $LLMQueryType + '_ApiEndpoint') -Value $null -Scope Global Microsoft.PowerShell.Utility\Set-Variable -Name ('AILLMSettings_' + $LLMQueryType + '_ApiKey') -Value $null -Scope Global Microsoft.PowerShell.Utility\Set-Variable -Name ('AILLMSettings_' + $LLMQueryType + '_NoSupportForJsonSchema') -Value $null -Scope Global Microsoft.PowerShell.Utility\Set-Variable -Name ('AILLMSettings_' + $LLMQueryType + '_NoSupportForImageUpload') -Value $null -Scope Global Microsoft.PowerShell.Utility\Set-Variable -Name ('AILLMSettings_' + $LLMQueryType + '_NoSupportForToolCalls') -Value $null -Scope Global # inform user that session variables have been cleared Microsoft.PowerShell.Utility\Write-Verbose ( 'Cleared session LLM settings for all properties' ) } # initialize the result hashtable with all possible setting properties $result = @{ Model = $null ApiEndpoint = $null ApiKey = $null NoSupportForJsonSchema = $null NoSupportForImageUpload = $null NoSupportForToolCalls = $null } # build splatted params for Get-GenXdevPreference to forward # PreferencesDatabasePath, SessionOnly, SkipSession etc. $getPrefParams = GenXdev\Copy-IdenticalParamValues ` -BoundParameters $PSBoundParameters ` -FunctionName 'GenXdev\Get-GenXdevPreference' # internal helper: finds any LLMQueryType that has been manually configured function Get-AnyConfiguredLLMQueryType { param( [string]$PreferencesDatabasePath ) $allTypes = @( 'SimpleIntelligence', 'Knowledge', 'Pictures', 'TextTranslation', 'Coding', 'ToolUse' ) foreach ($type in $allTypes) { $key = "AILLMSettings_${type}_ManuallyConfigured" try { $isConfigured = GenXdev\Get-GenXdevPreference @getPrefParams ` -Name $key ` -ErrorAction SilentlyContinue if ($isConfigured -eq $true -or $isConfigured -eq 'True') { Microsoft.PowerShell.Utility\Write-Verbose "Found manually configured type: ${type}" return $type } } catch { Microsoft.PowerShell.Utility\Write-Verbose "Error checking ${type}: $($_.Exception.Message)" } } Microsoft.PowerShell.Utility\Write-Verbose 'No manually configured LLMQueryType found' return $null } } process { # define helper function to retrieve individual setting values function Get-SettingValue { param( [string]$propertyName, [object]$parameterValue, [bool]$parameterExplicitlyProvided, [string]$llmQueryType, [switch]$sessionOnly, [switch]$skipSession, [hashtable]$defaultConfig ) # check if parameter was explicitly provided and has a meaningful value if ($parameterExplicitlyProvided) { # for string parameters, check if the value is meaningful (not null/empty/whitespace) $isStringParam = $propertyName -in @('Model', 'ApiEndpoint', 'ApiKey') if ($isStringParam) { if (-not [String]::IsNullOrWhiteSpace($parameterValue)) { Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] Using explicitly provided parameter value: $parameterValue" return $parameterValue } else { Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] Parameter explicitly provided but is null/empty, checking other sources..." } } else { # for numeric parameters, check if not null and not zero if ($null -ne $parameterValue -and $parameterValue -ne 0) { Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] Using explicitly provided parameter value: $parameterValue" return $parameterValue } else { Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] Parameter explicitly provided but is null/zero, checking other sources..." } } } else { Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] No parameter value provided, checking other sources..." } # check session variables unless SkipSession is specified if (-not $skipSession) { Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] Checking session variables..." # construct session variable name $sessionVarName = "AILLMSettings_$($llmQueryType)_$propertyName" # attempt to retrieve session variable value if (Microsoft.PowerShell.Utility\Get-Variable ` -Name $sessionVarName ` -Scope Global ` -ErrorAction SilentlyContinue) { # get the actual variable value $sessionValue = (Microsoft.PowerShell.Utility\Get-Variable ` -Name $sessionVarName ` -Scope Global).Value # return session value if it exists and is not empty if ($null -ne $sessionValue -and $sessionValue -ne '') { # Normalize NoSupportFor* switch values: legacy # session variables may hold "True"/"False" strings. if ($propertyName -like 'NoSupportFor*' -and $sessionValue -is [string]) { $sessionValue = $sessionValue -eq 'True' } Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] Found session value: $sessionValue" return $sessionValue } else { Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] Session variable exists but is null/empty" } } else { Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] No session variable found: $sessionVarName" } } else { Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] Skipping session variables (SkipSession=true)" } # return null if SessionOnly is specified and no session value found if ($sessionOnly) { Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] SessionOnly=true and no session value found, returning null" return $null } # check preference storage unless SessionOnly is specified if (-not $sessionOnly) { Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] Checking preference storage..." try { # construct preference key using query type and property name $preferenceKey = "AILLMSettings_$($llmQueryType)_$propertyName" # attempt to retrieve preference value $preferenceValue = GenXdev\Get-GenXdevPreference @getPrefParams ` -Name $preferenceKey ` -ErrorAction SilentlyContinue # return preference value if it exists and is not empty if ($null -ne $preferenceValue -and $preferenceValue -ne '') { # Normalize NoSupportFor* switch values: legacy # preferences may have stored "True"/"False" as # strings (from when these were [string] params). if ($propertyName -like 'NoSupportFor*' -and $preferenceValue -is [string]) { $preferenceValue = $preferenceValue -eq 'True' } Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] Found preference value: $preferenceValue" return $preferenceValue } else { Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] No preference value found for key: $preferenceKey" } } catch { # preference not found, continue to defaults Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] Error accessing preferences: $($_.Exception.Message)" } # use the auto-selected default configuration if available if ($null -ne $defaultConfig -and $defaultConfig.ContainsKey($propertyName)) { $finalValue = $defaultConfig[$propertyName] Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] Found in auto-selected default configuration: $finalValue" return $finalValue } else { Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] Property not found in auto-selected default configuration" } } else { Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] Skipping defaults (SessionOnly=true)" } Microsoft.PowerShell.Utility\Write-Verbose "[$propertyName] No value found from any source, returning null" return $null } # retrieve each setting value using the helper function or from defaults # create parameter hashtable for Get-AIDefaultLLMSettings $defaultSettingsParams = GenXdev\Copy-IdenticalParamValues ` -BoundParameters $PSBoundParameters ` -FunctionName 'GenXdev\Get-AIDefaultLLMSettings' # -DefaultValues (Get-Variable -Scope Local -ErrorAction SilentlyContinue) Microsoft.PowerShell.Utility\Write-Verbose '=== Using Get-AIDefaultLLMSettings for auto-selection ===' $defaultConfig = GenXdev\Get-AIDefaultLLMSettings @defaultSettingsParams if ($defaultConfig) { # Check if it's an array or a single hashtable if ($defaultConfig -is [array] -and $defaultConfig.Count -gt 0) { # for Pictures, prefer a provider that supports image upload if ($LLMQueryType -eq 'Pictures') { $selectedDefaultConfig = $null foreach ($candidate in $defaultConfig) { $ht = $candidate | GenXdev\ConvertTo-HashTable if ($ht.NoSupportForImageUpload -ne $true) { $selectedDefaultConfig = $ht Microsoft.PowerShell.Utility\Write-Verbose 'Auto-selected default configuration with image upload support' break } } if ($null -eq $selectedDefaultConfig) { # fallback to first if none supports images $selectedDefaultConfig = $defaultConfig[0] | GenXdev\ConvertTo-HashTable Microsoft.PowerShell.Utility\Write-Verbose 'No image-upload-capable provider found, using first available' } } else { $selectedDefaultConfig = $defaultConfig[0] | GenXdev\ConvertTo-HashTable Microsoft.PowerShell.Utility\Write-Verbose 'Auto-selected default configuration found (from array)' } } elseif ($defaultConfig -is [hashtable]) { # Single hashtable returned $selectedDefaultConfig = $defaultConfig Microsoft.PowerShell.Utility\Write-Verbose 'Auto-selected default configuration found (single hashtable)' } else { $selectedDefaultConfig = $null Microsoft.PowerShell.Utility\Write-Verbose "Default configuration has unexpected type: $($defaultConfig.GetType().FullName)" } } else { $selectedDefaultConfig = $null Microsoft.PowerShell.Utility\Write-Verbose 'No suitable default configuration found' } # Debug: Log the structure of the selected default configuration if ($null -ne $selectedDefaultConfig) { Microsoft.PowerShell.Utility\Write-Verbose '=== DEBUG: Selected Default Configuration ===' if ($selectedDefaultConfig -is [hashtable]) { Microsoft.PowerShell.Utility\Write-Verbose "Configuration is a hashtable with keys: $($selectedDefaultConfig.Keys -join ', ')" foreach ($key in $selectedDefaultConfig.Keys) { $value = if ($null -eq $selectedDefaultConfig[$key]) { '(null)' } else { $selectedDefaultConfig[$key] } Microsoft.PowerShell.Utility\Write-Verbose " $key = $value" } } else { Microsoft.PowerShell.Utility\Write-Verbose "Configuration type: $($selectedDefaultConfig.GetType().FullName)" } Microsoft.PowerShell.Utility\Write-Verbose '=== END DEBUG ===' } # for each property, use the priority: parameter > session > preferences > auto-selected default foreach ($propertyName in @('Model', 'ApiEndpoint', 'ApiKey', 'NoSupportForJsonSchema', 'NoSupportForImageUpload', 'NoSupportForToolCalls')) { $parameterValue = $null $parameterExplicitlyProvided = $false switch ($propertyName) { 'Model' { $parameterValue = $Model $parameterExplicitlyProvided = $PSBoundParameters.ContainsKey('Model') } 'ApiEndpoint' { $parameterValue = $ApiEndpoint $parameterExplicitlyProvided = $PSBoundParameters.ContainsKey('ApiEndpoint') } 'ApiKey' { $parameterValue = $ApiKey $parameterExplicitlyProvided = $PSBoundParameters.ContainsKey('ApiKey') } 'NoSupportForJsonSchema' { $parameterValue = $NoSupportForJsonSchema $parameterExplicitlyProvided = $PSBoundParameters.ContainsKey('NoSupportForJsonSchema') } 'NoSupportForImageUpload' { $parameterValue = $NoSupportForImageUpload $parameterExplicitlyProvided = $PSBoundParameters.ContainsKey('NoSupportForImageUpload') } 'NoSupportForToolCalls' { $parameterValue = $NoSupportForToolCalls $parameterExplicitlyProvided = $PSBoundParameters.ContainsKey('NoSupportForToolCalls') } } # get the final value using priority logic $finalValue = Get-SettingValue ` -propertyName $propertyName ` -parameterValue $parameterValue ` -parameterExplicitlyProvided $parameterExplicitlyProvided ` -llmQueryType $LLMQueryType ` -sessionOnly:$SessionOnly ` -skipSession:$SkipSession ` -defaultConfig $selectedDefaultConfig $result[$propertyName] = $finalValue } # check if ApiKey is missing — either nothing configured, or defaults # gave model/endpoint but no key to actually make API calls $missingKey = ($null -eq $result.ApiKey) if ($missingKey -and -not $SessionOnly -and -not $SkipSession -and -not $SuppressAutoPrompt) { # try cross-type fallback whenever ApiKey is missing Microsoft.PowerShell.Utility\Write-Verbose 'ApiKey missing, checking cross-type fallback...' $configuredType = Get-AnyConfiguredLLMQueryType ` -PreferencesDatabasePath $PreferencesDatabasePath if ($null -ne $configuredType) { Microsoft.PowerShell.Utility\Write-Verbose "Falling back to configured type: ${configuredType}" $fallbackParams = GenXdev\Copy-IdenticalParamValues ` -BoundParameters $PSBoundParameters ` -FunctionName 'GenXdev\Get-AILLMSettings' $result = GenXdev\Get-AILLMSettings @fallbackParams ` -LLMQueryType $configuredType } # if ApiKey is still null after potential fallback, try interactive if (($null -eq $result.ApiKey) -and [Environment]::UserInteractive) { Microsoft.PowerShell.Utility\Write-Verbose ( 'No ApiKey configured, delegating to Set-AILLMSettings -PromptForSettings...' ) GenXdev\Invoke-AILLMSettingsPrompt ` -LLMQueryType $LLMQueryType ` -PreferencesDatabasePath $PreferencesDatabasePath # re-resolve settings now that they've been saved Microsoft.PowerShell.Utility\Write-Verbose ( 'Re-resolving settings after interactive setup...' ) $reResolveParams = GenXdev\Copy-IdenticalParamValues ` -BoundParameters $PSBoundParameters ` -FunctionName 'GenXdev\Get-AILLMSettings' $result = GenXdev\Get-AILLMSettings @reResolveParams } elseif ($null -eq $result.ApiKey) { # non-interactive session with no settings — throw clear error throw ('No LLM settings have been configured. Run ' + 'Set-AILLMSettings -LLMQueryType "SimpleIntelligence" ' + '-ApiEndpoint "<endpoint>" -ApiKey "<key>" ' + '-Model "<model>" to configure, or run ' + 'Get-AILLMSettings interactively for guided setup.') } } } end { # log final result summary Microsoft.PowerShell.Utility\Write-Verbose '=== Final LLM Settings Summary ===' Microsoft.PowerShell.Utility\Write-Verbose "LLMQueryType: $LLMQueryType" foreach ($key in $result.Keys) { $value = if ($null -eq $result[$key]) { '(null)' } else { $result[$key] } Microsoft.PowerShell.Utility\Write-Verbose "$key = $value" } Microsoft.PowerShell.Utility\Write-Verbose '=== End Summary ===' # return the completed settings hashtable to the caller return $result } } ################################################################################ |