PowerGPT.psm1
function Send-LlmPrompt { 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; charset=utf-8" "Authorization"="Bearer $API_KEY" } $jsonPayload = ConvertTo-Json $body $body = [System.Text.Encoding]::UTF8.GetBytes($jsonPayload) # 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 $body -UseDefaultCredentials return $result } function Send-LlmPromptChat { param ( [Parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$Model = "gpt-3.5-turbo", [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()] [PSCustomObject[]]$Messages, [Parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$API_KEY ) $body = @{ model = $Model messages = $Messages max_tokens = $MaxTokens temperature = $Temperature top_p = $TopP n = $N stream = $false stop = $Stop } # Send request to openai completion API $endpoint = "https://api.openai.com/v1/chat/completions" $headers = @{ "Content-Type"="application/json" "Authorization"="Bearer $API_KEY" } $jsonPayload = ConvertTo-Json $body $body = [System.Text.Encoding]::UTF8.GetBytes($jsonPayload) # 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 $body -UseDefaultCredentials return $result } function Get-PromptMessages { param( [Parameter(Mandatory=$true)] [string]$UserPrompt, [Parameter(Mandatory=$true)] [string]$ShellVariant ) $SystemPromptContent = "You are a powerful and sophisticated multi-lingual chat bot that generates script given the user's instruction. User will describe the task followed by desired response script language. You will respond with a step-by-step explanation of how to construct the script followed by the full script. You must return a 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. " $DemoUserMessage0 = "list all files in current directory" $DemoAssitantMessage0 = " we use ``Get-ChildItem`` to get all files in current directory. `````` Get-ChildItem ``````" $DemoUserMessage2 = "For each file in current directory, print the first line and then print the last line of the file." $DemoAssitantMessage2 = " We use `Get-ChildItem` to get all files in current directory. Then we use `ForEach-Object` to iterate through each file. For each file, we use `Get-Content` to get the first line and the last line of the file. We use `Write-Host` to print the first line and the last line of the file. `````` 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 } ``````" $DemoUserMessage3 = "列出当前文件夹下所有文件 | Windows PowerShell" $DemoAssitantMessage3 = " 我们使用``Get-ChildItem``来列出当前文件夹下所有文件。 `````` Get-ChildItem ``````" $DemoUserMessage4 = "extract compressed.tar.gz | Windows PowerShell" $DemoAssitantMessage4 = " First we use `Get-Command` to check if the ``tar`` command is available. If not, we use ``Invoke-WebRequest`` to download the ``tar.exe.zip`` file. Then we use ``Expand-Archive`` to extract the ``tar.exe.zip`` file. After that, we add the ``tar`` command to the ``PATH`` environment variable. Finally, we use ``tar`` to extract the ``compressed.tar.gz`` file. `````` # Extract compressed.tar.gz in Windows using PowerShell # First, check if the tar command is available if (!(Get-Command tar -ErrorAction SilentlyContinue)) { # If not, install the tar command Invoke-WebRequest -Uri ""http://gnuwin32.sourceforge.net/downlinks/tar.exe.zip"" -OutFile ""tar.exe.zip"" Expand-Archive -Path ""tar.exe.zip"" -DestinationPath ""`$env:ProgramFiles\GnuWin32"" # Add the tar command to the PATH `$env:Path += "";`$env:ProgramFiles\GnuWin32"" } # Extract the compressed.tar.gz file tar -xvzf compressed.tar.gz ``````" $UserPromptContent = "$UserPrompt | $ShellVariant" $FullPromptMessages = @( @{ "role" = "system"; "content" = $SystemPromptContent }, @{ "role" = "user"; "content" = $DemoUserMessage0 }, @{ "role" = "assistant"; "content" = $DemoAssitantMessage0.Trim() }, @{ "role" = "user"; "content" = $DemoUserMessage2 }, @{ "role" = "assistant"; "content" = $DemoAssitantMessage2.Trim() }, @{ "role" = "user"; "content" = $DemoUserMessage3 }, @{ "role" = "assistant"; "content" = $DemoAssitantMessage3.Trim() }, @{ "role" = "user"; "content" = $DemoUserMessage4 }, @{ "role" = "assistant"; "content" = $DemoAssitantMessage4.Trim() }, @{ "role" = "user"; "content" = $UserPromptContent } ) return $FullPromptMessages } 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", [switch] $Chat ) $Config = Read-Configuration -ResetConfig:$ResetConfig if (-not $Chat) { $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 " # 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" -Temperature 0 -ErrorAction Stop -API_KEY $Config.API_KEY $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 } } } else { $PromptMessages = Get-PromptMessages -UserPrompt $Prompt -ShellVariant $ShellVariant $Result = Send-LlmPromptChat -Messages $PromptMessages -Temperature 0 -ErrorAction Stop -API_KEY $Config.API_KEY $ResultText = $Result.choices[0].message.content.Trim() # Response will often contain newline characters, we just trim to remove them Write-Host $ResultText"`n" $startIndex = $ResultText.IndexOf("``````") + 3 if ($startIndex -eq -1) { # No code area found, exit Write-Host "**BEEP** PowerGPT-Chat bot failed to genearte script. **BEEP**" return; } $endIndex = $ResultText.IndexOf("``````", $startIndex) $ScriptText = $ResultText.SubString($startIndex, $endIndex - $startIndex) if ($Print -or ($ShellVariant -ne "Windows PowerShell")) { Write-Output $ScriptText } else { $Execute = $true Write-Host "Will execute script:`n-----`n$ScriptText`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 $ScriptText } } } } Export-ModuleMember -Function PowerGPT |