ShellGPT.psm1

function Invoke-OpenAICompletion {
    param(
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [System.Collections.ArrayList]$prompt,                 # The prompt to send to the API to act upon
        [Parameter(Mandatory=$true)]
        [string]$APIKey,                        # The API key to authenticate the request.
        [Parameter(Mandatory=$false)]
        [string]$model = "gpt-3.5-turbo",       # The model to use from the endpoint.
        [Parameter(Mandatory=$false)]
        [string]$stop = "\n",                   # The stop instructor for the model.
        [Parameter(Mandatory=$false)]
        [double]$temperature = 0.4,             # The temperature value to use for sampling.
        [Parameter(Mandatory=$false)]
        [int]$max_tokens = 900,                 # The maximum number of tokens to generate in the response.
        [Parameter(Mandatory=$false)]
        [bool]$ShowOutput = $false,                    # The maximum number of tokens to generate in the response.
        [Parameter(Mandatory=$false)]
        [bool]$ShowTokenUsage = $false                 # The maximum number of tokens to generate in the response.
    )

    Write-Verbose ("ShellGPT-Invoke-OpenAICompletion @ "+(Get-Date)+" | Building request for sending off towards CompletionAPI...") 

    #Building Request for API
    $headers = @{
        "Content-Type" = "application/json"
        "Authorization" = "Bearer $APIKey"
    }

    $RequestBody = @{
        messages = $prompt
        model = $model
        temperature= $temperature
        max_tokens = $max_tokens
        stop=$stop
    } 

 
    #Convert the whole Body to be JSON, so that API can interpret it
    $RequestBody = $RequestBody | ConvertTo-Json -depth 3

    $uri = 'https://api.openai.com/v1/chat/completions'

    $RestMethodParameter=@{
        Method='Post'
        Uri = $uri
        body=$RequestBody
        Headers=$Headers
    }

    Write-Verbose ("ShellGPT-Invoke-OpenAICompletion @ "+(Get-Date)+" | Built request. This is the RequestBody: "+($RequestBody)) 

    try {
        #Call the OpenAI completions API
        Write-Verbose ("ShellGPT-Invoke-OpenAICompletion @ "+(Get-Date)+" | Sending off API Call using 'Invoke-RestMethod' to this URI: "+($uri)) 
        $APIresponse = Invoke-RestMethod @RestMethodParameter

        Write-Verbose ("ShellGPT-Invoke-OpenAICompletion @ "+(Get-Date)+" | Received response from API: "+($APIresponse | Out-String)) 

        #Extract Textresponse from API response
        $convertedResponseForOutput = $APIresponse.choices.message.content
        $tokenUsage = $APIresponse.usage

        Write-Verbose ("ShellGPT-Invoke-OpenAICompletion @ "+(Get-Date)+" | Extracted Output: "+($convertedResponseForOutput)) 
        Write-Verbose ("ShellGPT-Invoke-OpenAICompletion @ "+(Get-Date)+" | TokenUsage for this prompt: "+($TokenUsage.prompt_tokens)+" for completion: "+($TokenUsage.completion_tokens)+" Total tokens used: "+($TokenUsage.total_tokens)) 


    
        #Append text output to prompt for returning it
        Write-Verbose ("ShellGPT-Invoke-OpenAICompletion @ "+(Get-Date)+" | Creating new prompt with API response...") 
        [System.Collections.ArrayList]$prompt = New-OpenAICompletionPrompt -query $convertedResponseForOutput -role "assistant" -previousMessages $prompt -model $model

        Write-Verbose ("ShellGPT-Invoke-OpenAICompletion @ "+(Get-Date)+" | New Prompt is: "+($prompt | Out-String)) 

        If ($ShowTokenUsage -eq $true)
        {
            Write-Host ("ShellGPT-Invoke-OpenAICompletion @ "+(Get-Date)+" | TokenUsage for this prompt: "+($TokenUsage.prompt_tokens)+" for completion: "+($TokenUsage.completion_tokens)+" Total tokens used: "+($TokenUsage.total_tokens)) -ForegroundColor Yellow
        }

        if ($ShowOutput)
        {
            Write-Host ("ShellGPT @ "+(Get-Date)+" | "+($convertedResponseForOutput)) -ForegroundColor Green
        }

        [System.Collections.ArrayList]$promptToReturn = $prompt
    }
    catch {
        $errorDetails = $_.ErrorDetails.Message

        Write-Host ("ShellGPT-Invoke-OpenAICompletion @ "+(Get-Date)+" | Unable to handle Error "+($_.Exception.Message)+"See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later.") -ForegroundColor "Red"
        Write-Host ("ShellGPT-Invoke-OpenAICompletion @ "+(Get-Date)+" | Error Details: "+($errorDetails)) -ForegroundColor "Red"

        if ($errorDetails.contains("invalid JSON: 'utf-8'")) {
            Write-Host ("ShellGPT-Invoke-OpenAICompletion @ "+(Get-Date)+" | Your prompt seems to contain characters that can be misinterpreted in utf-8 encoding. Remove those characters and try again."+($promptToReturn |Out-String)) -ForegroundColor "Yellow"
            }

        [System.Collections.ArrayList]$prompt.RemoveAt($prompt.count-1) 
        [System.Collections.ArrayList]$promptToReturn = [System.Collections.ArrayList]$prompt
        Write-Verbose ("ShellGPT-Invoke-OpenAICompletion @ "+(Get-Date)+" | Returning Input prompt, without the last query due to error and to prevent the prompt from becoming unusable: "+($promptToReturn |Out-String)) -ForegroundColor "Yellow"
        }

    return [System.Collections.ArrayList]$promptToReturn
}

