diff --git a/app/application/classification/enrichment.py b/app/application/classification/enrichment.py index c021694d2..92fe241a6 100644 --- a/app/application/classification/enrichment.py +++ b/app/application/classification/enrichment.py @@ -15,16 +15,13 @@ from typing import Any, Protocol, TypeAlias, cast from pydantic import ValidationError -from app.domain.classification.evaluator import read_fact from app.domain.classification.conditions import condition_field_ids +from app.domain.classification.evaluator import read_fact from app.domain.classification.fields import ( classification_fact_matches_definition, field_definition_map, ) from app.schemas.category import ( - ClassificationCondition, - ClassificationConditionGroup, - ClassificationConditionNode, ClassificationEnrichmentRequest, ClassificationEnrichmentResponse, ClassificationFacts, diff --git a/app/application/classification/migration.py b/app/application/classification/migration.py index 005fc680e..868d67f72 100644 --- a/app/application/classification/migration.py +++ b/app/application/classification/migration.py @@ -16,13 +16,13 @@ from app.application.classification.compiler import ( _LegacyToken, _parse_legacy_tokens, ) +from app.domain.classification.conditions import condition_field_ids from app.domain.classification.vocabulary import ( COUNTRY_CODE_ALIASES as _COUNTRY_CODE_ALIASES, ) from app.domain.classification.vocabulary import ( TMDB_GENRE_KEYS as _TMDB_GENRE_KEYS, ) -from app.domain.classification.conditions import condition_field_ids from app.schemas.category import ( CategoryConfig, CategoryRule, diff --git a/app/application/history/__init__.py b/app/application/history/__init__.py index fc846ed85..372de1262 100644 --- a/app/application/history/__init__.py +++ b/app/application/history/__init__.py @@ -1,10 +1,9 @@ from __future__ import annotations from dataclasses import asdict, dataclass -from typing import Any, Callable, Dict, Optional, Protocol, Union +from typing import Any, Optional, Protocol, Union from app.application.history.contracts import ( - _freeze_json, TransferHistoryMonthlyStatistics, TransferHistoryQueryPort, TransferHistoryReplacePort, @@ -14,6 +13,7 @@ from app.application.history.contracts import ( TransferHistoryStatisticSnapshot, TransferHistoryWrite, TransferHistoryWritePort, + _freeze_json, configure_transfer_history_repository, get_transfer_history_repository, reset_transfer_history_repository, @@ -53,7 +53,6 @@ from app.schemas.history import ( from app.schemas.transfer import TransferInfo from app.schemas.types import MediaSource - __all__ = [ "DownloadFileMutationRepository", "DownloadFileSnapshot", diff --git a/app/application/history/contracts.py b/app/application/history/contracts.py index 3b725f62e..0ac5ef0b2 100644 --- a/app/application/history/contracts.py +++ b/app/application/history/contracts.py @@ -3,11 +3,12 @@ from __future__ import annotations from dataclasses import asdict, dataclass -from typing import Any, Callable, Dict, NoReturn, Optional, Protocol +from typing import Any, Callable, NoReturn, Optional, Protocol from app.schemas.common import JsonData from app.schemas.types import MediaSource + class _FrozenJsonDict(dict[str, JsonData]): """保留 JSON 字典读取与序列化行为,并拒绝常规原地修改。""" diff --git a/app/application/history/retry.py b/app/application/history/retry.py index 3d1853ca1..55c16d697 100644 --- a/app/application/history/retry.py +++ b/app/application/history/retry.py @@ -13,7 +13,6 @@ from app.application.history.contracts import ( from app.runtime.cache import TTLCache from app.runtime.log import logger - # 失败重试次数的合法区间,避免瞬时故障永久漏件或永久失败反复刷通知。 MIN_FAILED_RETRIES = 1 MAX_FAILED_RETRIES = 10 diff --git a/app/application/messaging/interaction/agent.py b/app/application/messaging/interaction/agent.py index 253c5972e..033ec80b3 100644 --- a/app/application/messaging/interaction/agent.py +++ b/app/application/messaging/interaction/agent.py @@ -1,10 +1,10 @@ """Agent 按钮选择交互契约与进程内待处理请求管理。""" +import uuid from dataclasses import dataclass, field from datetime import datetime, timedelta from threading import Lock from typing import Any, Optional -import uuid # Agent 选择按钮回调前缀,新旧两种格式都必须继续兼容。 AGENT_CHOICE_PREFIX = "agent_interaction:choice:" diff --git a/app/application/messaging/skill.py b/app/application/messaging/skill.py index fd0e642bc..af32dcb58 100644 --- a/app/application/messaging/skill.py +++ b/app/application/messaging/skill.py @@ -2,11 +2,11 @@ import re import uuid from dataclasses import dataclass, field from datetime import datetime -from typing import Any, Callable, Dict, List, Optional, Protocol, Tuple, Union +from typing import Any, Callable, List, Optional, Protocol, Tuple, Union from app.application.messaging.interaction import ( - _ExpiringUserInteractionStore, MessageGateway, + _ExpiringUserInteractionStore, build_navigation_buttons, page_items, supports_interaction_buttons, diff --git a/app/application/server/share.py b/app/application/server/share.py index 6329c3d9f..fc4a4eee5 100644 --- a/app/application/server/share.py +++ b/app/application/server/share.py @@ -7,8 +7,8 @@ from collections.abc import Awaitable, Callable from dataclasses import asdict, dataclass from typing import Any, Optional -from app.application.workflow import WorkflowSnapshot from app.application.server.payload import build_subscribe_payload +from app.application.workflow import WorkflowSnapshot @dataclass(frozen=True, slots=True) diff --git a/app/application/transfer/jobs.py b/app/application/transfer/jobs.py index 5ed8998be..9b855f224 100644 --- a/app/application/transfer/jobs.py +++ b/app/application/transfer/jobs.py @@ -2,15 +2,17 @@ from __future__ import annotations -import threading import sys +import threading from copy import deepcopy from pathlib import Path from time import monotonic as _system_monotonic -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Protocol, Tuple, Union, cast +from typing import TYPE_CHECKING, Dict, List, Optional, Protocol, Tuple, Union, cast from app.application.transfer.projection import ( domain_to_dict as _domain_to_dict, +) +from app.application.transfer.projection import ( transfer_task_meta as _transfer_task_meta, ) from app.domain.context import MediaInfo, MusicInfo diff --git a/app/application/transfer/models.py b/app/application/transfer/models.py index 2b0428e0a..c0bd83b1f 100644 --- a/app/application/transfer/models.py +++ b/app/application/transfer/models.py @@ -6,7 +6,7 @@ import hashlib from copy import deepcopy from dataclasses import dataclass, field from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Protocol, TypeAlias, Union +from typing import TYPE_CHECKING, Any, Callable, List, Optional, Protocol, TypeAlias, Union from pydantic import BaseModel, ConfigDict, PrivateAttr @@ -15,11 +15,9 @@ from app.application.transfer import checkpoint as checkpoint_codec from app.application.transfer.execution import TransferExecutionCheckpoint from app.application.transfer.projection import ( domain_to_dict as _domain_to_dict, - transfer_task_meta as _transfer_task_meta, ) from app.domain.context import MediaInfo, MusicInfo from app.domain.meta.metabase import MetaBase -from app.domain.meta.metamusic import MetaMusic from app.schemas.file import FileItem from app.schemas.media import OptionalMediaIdentityMixin from app.schemas.system import TransferDirectoryConf diff --git a/app/application/transfer/workflow.py b/app/application/transfer/workflow.py index 45542204d..ec6cf29c8 100644 --- a/app/application/transfer/workflow.py +++ b/app/application/transfer/workflow.py @@ -13,13 +13,12 @@ app.schemas -> app.schemas.transfer -> app.domain.* -> app.schemas.types -> app. TransferJob / TransferJobTask,那两个用 app.schemas 的同名 DTO——一个是工作项,一个是 视图,分开表达之后两边都不必再迁就对方。 """ -from time import monotonic from typing import ( Callable, List, - Optional, ) +from app.application.transfer.feedback import TransferFailureNotification from app.application.transfer.jobs import ( DirectorySize, JobManager, @@ -53,15 +52,9 @@ from app.application.transfer.notifications import ( TransferFailureNotificationAggregator, build_transfer_failure_group_key, ) -from app.application.transfer.feedback import TransferFailureNotification -from app.application.transfer.projection import ( - domain_to_dict as _domain_to_dict, - transfer_task_meta as _transfer_task_meta, -) from app.schemas.file import FileItem from app.schemas.transfer import TransferJob - __all__ = [ "DirectorySize", "JobManager", diff --git a/app/chain/message.py b/app/chain/message.py index ac4929761..4ba7989c4 100644 --- a/app/chain/message.py +++ b/app/chain/message.py @@ -19,8 +19,8 @@ from app.application.agent import ( transcribe_audio, ) from app.application.messaging import router as interaction_router -from app.application.messaging.interaction.agent import agent_interaction_manager, parse_agent_choice_callback from app.application.messaging.interaction import InteractionContext, InteractionDispatch +from app.application.messaging.interaction.agent import agent_interaction_manager, parse_agent_choice_callback from app.application.messaging.media import media_interaction_manager from app.application.messaging.plugin import PluginInputInteractionHandler from app.application.messaging.session import MessageSessionService diff --git a/app/startup/initializers/modules.py b/app/startup/initializers/modules.py index 5face7633..22ac5251f 100644 --- a/app/startup/initializers/modules.py +++ b/app/startup/initializers/modules.py @@ -26,7 +26,6 @@ from app.application.messaging.agent import ( shutdown_web_agent_background_tasks, wait_web_agent_background_tasks, ) -from app.application.messaging.webagent.events import dispatch_web_agent_message_event from app.application.messaging.chat import ( get_configured_agent_chat_persistence, ) @@ -34,6 +33,7 @@ from app.application.messaging.message import ( MessageHelper, stop_message, ) +from app.application.messaging.webagent.events import dispatch_web_agent_message_event from app.application.module import configure_module_runtime, reset_module_runtime from app.application.outbox import configure_outbox_dispatcher from app.application.plugin.runtime import get_existing_plugin_manager diff --git a/docs/architecture-overview.md b/docs/architecture-overview.md index 43f71da06..376070685 100644 --- a/docs/architecture-overview.md +++ b/docs/architecture-overview.md @@ -761,7 +761,7 @@ flowchart LR | 指标 | 当前值 | |---|---:| | Python 模块 | 1025 | -| 内部导入边 | 8,829 | +| 内部导入边 | 8,827 | | 非平凡 SCC | 1(精确 containment 的 TMDB 移植包环) | | Application / Chain 具体 Adapter 直连 | 0 / 0 | | Direct egress | 53(债务已清零,53 条精确 containment) | diff --git a/docs/refactor/optimization-checklist.md b/docs/refactor/optimization-checklist.md index 3454672d3..79a61d80d 100644 --- a/docs/refactor/optimization-checklist.md +++ b/docs/refactor/optimization-checklist.md @@ -94,7 +94,7 @@ ARCH-201 至 ARCH-204 均达到实现、验证、提交、推送和远端门禁 | 指标 | 当前值 | 解释 | |---|---:|---| -| 宿主 Python 模块 / 内部依赖边 | 1039 / 8,829 | `dependency-baseline.json` 当前快照;系统设置合同按 `app.application.settings` 包组织,系统设置 API 端点独立按 `app.api.endpoints.settings` 归档,分类、下载资源归类、订阅搜索、整理恢复、Agent 计划、工具视觉、终端生命周期与终端作用域模块的受控依赖 | +| 宿主 Python 模块 / 内部依赖边 | 1039 / 8,827 | `dependency-baseline.json` 当前快照;系统设置合同按 `app.application.settings` 包组织,系统设置 API 端点独立按 `app.api.endpoints.settings` 归档,分类、下载资源归类、订阅搜索、整理恢复、Agent 计划、工具视觉、终端生命周期与终端作用域模块的受控依赖 | | 非平凡 SCC | 1 | 仅保留精确 containment 的 29 模块 TMDB 移植包环 | | 跨层 DB 边界债务 | 0 | Application、Chain、API、Agent、Runtime、Workflow 到 DB 的受控债务均为零 | | Model/Oper 事务债务 | 0 | 自建 Session、自动事务装饰器、直接 commit/rollback 等基线均为零 | diff --git a/tests/fixtures/architecture/dependency-baseline.json b/tests/fixtures/architecture/dependency-baseline.json index c04e8b0e7..8acc49588 100644 --- a/tests/fixtures/architecture/dependency-baseline.json +++ b/tests/fixtures/architecture/dependency-baseline.json @@ -1077,8 +1077,8 @@ "runtime_only": true } }, - "edge_count": 8829, - "edge_sha256": "1c9a0e908924988746ee43009c931bd43c3df419ce4de07f663d564626d89f60", + "edge_count": 8827, + "edge_sha256": "793daf5a91297680e70a665611d7184f16bb61391bca7fca83ffc7d65ddc8c21", "edges": [ "app -> app.foundation", "app -> app.foundation.environment", @@ -4145,7 +4145,6 @@ "app.application.transfer.models -> app.domain.context", "app.application.transfer.models -> app.domain.meta", "app.application.transfer.models -> app.domain.meta.metabase", - "app.application.transfer.models -> app.domain.meta.metamusic", "app.application.transfer.models -> app.schemas", "app.application.transfer.models -> app.schemas.file", "app.application.transfer.models -> app.schemas.media", @@ -4172,7 +4171,6 @@ "app.application.transfer.workflow -> app.application.transfer.jobs", "app.application.transfer.workflow -> app.application.transfer.models", "app.application.transfer.workflow -> app.application.transfer.notifications", - "app.application.transfer.workflow -> app.application.transfer.projection", "app.application.transfer.workflow -> app.schemas", "app.application.transfer.workflow -> app.schemas.file", "app.application.transfer.workflow -> app.schemas.transfer", diff --git a/tests/test_agent_interaction.py b/tests/test_agent_interaction.py index 5dae8c206..cb66918a4 100644 --- a/tests/test_agent_interaction.py +++ b/tests/test_agent_interaction.py @@ -11,11 +11,11 @@ from app.agent.tools.impl.ask_user_choice import ( UserChoiceOptionInput, ) from app.agent.tools.impl.send_message import SendMessageTool +from app.application.messaging.interaction import InteractionContext from app.application.messaging.interaction.agent import ( AgentInteractionOption, agent_interaction_manager, ) -from app.application.messaging.interaction import InteractionContext from app.chain.message import MessageChain from app.runtime.config import settings from app.runtime.loop import main_loop_registry diff --git a/tests/test_application_messaging_agent_architecture.py b/tests/test_application_messaging_agent_architecture.py index 670766baf..3a49e8247 100644 --- a/tests/test_application_messaging_agent_architecture.py +++ b/tests/test_application_messaging_agent_architecture.py @@ -35,9 +35,7 @@ def test_agent_interaction_has_one_canonical_owner() -> None: "parse_agent_choice_callback", } facade = importlib.import_module("app.application.messaging.agent") - interaction = importlib.import_module( - "app.application.messaging.interaction.agent" - ) + importlib.import_module("app.application.messaging.interaction.agent") assert names <= _top_level_owners(AGENT_INTERACTION_PATH) assert names.isdisjoint(_top_level_owners(AGENT_FACADE_PATH)) @@ -52,9 +50,7 @@ def test_channel_admin_has_one_canonical_owner() -> None: "resolve_config_principal_ids", } facade = importlib.import_module("app.application.messaging.agent") - channel_admin = importlib.import_module( - "app.application.messaging.channel.admin" - ) + importlib.import_module("app.application.messaging.channel.admin") assert names <= _top_level_owners(CHANNEL_ADMIN_PATH) assert names.isdisjoint(_top_level_owners(AGENT_FACADE_PATH)) @@ -77,9 +73,7 @@ def test_web_agent_event_bridge_has_one_canonical_owner() -> None: "normalize_web_agent_button_rows", } facade = importlib.import_module("app.application.messaging.agent") - events = importlib.import_module( - "app.application.messaging.webagent.events" - ) + importlib.import_module("app.application.messaging.webagent.events") assert names <= _top_level_owners(WEB_AGENT_EVENTS_PATH) assert names.isdisjoint(_top_level_owners(AGENT_FACADE_PATH)) diff --git a/tests/test_application_shared_utilities.py b/tests/test_application_shared_utilities.py index da749d7fc..c27a433bc 100644 --- a/tests/test_application_shared_utilities.py +++ b/tests/test_application_shared_utilities.py @@ -10,7 +10,6 @@ from app.application.server.payload import build_subscribe_payload from app.domain.classification.conditions import condition_field_ids from app.schemas.category import ClassificationCondition, ClassificationConditionGroup - PROJECT_ROOT = Path(__file__).parents[1] diff --git a/tests/test_application_transfer_architecture.py b/tests/test_application_transfer_architecture.py index 0b476b71a..9f96a5684 100644 --- a/tests/test_application_transfer_architecture.py +++ b/tests/test_application_transfer_architecture.py @@ -4,7 +4,6 @@ import ast import importlib from pathlib import Path - PROJECT_ROOT = Path(__file__).parents[1] TRANSFER_ROOT = PROJECT_ROOT / "app" / "application" / "transfer" WORKFLOW_PATH = TRANSFER_ROOT / "workflow.py" diff --git a/tests/test_transfer_history_gate.py b/tests/test_transfer_history_gate.py index 03b0f72c2..72532cb36 100644 --- a/tests/test_transfer_history_gate.py +++ b/tests/test_transfer_history_gate.py @@ -8,6 +8,7 @@ from types import SimpleNamespace import pytest +from app.application.history import retry as transfer_history_helper from app.application.history.retry import ( HistoryGateAction, clear_transfer_failures, @@ -22,7 +23,6 @@ from app.application.history.retry import ( record_transfer_failure, resolve_history, ) -from app.application.history import retry as transfer_history_helper from app.runtime.config import settings diff --git a/tests/test_web_agent_stream.py b/tests/test_web_agent_stream.py index c4d2d5aa4..8d1537694 100644 --- a/tests/test_web_agent_stream.py +++ b/tests/test_web_agent_stream.py @@ -24,17 +24,6 @@ from app.application.messaging.agent import ( reset_web_agent_message_runtime, wait_web_agent_background_tasks, ) -from app.application.messaging.interaction.agent import ( - AgentInteractionOption, - agent_interaction_manager, -) -from app.application.messaging.webagent.events import ( - attach_web_agent_message_queue, - build_web_agent_message_update_event, - detach_web_agent_message_queue, - dispatch_web_agent_message_event, - extract_web_agent_message_from_event_data, -) from app.application.messaging.agent import ( WebAgentEventPublisher as _WebAgentEventPublisher, ) @@ -84,7 +73,18 @@ from app.application.messaging.agent import ( transcribe_web_agent_audio_files as _transcribe_web_agent_audio_files, ) from app.application.messaging.chat import AgentChatService, configure_agent_chat_service +from app.application.messaging.interaction.agent import ( + AgentInteractionOption, + agent_interaction_manager, +) from app.application.messaging.skill import skill_interaction_manager +from app.application.messaging.webagent.events import ( + attach_web_agent_message_queue, + build_web_agent_message_update_event, + detach_web_agent_message_queue, + dispatch_web_agent_message_event, + extract_web_agent_message_from_event_data, +) from app.application.messaging.webagent.stream import ( _build_steering_ack_stream, _build_web_agent_output_callback,