Public/Get-GPT4Completion.ps1

function Get-AzureOpenAIOptions {
    [CmdletBinding()]
    param()

    $Script:AzureOpenAIOptions
}

function Set-AzureOpenAIOptions {
    [CmdletBinding()]
    param(
        $Endpoint,
        $DeploymentName,
        $ApiVersion
    )

    $options = @{} + $PSBoundParameters

    foreach ($key in $options.Keys) {
        $Script:AzureOpenAIOptions[$key] = $options[$key]
    }
}


function Reset-AzureOpenAIOptions {
    [CmdletBinding()]
    param()

    $Script:AzureOpenAIOptions = @{
        Endpoint       = 'not set'
        DeploymentName = 'not set'
        ApiVersion     = 'not set'
    }
}

function Get-ChatAzureOpenAIURI {
    <#
        .SYNOPSIS
            Get the URI for the Azure OpenAI API.
        .EXAMPLE
            Get-ChatAzureOpenAIURI
    #>

    [CmdletBinding()]
    param()

    $options = Get-AzureOpenAIOptions

    if ($options.Endpoint -eq 'not set') {
        throw 'Azure Open AI Endpoint not set'
    }
    elseif ($options.DeploymentName -eq 'not set') {
        throw 'Azure Open AI DeploymentName not set'
    }
    elseif ($options.ApiVersion -eq 'not set') {
        throw 'Azure Open AI ApiVersion not set'
    }

    $uri = "$($options.Endpoint)/openai/deployments/$($options.DeploymentName)/chat/completions?api-version=$($options.ApiVersion)"

    $uri
}

function Get-ChatAPIProvider {
    <#
        .SYNOPSIS
            Get the current chat API provider.
        .EXAMPLE
            Get-ChatAPIProvider
    #>

    [CmdletBinding()]
    param()

    $Script:ChatAPIProvider
}

function Set-ChatAPIProvider {
    <#
        .SYNOPSIS
            Set the chat API provider.
        .PARAMETER Provider
            The chat API provider to use.
            Valid values are 'AzureOpenAI' and 'OpenAI'.
            Default value is 'OpenAI'.
        .EXAMPLE
            Set-ChatAPIProvider -Provider 'AzureOpenAI'
    #>

    [CmdletBinding()]
    param(
        [ValidateSet('AzureOpenAI', 'OpenAI')]
        $Provider = 'OpenAI'
    )

    $Script:ChatAPIProvider = $Provider
}

function Get-ChatSessionOptions {
    <#
        .SYNOPSIS
            Get the current chat session options.
        .EXAMPLE
            Get-ChatSessionOptions
    #>

    [CmdletBinding()]
    param()

    $Script:ChatSessionOptions
}

function Set-ChatSessionOption {
    <#
        .SYNOPSIS
            Set a chat session option.
                 
        .PARAMETER model
            The model to use for the chat session.
            Valid values are 'gpt-4' and 'gpt-3.5-turbo'.
            Default value is 'gpt-4'.
        .PARAMETER max_tokens
            The maximum number of tokens to generate.
            Default value is 256.
        .PARAMETER temperature
            The temperature of the model.
            Default value is 0.
        .PARAMETER top_p
            The top_p of the model.
            Default value is 1.
        .PARAMETER frequency_penalty
            The frequency penalty of the model.
            Default value is 0.
        .PARAMETER presence_penalty
            The presence penalty of the model.
            Default value is 0.
        .PARAMETER stop
            The stop sequence of the model.
            Default value is $null.
        .EXAMPLE
            Set-ChatSessionOption -model 'gpt-4'
        .EXAMPLE
            Set-ChatSessionOption -max_tokens 512
 
    #>

    [CmdletBinding()]
    param(
        [ValidateSet('gpt-4','gpt-3.5-turbo-1106', 'gpt-4-1106-preview', 'gpt-4-0613', 'gpt-3.5-turbo', 'gpt-3.5-turbo-16k', 'gpt-3.5-turbo-0613')]        
        $model,
        $max_tokens = 256,
        $temperature = 0,
        $top_p = 1,
        $frequency_penalty = 0,
        $presence_penalty = 0,
        $stop
    )

    $options = @{} + $PSBoundParameters
    
    foreach ($entry in $options.GetEnumerator()) {
        $Script:ChatSessionOptions["$($entry.Name)"] = $entry.Value
    }
}

