import type { ProviderConfig, ProviderId } from '@/lib/llm/providers/types'; import type { WireFormat } from '@/lib/llm/providers/wire-format'; /** Resolve the upstream endpoint for a request, by wire format. */ export function getApiEndpoint( provider: ProviderId, config: ProviderConfig, model?: string, options?: { apiKey?: string; stream?: boolean }, overrideBaseUrl?: string, wireFormat?: WireFormat, ): string { const baseUrl = overrideBaseUrl || config.baseUrl || 'https://openrouter.ai/api/v1'; if (wireFormat === 'anthropic') { return provider === 'anthropic' ? 'https://api.anthropic.com/v1/messages' : `${baseUrl}/messages`; } else if (provider === 'gemini') { const geminiModel = model || 'gemini-2.5-flash'; const action = options?.stream ? 'streamGenerateContent?alt=sse' : 'generateContent'; const key = options?.apiKey ? `${options.stream ? '&' : '?'}key=${options.apiKey}` : ''; return `https://generativelanguage.googleapis.com/v1beta/models/${geminiModel}:${action}${key}`; } else { return `${baseUrl}/chat/completions`; } } /** Build request headers, by wire format. `referer` is the incoming request's referer (for OpenRouter attribution). */ export function buildHeaders( provider: ProviderId, apiKey: string | undefined, referer: string | null, config: ProviderConfig, wireFormat?: WireFormat, ): Record { const headers: Record = { 'Content-Type': 'application/json' }; if (wireFormat === 'anthropic') { headers['x-api-key'] = apiKey || ''; headers['anthropic-version'] = '2023-06-01'; if (provider === 'anthropic' && config.supportsFunctions) { headers['anthropic-beta'] = 'tools-2024-04-04'; } } else if (provider === 'gemini') { // Gemini uses query-param key auth; no auth headers needed } else { if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`; if (provider === 'openrouter') { headers['HTTP-Referer'] = referer || 'http://localhost:3000'; headers['X-Title'] = 'OSW-Studio'; } } return headers; } /** Sampling temperature. A few models reject anything but 1: OpenAI's gpt-5-nano and * Opencode Go's Moonshot/Kimi models. Everything else uses 0.7. */ export function resolveTemperature(provider: ProviderId, model: string): number { const m = (model || '').toLowerCase(); if (provider === 'openai' && m.includes('gpt-5-nano')) return 1; if (provider === 'opencode-go' && m.startsWith('kimi-')) return 1; return 0.7; }