补充 OpenAI 主备验收与可复用双协议测试
This commit is contained in:
@@ -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())
|
||||
Reference in New Issue
Block a user