function Reset-ChatSessionOptions {
    <#
        .SYNOPSIS
            Reset the chat session options to their default values.
        .EXAMPLE
            Reset-ChatSessionOptions
    #>

    [CmdletBinding()]
    param()

    $Script:ChatSessionOptions = @{
        'model'             = 'gpt-4'
        'temperature'       = 0.0
        'max_tokens'        = 256
        'top_p'             = 1.0
        'frequency_penalty' = 0
        'presence_penalty'  = 0
        'stop'              = $null
    }

    Enable-ChatPersistence
}

function Clear-ChatMessages {
    <#
        .SYNOPSIS
            Clear the chat messages in the current chat session.
        .EXAMPLE
            Clear-ChatMessages
    #>

    [CmdletBinding()]
    param()

    $Script:ChatMessages.Clear()
}

function Add-ChatMessage {
    <#
        .SYNOPSIS
            Add a chat message to the current chat session.
        .PARAMETER Message
            The chat message to add.
        .EXAMPLE
            Add-ChatMessage -Message <#PSCustomObject#>

    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        $Message
    )

    $null = $Script:ChatMessages.Add($Message)
}

function New-ChatMessageTemplate {
    <#
        .SYNOPSIS
            Create a new chat message template.
        .PARAMETER Role
            The role of the chat message.
            Valid values are 'user', 'system', and 'assistant'.
        .PARAMETER Content
            The content of the chat message.
        .PARAMETER Name
            The name of the author of this message. name is required if role is function, and it should be the name of the function whose response is in the content
        .EXAMPLE
            New-ChatMessageTemplate -Role 'user' -Content <#string#>

    #>
    [CmdletBinding()]
    param( 
        [ValidateSet('user', 'system', 'assistant', 'function')]
        $Role,
        $Content,
        $Name
    )

    $returnObject = [ordered]@{
        role    = $Role
        content = $Content
    }

    if ($Role -eq 'function' -and $null -eq $Name) {
        throw 'Name is required if role is function'
    }
    
    if ($Name) {
        $returnObject.name = $Name
    }

    [PSCustomObject]$returnObject
}

function New-ChatMessage {
    <#
        .SYNOPSIS
            Create a new chat message.
        .DESCRIPTION
            Create a new chat message and add it to the current chat session.
        .PARAMETER Role
            The role of the chat message.
            Valid values are 'user', 'system', and 'assistant'.
        .PARAMETER Content
            The content of the chat message.
        .EXAMPLE
            New-ChatMessage -Role 'user' -Content <#string
    #>

    param(
        [Parameter(Mandatory)]
        [ValidateSet('user', 'system', 'assistant')]
        $Role,
        [Parameter(Mandatory)]
        $Content
    )

    $Script:ChatInProgress = $Script:true

    $message = New-ChatMessageTemplate -Role $Role -Content $Content

    Add-ChatMessage -Message $message

    #Export-ChatSession
}

function New-ChatSystemMessage {
    <#
        .SYNOPSIS
            Create a new chat system message.
        .DESCRIPTION
            Create a new chat system message and add it to the current chat session.
        .PARAMETER Content
            The content of the chat message.
        .EXAMPLE
            New-ChatSystemMessage -Content <#string#>
        
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        $Content
    )

    New-ChatMessage -Role 'system' -Content $Content
}

function New-ChatUserMessage {
    <#
        .SYNOPSIS
            Create a new chat user message.
        .DESCRIPTION
            Create a new chat user message and add it to the current chat session.
        .PARAMETER Content
            The content of the chat message.
        .EXAMPLE
            New-ChatUserMessage -Content <#string#>

    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        $Content
    )

    New-ChatMessage -Role 'user' -Content $Content
}

