初始化提交
This commit is contained in:
commit
887c1f449e
963 files changed
+166869
No files matched your search
@@ -0,0 +1,180 @@
|
||||
"""Skill 能力包服务(CRUD + 关键词触发匹配)。
|
||||
|
||||
CRUD 记审计;``match_active_skills`` 加载 enabled skills 调 matcher,返回 Skill ORM
|
||||
列表(供 send_message 取 prompt_fragment 注入 contract)。
|
||||
"""
|
||||
|
||||
from app.core.errors import AppError
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.skills import Skill
|
||||
from app.modules.skills.matcher import match_skills
|
||||
from app.repositories import skills as skills_repo
|
||||
from app.repositories.json_utils import dumps, loads_list
|
||||
from app.schemas.skills import SkillResponse, ToolCapability
|
||||
from app.services.audit import record_audit_event
|
||||
|
||||
|
||||
async def create_skill(
|
||||
*,
|
||||
name: str,
|
||||
description: str = "",
|
||||
trigger: str = "",
|
||||
prompt_fragment: str = "",
|
||||
tool_bindings: list[str] | None = None,
|
||||
risk_level: str = "L1",
|
||||
status: str = "enabled",
|
||||
) -> SkillResponse:
|
||||
async with AsyncSessionLocal() as session:
|
||||
existing = await skills_repo.get_skill_by_name(session, name)
|
||||
if existing is not None:
|
||||
raise AppError("conflict", f"Skill 名「{name}」已存在。")
|
||||
skill = Skill(
|
||||
name=name,
|
||||
description=description,
|
||||
trigger=trigger,
|
||||
prompt_fragment=prompt_fragment,
|
||||
tool_bindings_json=dumps(tool_bindings or []),
|
||||
risk_level=risk_level,
|
||||
status=status,
|
||||
)
|
||||
await skills_repo.add_skill(session, skill)
|
||||
await record_audit_event(
|
||||
session,
|
||||
action="skill.created",
|
||||
actor="user",
|
||||
input_summary=name[:200],
|
||||
resource_type="skill",
|
||||
resource_id=skill.id,
|
||||
)
|
||||
await session.commit()
|
||||
await session.refresh(skill)
|
||||
return _to_response(skill)
|
||||
|
||||
|
||||
async def get_skill(skill_id: str) -> SkillResponse | None:
|
||||
async with AsyncSessionLocal() as session:
|
||||
skill = await skills_repo.get_skill(session, skill_id)
|
||||
return _to_response(skill) if skill else None
|
||||
|
||||
|
||||
async def list_skills(*, status: str | None = None) -> list[SkillResponse]:
|
||||
async with AsyncSessionLocal() as session:
|
||||
skills = await skills_repo.list_skills(session, status=status)
|
||||
return [_to_response(s) for s in skills]
|
||||
|
||||
|
||||
async def list_all_capabilities(runtime) -> list[ToolCapability]:
|
||||
"""统一列举 builtin + mcp(runtime.list_specs)+ skill(DB),含来源标记。"""
|
||||
capabilities: list[ToolCapability] = []
|
||||
if runtime is not None:
|
||||
for spec in runtime.list_specs():
|
||||
source = "mcp" if spec.name.startswith("mcp:") else "builtin"
|
||||
capabilities.append(
|
||||
ToolCapability(
|
||||
name=spec.name,
|
||||
description=spec.description,
|
||||
source=source,
|
||||
risk_level=spec.risk_level,
|
||||
status="enabled",
|
||||
)
|
||||
)
|
||||
for s in await list_skills(status="enabled"):
|
||||
capabilities.append(
|
||||
ToolCapability(
|
||||
name=s.name,
|
||||
description=s.description,
|
||||
source="skill",
|
||||
risk_level=s.risk_level,
|
||||
status=s.status,
|
||||
)
|
||||
)
|
||||
return capabilities
|
||||
|
||||
|
||||
async def update_skill(
|
||||
skill_id: str,
|
||||
*,
|
||||
description: str | None = None,
|
||||
trigger: str | None = None,
|
||||
prompt_fragment: str | None = None,
|
||||
tool_bindings: list[str] | None = None,
|
||||
risk_level: str | None = None,
|
||||
status: str | None = None,
|
||||
) -> SkillResponse:
|
||||
async with AsyncSessionLocal() as session:
|
||||
skill = await skills_repo.get_skill(session, skill_id)
|
||||
if skill is None:
|
||||
raise AppError("not_found", "Skill 不存在。")
|
||||
if description is not None:
|
||||
skill.description = description
|
||||
if trigger is not None:
|
||||
skill.trigger = trigger
|
||||
if prompt_fragment is not None:
|
||||
skill.prompt_fragment = prompt_fragment
|
||||
if tool_bindings is not None:
|
||||
skill.tool_bindings_json = dumps(tool_bindings)
|
||||
if risk_level is not None:
|
||||
skill.risk_level = risk_level
|
||||
if status is not None:
|
||||
skill.status = status
|
||||
await record_audit_event(
|
||||
session,
|
||||
action="skill.updated",
|
||||
actor="user",
|
||||
input_summary=skill.name[:200],
|
||||
resource_type="skill",
|
||||
resource_id=skill.id,
|
||||
)
|
||||
await session.commit()
|
||||
await session.refresh(skill)
|
||||
return _to_response(skill)
|
||||
|
||||
|
||||
async def delete_skill(skill_id: str) -> None:
|
||||
async with AsyncSessionLocal() as session:
|
||||
skill = await skills_repo.get_skill(session, skill_id)
|
||||
if skill is None:
|
||||
raise AppError("not_found", "Skill 不存在。")
|
||||
name = skill.name
|
||||
await session.delete(skill)
|
||||
await record_audit_event(
|
||||
session,
|
||||
action="skill.deleted",
|
||||
actor="user",
|
||||
input_summary=name[:200],
|
||||
resource_type="skill",
|
||||
resource_id=skill_id,
|
||||
)
|
||||
await session.commit()
|
||||
|
||||
|
||||
async def match_active_skills(context_text: str) -> list[Skill]:
|
||||
"""加载 enabled skills,关键词匹配;返回命中的 Skill ORM(供注入 prompt_fragment)。"""
|
||||
async with AsyncSessionLocal() as session:
|
||||
skills = await skills_repo.list_skills(session, status="enabled")
|
||||
return match_skills(context_text, skills)
|
||||
|
||||
|
||||
async def get_enabled_skills_by_names(names: tuple[str, ...]) -> list[Skill]:
|
||||
"""按名称返回 enabled Skills,供 Agent 显式引用。"""
|
||||
wanted = set(names)
|
||||
if not wanted:
|
||||
return []
|
||||
async with AsyncSessionLocal() as session:
|
||||
skills = await skills_repo.list_skills(session, status="enabled")
|
||||
return [skill for skill in skills if skill.name in wanted]
|
||||
|
||||
|
||||
def _to_response(skill: Skill) -> SkillResponse:
|
||||
return SkillResponse(
|
||||
id=skill.id,
|
||||
name=skill.name,
|
||||
description=skill.description,
|
||||
trigger=skill.trigger,
|
||||
prompt_fragment=skill.prompt_fragment,
|
||||
tool_bindings=loads_list(skill.tool_bindings_json),
|
||||
risk_level=skill.risk_level,
|
||||
status=skill.status,
|
||||
created_at=skill.created_at,
|
||||
updated_at=skill.updated_at,
|
||||
)
|
||||
Reference in new issue
Block a user