mirror of
https://github.com/JefferyHcool/BiliNote.git
synced 2026-07-13 00:21:37 +08:00
问题1 — DeepSeek 模型下拉为空:
provider 的 /models 调用失败时,get_model_list 吞掉异常返回 [],
get_all_models_by_id 再对 [] 取 .data 触发 AttributeError 又被吞,
最终接口返回 {"code":0,"msg":"success","data":[]},把失败伪装成空成功,
用户看不到任何原因。
- 捕获并回传真实错误,不再二次吞
- 新增 model_fallback.normalize_models 兼容 SyncPage/list/dict,绝不再 .data 崩
- 内置供应商提供已知模型兜底清单(DeepSeek→deepseek-chat/deepseek-reasoner,
Qwen→qwen-plus 等),动态拿不到时回退,保证下拉非空;动态可用时仍以动态为准
问题2 — whisper 模型下载不走代理(Docker 里代理没生效):
snapshot_download 既不读 UI 配的代理,HF_ENDPOINT 又固定 hf-mirror.com。
- ProxyConfigManager.apply_to_env() 把生效代理 export 到 HTTP(S)_PROXY/ALL_PROXY,
huggingface_hub 即可复用;在每次下载前与启动时应用(覆盖转写按需下载)
- 网络类下载报错翻译成可操作提示(配代理 / 改 HF_ENDPOINT / 检查容器外网)
- .env.example 补充 HF_ENDPOINT 与代理覆盖说明
新增 tests/test_model_fallback.py、tests/test_proxy_apply_env.py(14 用例)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
90 lines
3.2 KiB
Python
90 lines
3.2 KiB
Python
import importlib.util
|
||
import pathlib
|
||
import unittest
|
||
|
||
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
||
MODULE_PATH = ROOT / "app" / "services" / "model_fallback.py"
|
||
spec = importlib.util.spec_from_file_location("model_fallback", MODULE_PATH)
|
||
if spec is None or spec.loader is None:
|
||
raise ImportError("model_fallback module spec not found")
|
||
model_fallback = importlib.util.module_from_spec(spec)
|
||
spec.loader.exec_module(model_fallback)
|
||
|
||
builtin_fallback_models = model_fallback.builtin_fallback_models
|
||
normalize_models = model_fallback.normalize_models
|
||
as_model_dicts = model_fallback.as_model_dicts
|
||
|
||
|
||
class FakeModel:
|
||
"""模拟 openai SDK 的 Model(pydantic)对象。"""
|
||
|
||
def __init__(self, mid, created=None, owned_by="x"):
|
||
self.id = mid
|
||
self.created = created
|
||
self.object = "model"
|
||
self.owned_by = owned_by
|
||
|
||
def dict(self):
|
||
return {"id": self.id, "created": self.created, "object": self.object, "owned_by": self.owned_by}
|
||
|
||
|
||
class FakeSyncPage:
|
||
def __init__(self, data):
|
||
self.data = data
|
||
|
||
|
||
class TestBuiltinFallbackModels(unittest.TestCase):
|
||
def test_deepseek_by_id_has_fallback(self):
|
||
# issue #417:DeepSeek /models 不稳定,必须有兜底清单
|
||
models = builtin_fallback_models({"id": "deepseek", "name": "DeepSeek"})
|
||
self.assertIn("deepseek-chat", models)
|
||
self.assertIn("deepseek-reasoner", models)
|
||
|
||
def test_match_by_name_case_insensitive(self):
|
||
models = builtin_fallback_models({"id": "whatever-uuid", "name": "deepseek"})
|
||
self.assertIn("deepseek-chat", models)
|
||
|
||
def test_unknown_provider_returns_empty(self):
|
||
self.assertEqual(builtin_fallback_models({"id": "nope", "name": "nope"}), [])
|
||
|
||
def test_none_provider_returns_empty(self):
|
||
self.assertEqual(builtin_fallback_models(None), [])
|
||
|
||
|
||
class TestNormalizeModels(unittest.TestCase):
|
||
def test_syncpage_with_models(self):
|
||
page = FakeSyncPage([FakeModel("deepseek-chat"), FakeModel("deepseek-reasoner")])
|
||
out = normalize_models(page)
|
||
self.assertEqual([m["id"] for m in out], ["deepseek-chat", "deepseek-reasoner"])
|
||
|
||
def test_plain_list_of_models(self):
|
||
out = normalize_models([FakeModel("a"), FakeModel("b")])
|
||
self.assertEqual([m["id"] for m in out], ["a", "b"])
|
||
|
||
def test_empty_list_does_not_raise(self):
|
||
# 关键回归:旧代码对 [] 取 .data 会 AttributeError
|
||
self.assertEqual(normalize_models([]), [])
|
||
|
||
def test_list_of_dicts(self):
|
||
out = normalize_models([{"id": "x", "object": "model"}])
|
||
self.assertEqual(out[0]["id"], "x")
|
||
|
||
def test_drops_entries_without_id(self):
|
||
out = normalize_models([{"object": "model"}, {"id": "ok"}])
|
||
self.assertEqual([m["id"] for m in out], ["ok"])
|
||
|
||
def test_none_returns_empty(self):
|
||
self.assertEqual(normalize_models(None), [])
|
||
|
||
|
||
class TestAsModelDicts(unittest.TestCase):
|
||
def test_shape_matches_sdk(self):
|
||
out = as_model_dicts(["deepseek-chat"], owned_by="DeepSeek")
|
||
self.assertEqual(out[0]["id"], "deepseek-chat")
|
||
self.assertEqual(out[0]["object"], "model")
|
||
self.assertEqual(out[0]["owned_by"], "DeepSeek")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
unittest.main()
|