补充 OpenAI 主备验收与可复用双协议测试

This commit is contained in:
Codex
2026-10-28 18:23:21 -07:00
parent 205bd3bbbc
commit 81c475464e
2 changed files with 84 additions and 0 deletions
+15
View File
@@ -41,6 +41,21 @@ GPT路线对应备用模型,不开放图像、语音等无关能力。Claude
依据:[OpenAI赠金说明](https://developers.openai.com/api/docs/guides/chatgpt-api-credits)、
[LiteLLM回退](https://docs.litellm.ai/docs/proxy/reliability)。
本轮验收:18项配置测试通过;官方Luna/Sol/Terra的Responses、Luna Chat、
Codex Luna Responses完整SSE通过(正常请求fallbacks=0);embedding small、
TTS与合成语音的gpt-transcribe/mini-transcribe通过。图像未实际生成,
diarize与embedding large只配置且模型目录可见,不能据此声称完成任务验收。
`verify_openai_fallback.py`在独立容器exec进程中对Chat与Responses各注入
429/401/503,六例均收到真实官方API的短回复;不是人为耗尽订阅,也不修改
生产worker的函数或真实OAuth token。复测命令(会有少量API用量):
```bash
ssh tools@192.168.50.49 'sudo -n docker exec -i tools-litellm python -' < verify_openai_fallback.py
```
Arcane Sync及Up终态success;PostgreSQL启动时间仍是
`2026-10-12T05:46:12.472887407Z`,数据库及OAuth卷未重建。
## 部署状态
2026-10-09:已通过 Arcane 上线,LiteLLM 1.104.2 与 PostgreSQL 均 healthy。
@@ -0,0 +1,69 @@
#!/usr/bin/env python3
"""Run inside a separate LiteLLM container exec: fake OAuth failures, real API backup."""
import asyncio
from copy import deepcopy
from functools import wraps
import json
import os
from pathlib import Path
from unittest.mock import patch
import litellm
import yaml
litellm.suppress_debug_info = True
async def main():
config = yaml.safe_load(Path('/tmp/litellm.yaml').read_text())
primary, backup = 'codex/gpt-luna-latest', 'openai/gpt-luna-latest'
routes = [deepcopy(r) for r in config['model_list'] if r['model_name'] in (primary, backup)]
assert len(routes) == 2
assert {primary: [backup]} in config['router_settings']['fallbacks']
for route in routes:
if route['model_name'] == backup:
route['litellm_params']['api_key'] = os.environ['OPENAI_API_KEY']
actual_chat, actual_responses = litellm.acompletion, litellm.aresponses
for protocol in ('chat', 'responses'):
for error_type, expected in ((litellm.RateLimitError, 429),
(litellm.AuthenticationError, 401),
(litellm.ServiceUnavailableError, 503)):
calls = []
async def invoke(real, *args, **kwargs):
model = kwargs.get('model', '')
if model.startswith('chatgpt/'):
calls.append('subscription-rejected')
raise error_type(message='Synthetic OAuth failure; isolated test only',
llm_provider='chatgpt', model=model)
assert model.startswith('openai/')
calls.append('official-api')
return await real(*args, **kwargs)
@wraps(actual_chat)
async def chat(*args, **kwargs):
return await invoke(actual_chat, *args, **kwargs)
@wraps(actual_responses)
async def responses(*args, **kwargs):
return await invoke(actual_responses, *args, **kwargs)
with patch.object(litellm, 'acompletion', chat), patch.object(litellm, 'aresponses', responses):
router = litellm.Router(model_list=deepcopy(routes), fallbacks=[{primary: [backup]}],
num_retries=0, timeout=60)
if protocol == 'chat':
result = await router.acompletion(model=primary, messages=[
{'role': 'user', 'content': 'Reply only OK. Synthetic test.'}], max_tokens=32)
assert result.choices and result.choices[0].message.content
else:
result = await router.aresponses(model=primary, input=[
{'role': 'user', 'content': 'Reply only OK. Synthetic test.'}],
max_output_tokens=32, store=False)
assert result.status == 'completed'
assert calls[0] == 'subscription-rejected' and calls[-1] == 'official-api', calls
print(json.dumps({'protocol': protocol, 'injected_error': expected,
'calls': calls, 'backup': backup, 'passed': True}), flush=True)
if __name__ == '__main__':
asyncio.run(main())