private/Invoke-OllamaRequest.ps1
|
function Resolve-OllamaBaseUri { <# .SYNOPSIS Normalizes an Ollama endpoint to a base URI with no trailing slash or API path. .DESCRIPTION Accepts "http://host:11434", "http://host:11434/" or a full path such as "http://host:11434/api/chat" and returns "http://host:11434" so callers can append the API path they need. #> [CmdletBinding()] param ( [Parameter(Mandatory=$true)][string]$Endpoint ) $base = $Endpoint.Trim().TrimEnd('/') $base = $base -replace '/api(/[^/]+)*$', '' return $base } function Invoke-OllamaRequest { <# .SYNOPSIS Invokes a chat request against an on-premises Ollama server with the given prompt and file. .DESCRIPTION Reads the specified diagnostics file and sends its contents inline, together with the prompt, to the Ollama /api/chat endpoint. Ollama has no file upload API, so the JSON content is embedded in the user message. Returns the response text from the model. Works in Windows PowerShell 5.1 and PowerShell 7+. .PARAMETER Prompt The prompt text to send to the model. .PARAMETER File The path to the diagnostics file whose content is included in the request. .PARAMETER Endpoint The Ollama server base URI. Defaults to "http://localhost:11434". A full "/api/chat" URI is also accepted. .PARAMETER ApiKey Optional. Ollama does not require authentication. If supplied (or if OLLAMA_API_KEY is set), it is sent as a Bearer token, which is useful when Ollama sits behind a reverse proxy. .PARAMETER Model The model to use. Defaults to "llama3.1:8b". The model must already be pulled on the server. .PARAMETER NumCtx Context window size in tokens. Defaults to 16384. Ollama's own default is small and will silently truncate large diagnostics, so raise this for multi-scope collections. .PARAMETER MaxTokens Maximum number of tokens to generate (num_predict). Defaults to the server/model setting. .PARAMETER TimeoutSec Request timeout in seconds. Defaults to 300. Local inference can be slow on modest hardware. .PARAMETER KeepAlive How long the model stays loaded after the request (for example "10m"). Defaults to the server setting. .PARAMETER Format Optional. Set to "json" to constrain the model output to valid JSON. .EXAMPLE Invoke-OllamaRequest -Prompt "Analyze this data" -File "Diagnostics.json" -Endpoint "http://ollama01.contoso.local:11434" -Model "qwen2.5:14b" .NOTES The -Prompt and -File parameters are provided from Invoke-AIDiagnostics automatically. A warning is written when the file is estimated to exceed about 80 percent of NumCtx. #> [CmdletBinding()] param ( [Parameter(Mandatory=$true)][string]$Prompt, [Parameter(Mandatory=$true)][string]$File, [Parameter(Mandatory=$false)][string]$Endpoint = "http://localhost:11434", [Parameter(Mandatory=$false)][string]$ApiKey, [Parameter(Mandatory=$false)][string]$Model = "llama3.1:8b", [Parameter(Mandatory=$false)][ValidateRange(512, 1048576)][int]$NumCtx = 16384, [Parameter(Mandatory=$false)][int]$MaxTokens, [Parameter(Mandatory=$false)][ValidateRange(1, 86400)][int]$TimeoutSec = 300, [Parameter(Mandatory=$false)][string]$KeepAlive, [Parameter(Mandatory=$false)][ValidateSet('json')][string]$Format ) Write-Verbose "Invoking Ollama request with model $Model" if (-not $ApiKey) { $ApiKey = $env:OLLAMA_API_KEY } if (-not (Test-Path $File)) { throw "File not found: $File" } $baseUri = Resolve-OllamaBaseUri -Endpoint $Endpoint $chatUri = "$baseUri/api/chat" Write-Verbose "Chat endpoint: $chatUri" $data = Get-Content -Path $File -Raw -ErrorAction Stop $estimatedTokens = [math]::Ceiling($data.Length / 4) if ($estimatedTokens -gt ($NumCtx * 0.8)) { $sizeWarning = "Diagnostics data is roughly $estimatedTokens tokens, which is close to or above the context window ($NumCtx). " + "The model may truncate its input. Increase numCtx, reduce -MaxItems, or collect fewer scopes." Write-Warning $sizeWarning } $content = "$Prompt`n`nDiagnostics data (JSON):`n``````json`n$data`n``````" $options = @{ num_ctx = $NumCtx } if ($PSBoundParameters.ContainsKey('MaxTokens') -and $MaxTokens -gt 0) { $options.num_predict = $MaxTokens } $payload = @{ model = $Model stream = $false messages = @( @{ role = "user"; content = $content } ) options = $options } if (-not [string]::IsNullOrWhiteSpace($KeepAlive)) { $payload.keep_alive = $KeepAlive } if (-not [string]::IsNullOrWhiteSpace($Format)) { $payload.format = $Format } $Body = $payload | ConvertTo-Json -Depth 10 $Headers = @{} if (-not [string]::IsNullOrWhiteSpace($ApiKey)) { $Headers["Authorization"] = "Bearer $ApiKey" } $requestParams = @{ Uri = $chatUri Method = "Post" Headers = $Headers Body = ([System.Text.Encoding]::UTF8.GetBytes($Body)) ContentType = "application/json; charset=utf-8" TimeoutSec = $TimeoutSec ErrorAction = 'Stop' } try { $Response = Invoke-RestMethod @requestParams } catch { $detail = $null if ($_.ErrorDetails -and $_.ErrorDetails.Message) { $detail = $_.ErrorDetails.Message } elseif ($_.Exception.Response) { try { $reader = [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream()) $detail = $reader.ReadToEnd() } catch { } } throw "Ollama API request failed: $($_.Exception.Message)`n$detail" } if ($Response.done_reason -eq 'length') { Write-Warning "The model stopped because it reached the output token limit. Increase maxTokens if the response looks cut off." } $Response.message.content } |