utilities/AzOpenAIAssistant.psm1 (361 lines of code) (raw):

# Requires -Module Az.Accounts function Get-AzOpenAIToken { $TokenRequest = Get-AzAccessToken -ResourceUrl "https://cognitiveservices.azure.com" return $TokenRequest.Token } function Invoke-AzOpenAIUploadFile { param ( [string]$FilePath, [string]$Endpoint, [string]$Purpose = "assistants" ) $authHeader = @{ Authorization = "Bearer $(Get-AzOpenAIToken)" } $uploadUri = "$Endpoint/openai/files?api-version=2024-03-01-preview" $fileItem = Get-Item -Path $FilePath $bodyFileUpload = @{ purpose = $Purpose file = $fileItem } Invoke-RestMethod -uri $uploadUri -Method Post -Headers $authHeader -ContentType "multipart/form-data" -Form $bodyFileUpload } function New-AzOpenAIAssistant { param ( [string]$MetaPromptFile, [string]$Model, [string[]]$FileIds = @(), [string]$Endpoint, [string]$AssistantName = "" ) $authHeader = @{ Authorization = "Bearer $(Get-AzOpenAIToken)" } $metaPrompt = (Get-Content -Path $MetaPromptFile) -join "`n" if (-not $AssistantName) { $AssistantName = (Get-Date -Format "dd-HH-mm-ss") + "-assistant" } $toolsArray = @(@{ type = "retrieval" }, @{ type = "code_interpreter" }) # Create body for the API request $bodyCreateAssistant = @{ instructions = $metaPrompt name = $AssistantName tools = $toolsArray model = $Model } # Add file_ids to the body only if $FileIds is not empty if ($FileIds) { $bodyCreateAssistant['file_ids'] = $FileIds } $bodyCreateAssistant = $bodyCreateAssistant | ConvertTo-Json -Depth 100 $assistantUri = "$Endpoint/openai/assistants?api-version=2024-03-01-preview" Invoke-RestMethod -uri $assistantUri -Method Post -Headers $authHeader -ContentType "application/json" -Body $bodyCreateAssistant } # Requires -Module Az function Get-AzOpenAIAssistant { param ( [string]$AssistantId = $null, [string]$Endpoint ) $authHeader = @{ Authorization = "Bearer $(Get-AzOpenAIToken)" } $assistantUri = "$Endpoint/openai/assistants" if ($AssistantId) { $assistantUri += "/$AssistantId" } $assistantUri += "?api-version=2024-02-15-preview" Invoke-RestMethod -uri $assistantUri -Method Get -Headers $authHeader } function New-AzOpenAIAssistantFunction { param ( [Parameter(Mandatory = $true)] [string]$Name, [Parameter(Mandatory = $true)] [string]$Description, [Parameter(Mandatory = $true)] [string]$RequiredPropertyName, [Parameter(Mandatory = $true)] [string]$PropertyDescription, [Parameter(Mandatory = $true)] [string]$Endpoint, [Parameter(Mandatory = $true)] [string]$AssistantId, [Parameter(Mandatory = $true)] [string]$Instructions ) $authHeader = @{ Authorization = "Bearer $(Get-AzOpenAIToken)" } $requiredProperties = @{ $RequiredPropertyName = @{ "type" = "string" "description" = $PropertyDescription } } # Placeholder for logic to derive model from AssistantId $model = Get-AzOpenAIAssistant -AssistantId $AssistantId -Endpoint $Endpoint | Select-Object -ExpandProperty model $body = @{ "instructions" = $Instructions "tools" = @( @{ "type" = "function" "function" = @{ "name" = $Name "description" = $Description "parameters" = @{ "type" = "object" "properties" = $requiredProperties "required" = @($RequiredPropertyName) } } } ) "id" = $AssistantId "model" = $model } | ConvertTo-Json -Depth 100 $ApiVersion = "2024-02-15-preview" $assistantUri = "$Endpoint/openai/assistants?api-version=$ApiVersion" Invoke-RestMethod -Uri $assistantUri -Method Post -Headers $authHeader -ContentType "application/json" -Body $body } # Function to create a new thread function New-AzOpenAIAssistantThread { param ( [string]$Endpoint ) $authHeader = @{ Authorization = "Bearer $(Get-AzOpenAIToken)" } # Corrected endpoint URL to create a new thread $threadUri = "$Endpoint/openai/threads?api-version=2024-03-01-preview" # Send the API request and return the thread ID $response = Invoke-RestMethod -uri $threadUri -Method Post -Headers $authHeader -ContentType "application/json" -Body $bodyJson return $response.id } # Function to retrieve an existing thread function Get-AzOpenAIAssistantThread { param ( [string]$Endpoint, [string]$ThreadID = "" ) $authHeader = @{ Authorization = "Bearer $(Get-AzOpenAIToken)" } # Corrected endpoint URL to create a new thread $threadUri = "$Endpoint/openai/threads/$ThreadID?api-version=2024-03-01-preview" # Send the API request and return the thread ID $response = Invoke-RestMethod -uri $threadUri -Method Get -Headers $authHeader -ContentType "application/json" -Body $bodyJson return $response } # Function to post a message to a thread function Post-AzOpenAIAssistantMessage { param ( [string]$ThreadID, [string]$Message, [string]$Endpoint ) $authHeader = @{ Authorization = "Bearer $(Get-AzOpenAIToken)" } $bodyNewMessage = @( @{ role = "user" content = $message } ) $bodyJson = $bodyNewMessage | ConvertTo-Json -Depth 100 # Corrected endpoint URL to post message to a thread $messageUri = "$Endpoint/openai/threads/$ThreadID/messages?api-version=2024-03-01-preview" # Send the API request to post the message and return the response $response = Invoke-RestMethod -uri $messageUri -Method Post -Headers $authHeader -ContentType "application/json" -Body $bodyJson return $response } function Start-AzOpenAIAssistantThreadRun { param ( [Parameter(Mandatory = $true)] [string]$AssistantId, [Parameter(Mandatory = $true)] [string]$ThreadID, [Parameter(Mandatory = $true)] [string]$Endpoint, [switch]$Async ) $authHeader = @{ Authorization = "Bearer $(Get-AzOpenAIToken)" } # Create the body for the run request, including the AssistantId $bodyStartRun = @{ assistant_id = $AssistantId } $bodyJson = $bodyStartRun | ConvertTo-Json -Depth 100 # Endpoint URL to start a run on a thread $threadRunUri = "$Endpoint/openai/threads/$ThreadID/runs?api-version=2024-03-01-preview" # Send the API request to start a run on the thread $runResponse = Invoke-RestMethod -uri $threadRunUri -Method Post -Headers $authHeader -ContentType "application/json" -Body $bodyJson if (-not $Async) { # Check status and wait for completion if not in Async mode $i = 0 do { $i++ Write-Host "Checking status $i" $statusUri = "$Endpoint/openai/threads/$ThreadID/runs/$($runResponse.id)?api-version=2024-03-01-preview" $runResult = Invoke-RestMethod -uri $statusUri -Headers $authHeader $runResult | Select-Object id, assistant_id, thread_id, status, last_error Start-Sleep -Seconds 10 } while ($runResult.status -ne "completed" -and $i -lt 100) if ($runResult.status -eq "completed") { # Get results if the run has completed $messagesUri = "$Endpoint/openai/threads/$ThreadID/messages?api-version=2024-03-01-preview" $resultMessage = Invoke-RestMethod -uri $messagesUri -Headers $authHeader $resultMessage.data.ForEach({ $_.content.text | Select-Object -ExpandProperty value }) } else { Write-Host "Run did not complete within the specified time." } } else { Write-Host "Run started in Async mode. Use Get-AzOpenAIAssistantThreadStatus to check the run status later." } # Return the run ID for later status checks if needed return $runResponse.id } function Get-AzOpenAIAssistantThreadStatus { param ( [string]$ThreadID, [string]$RunID, [string]$Endpoint ) $authHeader = @{ Authorization = "Bearer $(Get-AzOpenAIToken)" } # Make sure the URI is constructed correctly $statusUri = "$Endpoint/openai/threads/$ThreadID/runs/$RunID?api-version=2024-02-15-preview" # Use try-catch block to handle any errors that occur during the API call try { $response = Invoke-RestMethod -uri $statusUri -Method Get -Headers $authHeader return $response } catch { Write-Error "Failed to get thread status: $_" return $null } } function Get-AzOpenAIAssistantMessages { param ( [string]$ThreadID, [string]$Endpoint ) $authHeader = @{ Authorization = "Bearer $(Get-AzOpenAIToken)" } $messagesUri = "$Endpoint/openai/threads/$ThreadID/messages?api-version=2024-02-15-preview" Invoke-RestMethod -uri $messagesUri -Headers $authHeader } function Start-AzOpenAIAssistantThreadWithMessages { param ( [Parameter(Mandatory = $true)] [string]$AssistantId, [Parameter(Mandatory = $true)] [string]$Endpoint, [Parameter(Mandatory)] [string]$MessageContent, [switch]$Async ) $authHeader = @{ Authorization = "Bearer $(Get-AzOpenAIToken)" } # Create thread messages $messages = @( @{ role = "user" content = $MessageContent } ) # Create the body for starting a thread $bodyThread = @{ assistant_id = $AssistantId thread = @{ messages = $messages } } | ConvertTo-Json -Depth 100 # Endpoint URL to start a new thread $threadUri = "$Endpoint/openai/threads/runs?api-version=2024-03-01-preview" # Send the API request to start a new thread $threadResponse = Invoke-RestMethod -uri $threadUri -Method Post -Headers $authHeader -ContentType "application/json" -Body $bodyThread $ThreadID = $threadResponse.thread_id if (-not $Async) { # Check status and wait for completion if not in Async mode $i = 0 do { $i++ Write-Host "Checking status $i" $statusUri = "$Endpoint/openai/threads/$ThreadID/runs/$($threadResponse.id)?api-version=2024-03-01-preview" $runResult = Invoke-RestMethod -uri $statusUri -Headers $authHeader $runResult | Select-Object id, assistant_id, thread_id, status, last_error Start-Sleep -Seconds 10 } while ($runResult.status -ne "completed" -and $i -lt 100) if ($runResult.status -eq "completed") { # Get results if the run has completed $messagesUri = "$Endpoint/openai/threads/$ThreadID/messages?api-version=2024-03-01-preview" $resultMessage = Invoke-RestMethod -uri $messagesUri -Headers $authHeader $resultMessage.data.ForEach({ $_.content.text | Select-Object -ExpandProperty value }) } else { Write-Host "Run did not complete within the specified time." } } else { Write-Host "Thread run started in Async mode. Use Get-AzOpenAIAssistantThreadStatus to check the thread status later." } # Return the thread and run ID for later status checks if needed return @{ ThreadID = $ThreadID RunID = $threadResponse.id } } function Remove-AzOpenAIAssistants { param ( [string]$Endpoint ) $authHeader = @{ Authorization = "Bearer $(Get-AzOpenAIToken)" } $assistantsUri = "$Endpoint/openai/assistants?api-version=2024-02-15-preview" $assistants = Invoke-RestMethod -uri $assistantsUri -Headers $authHeader foreach ($assistant in $assistants.data) { Write-Host "Deleting $($assistant.id)" $deleteUri = "$Endpoint/openai/assistants/$($assistant.id)?api-version=2024-02-15-preview" Invoke-RestMethod -uri $deleteUri -Headers $authHeader -Method Delete } } function Get-AzOpenAIAssistantOutputFiles { param ( [Parameter(Mandatory = $true)] [string]$Endpoint, [string]$AssistantApiFileOutputName, [string]$LocalFilePath ) $authHeader = @{ Authorization = "Bearer $(Get-AzOpenAIToken)" } # Define the URI for getting the list of files $uri = "$Endpoint/openai/files?api-version=2024-02-15-preview" # Retrieve the list of files $getContent = Invoke-RestMethod -uri $uri -Headers $authHeader -Method Get # Check if a specific file name is provided if (![string]::IsNullOrWhiteSpace($AssistantApiFileOutputName)) { # Find the specific file based on filename $findMyFile = $getContent.data | Where-Object {$_.filename -eq "/mnt/data/$AssistantApiFileOutputName"} # If file is found, proceed to download if ($findMyFile -ne $null -and ![string]::IsNullOrWhiteSpace($LocalFilePath)) { $downloadUri = "$Endpoint/openai/files/$($findMyFile.id)/content?api-version=2024-02-15-preview" $getFileContent = Invoke-RestMethod -uri $downloadUri -Headers $authHeader -Method Get # Write the file content to a JSON file at the local machine path provided $getFileContent | ConvertTo-Json -Depth 100 | Out-File -FilePath $LocalFilePath -Force -Verbose } elseif ($findMyFile -eq $null) { Write-Error "File not found: $AssistantApiFileOutputName" } else { Write-Error "Local file path must be provided to download the file." } } else { # Return the list of all files if no specific filename is provided return $getContent.data } } Export-ModuleMember -Function Get-AzOpenAIToken, Get-AzOpenAIAssistantOutputFiles, Start-AzOpenAIAssistantThreadWithMessages, Get-AzOpenAIAssistantThread, Invoke-AzOpenAIUploadFile, New-AzOpenAIAssistant, Get-AzOpenAIAssistant, New-AzOpenAIAssistantFunction, New-AzOpenAIAssistantThread, Post-AzOpenAIAssistantMessage, Start-AzOpenAIAssistantThreadRun, Get-AzOpenAIAssistantThreadStatus, Get-AzOpenAIAssistantMessages, Remove-AzOpenAIAssistants