feat: add field passthrough controls for gateway channels (#6847)

This commit is contained in:
Seefs
2026-08-15 13:54:25 +08:00
committed by GitHub
parent 7d09c6954e
commit e90a7c48e5
3 changed files with 129 additions and 88 deletions
@@ -138,13 +138,16 @@ import {
} from '../../api' } from '../../api'
import { import {
ADD_MODE_OPTIONS, ADD_MODE_OPTIONS,
CLAUDE_FIELD_PASSTHROUGH_TYPES,
CHANNEL_STATUS_LABELS, CHANNEL_STATUS_LABELS,
CHANNEL_TYPE_OPTIONS, CHANNEL_TYPE_OPTIONS,
CHANNEL_TYPE_WARNINGS, CHANNEL_TYPE_WARNINGS,
ERROR_MESSAGES, ERROR_MESSAGES,
FIELD_PASSTHROUGH_TYPES,
FIELD_DESCRIPTIONS, FIELD_DESCRIPTIONS,
FIELD_PLACEHOLDERS, FIELD_PLACEHOLDERS,
MODEL_FETCHABLE_TYPES, MODEL_FETCHABLE_TYPES,
OPENAI_FIELD_PASSTHROUGH_TYPES,
} from '../../constants' } from '../../constants'
import { useChannelMutateForm } from '../../hooks/use-channel-mutate-form' import { useChannelMutateForm } from '../../hooks/use-channel-mutate-form'
import { import {
@@ -1026,7 +1029,7 @@ export function ChannelMutateDrawer({
(currentHttp2ConnectionShards != null && currentHttp2ConnectionShards > 1) (currentHttp2ConnectionShards != null && currentHttp2ConnectionShards > 1)
) )
let fieldPassthroughConfigured = false let fieldPassthroughConfigured = false
if (currentType === 1 || currentType === 57) { if (OPENAI_FIELD_PASSTHROUGH_TYPES.has(currentType)) {
fieldPassthroughConfigured = Boolean( fieldPassthroughConfigured = Boolean(
currentAllowServiceTier || currentAllowServiceTier ||
currentDisableStore || currentDisableStore ||
@@ -1034,13 +1037,16 @@ export function ChannelMutateDrawer({
currentAllowIncludeObfuscation || currentAllowIncludeObfuscation ||
currentAllowInferenceGeo currentAllowInferenceGeo
) )
} else if (currentType === 14) { }
fieldPassthroughConfigured = Boolean( if (CLAUDE_FIELD_PASSTHROUGH_TYPES.has(currentType)) {
currentAllowServiceTier || fieldPassthroughConfigured =
currentAllowInferenceGeo || fieldPassthroughConfigured ||
currentAllowSpeed || Boolean(
currentClaudeBetaQuery currentAllowServiceTier ||
) currentAllowInferenceGeo ||
currentAllowSpeed ||
(currentType === 14 && currentClaudeBetaQuery)
)
} }
const upstreamModelDetectionConfigured = Boolean( const upstreamModelDetectionConfigured = Boolean(
upstreamModelUpdateCheckEnabled || upstreamModelUpdateCheckEnabled ||
@@ -1077,7 +1083,7 @@ export function ChannelMutateDrawer({
configured: extraSettingsConfigured, configured: extraSettingsConfigured,
}, },
] ]
if (currentType === 1 || currentType === 14 || currentType === 57) { if (FIELD_PASSTHROUGH_TYPES.has(currentType)) {
advancedNavChildren.push({ advancedNavChildren.push({
id: ADVANCED_SETTINGS_SECTION_IDS.fieldPassthrough, id: ADVANCED_SETTINGS_SECTION_IDS.fieldPassthrough,
title: t('Field passthrough controls'), title: t('Field passthrough controls'),
@@ -4233,9 +4239,7 @@ export function ChannelMutateDrawer({
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent <SelectContent alignItemWithTrigger={false}>
alignItemWithTrigger={false}
>
<SelectGroup> <SelectGroup>
<SelectItem value='auto'> <SelectItem value='auto'>
{t('Auto')} {t('Auto')}
@@ -4369,9 +4373,7 @@ export function ChannelMutateDrawer({
</fieldset> </fieldset>
</div> </div>
{(currentType === 1 || {FIELD_PASSTHROUGH_TYPES.has(currentType) && (
currentType === 14 ||
currentType === 57) && (
<div <div
id={ADVANCED_SETTINGS_SECTION_IDS.fieldPassthrough} id={ADVANCED_SETTINGS_SECTION_IDS.fieldPassthrough}
className={sideDrawerSectionClassName( className={sideDrawerSectionClassName(
@@ -4416,7 +4418,9 @@ export function ChannelMutateDrawer({
)} )}
/> />
{(currentType === 1 || currentType === 57) && ( {OPENAI_FIELD_PASSTHROUGH_TYPES.has(
currentType
) && (
<> <>
<FormField <FormField
control={form.control} control={form.control}
@@ -4526,34 +4530,38 @@ export function ChannelMutateDrawer({
</> </>
)} )}
{currentType === 14 && ( {CLAUDE_FIELD_PASSTHROUGH_TYPES.has(
currentType
) && (
<> <>
<FormField {currentType === 14 && (
control={form.control} <FormField
name='allow_inference_geo' control={form.control}
render={({ field }) => ( name='allow_inference_geo'
<FormItem className='flex items-center justify-between gap-3 px-4 py-3'> render={({ field }) => (
<div className='space-y-0.5'> <FormItem className='flex items-center justify-between gap-3 px-4 py-3'>
<FormLabel className='text-sm'> <div className='space-y-0.5'>
{t( <FormLabel className='text-sm'>
'Allow inference_geo passthrough' {t(
)} 'Allow inference_geo passthrough'
</FormLabel> )}
<FormDescription> </FormLabel>
{t( <FormDescription>
'Pass through the inference_geo field for Claude data residency region control' {t(
)} 'Pass through the inference_geo field for Claude data residency region control'
</FormDescription> )}
</div> </FormDescription>
<FormControl> </div>
<Switch <FormControl>
checked={field.value} <Switch
onCheckedChange={field.onChange} checked={field.value}
/> onCheckedChange={field.onChange}
</FormControl> />
</FormItem> </FormControl>
)} </FormItem>
/> )}
/>
)}
<FormField <FormField
control={form.control} control={form.control}
@@ -4580,32 +4588,34 @@ export function ChannelMutateDrawer({
)} )}
/> />
<FormField {currentType === 14 && (
control={form.control} <FormField
name='claude_beta_query' control={form.control}
render={({ field }) => ( name='claude_beta_query'
<FormItem className='flex items-center justify-between gap-3 px-4 py-3'> render={({ field }) => (
<div className='space-y-0.5'> <FormItem className='flex items-center justify-between gap-3 px-4 py-3'>
<FormLabel className='text-sm'> <div className='space-y-0.5'>
{t( <FormLabel className='text-sm'>
'Allow Claude beta query passthrough' {t(
)} 'Allow Claude beta query passthrough'
</FormLabel> )}
<FormDescription> </FormLabel>
{t( <FormDescription>
'Pass through the anthropic-beta header for beta features' {t(
)} 'Pass through the anthropic-beta header for beta features'
</FormDescription> )}
</div> </FormDescription>
<FormControl> </div>
<Switch <FormControl>
checked={field.value} <Switch
onCheckedChange={field.onChange} checked={field.value}
/> onCheckedChange={field.onChange}
</FormControl> />
</FormItem> </FormControl>
)} </FormItem>
/> )}
/>
)}
</> </>
)} )}
</div> </div>
+24
View File
@@ -392,6 +392,30 @@ export const MODEL_FETCHABLE_TYPES = new Set([
59, 60, 59, 60,
]) ])
export const FIELD_PASSTHROUGH_TYPES = new Set([
1,
14,
57,
58,
59,
CHANNEL_TYPE_NEW_API,
])
export const OPENAI_FIELD_PASSTHROUGH_TYPES = new Set([
1,
57,
58,
59,
CHANNEL_TYPE_NEW_API,
])
export const CLAUDE_FIELD_PASSTHROUGH_TYPES = new Set([
14,
58,
59,
CHANNEL_TYPE_NEW_API,
])
export const TYPE_TO_KEY_PROMPT: Record<number, string> = { export const TYPE_TO_KEY_PROMPT: Record<number, string> = {
15: 'Format: APIKey|SecretKey', 15: 'Format: APIKey|SecretKey',
18: 'Format: APPID|APISecret|APIKey', 18: 'Format: APPID|APISecret|APIKey',
+26 -19
View File
@@ -19,10 +19,13 @@ For commercial licensing, please contact support@quantumnous.com
import { z } from 'zod' import { z } from 'zod'
import { import {
CLAUDE_FIELD_PASSTHROUGH_TYPES,
CHANNEL_TYPE_NEW_API, CHANNEL_TYPE_NEW_API,
CHANNEL_STATUS, CHANNEL_STATUS,
ERROR_MESSAGES, ERROR_MESSAGES,
FIELD_PASSTHROUGH_TYPES,
MODEL_FETCHABLE_TYPES, MODEL_FETCHABLE_TYPES,
OPENAI_FIELD_PASSTHROUGH_TYPES,
} from '../constants' } from '../constants'
import type { Channel } from '../types' import type { Channel } from '../types'
import { import {
@@ -487,8 +490,7 @@ export function transformChannelToFormDefaults(
thinking_to_content: parsed.thinking_to_content || false, thinking_to_content: parsed.thinking_to_content || false,
proxy: parsed.proxy || '', proxy: parsed.proxy || '',
http_protocol: protocol, http_protocol: protocol,
http2_connection_shards: http2_connection_shards: protocol === HTTP_PROTOCOL_HTTP1 ? 1 : shards,
protocol === HTTP_PROTOCOL_HTTP1 ? 1 : shards,
pass_through_body_enabled: parsed.pass_through_body_enabled || false, pass_through_body_enabled: parsed.pass_through_body_enabled || false,
system_prompt: parsed.system_prompt || '', system_prompt: parsed.system_prompt || '',
system_prompt_override: parsed.system_prompt_override || false, system_prompt_override: parsed.system_prompt_override || false,
@@ -672,21 +674,21 @@ function buildSettingsJSON(formData: ChannelFormValues): string {
} }
// Field passthrough controls: // Field passthrough controls:
// - OpenAI (type 1) and Anthropic (type 14): allow_service_tier // - OpenAI, Anthropic, Codex, and New API: allow_service_tier
// - OpenAI only: disable_store, allow_safety_identifier // - OpenAI request fields: OpenAI, Codex, and New API
if (formData.type === 1 || formData.type === 14 || formData.type === 57) { // - Claude request fields: Anthropic and New API
if (FIELD_PASSTHROUGH_TYPES.has(formData.type)) {
settingsObj.allow_service_tier = formData.allow_service_tier === true settingsObj.allow_service_tier = formData.allow_service_tier === true
} else if ('allow_service_tier' in settingsObj) { } else if ('allow_service_tier' in settingsObj) {
delete settingsObj.allow_service_tier delete settingsObj.allow_service_tier
} }
if (formData.type === 1 || formData.type === 57) { if (OPENAI_FIELD_PASSTHROUGH_TYPES.has(formData.type)) {
settingsObj.disable_store = formData.disable_store === true settingsObj.disable_store = formData.disable_store === true
settingsObj.allow_safety_identifier = settingsObj.allow_safety_identifier =
formData.allow_safety_identifier === true formData.allow_safety_identifier === true
settingsObj.allow_include_obfuscation = settingsObj.allow_include_obfuscation =
formData.allow_include_obfuscation === true formData.allow_include_obfuscation === true
settingsObj.allow_inference_geo = formData.allow_inference_geo === true
} else { } else {
if ('disable_store' in settingsObj) { if ('disable_store' in settingsObj) {
delete settingsObj.disable_store delete settingsObj.disable_store
@@ -697,23 +699,28 @@ function buildSettingsJSON(formData: ChannelFormValues): string {
if ('allow_include_obfuscation' in settingsObj) { if ('allow_include_obfuscation' in settingsObj) {
delete settingsObj.allow_include_obfuscation delete settingsObj.allow_include_obfuscation
} }
if (formData.type !== 14 && 'allow_inference_geo' in settingsObj) {
delete settingsObj.allow_inference_geo
}
} }
// Anthropic (type 14): claude_beta_query, allow_inference_geo, allow_speed if (
if (formData.type === 14) { OPENAI_FIELD_PASSTHROUGH_TYPES.has(formData.type) ||
CLAUDE_FIELD_PASSTHROUGH_TYPES.has(formData.type)
) {
settingsObj.allow_inference_geo = formData.allow_inference_geo === true settingsObj.allow_inference_geo = formData.allow_inference_geo === true
} else if ('allow_inference_geo' in settingsObj) {
delete settingsObj.allow_inference_geo
}
if (CLAUDE_FIELD_PASSTHROUGH_TYPES.has(formData.type)) {
settingsObj.allow_speed = formData.allow_speed === true settingsObj.allow_speed = formData.allow_speed === true
} else if ('allow_speed' in settingsObj) {
delete settingsObj.allow_speed
}
// Only the Anthropic adaptor supports forcing the Claude beta query.
if (formData.type === 14) {
settingsObj.claude_beta_query = formData.claude_beta_query === true settingsObj.claude_beta_query = formData.claude_beta_query === true
} else { } else if ('claude_beta_query' in settingsObj) {
if ('allow_speed' in settingsObj) { delete settingsObj.claude_beta_query
delete settingsObj.allow_speed
}
if ('claude_beta_query' in settingsObj) {
delete settingsObj.claude_beta_query
}
} }
settingsObj.disable_task_polling_sleep = settingsObj.disable_task_polling_sleep =