132 lines
5.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
pipeline-core: PipelineAbility — 产线能力包注册表
可插拔产线能力core 只提供通用 agent 内核(通用工具 + 通用心智),
每个产线SDLC 开发、未来 A/B 产线)以「能力包」形式独立注册,
包含该产线专属的工具定义、prompt 片段、工具 handler。
AgentExecutor 按 pipeline_id 从注册表动态挂载能力 → 多实例能力各异。
依赖方向:
pipeline-core (本文件,纯定义 + 注册表,不依赖任何产线)
pipeline-service sdlc_ability.py 注册 SDLC 能力,依赖 core + 底层函数)
"""
import logging
from dataclasses import dataclass, field
from typing import Callable, Dict, List, Optional
logger = logging.getLogger("pipeline.ability")
@dataclass
class RoleSpec:
"""产线内角色定义(可插拔)。
一个角色 = 工具白名单 + 专属 prompt + 专属模型 + 任务链位置 + 别名。
产线能力包通过 roles 字段定义自己的角色集(如 SDLC 的 requirement/design/develop/test/deploy
"""
name: str # "develop"
description: str = "" # 角色职责描述
aliases: List[str] = field(default_factory=list) # ["dev", "developer"]
system_prompt: str = "" # 角色专属 prompt职责 + 产出要求)
next_role: str = "" # 任务链下一角色(空 = 终结)
model_name: str = "" # 角色专属模型(空 = 继承产线)
tools: List[str] = field(default_factory=list) # 工具白名单(空 = 产线全部工具)
@dataclass
class PipelineAbility:
"""产线能力包:一个产线 = 一组工具 + 一段 prompt + 一组 handler + 一组角色。
handlers 签名统一为async def handler(sor, params, ctx) -> str
- sor: 已打开的 pipeline DB context由 AgentExecutor 统一管理)
- params: 工具参数 dict
- ctx: {"project_id", "user_id", "workspace_dir", "model_name", "config"}
"""
pipeline_id: str # 产线标识(对应 pipelines 表 id
name: str = "" # 产线名称
tools: List = field(default_factory=list) # [ToolDefinition]
system_prompt: str = "" # 产线专属 prompt 片段(追加到通用心智后)
handlers: Dict[str, Callable] = field(default_factory=dict) # {tool_name: handler}
roles: List[RoleSpec] = field(default_factory=list) # 产线角色集
menus: List[Dict] = field(default_factory=list) # 产线功能菜单AgentIO 上方)[{"label","icon","url","type"}]
# ── 全局注册表 ──
_abilities: Dict[str, PipelineAbility] = {}
def register_ability(ability: PipelineAbility):
"""注册一个产线能力包(幂等:重复注册覆盖)。"""
_abilities[ability.pipeline_id] = ability
logger.info(f"PipelineAbility registered: {ability.pipeline_id} "
f"({len(ability.tools)} tools, {len(ability.handlers)} handlers)")
def get_ability(pipeline_id: str) -> Optional[PipelineAbility]:
"""按 pipeline_id 取能力包。"""
if not pipeline_id:
return None
return _abilities.get(pipeline_id)
def list_abilities() -> List[PipelineAbility]:
"""列出所有已注册能力包。"""
return list(_abilities.values())
def get_ability_tools(pipeline_id: str) -> List:
"""取某产线的专属工具定义列表。"""
a = get_ability(pipeline_id)
return a.tools if a else []
def get_ability_handler(pipeline_id: str, tool_name: str) -> Optional[Callable]:
"""取某产线某工具的 handler。"""
a = get_ability(pipeline_id)
if not a:
return None
return a.handlers.get(tool_name)
# ── 角色查询 ──
def get_role_spec(pipeline_id: str, role: str) -> Optional[RoleSpec]:
"""取某产线某角色的定义(按 name 或 alias 匹配)。"""
a = get_ability(pipeline_id)
if not a or not role:
return None
r = (role or "").strip().lower()
for spec in a.roles:
if spec.name.lower() == r or r in [x.lower() for x in spec.aliases]:
return spec
return None
def normalize_role(pipeline_id: str, role: str) -> str:
"""角色标准化:别名 → 规范名。能力包未定义时原样返回lower"""
spec = get_role_spec(pipeline_id, role)
if spec:
return spec.name
return (role or "").strip().lower()
def get_next_role(pipeline_id: str, role: str) -> str:
"""任务链下一角色(空 = 终结)。"""
spec = get_role_spec(pipeline_id, role)
return spec.next_role if spec else ""
def list_roles(pipeline_id: str) -> List[RoleSpec]:
"""列出某产线的全部角色。"""
a = get_ability(pipeline_id)
return a.roles if a else []
def get_ability_menus(pipeline_id: str) -> List[Dict]:
"""取某产线的功能菜单AgentIO 上方,菜单/卡片/按钮)。"""
a = get_ability(pipeline_id)
return a.menus if a else []