fix(litellm): gate OAuth routes on real credentials
This commit is contained in:
@@ -29,6 +29,8 @@ Google AI Studio key 尚未取得;latest 自动维护尚未实现。
|
|||||||
只使用单个代理 worker,降低 OAuth refresh token 竞争的风险。
|
只使用单个代理 worker,降低 OAuth refresh token 竞争的风险。
|
||||||
所有配置从 Git 生成,不允许 UI 中的模型配置成为另一份权威。
|
所有配置从 Git 生成,不允许 UI 中的模型配置成为另一份权威。
|
||||||
未提供 key 或未完成 OAuth 登录的路线不会进入运行配置。
|
未提供 key 或未完成 OAuth 登录的路线不会进入运行配置。
|
||||||
|
OAuth 判定检查 auth.json 中 access_token,不仅检查文件存在性:
|
||||||
|
Codex 设备授权期间会先写入冷却占位文件,这不代表已经授权。
|
||||||
|
|
||||||
## Provider 与缓存
|
## Provider 与缓存
|
||||||
|
|
||||||
@@ -100,6 +102,8 @@ bash oauth-login.sh xai
|
|||||||
登录 helper 使用 host network 仅供本地 OAuth 回调,生产代理仍使用独立 bridge。
|
登录 helper 使用 host network 仅供本地 OAuth 回调,生产代理仍使用独立 bridge。
|
||||||
|
|
||||||
确认账号实际支持的模型 ID 后,在 `models.yaml` 的 `chatgpt`/`xai` 表添加别名。
|
确认账号实际支持的模型 ID 后,在 `models.yaml` 的 `chatgpt`/`xai` 表添加别名。
|
||||||
|
已预置的 OAuth 候选为 gpt-luna-latest/gpt-sol-latest/grok-latest,
|
||||||
|
只是先前 Hindsight 使用过的模型,不代表本代理独立账号已授权。
|
||||||
Codex 路线由 `chatgpt/` 访问,优先 `/v1/responses`;
|
Codex 路线由 `chatgpt/` 访问,优先 `/v1/responses`;
|
||||||
xAI 路线使用 `xai/` 与 `use_xai_oauth: true`,不能设置 XAI API key 覆盖它。
|
xAI 路线使用 `xai/` 与 `use_xai_oauth: true`,不能设置 XAI API key 覆盖它。
|
||||||
新增 token 后需重启代理,运行时 refresh 会写回同一个持久卷。
|
新增 token 后需重启代理,运行时 refresh 会写回同一个持久卷。
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ openrouter:
|
|||||||
kimi-latest: moonshotai/kimi-k3
|
kimi-latest: moonshotai/kimi-k3
|
||||||
mimo-flash-latest: xiaomi/mimo-v2.6-flash
|
mimo-flash-latest: xiaomi/mimo-v2.6-flash
|
||||||
google_ai_studio: {}
|
google_ai_studio: {}
|
||||||
# OAuth 模型需完成独立登录,并根据账号实际模型目录确认后启用。
|
# 已有 Hindsight 部署用过的候选模型;本代理仍需独立授权与请求验收。
|
||||||
chatgpt: {}
|
# renderer 在实际 token 出现前排除这些路线。
|
||||||
xai: {}
|
chatgpt:
|
||||||
|
gpt-luna-latest: gpt-6-luna
|
||||||
|
gpt-sol-latest: gpt-6-sol
|
||||||
|
xai:
|
||||||
|
grok-latest: grok-4.7
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
"""Render only credential-ready routes; credentials remain environment references."""
|
"""Render only credential-ready routes; credentials remain environment references."""
|
||||||
import os
|
import os
|
||||||
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
def oauth_ready(path):
|
||||||
|
try:
|
||||||
|
record = json.loads(path.read_text())
|
||||||
|
return isinstance(record, dict) and bool(record.get('access_token'))
|
||||||
|
except (OSError, ValueError):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def build_config(catalog, env, exists):
|
def build_config(catalog, env, exists):
|
||||||
routes = []
|
routes = []
|
||||||
|
|
||||||
@@ -49,7 +58,7 @@ def build_config(catalog, env, exists):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
catalog = yaml.safe_load(Path('/etc/litellm/models.yaml').read_text())
|
catalog = yaml.safe_load(Path('/etc/litellm/models.yaml').read_text())
|
||||||
config = build_config(catalog, os.environ, Path.is_file)
|
config = build_config(catalog, os.environ, oauth_ready)
|
||||||
output = Path('/tmp/litellm.yaml')
|
output = Path('/tmp/litellm.yaml')
|
||||||
output.write_text(yaml.safe_dump(config, allow_unicode=True, sort_keys=False))
|
output.write_text(yaml.safe_dump(config, allow_unicode=True, sort_keys=False))
|
||||||
output.chmod(0o600)
|
output.chmod(0o600)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from render_config import build_config
|
from pathlib import Path
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
|
from render_config import build_config, oauth_ready
|
||||||
|
|
||||||
|
|
||||||
class ConfigTests(unittest.TestCase):
|
class ConfigTests(unittest.TestCase):
|
||||||
@@ -33,6 +35,15 @@ class ConfigTests(unittest.TestCase):
|
|||||||
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'])
|
||||||
|
|
||||||
|
def test_pending_oauth_is_not_ready(self):
|
||||||
|
with TemporaryDirectory() as folder:
|
||||||
|
path = Path(folder) / 'auth.json'
|
||||||
|
self.assertFalse(oauth_ready(path))
|
||||||
|
path.write_text('{"device_code_requested_at": 1}')
|
||||||
|
self.assertFalse(oauth_ready(path))
|
||||||
|
path.write_text('{"access_token": "fixture-only"}')
|
||||||
|
self.assertTrue(oauth_ready(path))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user