feat(litellm): add provider namespaces with legacy compatibility
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
"""Render only credential-ready routes; credentials remain environment references."""
|
"""Render only credential-ready routes; credentials remain environment references."""
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
from copy import deepcopy
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
@@ -14,7 +15,67 @@ def oauth_ready(path):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def build_config(catalog, env, exists):
|
def namespace_config(config):
|
||||||
|
"""Add explicit provider names without removing existing client aliases."""
|
||||||
|
routes = config['model_list']
|
||||||
|
existing = {r['model_name']: r for r in routes}
|
||||||
|
fallbacks = config['router_settings']['fallbacks']
|
||||||
|
automatic = {name for group in fallbacks for name in group}
|
||||||
|
renamed = {}
|
||||||
|
|
||||||
|
def clone(route, alias):
|
||||||
|
if alias in existing:
|
||||||
|
if existing[alias]['litellm_params'] != route['litellm_params']:
|
||||||
|
raise ValueError('Conflicting namespace alias: ' + alias)
|
||||||
|
return
|
||||||
|
new = deepcopy(route)
|
||||||
|
new['model_name'] = alias
|
||||||
|
existing[alias] = new
|
||||||
|
routes.append(new)
|
||||||
|
|
||||||
|
for route in list(routes):
|
||||||
|
alias = route['model_name']
|
||||||
|
model = route['litellm_params']['model']
|
||||||
|
if alias in {'gemini-3.6-flash', 'gemini-3.5-flash-lite',
|
||||||
|
'google/gemini-embedding-2', 'google/gemini-3.8-flash'}:
|
||||||
|
continue
|
||||||
|
if alias.endswith('-openrouter'):
|
||||||
|
new = 'openrouter/' + alias.removesuffix('-openrouter')
|
||||||
|
elif alias in automatic:
|
||||||
|
new = 'auto/' + alias
|
||||||
|
clone(route, 'zenmux/' + alias)
|
||||||
|
elif model.startswith('anthropic/'):
|
||||||
|
new = ('zenmux-payg/' + alias.removesuffix('-payg')
|
||||||
|
if alias.endswith('-payg') else 'zenmux/' + alias)
|
||||||
|
elif model.startswith('openai/'):
|
||||||
|
name = model.removeprefix('openai/').split('/', 1)[-1]
|
||||||
|
new = 'zenmux/' + (name if alias.startswith('asr-') else
|
||||||
|
alias.replace('-zenmux', '-latest'))
|
||||||
|
elif model.startswith('openrouter/'):
|
||||||
|
new = 'openrouter/' + alias
|
||||||
|
elif model.startswith('gemini/'):
|
||||||
|
new = 'aistudio/' + alias.removesuffix('-studio')
|
||||||
|
elif model.startswith('vertex_ai/'):
|
||||||
|
name = {'google-rerank-default': 'rerank-default-latest',
|
||||||
|
'google-rerank-fast': 'rerank-fast-latest'}.get(alias,
|
||||||
|
alias.replace('-vertex', '-latest'))
|
||||||
|
new = 'vertex/' + name
|
||||||
|
elif model.startswith('chatgpt/'):
|
||||||
|
name = model.split('/', 1)[1] if alias in {'gpt-work-sol', 'gpt-review-5.5'} else alias
|
||||||
|
new = 'codex/' + name
|
||||||
|
elif model.startswith('xai/'):
|
||||||
|
new = 'xai/' + alias
|
||||||
|
else:
|
||||||
|
raise ValueError('Unrecognized namespace provider: ' + alias)
|
||||||
|
clone(route, new)
|
||||||
|
renamed[alias] = new
|
||||||
|
for group in list(fallbacks):
|
||||||
|
for source, targets in group.items():
|
||||||
|
fallbacks.append({renamed[source]: [renamed[target] for target in targets]})
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def build_config(catalog, env, exists, namespaced=True):
|
||||||
routes = []
|
routes = []
|
||||||
fallbacks = []
|
fallbacks = []
|
||||||
|
|
||||||
@@ -73,7 +134,7 @@ def build_config(catalog, env, exists):
|
|||||||
for alias, model in catalog[provider].items():
|
for alias, model in catalog[provider].items():
|
||||||
extra = {'use_xai_oauth': True} if provider == 'xai' else {}
|
extra = {'use_xai_oauth': True} if provider == 'xai' else {}
|
||||||
add(alias, provider + '/' + model, **extra)
|
add(alias, provider + '/' + model, **extra)
|
||||||
return {
|
config = {
|
||||||
'model_list': routes,
|
'model_list': routes,
|
||||||
'general_settings': {
|
'general_settings': {
|
||||||
'master_key': 'os.environ/LITELLM_MASTER_KEY',
|
'master_key': 'os.environ/LITELLM_MASTER_KEY',
|
||||||
@@ -85,6 +146,7 @@ def build_config(catalog, env, exists):
|
|||||||
# Only explicitly approved open-model routes have cross-provider fallbacks.
|
# Only explicitly approved open-model routes have cross-provider fallbacks.
|
||||||
'router_settings': {'num_retries': 1, 'timeout': 180, 'fallbacks': fallbacks},
|
'router_settings': {'num_retries': 1, 'timeout': 180, 'fallbacks': fallbacks},
|
||||||
}
|
}
|
||||||
|
return namespace_config(config) if namespaced else config
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ from pathlib import Path
|
|||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from provider_routes import build_config
|
from provider_routes import build_config, namespace_config
|
||||||
|
|
||||||
|
|
||||||
def build_hk_config(catalog, env):
|
def build_hk_config(catalog, env):
|
||||||
config = build_config(catalog, env, lambda path: False)
|
config = build_config(catalog, env, lambda path: False, namespaced=False)
|
||||||
config['general_settings'] = {'master_key': 'os.environ/LITELLM_MASTER_KEY'}
|
config['general_settings'] = {'master_key': 'os.environ/LITELLM_MASTER_KEY'}
|
||||||
routes = config['model_list']
|
routes = config['model_list']
|
||||||
routes[:] = [route for route in routes if route['model_name'] != 'gemini-transcribe-personal']
|
routes[:] = [route for route in routes if route['model_name'] != 'gemini-transcribe-personal']
|
||||||
@@ -47,7 +47,7 @@ def build_hk_config(catalog, env):
|
|||||||
'model': 'vertex_ai/' + model,
|
'model': 'vertex_ai/' + model,
|
||||||
'vertex_project': 'os.environ/VERTEXAI_PROJECT',
|
'vertex_project': 'os.environ/VERTEXAI_PROJECT',
|
||||||
'vertex_location': 'os.environ/VERTEXAI_LOCATION'}})
|
'vertex_location': 'os.environ/VERTEXAI_LOCATION'}})
|
||||||
return config
|
return namespace_config(config)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -30,7 +30,10 @@ class RoutingTests(unittest.TestCase):
|
|||||||
self.assertEqual(routes[alias]['api_key'], 'os.environ/ZENMUX_SUBSCRIPTION_API_KEY')
|
self.assertEqual(routes[alias]['api_key'], 'os.environ/ZENMUX_SUBSCRIPTION_API_KEY')
|
||||||
self.assertEqual(routes['gemini-transcribe']['model'], 'gemini/gemini-3.5-transcribe')
|
self.assertEqual(routes['gemini-transcribe']['model'], 'gemini/gemini-3.5-transcribe')
|
||||||
self.assertEqual(routes['gemini-3.6-flash']['model'], 'vertex_ai/gemini-3.8-flash')
|
self.assertEqual(routes['gemini-3.6-flash']['model'], 'vertex_ai/gemini-3.8-flash')
|
||||||
self.assertEqual(len(result['router_settings']['fallbacks']), 6)
|
self.assertEqual(len(result['router_settings']['fallbacks']), 12)
|
||||||
|
self.assertEqual(routes['vertex/gemini-transcribe-personal'], routes['gemini-transcribe-personal'])
|
||||||
|
self.assertNotIn('aistudio/gemini-transcribe-personal', routes)
|
||||||
|
self.assertEqual(routes['zenmux/gpt-transcribe'], routes['asr-gpt'])
|
||||||
self.assertEqual(result['general_settings'], {'master_key': 'os.environ/LITELLM_MASTER_KEY'})
|
self.assertEqual(result['general_settings'], {'master_key': 'os.environ/LITELLM_MASTER_KEY'})
|
||||||
self.assertNotIn('synthetic', yaml.safe_dump(result))
|
self.assertNotIn('synthetic', yaml.safe_dump(result))
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# 模型命名空间
|
||||||
|
|
||||||
|
2026-10-27 起新增客户端命名空间,保留旧名兼容。实际型号、协议、凭据、个人热词和 SMART 均不改变;latest 不代表自动跟踪发布。
|
||||||
|
|
||||||
|
| 前缀 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| zenmux/ | ZenMux 订阅直连,不跨 provider 回退 |
|
||||||
|
| zenmux-payg/ | ZenMux PAYG,仅显式请求 |
|
||||||
|
| openrouter/ | OpenRouter 直连 |
|
||||||
|
| auto/ | 开源模型 ZenMux 订阅优先,故障回退 OpenRouter |
|
||||||
|
| aistudio/ | Google AI Studio |
|
||||||
|
| vertex/ | Google Vertex |
|
||||||
|
| codex/ | Codex OAuth 订阅 |
|
||||||
|
| xai/ | xAI OAuth 订阅 |
|
||||||
|
|
||||||
|
常规旧别名加对应前缀。特殊转换:`gemini-flash-zenmux` → `zenmux/gemini-flash-latest`;`gemini-*-vertex` → `vertex/gemini-*-latest`;`gemini-embedding-2-studio` → `aistudio/gemini-embedding-2`;`google-rerank-default/fast` → `vertex/rerank-default/fast-latest`;`gpt-work-sol` → `codex/gpt-6-sol`;`gpt-review-5.5` → `codex/gpt-5.5`。PAYG 去掉旧 `-payg` 后缀,改加 `zenmux-payg/` 前缀。
|
||||||
|
|
||||||
|
六组开源模型 `deepseek-flash-latest`、`deepseek-pro-latest`、`glm-latest`、`glm-flash-latest`、`kimi-latest`、`mimo-flash-latest` 各提供 auto/、zenmux/、openrouter/ 三种选择。旧自动回退名和旧 -openrouter 名保留。
|
||||||
|
|
||||||
|
tools-pve 的个人语音名为 `aistudio/gemini-transcribe-personal`;HK 使用 `vertex/gemini-transcribe-personal`,不能互换两端 provider。HK 四款 ZenMux ASR 别名为 `zenmux/gpt-transcribe`、`zenmux/qwen-audio-3.1-asr-flash`、`zenmux/doubao-seed-asr-2.0`、`zenmux/mimo-v2.5-asr`。HK 的四个历史 Google 兼容名保留。
|
||||||
|
|
||||||
|
只有凭据已就绪的路线才生成。新旧别名的 litellm_params 必须完全相同,不能把客户端前缀当作上游 provider 参数。受模型白名单限制的 virtual key 需要另行授权新名;不扩大既有 key 权限。旧名暂不删除,待所有客户端完成迁移后单独处理。
|
||||||
|
|
||||||
|
修改 renderer 后,通过 Arcane Git Sync 同步,并重启对应 LiteLLM 进程使 /tmp/litellm.yaml 重建;仅同步代码不代表进程已加载。逐节点确认 /v1/models、新旧路由参数等价、fallback 配置及实际请求。
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
"""Render only credential-ready routes; credentials remain environment references."""
|
"""Render only credential-ready routes; credentials remain environment references."""
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
from copy import deepcopy
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
@@ -14,7 +15,67 @@ def oauth_ready(path):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def build_config(catalog, env, exists):
|
def namespace_config(config):
|
||||||
|
"""Add explicit provider names without removing existing client aliases."""
|
||||||
|
routes = config['model_list']
|
||||||
|
existing = {r['model_name']: r for r in routes}
|
||||||
|
fallbacks = config['router_settings']['fallbacks']
|
||||||
|
automatic = {name for group in fallbacks for name in group}
|
||||||
|
renamed = {}
|
||||||
|
|
||||||
|
def clone(route, alias):
|
||||||
|
if alias in existing:
|
||||||
|
if existing[alias]['litellm_params'] != route['litellm_params']:
|
||||||
|
raise ValueError('Conflicting namespace alias: ' + alias)
|
||||||
|
return
|
||||||
|
new = deepcopy(route)
|
||||||
|
new['model_name'] = alias
|
||||||
|
existing[alias] = new
|
||||||
|
routes.append(new)
|
||||||
|
|
||||||
|
for route in list(routes):
|
||||||
|
alias = route['model_name']
|
||||||
|
model = route['litellm_params']['model']
|
||||||
|
if alias in {'gemini-3.6-flash', 'gemini-3.5-flash-lite',
|
||||||
|
'google/gemini-embedding-2', 'google/gemini-3.8-flash'}:
|
||||||
|
continue
|
||||||
|
if alias.endswith('-openrouter'):
|
||||||
|
new = 'openrouter/' + alias.removesuffix('-openrouter')
|
||||||
|
elif alias in automatic:
|
||||||
|
new = 'auto/' + alias
|
||||||
|
clone(route, 'zenmux/' + alias)
|
||||||
|
elif model.startswith('anthropic/'):
|
||||||
|
new = ('zenmux-payg/' + alias.removesuffix('-payg')
|
||||||
|
if alias.endswith('-payg') else 'zenmux/' + alias)
|
||||||
|
elif model.startswith('openai/'):
|
||||||
|
name = model.removeprefix('openai/').split('/', 1)[-1]
|
||||||
|
new = 'zenmux/' + (name if alias.startswith('asr-') else
|
||||||
|
alias.replace('-zenmux', '-latest'))
|
||||||
|
elif model.startswith('openrouter/'):
|
||||||
|
new = 'openrouter/' + alias
|
||||||
|
elif model.startswith('gemini/'):
|
||||||
|
new = 'aistudio/' + alias.removesuffix('-studio')
|
||||||
|
elif model.startswith('vertex_ai/'):
|
||||||
|
name = {'google-rerank-default': 'rerank-default-latest',
|
||||||
|
'google-rerank-fast': 'rerank-fast-latest'}.get(alias,
|
||||||
|
alias.replace('-vertex', '-latest'))
|
||||||
|
new = 'vertex/' + name
|
||||||
|
elif model.startswith('chatgpt/'):
|
||||||
|
name = model.split('/', 1)[1] if alias in {'gpt-work-sol', 'gpt-review-5.5'} else alias
|
||||||
|
new = 'codex/' + name
|
||||||
|
elif model.startswith('xai/'):
|
||||||
|
new = 'xai/' + alias
|
||||||
|
else:
|
||||||
|
raise ValueError('Unrecognized namespace provider: ' + alias)
|
||||||
|
clone(route, new)
|
||||||
|
renamed[alias] = new
|
||||||
|
for group in list(fallbacks):
|
||||||
|
for source, targets in group.items():
|
||||||
|
fallbacks.append({renamed[source]: [renamed[target] for target in targets]})
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def build_config(catalog, env, exists, namespaced=True):
|
||||||
routes = []
|
routes = []
|
||||||
fallbacks = []
|
fallbacks = []
|
||||||
|
|
||||||
@@ -73,7 +134,7 @@ def build_config(catalog, env, exists):
|
|||||||
for alias, model in catalog[provider].items():
|
for alias, model in catalog[provider].items():
|
||||||
extra = {'use_xai_oauth': True} if provider == 'xai' else {}
|
extra = {'use_xai_oauth': True} if provider == 'xai' else {}
|
||||||
add(alias, provider + '/' + model, **extra)
|
add(alias, provider + '/' + model, **extra)
|
||||||
return {
|
config = {
|
||||||
'model_list': routes,
|
'model_list': routes,
|
||||||
'general_settings': {
|
'general_settings': {
|
||||||
'master_key': 'os.environ/LITELLM_MASTER_KEY',
|
'master_key': 'os.environ/LITELLM_MASTER_KEY',
|
||||||
@@ -85,6 +146,7 @@ def build_config(catalog, env, exists):
|
|||||||
# Only explicitly approved open-model routes have cross-provider fallbacks.
|
# Only explicitly approved open-model routes have cross-provider fallbacks.
|
||||||
'router_settings': {'num_retries': 1, 'timeout': 180, 'fallbacks': fallbacks},
|
'router_settings': {'num_retries': 1, 'timeout': 180, 'fallbacks': fallbacks},
|
||||||
}
|
}
|
||||||
|
return namespace_config(config) if namespaced else config
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
import yaml
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from render_config import build_config, oauth_ready
|
from render_config import build_config, oauth_ready
|
||||||
@@ -17,6 +18,29 @@ class ConfigTests(unittest.TestCase):
|
|||||||
def test_missing_credentials_exclude_routes(self):
|
def test_missing_credentials_exclude_routes(self):
|
||||||
self.assertEqual(build_config(self.catalog, {}, lambda _: False)['model_list'], [])
|
self.assertEqual(build_config(self.catalog, {}, lambda _: False)['model_list'], [])
|
||||||
|
|
||||||
|
def test_namespaces_preserve_every_legacy_route(self):
|
||||||
|
catalog = yaml.safe_load(Path(__file__).with_name('models.yaml').read_text())
|
||||||
|
env = {key: 'synthetic' for key in ['ZENMUX_SUBSCRIPTION_API_KEY',
|
||||||
|
'ZENMUX_PAYG_API_KEY', 'OPENROUTER_API_KEY', 'GOOGLE_AI_STUDIO_API_KEY',
|
||||||
|
'VERTEXAI_PROJECT']}
|
||||||
|
env['VERTEXAI_ENABLED'] = 'true'
|
||||||
|
old = build_config(catalog, env, lambda _: True, namespaced=False)
|
||||||
|
new = build_config(catalog, env, lambda _: True)
|
||||||
|
routes = {r['model_name']: r for r in new['model_list']}
|
||||||
|
self.assertEqual(len(routes), len(new['model_list']))
|
||||||
|
for route in old['model_list']:
|
||||||
|
self.assertEqual(route, routes[route['model_name']])
|
||||||
|
for old_name, new_name in {
|
||||||
|
'claude-sonnet-latest': 'zenmux/claude-sonnet-latest',
|
||||||
|
'claude-opus-latest-payg': 'zenmux-payg/claude-opus-latest',
|
||||||
|
'grok-latest': 'xai/grok-latest', 'gpt-sol-latest': 'codex/gpt-sol-latest',
|
||||||
|
'gpt-work-sol': 'codex/gpt-6-sol', 'gpt-review-5.5': 'codex/gpt-5.5',
|
||||||
|
'gemini-transcribe-personal': 'aistudio/gemini-transcribe-personal',
|
||||||
|
'gemini-embedding-2-studio': 'aistudio/gemini-embedding-2',
|
||||||
|
'google-rerank-fast': 'vertex/rerank-fast-latest',
|
||||||
|
}.items():
|
||||||
|
self.assertEqual(routes[old_name]['litellm_params'], routes[new_name]['litellm_params'])
|
||||||
|
|
||||||
def test_env_ui_login_disabled(self):
|
def test_env_ui_login_disabled(self):
|
||||||
settings = build_config(self.catalog, {}, lambda _: False)['general_settings']
|
settings = build_config(self.catalog, {}, lambda _: False)['general_settings']
|
||||||
self.assertTrue(settings['disable_env_credential_login'])
|
self.assertTrue(settings['disable_env_credential_login'])
|
||||||
@@ -32,7 +56,7 @@ class ConfigTests(unittest.TestCase):
|
|||||||
|
|
||||||
def test_oauth_requires_persistent_file(self):
|
def test_oauth_requires_persistent_file(self):
|
||||||
routes = build_config(self.catalog, {}, lambda path: str(path) == '/tokens/xai/auth.json')['model_list']
|
routes = build_config(self.catalog, {}, lambda path: str(path) == '/tokens/xai/auth.json')['model_list']
|
||||||
self.assertEqual(len(routes), 1)
|
self.assertEqual(len(routes), 2)
|
||||||
self.assertTrue(routes[0]['litellm_params']['use_xai_oauth'])
|
self.assertTrue(routes[0]['litellm_params']['use_xai_oauth'])
|
||||||
self.assertNotIn('api_key', routes[0]['litellm_params'])
|
self.assertNotIn('api_key', routes[0]['litellm_params'])
|
||||||
|
|
||||||
@@ -61,7 +85,12 @@ class ConfigTests(unittest.TestCase):
|
|||||||
routes = {r['model_name']: r for r in config['model_list']}
|
routes = {r['model_name']: r for r in config['model_list']}
|
||||||
self.assertEqual(routes['glm-latest']['litellm_params']['model'], 'openai/z-ai/glm-5.3-prime')
|
self.assertEqual(routes['glm-latest']['litellm_params']['model'], 'openai/z-ai/glm-5.3-prime')
|
||||||
self.assertEqual(routes['glm-latest-openrouter']['litellm_params']['model'], 'openrouter/z-ai/glm-5.3-prime')
|
self.assertEqual(routes['glm-latest-openrouter']['litellm_params']['model'], 'openrouter/z-ai/glm-5.3-prime')
|
||||||
self.assertEqual(config['router_settings']['fallbacks'], [{'glm-latest': ['glm-latest-openrouter']}])
|
self.assertEqual(config['router_settings']['fallbacks'], [
|
||||||
|
{'glm-latest': ['glm-latest-openrouter']},
|
||||||
|
{'auto/glm-latest': ['openrouter/glm-latest']}])
|
||||||
|
self.assertEqual(routes['auto/glm-latest']['litellm_params'], routes['glm-latest']['litellm_params'])
|
||||||
|
self.assertEqual(routes['zenmux/glm-latest']['litellm_params'], routes['glm-latest']['litellm_params'])
|
||||||
|
self.assertNotIn('zenmux/glm-latest', str(config['router_settings']['fallbacks']))
|
||||||
self.assertEqual(routes['glm-latest']['model_info']['cooldown_time'], 60)
|
self.assertEqual(routes['glm-latest']['model_info']['cooldown_time'], 60)
|
||||||
self.assertNotIn('glm-latest-payg', routes)
|
self.assertNotIn('glm-latest-payg', routes)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# 模型命名空间
|
||||||
|
|
||||||
|
2026-10-27 起新增客户端命名空间,保留旧名兼容。实际型号、协议、凭据、个人热词和 SMART 均不改变;latest 不代表自动跟踪发布。
|
||||||
|
|
||||||
|
| 前缀 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| zenmux/ | ZenMux 订阅直连,不跨 provider 回退 |
|
||||||
|
| zenmux-payg/ | ZenMux PAYG,仅显式请求 |
|
||||||
|
| openrouter/ | OpenRouter 直连 |
|
||||||
|
| auto/ | 开源模型 ZenMux 订阅优先,故障回退 OpenRouter |
|
||||||
|
| aistudio/ | Google AI Studio |
|
||||||
|
| vertex/ | Google Vertex |
|
||||||
|
| codex/ | Codex OAuth 订阅 |
|
||||||
|
| xai/ | xAI OAuth 订阅 |
|
||||||
|
|
||||||
|
常规旧别名加对应前缀。特殊转换:`gemini-flash-zenmux` → `zenmux/gemini-flash-latest`;`gemini-*-vertex` → `vertex/gemini-*-latest`;`gemini-embedding-2-studio` → `aistudio/gemini-embedding-2`;`google-rerank-default/fast` → `vertex/rerank-default/fast-latest`;`gpt-work-sol` → `codex/gpt-6-sol`;`gpt-review-5.5` → `codex/gpt-5.5`。PAYG 去掉旧 `-payg` 后缀,改加 `zenmux-payg/` 前缀。
|
||||||
|
|
||||||
|
六组开源模型 `deepseek-flash-latest`、`deepseek-pro-latest`、`glm-latest`、`glm-flash-latest`、`kimi-latest`、`mimo-flash-latest` 各提供 auto/、zenmux/、openrouter/ 三种选择。旧自动回退名和旧 -openrouter 名保留。
|
||||||
|
|
||||||
|
tools-pve 的个人语音名为 `aistudio/gemini-transcribe-personal`;HK 使用 `vertex/gemini-transcribe-personal`,不能互换两端 provider。HK 四款 ZenMux ASR 别名为 `zenmux/gpt-transcribe`、`zenmux/qwen-audio-3.1-asr-flash`、`zenmux/doubao-seed-asr-2.0`、`zenmux/mimo-v2.5-asr`。HK 的四个历史 Google 兼容名保留。
|
||||||
|
|
||||||
|
只有凭据已就绪的路线才生成。新旧别名的 litellm_params 必须完全相同,不能把客户端前缀当作上游 provider 参数。受模型白名单限制的 virtual key 需要另行授权新名;不扩大既有 key 权限。旧名暂不删除,待所有客户端完成迁移后单独处理。
|
||||||
|
|
||||||
|
修改 renderer 后,通过 Arcane Git Sync 同步,并重启对应 LiteLLM 进程使 /tmp/litellm.yaml 重建;仅同步代码不代表进程已加载。逐节点确认 /v1/models、新旧路由参数等价、fallback 配置及实际请求。
|
||||||
Reference in New Issue
Block a user