function New-ChatAssistantMessage {
    <#
        .SYNOPSIS
            Create a new chat assistant message.
        .DESCRIPTION
            Create a new chat assistant message and add it to the current chat session.
        .PARAMETER Content
            The content of the chat message.
        .EXAMPLE
            New-ChatAssistantMessage -Content <#string
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        $Content
    )

    New-ChatMessage -Role 'assistant' -Content $Content
}

function Get-ChatMessages {
    <#
        .SYNOPSIS
            Get the chat messages in the current chat session.
        .EXAMPLE
            Get-ChatMessages
    #>

    [CmdletBinding()]
    param()

    @($Script:ChatMessages)
}

function Get-ChatPayload {
    <#
        .SYNOPSIS
            Get the chat payload.
        .DESCRIPTION
            Get the chat payload as a PSCustomObject.
        .PARAMETER AsJson
            Return the chat payload as a JSON string.
        .EXAMPLE
            Get-ChatPayload
    #>

    [CmdletBinding()]
    param(
        [Switch]$AsJson
    )

    $payload = (Get-ChatSessionOptions).Clone()
    
    $payload.messages = @(Get-ChatMessages)

    if ($AsJson) {
        return $payload | ConvertTo-Json -Depth 10
    }
    else {
        return $payload
    }
    
}

function New-Chat {
    <#
        .SYNOPSIS
            Start a new chat session.
        .DESCRIPTION
            Start a new chat session and optionally send a message to the assistant.
        .PARAMETER Content
            The content of the chat message.
        .EXAMPLE
            New-Chat
        .EXAMPLE
            New-Chat -Content <#string#>

    #>
    [CmdletBinding()]
    param(
        $Content
    )

    Stop-Chat
    $Script:ChatInProgress = $true

    if (![string]::IsNullOrEmpty($Content)) {
        New-ChatSystemMessage -Content $Content
        Get-GPT4Response
    }
    
}

function Test-ChatInProgress {
    <#
        .SYNOPSIS
            Test if a chat session is in progress.
        .EXAMPLE
            Test-ChatInProgress
    #>

    [CmdletBinding()]
    param()
    $Script:ChatInProgress
}

function Stop-Chat {
    <#
        .SYNOPSIS
            Stop the current chat session.
        .EXAMPLE
            Stop-Chat
    #>

    [CmdletBinding()]
    param()

    $Script:ChatInProgress = $false
    
    Clear-ChatMessages
    Reset-ChatSessionTimeStamp 
}

function Get-GPT4Completion {
    <#
        .SYNOPSIS
            Get a GPT-4 completion.
        .DESCRIPTION
            Get a GPT-4 completion from the OpenAI API.
        .EXAMPLE
            chat "use powershell: what is my IP address?"
        .EXAMPLE
            Get-GPT4Completion -Prompt <#string#>

    #>
    [CmdletBinding()]
    [alias("chat")]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        $Content,
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [decimal]$temperature,
        # The maximum number of tokens to generate. default 256
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [ValidateRange(1, 4000)]
        $max_tokens = 256
    )

    New-ChatUserMessage -Content $Content

    Get-GPT4Response -Temperature $temperature -max_tokens $max_tokens
}

function Get-GPT4Response {
    [CmdletBinding()]
    param(
        [decimal]$Temperature,
        $max_tokens
    )

    $payload = Get-ChatPayload -AsJson
    $body = [System.Text.Encoding]::UTF8.GetBytes($payload)

    if ((Get-ChatAPIProvider) -eq 'OpenAI') {
        $uri = Get-OpenAIChatCompletionUri
    }
    elseif ((Get-ChatAPIProvider) -eq 'AzureOpenAI') {
        $uri = Get-ChatAzureOpenAIURI
    }
    
    if ($Temperature) {
        (Get-ChatSessionOptions)['temperature'] = $Temperature
    }

    if ($max_tokens) {
        (Get-ChatSessionOptions)['max_tokens'] = $max_tokens
    }

    $result = Invoke-OpenAIAPI -Uri $uri -Method 'Post' -Body $body 

    if ($result.choices) {
        $response = $result.choices[0].message.content
        New-ChatAssistantMessage -Content $response
        
        Export-ChatSession
        return $response
    }
}