Public/Invoke-PCCompletion.ps1

function Invoke-PCCompletion {
    <#
    .SYNOPSIS
        Send a prompt to an AI provider and get a response.
    .DESCRIPTION
        Unified completion function supporting OpenAI, Gemini, and Anthropic.
        Auto-detects provider from model name or uses the only configured provider.
    .EXAMPLE
        Invoke-PCCompletion -UserPrompt "What is PowerShell?"
    .EXAMPLE
        Invoke-PCCompletion -Provider openai -Model gpt-4o -SystemPrompt "Be concise" -UserPrompt "Explain closures"
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$UserPrompt,

        [string]$SystemPrompt,

        [ValidateSet('openai', 'gemini', 'anthropic')]
        [string]$Provider,

        [string]$Model,

        [int]$MaxTokens = 4096,

        [ValidateRange(0.0, 2.0)]
        [double]$Temperature = 0.3,

        [switch]$JsonMode,

        [switch]$WebSearch,

        [int]$TimeoutSec = 120
    )

    # Resolve provider
    if (-not $Provider) {
        if ($Model -match '^gemini') { $Provider = 'gemini' }
        elseif ($Model -match '^claude') { $Provider = 'anthropic' }
        elseif ($Model -match '^(gpt|o[1-9])') { $Provider = 'openai' }
        else {
            # Auto-detect: use the only configured provider
            $available = @()
            foreach ($p in $script:Providers.Keys) {
                if (Resolve-PCApiKey -Provider $p) { $available += $p }
            }
            if ($available.Count -eq 1) {
                $Provider = $available[0]
            }
            elseif ($available.Count -eq 0) {
                throw "No AI provider API keys configured. Set a key via Set-PCSecret or environment variable (OPENAI_API_KEY, GEMINI_API_KEY, ANTHROPIC_API_KEY)."
            }
            else {
                throw "Multiple providers configured ($($available -join ', ')). Specify -Provider or -Model to select one."
            }
        }
    }

    # Resolve model
    if (-not $Model) {
        $Model = $script:Providers[$Provider].DefaultModel
    }

    # Resolve API key
    $apiKey = Resolve-PCApiKey -Provider $Provider
    if (-not $apiKey) {
        throw "No API key found for provider '$Provider'. Set it via Set-PCSecret -Name '$($script:Providers[$Provider].SecretName)' or environment variable."
    }

    # Build messages array
    $messages = @()
    if ($SystemPrompt) {
        $messages += @{ role = 'system'; content = $SystemPrompt }
    }
    $messages += @{ role = 'user'; content = $UserPrompt }

    # Dispatch to provider
    $splat = @{
        ApiKey      = $apiKey
        Model       = $Model
        Messages    = $messages
        MaxTokens   = $MaxTokens
        Temperature = $Temperature
        JsonMode    = $JsonMode
        TimeoutSec  = $TimeoutSec
    }

    Write-Verbose "PCCompletion: Provider=$Provider Model=$Model MaxTokens=$MaxTokens"

    switch ($Provider) {
        'openai' {
            $splat['WebSearch'] = $WebSearch
            Invoke-PCOpenAIRequest @splat
        }
        'gemini' {
            Invoke-PCGeminiRequest @splat
        }
        'anthropic' {
            Invoke-PCAnthropicRequest @splat
        }
    }
}