feat(advanced-custom): remove fallback option and enhance path matching for advanced custom routes

This commit is contained in:
CaIon
2026-06-18 20:29:24 +08:00
parent 3f2c0aeda7
commit 55b00fcf09
22 changed files with 378 additions and 329 deletions
@@ -33,7 +33,6 @@ import {
SelectValue,
} from '@/components/ui/select'
import { Separator } from '@/components/ui/separator'
import { Switch } from '@/components/ui/switch'
import { Textarea } from '@/components/ui/textarea'
import { Dialog } from '@/components/dialog'
import {
@@ -152,13 +151,6 @@ export function AdvancedCustomEditorDialog({
})
}
const setFallbackEnabled = (enabled: boolean) => {
setConfig((current) => ({
...normalizeAdvancedCustomConfig(current),
advanced_fallback: { enabled },
}))
}
const parseJsonEditorConfig = (): AdvancedCustomConfig | null => {
const parsed = parseAdvancedCustomConfig(jsonText)
if (!parsed) {
@@ -215,11 +207,6 @@ export function AdvancedCustomEditorDialog({
...(base.advanced_routes || []),
...(template.advanced_routes || []),
],
advanced_fallback: {
enabled:
base.advanced_fallback?.enabled === true ||
template.advanced_fallback?.enabled === true,
},
}
}
@@ -355,23 +342,7 @@ export function AdvancedCustomEditorDialog({
{editMode === 'visual' ? (
<div className='flex flex-col gap-5 p-4'>
<div className='flex flex-col gap-3 border-y py-4 sm:flex-row sm:items-center sm:justify-between'>
<div className='flex items-center gap-3'>
<Switch
checked={normalizedConfig.advanced_fallback?.enabled === true}
onCheckedChange={setFallbackEnabled}
/>
<div className='space-y-1'>
<div className='text-sm font-medium'>
{t('Fallback routing')}
</div>
<div className='text-muted-foreground max-w-2xl text-xs leading-relaxed'>
{t(
'When enabled, requests that do not match any advanced route are forwarded to the channel base URL. When disabled, unmatched requests return an error.'
)}
</div>
</div>
</div>
<div className='flex justify-end border-y py-4'>
<Button
type='button'
variant='outline'
@@ -1845,18 +1845,6 @@ export function ChannelMutateDrawer({
{t('Routes')}:{' '}
{advancedCustomStats.routeCount}
</Badge>
<Badge
variant={
advancedCustomStats.fallbackEnabled
? 'default'
: 'outline'
}
>
{t('Fallback')}:{' '}
{advancedCustomStats.fallbackEnabled
? t('Enabled')
: t('Disabled')}
</Badge>
{!advancedCustomStats.valid && (
<Badge variant='destructive'>
{t('Incomplete')}
@@ -2254,6 +2242,7 @@ export function ChannelMutateDrawer({
allowCreate
createLabel='Add custom model "{{value}}"'
maxVisibleChips={8}
copyChipOnClick
/>
</FormControl>
{modelMappingGuardrail.exposedTargetModels
+9 -9
View File
@@ -28,14 +28,14 @@ export const CHANNEL_TYPES = {
3: 'Azure',
4: 'Ollama',
5: 'MidjourneyPlus',
6: 'OpenAIMax',
// 6: 'OpenAIMax',
7: 'OhMyGPT',
8: 'Custom',
9: 'AILS',
10: 'AI Proxy',
11: 'PaLM',
12: 'API2GPT',
13: 'AIGC2D',
// 9: 'AILS',
// 10: 'AI Proxy',
// 11: 'PaLM',
// 12: 'API2GPT',
// 13: 'AIGC2D',
14: 'Anthropic',
15: 'Baidu',
16: 'Zhipu',
@@ -43,7 +43,7 @@ export const CHANNEL_TYPES = {
18: 'Xunfei',
19: '360',
20: 'OpenRouter',
21: 'AI Proxy Library',
// 21: 'AI Proxy Library',
22: 'FastGPT',
23: 'Tencent',
24: 'Gemini',
@@ -80,8 +80,8 @@ export const CHANNEL_TYPES = {
} as const
const CHANNEL_TYPE_DISPLAY_ORDER: number[] = [
1, 14, 33, 24, 43, 3, 41, 48, 42, 34, 20, 4, 40, 27, 25, 17, 26, 15, 46, 23,
18, 45, 31, 35, 49, 19, 47, 37, 38, 39, 11, 8, 58, 57, 22, 21, 44, 2, 5, 36,
1, 14, 33, 24, 43, 3, 41, 48, 58, 42, 34, 20, 4, 40, 27, 25, 17, 26, 15, 46, 23,
18, 45, 31, 35, 49, 19, 47, 37, 38, 39, 11, 8, 57, 22, 21, 44, 2, 5, 36,
50, 51, 52, 53, 54, 55, 56,
]
+13 -32
View File
@@ -176,12 +176,11 @@ export const ADVANCED_CUSTOM_TEMPLATE_OPTIONS: AdvancedCustomTemplateOption[] =
advanced_routes: [
{
incoming_path: '/v1/chat/completions',
upstream_path: 'https://api.openai.com/v1/chat/completions',
upstream_path: '/v1/chat/completions',
converter: 'none',
auth: bearerHeaderAuth(),
},
],
advanced_fallback: { enabled: false },
},
},
{
@@ -191,12 +190,11 @@ export const ADVANCED_CUSTOM_TEMPLATE_OPTIONS: AdvancedCustomTemplateOption[] =
advanced_routes: [
{
incoming_path: '/v1/responses',
upstream_path: 'https://api.openai.com/v1/responses',
upstream_path: '/v1/responses',
converter: 'none',
auth: bearerHeaderAuth(),
},
],
advanced_fallback: { enabled: false },
},
},
{
@@ -206,12 +204,11 @@ export const ADVANCED_CUSTOM_TEMPLATE_OPTIONS: AdvancedCustomTemplateOption[] =
advanced_routes: [
{
incoming_path: '/v1/embeddings',
upstream_path: 'https://api.openai.com/v1/embeddings',
upstream_path: '/v1/embeddings',
converter: 'none',
auth: bearerHeaderAuth(),
},
],
advanced_fallback: { enabled: false },
},
},
{
@@ -221,18 +218,17 @@ export const ADVANCED_CUSTOM_TEMPLATE_OPTIONS: AdvancedCustomTemplateOption[] =
advanced_routes: [
{
incoming_path: '/v1/images/generations',
upstream_path: 'https://api.openai.com/v1/images/generations',
upstream_path: '/v1/images/generations',
converter: 'none',
auth: bearerHeaderAuth(),
},
{
incoming_path: '/v1/images/edits',
upstream_path: 'https://api.openai.com/v1/images/edits',
upstream_path: '/v1/images/edits',
converter: 'none',
auth: bearerHeaderAuth(),
},
],
advanced_fallback: { enabled: false },
},
},
{
@@ -242,12 +238,11 @@ export const ADVANCED_CUSTOM_TEMPLATE_OPTIONS: AdvancedCustomTemplateOption[] =
advanced_routes: [
{
incoming_path: '/v1/messages',
upstream_path: 'https://api.anthropic.com/v1/messages',
upstream_path: '/v1/messages',
converter: 'none',
auth: apiKeyHeaderAuth(),
},
],
advanced_fallback: { enabled: false },
},
},
{
@@ -257,27 +252,23 @@ export const ADVANCED_CUSTOM_TEMPLATE_OPTIONS: AdvancedCustomTemplateOption[] =
advanced_routes: [
{
incoming_path: '/v1beta/models/{model}:generateContent',
upstream_path:
'https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent',
upstream_path: '/v1beta/models/{model}:generateContent',
converter: 'none',
auth: geminiQueryAuth(),
},
{
incoming_path: '/v1beta/models/{model}:embedContent',
upstream_path:
'https://generativelanguage.googleapis.com/v1beta/models/{model}:embedContent',
upstream_path: '/v1beta/models/{model}:embedContent',
converter: 'none',
auth: geminiQueryAuth(),
},
{
incoming_path: '/v1beta/models/{model}:batchEmbedContents',
upstream_path:
'https://generativelanguage.googleapis.com/v1beta/models/{model}:batchEmbedContents',
upstream_path: '/v1beta/models/{model}:batchEmbedContents',
converter: 'none',
auth: geminiQueryAuth(),
},
],
advanced_fallback: { enabled: false },
},
},
{
@@ -287,13 +278,11 @@ export const ADVANCED_CUSTOM_TEMPLATE_OPTIONS: AdvancedCustomTemplateOption[] =
advanced_routes: [
{
incoming_path: '/v1/chat/completions',
upstream_path:
'https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent',
upstream_path: '/v1beta/models/{model}:generateContent',
converter: 'openai_chat_completions_to_gemini_generate_content',
auth: geminiQueryAuth(),
},
],
advanced_fallback: { enabled: false },
},
},
]
@@ -325,7 +314,6 @@ export function createAdvancedCustomRoute(): AdvancedCustomRoute {
export function createAdvancedCustomConfig(): AdvancedCustomConfig {
return {
advanced_routes: [createAdvancedCustomRoute()],
advanced_fallback: { enabled: false },
}
}
@@ -405,9 +393,6 @@ export function normalizeAdvancedCustomConfig(
return {
advanced_routes: routes,
advanced_fallback: {
enabled: config.advanced_fallback?.enabled === true,
},
}
}
@@ -420,11 +405,9 @@ export function validateAdvancedCustomConfig(
const normalized = normalizeAdvancedCustomConfig(config)
const routes = normalized.advanced_routes || []
const fallbackEnabled = normalized.advanced_fallback?.enabled === true
if (routes.length === 0 && !fallbackEnabled) {
if (routes.length === 0) {
return {
message:
'Advanced custom configuration requires at least one route or fallback',
message: 'Advanced custom configuration requires at least one route',
}
}
@@ -492,17 +475,15 @@ export function advancedCustomConfigUsesRelativeUpstreamPath(
export function getAdvancedCustomStats(value: string | undefined): {
routeCount: number
fallbackEnabled: boolean
valid: boolean
} {
const config = parseAdvancedCustomConfig(value)
if (!config) {
return { routeCount: 0, fallbackEnabled: false, valid: false }
return { routeCount: 0, valid: false }
}
const normalized = normalizeAdvancedCustomConfig(config)
return {
routeCount: normalized.advanced_routes?.length || 0,
fallbackEnabled: normalized.advanced_fallback?.enabled === true,
valid: validateAdvancedCustomConfig(normalized) === null,
}
}
-10
View File
@@ -226,16 +226,6 @@ export const channelFormSchema = z
if (advancedCustomError) {
addRequiredIssue(ctx, 'advanced_custom', advancedCustomError.message)
}
if (
advancedCustomConfig?.advanced_fallback?.enabled === true &&
!data.base_url?.trim()
) {
addRequiredIssue(
ctx,
'base_url',
'Base URL is required when fallback is enabled'
)
}
if (
advancedCustomConfigUsesRelativeUpstreamPath(advancedCustomConfig) &&
!data.base_url?.trim()
-5
View File
@@ -110,7 +110,6 @@ export interface ChannelOtherSettings {
export interface AdvancedCustomConfig {
advanced_routes?: AdvancedCustomRoute[]
advanced_fallback?: AdvancedCustomFallback
}
export interface AdvancedCustomRoute {
@@ -120,10 +119,6 @@ export interface AdvancedCustomRoute {
auth?: AdvancedCustomRouteAuth
}
export interface AdvancedCustomFallback {
enabled?: boolean
}
export interface AdvancedCustomRouteAuth {
type?: AdvancedCustomAuthType
name?: string