mkht / PSOpenAI

PowerShell module for OpenAI API.
MIT License
35 stars 10 forks source link

Module scoped PSDefaultParameterValues not working #16

Closed potatoqualitee closed 4 months ago

potatoqualitee commented 4 months ago

I'm looking to add Deployment to Set-OpenAIContext so that there's a uniform way of setting all the vars needed for Azure.

But I'm running into the strangest thing. Do you have any idea why setting PSDefaultParameterValues in the PSM1 then further setting it in Set-OpenAIContext, such as:

    if ($PSBoundParameters.ContainsKey('Deployment')) {
        $PSDefaultParameterValues['*:Deployment'] = $Deployment
        $PSDefaultParameterValues['*:Model'] = $Deployment
    }

Does not have an effect unless it's $global:PSDefaultParameterValues? Not even $script:PSDefaultParameterValues works. I can add the following to the body of Request-ChatCompletion and it shows up when executing the script:

$script:PSDefaultParameterValues['*:Model'] | Write-Warning

But $Model is not set and appears blank.

❯ ipmo ./psopenai -force
WARNING: PSDefaultParameterValues['*:Model'] is equal to gpt-3.5-turbo
❯ Set-OpenAIContext @azparms; Request-ChatCompletion -Message 'Hello Azure OpenAI' -Verbose
VERBOSE: API key to be used is XYZ
WARNING: PSDefaultParameterValues['*:Model'] is equal to gpt4
WARNING: Model is equal to
VERBOSE: Request to Azure OpenAI API
VERBOSE: Method = Post, Path =
https://XYZ.openai.azure.com//openai/deployments//chat/completions?api-version=2024-05-01-preview
VERBOSE: POST with -1-byte payload
Invoke-OpenAIAPIRequest : Azure OpenAI API returned an 404 (NotFound) Error: The API
deployment for this resource does not exist. If you created the deployment within the
last 5 minutes, please wait a moment and try again.
At C:\github\psopenai\Public\Request-ChatCompletion.ps1:457 char:25
+ $Response = Invoke-OpenAIAPIRequest @splat
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Invoke-OpenAIAPIRequest], NotFoundException
+ FullyQualifiedErrorId : PSOpenAI.APIRequest.NotFoundException,Invoke-OpenAIAPIRequest

I even removed all the decorators to no avail. Any idea what's going on with the scoping? This is PS 5.1

I even tried adding $script:DeploymentName = $Deployment to the context setter but that doesn't work either. I've never seen this before and I use PSDefaultParameterValues quite a lot in my modules, such as:

https://github.com/potatoqualitee/tentools/blob/main/tentools.psm1#L73

mkht commented 4 months ago

This is a specification behavior of PowerShell. The value of the PSDefaultParameterValues variable is always read only from scopes higher than its own, and values in its own scope are not used.

This is written in a comment in this code. https://github.com/PowerShell/PowerShell/blob/74d8bdba443895ea84da1ea6c0d26dac05f08d7b/src/System.Management.Automation/engine/pipeline.cs#L1068-L1071

Let's create a simple psm1 that defines a Test-MyPath function that simply wraps the Test-Path command. at the beginning of psm1 we set PSDefaultParameterValues in the script scope.

# MyModule.psm1
$script:PSDefaultParameterValues['*:PathType'] = 'Container'

function Test-MyPath {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, Position = 1)]
        [string]$Path,

        [Parameter()]
        [ValidateSet('Container', 'Leaf')]
        [string]$PathType = 'Leaf'
    )
    Write-Warning "Path is : $Path"
    Write-Warning "PathType is : $PathType"

    # Just call native Test-Path cmdlet
    $result = Microsoft.PowerShell.Management\Test-Path @PSBoundParameters
    Write-Host "Result is : $result"
}

Export-ModuleMember -Function @('Test-MyPath')

Then, create and run a ps1 script that uses this module.

# test.ps1
ipmo .\MyModule.psm1 -Force
mkdir 'Folder_Foo' -Force
Test-MyPath '.\Folder_Foo'

The "Folder_Foo" specified in Path is a folder. It is not a file. So it should return True if PathType is Container and False if PathType is Leaf. The PathType default for Test-MyPath is Leaf, but since we set PSDefaultParameterValues at the beginning of psm1, we would expect Container to be used unless we explicitly specify PathType.

When I run this ps1, We get a different result than expected.

C:\> Test-MyPath '.\Folder_Foo'
WARNING: Path is : .\Folder_Foo
WARNING: PathType is : Leaf
Result is : True

We will get a warning message that the PathType is Leaf. Why? From Test-MyPath's POV, the PSDefaultParameterValues set at the beginning of psm1 are in the same scope as itself, so Test-MyPath does not read them. However, from Test-Path's POV, Test-MyPath that called itself is a higher scope than itself. So the PSDefaultParameterValues set in psm1 are reflected.

potatoqualitee commented 4 months ago

Ohhhhhhhh! Thank you for the explanation. I must have been setting values for "internal" commands like Test-Path. Boo. That will make the solution less elegant.