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)) { 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 while ($iteration -lt $MaxIterations) { $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 } 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-Error "Vercel AI Gateway API Error: $errorMessage" return "Error: $errorMessage" } if (-not $response.choices -or @($response.choices).Count -eq 0) { 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) { $body.messages += $assistantMessage foreach ($call in $toolCalls) { $functionName = $call.function.name $functionArgs = @{} if ($call.function.arguments) { try { $functionArgs = $call.function.arguments | ConvertFrom-Json -AsHashtable } catch { $functionArgs = @{} } } try { if (Get-Command Invoke-OpenAITool -ErrorAction SilentlyContinue) { $result = Invoke-OpenAITool -FunctionName $functionName -FunctionArgs $functionArgs } elseif (Get-Command $functionName -ErrorAction SilentlyContinue) { $result = & $functionName @functionArgs | Out-String } else { $result = "Error: Function $functionName not found" } } catch { $result = "Error executing $functionName`: $($_.Exception.Message)" } $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)) { return 'No text content in response from Vercel AI Gateway.' } return [string]$content } return "Maximum iterations reached without completing the response after $MaxIterations iterations." } |