-
{label}
+
+
+ {label}
+
{children}
)
diff --git a/web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx b/web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx
index 0824b4eb..4ee913d1 100644
--- a/web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx
+++ b/web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx
@@ -256,6 +256,7 @@ const ADVANCED_SETTINGS_SECTION_IDS = {
const ADVANCED_SETTINGS_CHILD_SECTION_IDS: string[] = Object.values(
ADVANCED_SETTINGS_SECTION_IDS
)
+const ADVANCED_CUSTOM_ROUTE_TYPE_PREVIEW_LIMIT = 3
const UPSTREAM_DETECTED_MODEL_PREVIEW_LIMIT = 8
const SENSITIVE_FORM_FIELDS = [
'type',
@@ -776,6 +777,18 @@ export function ChannelMutateDrawer({
() => getAdvancedCustomStats(currentAdvancedCustom),
[currentAdvancedCustom]
)
+ const advancedCustomRouteTypeLabels =
+ advancedCustomStats.routeTypeLabels.slice(
+ 0,
+ ADVANCED_CUSTOM_ROUTE_TYPE_PREVIEW_LIMIT
+ )
+ const hiddenAdvancedCustomRouteTypeCount =
+ advancedCustomStats.routeTypeLabels.length -
+ advancedCustomRouteTypeLabels.length
+ const advancedCustomRouteTypeTitle =
+ hiddenAdvancedCustomRouteTypeCount > 0
+ ? advancedCustomStats.routeTypeLabels.join(', ')
+ : undefined
// Get all models list
const allModelsList = useMemo(
@@ -2647,6 +2660,30 @@ export function ChannelMutateDrawer({
{t('Routes')}:{' '}
{advancedCustomStats.routeCount}
+ {advancedCustomRouteTypeLabels.map(
+ (label) => (
+
+
+ {label}
+
+
+ )
+ )}
+ {hiddenAdvancedCustomRouteTypeCount > 0 && (
+
+ +{hiddenAdvancedCustomRouteTypeCount}
+
+ )}
{!advancedCustomStats.valid && (
{t('Incomplete')}
diff --git a/web/default/src/features/channels/lib/advanced-custom.ts b/web/default/src/features/channels/lib/advanced-custom.ts
index 823e55f7..150ce11b 100644
--- a/web/default/src/features/channels/lib/advanced-custom.ts
+++ b/web/default/src/features/channels/lib/advanced-custom.ts
@@ -78,11 +78,7 @@ export const ADVANCED_CUSTOM_INCOMING_PATH_OPTIONS: AdvancedCustomIncomingPathOp
[
{
value: '/v1/chat/completions',
- label: 'OpenAI Chat Completions',
- },
- {
- value: '/v1/completions',
- label: 'OpenAI Completions',
+ label: 'OpenAI Chat',
},
{
value: '/v1/responses',
@@ -104,6 +100,10 @@ export const ADVANCED_CUSTOM_INCOMING_PATH_OPTIONS: AdvancedCustomIncomingPathOp
value: '/v1/images/edits',
label: 'OpenAI Image Edits',
},
+ {
+ value: '/v1/completions',
+ label: 'OpenAI Completions',
+ },
{
value: '/v1/audio/speech',
label: 'OpenAI Audio Speech',
@@ -142,6 +142,10 @@ export const ADVANCED_CUSTOM_INCOMING_PATH_OPTIONS: AdvancedCustomIncomingPathOp
},
]
+const ADVANCED_CUSTOM_ROUTE_SUMMARY_LABELS: Record = {
+ '/v1/chat/completions': 'OpenAI Chat',
+}
+
export type AdvancedCustomValidationError = {
message: string
routeIndex?: number
@@ -357,8 +361,17 @@ export function isAdvancedCustomIncomingPathAllowed(
incomingPath: string,
converter: AdvancedCustomConverter
): boolean {
- return getAdvancedCustomIncomingPathOptions(converter).some(
- (option) => option.value === incomingPath
+ return isConverterPathAllowed(incomingPath, converter)
+}
+
+export function getAdvancedCustomConverterOptions(
+ incomingPath: string
+): typeof ADVANCED_CUSTOM_CONVERTER_OPTIONS {
+ const normalizedIncomingPath = incomingPath.trim()
+ return ADVANCED_CUSTOM_CONVERTER_OPTIONS.filter(
+ (option) =>
+ option.value === 'none' ||
+ isConverterPathAllowed(normalizedIncomingPath, option.value)
)
}
@@ -483,15 +496,28 @@ export function advancedCustomConfigUsesRelativeUpstreamPath(
export function getAdvancedCustomStats(value: string | undefined): {
routeCount: number
valid: boolean
+ routeTypeLabels: string[]
} {
const config = parseAdvancedCustomConfig(value)
if (!config) {
- return { routeCount: 0, valid: false }
+ return { routeCount: 0, valid: false, routeTypeLabels: [] }
}
const normalized = normalizeAdvancedCustomConfig(config)
+ const routes = normalized.advanced_routes || []
+ const routeTypeLabels: string[] = []
+ const seenRouteTypeLabels = new Set()
+
+ for (const route of routes) {
+ const label = getAdvancedCustomRouteSummaryLabel(route)
+ if (!label || seenRouteTypeLabels.has(label)) continue
+ routeTypeLabels.push(label)
+ seenRouteTypeLabels.add(label)
+ }
+
return {
- routeCount: normalized.advanced_routes?.length || 0,
+ routeCount: routes.length,
valid: validateAdvancedCustomConfig(normalized) === null,
+ routeTypeLabels,
}
}
@@ -543,6 +569,17 @@ function getAdvancedCustomRouteUpstreamPath(route: AdvancedCustomRoute): string
return (route.upstream_path || '').trim()
}
+function getAdvancedCustomRouteSummaryLabel(
+ route: AdvancedCustomRoute
+): string | null {
+ const incomingPath = route.incoming_path?.trim() || ''
+ if (!incomingPath) return null
+ return (
+ ADVANCED_CUSTOM_ROUTE_SUMMARY_LABELS[incomingPath] ||
+ getAdvancedCustomIncomingPathLabel(incomingPath)
+ )
+}
+
function isFullHttpURLOrAbsolutePath(value: string): boolean {
if (value.startsWith('/')) return !value.startsWith('//')