Buckets:
| /** | |
| * Shared model resolution utilities for API routes. | |
| * | |
| * Extracts the repeated parseModelString → resolveApiKey → resolveBaseUrl → | |
| * resolveProxy → getModel boilerplate into a single call. | |
| */ | |
| import type { NextRequest } from 'next/server'; | |
| import { getModel, parseModelString, type ModelWithInfo } from '@/lib/ai/providers'; | |
| import type { ThinkingConfig } from '@/lib/types/provider'; | |
| import { resolveApiKey, resolveBaseUrl, resolveProxy } from '@/lib/server/provider-config'; | |
| import { validateUrlForSSRF } from '@/lib/server/ssrf-guard'; | |
| export interface ResolvedModel extends ModelWithInfo { | |
| /** Original model string (e.g. "openai/gpt-4o-mini") */ | |
| modelString: string; | |
| /** Resolved provider ID (e.g. "openai", "ollama") */ | |
| providerId: string; | |
| /** Effective API key after server-side fallback resolution */ | |
| apiKey: string; | |
| /** Optional per-request thinking configuration from the client. */ | |
| thinkingConfig?: ThinkingConfig; | |
| } | |
| /** | |
| * Resolve a language model from explicit parameters. | |
| * | |
| * Use this when model config comes from the request body. | |
| */ | |
| export async function resolveModel(params: { | |
| modelString?: string; | |
| apiKey?: string; | |
| baseUrl?: string; | |
| providerType?: string; | |
| thinkingConfig?: ThinkingConfig; | |
| }): Promise<ResolvedModel> { | |
| const modelString = params.modelString || process.env.DEFAULT_MODEL || 'gpt-5.4-mini'; | |
| const { providerId, modelId } = parseModelString(modelString); | |
| // SSRF validation applies only to client-supplied base URLs. | |
| // Server-configured URLs (e.g. OLLAMA_BASE_URL from env/YAML) flow through | |
| // resolveBaseUrl() and bypass this check — they're trusted by the operator. | |
| const clientBaseUrl = params.baseUrl || undefined; | |
| if (clientBaseUrl && process.env.NODE_ENV === 'production') { | |
| const ssrfError = await validateUrlForSSRF(clientBaseUrl); | |
| if (ssrfError) { | |
| throw new Error(ssrfError); | |
| } | |
| } | |
| const apiKey = clientBaseUrl | |
| ? params.apiKey || '' | |
| : resolveApiKey(providerId, params.apiKey || ''); | |
| const baseUrl = clientBaseUrl ? clientBaseUrl : resolveBaseUrl(providerId, params.baseUrl); | |
| const proxy = resolveProxy(providerId); | |
| const { model, modelInfo } = getModel({ | |
| providerId, | |
| modelId, | |
| apiKey, | |
| baseUrl, | |
| proxy, | |
| providerType: params.providerType as 'openai' | 'anthropic' | 'google' | undefined, | |
| }); | |
| return { | |
| model, | |
| modelInfo, | |
| modelString, | |
| providerId, | |
| apiKey, | |
| thinkingConfig: params.thinkingConfig, | |
| }; | |
| } | |
| function getThinkingConfigFromBody(body: unknown): ThinkingConfig | undefined { | |
| if (!body || typeof body !== 'object') return undefined; | |
| const record = body as { thinkingConfig?: unknown; thinking?: unknown }; | |
| const config = record.thinkingConfig ?? record.thinking; | |
| return config && typeof config === 'object' ? (config as ThinkingConfig) : undefined; | |
| } | |
| /** | |
| * Resolve a language model from standard request headers. | |
| * | |
| * Reads: x-model, x-api-key, x-base-url, x-provider-type | |
| * Note: requiresApiKey is derived server-side from the provider registry, | |
| * never from client headers, to prevent auth bypass. | |
| */ | |
| export async function resolveModelFromHeaders(req: NextRequest): Promise<ResolvedModel> { | |
| return resolveModel({ | |
| modelString: req.headers.get('x-model') || undefined, | |
| apiKey: req.headers.get('x-api-key') || undefined, | |
| baseUrl: req.headers.get('x-base-url') || undefined, | |
| providerType: req.headers.get('x-provider-type') || undefined, | |
| }); | |
| } | |
| /** | |
| * Resolve a language model from standard request headers plus body fields. | |
| * | |
| * Reads model credentials from headers and per-request thinking config from | |
| * the JSON body field `thinkingConfig` (or legacy/eval field `thinking`). | |
| */ | |
| export async function resolveModelFromRequest( | |
| req: NextRequest, | |
| body: unknown, | |
| ): Promise<ResolvedModel> { | |
| const resolved = await resolveModelFromHeaders(req); | |
| return { | |
| ...resolved, | |
| thinkingConfig: getThinkingConfigFromBody(body) ?? resolved.thinkingConfig, | |
| }; | |
| } | |
Xet Storage Details
- Size:
- 3.99 kB
- Xet hash:
- 1dfd851aaa1fdd98af1c52d3b82046fd6aeadd2eb001fa1a0cdeff743434dc9b
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.