Merge pull request #3488 from clansty/feature/channel-affinity-include-model
feat: add IncludeModelName option to channel affinity rules
This commit is contained in:
@@ -166,12 +166,22 @@ func GetChannelAffinityCacheStats() ChannelAffinityCacheStats {
|
||||
unknown++
|
||||
continue
|
||||
}
|
||||
if rule.IncludeUsingGroup {
|
||||
if rule.IncludeModelName {
|
||||
if len(parts) < 3 {
|
||||
unknown++
|
||||
continue
|
||||
}
|
||||
}
|
||||
if rule.IncludeUsingGroup {
|
||||
minParts := 3
|
||||
if rule.IncludeModelName {
|
||||
minParts = 4
|
||||
}
|
||||
if len(parts) < minParts {
|
||||
unknown++
|
||||
continue
|
||||
}
|
||||
}
|
||||
byRuleName[ruleName]++
|
||||
}
|
||||
|
||||
@@ -319,11 +329,14 @@ func extractChannelAffinityValue(c *gin.Context, src operation_setting.ChannelAf
|
||||
}
|
||||
}
|
||||
|
||||
func buildChannelAffinityCacheKeySuffix(rule operation_setting.ChannelAffinityRule, usingGroup string, affinityValue string) string {
|
||||
parts := make([]string, 0, 3)
|
||||
func buildChannelAffinityCacheKeySuffix(rule operation_setting.ChannelAffinityRule, modelName string, usingGroup string, affinityValue string) string {
|
||||
parts := make([]string, 0, 4)
|
||||
if rule.IncludeRuleName && rule.Name != "" {
|
||||
parts = append(parts, rule.Name)
|
||||
}
|
||||
if rule.IncludeModelName && modelName != "" {
|
||||
parts = append(parts, modelName)
|
||||
}
|
||||
if rule.IncludeUsingGroup && usingGroup != "" {
|
||||
parts = append(parts, usingGroup)
|
||||
}
|
||||
@@ -573,7 +586,7 @@ func GetPreferredChannelByAffinity(c *gin.Context, modelName string, usingGroup
|
||||
if ttlSeconds <= 0 {
|
||||
ttlSeconds = setting.DefaultTTLSeconds
|
||||
}
|
||||
cacheKeySuffix := buildChannelAffinityCacheKeySuffix(rule, usingGroup, affinityValue)
|
||||
cacheKeySuffix := buildChannelAffinityCacheKeySuffix(rule, modelName, usingGroup, affinityValue)
|
||||
cacheKeyFull := channelAffinityCacheNamespace + ":" + cacheKeySuffix
|
||||
setChannelAffinityContext(c, channelAffinityMeta{
|
||||
CacheKey: cacheKeyFull,
|
||||
|
||||
@@ -193,7 +193,7 @@ func TestChannelAffinityHitCodexTemplatePassHeadersEffective(t *testing.T) {
|
||||
require.NotNil(t, codexRule)
|
||||
|
||||
affinityValue := fmt.Sprintf("pc-hit-%d", time.Now().UnixNano())
|
||||
cacheKeySuffix := buildChannelAffinityCacheKeySuffix(*codexRule, "default", affinityValue)
|
||||
cacheKeySuffix := buildChannelAffinityCacheKeySuffix(*codexRule, "gpt-5", "default", affinityValue)
|
||||
|
||||
cache := getChannelAffinityCache()
|
||||
require.NoError(t, cache.SetWithTTL(cacheKeySuffix, 9527, time.Minute))
|
||||
|
||||
@@ -23,6 +23,7 @@ type ChannelAffinityRule struct {
|
||||
SkipRetryOnFailure bool `json:"skip_retry_on_failure,omitempty"`
|
||||
|
||||
IncludeUsingGroup bool `json:"include_using_group"`
|
||||
IncludeModelName bool `json:"include_model_name"`
|
||||
IncludeRuleName bool `json:"include_rule_name"`
|
||||
}
|
||||
|
||||
|
||||
Vendored
+2
@@ -443,6 +443,8 @@
|
||||
"作废后该订阅将立即失效,历史记录不受影响。是否继续?": "After invalidation, the subscription becomes invalid immediately. History is not affected. Continue?",
|
||||
"作用域": "Scope",
|
||||
"作用域:包含分组": "Scope: Include Group",
|
||||
"作用域:包含模型名称": "Scope: Include Model Name",
|
||||
"开启后,模型名称会参与 cache key(不同模型隔离)。": "When enabled, the model name is included in the cache key (isolates different models).",
|
||||
"作用域:包含规则名称": "Scope: Include Rule Name",
|
||||
"你似乎并没有修改什么": "You seem to have not modified anything",
|
||||
"你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。": "You can manually add them under “Custom model names”, click Fill and submit, or use the actions below to handle them automatically.",
|
||||
|
||||
Vendored
+2
@@ -438,6 +438,8 @@
|
||||
"作废后该订阅将立即失效,历史记录不受影响。是否继续?": "Après invalidation, l'abonnement devient immédiatement invalide. L'historique n'est pas affecté. Continuer ?",
|
||||
"作用域": "Portée",
|
||||
"作用域:包含分组": "Portée : inclure le groupe",
|
||||
"作用域:包含模型名称": "Portée : inclure le nom du modèle",
|
||||
"开启后,模型名称会参与 cache key(不同模型隔离)。": "Lorsque activé, le nom du modèle est inclus dans la clé de cache (isole les différents modèles).",
|
||||
"作用域:包含规则名称": "Portée : inclure le nom de la règle",
|
||||
"你似乎并没有修改什么": "Vous ne semblez rien avoir modifié",
|
||||
"你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。": "Vous pouvez les ajouter manuellement dans « Noms de modèles personnalisés », cliquer sur Remplir puis soumettre, ou utiliser directement les actions ci-dessous pour les traiter automatiquement.",
|
||||
|
||||
Vendored
+2
@@ -434,6 +434,8 @@
|
||||
"作废后该订阅将立即失效,历史记录不受影响。是否继续?": "無効化するとこのサブスクリプションは直ちに失効します。履歴には影響しません。続行しますか?",
|
||||
"作用域": "スコープ",
|
||||
"作用域:包含分组": "スコープ:グループを含む",
|
||||
"作用域:包含模型名称": "スコープ:モデル名を含む",
|
||||
"开启后,模型名称会参与 cache key(不同模型隔离)。": "有効にすると、モデル名がキャッシュキーに含まれます(異なるモデルを分離)。",
|
||||
"作用域:包含规则名称": "スコープ:ルール名を含む",
|
||||
"你似乎并没有修改什么": "何も変更されていないようです",
|
||||
"你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。": "You can manually add them under “Custom model names”, click Fill and submit, or use the actions below to handle them automatically.",
|
||||
|
||||
Vendored
+2
@@ -441,6 +441,8 @@
|
||||
"作废后该订阅将立即失效,历史记录不受影响。是否继续?": "После аннулирования подписка сразу станет недействительной. История не изменится. Продолжить?",
|
||||
"作用域": "Область действия",
|
||||
"作用域:包含分组": "Область действия: включить группу",
|
||||
"作用域:包含模型名称": "Область действия: включить имя модели",
|
||||
"开启后,模型名称会参与 cache key(不同模型隔离)。": "При включении имя модели включается в ключ кэша (изолирует разные модели).",
|
||||
"作用域:包含规则名称": "Область действия: включить имя правила",
|
||||
"你似乎并没有修改什么": "Похоже, вы ничего не изменили",
|
||||
"你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。": "Вы можете добавить их вручную в разделе «Пользовательские названия моделей», нажать «Заполнить», затем отправить или воспользоваться действиями ниже для автоматической обработки.",
|
||||
|
||||
Vendored
+2
@@ -435,6 +435,8 @@
|
||||
"作废后该订阅将立即失效,历史记录不受影响。是否继续?": "Sau khi vô hiệu, đăng ký sẽ mất hiệu lực ngay. Lịch sử không bị ảnh hưởng. Tiếp tục?",
|
||||
"作用域": "Phạm vi",
|
||||
"作用域:包含分组": "Phạm vi: Bao gồm nhóm",
|
||||
"作用域:包含模型名称": "Phạm vi: Bao gồm tên mô hình",
|
||||
"开启后,模型名称会参与 cache key(不同模型隔离)。": "Khi bật, tên mô hình sẽ được bao gồm trong cache key (cách ly các mô hình khác nhau).",
|
||||
"作用域:包含规则名称": "Phạm vi: Bao gồm tên quy tắc",
|
||||
"你似乎并没有修改什么": "Bạn dường như không sửa đổi gì cả",
|
||||
"你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。": "You can manually add them under “Custom model names”, click Fill and submit, or use the actions below to handle them automatically.",
|
||||
|
||||
@@ -103,6 +103,7 @@ const RULES_JSON_PLACEHOLDER = `[
|
||||
},
|
||||
"skip_retry_on_failure": false,
|
||||
"include_using_group": true,
|
||||
"include_model_name": false,
|
||||
"include_rule_name": true
|
||||
}
|
||||
]`;
|
||||
@@ -246,6 +247,7 @@ export default function SettingsChannelAffinity(props) {
|
||||
ttl_seconds: Number(r.ttl_seconds || 0),
|
||||
skip_retry_on_failure: !!r.skip_retry_on_failure,
|
||||
include_using_group: r.include_using_group ?? true,
|
||||
include_model_name: !!r.include_model_name,
|
||||
include_rule_name: r.include_rule_name ?? true,
|
||||
param_override_template_json: r.param_override_template
|
||||
? stringifyPretty(r.param_override_template)
|
||||
@@ -581,8 +583,9 @@ export default function SettingsChannelAffinity(props) {
|
||||
title: t('作用域'),
|
||||
render: (_, record) => {
|
||||
const tags = [];
|
||||
if (record?.include_using_group) tags.push('分组');
|
||||
if (record?.include_rule_name) tags.push('规则');
|
||||
if (record?.include_using_group) tags.push(t('分组'));
|
||||
if (record?.include_model_name) tags.push(t('模型'));
|
||||
if (record?.include_rule_name) tags.push(t('规则'));
|
||||
if (tags.length === 0) return '-';
|
||||
return tags.map((x) => (
|
||||
<Tag key={x} style={{ marginRight: 4 }}>
|
||||
@@ -650,6 +653,7 @@ export default function SettingsChannelAffinity(props) {
|
||||
ttl_seconds: 0,
|
||||
skip_retry_on_failure: false,
|
||||
include_using_group: true,
|
||||
include_model_name: false,
|
||||
include_rule_name: true,
|
||||
};
|
||||
setEditingRule(nextRule);
|
||||
@@ -721,6 +725,7 @@ export default function SettingsChannelAffinity(props) {
|
||||
value_regex: (values.value_regex || '').trim(),
|
||||
ttl_seconds: Number(values.ttl_seconds || 0),
|
||||
include_using_group: !!values.include_using_group,
|
||||
include_model_name: !!values.include_model_name,
|
||||
include_rule_name: !!values.include_rule_name,
|
||||
...(values.skip_retry_on_failure
|
||||
? { skip_retry_on_failure: true }
|
||||
@@ -1251,7 +1256,7 @@ export default function SettingsChannelAffinity(props) {
|
||||
</Row>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col xs={24} sm={12}>
|
||||
<Col xs={24} sm={8}>
|
||||
<Form.Switch
|
||||
field='include_using_group'
|
||||
label={t('作用域:包含分组')}
|
||||
@@ -1262,7 +1267,18 @@ export default function SettingsChannelAffinity(props) {
|
||||
)}
|
||||
</Text>
|
||||
</Col>
|
||||
<Col xs={24} sm={12}>
|
||||
<Col xs={24} sm={8}>
|
||||
<Form.Switch
|
||||
field='include_model_name'
|
||||
label={t('作用域:包含模型名称')}
|
||||
/>
|
||||
<Text type='tertiary' size='small'>
|
||||
{t(
|
||||
'开启后,模型名称会参与 cache key(不同模型隔离)。',
|
||||
)}
|
||||
</Text>
|
||||
</Col>
|
||||
<Col xs={24} sm={8}>
|
||||
<Form.Switch
|
||||
field='include_rule_name'
|
||||
label={t('作用域:包含规则名称')}
|
||||
|
||||
Reference in New Issue
Block a user