From 75ee313f4c9fea331afe0cf44abd71034487efbe Mon Sep 17 00:00:00 2001 From: aiapienthusiast <327629069+aiapienthusiast@users.noreply.github.com> Date: Mon, 19 Oct 2026 19:02:38 +0400 Subject: [PATCH] feat(llm): add Cheaper Inference provider (#7088) --- app/agent/llm/catalog.py | 8 +++ tests/test_llm_provider_cheaperinference.py | 60 +++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 tests/test_llm_provider_cheaperinference.py diff --git a/app/agent/llm/catalog.py b/app/agent/llm/catalog.py index 71976a32c..73c095b96 100644 --- a/app/agent/llm/catalog.py +++ b/app/agent/llm/catalog.py @@ -407,6 +407,14 @@ class _ProviderCatalog: api_key_hint="填写 API Route API Key,可在 https://api-route.com 获取。", description="API Route OpenAI-compatible 聚合模型平台,支持刷新账户可用模型目录。", ), + openai_provider( + provider_id="cheaperinference", + name="Cheaper Inference", + default_base_url="https://api.cheaperinference.com/v1", + sort_order=57, + api_key_hint="填写 Cheaper Inference API Key,可在 https://cheaperinference.com/signup 获取。", + description="Cheaper Inference OpenAI-compatible 聚合模型平台,一个 Key 覆盖多家厂商的模型。", + ), ProviderSpec( id="github-copilot", name="GitHub Copilot", diff --git a/tests/test_llm_provider_cheaperinference.py b/tests/test_llm_provider_cheaperinference.py new file mode 100644 index 000000000..c2278a62a --- /dev/null +++ b/tests/test_llm_provider_cheaperinference.py @@ -0,0 +1,60 @@ +"""Cheaper Inference 预设通过共享 OpenAI 兼容通道发现模型的边界回归。""" + +from unittest.mock import AsyncMock + +import httpx +import pytest + +from app.agent.llm.provider import LLMProviderManager +from app.foundation.singleton import Singleton + + +@pytest.fixture +def manager(monkeypatch): + """隔离管理器单例及目录缓存,防止测试影响其他 provider。""" + key = (LLMProviderManager, (), frozenset()) + monkeypatch.setattr(Singleton, "_instances", {k: v for k, v in Singleton._instances.items() if k != key}) + instance = LLMProviderManager() + monkeypatch.setattr(instance, "get_models_dev_data", AsyncMock(return_value={})) + return instance + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "base_url, expected_url", + [ + (None, "https://api.cheaperinference.com/v1/models"), + ("https://relay.example/v1", "https://relay.example/v1/models"), + ], +) +async def test_cheaperinference_discovers_authenticated_models(manager, monkeypatch, base_url, expected_url): + """预设及覆盖地址都传递密钥,保留完整模型 ID,不推断缺失的上下文元数据。""" + requests = [] + + def respond(request): + requests.append(request) + return httpx.Response( + 200, + json={ + "object": "list", + "data": [ + {"id": "gpt-5.4-mini", "object": "model", "created": 0, "owned_by": "cheaperinference"}, + {"id": "other/model", "object": "model", "created": 0, "owned_by": "cheaperinference"}, + ], + }, + ) + + monkeypatch.setattr( + manager, + "_build_sdk_http_client", + lambda use_proxy: httpx.AsyncClient(transport=httpx.MockTransport(respond)), + ) + + models = await manager.list_models("cheaperinference", api_key="test-cheaperinference-key", base_url=base_url) + + assert len(requests) == 1 + assert requests[0].method == "GET" + assert str(requests[0].url) == expected_url + assert requests[0].headers["Authorization"] == "Bearer test-cheaperinference-key" + assert [model["id"] for model in models] == ["gpt-5.4-mini", "other/model"] + assert all(model["source"] == "provider" and model["context_tokens"] is None for model in models)