feat(config): add session affinity routing settings

This commit is contained in:
Supra4E8C
2026-04-20 23:44:22 +08:00
parent a28920de94
commit 3212f6591b
7 changed files with 62 additions and 1 deletions
@@ -888,6 +888,13 @@ export function VisualConfigEditor({
}
/>
</FieldShell>
<Input
label={t('config_management.visual.sections.network.session_affinity_ttl')}
placeholder="1h"
value={values.routingSessionAffinityTTL}
onChange={(e) => onChange({ routingSessionAffinityTTL: e.target.value })}
disabled={disabled}
/>
</SectionGrid>
<SectionGrid>
@@ -900,6 +907,12 @@ export function VisualConfigEditor({
disabled={disabled}
onChange={(forceModelPrefix) => onChange({ forceModelPrefix })}
/>
<ToggleRow
title={t('config_management.visual.sections.network.session_affinity')}
checked={values.routingSessionAffinity}
disabled={disabled}
onChange={(routingSessionAffinity) => onChange({ routingSessionAffinity })}
/>
<ToggleRow
title={t('config_management.visual.sections.network.ws_auth')}
description={t('config_management.visual.sections.network.ws_auth_desc')}
+37 -1
View File
@@ -656,6 +656,18 @@ function getNextDirtyFields(
if (Object.prototype.hasOwnProperty.call(patch, 'routingStrategy')) {
updateDirty('routingStrategy', nextValues.routingStrategy === baselineValues.routingStrategy);
}
if (Object.prototype.hasOwnProperty.call(patch, 'routingSessionAffinity')) {
updateDirty(
'routingSessionAffinity',
nextValues.routingSessionAffinity === baselineValues.routingSessionAffinity
);
}
if (Object.prototype.hasOwnProperty.call(patch, 'routingSessionAffinityTTL')) {
updateDirty(
'routingSessionAffinityTTL',
nextValues.routingSessionAffinityTTL === baselineValues.routingSessionAffinityTTL
);
}
if (Object.prototype.hasOwnProperty.call(patch, 'payloadDefaultRules')) {
updateDirty(
'payloadDefaultRules',
@@ -836,6 +848,19 @@ export function useVisualConfig() {
quotaAntigravityCredits: Boolean(quotaExceeded?.['antigravity-credits'] ?? true),
routingStrategy: routing?.strategy === 'fill-first' ? 'fill-first' : 'round-robin',
routingSessionAffinity: Boolean(
routing?.['session-affinity'] ??
routing?.sessionAffinity ??
routing?.['sessionAffinity']
),
routingSessionAffinityTTL:
typeof routing?.['session-affinity-ttl'] === 'string'
? routing['session-affinity-ttl']
: typeof routing?.sessionAffinityTTL === 'string'
? routing.sessionAffinityTTL
: typeof routing?.['sessionAffinityTTL'] === 'string'
? routing['sessionAffinityTTL']
: '',
payloadDefaultRules: parsePayloadRules(payload?.default),
payloadDefaultRawRules: parseRawPayloadRules(payload?.['default-raw']),
@@ -949,9 +974,20 @@ export function useVisualConfig() {
deleteIfMapEmpty(doc, ['quota-exceeded']);
}
if (docHas(doc, ['routing']) || values.routingStrategy !== 'round-robin') {
if (
docHas(doc, ['routing']) ||
values.routingStrategy !== 'round-robin' ||
values.routingSessionAffinity ||
values.routingSessionAffinityTTL.trim()
) {
ensureMapInDoc(doc, ['routing']);
doc.setIn(['routing', 'strategy'], values.routingStrategy);
setBooleanInDoc(doc, ['routing', 'session-affinity'], values.routingSessionAffinity);
setStringInDoc(
doc,
['routing', 'session-affinity-ttl'],
values.routingSessionAffinityTTL
);
deleteIfMapEmpty(doc, ['routing']);
}
+2
View File
@@ -1231,8 +1231,10 @@
"routing_strategy_hint": "Select credential selection strategy",
"strategy_round_robin": "Round Robin",
"strategy_fill_first": "Fill First",
"session_affinity_ttl": "Session Affinity TTL",
"force_model_prefix": "Force Model Prefix",
"force_model_prefix_desc": "Unprefixed model requests only use credentials without prefix",
"session_affinity": "Session Affinity Routing",
"ws_auth": "WebSocket Authentication",
"ws_auth_desc": "Enable WebSocket authentication (/v1/ws)"
},
+2
View File
@@ -1230,8 +1230,10 @@
"routing_strategy_hint": "Выберите стратегию подбора учётных данных",
"strategy_round_robin": "По кругу",
"strategy_fill_first": "Сначала заполнить",
"session_affinity_ttl": "TTL привязки сессии",
"force_model_prefix": "Принудительный префикс модели",
"force_model_prefix_desc": "Запросы к моделям без префикса используют только учётные данные без префикса",
"session_affinity": "Маршрутизация с привязкой к сессии",
"ws_auth": "Аутентификация WebSocket",
"ws_auth_desc": "Включить аутентификацию WebSocket (/v1/ws)"
},
+2
View File
@@ -1231,8 +1231,10 @@
"routing_strategy_hint": "选择凭据选择策略",
"strategy_round_robin": "轮询 (Round Robin)",
"strategy_fill_first": "填充优先 (Fill First)",
"session_affinity_ttl": "会话粘性 TTL",
"force_model_prefix": "强制模型前缀",
"force_model_prefix_desc": "未带前缀的模型请求只使用无前缀凭据",
"session_affinity": "会话粘性路由",
"ws_auth": "WebSocket 认证",
"ws_auth_desc": "启用 WebSocket 连接认证 (/v1/ws)"
},
+2
View File
@@ -1259,8 +1259,10 @@
"routing_strategy_hint": "選擇憑證選擇策略",
"strategy_round_robin": "輪詢(Round Robin",
"strategy_fill_first": "填充優先(Fill First",
"session_affinity_ttl": "會話黏性 TTL",
"force_model_prefix": "強制模型前綴",
"force_model_prefix_desc": "未帶前綴的模型請求只使用無前綴憑證",
"session_affinity": "會話黏性路由",
"ws_auth": "WebSocket 驗證",
"ws_auth_desc": "啟用 WebSocket 連線驗證(/v1/ws"
},
+4
View File
@@ -77,6 +77,8 @@ export type VisualConfigValues = {
quotaSwitchPreviewModel: boolean;
quotaAntigravityCredits: boolean;
routingStrategy: 'round-robin' | 'fill-first';
routingSessionAffinity: boolean;
routingSessionAffinityTTL: string;
wsAuth: boolean;
payloadDefaultRules: PayloadRule[];
payloadDefaultRawRules: PayloadRule[];
@@ -117,6 +119,8 @@ export const DEFAULT_VISUAL_VALUES: VisualConfigValues = {
quotaSwitchPreviewModel: true,
quotaAntigravityCredits: true,
routingStrategy: 'round-robin',
routingSessionAffinity: false,
routingSessionAffinityTTL: '',
wsAuth: false,
payloadDefaultRules: [],
payloadDefaultRawRules: [],