Demos/Invoke-AICompare.ps1

#requires -Version 7.0
#requires -Module PSAI

<#
.SYNOPSIS
    A script to compare responses from multiple AI providers in parallel.
.DESCRIPTION
    This script takes a prompt and an array of provider:model strings, sends the prompt to each
.EXAMPLE
    $models = @(
        'openai:gpt-4.1',
        'xAI:grok-4-1-fast-non-reasoning',
        'anthropic:claude-sonnet-4-5-20250929',
        'google:gemini-flash-latest'
    )
    .\Invoke-AICompare.ps1 -Prompt "Date: $(Get-Date) - latest PowerShell news" -Models $models
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory = $true, HelpMessage = "The query or instruction for the models.")]
    [string]$Prompt,

    [Parameter(Mandatory = $true, HelpMessage = "Array of 'provider:model' strings.")]
    [string[]]$Models,

    [Parameter(Mandatory = $false)]
    [object[]]$Tools
)

Write-Host "`n🚀 Parallel Bake-off: Sending prompt to $($Models.Count) models..." -ForegroundColor Cyan
Write-Host "Prompt: $Prompt`n" -ForegroundColor Yellow

# Create Windows Form for displaying results
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$form = New-Object System.Windows.Forms.Form
$form.Text = "AI Model Comparison Results"
$form.Size = New-Object System.Drawing.Size(800, 600)
$form.StartPosition = 'CenterScreen'

# Top label with details
$label = New-Object System.Windows.Forms.Label
$label.Text = "🚀 Parallel Bake-off: Sending prompt to $($Models.Count) models...$([Environment]::NewLine)Prompt: $Prompt"
$label.AutoSize = $true
$label.Location = New-Object System.Drawing.Point(10, 10)
$label.Font = New-Object System.Drawing.Font("Arial", 10, [System.Drawing.FontStyle]::Bold)
$form.Controls.Add($label)

# TabControl
$tabControl = New-Object System.Windows.Forms.TabControl
$tabControl.Location = New-Object System.Drawing.Point(10, 60)
$tabControl.Size = New-Object System.Drawing.Size(760, 490)
$tabControl.DrawMode = 'OwnerDrawFixed'
$tabControl.Add_DrawItem({
        param($sender, $e)
        $tabPage = $tabControl.TabPages[$e.Index]
        $e.Graphics.FillRectangle((New-Object System.Drawing.SolidBrush($tabPage.BackColor)), $e.Bounds)
        $brush = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::Black)
        $e.Graphics.DrawString($tabPage.Text, $tabControl.Font, $brush, $e.Bounds.X + 3, $e.Bounds.Y + 3)
        $brush.Dispose()
    })
$form.Controls.Add($tabControl)

# Create tabPages array with empty textboxes
$tabPages = @()
foreach ($model in $Models) {
    $tabPage = New-Object System.Windows.Forms.TabPage
    $tabPage.Text = $model
    $tabPage.BackColor = [System.Drawing.Color]::Yellow
    $tabPage.ForeColor = [System.Drawing.Color]::Black

    $textBox = New-Object System.Windows.Forms.TextBox
    $textBox.Multiline = $true
    $textBox.ReadOnly = $true
    $textBox.ScrollBars = 'Vertical'
    $textBox.WordWrap = $true
    $textBox.Text = "Processing..."
    $textBox.Dock = 'Fill'
    $textBox.Font = New-Object System.Drawing.Font("Consolas", 9)

    $tabPage.Controls.Add($textBox)
    $tabControl.TabPages.Add($tabPage)
    $tabPages += @{Model = $model; TextBox = $textBox; TabPage = $tabPage }
}

# Start background jobs for each model
$jobs = foreach ($model in $Models) {
    Start-Job -ScriptBlock {
        param($Prompt, $Tools, $Model)

        # Define the web search function in the job
        function Global:Invoke-WebSearch {
            param([Parameter(Mandatory)][string]$query)
            if (!$env:TAVILY_API_KEY) {
                return "Error: TAVILY_API_KEY environment variable is not set. Please set it to use the web search tool."
            }
            $tavilyParams = @{ api_key = $env:TAVILY_API_KEY; query = $query }
            $body = $tavilyParams | ConvertTo-Json -Depth 10
            Invoke-RestMethod -Method Post -Uri "https://api.tavily.com/search" -ContentType 'application/json' -Body $body
        }

        # Register tool
        $Tools += @(Register-Tool Invoke-WebSearch)

        $Timer = [System.Diagnostics.Stopwatch]::StartNew()

        try {
            $IccParams = @{ Prompt = $Prompt; Model = $Model }
            if ($Tools) { $IccParams.Tools = $Tools }
            $Response = Invoke-ChatCompletion @IccParams
            $Timer.Stop()
            [PSCustomObject]@{ Model = $Model; Status = "✅ Success"; Time = "$([math]::Round($Timer.Elapsed.TotalSeconds, 2))s"; Response = $Response }
        }
        catch {
            [PSCustomObject]@{ Model = $Model; Status = "❌ Failed"; Time = "$([math]::Round($Timer.Elapsed.TotalSeconds, 2))s"; Response = $_.Exception.Message }
        }
    } -ArgumentList $Prompt, $Tools, $model
}

# Timer to check for completed jobs and update textboxes
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 500  # Check every 500ms
$timer.Add_Tick({
        $completedJobs = $jobs | Where-Object { $_.State -eq 'Completed' }
        foreach ($job in $completedJobs) {
            $result = Receive-Job $job
            $tab = $tabPages | Where-Object { $_.Model -eq $result.Model }
            if ($tab) {
                $responseText = $result.Response -replace "`n", [Environment]::NewLine
                $tab.TextBox.Text = "Status: $($result.Status)$([Environment]::NewLine)Time: $($result.Time)$([Environment]::NewLine)$([Environment]::NewLine)Response:$([Environment]::NewLine)$responseText"
                $tab.TabPage.BackColor = [System.Drawing.Color]::Lime
                $tabControl.Invalidate()
            }
            $jobs = $jobs | Where-Object { $_ -ne $job }
        }
        if ($jobs.Count -eq 0) {
            $timer.Stop()
        }
    })
$timer.Start()

# Show the form (modal)
$null = $form.ShowDialog()