Files
Aether/frontend/src/features/providers/components/endpoint-default-paths.ts
T
elky 531cf11025 feat(gateway): harden provider request execution
Preserve exact request payloads and model client surface and API operation explicitly.

Add Anthropic compatibility profiles, bounded stream commitment, and scoped OAuth retry behavior across provider transports.
2026-07-27 09:36:31 +08:00

211 lines
6.8 KiB
TypeScript

interface ApiFormatPathDefinition {
value: string
default_path: string
}
export function normalizeEndpointApiFormat(apiFormat: string): string {
switch (apiFormat.trim().toLowerCase()) {
default:
return apiFormat.trim().toLowerCase()
}
}
function isCodexUrl(baseUrl: string): boolean {
const url = baseUrl.replace(/\/+$/, '')
return url.includes('/backend-api/codex') || url.endsWith('/codex')
}
function parseBaseUrlParts(baseUrl?: string | null): { host: string; path: string } | null {
const raw = (baseUrl || '').trim()
if (!raw) return null
try {
const parsed = new URL(raw)
return {
host: parsed.hostname.toLowerCase(),
path: parsed.pathname.replace(/\/+$/, '').toLowerCase(),
}
} catch {
const pathStart = raw.indexOf('/')
return {
host: '',
path: pathStart >= 0 ? raw.slice(pathStart).split('?')[0].replace(/\/+$/, '').toLowerCase() : '',
}
}
}
function baseUrlHasPathApiRoot(baseUrl?: string | null): boolean {
const path = parseBaseUrlParts(baseUrl)?.path
return !!path && path !== '/'
}
function baseUrlEndsWithV1Root(baseUrl?: string | null): boolean {
return parseBaseUrlParts(baseUrl)?.path.endsWith('/v1') ?? false
}
function baseUrlHasVersionedApiRoot(baseUrl?: string | null): boolean {
const path = parseBaseUrlParts(baseUrl)?.path || ''
return /\/v\d+(?:beta\d*)?(?:\/|$)/i.test(path)
}
function isBigModelCodingApiRoot(baseUrl?: string | null): boolean {
const parts = parseBaseUrlParts(baseUrl)
return parts?.host === 'open.bigmodel.cn' && parts.path === '/api/coding/paas/v4'
}
function isDeepSeekApiRoot(baseUrl?: string | null): boolean {
const parts = parseBaseUrlParts(baseUrl)
return parts?.host === 'api.deepseek.com'
}
function isGoogleOpenAiCompatApiRoot(baseUrl?: string | null): boolean {
const parts = parseBaseUrlParts(baseUrl)
return parts?.host === 'generativelanguage.googleapis.com'
&& (parts.path === '/v1beta/openai' || parts.path === '/v1/openai')
}
function isVertexOpenAiCompatApiRoot(baseUrl?: string | null): boolean {
const parts = parseBaseUrlParts(baseUrl)
return !!parts
&& (parts.host === 'aiplatform.googleapis.com' || parts.host.endsWith('.aiplatform.googleapis.com') || parts.host.endsWith('-aiplatform.googleapis.com'))
&& parts.path.endsWith('/endpoints/openapi')
}
function openAiCompatibleBaseIncludesApiRoot(baseUrl?: string | null): boolean {
return baseUrlEndsWithV1Root(baseUrl)
|| baseUrlHasPathApiRoot(baseUrl)
|| isBigModelCodingApiRoot(baseUrl)
|| isGoogleOpenAiCompatApiRoot(baseUrl)
|| isVertexOpenAiCompatApiRoot(baseUrl)
}
function stripVersionPrefixForApiRoot(path: string): string {
return path.replace(/^\/v\d+(?:beta\d*)?(?=\/)/i, '')
}
function isOpenAiCompatibleFormat(apiFormat: string): boolean {
return apiFormat.startsWith('openai:') || apiFormat.startsWith('jina:')
}
function usesVersionedApiRootByDefault(apiFormat: string): boolean {
return apiFormat === 'openai:chat'
|| apiFormat === 'openai:responses'
|| apiFormat === 'openai:responses:compact'
|| apiFormat === 'openai:search'
|| apiFormat === 'openai:embedding'
|| apiFormat === 'openai:rerank'
|| apiFormat === 'openai:image'
|| apiFormat === 'openai:video'
|| apiFormat === 'jina:embedding'
|| apiFormat === 'jina:rerank'
|| apiFormat === 'claude:messages'
|| apiFormat === 'gemini:generate_content'
|| apiFormat === 'gemini:interactions'
|| apiFormat === 'gemini:embedding'
|| apiFormat === 'gemini:video'
}
function versionedApiRootSuffix(apiFormat: string): '/v1' | '/v1beta' {
if (
apiFormat === 'gemini:interactions'
) {
return '/v1'
}
if (
apiFormat === 'gemini:generate_content'
|| apiFormat === 'gemini:embedding'
|| apiFormat === 'gemini:video'
) {
return '/v1beta'
}
return '/v1'
}
function skipsVersionedApiRootDefault(apiFormat: string, baseUrl: string): boolean {
if (
apiFormat === 'gemini:generate_content'
|| apiFormat === 'gemini:interactions'
|| apiFormat === 'gemini:embedding'
|| apiFormat === 'gemini:video'
) {
return false
}
return isDeepSeekApiRoot(baseUrl)
|| isCodexUrl(baseUrl)
|| isBigModelCodingApiRoot(baseUrl)
|| isGoogleOpenAiCompatApiRoot(baseUrl)
|| isVertexOpenAiCompatApiRoot(baseUrl)
}
function appendVersionedApiRoot(baseUrl: string, suffix: '/v1' | '/v1beta'): string {
const raw = baseUrl.trim()
if (!raw) return ''
try {
const parsed = new URL(raw)
parsed.pathname = `${parsed.pathname.replace(/\/+$/, '')}${suffix}`
return parsed.toString().replace(/\/$/, '')
} catch {
const [base, query] = raw.split('?', 2)
const normalizedBase = base.replace(/\/+$/, '')
return query === undefined ? `${normalizedBase}${suffix}` : `${normalizedBase}${suffix}?${query}`
}
}
export function getDefaultEndpointBaseUrl(params: {
apiFormat: string
baseUrl?: string | null
}): string {
const normalizedApiFormat = normalizeEndpointApiFormat(params.apiFormat)
const rawBaseUrl = (params.baseUrl || '').trim()
if (!rawBaseUrl) return ''
if (
usesVersionedApiRootByDefault(normalizedApiFormat)
&& !baseUrlHasVersionedApiRoot(rawBaseUrl)
&& !skipsVersionedApiRootDefault(normalizedApiFormat, rawBaseUrl)
) {
return appendVersionedApiRoot(rawBaseUrl, versionedApiRootSuffix(normalizedApiFormat))
}
return rawBaseUrl
}
export function getDefaultEndpointPath(params: {
apiFormat: string
providerType?: string | null
baseUrl?: string
apiFormats: ApiFormatPathDefinition[]
}): string {
const providerType = (params.providerType || '').toLowerCase()
const normalizedApiFormat = normalizeEndpointApiFormat(params.apiFormat)
if (providerType === 'gemini_cli') {
if (normalizedApiFormat === 'gemini:generate_content') {
return '/v1internal:{action}'
}
}
if (providerType === 'vertex_ai') {
if (normalizedApiFormat === 'gemini:generate_content') {
return '/v1/projects/{project_id}/locations/{region}/publishers/google/models/{model}:{action}'
}
if (normalizedApiFormat === 'gemini:embedding') {
return '/v1/projects/{project_id}/locations/{region}/publishers/google/models/{model}:predict'
}
}
const format = params.apiFormats.find(f => f.value === normalizedApiFormat)
const defaultPath = format?.default_path || ''
const isCodex = providerType
? providerType === 'codex'
: (!!params.baseUrl && isCodexUrl(params.baseUrl))
if (normalizedApiFormat === 'openai:responses' && isCodex) {
return '/responses'
}
if (normalizedApiFormat === 'openai:search' && isCodex) {
return '/alpha/search'
}
if (usesVersionedApiRootByDefault(normalizedApiFormat)) {
return stripVersionPrefixForApiRoot(defaultPath)
}
if (openAiCompatibleBaseIncludesApiRoot(params.baseUrl) && isOpenAiCompatibleFormat(normalizedApiFormat)) {
return stripVersionPrefixForApiRoot(defaultPath)
}
return defaultPath
}