Normalize endpoint API root handling

This commit is contained in:
fawney19
2026-05-29 02:29:33 +08:00
parent ef2953038e
commit b59c724455
46 changed files with 1179 additions and 205 deletions
@@ -1,29 +1,41 @@
import { describe, expect, it } from 'vitest'
import { getDefaultEndpointPath } from '../endpoint-default-paths'
import { getDefaultEndpointBaseUrl, getDefaultEndpointPath } from '../endpoint-default-paths'
const apiFormats = [
{ value: 'openai:chat', default_path: '/v1/chat/completions' },
{ value: 'gemini:generate_content', default_path: '/v1beta/models/{model}:{action}' },
{ value: 'gemini:embedding', default_path: '/v1beta/models/{model}:{action}' },
{ value: 'gemini:embedding', default_path: '/v1beta/models/{model}:embedContent' },
{ value: 'gemini:video', default_path: '/v1beta/models/{model}:predictLongRunning' },
{ value: 'openai:responses', default_path: '/v1/responses' },
{ value: 'openai:embedding', default_path: '/v1/embeddings' },
{ value: 'openai:rerank', default_path: '/v1/rerank' },
{ value: 'openai:image', default_path: '/v1/images/generations' },
{ value: 'openai:video', default_path: '/v1/videos' },
{ value: 'jina:embedding', default_path: '/v1/embeddings' },
{ value: 'jina:rerank', default_path: '/v1/rerank' },
{ value: 'claude:messages', default_path: '/v1/messages' },
]
describe('endpoint default paths', () => {
it('uses Gemini Developer API paths for custom Gemini endpoints', () => {
it('uses Gemini Developer API resource paths for custom Gemini endpoints', () => {
expect(getDefaultEndpointPath({
apiFormat: 'gemini:generate_content',
providerType: 'custom',
apiFormats,
})).toBe('/v1beta/models/{model}:{action}')
})).toBe('/models/{model}:{action}')
expect(getDefaultEndpointPath({
apiFormat: 'gemini:embedding',
providerType: 'custom',
apiFormats,
})).toBe('/v1beta/models/{model}:{action}')
})).toBe('/models/{model}:embedContent')
expect(getDefaultEndpointPath({
apiFormat: 'gemini:video',
providerType: 'custom',
apiFormats,
})).toBe('/models/{model}:predictLongRunning')
})
it('uses Vertex AI project/location paths for Vertex provider Gemini endpoints', () => {
@@ -56,7 +68,7 @@ describe('endpoint default paths', () => {
})).toBe('/responses')
})
it('drops /v1 from OpenAI-compatible defaults when base URL includes a path', () => {
it('drops /v1 from API-root defaults because base URL is the API root', () => {
expect(getDefaultEndpointPath({
apiFormat: 'openai:chat',
providerType: 'custom',
@@ -71,6 +83,41 @@ describe('endpoint default paths', () => {
apiFormats,
})).toBe('/embeddings')
expect(getDefaultEndpointPath({
apiFormat: 'openai:rerank',
providerType: 'custom',
baseUrl: 'https://proxy.example.com/api?tenant=demo',
apiFormats,
})).toBe('/rerank')
expect(getDefaultEndpointPath({
apiFormat: 'openai:image',
providerType: 'custom',
baseUrl: 'https://proxy.example.com/api',
apiFormats,
})).toBe('/images/generations')
expect(getDefaultEndpointPath({
apiFormat: 'openai:video',
providerType: 'custom',
baseUrl: 'https://proxy.example.com/api',
apiFormats,
})).toBe('/videos')
expect(getDefaultEndpointPath({
apiFormat: 'jina:embedding',
providerType: 'custom',
baseUrl: 'https://api.jina.ai/v1',
apiFormats,
})).toBe('/embeddings')
expect(getDefaultEndpointPath({
apiFormat: 'jina:rerank',
providerType: 'custom',
baseUrl: 'https://api.jina.ai/v1',
apiFormats,
})).toBe('/rerank')
expect(getDefaultEndpointPath({
apiFormat: 'openai:chat',
providerType: 'custom',
@@ -83,7 +130,7 @@ describe('endpoint default paths', () => {
providerType: 'custom',
baseUrl: 'https://proxy.example.com',
apiFormats,
})).toBe('/v1/chat/completions')
})).toBe('/chat/completions')
})
it('drops /v1 from OpenAI-compatible defaults when base URL already includes a known API root', () => {
@@ -102,7 +149,7 @@ describe('endpoint default paths', () => {
})).toBe('/responses')
})
it('keeps /v1 for Claude Messages defaults unless base URL already ends with v1', () => {
it('drops /v1 from Claude Messages defaults because base URL is the API root', () => {
expect(getDefaultEndpointPath({
apiFormat: 'claude:messages',
providerType: 'custom',
@@ -115,13 +162,80 @@ describe('endpoint default paths', () => {
providerType: 'custom',
baseUrl: 'https://proxy.example.com/api',
apiFormats,
})).toBe('/v1/messages')
})).toBe('/messages')
expect(getDefaultEndpointPath({
apiFormat: 'claude:messages',
providerType: 'custom',
baseUrl: 'https://proxy.example.com/anthropic',
apiFormats,
})).toBe('/v1/messages')
})).toBe('/messages')
})
it('defaults API-root base URLs to the format version when using a provider website', () => {
expect(getDefaultEndpointBaseUrl({
apiFormat: 'openai:chat',
baseUrl: 'https://api.openai.com',
})).toBe('https://api.openai.com/v1')
expect(getDefaultEndpointBaseUrl({
apiFormat: 'openai:responses',
baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode',
})).toBe('https://dashscope.aliyuncs.com/compatible-mode/v1')
expect(getDefaultEndpointBaseUrl({
apiFormat: 'claude:messages',
baseUrl: 'https://api.anthropic.com',
})).toBe('https://api.anthropic.com/v1')
expect(getDefaultEndpointBaseUrl({
apiFormat: 'openai:embedding',
baseUrl: 'https://api.openai.com',
})).toBe('https://api.openai.com/v1')
expect(getDefaultEndpointBaseUrl({
apiFormat: 'openai:image',
baseUrl: 'https://api.openai.com',
})).toBe('https://api.openai.com/v1')
expect(getDefaultEndpointBaseUrl({
apiFormat: 'openai:video',
baseUrl: 'https://api.openai.com',
})).toBe('https://api.openai.com/v1')
expect(getDefaultEndpointBaseUrl({
apiFormat: 'jina:embedding',
baseUrl: 'https://api.jina.ai',
})).toBe('https://api.jina.ai/v1')
expect(getDefaultEndpointBaseUrl({
apiFormat: 'gemini:generate_content',
baseUrl: 'https://generativelanguage.googleapis.com',
})).toBe('https://generativelanguage.googleapis.com/v1beta')
expect(getDefaultEndpointBaseUrl({
apiFormat: 'gemini:embedding',
baseUrl: 'https://generativelanguage.googleapis.com',
})).toBe('https://generativelanguage.googleapis.com/v1beta')
expect(getDefaultEndpointBaseUrl({
apiFormat: 'gemini:video',
baseUrl: 'https://generativelanguage.googleapis.com',
})).toBe('https://generativelanguage.googleapis.com/v1beta')
expect(getDefaultEndpointBaseUrl({
apiFormat: 'openai:chat',
baseUrl: 'https://open.bigmodel.cn/api/coding/paas/v4',
})).toBe('https://open.bigmodel.cn/api/coding/paas/v4')
expect(getDefaultEndpointBaseUrl({
apiFormat: 'openai:chat',
baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
})).toBe('https://generativelanguage.googleapis.com/v1beta/openai')
expect(getDefaultEndpointBaseUrl({
apiFormat: 'openai:chat',
baseUrl: 'https://api.deepseek.com',
})).toBe('https://api.deepseek.com')
})
})