mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-01 12:56:34 +08:00
严格检查全部宿主源码,清零类型错误预算并移除按模块类型豁免。 按原因、所属方法、错误码及语法指纹锁定必要兼容例外和站点类型桩, CI 阻断新增忽略、检查范围退化、本地桩替代真实接口及新的类型错误。 修复空值、异步和供应方类型边界,保持插件与 API 兼容合同, 并补充存储、订阅、通知、Agent 和工作流边界的回归覆盖。
122 lines
6.2 KiB
Python
122 lines
6.2 KiB
Python
"""
|
||
skill_manage 按动作区分参数形态,避免正文写进错误字段。
|
||
"""
|
||
|
||
from typing import Literal
|
||
|
||
from pydantic import BaseModel, ConfigDict, Field
|
||
|
||
|
||
class SkillOperation(BaseModel):
|
||
"""
|
||
技能身份由名称选择,个人根与后台身份始终由宿主绑定。
|
||
"""
|
||
|
||
model_config = ConfigDict(extra='forbid')
|
||
name: str = Field(pattern=r'^[a-z0-9][a-z0-9_-]{0,63}$', description="技能名称,须以小写字母或数字开头,后续仅允许小写字母、数字、下划线或连字符,最多 64 字符")
|
||
|
||
|
||
class CreateSkill(SkillOperation):
|
||
"""
|
||
新建完整大类技能,正文必须含可加载的 YAML 前言。
|
||
"""
|
||
|
||
action: Literal['create'] = Field(description="固定为 create,表示新建技能")
|
||
content: str = Field(min_length=1, max_length=100_000, description="完整 SKILL.md 正文,须包含可加载的 YAML 前言")
|
||
category: str | None = Field(default=None, pattern=r'^[a-z0-9][a-z0-9_-]{0,63}$', description="可选的大类目录名称,遵循技能名称约束;None 表示未指定分类")
|
||
|
||
|
||
class PatchSkill(SkillOperation):
|
||
"""
|
||
局部修改必须提供旧片段;允许明确替换所有命中。
|
||
"""
|
||
|
||
action: Literal['patch'] = Field(description="固定为 patch,表示局部修改技能文件")
|
||
old_string: str = Field(min_length=1, max_length=100_000, description="需要定位并替换的非空旧片段")
|
||
new_string: str = Field(max_length=100_000, description="替换后的新片段;空字符串表示移除匹配片段")
|
||
file_path: str | None = Field(default=None, max_length=255, description="技能目录内的目标文件相对路径;None 表示主技能文档")
|
||
replace_all: bool = Field(default=False, description="是否替换全部命中,默认 False;多处匹配时须明确选择")
|
||
|
||
|
||
class RewriteSkill(SkillOperation):
|
||
"""
|
||
完整重写形态;不能与局部修改字段混用。
|
||
"""
|
||
|
||
action: Literal['patch'] = Field(description="固定为 patch,结合 content 表示完整重写")
|
||
content: str = Field(min_length=1, max_length=100_000, description="替换原技能文档的完整非空正文")
|
||
|
||
|
||
class WriteSkillFile(SkillOperation):
|
||
"""
|
||
写入技能支持文件;不能借此更新所有权或绕过主文档校验。
|
||
"""
|
||
|
||
action: Literal['write_file'] = Field(description="固定为 write_file,表示写入技能支持文件")
|
||
file_path: str = Field(min_length=1, max_length=255, description="技能目录内的目标文件相对路径,不得越过技能边界")
|
||
file_content: str = Field(max_length=100_000, description="支持文件的完整文本内容;允许空字符串")
|
||
|
||
|
||
class RemoveSkillFile(SkillOperation):
|
||
"""
|
||
删除精确支持文件,不能递归删除目录。
|
||
"""
|
||
|
||
action: Literal['remove_file'] = Field(description="固定为 remove_file,表示删除单个支持文件")
|
||
file_path: str = Field(min_length=1, max_length=255, description="需要删除的精确支持文件相对路径,不能指向目录")
|
||
|
||
|
||
class DeleteSkill(SkillOperation):
|
||
"""
|
||
整项删除必须独占批次;合并目标可作为追溯依据。
|
||
"""
|
||
|
||
action: Literal['delete'] = Field(description="固定为 delete,删除整个个人技能;此操作必须独占批次")
|
||
absorbed_into: str | None = Field(default=None, pattern=r'^(?:[a-z0-9][a-z0-9_-]{0,63})?$', description="合并后承接内容的技能名称,作为追溯依据;未合并时可为 None 或空字符串")
|
||
|
||
|
||
class ManageSkillsInput(BaseModel):
|
||
"""
|
||
最多二十个有序操作,任一失败回滚本次全部技能变更。
|
||
"""
|
||
|
||
model_config = ConfigDict(extra='forbid')
|
||
operations: list[CreateSkill | PatchSkill | RewriteSkill | WriteSkillFile | RemoveSkillFile | DeleteSkill] = Field(
|
||
min_length=1, max_length=20, description="按顺序执行的 1 至 20 个技能操作;任一操作失败时回滚本次全部变更")
|
||
|
||
|
||
class ViewSkillInput(BaseModel):
|
||
"""
|
||
读取具体技能或支持文件,并登记本轮实际读取的版本。
|
||
"""
|
||
|
||
model_config = ConfigDict(extra='forbid')
|
||
name: str = Field(pattern=r'^[a-z0-9][a-z0-9_-]{0,63}$', description="当前宿主技能范围内的技能名称")
|
||
file_path: str = Field(default='SKILL.md', max_length=255, description="待读取文件在技能目录内的相对路径,默认 SKILL.md")
|
||
|
||
|
||
class MemoryOperation(BaseModel):
|
||
"""
|
||
old_text 只定位整条记忆,replace 的 content 会替换整条而非子串。
|
||
"""
|
||
|
||
model_config = ConfigDict(extra='forbid')
|
||
action: Literal['add', 'replace', 'remove'] = Field(description="记忆动作:add 新增、replace 整条替换、remove 删除")
|
||
content: str | None = Field(default=None, max_length=100_000, description="新增或整条替换后的记忆内容;不需要内容的操作可省略")
|
||
old_text: str | None = Field(default=None, max_length=100_000, description="用于定位整条已有记忆的原文本;不是局部子串替换规则")
|
||
new_text: str | None = Field(default=None, max_length=100_000, description="替换操作兼容的新内容字段;未提供时为 None")
|
||
|
||
|
||
class MemoryInput(BaseModel):
|
||
"""
|
||
支持单操作及原子批次形态,pending 只读列出等待人工处理的提案。
|
||
"""
|
||
|
||
model_config = ConfigDict(extra='forbid')
|
||
action: Literal['add', 'replace', 'remove', 'pending'] | None = Field(default=None, description="单操作动作:add、replace、remove 或只读的 pending;使用 operations 批次时可省略")
|
||
target: Literal['memory', 'user'] = Field(default='memory', description="目标记忆:memory 表示 MEMORY.md,user 表示当前用户的 USER.md,默认 memory")
|
||
content: str | None = Field(default=None, max_length=100_000, description="单操作新增或整条替换后的记忆内容;可省略")
|
||
old_text: str | None = Field(default=None, max_length=100_000, description="用于定位要替换或移除的整条记忆;可省略")
|
||
new_text: str | None = Field(default=None, max_length=100_000, description="单操作兼容的新内容字段;未提供时为 None")
|
||
operations: list[MemoryOperation] | None = Field(default=None, min_length=1, max_length=20, description="可选的 1 至 20 个原子记忆操作;None 表示使用单操作字段")
|