mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-09 01:16:50 +08:00
refactor(agent): relocate static resources
This commit is contained in:
@@ -97,7 +97,7 @@ jobs:
|
||||
- name: Download models.dev catalog
|
||||
id: models_catalog
|
||||
run: |
|
||||
temp_file=$(mktemp app/agent/llm/models.json.XXXXXX)
|
||||
temp_file=$(mktemp app/agent/llm/resources/models.json.XXXXXX)
|
||||
trap 'rm -f "$temp_file"' EXIT
|
||||
curl --fail --show-error --silent --location --retry 3 \
|
||||
--connect-timeout 10 --max-time 120 \
|
||||
@@ -105,9 +105,9 @@ jobs:
|
||||
jq -e 'type == "object"' "$temp_file" >/dev/null
|
||||
# Git keeps only a small placeholder; the beta image receives the current catalog.
|
||||
chmod 644 "$temp_file"
|
||||
mv "$temp_file" app/agent/llm/models.json
|
||||
echo "digest=sha256:$(sha256sum app/agent/llm/models.json | awk '{print $1}')" >> "$GITHUB_OUTPUT"
|
||||
echo "Downloaded models.dev catalog ($(wc -c < app/agent/llm/models.json) bytes)"
|
||||
mv "$temp_file" app/agent/llm/resources/models.json
|
||||
echo "digest=sha256:$(sha256sum app/agent/llm/resources/models.json | awk '{print $1}')" >> "$GITHUB_OUTPUT"
|
||||
echo "Downloaded models.dev catalog ($(wc -c < app/agent/llm/resources/models.json) bytes)"
|
||||
|
||||
- name: Docker Meta
|
||||
id: meta
|
||||
|
||||
@@ -105,7 +105,7 @@ jobs:
|
||||
- name: Download models.dev catalog
|
||||
id: models_catalog
|
||||
run: |
|
||||
temp_file=$(mktemp app/agent/llm/models.json.XXXXXX)
|
||||
temp_file=$(mktemp app/agent/llm/resources/models.json.XXXXXX)
|
||||
trap 'rm -f "$temp_file"' EXIT
|
||||
curl --fail --show-error --silent --location --retry 3 \
|
||||
--connect-timeout 10 --max-time 120 \
|
||||
@@ -113,9 +113,9 @@ jobs:
|
||||
jq -e 'type == "object"' "$temp_file" >/dev/null
|
||||
# Git keeps only a small placeholder; the release image receives the current catalog.
|
||||
chmod 644 "$temp_file"
|
||||
mv "$temp_file" app/agent/llm/models.json
|
||||
echo "digest=sha256:$(sha256sum app/agent/llm/models.json | awk '{print $1}')" >> "$GITHUB_OUTPUT"
|
||||
echo "Downloaded models.dev catalog ($(wc -c < app/agent/llm/models.json) bytes)"
|
||||
mv "$temp_file" app/agent/llm/resources/models.json
|
||||
echo "digest=sha256:$(sha256sum app/agent/llm/resources/models.json | awk '{print $1}')" >> "$GITHUB_OUTPUT"
|
||||
echo "Downloaded models.dev catalog ($(wc -c < app/agent/llm/resources/models.json) bytes)"
|
||||
|
||||
- name: Create Release Snapshot
|
||||
id: release_snapshot
|
||||
|
||||
@@ -50,7 +50,7 @@ class _ProviderDiscovery:
|
||||
|
||||
_MODELS_DEV_URL = "https://models.dev/api.json"
|
||||
|
||||
_MODELS_DEV_BUNDLED_PATH = Path(__file__).with_name("models.json")
|
||||
_MODELS_DEV_BUNDLED_PATH = Path(__file__).with_name("resources") / "models.json"
|
||||
|
||||
_MODELS_DEV_CACHE_TTL = 7 * 24 * 60 * 60
|
||||
|
||||
|
||||
@@ -19,7 +19,12 @@ from app.schemas.types import NotificationChannel
|
||||
@lru_cache(maxsize=1)
|
||||
def _load_api_mcp_input_schema() -> dict[str, Any]:
|
||||
"""读取由业务 OpenAPI 生成并经漂移测试锁定的外部 MCP schema。"""
|
||||
schema_path = Path(__file__).resolve().parents[2] / "policy" / "api_mcp_schema.json"
|
||||
schema_path = (
|
||||
Path(__file__).resolve().parents[2]
|
||||
/ "policy"
|
||||
/ "resources"
|
||||
/ "api_mcp_schema.json"
|
||||
)
|
||||
payload = json.loads(schema_path.read_text(encoding="utf-8"))
|
||||
if not isinstance(payload, dict) or not isinstance(payload.get("oneOf"), list):
|
||||
raise RuntimeError("moviepilot_api MCP schema 无效")
|
||||
|
||||
@@ -296,7 +296,7 @@ action,并使用 MoviePilot 已配置的具体服务实例访问其自身 API
|
||||
- Skill 文档生成器会对数据库说明与 ORM 元数据做双向缺项校验;新增、删除或改名表但未同步说明时生成直接失败,避免文档再次出现漏表或只有字段没有语义的问题
|
||||
- 系统设置继续由 `config.system.get/update` 统一承载,不恢复旧配置工具;Skill 不再复制易漂移的 `Settings` / `SystemConfigKey` 全量清单,而是要求先动态发现 `definition`(声明类型、当前形状、敏感性、允许操作、列表匹配字段和持久化位置),再按精确键更新
|
||||
- `Settings` 更新执行类型转换并持久化到 `app.env`;`SystemConfigKey` 通过配置服务写入数据库,保留插件 mutation 门禁、敏感值脱敏和配置变更事件
|
||||
- `app/agent/policy/api_mcp_schema.json` 明确为 OpenAPI + 固定 operation 合同生成的 MCP `tools/list` 制品,禁止手工维护;所有 operation、模型和字段必须具备具体英文说明,抽象占位文本或中文说明会由测试拒绝
|
||||
- `app/agent/policy/resources/api_mcp_schema.json` 明确为 OpenAPI + 固定 operation 合同生成的 MCP `tools/list` 制品,禁止手工维护;所有 operation、模型和字段必须具备具体英文说明,抽象占位文本或中文说明会由测试拒绝
|
||||
- API 权限保持双层硬门禁:`moviepilot_api` 按 operation 的 `required_role` 在发起 HTTP 前拒绝非管理员,最终 FastAPI 端点继续用真实当前用户令牌执行 superuser/manage/user 级鉴权;下载器、媒体服务器、数据库外部工具整体为 admin-only
|
||||
- 异步边界复核:内置 Skill 脚本经 asyncio subprocess 执行;外部服务/数据库 MCP 工具把同步脚本放入 downloader、mediaserver、db 分域线程池;`moviepilot_api` 使用异步 HTTP,不阻塞 Agent event loop
|
||||
- 当前阶段进入最终验证:聚焦 Agent/Skill/MCP 回归、全量锁定测试、80% 固定覆盖率 CI、提交推送与远端终态确认
|
||||
|
||||
+2
-2
@@ -43,14 +43,14 @@ MCP 当前不会主动发送工具列表变更通知(`listChanged=false`)。
|
||||
|
||||
| MCP 工具 | 用途 | 参数合同来源 |
|
||||
| :--- | :--- | :--- |
|
||||
| `moviepilot_api` | MoviePilot 产品业务 API:媒体、搜索、订阅、下载、整理、站点、存储、调度、工作流、插件、过滤规则和系统配置 | `skills/moviepilot-api/SKILL.md`;运行时 schema 为 `app/agent/policy/api_mcp_schema.json` |
|
||||
| `moviepilot_api` | MoviePilot 产品业务 API:媒体、搜索、订阅、下载、整理、站点、存储、调度、工作流、插件、过滤规则和系统配置 | `skills/moviepilot-api/SKILL.md`;运行时 schema 为 `app/agent/policy/resources/api_mcp_schema.json` |
|
||||
| `downloader_operation` | qBittorrent、Transmission、rTorrent 原生任务、队列、文件、限速、标签和会话操作 | `skills/downloader-operation/SKILL.md` 与 `skills/downloader-operation/scripts/mp-downloader.py` 的 `ACTIONS` |
|
||||
| `mediaserver_operation` | Emby、Jellyfin、Plex、ZSpace、UGREEN、TrimeMedia、Navidrome 原生媒体库、搜索、播放、扫描和刷新操作 | `skills/mediaserver-operation/SKILL.md` 与 `skills/mediaserver-operation/scripts/mp-mediaserver.py` 的 `ACTIONS` |
|
||||
| `database_operation` | MoviePilot 配置数据库表清单、实时 schema、只读 SQL 和明确授权写入 | `skills/database-operation/SKILL.md` 与 `skills/database-operation/scripts/mp-db.py` 的 `ACTIONS` |
|
||||
|
||||
这四个工具都要求管理员级 MCP 集成身份;`tools/list` 的可见性不等于绕过业务权限或写操作确认。下载器和媒体服务器工具会在一次调用内自动选择默认/唯一实例;实例不明确时,错误结果会列出可复用的精确实例名。数据库工具不接受任意连接串或凭据,脚本从 MoviePilot 运行时配置读取数据库连接。
|
||||
|
||||
`app/agent/policy/api_mcp_schema.json` 是 `moviepilot_api` 的生成制品,不是设置项或 API 参数的手工事实源。`scripts/generate_agent_api_mcp_schema.py` 从当前 FastAPI OpenAPI、固定 operation 路由和 Agent 专用英文参数说明生成该文件;运行时直接读取它响应外部 MCP `tools/list`,测试会校验生成结果没有漂移。修改 API、请求模型或 operation 后应重新生成并提交该文件,不应直接编辑 JSON。
|
||||
`app/agent/policy/resources/api_mcp_schema.json` 是 `moviepilot_api` 的生成制品,不是设置项或 API 参数的手工事实源。`scripts/generate_agent_api_mcp_schema.py` 从当前 FastAPI OpenAPI、固定 operation 路由和 Agent 专用英文参数说明生成该文件;运行时直接读取它响应外部 MCP `tools/list`,测试会校验生成结果没有漂移。修改 API、请求模型或 operation 后应重新生成并提交该文件,不应直接编辑 JSON。
|
||||
|
||||
当前完整 FastAPI OpenAPI 包含 375 个 HTTP 操作,其中 203 个稳定业务操作进入
|
||||
`moviepilot_api`,使用 201 个固定路由模板:200 条 OpenAPI 路由直接匹配,另有 1 条只允许
|
||||
|
||||
@@ -17,7 +17,7 @@ from app.agent.policy.api import API_OPERATION_ROUTES, API_OPERATION_SPECS # no
|
||||
from app.agent.policy.mcp import build_api_mcp_input_schema # noqa: E402
|
||||
from app.api.apiv1 import api_router # noqa: E402
|
||||
|
||||
OUTPUT_PATH = PROJECT_ROOT / "app/agent/policy/api_mcp_schema.json"
|
||||
OUTPUT_PATH = PROJECT_ROOT / "app/agent/policy/resources/api_mcp_schema.json"
|
||||
|
||||
|
||||
def generate_schema() -> dict:
|
||||
|
||||
@@ -161,7 +161,7 @@ DATABASE_TABLE_GUIDES: dict[str, tuple[str, str, str]] = {
|
||||
|
||||
def _load_json_schema() -> dict[str, Any]:
|
||||
"""Load the generated moviepilot_api MCP schema."""
|
||||
path = PROJECT_ROOT / "app/agent/policy/api_mcp_schema.json"
|
||||
path = PROJECT_ROOT / "app/agent/policy/resources/api_mcp_schema.json"
|
||||
payload = json.loads(path.read_text(encoding="utf-8"))
|
||||
if not isinstance(payload, dict):
|
||||
raise ValueError("The API MCP schema must be a JSON object")
|
||||
|
||||
Reference in New Issue
Block a user