feat: 工具系统迁移 + 重型插件骨架 + 前端交互增强

- 工具系统从 kilostar/plugin/tool_plugin/ 迁移到 data/toolset/(manifest.json 声明式)
- 新增 plugin_runtime 模块:BaseOrganization / GlobalPluginManager / loader / tool_bridge
- 新增 org_task + org_task_event 表及 DAO(alembic 0009)
- 新增 /api/v1/plugin 路由(submit/status/stream/install/reload)
- 新增 data/plugin/example_dept 示例重型插件
- regulatory_node 支持聊天历史上下文注入
- send_file 改为 artifact 存盘 + SSE 推送下载链接
- 前端 WorkflowFileCard 组件 + ToolSettings README 渲染
- utils 整理:合并 access/role_check、standalone_proxy→ray_compat、删除废弃模块
- 项目结构文档移至 docs/STRUCTURE.md 并详细展开

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 05:20:00 +00:00
parent 9b73ae4db4
commit 6d658b4f4d
74 changed files with 2591 additions and 1308 deletions
+63
View File
@@ -0,0 +1,63 @@
"""Tests for the heavy plugin (Organization) runtime."""
import json
from pathlib import Path
from kilostar.plugin_runtime.manifest import OrgManifest
from kilostar.plugin_runtime.agents_config import AgentsConfig
from kilostar.plugin_runtime.loader import discover_plugins, load_plugin
from kilostar.plugin_runtime.tool_bridge import make_dispatch_tool
_PLUGIN_ROOT = Path(__file__).parent.parent.parent / "data" / "plugin"
_EXAMPLE_DEPT = _PLUGIN_ROOT / "example_dept"
def test_example_dept_structure():
"""example_dept stub has the required files."""
assert (_EXAMPLE_DEPT / "manifest.json").exists()
assert (_EXAMPLE_DEPT / "agents.json").exists()
assert (_EXAMPLE_DEPT / "README.md").exists()
assert (_EXAMPLE_DEPT / "core" / "organization.py").exists()
def test_example_dept_manifest_valid():
with open(_EXAMPLE_DEPT / "manifest.json", "r", encoding="utf-8") as f:
data = json.load(f)
manifest = OrgManifest.model_validate(data)
assert manifest.name == "example_dept"
assert manifest.actor_name == "org_example_dept"
assert manifest.entry == "core.organization:ExampleOrganization"
def test_example_dept_agents_valid():
with open(_EXAMPLE_DEPT / "agents.json", "r", encoding="utf-8") as f:
data = json.load(f)
config = AgentsConfig.model_validate(data)
assert len(config.agents) == 2
names = {a.name for a in config.agents}
assert names == {"analyst", "executor"}
assert config.orchestration.entry == "analyst"
analyst = config.get("analyst")
assert analyst is not None
assert "executor" in analyst.peers
def test_discover_plugins_finds_example_dept():
plugins = discover_plugins(_PLUGIN_ROOT)
names = {p.name for p in plugins}
assert "example_dept" in names
def test_load_plugin_returns_class():
cls, manifest_dict, agents_dict, dir_str = load_plugin(_EXAMPLE_DEPT)
assert cls.__name__ == "ExampleOrganization"
assert manifest_dict["name"] == "example_dept"
assert "agents" in agents_dict
assert dir_str == str(_EXAMPLE_DEPT)
def test_make_dispatch_tool_signature():
tool = make_dispatch_tool("example_dept", "示例部门", "演示用")
assert tool.__name__ == "dispatch_to_example_dept"
assert callable(tool)
assert "演示用" in tool.__doc__