refactor backend module architecture

This commit is contained in:
jxxghp
2026-08-14 15:45:38 +08:00
parent 557cc0e2e3
commit 7b3444c366
716 changed files with 10378 additions and 7709 deletions
+1 -4
View File
@@ -10,10 +10,7 @@ rust/**/target/
nginx/
test.py
safety_report.txt
app/helper/sites.py
app/helper/*.so
app/helper/*.pyd
app/helper/*.bin
app/infrastructure/*.bin
app/plugins/**
!app/plugins/__init__.py
config/cookies/
+2 -2
View File
@@ -73,5 +73,5 @@ ignore-imports=yes
[TYPECHECK]
# 生成缺失成员提示的类列表
generated-members=requests.packages.urllib3
# app.helper.sites 会主动隐藏模块属性枚举,避免误报 no-name-in-module
ignored-modules=app.helper.sites
# app.infrastructure.sites 会主动隐藏模块属性枚举,接口由同目录 sites.pyi 声明
ignored-modules=app.infrastructure.sites
+57 -1
View File
@@ -50,6 +50,61 @@ For work that changes or reviews repository behavior, identify the domains actua
---
## Canonical Package Ownership
The historical `app/core`, `app/helper`, and `app/utils` directories are compatibility-only virtual import roots. Never add physical Python source there and never use those imports from host code. Choose an owner by responsibility, not by whether a function is "shared" or has historically been called a helper.
| Package | Owns | Must Not Own | Representative Files |
|---|---|---|---|
| `app/foundation/` | Generic mechanisms that work without MoviePilot business state: HTTP transport, reflection/module loading, crypto, URL/object primitives, and structures | `settings`, DB/SystemConfig, MoviePilot business rules, concrete products, legacy import paths | `http.py`, `module.py`, `url.py`, `structures.py` |
| `app/domain/` | Pure MoviePilot business semantics and models for media, recognition, sites, and torrents | Persistence, global settings reads, network/filesystem clients, Rust imports, service discovery, process lifecycle | `context.py`, `media.py`, `metainfo.py`, `scraper.py`, `meta/` |
| `app/platform/` | Process-wide runtime contracts and policy: configuration, events, complete logging runtime, cache contracts/in-memory behavior, concurrency, scheduling, rate limiting, localization, GC, restart state | Named third-party integrations, DB-backed business services, Redis/file cache adapter implementations | `config.py`, `events.py`, `log.py`, `cache.py`, `thread.py`, `gc.py`, `runtime.py` |
| `app/infrastructure/` | Configured technical adapters to Redis, files and standard streams, OS/process facilities, browser/display, DNS, RSS transport, package/resource installation, Rust, generated site resources | Media/site/torrent business decisions, plugin lifecycle, named ecosystem workflows, process restart policy after an adapter action | `cache.py`, `stdio.py`, `redis.py`, `resource.py`, `rss.py`, `rust.py`, `system.py` |
| `app/integrations/` | Concrete external products and ecosystems, including plugin markets, MoviePilot remote service, CookieCloud, OCR, and IP-location providers | Generic HTTP/DNS/filesystem primitives or reusable domain semantics | `market.py`, `server.py`, `cookiecloud.py`, `ocr.py`, `location.py` |
| `app/extensions/` | Discovery, loading, registration, and lifecycle of runtime modules, plugins, and configured service implementations | Generic module import mechanics, plugin-facing compatibility APIs, unrelated business workflows | `module_manager.py`, `plugin_manager.py`, `service_registry.py` |
| `app/messaging/` | Message rendering/routing, interactions, and Agent-to-message bridging | Authentication policy, generic HTTP clients, configured service discovery, endpoint-only Web Push behavior | `message.py`, `interaction.py`, `agent.py` |
| `app/security/` | Authentication, authorization, cookies, passkeys, OTP/two-factor, path/URL safety, SSRF and signing policy | Generic URL parsing, process runtime policy, ordinary business validation | `access.py`, `auth.py`, `cookie.py`, `passkey.py`, `otp.py`, `twofactor.py`, `url.py` |
| `app/services/` | Focused application services and service-bound rules, including persisted configuration, configured capability discovery, notification selection, and media-server normalization/matching | Multi-domain use-case orchestration, generic primitives, concrete product protocols | `recognition.py`, `filter.py`, `notification.py`, `mediaserver.py`, `history.py`, `image.py`, `torrent.py` |
| `app/chain/` | Reusable use-case orchestration across modules, services, Oper classes, events, and caches | Transport schemas, backend-specific protocol details, generic primitives | `media.py`, `download.py`, `subscribe.py`, `transfer.py` |
| `app/startup/` | Composition root: inject providers/adapters, order initialization and shutdown, decide restart/lifecycle policy | Reusable business rules or adapter implementation details | `lifecycle.py`, `domain_initializer.py`, `cache_initializer.py`, `modules_initializer.py` |
| `app/sdk/` | Deliberately curated stable imports for new plugins | Canonical implementation logic or host-internal dependencies | `cache.py`, `logging.py`, `media.py`, `network.py`, `services.py` |
| `app/compat/` | Standard-library-only exact legacy import routing and DEBUG diagnostics | Business implementation, wildcard alias guessing, eager canonical imports | `manifest.py`, `imports.py`, `diagnostics.py` |
### Placement Decision Order
Use these questions in order before creating or moving a module:
1. Is it generic and independent of MoviePilot state? Put it in `foundation`.
2. Is it a pure core MoviePilot rule/model that is independent of a configured service boundary? Put it in `domain`.
3. Is it process-wide runtime policy or a contract used by adapters? Put it in `platform`.
4. Does it perform configured technical I/O against Redis, files, OS, browser, RSS, or Rust? Put it in `infrastructure`.
5. Does it implement a named external product/ecosystem workflow? Put it in `integrations`.
6. Does it discover or manage modules/plugins/service implementations? Put it in `extensions`.
7. Does it own authentication, authorization, signing, SSRF, URL/path safety, OTP, passkeys, or two-factor behavior? Put it in `security`.
8. Does it read persisted user configuration, coordinate one bounded capability, or normalize/match one service family? Put it in `services`.
9. Does it coordinate several modules/services/Oper classes for one use case? Put it in `chain`.
10. Is it public to plugins or only preserving an old path? Curate it in `sdk` or map it in `compat`; do not move implementation there.
### Enforced Split Examples
These decisions are architectural constraints, not naming suggestions:
* Cache contracts, memory backends, decorators, and proxies stay in `app/platform/cache.py`; Redis and filesystem implementations stay in `app/infrastructure/cache.py`. Startup registers concrete factories before decorated business modules are imported. Legacy `app.core.cache` resolves to the complete `app.sdk.cache` facade.
* The complete logging runtime stays in `app/platform/log.py`: policy, console/plugin routing, async rotating file output, and shutdown. `app.platform.config` supplies the resolved settings and log path. `platform/log.py` remains a dependency leaf with no `app.*` imports. Plugins use `app.sdk.logging`; legacy `app.log` resolves to that SDK facade.
* Recognition parsing stays pure in `app/domain/meta/` and `app/domain/metainfo.py`. `app/services/recognition.py` reads `SystemConfigOper`; `app/startup/domain_initializer.py` injects rules, extension policy, source defaults, TMDB image construction, and the optional Rust accelerator.
* Kodi-style NFO reading and metadata document generation are one domain capability and stay together in `app/domain/scraper.py`; a separate `domain/nfo.py` must not be recreated.
* `app/services/mediaserver.py` is the single media-server service capability module. It owns configured service discovery together with Provider ID normalization and music-library matching, while reusing generic identity rules from `app/domain/media.py`.
* Configured notification-service discovery belongs in `app/services/notification.py`. Web Push subscription and manual-send HTTP behavior stays in `app/api/endpoints/message.py`; it is not a reusable messaging capability module.
* `app/infrastructure/resource.py` detects/downloads/installs resources and returns whether installation occurred. Only `app/startup/modules_initializer.py` may decide to restart the process afterward.
* Process memory/GC policy belongs in `app/platform/gc.py`; external IP-location APIs belong in `app/integrations/location.py`.
* Security implementation filenames use package-context nouns: `app/security/url.py` and `app/security/twofactor.py`. Historical `app.utils.security` and `app.helper.twofa` remain compatibility mappings only.
Foundation modules do not emit runtime logs. They return documented fallback values or raise according to their public contract; application callers decide whether a failure is operationally relevant and log it from the owning upper layer.
Any ownership move must update canonical host imports, `app/compat/manifest.py`, curated SDK exports when applicable, `docs/rules/05-architecture.md`, and `tests/test_architecture_dependencies.py`. Run that architecture test before broader tests; it rejects physical legacy sources, forbidden upward dependencies, retired canonical filenames, and import cycles.
---
## Agent Execution Rules
### Pre-Flight Check
@@ -85,6 +140,7 @@ When modifying the following, you must also update the listed artifacts:
| Database model schema | New Alembic migration under `database/versions/` |
| User-visible config or init flow | Related docs, help text, setup/init flows, tests |
| New skill | Follow `skills/<name>/SKILL.md` structure, keep YAML front matter |
| Canonical module ownership or import path | `docs/rules/05-architecture.md`, `app/compat/manifest.py`, SDK exports when public, architecture/compatibility tests |
---
@@ -94,4 +150,4 @@ For the full documentation map and cross-references, refer to:
**[Documentation Hub Index](./docs/rules/README.md)**
*Last Updated: 2026-05-25*
*Last Updated: 2026-08-14*
+3
View File
@@ -1,5 +1,7 @@
import warnings
from app.compat.imports import install_legacy_import_hook
def _filter_third_party_startup_warnings() -> None:
"""
@@ -13,3 +15,4 @@ def _filter_third_party_startup_warnings() -> None:
_filter_third_party_startup_warnings()
install_legacy_import_hook()
+13 -3076
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -6,7 +6,7 @@ from fastapi.concurrency import run_in_threadpool
from app.agent.policy import sanitize_for_host
from app.chain import ChainBase
from app.log import logger
from app.platform.log import logger
from app.schemas import Notification
from app.schemas.message import (
MessageResponse,
+4 -4
View File
@@ -12,9 +12,9 @@ from pathlib import Path
from typing import Any, Dict, Optional
from uuid import uuid4
from app.core.config import settings
from app.log import logger
from app.utils.http import RequestUtils
from app.platform.config import settings
from app.platform.log import logger
from app.foundation.http import RequestUtils
class AgentCapabilityProvider(ABC):
@@ -799,7 +799,7 @@ class AgentCapabilityManager:
if not source:
return False
from app.helper.service import ServiceConfigHelper
from app.extensions.service_registry import ServiceConfigHelper
for config in ServiceConfigHelper.get_notification_configs():
if config.name != source:
+2 -2
View File
@@ -10,8 +10,8 @@ from urllib.parse import urlsplit
from langchain_core.messages import AIMessage, AIMessageChunk
from app.core.config import settings
from app.log import logger
from app.platform.config import settings
from app.platform.log import logger
if TYPE_CHECKING:
from app.agent.llm.server_tools import ServerToolResolution
+3 -3
View File
@@ -20,11 +20,11 @@ import aiofiles
import httpx
import jwt
from app.core.config import settings
from app.platform.config import settings
from app.db.systemconfig_oper import SystemConfigOper
from app.log import logger
from app.platform.log import logger
from app.schemas.types import SystemConfigKey
from app.utils.singleton import Singleton
from app.foundation.singleton import Singleton
class LLMProviderError(RuntimeError):
+2 -2
View File
@@ -13,14 +13,14 @@ from typing import Any, Optional
from urllib.parse import urljoin
from app.db.systemconfig_oper import SystemConfigOper
from app.log import logger
from app.platform.log import logger
from app.schemas.agent import (
AgentMcpServerConfig,
AgentMcpServerTestResult,
AgentMcpServerToolInfo,
)
from app.schemas.types import SystemConfigKey
from app.utils.http import AsyncRequestUtils
from app.foundation.http import AsyncRequestUtils
MCP_PROTOCOL_VERSION = "2025-11-25"
MCP_CLIENT_NAME = "MoviePilot Agent"
+2 -2
View File
@@ -6,9 +6,9 @@ from typing import Dict, List, Optional
from langchain_core.messages import BaseMessage, messages_from_dict, messages_to_dict
from app.core.config import settings
from app.platform.config import settings
from app.db.agentchat_oper import AgentChatOper
from app.log import logger
from app.platform.log import logger
from app.schemas.agent import ConversationMemory
+1 -1
View File
@@ -35,7 +35,7 @@ from pydantic import BaseModel, Field
from app.agent.middleware.utils import append_to_system_message
from app.agent.policy import sanitize_for_host, summarize_error, summarize_result
from app.agent.tools.tags import ToolTag
from app.log import logger
from app.platform.log import logger
# 活动日志保留天数
DEFAULT_RETENTION_DAYS = 7
+1 -1
View File
@@ -17,7 +17,7 @@ from langchain_core.runnables import RunnableConfig
from langgraph.runtime import Runtime
from app.agent.middleware.utils import append_to_system_message
from app.log import logger
from app.platform.log import logger
# JOB.md 文件最大限制为 1MB
MAX_JOB_FILE_SIZE = 1 * 1024 * 1024
+1 -1
View File
@@ -15,7 +15,7 @@ from langchain_core.runnables import RunnableConfig
from langgraph.runtime import Runtime
from app.agent.middleware.utils import append_to_system_message
from app.log import logger
from app.platform.log import logger
# 记忆文件最大限制为 100KB,防止单文件过大导致上下文溢出
MAX_MEMORY_FILE_SIZE = 100 * 1024
+9 -166
View File
@@ -26,57 +26,17 @@ from pydantic import BaseModel, Field
from app.agent.middleware.utils import append_to_system_message
from app.agent.policy import sanitize_for_host, summarize_error
from app.agent.tools.tags import ToolTag
from app.log import logger
from app.agent.skills.metadata import (
MAX_SKILL_FILE_SIZE,
SkillMetadata,
parse_skill_metadata,
)
from app.platform.log import logger
# 磁盘读取上限与模型返回上限分离,避免异常大的 Skill 文件撑爆内存或上下文。
MAX_SKILL_FILE_SIZE = 1 * 1024 * 1024
# 模型返回上限独立于领域层的磁盘读取上限,避免异常内容撑爆上下文。
MAX_SKILL_RESULT_CHARS = 64 * 1024
SKILL_CONTENT_TRUNCATION_SUFFIX = "\n...(Skill 内容已截断)"
# Agent Skills 规范约束 (https://agentskills.io/specification)
MAX_SKILL_NAME_LENGTH = 64
MAX_SKILL_DESCRIPTION_LENGTH = 1024
MAX_SKILL_COMPATIBILITY_LENGTH = 500
class SkillMetadata(TypedDict):
"""Skill 元数据,符合 Agent Skills 规范。"""
path: str
"""SKILL.md 文件路径。"""
id: str
"""Skill 标识符。
约束: 1-64 字符仅限小写字母/数字/连字符不能以连字符开头或结尾无连续连字符需与父目录名一致
"""
name: str
"""Skill 名称。
约束: Skill中文描述
"""
version: int
"""Skill 版本号。
用于内置技能的版本管理同步时比较版本号决定是否覆盖用户目录中的旧版本
"""
description: str
"""Skill 功能描述。
约束: 1-1024 字符应说明功能及适用场景
"""
license: str | None
"""许可证信息。"""
compatibility: str | None
"""环境依赖或兼容性要求 (最多 500 字符)。"""
metadata: dict[str, str]
"""附加元数据。"""
allowed_tools: list[str]
"""(实验性) Skill 建议使用的工具列表。"""
class SkillsState(AgentState):
"""skills 中间件状态。"""
@@ -101,123 +61,6 @@ class SkillToolInput(BaseModel):
)
def _parse_skill_metadata( # noqa: C901
content: str,
skill_path: str,
skill_id: str,
) -> SkillMetadata | None:
"""从 SKILL.md 内容中解析 YAML 前言并验证元数据。"""
if len(content) > MAX_SKILL_FILE_SIZE:
logger.warning(
"Skipping %s: content too large (%d bytes)", skill_path, len(content)
)
return None
# 匹配 --- 分隔的 YAML 前言
frontmatter_pattern = r"^---\s*\n(.*?)\n---\s*\n"
match = re.match(frontmatter_pattern, content, re.DOTALL)
if not match:
logger.warning("Skipping %s: no valid YAML frontmatter found", skill_path)
return None
frontmatter_str = match.group(1)
# 解析 YAML
try:
frontmatter_data = yaml.safe_load(frontmatter_str)
except yaml.YAMLError as e:
logger.warning("Invalid YAML in %s: %s", skill_path, summarize_error(e))
return None
if not isinstance(frontmatter_data, dict):
logger.warning("Skipping %s: frontmatter is not a mapping", skill_path)
return None
# SKill名称和描述
name = str(frontmatter_data.get("name", "")).strip()
description = str(frontmatter_data.get("description", "")).strip()
if not name or not description:
logger.warning(
"Skipping %s: missing required 'name' or 'description'", skill_path
)
return None
description_str = description
if len(description_str) > MAX_SKILL_DESCRIPTION_LENGTH:
logger.warning(
"Description exceeds %d characters in %s, truncating",
MAX_SKILL_DESCRIPTION_LENGTH,
skill_path,
)
description_str = description_str[:MAX_SKILL_DESCRIPTION_LENGTH]
# 可选的工具列表,支持空格或逗号分隔
raw_tools = frontmatter_data.get("allowed-tools")
if isinstance(raw_tools, str):
allowed_tools = [
t.strip(",") # 兼容 Claude Code 风格的逗号分隔
for t in raw_tools.split()
if t.strip(",")
]
else:
if raw_tools is not None:
logger.warning(
"Ignoring non-string 'allowed-tools' in %s (got %s)",
skill_path,
type(raw_tools).__name__,
)
allowed_tools = []
# 能力或环境兼容性说明,最多 500 字符
compatibility_str = str(frontmatter_data.get("compatibility", "")).strip() or None
if compatibility_str and len(compatibility_str) > MAX_SKILL_COMPATIBILITY_LENGTH:
logger.warning(
"Compatibility exceeds %d characters in %s, truncating",
MAX_SKILL_COMPATIBILITY_LENGTH,
skill_path,
)
compatibility_str = str(compatibility_str)[:MAX_SKILL_COMPATIBILITY_LENGTH]
# 版本号,默认为 0(表示未设置版本)
raw_version = frontmatter_data.get("version")
version = 0
if raw_version is not None:
try:
version = int(raw_version)
except (ValueError, TypeError):
logger.warning(
"Invalid 'version' in %s (got %r), defaulting to 0",
skill_path,
raw_version,
)
return SkillMetadata(
id=skill_id,
name=name,
version=version,
description=description_str,
path=skill_path,
metadata=_validate_metadata(frontmatter_data.get("metadata", {}), skill_path),
license=str(frontmatter_data.get("license", "")).strip() or None,
compatibility=compatibility_str,
allowed_tools=allowed_tools,
)
def _validate_metadata(
raw: object,
skill_path: str,
) -> dict[str, str]:
"""验证并规范化 YAML 前言中的元数据字段,确保为 dict[str, str] 类型。"""
if not isinstance(raw, dict):
if raw:
logger.warning(
"Ignoring non-dict metadata in %s (got %s)",
skill_path,
type(raw).__name__,
)
return {}
return {str(k): str(v) for k, v in raw.items()}
def _format_skill_annotations(skill: SkillMetadata) -> str:
"""构建许可证和兼容性说明字符串。"""
parts: list[str] = []
@@ -264,7 +107,7 @@ async def _alist_skills(source_path: AsyncPath) -> list[SkillMetadata]:
)
# 解析元数据
skill_metadata = _parse_skill_metadata(
skill_metadata = parse_skill_metadata(
content=skill_content,
skill_path=str(skill_md_path),
skill_id=skill_path.name,
@@ -303,7 +146,7 @@ def _list_skills(source_path: Path) -> list[SkillMetadata]:
skill_content = skill_md_path.read_bytes().decode(
"utf-8", errors="replace"
)
skill_metadata = _parse_skill_metadata(
skill_metadata = parse_skill_metadata(
content=skill_content,
skill_path=str(skill_md_path),
skill_id=skill_path.name,
+1 -1
View File
@@ -37,7 +37,7 @@ from app.agent.policy import (
from app.agent.runtime import SubAgentDefinition, agent_runtime_manager
from app.agent.tools.tags import ToolTag
from app.agent.tools.catalog import ToolCatalogSnapshot
from app.log import logger
from app.platform.log import logger
SUBAGENT_TASK_TOOL_NAME = "task"
+1 -1
View File
@@ -17,7 +17,7 @@ from langgraph.graph.message import REMOVE_ALL_MESSAGES
from langgraph.types import Command
from app.agent.middleware.usage import UsageMiddleware
from app.log import logger
from app.platform.log import logger
try:
_internal_call_metadata = import_module(
+1 -1
View File
@@ -28,7 +28,7 @@ from typing_extensions import TypedDict # noqa
from app.agent.llm import LLMHelper
from app.agent.tools.tags import ToolTag
from app.log import logger
from app.platform.log import logger
MIN_SELECTED_TOOL_COUNT = 4
RECENT_SELECTION_CONTEXT_MESSAGE_LIMIT = 6
+1 -1
View File
@@ -11,7 +11,7 @@ from langchain.agents.middleware.types import (
from langchain_core.messages import AIMessage
from langchain_core.messages.utils import count_tokens_approximately
from app.log import logger
from app.platform.log import logger
class UsageMiddleware(AgentMiddleware):
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -25,7 +25,7 @@ from app.agent.policy.sanitizer import (
summarize_input,
summarize_result,
)
from app.log import logger
from app.platform.log import logger
_HookResult = TypeVar("_HookResult")
+3 -3
View File
@@ -10,15 +10,15 @@ from typing import Any, Dict, Optional
import yaml
from app.agent.llm.capability import AgentCapabilityManager
from app.core.config import settings
from app.log import logger
from app.platform.config import settings
from app.platform.log import logger
from app.schemas import (
ChannelCapability,
ChannelCapabilities,
MessageChannel,
ChannelCapabilityManager,
)
from app.utils.system import SystemUtils
from app.infrastructure.system import SystemUtils
SYSTEM_TASKS_FILE = "System Tasks.yaml"
SYSTEM_TASKS_SCHEMA_VERSION = 2
+2 -2
View File
@@ -12,8 +12,8 @@ from typing import Any, Iterable, Optional
import yaml
from app.core.config import settings
from app.log import logger
from app.platform.config import settings
from app.platform.log import logger
CURRENT_PERSONA_FILE = "CURRENT_PERSONA.md"
SYSTEM_RUNTIME_DIR = "runtime"
+1
View File
@@ -0,0 +1 @@
"""Agent Skill 元数据、市场和本地生命周期能力。"""
+132
View File
@@ -0,0 +1,132 @@
import logging
import re
from typing import TypedDict
import yaml
logger = logging.getLogger(__name__)
# 磁盘读取上限属于 Skill 文档格式约束,市场扫描和 Agent 加载必须共用。
MAX_SKILL_FILE_SIZE = 1 * 1024 * 1024
MAX_SKILL_NAME_LENGTH = 64
MAX_SKILL_DESCRIPTION_LENGTH = 1024
MAX_SKILL_COMPATIBILITY_LENGTH = 500
class SkillMetadata(TypedDict):
"""描述符合 Agent Skills 规范的已校验元数据。"""
path: str
id: str
name: str
version: int
description: str
license: str | None
compatibility: str | None
metadata: dict[str, str]
allowed_tools: list[str]
def _validate_metadata(raw: object, skill_path: str) -> dict[str, str]:
"""将 YAML metadata 字段规范化为字符串键值映射。"""
if not isinstance(raw, dict):
if raw:
logger.warning(
"Ignoring non-dict metadata in %s (got %s)",
skill_path,
type(raw).__name__,
)
return {}
return {str(key): str(value) for key, value in raw.items()}
def parse_skill_metadata( # noqa: C901
content: str,
skill_path: str,
skill_id: str,
) -> SkillMetadata | None:
"""解析并校验一个 SKILL.md 的 YAML 前言。"""
if len(content) > MAX_SKILL_FILE_SIZE:
logger.warning(
"Skipping %s: content too large (%d bytes)", skill_path, len(content)
)
return None
match = re.match(r"^---\s*\n(.*?)\n---\s*\n", content, re.DOTALL)
if not match:
logger.warning("Skipping %s: no valid YAML frontmatter found", skill_path)
return None
try:
frontmatter_data = yaml.safe_load(match.group(1))
except yaml.YAMLError as err:
logger.warning("Invalid YAML in %s: %s", skill_path, err)
return None
if not isinstance(frontmatter_data, dict):
logger.warning("Skipping %s: frontmatter is not a mapping", skill_path)
return None
name = str(frontmatter_data.get("name", "")).strip()
description = str(frontmatter_data.get("description", "")).strip()
if not name or not description:
logger.warning(
"Skipping %s: missing required 'name' or 'description'", skill_path
)
return None
if len(description) > MAX_SKILL_DESCRIPTION_LENGTH:
logger.warning(
"Description exceeds %d characters in %s, truncating",
MAX_SKILL_DESCRIPTION_LENGTH,
skill_path,
)
description = description[:MAX_SKILL_DESCRIPTION_LENGTH]
raw_tools = frontmatter_data.get("allowed-tools")
if isinstance(raw_tools, str):
allowed_tools = [
tool.strip(",")
for tool in raw_tools.split()
if tool.strip(",")
]
else:
if raw_tools is not None:
logger.warning(
"Ignoring non-string 'allowed-tools' in %s (got %s)",
skill_path,
type(raw_tools).__name__,
)
allowed_tools = []
compatibility = str(frontmatter_data.get("compatibility", "")).strip() or None
if compatibility and len(compatibility) > MAX_SKILL_COMPATIBILITY_LENGTH:
logger.warning(
"Compatibility exceeds %d characters in %s, truncating",
MAX_SKILL_COMPATIBILITY_LENGTH,
skill_path,
)
compatibility = compatibility[:MAX_SKILL_COMPATIBILITY_LENGTH]
raw_version = frontmatter_data.get("version")
version = 0
if raw_version is not None:
try:
version = int(raw_version)
except (ValueError, TypeError):
logger.warning(
"Invalid 'version' in %s (got %r), defaulting to 0",
skill_path,
raw_version,
)
return SkillMetadata(
id=skill_id,
name=name,
version=version,
description=description,
path=skill_path,
metadata=_validate_metadata(frontmatter_data.get("metadata", {}), skill_path),
license=str(frontmatter_data.get("license", "")).strip() or None,
compatibility=compatibility,
allowed_tools=allowed_tools,
)
@@ -9,13 +9,13 @@ from pathlib import Path
from typing import Dict, List, Optional, Tuple
from urllib.parse import urlencode, urljoin, urlparse
from app.agent.middleware.skills import _parse_skill_metadata
from app.core.cache import cached, fresh
from app.core.config import settings
from app.log import logger
from app.utils.http import RequestUtils
from app.utils.singleton import WeakSingleton
from app.utils.url import UrlUtils
from app.agent.skills.metadata import parse_skill_metadata
from app.platform.cache import cached, fresh
from app.platform.config import settings
from app.platform.log import logger
from app.foundation.http import RequestUtils
from app.foundation.singleton import WeakSingleton
from app.foundation.url import UrlUtils
_SOURCE_META_FILENAME = ".moviepilot-skill-source.json"
_DEFAULT_BRANCHES = ("main", "master")
@@ -35,6 +35,8 @@ _OFFICIAL_SKILL_REPOS = {
@dataclass
class SkillInfo:
"""描述本地或市场中的一个 Skill。"""
id: str
name: str
description: str
@@ -55,6 +57,8 @@ class SkillInfo:
@dataclass
class SkillMarketSource:
"""描述一个 Skill 市场来源及其可移除状态。"""
source: str
label: str
builtin: bool = True
@@ -386,7 +390,7 @@ class SkillHelper(metaclass=WeakSingleton):
logger.warning("读取技能文件失败:%s - %s", skill_md, e)
continue
metadata = _parse_skill_metadata(content, str(skill_md), path.name)
metadata = parse_skill_metadata(content, str(skill_md), path.name)
if not metadata:
continue
@@ -551,7 +555,7 @@ class SkillHelper(metaclass=WeakSingleton):
logger.warning("读取市场技能失败:%s - %s", archive_name, e)
continue
metadata = _parse_skill_metadata(
metadata = parse_skill_metadata(
content,
f"{repo['repo_url']}:{rel_path}",
skill_id,
+5 -5
View File
@@ -10,7 +10,7 @@ from typing import Any, Callable, ClassVar, Optional
from langchain_core.tools import BaseTool
from pydantic import PrivateAttr
from app.agent import StreamingHandler
from app.agent.callback import StreamingHandler
from app.agent.policy.sanitizer import (
summarize_error,
summarize_input,
@@ -18,10 +18,10 @@ from app.agent.policy.sanitizer import (
)
from app.agent.tools.tags import ToolTag
from app.chain import ChainBase
from app.core.config import settings
from app.helper.agent import matches_channel_admin
from app.helper.service import ServiceConfigHelper
from app.log import logger
from app.platform.config import settings
from app.messaging.agent import matches_channel_admin
from app.extensions.service_registry import ServiceConfigHelper
from app.platform.log import logger
from app.schemas import Notification
from app.schemas.types import MessageChannel, NotificationType
+2 -2
View File
@@ -87,8 +87,8 @@ from app.agent.tools.impl.update_custom_identifiers import UpdateCustomIdentifie
from app.agent.tools.impl.query_system_settings import QuerySystemSettingsTool
from app.agent.tools.impl.update_system_settings import UpdateSystemSettingsTool
from app.agent.llm.capability import AgentCapabilityManager
from app.core.plugin import PluginManager
from app.log import logger
from app.extensions.plugin_manager import PluginManager
from app.platform.log import logger
from app.schemas.message import ChannelCapabilityManager
from app.schemas.types import MessageChannel
from .base import MoviePilotTool
+2 -2
View File
@@ -4,10 +4,10 @@ import copy
import re
from typing import Any, Dict, Iterable, Optional
from app.core.event import eventmanager
from app.platform.events import eventmanager
from app.db.subscribe_oper import SubscribeOper
from app.db.systemconfig_oper import SystemConfigOper
from app.helper.rule import RuleHelper
from app.services.filter import RuleHelper
from app.modules.filter.RuleParser import RuleParser
from app.modules.filter.builtin_rules import BUILTIN_RULE_SET
from app.schemas import CustomRule, FilterRuleGroup
+2 -2
View File
@@ -2,7 +2,7 @@
from typing import Any, Optional
from app.core.context import (
from app.domain.context import (
MusicAlbumInfo,
MusicArtistInfo,
MusicInfo,
@@ -12,7 +12,7 @@ from app.schemas.types import (
MUSIC_SUBSCRIBABLE_TYPES,
media_type_to_agent,
)
from app.utils.media import normalize_music_type
from app.domain.media import normalize_music_type
MUSIC_TRACK_PREVIEW_LIMIT = 100
+4 -4
View File
@@ -4,11 +4,11 @@ import json
import shutil
from typing import Any, Optional
from app.core.config import settings
from app.core.plugin import PluginManager
from app.platform.config import settings
from app.extensions.plugin_manager import PluginManager
from app.db.systemconfig_oper import SystemConfigOper
from app.helper.server import MoviePilotServerHelper
from app.helper.plugin import PluginHelper
from app.integrations.server import MoviePilotServerHelper
from app.integrations.market import PluginHelper
from app.schemas.types import SystemConfigKey
# 默认只向智能体返回一个可读预览,避免超大插件数据挤爆上下文窗口。
@@ -4,7 +4,7 @@ from dataclasses import dataclass
from typing import Any, Optional
from app.agent.policy.secret_fields import is_secret_setting_key
from app.core.config import Settings
from app.platform.config import Settings
from app.schemas.types import SystemConfigKey
+2 -2
View File
@@ -14,8 +14,8 @@ from pathlib import Path
from typing import Any, Optional
from app.agent.tools.impl._command_safety import validate_command_safety
from app.core.config import settings
from app.log import logger
from app.platform.config import settings
from app.platform.log import logger
if os.name == "posix":
import fcntl as _fcntl
@@ -3,10 +3,10 @@
import re
from typing import List, Optional
from app.core.context import Context
from app.domain.context import Context
from app.schemas.types import MediaType, media_type_to_agent
from app.utils.crypto import HashUtils
from app.utils.string import StringUtils
from app.foundation.crypto import HashUtils
from app.domain.string import StringUtils
from ._music_utils import simplify_music_info
SEARCH_RESULT_CACHE_FILE = "__search_result__"
@@ -13,7 +13,7 @@ from app.agent.tools.impl._filter_rule_utils import (
save_system_config,
serialize_custom_rule,
)
from app.log import logger
from app.platform.log import logger
from app.schemas.types import SystemConfigKey
+6 -6
View File
@@ -12,14 +12,14 @@ from app.agent.tools.tags import ToolTag
from app.chain.download import DownloadChain
from app.chain.media import MediaChain
from app.chain.search import SearchChain
from app.core.config import settings
from app.core.context import Context
from app.core.metainfo import MetaInfo
from app.platform.config import settings
from app.domain.context import Context
from app.domain.metainfo import MetaInfo
from app.db.site_oper import SiteOper
from app.helper.directory import DirectoryHelper, validate_download_save_path
from app.log import logger
from app.services.directory import DirectoryHelper, validate_download_save_path
from app.platform.log import logger
from app.schemas import FileURI
from app.utils.crypto import HashUtils
from app.foundation.crypto import HashUtils
class AddDownloadTasksInput(BaseModel):
+1 -1
View File
@@ -17,7 +17,7 @@ from app.agent.tools.impl._filter_rule_utils import (
save_system_config,
serialize_rule_group,
)
from app.log import logger
from app.platform.log import logger
from app.schemas.types import SystemConfigKey
+1 -1
View File
@@ -8,7 +8,7 @@ from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.subscribe import SubscribeChain
from app.db.user_oper import UserOper
from app.log import logger
from app.platform.log import logger
from app.schemas.types import MUSIC_ENTITY_ALBUM, MediaSource, MediaType, MessageChannel
from ._music_utils import normalize_music_type
+2 -2
View File
@@ -6,11 +6,11 @@ from pydantic import BaseModel, Field, model_validator
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.helper.interaction import (
from app.messaging.interaction import (
AgentInteractionOption,
agent_interaction_manager,
)
from app.log import logger
from app.platform.log import logger
from app.schemas import Notification, NotificationType
from app.schemas.message import ChannelCapabilityManager
from app.schemas.types import MessageChannel
+2 -2
View File
@@ -9,8 +9,8 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.helper.browser import BrowserSessionHelper
from app.log import logger
from app.infrastructure.browser import BrowserSessionHelper
from app.platform.log import logger
# 页面内容最大长度;保留在全局工具结果兜底上限以内。
MAX_CONTENT_LENGTH = 12_000
+2 -2
View File
@@ -7,10 +7,10 @@ from pydantic import BaseModel, Field, model_validator
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.core.config import settings
from app.platform.config import settings
from app.db.agentchat_oper import AgentChatOper
from app.db.agenttask_oper import AgentTaskOper
from app.utils.timer import TimerUtils
from app.platform.scheduling import TimerUtils
class CreateAgentTaskInput(BaseModel):
@@ -13,7 +13,7 @@ from app.agent.tools.impl._filter_rule_utils import (
get_rule_groups,
save_system_config,
)
from app.log import logger
from app.platform.log import logger
from app.schemas.types import SystemConfigKey
@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.db.downloadhistory_oper import DownloadHistoryOper
from app.log import logger
from app.platform.log import logger
class DeleteDownloadHistoryInput(BaseModel):
@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.download import DownloadChain
from app.log import logger
from app.platform.log import logger
class DeleteDownloadTasksInput(BaseModel):
+1 -1
View File
@@ -12,7 +12,7 @@ from app.agent.tools.impl._filter_rule_utils import (
remove_rule_group_references,
save_system_config,
)
from app.log import logger
from app.platform.log import logger
from app.schemas.types import SystemConfigKey
+3 -3
View File
@@ -6,10 +6,10 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.core.event import eventmanager
from app.platform.events import eventmanager
from app.db.subscribe_oper import SubscribeOper
from app.helper.server import MoviePilotServerHelper
from app.log import logger
from app.integrations.server import MoviePilotServerHelper
from app.platform.log import logger
from app.schemas.types import EventType
@@ -8,7 +8,7 @@ from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.storage import StorageChain
from app.db.transferhistory_oper import TransferHistoryOper
from app.log import logger
from app.platform.log import logger
from app.schemas import FileItem
+1 -1
View File
@@ -13,7 +13,7 @@ from app.agent.tools.impl._file_write_utils import (
calculate_file_sha256,
)
from app.agent.tools.tags import ToolTag
from app.log import logger
from app.platform.log import logger
class EditFileInput(BaseModel):
+1 -1
View File
@@ -23,7 +23,7 @@ from app.agent.tools.impl._terminal_session import (
TERMINAL_WAIT_DEFAULT_MS,
terminal_session_manager,
)
from app.log import logger
from app.platform.log import logger
DEFAULT_TIMEOUT_SECONDS = 60
+1 -1
View File
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.recommend import RecommendChain
from app.log import logger
from app.platform.log import logger
from app.modules.listenbrainz import (
LISTENBRAINZ_CHART_RANGES,
LISTENBRAINZ_FRESH_MAX_DAYS,
+1 -1
View File
@@ -9,7 +9,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.search import SearchChain
from app.log import logger
from app.platform.log import logger
from ._torrent_search_utils import (
TORRENT_RESULT_LIMIT,
build_filter_options,
+1 -1
View File
@@ -13,7 +13,7 @@ from app.agent.tools.impl._plugin_tool_utils import (
load_market_plugins,
summarize_plugin,
)
from app.log import logger
from app.platform.log import logger
class InstallPluginInput(BaseModel):
+2 -2
View File
@@ -10,9 +10,9 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.storage import StorageChain
from app.log import logger
from app.platform.log import logger
from app.schemas.file import FileItem
from app.utils.string import StringUtils
from app.domain.string import StringUtils
DEFAULT_DIRECTORY_PAGE_SIZE = 50
+1 -1
View File
@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.log import logger
from app.platform.log import logger
class ListSlashCommandsInput(BaseModel):
+1 -1
View File
@@ -5,7 +5,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.core.config import settings
from app.platform.config import settings
from app.db.agenttask_oper import AgentTaskOper
@@ -12,7 +12,7 @@ from app.agent.tools.impl._filter_rule_utils import (
serialize_builtin_rule,
RULE_STRING_SYNTAX,
)
from app.log import logger
from app.platform.log import logger
class QueryBuiltinFilterRulesInput(BaseModel):
@@ -13,7 +13,7 @@ from app.agent.tools.impl._filter_rule_utils import (
get_rule_groups,
serialize_custom_rule,
)
from app.log import logger
from app.platform.log import logger
class QueryCustomFilterRulesInput(BaseModel):
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.db.systemconfig_oper import SystemConfigOper
from app.log import logger
from app.platform.log import logger
from app.schemas.types import SystemConfigKey
@@ -7,8 +7,8 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.helper.directory import DirectoryHelper
from app.log import logger
from app.services.directory import DirectoryHelper
from app.platform.log import logger
class QueryDirectorySettingsInput(BaseModel):
+1 -1
View File
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.doctor import run_doctor
from app.log import logger
from app.platform.log import logger
class QueryDoctorReportInput(BaseModel):
+1 -1
View File
@@ -9,7 +9,7 @@ from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.download import DownloadChain
from app.db.downloadhistory_oper import DownloadHistoryOper
from app.log import logger
from app.platform.log import logger
from app.schemas import DownloaderTorrent
from app.schemas.types import MUSIC_ENTITY_RECORDING, TorrentQueryStatus, media_type_to_agent
+1 -1
View File
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.db.systemconfig_oper import SystemConfigOper
from app.log import logger
from app.platform.log import logger
from app.schemas.types import SystemConfigKey
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.tmdb import TmdbChain
from app.log import logger
from app.platform.log import logger
class QueryEpisodeScheduleInput(BaseModel):
@@ -16,7 +16,7 @@ from app.agent.tools.impl._plugin_tool_utils import (
summarize_candidates,
summarize_plugin,
)
from app.log import logger
from app.platform.log import logger
class QueryInstalledPluginsInput(BaseModel):
+2 -2
View File
@@ -10,8 +10,8 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.mediaserver import MediaServerChain
from app.helper.mediaserver import MediaServerHelper
from app.log import logger
from app.services.mediaserver import MediaServerHelper
from app.platform.log import logger
from app.schemas.types import (
MUSIC_ENTITY_ALBUM,
MediaSource,
+2 -2
View File
@@ -9,8 +9,8 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.mediaserver import MediaServerChain
from app.helper.service import ServiceConfigHelper
from app.log import logger
from app.extensions.service_registry import ServiceConfigHelper
from app.platform.log import logger
PAGE_SIZE = 20
+1 -1
View File
@@ -15,7 +15,7 @@ from app.agent.tools.impl._plugin_tool_utils import (
summarize_candidates,
summarize_plugin,
)
from app.log import logger
from app.platform.log import logger
class QueryMarketPluginsInput(BaseModel):
+1 -1
View File
@@ -9,7 +9,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.media import MediaChain
from app.log import logger
from app.platform.log import logger
from app.schemas.types import (
MUSIC_ENTITY_ALBUM,
MUSIC_ENTITY_ARTIST,
+1 -1
View File
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.runtime import agent_runtime_manager
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.log import logger
from app.platform.log import logger
class QueryPersonasInput(BaseModel):
@@ -7,8 +7,8 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.core.plugin import PluginManager
from app.log import logger
from app.extensions.plugin_manager import PluginManager
from app.platform.log import logger
class QueryPluginCapabilitiesInput(BaseModel):
+2 -2
View File
@@ -8,8 +8,8 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.agent.tools.impl._plugin_tool_utils import get_plugin_snapshot
from app.core.plugin import PluginManager
from app.log import logger
from app.extensions.plugin_manager import PluginManager
from app.platform.log import logger
class QueryPluginConfigInput(BaseModel):
+1 -1
View File
@@ -13,7 +13,7 @@ from app.agent.tools.impl._plugin_tool_utils import (
get_plugin_snapshot,
)
from app.db.plugindata_oper import PluginDataOper
from app.log import logger
from app.platform.log import logger
class QueryPluginDataInput(BaseModel):
@@ -8,9 +8,9 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.core.context import MediaInfo
from app.helper.server import MoviePilotServerHelper
from app.log import logger
from app.domain.context import MediaInfo
from app.integrations.server import MoviePilotServerHelper
from app.platform.log import logger
from app.schemas.types import MUSIC_ENTITY_RECORDING, MediaType, media_type_to_agent
from ._music_utils import normalize_music_type
+1 -1
View File
@@ -13,7 +13,7 @@ from app.agent.tools.impl._filter_rule_utils import (
serialize_rule_group,
RULE_STRING_SYNTAX,
)
from app.log import logger
from app.platform.log import logger
class QueryRuleGroupsInput(BaseModel):
+1 -1
View File
@@ -7,7 +7,7 @@ from pydantic import BaseModel
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.log import logger
from app.platform.log import logger
class QuerySchedulersInput(BaseModel):
+1 -1
View File
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.db.site_oper import SiteOper
from app.log import logger
from app.platform.log import logger
SITE_USERDATA_DETAIL_PREVIEW_LIMIT = 10
+1 -1
View File
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.db.site_oper import SiteOper
from app.log import logger
from app.platform.log import logger
class QuerySitesInput(BaseModel):
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.db.subscribehistory_oper import SubscribeHistoryOper
from app.log import logger
from app.platform.log import logger
from app.schemas.types import MUSIC_ENTITY_RECORDING, MediaType, media_type_to_agent
from ._music_utils import normalize_music_type
@@ -7,8 +7,8 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.helper.server import MoviePilotServerHelper
from app.log import logger
from app.integrations.server import MoviePilotServerHelper
from app.platform.log import logger
from app.schemas.types import MUSIC_ENTITY_RECORDING, media_type_to_agent
from ._music_utils import normalize_music_type
+1 -1
View File
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.db.subscribe_oper import SubscribeOper
from app.log import logger
from app.platform.log import logger
from app.schemas.subscribe import Subscribe as SubscribeSchema
from app.schemas.types import (
MUSIC_ENTITY_RECORDING,
@@ -15,9 +15,9 @@ from app.agent.tools.impl._system_setting_utils import (
resolve_setting_spec,
should_redact_setting,
)
from app.core.config import settings
from app.platform.config import settings
from app.db.systemconfig_oper import SystemConfigOper
from app.log import logger
from app.platform.log import logger
class QuerySystemSettingsInput(BaseModel):
@@ -8,9 +8,9 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.db.transferhistory_oper import TransferHistoryOper
from app.log import logger
from app.platform.log import logger
from app.schemas.types import media_type_to_agent
from app.utils.jieba import cut as jieba_cut
from app.foundation.jieba import cut as jieba_cut
class QueryTransferHistoryInput(BaseModel):
+1 -1
View File
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.db.workflow_oper import WorkflowOper
from app.log import logger
from app.platform.log import logger
class QueryWorkflowsInput(BaseModel):
+1 -1
View File
@@ -10,7 +10,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.log import logger
from app.platform.log import logger
# 最大读取大小 50KB
MAX_READ_SIZE = 50 * 1024
+3 -3
View File
@@ -7,9 +7,9 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.helper.browser import BrowserSessionHelper
from app.helper.ocr import OcrHelper
from app.log import logger
from app.infrastructure.browser import BrowserSessionHelper
from app.integrations.ocr import OcrHelper
from app.platform.log import logger
class RecognizeCaptchaInput(BaseModel):
+5 -5
View File
@@ -9,11 +9,11 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.media import MediaChain
from app.core.config import settings
from app.core.context import Context
from app.core.meta import MetaMusic
from app.core.metainfo import MetaInfo
from app.log import logger
from app.platform.config import settings
from app.domain.context import Context
from app.domain.meta.metamusic import MetaMusic
from app.domain.metainfo import MetaInfo
from app.platform.log import logger
from app.schemas.types import MediaType, media_type_to_agent
from ._music_utils import simplify_music_info
+1 -1
View File
@@ -11,7 +11,7 @@ from app.agent.tools.impl._plugin_tool_utils import (
get_plugin_snapshot,
reload_plugin_runtime,
)
from app.log import logger
from app.platform.log import logger
class ReloadPluginInput(BaseModel):
+1 -1
View File
@@ -6,7 +6,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.log import logger
from app.platform.log import logger
class RunSchedulerInput(BaseModel):
+2 -2
View File
@@ -7,8 +7,8 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.core.event import eventmanager
from app.log import logger
from app.platform.events import eventmanager
from app.platform.log import logger
from app.schemas.types import EventType, MessageChannel
+1 -1
View File
@@ -8,7 +8,7 @@ from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.workflow import WorkflowChain
from app.db.workflow_oper import WorkflowOper
from app.log import logger
from app.platform.log import logger
class RunWorkflowInput(BaseModel):
+3 -3
View File
@@ -10,8 +10,8 @@ from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.media import MediaChain
from app.chain.scraping import ScrapingChain
from app.core.config import settings
from app.log import logger
from app.platform.config import settings
from app.platform.log import logger
from app.schemas import FileItem
from app.schemas.types import (
MUSIC_ENTITY_ARTIST,
@@ -19,7 +19,7 @@ from app.schemas.types import (
MediaType,
media_type_to_agent,
)
from app.utils.media import normalize_media_source
from app.domain.media import normalize_media_source
from ._music_utils import normalize_music_type, simplify_music_info
+2 -2
View File
@@ -8,9 +8,9 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.media import MediaChain
from app.log import logger
from app.platform.log import logger
from app.schemas.types import MediaType, media_type_to_agent
from app.utils.media import resolve_media_identity
from app.domain.media import resolve_media_identity
from ._music_utils import normalize_music_type, simplify_music_info
+1 -1
View File
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.media import MediaChain
from app.log import logger
from app.platform.log import logger
class SearchPersonInput(BaseModel):
@@ -10,8 +10,8 @@ from app.agent.tools.tags import ToolTag
from app.chain.douban import DoubanChain
from app.chain.tmdb import TmdbChain
from app.chain.bangumi import BangumiChain
from app.log import logger
from app.utils.media import resolve_media_identity
from app.platform.log import logger
from app.domain.media import resolve_media_identity
class SearchPersonCreditsInput(BaseModel):
+1 -1
View File
@@ -9,7 +9,7 @@ from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.subscribe import SubscribeChain
from app.db.subscribe_oper import SubscribeOper
from app.log import logger
from app.platform.log import logger
from app.schemas.types import media_type_to_agent
+2 -2
View File
@@ -9,8 +9,8 @@ from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.search import SearchChain
from app.db.systemconfig_oper import SystemConfigOper
from app.helper.sites import SitesHelper # noqa
from app.log import logger
from app.infrastructure.sites import SitesHelper # noqa
from app.platform.log import logger
from app.schemas.types import MediaSource, MediaType, SystemConfigKey
from ._music_utils import normalize_music_type
from ._torrent_search_utils import (
+2 -2
View File
@@ -9,8 +9,8 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.core.config import settings
from app.log import logger
from app.platform.config import settings
from app.platform.log import logger
# 搜索超时时间(秒)
SEARCH_TIMEOUT = 20
+1 -1
View File
@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field, model_validator
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.log import logger
from app.platform.log import logger
from app.schemas import Notification, NotificationType
from app.schemas.message import ChannelCapabilityManager, ChannelCapability
from app.schemas.types import MessageChannel
+1 -1
View File
@@ -6,7 +6,7 @@ from pydantic import BaseModel, Field, model_validator
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.log import logger
from app.platform.log import logger
from app.schemas import Notification
from app.schemas.types import NotificationType
+2 -2
View File
@@ -6,8 +6,8 @@ from pydantic import BaseModel, Field
from app.agent.llm.capability import AgentCapabilityManager
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.core.config import settings
from app.log import logger
from app.platform.config import settings
from app.platform.log import logger
from app.schemas import Notification, NotificationType

Some files were not shown because too many files have changed in this diff Show More