refactor: expand api and chain config snapshots

This commit is contained in:
jxxghp
2026-08-22 13:22:33 +08:00
parent 7bc5e01afd
commit 996a1a0705
17 changed files with 197 additions and 107 deletions
@@ -9,7 +9,7 @@
"root": "app"
},
"settings_imports": {
"count": 161,
"count": 154,
"files": [
"app/adapters/cache/backends.py",
"app/adapters/cache/redis.py",
@@ -47,17 +47,12 @@
"app/agent/tools/impl/send_voice_message.py",
"app/agent/tools/impl/update_agent_task.py",
"app/agent/tools/impl/update_system_settings.py",
"app/api/endpoints/agent.py",
"app/api/endpoints/anthropic.py",
"app/api/endpoints/media.py",
"app/api/endpoints/message.py",
"app/api/endpoints/openai.py",
"app/api/endpoints/plugin.py",
"app/api/endpoints/storage.py",
"app/api/endpoints/subscribe.py",
"app/api/endpoints/system.py",
"app/api/endpoints/tmdb.py",
"app/api/endpoints/torrent.py",
"app/api/endpoints/transfer.py",
"app/api/servcookie.py",
"app/application/formatting.py",
@@ -75,10 +70,8 @@
"app/chain/message.py",
"app/chain/scraping.py",
"app/chain/search.py",
"app/chain/site.py",
"app/chain/subscribe.py",
"app/chain/system.py",
"app/chain/torrents.py",
"app/chain/transfer.py",
"app/cli.py",
"app/db/base.py",
+9 -10
View File
@@ -13,8 +13,8 @@
"runtime_to_db": [],
"workflow_to_db": []
},
"edge_count": 6369,
"edge_sha256": "62638f6e334e7deb05902ca1e50cdcd03245a85c207dd2b4efaa03306cd28187",
"edge_count": 6368,
"edge_sha256": "03022352d218ecfb93295438f8264140fa7f6be2f7e55734e094989e454062ae",
"edges": [
"app -> app.runtime",
"app -> app.runtime.compat",
@@ -1632,6 +1632,7 @@
"app.api.endpoints.agent -> app.api.principal",
"app.api.endpoints.agent -> app.api.response",
"app.api.endpoints.agent -> app.application",
"app.api.endpoints.agent -> app.application.configuration",
"app.api.endpoints.agent -> app.application.messaging",
"app.api.endpoints.agent -> app.application.messaging.agent",
"app.api.endpoints.agent -> app.application.messaging.chat",
@@ -1677,8 +1678,8 @@
"app.api.endpoints.anthropic -> app.api.openai_utils",
"app.api.endpoints.anthropic -> app.api.presentation",
"app.api.endpoints.anthropic -> app.api.presentation.sse",
"app.api.endpoints.anthropic -> app.runtime",
"app.api.endpoints.anthropic -> app.runtime.config",
"app.api.endpoints.anthropic -> app.application",
"app.api.endpoints.anthropic -> app.application.configuration",
"app.api.endpoints.anthropic -> app.schemas",
"app.api.endpoints.anthropic -> app.schemas.openai",
"app.api.endpoints.auth -> app.api",
@@ -2023,8 +2024,8 @@
"app.api.endpoints.openai -> app.api.openai_utils",
"app.api.endpoints.openai -> app.api.presentation",
"app.api.endpoints.openai -> app.api.presentation.sse",
"app.api.endpoints.openai -> app.runtime",
"app.api.endpoints.openai -> app.runtime.config",
"app.api.endpoints.openai -> app.application",
"app.api.endpoints.openai -> app.application.configuration",
"app.api.endpoints.openai -> app.schemas",
"app.api.endpoints.openai -> app.schemas.openai",
"app.api.endpoints.openai -> app.schemas.types",
@@ -2268,8 +2269,6 @@
"app.api.endpoints.tmdb -> app.application.configuration",
"app.api.endpoints.tmdb -> app.chain",
"app.api.endpoints.tmdb -> app.chain.tmdb",
"app.api.endpoints.tmdb -> app.runtime",
"app.api.endpoints.tmdb -> app.runtime.config",
"app.api.endpoints.tmdb -> app.schemas",
"app.api.endpoints.tmdb -> app.schemas.context",
"app.api.endpoints.tmdb -> app.schemas.response",
@@ -2281,6 +2280,8 @@
"app.api.endpoints.torrent -> app.api.dependencies",
"app.api.endpoints.torrent -> app.api.dependencies.auth",
"app.api.endpoints.torrent -> app.api.response",
"app.api.endpoints.torrent -> app.application",
"app.api.endpoints.torrent -> app.application.configuration",
"app.api.endpoints.torrent -> app.chain",
"app.api.endpoints.torrent -> app.chain.media",
"app.api.endpoints.torrent -> app.chain.torrents",
@@ -2292,8 +2293,6 @@
"app.api.endpoints.torrent -> app.domain.metainfo",
"app.api.endpoints.torrent -> app.foundation",
"app.api.endpoints.torrent -> app.foundation.crypto",
"app.api.endpoints.torrent -> app.runtime",
"app.api.endpoints.torrent -> app.runtime.config",
"app.api.endpoints.torrent -> app.schemas",
"app.api.endpoints.torrent -> app.schemas.cache",
"app.api.endpoints.torrent -> app.schemas.media",
+16
View File
@@ -132,10 +132,19 @@ sys.modules["app.application.site.sites"] = sites
from fastapi.security import HTTPAuthorizationCredentials
from app import schemas
from app.api.endpoints import anthropic, openai
from app.api.endpoints.anthropic import messages as anthropic_messages
from app.api.endpoints.openai import chat_completions, responses
from app.application.configuration import ApiRuntimeConfig
from app.runtime.config import settings
runtime_config = ApiRuntimeConfig(
False, 60, False, settings.AI_AGENT_ENABLE,
api_token=settings.API_TOKEN,
)
anthropic.get_api_runtime_config_snapshot = lambda: runtime_config
openai.get_api_runtime_config_snapshot = lambda: runtime_config
credentials = HTTPAuthorizationCredentials(
scheme="Bearer",
credentials=settings.API_TOKEN,
@@ -367,9 +376,16 @@ sys.modules["app.application.site.sites"] = sites
from fastapi.security import HTTPAuthorizationCredentials
from app import schemas
from app.api.endpoints import anthropic, openai
from app.application.configuration import ApiRuntimeConfig
from app.runtime.config import settings
settings.AI_AGENT_ENABLE = True
runtime_config = ApiRuntimeConfig(
False, 60, False, True,
api_token=settings.API_TOKEN,
)
anthropic.get_api_runtime_config_snapshot = lambda: runtime_config
openai.get_api_runtime_config_snapshot = lambda: runtime_config
credentials = HTTPAuthorizationCredentials(
scheme="Bearer",
credentials=settings.API_TOKEN,
+9 -1
View File
@@ -387,7 +387,15 @@ def test_music_cache_not_evicted_by_video_torrents():
fake_settings.NO_CACHE_SITE_KEY = "no-cache-site.invalid"
with (
patch("app.chain.torrents.settings", fake_settings),
patch.object(
chain,
"runtime_config",
SimpleNamespace(
torrent_cache_size=fake_settings.CONF.torrents,
refresh_batch_size=fake_settings.CONF.refresh,
no_cache_site_key=fake_settings.NO_CACHE_SITE_KEY,
),
),
patch.object(chain, "load_cache", side_effect=_fake_load),
patch.object(chain, "browse", side_effect=_fake_browse),
patch.object(chain, "save_cache", save_cache),
+3 -1
View File
@@ -426,7 +426,9 @@ def test_sunnypt_site_test_uses_profile_api(monkeypatch):
timeout=15,
)
state, message = SiteChain._SiteChain__sunnypt_test(site)
chain = object.__new__(SiteChain)
chain.runtime_config = SimpleNamespace(proxy=None, user_agent="MoviePilot-Test")
state, message = chain._SiteChain__sunnypt_test(site)
assert state
assert message == "连接成功"
+6 -1
View File
@@ -1,6 +1,7 @@
import asyncio
import inspect
import pickle
from types import SimpleNamespace
from unittest.mock import Mock
from app.api.endpoints import tmdb as tmdb_endpoint
@@ -282,7 +283,11 @@ def test_tmdb_cache_endpoint_returns_management_statistics(monkeypatch):
"get_configured_system_config",
lambda: type("SystemConfigStub", (), {"get": get_system_config})(),
)
monkeypatch.setattr(tmdb_endpoint.settings, "MEDIA_RECOGNIZE_SHARE", True)
monkeypatch.setattr(
tmdb_endpoint,
"get_api_runtime_config_snapshot",
lambda: SimpleNamespace(media_recognize_share=True),
)
response = asyncio.run(tmdb_endpoint.tmdb_recognition_cache(None))
+52 -13
View File
@@ -633,7 +633,10 @@ def test_prepare_web_agent_audio_attachment_converts_unsupported_audio(tmp_path)
return SimpleNamespace(returncode=0, stderr="")
run.side_effect = write_converted_file
with patch("app.api.endpoints.agent.settings", SimpleNamespace(TEMP_PATH=tmp_path)):
with patch(
"app.api.endpoints.agent.get_api_runtime_config_snapshot",
return_value=SimpleNamespace(temp_path=tmp_path),
):
output_path = _prepare_web_agent_audio_attachment_path(str(source_path))
assert output_path == converted_path
@@ -684,7 +687,10 @@ def test_web_agent_stream_returns_error_when_voice_transcription_fails():
request = SimpleNamespace()
user = SimpleNamespace(id=1, name="admin")
with patch("app.api.endpoints.agent.settings.AI_AGENT_ENABLE", True), patch(
with patch(
"app.api.endpoints.agent.get_api_runtime_config_snapshot",
return_value=SimpleNamespace(ai_agent_enable=True),
), patch(
"app.api.endpoints.agent._transcribe_web_agent_audio_files",
return_value=None,
) as transcribe_audio:
@@ -722,7 +728,10 @@ def test_web_agent_stream_does_not_block_event_loop_during_transcription():
return await stream_task
try:
with patch("app.api.endpoints.agent.settings.AI_AGENT_ENABLE", True), patch(
with patch(
"app.api.endpoints.agent.get_api_runtime_config_snapshot",
return_value=SimpleNamespace(ai_agent_enable=True),
), patch(
"app.api.endpoints.agent._resolve_web_agent_audio_refs",
return_value=[Mock()],
), patch(
@@ -788,7 +797,10 @@ def test_web_agent_stream_binds_session_to_agent_manager():
return "".join(await _collect_streaming_response(response))
try:
with patch("app.api.endpoints.agent.settings.AI_AGENT_ENABLE", True), patch(
with patch(
"app.api.endpoints.agent.get_api_runtime_config_snapshot",
return_value=SimpleNamespace(ai_agent_enable=True),
), patch(
"app.api.endpoints.agent._get_web_agent_type",
return_value=FakeWebAgent,
):
@@ -884,7 +896,10 @@ def test_web_agent_stream_emits_secret_result_only_as_protected_event():
return "".join(await _collect_streaming_response(response))
try:
with patch("app.api.endpoints.agent.settings.AI_AGENT_ENABLE", True), patch(
with patch(
"app.api.endpoints.agent.get_api_runtime_config_snapshot",
return_value=SimpleNamespace(ai_agent_enable=True),
), patch(
"app.api.endpoints.agent._get_web_agent_type",
return_value=FakeProtectedAgent,
), patch(
@@ -942,7 +957,10 @@ def test_web_agent_cancel_keeps_existing_display_history():
return "".join(await _collect_streaming_response(response))
try:
with patch("app.api.endpoints.agent.settings.AI_AGENT_ENABLE", True), patch.object(
with patch(
"app.api.endpoints.agent.get_api_runtime_config_snapshot",
return_value=SimpleNamespace(ai_agent_enable=True),
), patch.object(
agent_manager,
"matches_secret_confirmation",
return_value=True,
@@ -982,7 +1000,10 @@ def test_web_agent_stream_rejects_confirmation_without_protected_capability():
response = await web_agent_stream(payload, request, user)
return "".join(await _collect_streaming_response(response))
with patch("app.api.endpoints.agent.settings.AI_AGENT_ENABLE", True), patch.object(
with patch(
"app.api.endpoints.agent.get_api_runtime_config_snapshot",
return_value=SimpleNamespace(ai_agent_enable=True),
), patch.object(
agent_manager,
"matches_secret_confirmation",
return_value=True,
@@ -1008,7 +1029,10 @@ def test_web_agent_stream_keeps_confirmation_without_pending_on_normal_path():
body = "".join(await _collect_streaming_response(response))
return response, body
with patch("app.api.endpoints.agent.settings.AI_AGENT_ENABLE", True), patch.object(
with patch(
"app.api.endpoints.agent.get_api_runtime_config_snapshot",
return_value=SimpleNamespace(ai_agent_enable=True),
), patch.object(
agent_manager,
"process_message",
new=AsyncMock(return_value="普通回复"),
@@ -1076,7 +1100,10 @@ def test_web_agent_stream_drops_secret_result_after_disconnect():
return body
try:
with patch("app.api.endpoints.agent.settings.AI_AGENT_ENABLE", True), patch.object(
with patch(
"app.api.endpoints.agent.get_api_runtime_config_snapshot",
return_value=SimpleNamespace(ai_agent_enable=True),
), patch.object(
agent_manager,
"matches_secret_confirmation",
return_value=True,
@@ -1116,7 +1143,10 @@ def test_web_agent_stream_emits_heartbeat_during_idle_tool_wait():
response = await web_agent_stream(payload, request, user)
return "".join(await _collect_streaming_response(response))
with patch("app.api.endpoints.agent.settings.AI_AGENT_ENABLE", True), patch(
with patch(
"app.api.endpoints.agent.get_api_runtime_config_snapshot",
return_value=SimpleNamespace(ai_agent_enable=True),
), patch(
"app.api.endpoints.agent.WEB_AGENT_STREAM_HEARTBEAT_SECONDS",
0.01,
), patch(
@@ -1188,7 +1218,10 @@ def test_web_agent_stop_finishes_stream_without_error():
return "".join(received)
try:
with patch("app.api.endpoints.agent.settings.AI_AGENT_ENABLE", True), patch(
with patch(
"app.api.endpoints.agent.get_api_runtime_config_snapshot",
return_value=SimpleNamespace(ai_agent_enable=True),
), patch(
"app.api.endpoints.agent._is_web_agent_traditional_message",
return_value=False,
), patch(
@@ -1230,7 +1263,10 @@ def test_web_agent_stream_rechecks_running_service_before_enqueue():
response = await web_agent_stream(payload, request, user)
return "".join(await _collect_streaming_response(response))
with patch("app.api.endpoints.agent.settings.AI_AGENT_ENABLE", True), patch(
with patch(
"app.api.endpoints.agent.get_api_runtime_config_snapshot",
return_value=SimpleNamespace(ai_agent_enable=True),
), patch(
"app.api.endpoints.agent._is_web_agent_traditional_message",
return_value=False,
), patch(
@@ -1366,7 +1402,10 @@ def test_web_agent_stream_sends_done_before_snapshot_persistence_finishes():
return "".join(received)
try:
with patch("app.api.endpoints.agent.settings.AI_AGENT_ENABLE", True), patch(
with patch(
"app.api.endpoints.agent.get_api_runtime_config_snapshot",
return_value=SimpleNamespace(ai_agent_enable=True),
), patch(
"app.api.endpoints.agent._is_web_agent_traditional_message",
return_value=False,
), patch(