Providers/Gemini.ps1

<#
.SYNOPSIS
    Invokes the Google Gemini API to generate responses using specified models.
 
.DESCRIPTION
    The Invoke-GeminiProvider function sends requests to the Google Gemini API and returns the generated content.
    It requires an API key to be set in the environment variable 'GeminiKey'.
 
.PARAMETER ModelName
    The name of the Gemini model to use (e.g., 'gemini-1.0-pro', 'gemini-2.0-flash-exp').
    Note: Use the exact model name as specified by Google without any prefix.
 
.PARAMETER Messages
    An array of hashtables containing the messages to send to the model.
 
.EXAMPLE
    $Message = New-ChatMessage -Prompt 'Explain how CRISPR works'
    $response = Invoke-GeminiProvider -ModelName 'gemini-1.5-pro' -Message $Message
     
.NOTES
    Requires the GeminiKey environment variable to be set with a valid API key.
    The API key is passed as a URL parameter rather than in the headers.
    API Reference: https://ai.google.dev/gemini-api/docs
#>

function Invoke-GeminiProvider {
    param(
        [Parameter(Mandatory)]
        [string]$ModelName,
        [Parameter(Mandatory)]
        [hashtable[]]$Messages
    )
    
    if (-not $env:GeminiKey) {
        throw "Gemini API key not found. Please set the GeminiKey environment variable."
    }
    
    $apiKey = $env:GeminiKey
    
    foreach ($Msg in $Messages) {
        if ($Msg.role -eq 'system') {
            $SystemRole = $Msg.content
        }
        elseif ($Msg.role -eq 'user') {
            $Prompt = $Msg.content
        }
        else {
            throw "Invalid message role: $($Msg.role)"
        }
    }
   
    $body = @{
        'contents' = @(
            @{
                'role'  = 'user'
                'parts' = @(
                    @{
                        'text' = $Prompt
                    }
                )
            }
        )
    }
    
    if ($SystemRole) {
        $body['system_instruction'] = @{
            'parts' = @(
                @{
                    'text' = $SystemRole
                }
            )
        }
    }

    # Gemini uses the API key as a URL parameter
    # Fix model name - should be exactly as Google specifies (don't add prefix)
    # $Uri = "https://generativelanguage.googleapis.com/v1/models/$ModelName`:generateContent?key=$apiKey"
    
    $Uri = "https://generativelanguage.googleapis.com/v1beta/models/$($ModelName):generateContent?key=$apiKey"
    
    $params = @{
        Uri     = $Uri
        Method  = 'POST'
        Headers = @{'content-type' = 'application/json' }
        Body    = $body | ConvertTo-Json -Depth 10
    }
    
    try {
        $response = Invoke-RestMethod @params
        # Gemini has a different response structure
        return $response.candidates[0].content.parts[0].text
    }
    catch {
        $statusCode = $_.Exception.Response.StatusCode.value__
        $errorMessage = $_.ErrorDetails.Message
        Write-Error "Gemini API Error (HTTP $statusCode): $errorMessage"
        return "Error calling Gemini API: $($_.Exception.Message)"
    }
}