PowerGPT.psm1
function Send-LlmPrompt { <# .SYNOPSIS Script that sends prompts to Large Language Models via Substrate LLM API. .EXAMPLE .\Send-LlmPrompt.ps1 -Prompts @("Once upon a time, a little boy") Using the default 'text-davinci-003' model. This will prompt the caller for authentication. .EXAMPLE .\Send-LlmPrompt.ps1 -Prompts @("Who is the fastest person on earth?") -Model 'text-chat-davinci-002' Asking a question to ChatGPT model. .EXAMPLE .\Send-LlmPrompt.ps1 -Prompts @("Who is the fastest person on earth?") -AccessToken $accessToken Providing an authentication token, which can be retrieved using the another script: PS C:\> $accessToken = .\Get-AccessTokenViaDeviceFlow.ps1 #> param ( [Parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$Model = "text-davinci-003", [Parameter(Mandatory=$false)] [int]$MaxTokens = 400, [Parameter(Mandatory=$false)] [int]$N = 1, [Parameter(Mandatory=$false)] [float]$Temperature = 1, [Parameter(Mandatory=$false)] [float]$TopP = 1, [Parameter(Mandatory=$false)] [string]$Stop = $null, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string[]]$Prompts, [Parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$API_KEY ) $body = @{ model = $Model prompt = $Prompts max_tokens = $MaxTokens temperature = $Temperature top_p = $TopP n = $N stream = $false logprobs = $null stop = $Stop } # Send request to openai completion API $endpoint = "https://api.openai.com/v1/completions" $headers = @{ "Content-Type"="application/json" "Authorization"="Bearer $API_KEY" } $jsonPayload = ConvertTo-Json $body # Write-Host -ForegroundColor Yellow "POST $endpoint" # $headers.Keys | %{ if ($_ -eq "Authorization") { Write-Host "Authorization: Bearer (...)" } else { Write-Host "$($_): $($headers.$_)" }} # Write-Host $jsonPayload # Write-Host -ForegroundColor Yellow "Response:" $result = Invoke-RestMethod -Uri $endpoint -Headers $headers -Method Post -Body $jsonPayload -UseDefaultCredentials return $result } function Read-Configuration { param ( [switch] $ResetConfig ) $ConfigPath = Join-Path $HOME "PowerGPT.config.json" if ($ResetConfig -and (Test-Path $ConfigPath)) { Remove-Item $ConfigPath -Force } # Check if config file exists, if it doesn't, create it if (-not (Test-Path $ConfigPath)) { $API_KEY = (Read-Host -Prompt "Input your API key").Trim() $Config = @{ API_KEY = $API_KEY } # Store config to config path $Config | ConvertTo-Json | Out-File $ConfigPath return $Config } $Config = Get-Content $ConfigPath | ConvertFrom-Json return $Config } function PowerGPT { param ( [Parameter(Mandatory=$true, Position=0)] [ValidateNotNullOrEmpty()] [string]$Prompt, [switch] $Print, [switch] $ResetConfig, [Parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$ShellVariant = "Windows PowerShell" ) $FullPrompt = "PowerGPT is a powerful and sophisticated chat bot that generates script given the user's instruction. User will describe the task in natural language and PowerGPT will respond with a script. When user's message is unrelated to a task, PowerGPT will respond BEEP. PowerGPT will return script that matches user's instruction. The script must be runnable in script environment. The script must be correct and contain no bugs or potential bugs. The script must do exactly what the instruction says. The script should only use commands that's available in the script environment. The script should follow best practice and conform to style guides. The script should be the most frequently used one. The script should be clear and readable. The script should be well documented. When PowerGPT cannot interpret user's intention or user's instruction is too vague and can have different imterpretations, PowerGPT will make inference about user's intention and present them as choices for user. The number of choices should not exceed 5. Each choice will be prepended by a serial number. Each choice is a short description of what the user is likely to want to do. Each choice will take one line. User will respond with one number. Then PowerGPT must respond with the script. Below are some examples of user interacting with PowerGPT. Each interaction between user and PowerGPT will start with INTERACTION_START and the context language user is interested in. The interaction will end with INTERACTION_END. Each response of PowerGPT will start with POWERGPT_START and end with POWERGPT_END. INTERACTION_START `"Windows PowerShell`" User: list all files in current directory PowerGPT: POWERGPT_START Get-ChildItem POWERGPT_END INTERACTION_END INTERACTION_START `"Windows PowerShell`" User: print first lines and last lines of files in current folder PowerGPT: POWERGPT_START [0] For each file in current directory, print the first line and then print the last line of the file. [1] For each file in current directory, print the first line of the file. After that, for each file, print the last line of the file. POWERGPT_END User: [0] PowerGPT: POWERGPT_START Get-ChildItem | ForEach-Object { `$file = `$_.FullName Write-Host `"First line of `$file:`" Get-Content `$file -TotalCount 1 Write-Host `"Last line of `$file:`" Get-Content `$file -Tail 1 } POWERGPT_END INTERACTION_END INTERACTION_START `"$ShellVariant`" User: $Prompt PowerGPT: POWERGPT_START " $Config = Read-Configuration -ResetConfig:$ResetConfig # We use text-chat-davinci-002 and set temparature to 0 # Temparature controls "creativeness" according to https://platform.openai.com/docs/api-reference/completions/create # We set it to 0 to avoid false results $Result = Send-LlmPrompt -Prompts @($FullPrompt) -Stop "POWERGPT_END" -Temperature 0 -ErrorAction Stop -API_KEY $Config.API_KEY $ResultText = $Result.choices[0].text.Trim() # Response will often contain newline characters, we just trim to remove them # Check if response is a script or if ($ResultText[0] -eq "[" -and $ResultText[1] -le "9" -and $ResultText[1] -ge "0" -and $ResultText[2] -eq "]") { Write-Host "The description is a little vague, do you mean:" Write-Host $ResultText $NumOfChoices = $ResultText.split("`n").Count $Choice = -1 while ($true) { $Opt = Read-Host -Prompt "Choose one description that matches your task, or [n]o" if ($Opt[0] -eq "n") { #user chooses to end flow return } $Success = [int]::tryparse($Opt,[ref]$Choice) if ($Success -and ($Choice -ge 0) -and ($Choice -lt $NumOfChoices)) { break } } $FullPromptWithHistory = $FullPrompt + $ResultText + "`nPOWERGPT_END`nUser:`n[$Choice]`nPowerGPT:`nPOWERGPT_START`n" $Result = Send-LlmPrompt -Prompts @($FullPromptWithHistory) -Stop "POWERGPT_END" -Model "text-chat-davinci-002" -Temperature 0 -ErrorAction Stop $ResultText = $Result.choices[0].text.Trim() } if ($ResultText -eq "BEEP") { Write-Host "**BEEP** PowerGPT bot failed to understand your input **BEEP**" return } if ($Print -or ($ShellVariant -ne "Windows PowerShell")) { Write-Output $ResultText } else { $Execute = $true Write-Host "Will execute script:`n-----`n$ResultText`n-----" # Prompt to ask if user wants to continue execute script while ($true) { $Opt = Read-Host -Prompt "continue?([y]es, [n]o)" if ($Opt -eq "y") { break } if ($Opt -eq "n") { $Execute = $false break } } if ($Execute) { Invoke-Expression $ResultText } } } Export-ModuleMember -Function PowerGPT |