Providers/Vercel.ps1
|
<#
.SYNOPSIS Invokes Vercel AI Gateway using its OpenAI-compatible Chat Completions API. .DESCRIPTION The Invoke-VercelProvider function sends requests to Vercel AI Gateway and returns generated text. AI Gateway can route a request to models from multiple providers by using a model name such as 'anthropic/claude-sonnet-4.6'. .PARAMETER ModelName The AI Gateway model name. Use the provider/model format documented by Vercel, for example 'openai/gpt-5.6' or 'anthropic/claude-sonnet-4.6'. .PARAMETER Messages An array of hashtables containing the messages to send to the model. .PARAMETER Tools An array of tool definitions for function calling. Strings are resolved as PowerShell command names; hashtables can be supplied as provider-neutral or OpenAI-compatible tool definitions. .PARAMETER MaxIterations The maximum number of tool-calling rounds allowed before the request stops. .EXAMPLE $message = New-ChatMessage -Prompt 'Explain edge computing in one paragraph.' Invoke-VercelProvider -ModelName 'openai/gpt-5.6' -Messages $message .EXAMPLE Invoke-ChatCompletion -Model 'vercel:anthropic/claude-sonnet-4.6' ` -Messages 'List the files in the current directory.' ` -Tools Get-ChildItem .NOTES Requires AI_GATEWAY_API_KEY or VERCEL_OIDC_TOKEN to be set. API reference: https://vercel.com/docs/ai-gateway/openai-compat/rest-api #> function Invoke-VercelProvider { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$ModelName, [Parameter(Mandatory)] [hashtable[]]$Messages, [object[]]$Tools, [ValidateRange(1, 100)] [int]$MaxIterations = 5 ) $apiKey = if ($env:AI_GATEWAY_API_KEY) { $env:AI_GATEWAY_API_KEY } elseif ($env:VERCEL_OIDC_TOKEN) { $env:VERCEL_OIDC_TOKEN } if ([string]::IsNullOrWhiteSpace($apiKey)) { $activity = New-AgentActivity -Provider 'Vercel' -Model $ModelName -MaxIterations $MaxIterations Write-AgentActivity -Activity $activity -Message 'Request failed: Vercel AI Gateway credential is missing' -Completed Write-Error 'Please set the AI_GATEWAY_API_KEY or VERCEL_OIDC_TOKEN environment variable with a valid Vercel AI Gateway credential.' return } if ($Tools) { $toolDefinitions = New-Object System.Collections.Generic.List[object] foreach ($tool in $Tools) { if ($tool -is [string]) { $toolDefinitions.Add((Register-Tool $tool)) } else { $toolDefinitions.Add($tool) } } # AI Gateway follows the OpenAI Chat Completions tool schema. $Tools = ConvertTo-ProviderToolSchema -Tools $toolDefinitions -Provider openai } $headers = @{ Authorization = "Bearer $apiKey" 'Content-Type' = 'application/json' } $body = [ordered]@{ model = $ModelName messages = [hashtable[]]$Messages stream = $false } if ($Tools) { $body.tools = @($Tools) $body.tool_choice = 'auto' } $uri = 'https://ai-gateway.vercel.sh/v1/chat/completions' $iteration = 0 $activity = New-AgentActivity -Provider 'Vercel' -Model $ModelName -MaxIterations $MaxIterations try { while ($iteration -lt $MaxIterations) { Write-AgentActivity -Activity $activity -Message 'Waiting for response' -Round ($iteration + 1) $params = @{ Uri = $uri Method = 'POST' Headers = $headers Body = $body | ConvertTo-Json -Depth 20 } try { $response = Invoke-RestMethod @params } catch { $statusCode = $null if ($_.Exception.Response) { $statusCode = $_.Exception.Response.StatusCode.value__ } $errorMessage = $_.ErrorDetails.Message if ([string]::IsNullOrWhiteSpace($errorMessage)) { $errorMessage = $_.Exception.Message } Write-AgentActivity -Activity $activity -Message 'Request failed' -Completed if ($statusCode) { Write-Error "Vercel AI Gateway API Error (HTTP $statusCode): $errorMessage" } else { Write-Error "Vercel AI Gateway API Error: $errorMessage" } return "Error calling Vercel AI Gateway: $($_.Exception.Message)" } if ($response.error) { $errorMessage = if ($response.error.message) { $response.error.message } else { $response.error | Out-String } Write-AgentActivity -Activity $activity -Message 'Request failed: provider returned an error' -Completed Write-Error "Vercel AI Gateway API Error: $errorMessage" return "Error: $errorMessage" } if (-not $response.choices -or @($response.choices).Count -eq 0) { Write-AgentActivity -Activity $activity -Message 'Request failed: no choices in response' -Completed return 'No choices in response from Vercel AI Gateway.' } $assistantMessage = $response.choices[0].message $toolCalls = @() if ($assistantMessage.tool_calls) { $toolCalls = @($assistantMessage.tool_calls) } if ($toolCalls.Count -gt 0) { Write-AgentActivity -Activity $activity -Message "Model requested $($toolCalls.Count) tool call(s)" $body.messages += $assistantMessage foreach ($call in $toolCalls) { $functionName = $call.function.name $functionArgs = @{} $toolStopwatch = [System.Diagnostics.Stopwatch]::StartNew() Write-AgentActivity -Activity $activity -Message "Tool started: $functionName" if ($call.function.arguments) { try { $functionArgs = $call.function.arguments | ConvertFrom-Json -AsHashtable } catch { $functionArgs = @{} } } $toolFailed = $false try { if (Get-Command Invoke-OpenAITool -ErrorAction SilentlyContinue) { $result = Invoke-OpenAITool -FunctionName $functionName -FunctionArgs $functionArgs $toolFailed = ([string]$result) -match '^Error(?: executing|:)' } elseif (Get-Command $functionName -ErrorAction SilentlyContinue) { $toolResult = @(& $functionName @functionArgs 2>&1) $toolFailed = @($toolResult | Where-Object { $_ -is [System.Management.Automation.ErrorRecord] }).Count -gt 0 $result = $toolResult | Out-String } else { $toolFailed = $true $result = "Error: Function $functionName not found" } } catch { $toolFailed = $true $result = "Error executing $functionName`: $($_.Exception.Message)" } $toolStopwatch.Stop() $toolStatus = if ($toolFailed) { 'Tool failed' } else { 'Tool completed' } Write-AgentActivity -Activity $activity -Message "$toolStatus`: $functionName ($($toolStopwatch.ElapsedMilliseconds) ms)" $body.messages += @{ role = 'tool' tool_call_id = $call.id content = [string]$result } } $iteration++ continue } $content = $assistantMessage.content if ($content -is [array]) { $content = ($content | ForEach-Object { if ($_.text) { $_.text } else { [string]$_ } }) -join '' } if ([string]::IsNullOrWhiteSpace([string]$content)) { Write-AgentActivity -Activity $activity -Message 'Request failed: no text in response' -Completed return 'No text content in response from Vercel AI Gateway.' } Write-AgentActivity -Activity $activity -Message 'Response completed' -Completed return [string]$content } Write-AgentActivity -Activity $activity -Message 'Stopped: maximum iterations reached' -Completed return "Maximum iterations reached without completing the response after $MaxIterations iterations." } finally { Write-AgentActivity -Activity $activity -Message 'Request stopped' -Completed } } |