mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-06-11 18:50:59 +08:00
feat: support llm user agent
This commit is contained in:
@@ -79,6 +79,7 @@ class AgentTokensEventsTest(unittest.IsolatedAsyncioTestCase):
|
||||
data.api_key = "sk-agent-token"
|
||||
data.model = "free-model"
|
||||
data.base_url_preset = None
|
||||
data.user_agent = "AgentTokens-UA/1.0"
|
||||
data.selected_provider_id = "provider-1"
|
||||
data.selected_provider_name = "Free Provider"
|
||||
data.source = "AgentTokens"
|
||||
@@ -105,6 +106,7 @@ class AgentTokensEventsTest(unittest.IsolatedAsyncioTestCase):
|
||||
api_key="sk-agent-token",
|
||||
base_url="https://tokens.example.com/v1",
|
||||
base_url_preset=None,
|
||||
user_agent="AgentTokens-UA/1.0",
|
||||
thinking_level=None,
|
||||
)
|
||||
self.assertEqual("provider-1", agent._llm_provider_selection["selected_provider_id"])
|
||||
|
||||
@@ -118,6 +118,10 @@ def _build_fake_openai_modules(chat_openai_cls=_FakeChatOpenAIForPatch):
|
||||
}, base_module
|
||||
|
||||
|
||||
_ORIGINAL_STUBBED_MODULES = {
|
||||
name: sys.modules.get(name)
|
||||
for name in ("app.core.config", "app.log")
|
||||
}
|
||||
sys.modules.pop("app.agent.llm.helper", None)
|
||||
_stub_module(
|
||||
"app.core.config",
|
||||
@@ -127,6 +131,7 @@ _stub_module(
|
||||
LLM_API_KEY="global-key",
|
||||
LLM_BASE_URL="https://global.example.com",
|
||||
LLM_BASE_URL_PRESET=None,
|
||||
LLM_USER_AGENT=None,
|
||||
LLM_THINKING_LEVEL=None,
|
||||
LLM_TEMPERATURE=0.1,
|
||||
LLM_MAX_CONTEXT_TOKENS=64,
|
||||
@@ -140,6 +145,11 @@ spec = importlib.util.spec_from_file_location("test_llm_module", module_path)
|
||||
llm_module = importlib.util.module_from_spec(spec)
|
||||
assert spec and spec.loader
|
||||
spec.loader.exec_module(llm_module)
|
||||
for _module_name, _module in _ORIGINAL_STUBBED_MODULES.items():
|
||||
if _module is None:
|
||||
sys.modules.pop(_module_name, None)
|
||||
else:
|
||||
sys.modules[_module_name] = _module
|
||||
|
||||
|
||||
class LlmHelperTestCallTest(unittest.TestCase):
|
||||
@@ -177,6 +187,7 @@ class LlmHelperTestCallTest(unittest.TestCase):
|
||||
api_key="sk-test",
|
||||
base_url="https://api.deepseek.com",
|
||||
base_url_preset="deepseek-default",
|
||||
user_agent=None,
|
||||
)
|
||||
self.assertEqual(result["provider"], "deepseek")
|
||||
self.assertEqual(result["model"], "deepseek-chat")
|
||||
@@ -436,7 +447,7 @@ class LlmHelperTestCallTest(unittest.TestCase):
|
||||
"model_id": kwargs["model"],
|
||||
"api_key": kwargs["api_key"],
|
||||
"base_url": kwargs["base_url"],
|
||||
"default_headers": None,
|
||||
"default_headers": {"X-Test": "1"},
|
||||
"use_responses_api": None,
|
||||
"model_record": None,
|
||||
"model_metadata": None,
|
||||
@@ -477,6 +488,7 @@ class LlmHelperTestCallTest(unittest.TestCase):
|
||||
"api_key": "updated-key",
|
||||
"base_url": "https://updated.example.com/v1",
|
||||
"base_url_preset_id": "updated-preset",
|
||||
"user_agent": None,
|
||||
},
|
||||
)
|
||||
self.assertEqual(len(llm_calls), 1)
|
||||
@@ -486,6 +498,36 @@ class LlmHelperTestCallTest(unittest.TestCase):
|
||||
llm_calls[0].get("base_url"),
|
||||
"https://updated.example.com/v1",
|
||||
)
|
||||
self.assertEqual(llm_calls[0].get("default_headers"), {"X-Test": "1"})
|
||||
|
||||
def test_get_llm_passes_user_agent_as_openai_default_header(self):
|
||||
calls = []
|
||||
|
||||
class _FakeChatOpenAI:
|
||||
def __init__(self, **kwargs):
|
||||
calls.append(kwargs)
|
||||
self.model = kwargs["model"]
|
||||
self.profile = None
|
||||
|
||||
with patch.dict(
|
||||
sys.modules,
|
||||
{"langchain_openai": SimpleNamespace(ChatOpenAI=_FakeChatOpenAI)},
|
||||
):
|
||||
asyncio.run(
|
||||
llm_module.LLMHelper.get_llm(
|
||||
provider="openai",
|
||||
model="gpt-5-mini",
|
||||
api_key="sk-test",
|
||||
base_url="https://api.example.com/v1",
|
||||
user_agent="MoviePilot-Test/1.0",
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(len(calls), 1)
|
||||
self.assertEqual(
|
||||
calls[0].get("default_headers"),
|
||||
{"User-Agent": "MoviePilot-Test/1.0"},
|
||||
)
|
||||
|
||||
def test_get_llm_keeps_openai_patch_global_without_model_marker(self):
|
||||
class _FakeProviderManager:
|
||||
|
||||
@@ -5,7 +5,12 @@ from types import ModuleType
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
|
||||
_ORIGINAL_STUBBED_MODULES = {}
|
||||
|
||||
|
||||
def _stub_module(name: str, **attrs):
|
||||
if name not in _ORIGINAL_STUBBED_MODULES:
|
||||
_ORIGINAL_STUBBED_MODULES[name] = sys.modules.get(name)
|
||||
module = sys.modules.get(name)
|
||||
if module is None:
|
||||
module = ModuleType(name)
|
||||
@@ -36,7 +41,15 @@ _stub_module("app.helper.sites", SitesHelper=_Dummy)
|
||||
_stub_module("app.chain.mediaserver", MediaServerChain=_Dummy)
|
||||
_stub_module("app.chain.search", SearchChain=_Dummy)
|
||||
_stub_module("app.chain.system", SystemChain=_Dummy)
|
||||
_stub_module("app.core.event", eventmanager=_Dummy())
|
||||
_stub_module(
|
||||
"app.agent.llm",
|
||||
LLMHelper=_Dummy,
|
||||
LLMProviderManager=_Dummy,
|
||||
LLMTestError=_DummyError,
|
||||
LLMTestTimeout=_DummyError,
|
||||
render_auth_result_html=lambda success, message: message,
|
||||
)
|
||||
_stub_module("app.core.event", eventmanager=_Dummy(), Event=_Dummy, EventManager=_Dummy)
|
||||
_stub_module("app.core.metainfo", MetaInfo=_Dummy)
|
||||
_stub_module("app.core.module", ModuleManager=_Dummy)
|
||||
_stub_module(
|
||||
@@ -79,6 +92,12 @@ _stub_module("version", APP_VERSION="test")
|
||||
|
||||
from app.api.endpoints import llm as system_endpoint
|
||||
|
||||
for _module_name, _module in _ORIGINAL_STUBBED_MODULES.items():
|
||||
if _module is None:
|
||||
sys.modules.pop(_module_name, None)
|
||||
else:
|
||||
sys.modules[_module_name] = _module
|
||||
|
||||
|
||||
class LlmTestEndpointTest(unittest.TestCase):
|
||||
def test_llm_test_requires_ai_agent_enabled(self):
|
||||
@@ -126,6 +145,8 @@ class LlmTestEndpointTest(unittest.TestCase):
|
||||
system_endpoint.settings, "LLM_BASE_URL", "https://api.deepseek.com"
|
||||
), patch.object(
|
||||
system_endpoint.settings, "LLM_BASE_URL_PRESET", "deepseek-default"
|
||||
), patch.object(
|
||||
system_endpoint.settings, "LLM_USER_AGENT", "MoviePilot-Test/1.0", create=True
|
||||
), patch.object(
|
||||
system_endpoint.LLMHelper,
|
||||
"test_current_settings",
|
||||
@@ -141,6 +162,7 @@ class LlmTestEndpointTest(unittest.TestCase):
|
||||
api_key="sk-test",
|
||||
base_url="https://api.deepseek.com",
|
||||
base_url_preset="deepseek-default",
|
||||
user_agent="MoviePilot-Test/1.0",
|
||||
)
|
||||
self.assertTrue(resp.success)
|
||||
self.assertEqual(resp.data["provider"], "deepseek")
|
||||
@@ -165,6 +187,7 @@ class LlmTestEndpointTest(unittest.TestCase):
|
||||
api_key="sk-live",
|
||||
base_url="https://example.com/v1",
|
||||
base_url_preset="openai-default",
|
||||
user_agent="MoviePilot-Custom/1.0",
|
||||
)
|
||||
|
||||
with patch.object(system_endpoint.settings, "AI_AGENT_ENABLE", False), patch.object(
|
||||
@@ -188,6 +211,7 @@ class LlmTestEndpointTest(unittest.TestCase):
|
||||
api_key="sk-live",
|
||||
base_url="https://example.com/v1",
|
||||
base_url_preset="openai-default",
|
||||
user_agent="MoviePilot-Custom/1.0",
|
||||
)
|
||||
self.assertTrue(resp.success)
|
||||
self.assertEqual(resp.data["provider"], "openai")
|
||||
@@ -209,6 +233,7 @@ class LlmTestEndpointTest(unittest.TestCase):
|
||||
api_key="sk-live",
|
||||
base_url="https://api.deepseek.com",
|
||||
base_url_preset="deepseek-default",
|
||||
user_agent=None,
|
||||
)
|
||||
|
||||
with patch.object(system_endpoint.settings, "AI_AGENT_ENABLE", False), patch.object(
|
||||
@@ -226,6 +251,7 @@ class LlmTestEndpointTest(unittest.TestCase):
|
||||
api_key="sk-live",
|
||||
base_url="https://api.deepseek.com",
|
||||
base_url_preset="deepseek-default",
|
||||
user_agent=None,
|
||||
)
|
||||
self.assertTrue(resp.success)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user