function New-OpenAICompletionPrompt {
    param (   
        [Parameter(Mandatory=$true)]        
        [string]$query,                                                                              # The user's query to add to the prompt.
        [Parameter(Mandatory=$false)]    
        [ValidateSet("system", "assistant", "user")] 
        [string]$role = "user",                                                                       # The role to add to the prompt.
        [Parameter(Mandatory=$false)]    
        [string]$instructor = "You are ChatGPT, a helpful AI Assistant.",                            # The instruction string to add to the prompt.
        [Parameter(Mandatory=$false)]    
        [string]$assistantReply = "Hello! I'm ChatGPT, a GPT Model. How can I assist you today?",    # The first, unseen reply by the model. Can be used to help train it and get expected output.
        [Parameter(Mandatory=$false)]    
        [System.Collections.ArrayList]$previousMessages,                                                             # An array of previous messages in the conversation.
        [Parameter(Mandatory=$false)]    
        [string]$filePath,                                                                                # An array of previous messages in the conversation.
        [Parameter(Mandatory=$false)]    
        [string]$model      
        )

    if ($filePath)
    {
        Write-Verbose ("ShellGPT-New-OpenAICompletionPrompt @ "+(Get-Date)+" | File path was provided: "+($filepath)) 


        if ($filePath.EndsWith(".pdf"))
        {
            Write-Verbose ("ShellGPT @ "+(Get-Date)+" | File is PDF. Trying to read content and generate .txt...") 
            Write-Verbose ("ShellGPT @ "+(Get-Date)+" | File is PDF. Reworking filepath to only have forward slashes...") 
            $filePath = $filePath.Replace("\","/")

            try {            
                $filePath = Convert-PDFtoText -filePath $filePath -TypeToExport txt
                Write-Verbose ("ShellGPT @ "+(Get-Date)+" | PDF Content was read, and .txt created at this path: "+($filepath)) 

            }
            catch {
                Write-Verbose ("ShellGPT @ "+(Get-Date)+" | We ran into trouble reading the PDF content and writing it to a .txt file "+($filepath)) 
                $errorToReport = $_.Exception.Message
                $errorDetails = $_.ErrorDetails.Message
                $message = "Unable to handle Error: "+$errorToReport+" See Error details below."
            
                write-host "Error:"$message -ForegroundColor red
            
                if ($errorDetails)
                    {
                        write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
                    }        
                }
        }

        try {
            Write-Verbose ("ShellGPT-New-OpenAICompletionPrompt @ "+(Get-Date)+" | Trying to read content of file using UTF-8 encoding...") 
            $filecontent = Get-Content -Path $filePath -Raw -Encoding utf8
            Write-Verbose ("ShellGPT-New-OpenAICompletionPrompt @ "+(Get-Date)+" | File content extracted...") 
            $query = "$query $filecontent" 
        }
        catch {
            $errorToReport = $_.Exception.Message
            $errorDetails = $_.ErrorDetails.Message
            $message = "Unable to handle Error: "+$errorToReport+" See Error details below."
        
            write-host "Error:"$message -ForegroundColor red
        
            if ($errorDetails)
            {
                write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
            }    
        }
    }

    #Remove characters the API can not interpret:
    $query = $query -replace '(?m)^\s+',''
    $query = $query -replace '\r',''
    $query = $query -replace '●',''
    $query = $query -replace '“',"'"
    $query = $query -replace '”',"'"
    $query = $query -replace 'ä',"ae"
    $query = $query -replace 'ö',"oe"
    $query = $query -replace 'ü',"ue"
    $query = $query -replace 'ß',"ss"
    $query = $query -replace '\u00A0', ' '

    
    <#
    $iso = [System.Text.Encoding]::GetEncoding("iso-8859-1")
    $bytes = $iso.GetBytes($query)
    $query = [System.Text.Encoding]::Default.GetString($bytes)
    #>


    if ($previousMessages)
    {
        Write-Verbose ("ShellGPT-New-OpenAICompletionPrompt @ "+(Get-Date)+" | Previous Messages are present: "+($previousMessages | Out-String))

        Write-Verbose ("ShellGPT-New-OpenAICompletionPrompt @ "+(Get-Date)+" | Adding new query: "+($query)+" for role: "+($role)+" to previous Messages")

        $previousMessages.Add(@{
            role = $role
            content = $query
        }) | Out-Null

        Write-Verbose ("ShellGPT-New-OpenAICompletionPrompt @ "+(Get-Date)+" | Added new query to previousmessages")

        [System.Collections.ArrayList]$promptToReturn = [System.Collections.ArrayList]$previousMessages 
    }

    else 
    {
        [System.Collections.ArrayList]$promptToReturn = @(
            @{
                role = "system"
                content = $instructor
            },
            @{
                role = "assistant"
                content = $assistantReply
            },
            @{
                role = $role
                content = $query
            }
        )
    }

    return $promptToReturn
}

function Set-OpenAICompletionCharacter {
    param (
        [Parameter(Mandatory=$true)]  
        [ValidateSet("Chat", "SentimentAndTickerAnalysis", "SentimentAnalysis", "IntentAnalysis","IntentAndSubjectAnalysis")]
        $mode,
        [Parameter(Mandatory=$false)] 
        $instructor = "You are a helpful AI. You answer as concisely as possible.",
        [Parameter(Mandatory=$false)] 
        $assistantReply = "Hello! I'm a ChatGPT-3.5 Model. How can I help you?"
    )

    switch ($mode)
    {
        "Chat" {
            $instructor = $instructor
            $assistantReply = $assistantReply
        }

        "SentimentAndTickerAnalysis" {
            $assistantReply = @{ 
                ticker = "BTC"
                asset_type = "Cryptocurrency"
                sentiment = 0.9
            }
            $instructor = "You are part of a trading bot API that analyzes tweets. When presented with a text message, you extract either the Cryptocurrency or Stockmarket abbrev for that ticker and you also analyze the text for sentiment. You provide your answer in a .JSON format in the following structure: { 'ticker': 'USDT', 'asset_type': 'Cryptocurrency', 'sentiment': 0.8 } You only answer with the .JSON object. You do not provide any reasoning why you did it that way. The sentiment is a value between 0 - 1. Where 1 is the most positive sentiment and 0 is the most negative. If you can not extract the Ticker, you specify it with 'unknown' in your response. Same for sentiment."
        }

        "SentimentAnalysis" {
            $assistantReply = @{ 
                sentiment = 0.9
            }
            $instructor = 'You are an API that analyzes text sentiment. You provide your answer in a .JSON format in the following structure: { "sentiment": 0.9 } You only answer with the .JSON object. You do not provide any reasoning why you did it that way. The sentiment is a value between 0 - 1. Where 1 is the most positive sentiment and 0 is the most negative. If you can not extract the sentiment, you specify it with "unknown" in your response.'
        }

        "IntentAnalysis" {
            $assistantReply = @{ 
                intent = "purchase"
            }
            $instructor = 'You are an API that analyzes the core intent of the text. You provide your answer in a .JSON format in the following structure: { "intent": descriptive verb for intent } You only answer with the .JSON object. You do not provide any reasoning why you did it that way. The intent represents the one intent you extracted during your analysis. If you can not extract the intent with a probability of 70% or more, you specify it with "unknown" in your response.'
        }

        "IntentAndSubjectAnalysis" {
            $assistantReply = @{ 
                intent = "purchase"
                topic = "bananas"
            }
            $instructor = 'You are an API that analyzes the core intent of the text and the subject the the intent wants to act upon. You provide your answer in a .JSON format in the following structure: { "intent": "descriptive verb for intent", "subject": "bananas" } You only answer with the .JSON object. You do not provide any reasoning why you did it that way. The intent represents the one intent you extracted during your analysis. The subject is the thing the intent wants to act upon (what does some want to buy? want information do they want?). If you can not extract the intent and or subject with a probability of 70% or more, you specify it with "unknown" in your response.'
        }

        default {
            throw "Invalid mode parameter. Allowed values are 'Chat', 'SentimentAndTickerAnalysis', and 'SentimentAnalysis'."
        }
    }

    [System.Collections.ArrayList]$promptToReturn = @(
        @{
            role = "system"
            content = $instructor
        },
        @{
            role = "assistant"
            content = $assistantReply
        }
    )
    
    return [System.Collections.ArrayList]$promptToReturn
}

function New-OpenAICompletionConversation {
    param (
        [Parameter(Mandatory=$false)]      
        [ValidateSet("Chat", "SentimentAndTickerAnalysis","SentimentAnalysis","IntentAnalysis","IntentAndSubjectAnalysis")]
        [System.Object]$Character,              # The character to use. If specified, do not add instructor and assistantReply
        [Parameter(Mandatory=$true)]  
        [string]$query,                         # The user's query to add to the prompt.
        [Parameter(Mandatory=$true)]  
        [string]$APIKey,                        # API key for ChatGPT.
        [Parameter(Mandatory=$false)] 
        $instructor = "You are a helpful AI. You answer as concisely as possible.",
        [Parameter(Mandatory=$false)] 
        $assistantReply = "Hello! I'm a ChatGPT-3.5 Model. How can I help you?",
        [Parameter(Mandatory=$false)]
        [string]$model = "gpt-3.5-turbo",       # The model to use from the endpoint.
        [Parameter(Mandatory=$false)]
        [string]$stop = "\n",                   # The stop instructor for the model.
        [Parameter(Mandatory=$false)]
        [double]$temperature = 0.4,             # The temperature value to use for sampling.
        [Parameter(Mandatory=$false)]
        [int]$max_tokens = 900,                 # The maximum number of tokens to generate in the response.
        [Parameter(Mandatory=$false)]
        [string]$filePath,                      # An array of previous messages in the conversation.
        [Parameter(Mandatory=$false)]
        [bool]$ShowOutput = $false,           # The maximum number of tokens to generate in the response.
        [Parameter(Mandatory=$false)]
        [bool]$ShowTokenUsage = $false        # The maximum number of tokens to generate in the response.
    )

    Write-Verbose ("ShellGPT-New-OpenAICompletionConversation @ "+(Get-Date)+" | Initializing new conversation...")

    if ($Character -eq $null)
    {
        Write-Verbose ("ShellGPT-New-OpenAICompletionConversation @ "+(Get-Date)+" | Character is not provided.") 
        
        if ($filePath)
        {   
            Write-Verbose ("ShellGPT-New-OpenAICompletionConversation @ "+(Get-Date)+" | FilePath is provided: "+($filePath)) 

            [System.Collections.ArrayList]$promptForAPI = New-OpenAICompletionPrompt -query $query -instructor $instructor -role "user" -assistantReply $assistantReply -filePath $filePath -model $model
            Write-Verbose ("ShellGPT-New-OpenAICompletionConversation @ "+(Get-Date)+" | Prompt is: "+($promptForAPI | Out-String))  
        }
        else {
            Write-Verbose ("ShellGPT-New-OpenAICompletionConversation @ "+(Get-Date)+" | FilePath is not provided") 

            [System.Collections.ArrayList]$promptForAPI = New-OpenAICompletionPrompt -query $query -instructor $instructor -role "user" -assistantReply $assistantReply -model $model
            Write-Verbose ("ShellGPT-New-OpenAICompletionConversation @ "+(Get-Date)+" | Prompt is: "+($promptForAPI | Out-String))   
        }
    }
    else 
    {
        Write-Verbose ("ShellGPT-New-OpenAICompletionConversation @ "+(Get-Date)+" | Character is provided: "+$Character) 

        [System.Collections.ArrayList]$characterPrompt= Set-OpenAICompletionCharacter -mode $Character -instructor $instructor -assistantReply $assistantReply
        Write-Verbose ("ShellGPT-New-OpenAICompletionConversation @ "+(Get-Date)+" | Character prompt is: ") 
        If ($filePath)
        {
            Write-Verbose ("ShellGPT-New-OpenAICompletionConversation @ "+(Get-Date)+" | FilePath is provided: "+($filePath)) 

            [System.Collections.ArrayList]$promptForAPI = New-OpenAICompletionPrompt -query $query -role "user" -previousMessages $characterPrompt -filePath $filePath -model $model
            Write-Verbose ("ShellGPT-New-OpenAICompletionConversation @ "+(Get-Date)+" | Prompt is: "+($promptForAPI | Out-String))  
        }

        else {
            Write-Verbose ("ShellGPT-New-OpenAICompletionConversation @ "+(Get-Date)+" | FilePath is not provided") 

            [System.Collections.ArrayList]$promptForAPI = New-OpenAICompletionPrompt -query $query -role "user" -previousMessages $characterPrompt -model $model
            Write-Verbose ("ShellGPT-New-OpenAICompletionConversation @ "+(Get-Date)+" | Prompt is: "+($promptForAPI | Out-String))   
        }
        
    }
    
    Write-Verbose ("ShellGPT-New-OpenAICompletionConversation @ "+(Get-Date)+" | Calling OpenAI Completion API with prompt...") 
    [System.Collections.ArrayList]$promptToReturn = Invoke-OpenAICompletion -Prompt $promptForAPI -APIKey $APIKey -temperature $temperature -max_tokens $max_tokens -model $model -stop $stop -ShowTokenUsage $ShowTokenUsage -ShowOutput $ShowOutput
        
    return [System.Collections.ArrayList]$promptToReturn
}

function Add-OpenAICompletionMessageToConversation {
    param (
        [Parameter(Mandatory=$true)]  
        [string]$query,                         # The user's query to be added to the conversation.
        [Parameter(Mandatory=$true)]  
        [System.Collections.ArrayList]$previousMessages,       # An array of previous messages in the conversation.
        [Parameter(Mandatory=$true)]    
        [string]$APIKey,                        # API key for ChatGPT
        [Parameter(Mandatory=$false)]
        [string]$model = "gpt-3.5-turbo",       # The model to use from the endpoint.
        [Parameter(Mandatory=$false)]
        [string]$stop = "\n",                   # The stop instructor for the model.
        [Parameter(Mandatory=$false)]
        [double]$temperature = 0.4,             # The temperature value to use for sampling.
        [Parameter(Mandatory=$false)]
        [int]$max_tokens = 900,                  # The maximum number of tokens to generate in the response.
        [Parameter(Mandatory=$false)]
        [string]$filePath,                       # An array of previous messages in the conversation.
        [Parameter(Mandatory=$false)]
        [bool]$ShowOutput = $false,                    # The maximum number of tokens to generate in the response.
        [Parameter(Mandatory=$false)]
        [bool]$ShowTokenUsage = $false                 # The maximum number of tokens to generate in the response.
    )

    if ($filePath)
    {
        Write-Verbose ("ShellGPT-Add-OpenAICompletionMessageToConversation @ "+(Get-Date)+" | FilePath is provided: "+($filePath | Out-String))  
        [System.Collections.ArrayList]$prompt = New-OpenAICompletionPrompt -query $query -role "user" -previousMessages $previousMessages -filePath $filePath -model $model
    }
    else {
        Write-Verbose ("ShellGPT-Add-OpenAICompletionMessageToConversation @ "+(Get-Date)+" | FilePath is not provided")
        [System.Collections.ArrayList]$prompt = New-OpenAICompletionPrompt -query $query -role "user" -previousMessages $previousMessages -model $model
    }

    # Call the Invoke-ChatGPT function to get the response from the API.
    
    try {
        [System.Collections.ArrayList]$returnPromptFromAPI = Invoke-OpenAICompletion -prompt $prompt -APIKey $APIKey -temperature $temperature -max_tokens $max_tokens -model $model -stop $stop -ShowTokenUsage $ShowTokenUsage -ShowOutput $ShowOutput
    }
    catch {
        [System.Collections.ArrayList]$returnPromptFromAPI = $prompt }

    
    # Return the response from the API and the updated previous messages.
    return [System.Collections.ArrayList]$returnPromptFromAPI
    
}

function New-OpenAIEdit {
    param (
        [Parameter(Mandatory=$true)]
        [string]$query,                                    # The prompt to send to the API to act upon.
        [Parameter(Mandatory=$true)]
        [string]$APIKey,                                   # The API key to authenticate the request.
        [Parameter(Mandatory=$true)]
        [string]$instruction,                              # The instruction for the model like "Fix the grammar"
        [Parameter(Mandatory=$false)]
        [ValidateSet("text-davinci-edit-001", "code-davinci-edit-001")]
        [string]$model = "text-davinci-edit-001",         # The model to use from the endpoint.
        [Parameter(Mandatory=$false)]
        [double]$temperature = 0.4                        # The temperature value to use for sampling.
    )

    #Building Request for API
    $headers = @{
        "Content-Type" = "application/json"
        "Authorization" = "Bearer $APIKey"
    }

    $RequestBody = @{
        model = $model
        input = $query
        instruction = $instruction
        temperature= $temperature
    } 

    #Convert the whole Body to be JSON, so that API can interpret it
    $RequestBody = $RequestBody | ConvertTo-Json

    $RestMethodParameter=@{
        Method='Post'
        Uri ='https://api.openai.com/v1/edits'
        body=$RequestBody
        Headers=$Headers
    }

    try {
        #Call the OpenAI Edit API
        $APIresponse = Invoke-RestMethod @RestMethodParameter

        #Extract Textresponse from API response
        $convertedResponseForOutput = $APIresponse.choices.text

        $outputColor = "Green"
    }
    catch {
        # If there was an error, define an error message to the written.
        $errorToReport = $_.Exception.Message
        $errorDetails = $_.ErrorDetails.Message
        $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."

        $outputColor = "Red"
    }

    #Output the text response.
    write-host "EditAPI:"$convertedResponseForOutput -ForegroundColor $outputColor

    if ($errorDetails)
        {
            write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
            $convertedResponseForOutput = "Error. See above for details."
        }

    return $convertedResponseForOutput
}

function New-OpenAIImage {
    param (
        [Parameter(Mandatory=$true)]
        [string]$query,                     # The prompt to send to the API to act upon.
        [Parameter(Mandatory=$true)]
        [string]$APIKey,                    # The API key to authenticate the request.
        [Parameter(Mandatory=$false)]
        [int]$n = 1,                        # The temperature value to use for sampling.
        [Parameter(Mandatory=$false)]
        [ValidateSet("256x256", "512x512", "1024x1024")]
        [string]$size = "256x256"           # The model to use from the endpoint.
    )

    #Building Request for API
    $headers = @{
        "Content-Type" = "application/json"
        "Authorization" = "Bearer $APIKey"
    }

    $RequestBody = @{
        prompt = $query
        n = $n
        size = $size
    } 

    #Convert the whole Body to be JSON, so that API can interpret it
    $RequestBody = $RequestBody | ConvertTo-Json

    $RestMethodParameter=@{
        Method='Post'
        Uri ='https://api.openai.com/v1/images/generations'
        body=$RequestBody
        Headers=$Headers
    }

    try {
        #Call the OpenAI Edit API
        $APIresponse = Invoke-RestMethod @RestMethodParameter

        #Extract Textresponse from API response
        $convertedResponseForOutput = $APIresponse.data.url

        $outputColor = "Green"
    }
    catch {
        # If there was an error, define an error message to the written.
        $errorToReport = $_.Exception.Message
        $errorDetails = $_.ErrorDetails.Message
        $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."

        $outputColor = "Red"
    }

    if ($errorDetails)
        {
            write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
            $convertedResponseForOutput = "Error. See above for details."
        }

    return $convertedResponseForOutput
}

function Get-OpenAIModels {
    param (
        [Parameter(Mandatory=$true)]
        [string]$APIKey        # The API key to authenticate the request.
    )

    $uri = 'https://api.openai.com/v1/models'

    #Building Request for API
    $headers = @{
        "Content-Type" = "application/json"
        "Authorization" = "Bearer $APIKey"
    }

    $RestMethodParameter=@{
        Method='Get'
        Uri = $uri
        Headers=$Headers
    }

    try {
        #Call the OpenAI Edit API
        $APIresponse = Invoke-RestMethod @RestMethodParameter

        $convertedResponseForOutput = $APIresponse.data | Select-Object id, owned_by 
        
    }
    catch {
        # If there was an error, define an error message to the written.
        $errorToReport = $_.Exception.Message
        $errorDetails = $_.ErrorDetails.Message
        $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."
    }

    if ($errorDetails)
        {
            write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
            $convertedResponseForOutput = "Error. See above for details."
        } 

    return $convertedResponseForOutput
}

function Get-OpenAIModelById {
    param (
        [Parameter(Mandatory=$true, ParameterSetName='Retrieve')]
        [string]$ModelId,
        [Parameter(Mandatory=$true)]
        [string]$APIKey        # The API key to authenticate the request.
    )


    $uri = 'https://api.openai.com/v1/models/'+$ModelId
    
    #Building Request for API
    $headers = @{
        "Content-Type" = "application/json"
        "Authorization" = "Bearer $APIKey"
    }

    $RestMethodParameter=@{
        Method='Get'
        Uri = $uri
        Headers=$Headers
    }

    try {
        #Call the OpenAI Edit API
        $APIresponse = Invoke-RestMethod @RestMethodParameter

        $convertedResponseForOutput = $APIresponse
        
    }
    catch {
        # If there was an error, define an error message to the written.
        $errorToReport = $_.Exception.Message
        $errorDetails = $_.ErrorDetails.Message
        $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."
    }

    if ($errorDetails)
        {
            write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
            $convertedResponseForOutput = "Error. See above for details."
        } 

    return $convertedResponseForOutput
}

function Get-OpenAIFiles {
    param (
        [Parameter(Mandatory=$true)]
        [string]$APIKey        # The API key to authenticate the request.
    )

    #Building Request for API
    $ContentType = "application/json"
    $uri = 'https://api.openai.com/v1/files'
    $method = 'Get'
 
    $headers = @{
        "Content-Type" = $ContentType
        "Authorization" = "Bearer $APIKey"
    }
    $RestMethodParameter=@{
        Method=$method
        Uri =$uri
        body=$body
        Headers=$Headers
    }

    try {
        #Call the OpenAI Edit API
        $APIresponse = Invoke-RestMethod @RestMethodParameter
        $convertedResponseForOutput = $APIresponse.data | select-object id, purpose, filename, status, bytes
        #Extract Textresponse from API response
        
    }
    catch {
        # If there was an error, define an error message to the written.
        $errorToReport = $_.Exception.Message
        $errorDetails = $_.ErrorDetails.Message
        $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."
    }

    if ($errorDetails)
        {
            write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
            $convertedResponseForOutput = "Error. See above for details."
        } 

    return $convertedResponseForOutput 
}

function Get-OpenAIFileById {
    param (
        [Parameter(Mandatory=$true)]
        [string]$FileIdToRetrieve,
        [Parameter(Mandatory=$true)]
        [string]$APIKey        # The API key to authenticate the request.
    )

    #Building Request for API
    $ContentType = "application/json"
    $uri = 'https://api.openai.com/v1/files/'+$FileIdToRetrieve
    $method = 'Get'

    $headers = @{
        "Content-Type" = $ContentType
        "Authorization" = "Bearer $APIKey"
    }

    $RestMethodParameter=@{
        Method=$method
        Uri =$uri
        body=$body
        Headers=$Headers
    }

    try {
        #Call the OpenAI Edit API
        $APIresponse = Invoke-RestMethod @RestMethodParameter
        $convertedResponseForOutput = $APIresponse
        #Extract Textresponse from API response
        
    }
    catch {
        # If there was an error, define an error message to the written.
        $errorToReport = $_.Exception.Message
        $errorDetails = $_.ErrorDetails.Message
        $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."
    }

    if ($errorDetails)
        {
            write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
            $convertedResponseForOutput = "Error. See above for details."
        } 

    return $convertedResponseForOutput
    
}

function Get-OpenAIFileContent {
    param (
        [Parameter(Mandatory=$true)]
        [string]$FileIdToRetrieveContent,
        [Parameter(Mandatory=$true)]
        [string]$APIKey        # The API key to authenticate the request.
    )

    #Building Request for API
    $ContentType = "application/json"
    $uri = 'https://api.openai.com/v1/files/'+$FileIdToRetrieveContent+'/content'
    $method = 'Get'

    $headers = @{
        "Content-Type" = $ContentType
        "Authorization" = "Bearer $APIKey"
    }

    $RestMethodParameter=@{
        Method=$method
        Uri =$uri
        body=$body
        Headers=$Headers
    }

    try {
        #Call the OpenAI Edit API
        $APIresponse = Invoke-RestMethod @RestMethodParameter
        $convertedResponseForOutput = $APIresponse
        
    }
    catch {
        # If there was an error, define an error message to the written.
        $errorToReport = $_.Exception.Message
        $errorDetails = $_.ErrorDetails.Message
        $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."
    }

    if ($errorDetails)
        {
            write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
            $convertedResponseForOutput = "Error. See above for details."
        } 

    return $convertedResponseForOutput
    
}

function New-OpenAIFile {
    param (
        [Parameter(Mandatory=$true)]
        [string]$FileToUpload,                                          # The trainings file to upload. Provide the full path to it.
        [Parameter(Mandatory=$true)]            
        [string]$APIKey,                                                # The API key to authenticate the request.
        [Parameter(Mandatory=$false)]
        [string]$Purpose = "fine-tune"                                  # The purpose label for the file. The API currently expects the value "fine-tune" only.

    )
    
    # Read the file into a byte array
    $fileBytes = [System.IO.File]::ReadAllBytes($FileToUpload)

    # Create the multipart/form-data request body
    $body = [System.Net.Http.MultipartFormDataContent]::new()
    $fileContent = [System.Net.Http.ByteArrayContent]::new($fileBytes)
    $body.Add($fileContent, "file", [System.IO.Path]::GetFileName($FileToUpload))
    $body.Add([System.Net.Http.StringContent]::new($purpose), "purpose")

    $ContentType = "multipart/form-data"
    $uri = 'https://api.openai.com/v1/files'
    $method = 'Post'
    

    $headers = @{
        "Content-Type" = $ContentType
        "Authorization" = "Bearer $APIKey"
    }

    $RestMethodParameter=@{
        Method=$method
        Uri =$uri
        body=$body
        Headers=$Headers
    }

    try {
        #Call the OpenAI Edit API
        $APIresponse = Invoke-RestMethod @RestMethodParameter
        $convertedResponseForOutput = $APIresponse
        
    }
    catch {
        # If there was an error, define an error message to the written.
        $errorToReport = $_.Exception.Message
        $errorDetails = $_.ErrorDetails.Message
        $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."
    }

    if ($errorDetails)
        {
            write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
            $convertedResponseForOutput = "Error. See above for details."
        } 

    return $convertedResponseForOutput 
}

function Remove-OpenAIFile {
    param (
        [Parameter(Mandatory=$true)]
        [string]$FileIdToDelete,            # The Id of the File to delete
        [Parameter(Mandatory=$true)]
        [string]$APIKey                     # The API key to authenticate the request.
    )

    #Building Request for API
    $ContentType = "application/json"
    $uri = 'https://api.openai.com/v1/files/'+$FileIdToDelete
    $method = 'Delete'
    
    $headers = @{
        "Content-Type" = $ContentType
        "Authorization" = "Bearer $APIKey"
    }

    $RestMethodParameter=@{
        Method=$method
        Uri =$uri
        body=$body
        Headers=$Headers
    }

    try {
        #Call the OpenAI Edit API
        $APIresponse = Invoke-RestMethod @RestMethodParameter
        $convertedResponseForOutput = $APIresponse
        #Extract Textresponse from API response
        
    }
    catch {
        # If there was an error, define an error message to the written.
        $errorToReport = $_.Exception.Message
        $errorDetails = $_.ErrorDetails.Message
        $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."
    }

    if ($errorDetails)
        {
            write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
            $convertedResponseForOutput = "Error. See above for details."
        } 

    return $convertedResponseForOutput
}

function Get-OpenAIFineTuneJobs {
    param (
        [Parameter(Mandatory=$true)]
        [string]$APIKey        # The API key to authenticate the request.
    )

     #Building Request for API
    $uri = 'https://api.openai.com/v1/fine-tunes'
    $method = 'Get'
     
    $headers = @{
         "Content-Type" = "application/json"
         "Authorization" = "Bearer $APIKey"
     }

     $RestMethodParameter=@{
         Method=$method
         Uri =$uri
         body=$RequestBody
         Headers=$Headers
     }
 
     try {
        #Call the OpenAI Edit API
        $APIresponse = Invoke-RestMethod @RestMethodParameter
        $convertedResponseForOutput = $APIresponse.data 
        
        #Extract Textresponse from API response
         
     }
     catch {
         # If there was an error, define an error message to the written.
         $errorToReport = $_.Exception.Message
         $errorDetails = $_.ErrorDetails.Message
         $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."
     }
 
     if ($errorDetails)
         {
             write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
             $convertedResponseForOutput = "Error. See above for details."
         } 
 
     return $convertedResponseForOutput 
}

function Get-OpenAIFineTuneJobById {
    param (
        [Parameter(Mandatory=$true)]
        [string]$FineTuneId,                # The Id of the FineTuneJob you want to get details on.
        [Parameter(Mandatory=$true)]
        [string]$APIKey                     # The API key to authenticate the request.
    )

    #Building Request for API
    $uri = 'https://api.openai.com/v1/fine-tunes/'+$FineTuneId
    $method = 'Get'
    $headers = @{
         "Content-Type" = "application/json"
         "Authorization" = "Bearer $APIKey"
    }
    
     $RestMethodParameter=@{
         Method=$method
         Uri =$uri
         body=$RequestBody
         Headers=$Headers
     }
 
     try {
         #Call the OpenAI Edit API
         $APIresponse = Invoke-RestMethod @RestMethodParameter
         $convertedResponseForOutput = $APIresponse
     
         #Extract Textresponse from API response
         
     }
     catch {
         # If there was an error, define an error message to the written.
         $errorToReport = $_.Exception.Message
         $errorDetails = $_.ErrorDetails.Message
         $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."
     }
 
     if ($errorDetails)
         {
             write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
             $convertedResponseForOutput = "Error. See above for details."
         } 
 
     return $convertedResponseForOutput
    
}

function Get-OpenAIFineTuneEvents {
    param (
        [Parameter(Mandatory=$true)]            
        [string]$FineTuneIdToListEvents,                # The Id of the FineTuneJob you want to get the events for.
        [Parameter(Mandatory=$true)]
        [string]$APIKey                                 # The API key to authenticate the request.
    )

    #Building Request for API
    
    $uri = 'https://api.openai.com/v1/fine-tunes/'+$FineTuneIdToListEvents+'/events'
    $method = 'Get'
 
    $headers = @{
        "Content-Type" = "application/json"
        "Authorization" = "Bearer $APIKey"
    }

    $RestMethodParameter=@{
        Method=$method
        Uri =$uri
        body=$RequestBody
        Headers=$Headers
    }
 
     try {
        #Call the OpenAI Edit API
        $APIresponse = Invoke-RestMethod @RestMethodParameter
        $convertedResponseForOutput = $APIresponse
         
     }
     catch {
         # If there was an error, define an error message to the written.
         $errorToReport = $_.Exception.Message
         $errorDetails = $_.ErrorDetails.Message
         $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."
     }
 
     if ($errorDetails)
         {
             write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
             $convertedResponseForOutput = "Error. See above for details."
         } 
 
     return $convertedResponseForOutput
}

function Remove-OpenAIFineTuneModel {
    param (
        [Parameter(Mandatory=$true)]
        [string]$ModelToDelete,                 # The Name of the Model you want to delete.
        [Parameter(Mandatory=$true)]
        [string]$APIKey                         # The API key to authenticate the request.
    )

    $uri = 'https://api.openai.com/v1/models/'+$ModelToDelete
    $method = 'Delete'
     
    $headers = @{
        "Content-Type" = "application/json"
        "Authorization" = "Bearer $APIKey"
    }

    $RestMethodParameter=@{
        Method=$method
        Uri =$uri
        body=$RequestBody
        Headers=$Headers
     }
 
     try {
        #Call the OpenAI Edit API
        $APIresponse = Invoke-RestMethod @RestMethodParameter
        $convertedResponseForOutput = $APIresponse
         
     }
     catch {
        # If there was an error, define an error message to the written.
        $errorToReport = $_.Exception.Message
        $errorDetails = $_.ErrorDetails.Message
        $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."
    }
 
    if ($errorDetails)
        {
            write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
            $convertedResponseForOutput = "Error. See above for details."
        } 
 
     return $convertedResponseForOutput
}

function Stop-OpenAIFineTuneJob {
    param (
        [Parameter(Mandatory=$true, ParameterSetName='Cancel')]
        [string]$FineTuneIdToCancel,            # The Id of the FineTuneJob you want to cancel.
        [Parameter(Mandatory=$true)]
        [string]$APIKey                         # The API key to authenticate the request.
    )

    #Building Request for API
     
    $uri = 'https://api.openai.com/v1/fine-tunes/'+$FineTuneIdToCancel+'/cancel'
    $method = 'Post'
    
    $headers = @{
        "Content-Type" = "application/json"
        "Authorization" = "Bearer $APIKey"
    }

    $RestMethodParameter=@{
        Method=$method
        Uri =$uri
        body=$RequestBody
        Headers=$Headers
    }
 
    try {
        #Call the OpenAI Edit API
        $APIresponse = Invoke-RestMethod @RestMethodParameter
        $convertedResponseForOutput = $APIresponse
         
     }
     catch {
        # If there was an error, define an error message to the written.
        $errorToReport = $_.Exception.Message
        $errorDetails = $_.ErrorDetails.Message
        $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."
     }
 
     if ($errorDetails)
         {
             write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
             $convertedResponseForOutput = "Error. See above for details."
         } 
 
     return $convertedResponseForOutput
    
}

function New-OpenAIFineTuneJob {
    param (
        [Parameter(Mandatory=$true)]
        [string]$trainingFileId,                # The Id of the trainings file (.jsonl) you want to create a fine tuned model for.
        [Parameter(Mandatory=$true)]
        [string]$APIKey,                        # The API key to authenticate the request.
        [Parameter(Mandatory=$false)]
        [string]$model,                         # The name of the model you want to create a fine-tuned version of.
        [Parameter(Mandatory=$false)]
        [integer]$n_epochs,                      # The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.
        [Parameter(Mandatory=$false)]
        [string]$suffix,                        # A string of up to 40 characters that will be added to your fine-tuned model name
        [Parameter(Mandatory=$false)]
        [string]$validation_fileId,             # The ID of an uploaded file that contains validation data.
        [Parameter(Mandatory=$false)]
        [int]$batch_size,                       # The batch size to use for training. The batch size is the number of training examples used to train a single forward and backward pass.
        [Parameter(Mandatory=$false)]
        [double]$learning_rate_multiplier,      # The learning rate multiplier to use for training. The fine-tuning learning rate is the original learning rate used for pretraining multiplied by this value.
        [Parameter(Mandatory=$false)]
        [double]$prompt_loss_weight,            # The weight to use for loss on the prompt tokens. This controls how much the model tries to learn to generate the prompt (as compared to the completion which always has a weight of 1.0), and can add a stabilizing effect to training when completions are short.
        [Parameter(Mandatory=$false)]
        [bool]$compute_classification_metrics,  # If set, we calculate classification-specific metrics such as accuracy and F-1 score using the validation set at the end of every epoch
        [Parameter(Mandatory=$false)]
        [integer]$classification_n_classes,     # The number of classes in a classification task.
        [Parameter(Mandatory=$false)]
        [string]$classification_positive_class, # The positive class in binary classification.
        [Parameter(Mandatory=$false)]
        [array]$classification_betas           # If this is provided, we calculate F-beta scores at the specified beta values. The F-beta score is a generalization of F-1 score. This is only used for binary classification.
    )

    #Building Request for API
    $uri = 'https://api.openai.com/v1/fine-tunes'
    $method = 'Post'

    $RequestBody = @{
        training_file = $trainingFileId
    } 

    if ($model)
    {
        $RequestBody.Add("model", $model)
    }

    if ($epochs)
    {
        $RequestBody.Add("n_epochs", $n_epochs)
    }

    if ($suffix)
    {
        $RequestBody.Add("suffix", $suffix)
    }

    if ($validation_fileId)
    {
        $RequestBody.Add("validation_file", $validation_fileId)
    }

    if ($batch_size)
    {
        $RequestBody.Add("batch_size", $batch_size)
    }

    if ($learning_rate_multiplier)
    {
        $RequestBody.Add("learning_rate_multiplier", $learning_rate_multiplier)
    }

    if ($prompt_loss_weight)
    {
        $RequestBody.Add("prompt_loss_weight", $prompt_loss_weight)
    }

    if ($compute_classification_metrics)
    {
        $RequestBody.Add("compute_classification_metrics", $compute_classification_metrics)
    }

    if ($classification_n_classes)
    {
        $RequestBody.Add("classification_n_classes", $classification_n_classess)
    }

    if ($classification_positive_classes)
    {
        $RequestBody.Add("classification_positive_classes", $classification_positive_classes)
    }

    if ($classification_betas)
    {
        $RequestBody.Add("classification_betas", $classification_betass)
    }
    
    $RequestBody = $RequestBody | ConvertTo-Json
    
    $headers = @{
        "Content-Type" = "application/json"
        "Authorization" = "Bearer $APIKey"
    }
    $RestMethodParameter=@{
        Method=$method
        Uri =$uri
        body=$RequestBody
        Headers=$Headers
     }
 
     try {
        #Call the OpenAI Edit API
        $APIresponse = Invoke-RestMethod @RestMethodParameter
        $convertedResponseForOutput = $APIresponse
         
     }
     catch {
        # If there was an error, define an error message to the written.
        $errorToReport = $_.Exception.Message
        $errorDetails = $_.ErrorDetails.Message
        $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."
     }
 
     if ($errorDetails)
        {
            write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
            $convertedResponseForOutput = "Error. See above for details."
        } 
 
     return $convertedResponseForOutput 
}

function New-OpenAIFineTuneTrainingFile {
    param (
        [Parameter(Mandatory=$true)]
        [string]$prompt,                # The prompt to create a training file with. This will be the first prompt.
        [Parameter(Mandatory=$true)]
        [string]$completion,            # The completion for the first prompt.
        [Parameter(Mandatory=$true)]
        [string]$Path                   # the full path to the .JSONL file to be created and stored.
    )

    $objects = @(
        @{ prompt = $prompt ; completion = $completion }
    )
    $jsonStrings = $objects | ForEach-Object { $_ | ConvertTo-Json -Compress }

    if ($Path.EndsWith(".jsonl"))
    {
        $NewName = $Path
    }
    else {
        $NewName = ($path+".jsonl")
    }
    
    try {
        New-Item -Name $NewName -ItemType File -Force
        $jsonStrings | Out-File -FilePath $NewName -Encoding utf8 -Append -Force
    }
    catch {
        $errorToReport = $_.Exception.Message
        $errorDetails = $_.ErrorDetails.Message
        $message = "Unable to handle Error: "+$errorToReport+" See Error details below."

        write-host "Error:"$message -ForegroundColor $outputColor

        if ($errorDetails)
            {
                write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
            }        
    }

}

function Import-OpenAIPromptFromJson {
    param (
        [Parameter(Mandatory=$true)]    
        [string]$Path                   #Required. The file path to the JSON file containing the prompt. This parameter is mandatory.
    )

    $promptJson = Get-Content -Path $Path -Raw
    $prompt = $promptJson | ConvertFrom-Json

    return $prompt
}

function Export-OpenAIPromptToJson {
    param (
        [Parameter(Mandatory=$true)]    
        [string]$Path,                 #The file path to the JSON file containing the prompt. This parameter is mandatory.
        [Parameter(Mandatory=$true)]    
        [System.Object]$prompt         #The prompt (System.Object) to export to a .JSON file.
    )

    $prompt | ConvertTo-Json | Out-File -Encoding utf8 -FilePath $path
    

    return $prompt
}

function New-OpenAIEmbedding {
    param (
        [Parameter(Mandatory=$true)]
        [string]$APIKey,                                 # The API key to authenticate the request.
        [Parameter(Mandatory=$false)]            
        [string]$text,                                   # The Id of the FineTuneJob you want to get the events for.
        [Parameter(Mandatory=$false)]            
        [string]$Model = "text-embedding-ada-002"        # The Id of the FineTuneJob you want to get the events for.
    )

    #Building Request for API
    $uri = 'https://api.openai.com/v1/embeddings'
    $method = 'Post'
 
    $headers = @{
        "Content-Type" = "application/json"
        "Authorization" = "Bearer $APIKey"
    }

    $RequestBody = @{
        input = $text
        model = $Model
    } 

    #Convert the whole Body to be JSON, so that API can interpret it
    $RequestBody = $RequestBody | ConvertTo-Json

    $RestMethodParameter=@{
        Method=$method
        Uri =$uri
        body=$RequestBody
        Headers=$Headers
    }
 
     try {
        #Call the OpenAI Edit API
        $APIresponse = Invoke-RestMethod @RestMethodParameter
        $convertedResponseForOutput = $APIresponse
         
     }
     catch {
         # If there was an error, define an error message to the written.
         $errorToReport = $_.Exception.Message
         $errorDetails = $_.ErrorDetails.Message
         $convertedResponseForOutput = "Unable to handle Error: "+$errorToReport+" See Error details below. Retry query. If the error persists, consider exporting your current prompt and to continue later."
     }
 
     if ($errorDetails)
         {
             write-host "ErrorDetails:"$errorDetails -ForegroundColor "Red"
             $convertedResponseForOutput = "Error. See above for details."
         } 
 
     return $convertedResponseForOutput
    
}


function Convert-PDFtoText {
    param(
        [Parameter(Mandatory=$true)]
        [string]$filePath,
        [Parameter(Mandatory=$true)]
        [ValidateSet("txt", "csv", "json")]
        [string]$TypeToExport
    )

    Write-Verbose ("ShellGPT-Convert-PDFtoText @ "+(Get-Date)+" | Convertig PDF to Text: "+($filepath)) 
    
    #Need to generalize this. Make .dll part of module.
    
    Add-Type -Path "C:\ps\itextsharp.dll"
    Write-Verbose ("ShellGPT-Convert-PDFtoText @ "+(Get-Date)+" | Loaded itextsharp.dll") 
    $pdf = New-Object iTextSharp.text.pdf.pdfreader -ArgumentList $filePath
    Write-Verbose ("ShellGPT-Convert-PDFtoText @ "+(Get-Date)+" | PDF was found.") 


    $text = ""
    for ($page = 1; $page -le $pdf.NumberOfPages; $page++){
        Write-Verbose ("ShellGPT-Convert-PDFtoText @ "+(Get-Date)+" | Parsing text...") 
        $text+=([iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($pdf,$page))
    }    
    $pdf.Close()


    if ($text -eq "" -or $text -eq " " -or $text -eq $null)
    {
        Write-Host ("ShellGPT-Convert-PDFtoText @ "+(Get-Date)+" | PDF was found, but it looks like its empty. Either it really has no text or it consist only of pictures or a Scan. ShellGPT does not have OCR. The prompt will not have any additional content in it.") -ForegroundColor Red
    }

    Write-Verbose ("ShellGPT-Convert-PDFtoText @ "+(Get-Date)+" | Done parsing PDF. Preparing export to .txt") 

    if ($filePath.Contains("\"))
    {
        Write-Verbose ("ShellGPT-Convert-PDFtoText @ "+(Get-Date)+" | Filepath is the whole path. Splitting it up...") 
        $filename = $filepath.split("\")[($filepath.split("\")).count-1]
        $basenamefile = ($filename.Split(".pdf"))[0]
        $Outputfolder = ($filepath.split($basenamefile))[0]
    }
    else {
        Write-Verbose ("ShellGPT-Convert-PDFtoText @ "+(Get-Date)+" | Filepath is only filename. Indicates run in the dir where the script was launched") 
        $filename = $filePath
        $basenamefile = ($filename.Split(".pdf"))[0]
        $Outputfolder = ""
    }

    switch ($TypeToExport) {
        "txt" {
            $exportEnding = ".txt"
        }
        "csv" {
            $exportEnding = ".csv"

        }
        "json" {
            $exportEnding = ".json"

        }
        "jsonl" {
            $exportEnding = ".jsonl"
        }
        default {
            Write-Host ("ShellGPT-Convert-PDFtoText @ "+(Get-Date)+" | Invalid option for Switch.")
        }
    }

    Write-Verbose ("ShellGPT-Convert-PDFtoText @ "+(Get-Date)+" | Export type is: "+($exportEnding)) 

    $OutputPath = $Outputfolder+$basenamefile+$exportEnding

    Write-Verbose ("ShellGPT-Convert-PDFtoText @ "+(Get-Date)+" | Outputpath is: "+($OutputPath)) 

    $text | Out-File $OutputPath -Force
    return $OutputPath
}

function Get-ShellGPTHelpMessage {
    Write-Host ("ShellGPT @ "+(Get-Date)+" | To include a file for the model to reference in the prompt, use the following notation 'file | pathtofile | instruction' in the query.") -ForegroundColor DarkMagenta
    Write-Host ("ShellGPT @ "+(Get-Date)+" | Supported file types are: .txt, .pdf, .csv, .json") -ForegroundColor DarkMagenta
    Write-Host ("-------------------------------------------------------------------------------------------------------------------------") -ForegroundColor DarkGray
    Write-Host ("ShellGPT @ "+(Get-Date)+" | Example: file | C:\Users\Yanik\test.txt | Summarize this:") -ForegroundColor DarkGray
    Write-Host ("-------------------------------------------------------------------------------------------------------------------------") -ForegroundColor DarkGray
    Write-Host ("ShellGPT @ "+(Get-Date)+" | This will summarize the content in the file 'test.txt'") -ForegroundColor DarkGray
    Write-Host ("ShellGPT @ "+(Get-Date)+" | Example: file | C:\Users\Yanik\test.pdf | Summarize this:") -ForegroundColor DarkGray
    Write-Host ("ShellGPT @ "+(Get-Date)+" | This will create a .txt file with the content of the .PDF, read it and summarize the content in the file 'test.txt'") -ForegroundColor DarkGray
    Write-Host ("-------------------------------------------------------------------------------------------------------------------------") -ForegroundColor DarkGray
    Write-Host ("ShellGPT @ "+(Get-Date)+" | There are a few other commands available. ") -ForegroundColor DarkMagenta
    Write-Host ("-------------------------------------------------------------------------------------------------------------------------") -ForegroundColor DarkGray
    Write-Host ("ShellGPT @ "+(Get-Date)+" | Start a new conversation: ") -ForegroundColor DarkGray
    Write-Host ("ShellGPT @ "+(Get-Date)+" | newconvo | ") -ForegroundColor DarkGray
    Write-Host ("-------------------------------------------------------------------------------------------------------------------------") -ForegroundColor DarkGray
    Write-Host ("ShellGPT @ "+(Get-Date)+" | Export the current prompt: ") -ForegroundColor DarkGray
    Write-Host ("ShellGPT @ "+(Get-Date)+" | export | ") -ForegroundColor DarkGray
    Write-Host ("-------------------------------------------------------------------------------------------------------------------------") -ForegroundColor DarkGray

    Write-Host ("ShellGPT @ "+(Get-Date)+" | Stop ShellGPT: ") -ForegroundColor DarkGray
    Write-Host ("ShellGPT @ "+(Get-Date)+" | quit | ") -ForegroundColor DarkGray
    Write-Host ("-------------------------------------------------------------------------------------------------------------------------") -ForegroundColor DarkGray

    Write-Host ("ShellGPT @ "+(Get-Date)+" | Stop ShellGPT and export the prompt: ") -ForegroundColor DarkGray
    Write-Host ("-------------------------------------------------------------------------------------------------------------------------") -ForegroundColor DarkGray
}

function Start-ShellGPT {
    param (
        [Parameter(Mandatory=$true)]    
        [string]$APIKey,                        
        [Parameter(Mandatory=$false)]
        [string]$model = "gpt-3.5-turbo",       
        [Parameter(Mandatory=$false)]
        [string]$stop = "\n",                    
        [Parameter(Mandatory=$false)]
        [double]$temperature = 0.4,             
        [Parameter(Mandatory=$false)]
        [int]$max_tokens = 900,                 
        [bool]$ShowOutput = $false,                    
        [Parameter(Mandatory=$false)]
        [bool]$ShowTokenUsage = $false,                 
        [Parameter(Mandatory=$false)]
        [string]$instructor = "You are a helpful AI. You answer as concisely as possible.",
        [Parameter(Mandatory=$false)] 
        [string]$assistantReply = "Hello! I'm a ChatGPT-3.5 Model. How can I help you?"
    )

    Write-Verbose ("ShellGPT @ "+(Get-Date)+" | Initializing... ") 
    Write-Verbose ("ShellGPT @ "+(Get-Date)+" | Used Model is : "+($model)) 
    Write-Verbose ("ShellGPT @ "+(Get-Date)+" | Used stop instructor is : "+($stop))
    Write-Verbose ("ShellGPT @ "+(Get-Date)+" | Used temperature is: "+($temperature))  
    Write-Verbose ("ShellGPT @ "+(Get-Date)+" | Used max_tokens is: "+($max_tokens)) 

    $contiueConversation = $(Write-Host ("ShellGPT @ "+(Get-Date)+" | Do you want to restore an existing conversation? (enter 'y' or 'yes'): ") -ForegroundColor yellow -NoNewLine; Read-Host) 

    if ($contiueConversation  -eq "y" -or $contiueConversation -eq "yes")
    {
        Get-ShellGPTHelpMessage
        $importPath = $(Write-Host ("ShellGPT @ "+(Get-Date)+" | Provide the full path to the prompt*.json file you want to continue the conversation on: ") -ForegroundColor yellow -NoNewLine; Read-Host) 
        [System.Collections.ArrayList]$importedPrompt = Import-OpenAIPromptFromJson -Path $importPath
        [System.Collections.ArrayList]$previousMessages = $importedPrompt
    }
    else 
    {
        # Display a welcome message and instructions for stopping the conversation.
        Get-ShellGPTHelpMessage

        # Initialize the previous messages array.
        [System.Collections.ArrayList]$previousMessages = @()

        $option = Read-Host ("ShellGPT @ "+(Get-Date)+" | Select the Character the Model should assume:`n1: Chat`n2: Ticker and Sentiment Analysis`n3: Sentiment Analysis`n4: Intent Analysis`n5: Intent & Topic Analysis`nShellGPT @ "+(Get-Date)+" | Enter the according number of the character you'd like")

        switch ($option) {
            "1" {
                $Character = "Chat"
            }
            "2" {
                $Character = "SentimentAndTickerAnalysis"
            }
            "3" {
                $Character = "SentimentAnalysis"
            }
            "4" {
                $Character = "IntentAnalysis"
            }
            "5" {
                $Character = "IntentAndSubjectAnalysis"
            }

            default {
                $Character = "Chat"
                Write-Host ("ShellGPT @ "+(Get-Date)+" | Invalid option selected.") -ForegroundColor Yellow 
            }
        }
        
        Write-Host ("ShellGPT @ "+(Get-Date)+" | Selected Character is: "+($Character)) -ForegroundColor Yellow 
        $InitialQuery = Read-Host ("ShellGPT @ "+(Get-Date)+" | Your query for ChatGPT or commands for ShellGPT")

        Write-Verbose ("ShellGPT @ "+(Get-Date)+" | InitialQuery is: "+($InitialQuery)) 

        switch -Regex ($InitialQuery) {
            "^file \|.*" {
                Write-Verbose ("ShellGPT @ "+(Get-Date)+" | InitialQuery is File command")

                $filePath = (($InitialQuery.split("|"))[1]).TrimStart(" ")
                $filepath = $filePath.TrimEnd(" ")
                $filePath = $filePath.Replace('"','')
                $FileQuery = (($InitialQuery.split("|"))[2]).TrimStart(" ")

                Write-Verbose ("ShellGPT @ "+(Get-Date)+" | Extracted FilePath from Query is: "+($filePath)) 
                Write-Verbose ("ShellGPT @ "+(Get-Date)+" | Extracted Query is: "+($FileQuery))
                Write-Verbose ("ShellGPT @ "+(Get-Date)+" | Starting Conversation...") 

                [System.Collections.ArrayList]$conversationPrompt = New-OpenAICompletionConversation -Character $Character -query $FileQuery -instructor $instructor -APIKey $APIKey -temperature $temperature -max_tokens $max_tokens -model $model -stop $stop -filePath $filePath -ShowTokenUsage $ShowTokenUsage -ShowOutput $ShowOutput -assistantReply $assistantReply
                Write-Host ("CompletionAPI @ "+(Get-Date)+" | "+($conversationPrompt[($conversationPrompt.count)-1].content)) -ForegroundColor Green

                if ($InitialQuery.Contains("| out |"))
                    {
                        $filePathOut = (($InitialQuery.split("|"))[4]).TrimStart(" ")
                        $filePathOut = $filePathOut.TrimEnd(" ")
                        Write-Host ("ShellGPT @ "+(Get-Date)+" | Writing output to file: "+($filePathOut)) -ForegroundColor Yellow

                        try {
                            ($conversationPrompt[($conversationPrompt.count)-1].content) | Out-File -Encoding utf8 -FilePath $filePathOut
                            Write-Host ("ShellGPT @ "+(Get-Date)+" | Successfully created file with output at: "+($filePathOut)) -ForegroundColor Green
    
                        }
                        catch {
                            Write-Host ("ShellGPT @ "+(Get-Date)+" | Could not write output to file: "+($filePathOut)) -ForegroundColor Red
                        }
                    }
            }
            "^quit \|.*" {
                Write-Host ("ShellGPT @ "+(Get-Date)+" | ShellGPT is exiting now...") -ForegroundColor Yellow
                Start-Sleep 5
                exit
            }
            "^export \|.*" {
                Write-Host ("ShellGPT @ "+(Get-Date)+" | ShellGPT has nothing to export :(") -ForegroundColor Yellow
            }
            "^\s*$" {
                Write-Host ("ShellGPT @ "+(Get-Date)+" | You have not provided any input. Will not send this query to the CompletionAPI") -ForegroundColor Yellow
                [System.Collections.ArrayList]$conversationPrompt = Set-OpenAICompletionCharacter $Character
            }
            default {

                if ($InitialQuery.contains("| out |"))
                {
                    $filePathOut = (($InitialQuery.split("|"))[2]).TrimStart(" ")
                    $filePathOut = $filePathOut.TrimEnd(" ")
                    $InitialQuery = (($InitialQuery.split("|"))[0]).TrimStart(" ")
                    $InitialQuery = $InitialQuery.TrimEnd(" ")

                    [System.Collections.ArrayList]$conversationPrompt = New-OpenAICompletionConversation -Character $Character -query $InitialQuery -instructor $instructor -APIKey $APIKey -temperature $temperature -max_tokens $max_tokens -model $model -stop $stop -ShowTokenUsage $ShowTokenUsage -ShowOutput $ShowOutput -assistantReply $assistantReply
                    Write-Host ("ShellGPT @ "+(Get-Date)+" | Writing output to file: "+($filePathOut)) -ForegroundColor Yellow
    
                    try {
                        ($conversationPrompt[($conversationPrompt.count)-1].content) | Out-File -Encoding utf8 -FilePath $filePathOut
                        Write-Host ("ShellGPT @ "+(Get-Date)+" | Successfully created file with output at: "+($filePathOut)) -ForegroundColor Green
    
                    }
                    catch {
                        Write-Host ("ShellGPT @ "+(Get-Date)+" | Could not write output to file: "+($filePathOut)) -ForegroundColor Red
                    }
                }
                else
                {
                    [System.Collections.ArrayList]$conversationPrompt = New-OpenAICompletionConversation -Character $Character -query $InitialQuery -instructor $instructor -APIKey $APIKey -temperature $temperature -max_tokens $max_tokens -model $model -stop $stop -ShowTokenUsage $ShowTokenUsage -ShowOutput $ShowOutput -assistantReply $assistantReply
                }    
                Write-Host ("CompletionAPI @ "+(Get-Date)+" | "+($conversationPrompt[($conversationPrompt.count)-1].content)) -ForegroundColor Green
            }
        }

        [System.Collections.ArrayList]$previousMessages = $conversationPrompt
    }

    # Initialize the continue variable.
    $continue = $true

    # Loop until the user stops the conversation.
    while ($continue) {

        # Prompt the user to enter their query for ChatGPT or commands for ShellGPT.
        $userQuery = Read-Host ("ShellGPT @ "+(Get-Date)+" | Your query for ChatGPT or commands for ShellGPT")

        switch -Regex ($userQuery) {
            "^file \|.*" {
                Write-Verbose ("ShellGPT @ "+(Get-Date)+" | InitialQuery is File command")

                $filePath = (($userQuery.split("|"))[1]).TrimStart(" ")
                $filepath = $filePath.TrimEnd(" ")
                $filePath = $filePath.Replace('"','')

                $FileQuery = (($userQuery.split("|"))[2]).TrimStart(" ")

                Write-Verbose ("ShellGPT @ "+(Get-Date)+" | Extracted FilePath from Query is: "+($filePath)) 
                Write-Verbose ("ShellGPT @ "+(Get-Date)+" | Extracted Query is: "+($FileQuery))

                [System.Collections.ArrayList]$conversationPrompt = New-OpenAICompletionConversation -Character $Character -query $FileQuery -instructor $instructor -APIKey $APIKey -temperature $temperature -max_tokens $max_tokens -model $model -stop $stop -filePath $filePath -ShowTokenUsage $ShowTokenUsage -ShowOutput $ShowOutput
                Write-Host ("CompletionAPI @ "+(Get-Date)+" | "+($conversationPrompt[($conversationPrompt.count)-1].content)) -ForegroundColor Green

                if ($userQuery.Contains("| out |"))
                {

                    $filePathOut = (($UserQuery.split("|"))[4]).TrimStart(" ")
                    $filePathOut = $filePathOut.TrimEnd(" ")
                    Write-Host ("ShellGPT @ "+(Get-Date)+" | Writing output to file: "+($filePathOut)) -ForegroundColor Yellow

                    try {
                        ($conversationPrompt[($conversationPrompt.count)-1].content) | Out-File -Encoding utf8 -FilePath $filePathOut
                        Write-Host ("ShellGPT @ "+(Get-Date)+" | Successfully created file with output at: "+($filePathOut)) -ForegroundColor Green

                    }
                    catch {
                        Write-Host ("ShellGPT @ "+(Get-Date)+" | Could not write output to file: "+($filePathOut)) -ForegroundColor Red
                    }
                    
                }

            }
            "^newconvo \|.*" {
                Start-ShellGPT -APIKey $APIKey -temperature $temperature -max_tokens $max_tokens -model $model -stop $stop
            }
            "^quit \|.*" {
                $exportBool = $(Write-Host ("ShellGPT @ "+(Get-Date)+" | Do you want to export the current prompt before exiting? Enter 'y' or 'yes': ") -ForegroundColor yellow -NoNewLine; Read-Host) 
                if ($exportBool -eq "y" -or $exportBool -eq "yes" -or $exportBool -eq "Y" -or $exportBool -eq "YES")
                {
                    $exportPath = $(Write-Host ("ShellGPT @ "+(Get-Date)+" | Provide the full path to the prompt*.json file that you want to export now and later continue the conversation on: ") -ForegroundColor yellow -NoNewLine; Read-Host) 
                    Write-Host ("ShellGPT @ "+(Get-Date)+" | ShellGPT exported the prompt to: "+($exportPath)) -ForegroundColor yellow
                }
                Write-Host ("ShellGPT @ "+(Get-Date)+" | ShellGPT is exiting now...") -ForegroundColor yellow
                Start-Sleep 5
                exit
            }
            "^export \|.*" {
                $exportPath = $(Write-Host ("ShellGPT @ "+(Get-Date)+" | Provide the full path to the prompt*.json file that you want to export now and later continue the conversation on: ") -ForegroundColor yellow -NoNewLine; Read-Host) 
                Export-OpenAIPromptToJson -Path $exportPath -prompt $previousMessages
                Write-Host ("ShellGPT @ "+(Get-Date)+" | ShellGPT exported the prompt to: "+($exportPath)) -ForegroundColor yellow
            }
            "^\s*$" {
                Write-Host ("ShellGPT @ "+(Get-Date)+" | You have not provided any input. Will not send this query to the CompletionAPI") -ForegroundColor Yellow
                [System.Collections.ArrayList]$conversationPrompt = Set-OpenAICompletionCharacter $Character
            }
            default {            
                if ($userQuery.contains("| out |"))
                {
                    $filePathOut = (($InitialQuery.split("|"))[2]).TrimStart(" ")
                    $filePathOut = $filePathOut.TrimEnd(" ")
                    $UserQuery = (($UserQuery.split("|"))[0]).TrimStart(" ")
                    $UserQuery = $UserQuery.TrimEnd(" ")

                    [System.Collections.ArrayList]$conversationPrompt = Add-OpenAICompletionMessageToConversation -query $userQuery -previousMessages $previousMessages -APIKey $APIKey -temperature $temperature -max_tokens $max_tokens -model $model -stop $stop -ShowTokenUsage $ShowTokenUsage -ShowOutput $ShowOutput

                    Write-Host ("ShellGPT @ "+(Get-Date)+" | Writing output to file: "+($filePathOut)) -ForegroundColor Yellow
    
                    try {
                        ($conversationPrompt[($conversationPrompt.count)-1].content) | Out-File -Encoding utf8 -FilePath $filePathOut
                        Write-Host ("ShellGPT @ "+(Get-Date)+" | Successfully created file with output at: "+($filePathOut)) -ForegroundColor Green
    
                    }
                    catch {
                        Write-Host ("ShellGPT @ "+(Get-Date)+" | Could not write output to file: "+($filePathOut)) -ForegroundColor Red
                    }

                }
                else
                {
                    [System.Collections.ArrayList]$conversationPrompt = Add-OpenAICompletionMessageToConversation -query $userQuery -previousMessages $previousMessages -APIKey $APIKey -temperature $temperature -max_tokens $max_tokens -model $model -stop $stop -ShowTokenUsage $ShowTokenUsage -ShowOutput $ShowOutput
                }

                Write-Host ("CompletionAPI @ "+(Get-Date)+" | "+($conversationPrompt[($conversationPrompt.count)-1].content)) -ForegroundColor Green
            }
        }

        [System.Collections.ArrayList]$previousMessages = $conversationPrompt
    }
}