Public/New-VisionCompletion.ps1
function New-VisionCompletion { <# .SYNOPSIS Generate text from a prompt and an image. .DESCRIPTION Generate text from a prompt and an image by using the newest GPT-4-Vision-preview model. You can use this cmdlet to get completion from OpenAI API.The cmdlet accept pipeline input. You can also assign the prompt, api_key, engine, endpoint, max_tokens, temperature. You can input multiple images, and we will use all the images to generate the text. .PARAMETER prompt The prompt to get completion from OpenAI API. If yuo provide a file path, we will read the file as prompt. You can also set prompt in pipeline input. You can also specify a url, we will read the url as prompt. You can read the prompt from a library (https://github.com/code365opensource/promptlibrary), by use "lib:xxxxx" as the prompt, for example, "lib:fitness". .PARAMETER files The image files to get completion from OpenAI API. We support jpg, png, gif, and you can input multiple images, and you can even mix local file path and online url. .PARAMETER api_key The api_key to get completion from OpenAI API. You can also set api_key in environment variable OPENAI_API_KEY or OPENAI_API_KEY_AZURE (if you want to use Azure OpenAI Service API). .PARAMETER engine The engine (refer to the deployment name if you use Azure OpenAI service) to get completion from OpenAI API. You can also set engine in environment variable OPENAI_VISION_ENGINE or OPENAI_VISION_ENGINE_AZURE (if you want to use Azure OpenAI Service API). The default value is gpt-4-vision-preview. You can use model or deployment as the alias of engine. .PARAMETER endpoint The endpoint to get completion from OpenAI API. You can also set endpoint in environment variable OPENAI_ENDPOINT or OPENAI_ENDPOINT_AZURE (if you want to use Azure OpenAI Service API). .PARAMETER max_tokens The max_tokens to get completion from OpenAI API. The default value is 1024. .PARAMETER temperature The temperature to get completion from OpenAI API. The default value is 1, which means most creatively. .PARAMETER azure If you want to use Azure OpenAI API, you can use this switch. .PARAMETER environment If you want to use Azure OpenAI API, you can use this parameter to set the environment. We will read environment variable OPENAI_API_KEY_AZURE_$environment, OPENAI_VISION_ENGINE_AZURE_$environment, OPENAI_ENDPOINT_AZURE_$environment. if you don't set this parameter (or the environment doesn't exist), we will read environment variable OPENAI_API_KEY_AZURE, OPENAI_ENGINE_AZURE, OPENAI_ENDPOINT_AZURE. You can use env as the alias of environment. .PARAMETER api_version If you want to use Azure OpenAI API, you can use this parameter to set the api_version. The default value is 2023-07-01-preview. .PARAMETER outFile If you want to save the result to a file, you can use this parameter to set the file path. .EXAMPLE New-VisionCompletion -prompt "What's in below pictures?" -files "c:\temp\image1.jpg","c:\temp\image2.jpg" Use default api_key, engine, endpoint from environment varaibles .EXAMPLE vc "What's in below pictures?" -files "c:\temp\image1.jpg","c:\temp\image2.jpg" Use alias of the cmdlet with default api_key, engine, endpoint from environment varaibles .EXAMPLE "What's in below pictures?" | vc -files "c:\temp\image1.jpg","c:\temp\image2.jpg" Use pipeline input .EXAMPLE vc "What's in below pictures?" -files "c:\temp\image1.jpg","c:\temp\image2.jpg" -api_key "your api key" Set api_key in the command .EXAMPLE vc "What's in below pictures?" -files "c:\temp\image1.jpg","c:\temp\image2.jpg" -api_key "your api key" -azure Set api_key in the command and use Azure OpenAI API .EXAMPLE vc "What's in below pictures?" -files "c:\temp\image1.jpg","c:\temp\image2.jpg" -api_key "your api key" -azure -environment "dev" Set api_key in the command and use Azure OpenAI API with environment variable OPENAI_API_KEY_AZURE_dev, OPENAI_VISION_ENGINE_AZURE_dev, OPENAI_ENDPOINT_AZURE_dev .LINK https://github.com/chenxizhang/openai-powershell .INPUTS System.String, you can pass one or more string to the cmdlet, and we will get the completion for you. .OUTPUTS System.String, the completion from OpenAI API #> [CmdletBinding()] [Alias("vc","vision")] param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)][string]$prompt, [Parameter(Mandatory = $true)][string[]]$files, [string]$api_key, [Parameter()] [Alias("model", "deployment")] [string]$engine, [Parameter()][string]$endpoint, [Parameter()][int]$max_tokens = 1024, [Parameter()][double]$temperature = 1, [switch]$azure, [Alias("env")] [string]$environment, [string]$api_version = "2023-07-01-preview", [string]$outFile ) BEGIN { Write-Verbose "Parameter received`n$($PSBoundParameters | Out-String)" Write-Verbose "Environment variable detected.`n$(Get-ChildItem Env:OPENAI_* | Out-String)" if ($azure) { $api_key = if ($api_key) { $api_key } else { Get-FirstNonNullItemInArray("OPENAI_API_KEY_AZURE_$environment", "OPENAI_API_KEY_AZURE") } $engine = if ($engine) { $engine } else { Get-FirstNonNullItemInArray("OPENAI_VISION_ENGINE_AZURE_$environment", "OPENAI_VISION_ENGINE_AZURE") } $endpoint = "{0}openai/deployments/{1}/chat/completions?api-version=$api_version" -f $(if ($endpoint) { $endpoint }else { Get-FirstNonNullItemInArray("OPENAI_ENDPOINT_AZURE_$environment", "OPENAI_ENDPOINT_AZURE") }), $engine } else { $api_key = if ($api_key) { $api_key } else { $env:OPENAI_API_KEY } $engine = if ($engine) { $engine } else { if ($env:OPENAI_VISION_ENGINE) { $env:OPENAI_VISION_ENGINE }else { "gpt-4-vision-preview" } } $endpoint = if ($endpoint) { $endpoint } else { if ($env:OPENAI_ENDPOINT) { $env:OPENAI_ENDPOINT }else { "https://api.openai.com/v1/chat/completions" } } } Write-Verbose "Parameter parsed, api_key: $api_key, engine: $engine, endpoint: $endpoint" $hasError = $false if ((!$azure) -and ((Test-OpenAIConnectivity) -eq $False)) { Write-Error $resources.openai_unavaliable $hasError = $true } if (!$api_key) { Write-Error $resources.error_missing_api_key $hasError = $true } if (!$engine) { Write-Error $resources.error_missing_engine $hasError = $true } if (!$endpoint) { Write-Error $resources.error_missing_endpoint $hasError = $true } # ensure all the files are valid image(jpg,png,gif) $files | ForEach-Object { # if the file not startwith http/https and endwith jpg/png/gif, then we will treat it as a local file path and check if the file exists if ((Get-IsValidImage -path $_) -eq $false) { Write-Error "File $_ is not a url and not a valid local image(jpg,png,gif)." Set-Variable -Name "hasError" -Value $true } } } PROCESS { if ($hasError) { return } $telemetries = @{ useAzure = $azure } # if prompt is a file path, and the file is exist, then read the file as the prompt $parsedprompt = Get-PromptContent $prompt $prompt = $parsedprompt.content $telemetries.promptType = $parsedprompt.type $telemetries.promptLib = $parsedprompt.lib # collect the telemetry data Submit-Telemetry -cmdletName $MyInvocation.MyCommand.Name -innovationName $MyInvocation.InvocationName -props $telemetries $imageContent = $files | ForEach-Object { Write-Verbose "Processing file $_" $uri = Get-ImageBase64Uri $_ Write-Verbose "Image uri: $uri" Write-Output @{ type = "image_url" image_url = $uri } } $imageContent = @(@{type = "text"; text = $prompt }) + $imageContent $params = @{ Uri = $endpoint Method = "POST" Body = @{ model = "$engine" messages = @( @{ role = "user" content = $imageContent } ) max_tokens = $max_tokens temperature = $temperature } | ConvertTo-Json -Depth 10 Headers = if ($azure) { @{"api-key" = "$api_key" } } else { @{"Authorization" = "Bearer $api_key" } } ContentType = "application/json;charset=utf-8" } Write-Verbose $params $response = Invoke-RestMethod @params if ($PSVersionTable['PSVersion'].Major -eq 5) { Write-Verbose "Powershell 5.0 detected, convert the response to UTF8" $dstEncoding = [System.Text.Encoding]::GetEncoding('iso-8859-1') $srcEncoding = [System.Text.Encoding]::UTF8 $response.choices | ForEach-Object { $_.message.content = $srcEncoding.GetString([System.Text.Encoding]::Convert($srcEncoding, $dstEncoding, $srcEncoding.GetBytes($_.message.content))) } } Write-Verbose "Response converted to UTF8: $($response | ConvertTo-Json -Depth 10)" $result = $response.choices[0].message.content Write-Verbose "Response parsed to plain text: $result" if ($outFile) { Write-Verbose "Write result to file $outFile" $result | Out-File $outFile -Encoding utf8 } else { Write-Verbose "Output result to pipeline" Write-Output $result Set-Clipboard $result Write-Host "Copied the response to clipboard." -ForegroundColor Green } } } # SIG # Begin signature block # MIIc/wYJKoZIhvcNAQcCoIIc8DCCHOwCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBBzMY8jdEaSnoy # QerrMvAvfvdIkt0dhUnxOGnujZZJUaCCAyowggMmMIICDqADAgECAhBcsg5m3zM9 # kUZxmeNzIQNjMA0GCSqGSIb3DQEBCwUAMCoxKDAmBgNVBAMMH0NIRU5YSVpIQU5H # IC0gQ29kZSBTaWduaW5nIENlcnQwIBcNMjQwMTA4MTMwMjA0WhgPMjA5OTEyMzEx # NjAwMDBaMCoxKDAmBgNVBAMMH0NIRU5YSVpIQU5HIC0gQ29kZSBTaWduaW5nIENl # cnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKDY3QG81JOKZG9jTb # QriDMDhq6gy93Pmoqgav9wErj+CgVvXKk+lGpUu74MWVyLUrJx8/ACb4b287wsXx # mQj8zQ3SqGn5CCjPKoAPsSbry0LOSl8bsFpwBr3YBJVL6cibhus2KLCbNu/u7sND # wyivKXYA1Iy1uTQPNVPcBx36krZTZyyE4CmngO75YbTMEzvHEjM3BIXdKtEt673t # iNOVSP6doh0zRwWEh2Y/eoOpv+FUokORwhKonxMtmIIET+ZPx7Ex+9aqHrliEabx # FsN4ETnuVT3rST++7Q2fquWFnl5scDnisFhU8JL8k+OGUzpLlo/nOpiRZkbKCEkZ # FCLhAgMBAAGjRjBEMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcD # AzAdBgNVHQ4EFgQUwcR3UUOZ6TxpBp9MxnBygyIMhUQwDQYJKoZIhvcNAQELBQAD # ggEBADwiE9nowKxUNN84BTk9an1ZkdU95ouj+q6MRbafH08u4XV7CxXpkPR8Za/c # BJWTOqCuz9pMPo0TylqWPm+++Tqy1OJ7Qewvy1+DXPuFGkTqY721uZ+YsHY3CueC # VSRZRNsWSYE9UxXXFRsjDu/M3+EvyaNDE4xQkwrP8obFJoHq7WaOCCD2wMbKjLb5 # bS/VgtOK7Yn9pU/ghrW+Em+zHOX87wNRh/I5jd+LsnY8bR6REzgdmogIyvD4dsJD # /IZLxRtbm2BHOn/aGBdu+GpEaYEEb6VkWcJhrQnpiNjjlu43CbRz5Bw14XPWGUDH # +EkUqkWS4h8zsRiyvR9Pnwklg6UxghkrMIIZJwIBATA+MCoxKDAmBgNVBAMMH0NI # RU5YSVpIQU5HIC0gQ29kZSBTaWduaW5nIENlcnQCEFyyDmbfMz2RRnGZ43MhA2Mw # DQYJYIZIAWUDBAIBBQCgfDAQBgorBgEEAYI3AgEMMQIwADAZBgkqhkiG9w0BCQMx # DAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkq # hkiG9w0BCQQxIgQghwHtZPfAZLWzLMJsMF8Kgyrm+DeZtO2Pp7x27+JzRaowDQYJ # KoZIhvcNAQEBBQAEggEAsypAUq5TM/TzO6Ia0A7Wb0hhbFOcXAndgJRIvbUDA7Iq # VrXRs2+zZyloTlKc9+AQFoeRhjkaCXpcMjfZ+nEzFiKLAfd6A7c91q6TWHoqmDZo # NXT12GeL2MHVEO9eb600fgBQV4ash+cgFYst2qnVxGOrLf6CBOm5qeNUdKWmj1OI # BfCgRmRPBl1FT6Ky602HRXEd12WAQWCtx/YY/Ed93G2x/MrNYfZDu5FXB8Aq7+Nw # +t1cpFaRMnELzayTsSHJgenCxeET1WyY6PRw+v+zbzs9HGC2VyD+1S3HBmKa96do # A6wzSVAX4neDX2d88zBfLTWKgULB03inyx8Fara0P6GCF0Awghc8BgorBgEEAYI3 # AwMBMYIXLDCCFygGCSqGSIb3DQEHAqCCFxkwghcVAgEDMQ8wDQYJYIZIAWUDBAIB # BQAweAYLKoZIhvcNAQkQAQSgaQRnMGUCAQEGCWCGSAGG/WwHATAxMA0GCWCGSAFl # AwQCAQUABCDTxDFBN/rOpuvIX3zIikyfzbMQv0L6MG24XOn8+35ozgIRAL80cNBX # 3lMCUj5Rgcn2/a8YDzIwMjQwNTAxMDYxNTMyWqCCEwkwggbCMIIEqqADAgECAhAF # RK/zlJ0IOaa/2z9f5WEWMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcw # FQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3Rl # ZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjMwNzE0MDAw # MDAwWhcNMzQxMDEzMjM1OTU5WjBIMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln # aUNlcnQsIEluYy4xIDAeBgNVBAMTF0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIzMIIC # IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAo1NFhx2DjlusPlSzI+DPn9fl # 0uddoQ4J3C9Io5d6OyqcZ9xiFVjBqZMRp82qsmrdECmKHmJjadNYnDVxvzqX65RQ # jxwg6seaOy+WZuNp52n+W8PWKyAcwZeUtKVQgfLPywemMGjKg0La/H8JJJSkghra # arrYO8pd3hkYhftF6g1hbJ3+cV7EBpo88MUueQ8bZlLjyNY+X9pD04T10Mf2SC1e # RXWWdf7dEKEbg8G45lKVtUfXeCk5a+B4WZfjRCtK1ZXO7wgX6oJkTf8j48qG7rSk # IWRw69XloNpjsy7pBe6q9iT1HbybHLK3X9/w7nZ9MZllR1WdSiQvrCuXvp/k/Xtz # PjLuUjT71Lvr1KAsNJvj3m5kGQc3AZEPHLVRzapMZoOIaGK7vEEbeBlt5NkP4FhB # +9ixLOFRr7StFQYU6mIIE9NpHnxkTZ0P387RXoyqq1AVybPKvNfEO2hEo6U7Qv1z # fe7dCv95NBB+plwKWEwAPoVpdceDZNZ1zY8SdlalJPrXxGshuugfNJgvOuprAbD3 # +yqG7HtSOKmYCaFxsmxxrz64b5bV4RAT/mFHCoz+8LbH1cfebCTwv0KCyqBxPZyS # kwS0aXAnDU+3tTbRyV8IpHCj7ArxES5k4MsiK8rxKBMhSVF+BmbTO77665E42FEH # ypS34lCh8zrTioPLQHsCAwEAAaOCAYswggGHMA4GA1UdDwEB/wQEAwIHgDAMBgNV # HRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMCAGA1UdIAQZMBcwCAYG # Z4EMAQQCMAsGCWCGSAGG/WwHATAfBgNVHSMEGDAWgBS6FtltTYUvcyl2mi91jGog # j57IbzAdBgNVHQ4EFgQUpbbvE+fvzdBkodVWqWUxo97V40kwWgYDVR0fBFMwUTBP # oE2gS4ZJaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0 # UlNBNDA5NlNIQTI1NlRpbWVTdGFtcGluZ0NBLmNybDCBkAYIKwYBBQUHAQEEgYMw # gYAwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBYBggrBgEF # BQcwAoZMaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3Rl # ZEc0UlNBNDA5NlNIQTI1NlRpbWVTdGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsF # AAOCAgEAgRrW3qCptZgXvHCNT4o8aJzYJf/LLOTN6l0ikuyMIgKpuM+AqNnn48Xt # JoKKcS8Y3U623mzX4WCcK+3tPUiOuGu6fF29wmE3aEl3o+uQqhLXJ4Xzjh6S2sJA # OJ9dyKAuJXglnSoFeoQpmLZXeY/bJlYrsPOnvTcM2Jh2T1a5UsK2nTipgedtQVyM # adG5K8TGe8+c+njikxp2oml101DkRBK+IA2eqUTQ+OVJdwhaIcW0z5iVGlS6ubzB # aRm6zxbygzc0brBBJt3eWpdPM43UjXd9dUWhpVgmagNF3tlQtVCMr1a9TMXhRsUo # 063nQwBw3syYnhmJA+rUkTfvTVLzyWAhxFZH7doRS4wyw4jmWOK22z75X7BC1o/j # F5HRqsBV44a/rCcsQdCaM0qoNtS5cpZ+l3k4SF/Kwtw9Mt911jZnWon49qfH5U81 # PAC9vpwqbHkB3NpE5jreODsHXjlY9HxzMVWggBHLFAx+rrz+pOt5Zapo1iLKO+ua # gjVXKBbLafIymrLS2Dq4sUaGa7oX/cR3bBVsrquvczroSUa31X/MtjjA2Owc9bah # uEMs305MfR5ocMB3CtQC4Fxguyj/OOVSWtasFyIjTvTs0xf7UGv/B3cfcZdEQcm4 # RtNsMnxYL2dHZeUbc7aZ+WssBkbvQR7w8F/g29mtkIBEr4AQQYowggauMIIElqAD # AgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYT # AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2Vy # dC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAz # MjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQK # Ew5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBS # U0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDM # g/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOx # s+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXnHwZljZQp09ns # ad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtA # rF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4fsbVYTXn+149z # k6wsOeKlSNbwsDETqVcplicu9Yemj052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6 # OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0QCirc0PO30qh # HGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1 # KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX # 6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0 # sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQID # AQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2F # L3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08w # DgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEB # BGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsG # AQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdp # Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgG # BmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+Y # qUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjY # C+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0 # FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6 # WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGj # VoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzp # SwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwd # eDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o # 08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n # +2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y # 3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIO # K+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv # 21DiCEAYWjANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMM # RGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQD # ExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcN # MzExMTA5MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQg # SW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2Vy # dCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC # AQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf # 8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1 # mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe # 7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecx # y9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX # 2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX # 9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp49 # 3ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCq # sWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH # dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauG # i0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYw # DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08w # HwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGG # MHkGCCsGAQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl # cnQuY29tMEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v # RGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0 # dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5j # cmwwEQYDVR0gBAowCDAGBgRVHSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXn # OF+go3QbPbYW1/e/Vwe9mqyhhyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23 # OO/0/4C5+KH38nLeJLxSA8hO0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFI # tJnLnU+nBgMTdydE1Od/6Fmo8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7s # pNU96LHc/RzY9HdaXFSMb++hUD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgi # wbJZ9VVrzyerbHbObyMt9H5xaiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cB # qZ9Xql4o4rmUMYIDdjCCA3ICAQEwdzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMO # RGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNB # NDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBAhAFRK/zlJ0IOaa/2z9f5WEWMA0G # CWCGSAFlAwQCAQUAoIHRMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkq # hkiG9w0BCQUxDxcNMjQwNTAxMDYxNTMyWjArBgsqhkiG9w0BCRACDDEcMBowGDAW # BBRm8CsywsLJD4JdzqqKycZPGZzPQDAvBgkqhkiG9w0BCQQxIgQgSqgcJD6GeuWu # G0abvDsj9TtVmgXACM/gpiI4nbP2QtgwNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQg # 0vbkbe10IszR1EBXaEE2b4KK2lWarjMWr00amtQMeCgwDQYJKoZIhvcNAQEBBQAE # ggIAihqlaz6eWVZPLDU08WKmx4rq5qG3gLaunxKFWfgLp98DUO42f9lyIXB4bm1g # QfDxYAzuZ73SNL5kP0ysJsIzX6JHxhFk2otWPc1xHJdkH02SrjSftA5mc9d2d2L6 # BuzI2nEKHTxynOpeDAJupaet0GmloMvzjwtO9w6c+TW0Fp1zI18RqjW9cGsrE5c8 # AHLY3QkJMabfzBdHKxI3t4ZVmLUE3JnyTBs+/4uOeDLPK3zz9TDfdLaW3e+X7iwk # gbMVaLXYxA8hf01qsfbApCi7ol2ZyxUCT5BKhv0lUptHtWNzu4XN9KNqb0OCcu1e # gmA29nv6PKQmdpwnQnTzD4gjEJFo/ouCvlYq4ZVAawd9i1BX1U+QWIRK63Za8f7E # dmFq86DSFn46WiUTx+VolpDwKPliXWJbozPTZzB7UqB1cUklzQzyFeSsxKW9JUs4 # HcoT9SYVRN2erhZb5e1ALeSx6iqe0tkBryzNmRRyIwJi9HKDw1lafrJQuZGCAYPM # HhVzNQ1Tjk60Gzhx9OquMRi0awgHny6ULOzaZYa1tTlqcNfs+fgWxsWu2yCwBuXh # Zle70ET1qKrconJc5hdK6t6kdt2sofDpjfx35rFzh04bGqM/4TdWlkMoc9FlNPdc # GoDn3DFio6KlpT0XVHoUF8a7GcGow9u+u2ztYfnokPasGM8= # SIG # End signature block |