Public/Invoke-PSAOAIDalle3.ps1

function Invoke-PSAOAIDalle3 {
    <#
    .SYNOPSIS
    This function interacts with Azure OpenAI Services.
 
    .DESCRIPTION
    The Invoke-PSAOAIDalle3 function sends a request to the Azure OpenAI Services and retrieves the response. It takes several parameters including the service name, prompt, model, size, response format, quality, style, user, API version, save path, image loops, n, and timeout in seconds.
 
    .PARAMETER serviceName
    The name of the service to interact with.
 
    .PARAMETER Prompt
    The prompt to be used.
 
    .PARAMETER model
    The model to be used. Default is 'dalle3'.
 
    .PARAMETER size
    The size of the output. Default is '1792x1024'.
 
    .PARAMETER response_format
    The format of the response. Default is 'url'.
 
    .PARAMETER quality
    The quality of the output. Default is 'hd'.
 
    .PARAMETER style
    The style of the output. Default is 'natural'.
 
    .PARAMETER user
    The user for the request.
 
    .PARAMETER ApiVersion
    The API version to be used. Default is '2023-12-01-preview'.
 
    .PARAMETER SavePath
    The path where the output will be saved. Default is the MyPictures folder.
 
    .PARAMETER ImageLoops
    The number of image loops. Default is 1.
 
    .PARAMETER n
    The number of outputs. Default is 1.
 
    .PARAMETER timeoutSec
    The timeout in seconds. Default is 60.
 
    .EXAMPLE
    Invoke-PSAOAIDalle3 -serviceName "MyService" -Prompt "MyPrompt" -user "MyUser"
 
    This command sends a request to the "MyService" service with the prompt "MyPrompt" and the user "MyUser", and retrieves the response.
 
    .NOTES
    Author: Wojciech Napierala
    Date: 2023-06
    #>

    [CmdletBinding()]
    param (
        [string]$serviceName,

        [Parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [string]$Prompt,

        [string]$model = 'dalle3',

        [ValidateSet("1024x1024", "1792x1024", "1024x1792")]
        [string]$size = "1792x1024",

        [ValidateSet("url", "b64_json")]
        [string]$response_format = "url",

        [ValidateSet("standard", "hd")]
        [string]$quality = "hd",

        [ValidateSet("natural", "vivid")]
        [string]$style = "natural",

        [string]$user,

        [string]$ApiVersion = (Get-EnvironmentVariable -VariableName $script:API_AZURE_OPENAI_APIVERSION),

        [Parameter(Mandatory = $false)]
        [string]$Endpoint = (Set-EnvironmentVariable -VariableName $script:API_AZURE_OPENAI_ENDPOINT -PromptMessage "Please enter the endpoint"),
        [Parameter(Mandatory = $false)]
        [string]$Deployment = (Set-EnvironmentVariable -VariableName $script:API_AZURE_OPENAI_D3_DEPLOYMENT -PromptMessage "Please enter the deployment"),

        [string]$SavePath = [Environment]::GetFolderPath([Environment+SpecialFolder]::MyPictures),

        [int]$ImageLoops = 1,

        [int]$n = 1,

        [int]$timeoutSec = 240
    )

    # This function constructs and returns the body for the API request
    function Get-BodyJSON {
        param(
            [Parameter(Mandatory = $true)]
            [string]$prompt,

            [Parameter(Mandatory = $false)]
            [int]$n = 1,

            [Parameter(Mandatory = $false)]
            [string]$user,

            [Parameter(Mandatory = $false)]
            [string]$size,

            [Parameter(Mandatory = $false)]
            [string]$response_format,

            [Parameter(Mandatory = $false)]
            [string]$quality,

            [Parameter(Mandatory = $false)]
            [string]$style

        )
        
        $body = [ordered]@{
            'prompt'          = $prompt
            'n'               = $n
            'user'            = $user
            'size'            = $size
            'response_format' = $response_format
            'quality'         = $quality
            'style'           = $style
        }
        return ($body | ConvertTo-Json)
    }
    
    while (-not $APIVersion) {
        $APIVersion = Set-EnvironmentVariable -VariableName $script:API_AZURE_OPENAI_APIVERSION -PromptMessage "Please enter the API Version"
    }

    while (-not $Endpoint) {
        # Get the endpoint from the environment variable
        $Endpoint = Set-EnvironmentVariable -VariableName $script:API_AZURE_OPENAI_ENDPOINT -PromptMessage "Please enter the Endpoint"
    }

    while (-not $Deployment) {
        # Get the deployment from the environment variable
        $Deployment = Set-EnvironmentVariable -VariableName $script:API_AZURE_OPENAI_D3_DEPLOYMENT -PromptMessage "Please enter the Deployment"
    }

    while ([string]::IsNullOrEmpty($ApiKey)) {
        if (-not ($ApiKey = Set-EnvironmentVariable -VariableName $script:API_AZURE_OPENAI_KEY -PromptMessage "Please enter the API Key" -Secure)) {
            Set-EnvironmentVariable -VariableName $script:API_AZURE_OPENAI_KEY -VariableValue $null
            $ApiKey = Set-EnvironmentVariable -VariableName $script:API_AZURE_OPENAI_KEY -PromptMessage "Please enter the API Key" -Secure
        }
    }

    if ($VerbosePreference -eq 'Continue') {
        Write-Host "Parameters and values:"
        Write-Host "Prompt: $prompt"
        Write-Host "n: $n"
        Write-Host "User: $user"
        Write-Host "Size: $size"
        Write-Host "Response format: $response_format"
        Write-Host "Quality: $quality"
        Write-Host "Style: $style"
    }

    # Create the JSON request body for the API call
    $requestBodyJSON = get-BodyJSON -prompt $prompt -n $n -user $user -size $size -response_format $response_format -quality $quality -style $style

    Write-Verbose $requestBodyJSON

    # Define the headers for the API call
    $headers = Get-Headers -ApiKey $script:API_AZURE_OPENAI_KEY -Secure
    
    # Define the URI for the API call
    $URI = Get-PSAOAIUrl -apiVersion $ApiVersion -Mode Dalle3 -Endpoint $Endpoint -Deployment $Deployment

    Write-Verbose $URI


    # Use a try-catch block to handle potential errors in the API call
    try {
        for ($i = 0; $i -lt $ImageLoops; $i++) {

            # Make the API call and start a job to prevent blocking
            # It uses the URI, body, and headers defined above
            $job = Start-Job -ScriptBlock {
                param($URI, $requestBodyJSON, $headers, $timeoutSec)
                Invoke-RestMethod -Method Post -Uri $URI -Body $requestBodyJSON -Headers $headers -TimeoutSec $timeoutSec
            } -ArgumentList $URI, $requestBodyJSON, $headers, $timeoutSec

            Write-Host "[dalle3]" -ForegroundColor Green
            Write-Host "{n:'${n}', size:'${size}', rf:'${response_format}', quality:'${quality}', user:'${user}', style:'${style}', imageloops:'$($j=$i+1;$j)/${imageloops}'} " -NoNewline -ForegroundColor Magenta

            # If the job is still running, it does progress
            while (($job.JobStateInfo.State -eq 'Running') -or ($job.JobStateInfo.State -eq 'NotStarted')) {
                Write-Host "." -NoNewline -ForegroundColor Blue
                Start-Sleep -Milliseconds 1000
            }
            Write-Host ""

            # Define the filenames for the prompt and image
            $data = (get-date).ToString("yyyyMMddHHmmss")
            $PromptFileName = "$data.png.DATA.txt"
            $ImageFileName = "$data.png"
            
            # Get the full paths for the prompt and image
            $promptFullName = Join-Path $SavePath $PromptFileName
            $ImageFullName = Join-Path $SavePath $ImageFileName
        
            # Check if the job failed and display an error message if it did
            # If it did not fail, it retrieves the response and extracts the image URL and revised prompt
            # It then saves the image and revised prompt to files
            if ($job.JobStateInfo.State -ne 'Failed') {

                # Wait for the job to finish and collect the response
                $response = Receive-Job -Id $job.Id -Wait

                # Get the revised prompt and image URL from the response
                $imageRevisedPrompt = $response.data[0].revised_prompt
                $imageUrl = $response.data[0].url

                # Display the revised prompt
                write-host $imageRevisedPrompt -ForegroundColor Cyan
    
                # Save the revised prompt to a file
                "Prompt: $prompt" | Add-Content -Path $promptFullName -Force
                "Revised prompt: $imageRevisedPrompt" | Add-Content -Path $promptFullName -Force

                # Download the image and save it to a file using .NET WebClient for better performance
                $webClient = New-Object System.Net.WebClient
                $webClient.DownloadFile($imageUrl, $ImageFullName)
        
                # Display the paths of the saved files
                Write-Host $ImageFullName -ForegroundColor Blue
                Write-Host $promptFullName -ForegroundColor Blue
            }
            else {

                Write-Host "Job failed: " -NoNewline -ForegroundColor DarkRed
                [void]($response = Receive-Job -Id $job.Id -Wait -ErrorVariable joberror -ErrorAction SilentlyContinue)
                #$joberror | ConvertTo-Json
                write-host "$($jobError.Exception.ErrorRecord.Exception)" -NoNewline -ForegroundColor DarkYellow
                write-host " $($($($jobError.ErrorDetails.Message) | convertfrom-json).error.message)" -ForegroundColor DarkYellow

                # Save the prompt and joberrormessageto a file
                "[Error] ($($jobError.Exception.ErrorRecord.Exception))" | Add-Content -Path $promptFullName -Force
                "Prompt: $prompt" | Add-Content -Path $promptFullName -Force

                # Display the paths of the saved files
                Write-Host $promptFullName -ForegroundColor Blue
            }
        }
    }
    catch {
        # If any error occurs during the process, the error details (file, line, character, and message) are printed to the console.
        Show-Error $_
    }

}