Skip to content

Instantly share code, notes, and snippets.

@SMSAgentSoftware
Created December 13, 2024 21:46
PowerShell function to interact with Google's Gemini AI models via the REST API. Requires the API key to be stored in an Azure KeyVault.
function Invoke-GoogleAIChat {
<#
.SYNOPSIS
PowerShell function for Google Chat API interaction via REST.
.DESCRIPTION
Provides a robust interface to Google Chat API with features including:
- Conversation history management
- Token usage tracking
- Markdown rendering support
- Temperature control for response creativity
- Verbosity levels for response detail
- Browser-based markdown viewing option
.PARAMETER Prompt
The message to send to the Google Chat API.
Mandatory: Yes
Pipeline: Accepts input
Position: 0
.PARAMETER Endpoint
The Google API endpoint URL.
Mandatory: No
Default: Standard endpoint URL
Validation: Must be HTTPS URL
.PARAMETER Model
The deployment name of the AI model.
Mandatory: No
Default: o1-mini
Valid values: You define the values that the parameter can accept.
.PARAMETER KeyVaultName
The name of your Azure Key Vault.
Mandatory: No
.PARAMETER SecretName
The name of the secret in your Azure Key Vault containing the Google API key.
Mandatory: No
.PARAMETER MaxTokens
The maximum number of tokens to generate in the response.
Mandatory: No
Default: 16384
Range: 1 - 16384
.PARAMETER IncludeTokenUsage
Switch to include token usage statistics in response.
Mandatory: No
.PARAMETER Temperature
Controls response creativity and randomness.
Mandatory: No
Default: 0.2
Range: 0.0 - 2.0
.PARAMETER StartNewChat
Switch to start a new chat session / clear chat history.
Mandatory: No
.PARAMETER ModelContext
System context/prompt for AI behavior.
Mandatory: No
Default: PowerShell expert assistant context
.PARAMETER NoMarkdown
Switch to disable markdown rendering.
Mandatory: No
.PARAMETER UseBrowser
Switch to view markdown in browser.
Mandatory: No
.PARAMETER Verbosity
Controls response detail level.
Mandatory: No
Default: medium
Valid values: none, low, medium, high
.EXAMPLE
PS> Invoke-GoogleAIChat "Write a function to get system info"
Returns AI-generated PowerShell code with explanations
.EXAMPLE
PS> "How to use arrays?" | Invoke-GoogleAIChat -Temperature 0.8 -Verbosity high
Returns detailed array explanation with high creativity
.NOTES
Author: Trevor Jones
Version: 1.0
Requires: Az.Accounts, Az.KeyVault modules
#>
[CmdletBinding()]
param (
# Main prompt parameter
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
Position = 0)]
[ValidateNotNullOrEmpty()]
[string]$Prompt,
# OpenAI endpoint URL
[Parameter(Mandatory = $false)]
[ValidatePattern('^https:\/\/.*')]
[string]$Endpoint = "https://generativelanguage.googleapis.com/v1beta/models",
# AI model selection
[Parameter(Mandatory = $false)]
[ValidateSet("gemini-2.0-flash-exp","gemini-1.5-flash","gemini-1.5-pro")]
[string]$Model = "gemini-1.5-pro",
# Azure Key Vault name
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$KeyVaultName = "<YourAzureKeyVaultName>",
# Azure Key Vault secret name
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$SecretName = "<YourGoogleAPISecretName>",
# Maximum number of tokens to generate
[Parameter(Mandatory = $false)]
[ValidateRange(1, 8192)]
[int]$MaxTokens = 8192,
# Token usage tracking switch
[Parameter(Mandatory = $false)]
[switch]$IncludeTokenUsage,
# Response creativity control
[Parameter(Mandatory = $false)]
[ValidateRange(0.0, 2.0)]
[double]$Temperature = 0.2,
# New chat session control
[Parameter(Mandatory = $false)]
[switch]$StartNewChat,
# AI behavior context
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$ModelContext = "You are a helpful and friendly assistant with expertise in PowerShell scripting and command line. Assume user is using the operating system 'Windows 11' unless otherwise specified. ",
# Output format controls
[Parameter(Mandatory = $false)]
[switch]$NoMarkdown,
[Parameter(Mandatory = $false)]
[switch]$UseBrowser,
# Response detail level
[Parameter(Mandatory = $false)]
[ValidateSet("none", "low", "medium", "high")]
[string]$Verbosity = "medium"
)
begin {
# Module dependency check
$requiredModules = @("Az.Accounts", "Az.KeyVault")
# Find modules that are not installed
$missingModules = $requiredModules | Where-Object { -not (Get-Module -Name $_ -ListAvailable) }
# Install missing modules if any
if ($missingModules) {
try {
Install-Module -Name $missingModules -Force -AllowClobber -Scope CurrentUser -Repository PSGallery -ErrorAction Stop
}
catch {
throw "Failed to install module(s) '$($missingModules -join ', ')': $_"
}
}
Import-Module Az.Accounts -ErrorAction Stop
Import-Module Az.KeyVault -ErrorAction Stop
# Retrieve secret from Azure Key Vault
try
{
$secureSecretValue = Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name $SecretName -ErrorAction Stop
}
catch
{
try
{
Connect-AzAccount -AuthScope KeyVault -WarningAction SilentlyContinue -ErrorAction Stop
$secureSecretValue = Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name $SecretName -ErrorAction Stop
}
catch
{
Write-Error "An error occurred while retrieving the secret from Azure KeyVault: $_"
return $null
}
}
# Convert the secure string secret to plain text
if ($PSVersionTable.PSVersion -ge [Version]"7.0") {
$secretValue = $secureSecretValue.SecretValue | ConvertFrom-SecureString -AsPlainText
}
else {
$secretValue = [PSCredential]::new('dummy', $secureSecretValue.SecretValue).GetNetworkCredential().Password
}
# Set verbosity level text
$verbosityText = switch ($Verbosity) {
"none" { "Just provide the code and do not provide any explanation of it." }
"low" { "Please keep the response concise and to the point. Focus on providing code, and just explain it very briefly." }
"medium" { "Please keep the response concise. Provide a reasonable level of explanation for the code, adding some inline comments." }
"high" { "Provide a detailed explanation of the code and add inline comments to it." }
}
$ModelContext += $verbosityText
# Initialize chat history
if ($StartNewChat -or -not (Get-Variable -Name googleaichatmessages -ErrorAction SilentlyContinue)) {
$global:googleaichatmessages = @()
}
}
process {
try {
# Add user prompt to chat history
$global:googleaichatmessages += @{
role = "user"
parts = @(
@{
text = $Prompt
}
)
}
# Construct a body object for the API request
$body = @{
"contents" = @($global:googleaichatmessages)
"generationConfig" = @{
"temperature" = $Temperature
"maxOutputTokens" = $MaxTokens
}
"systemInstruction" = @{
"role" = "model"
"parts" = @(
@{
"text" = $ModelContext
}
)
}
} | ConvertTo-Json -Depth 5
# Create the headers for the request
$headers = @{
"content-type" = "application/json"
}
# Contruct the URL for the API request
$url = "$Endpoint/$model`:generateContent?key=$secretValue"
# Make API request with comprehensive error handling
try {
$response = Invoke-RestMethod -Uri $url -Method Post -Headers $headers -Body $body -ErrorAction Stop
}
catch [System.Net.WebException] {
$statusCode = [int]$_.Exception.Response.StatusCode
$errorMessage = switch ($statusCode) {
401 { "Unauthorized: Please check your authentication token" }
403 { "Forbidden: Access denied to the API endpoint" }
404 { "Not Found: Invalid endpoint or model deployment" }
429 { "Rate Limited: Too many requests, please try again later" }
500 { "Internal Server Error: Azure OpenAI service error" }
503 { "Service Unavailable: Azure OpenAI service is temporarily unavailable" }
default {
$reader = New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream())
$reader.ReadToEnd()
}
}
throw "API Request Failed ($statusCode): $errorMessage"
}
catch {
throw "Unexpected error during API request: $_"
}
# Update chat history with response
$global:googleaichatmessages += @{
role = 'model'
parts = @(
@{
text = $response.candidates[0].content.parts[0].text
}
)
}
# Format response
$content = $response.candidates[0].content.parts[0].text
if ($IncludeTokenUsage) {
$content += "`n`nPrompt tokens: $($response.usageMetadata.promptTokenCount) | "
$content += "Candidates tokens: $($response.usageMetadata.candidatesTokenCount) | "
$content += "Total tokens: $($response.usageMetadata.totalTokenCount)"
}
# Handle markdown rendering based on PowerShell version and preferences
if ($PSVersionTable.PSVersion -ge [Version]"6.1" -and -not $NoMarkdown) {
if ($UseBrowser) {
return $content | Show-Markdown -UseBrowser
}
return $content | Show-Markdown
}
return $content
}
catch {
Write-Error "Unexpected error: $_"
return $null
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment