feat: Provider model_settings 全链路 + 监管节点工具集 + 重型插件注入 + 前端打磨

- Provider model_settings (Provider+Model 级别参数配置): DB JSONB → API → GSM → AgentFactory.resolve → 三节点 agent.run 注入
- 新增 data/toolset/regulatory_toolset/: 监管节点专属工具(query_workflow_status / query_task_list / send_file)
- send_file 从 interactive_toolset 迁移至 regulatory_toolset,interactive 仅保留 approval
- mcp_helper 合入 GlobalPluginManager dispatch tools
- 前端 Provider 弹窗参数设置区加 JSON 编辑器(model_settings)
- 前端 Plugin 页面新增"重型插件"Tab(HeavyPluginList 占位)
- .gitignore 精简:去除系统默认项,修复 data/ 子目录追踪
- data/toolset/ 与 data/plugin/ 首次纳入版本控制

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 13:10:31 +00:00
parent 6d658b4f4d
commit 005ce566a8
49 changed files with 1093 additions and 30 deletions
@@ -0,0 +1,9 @@
from .query_workflow_status import query_workflow_status
from .query_task_list import query_task_list
from .send_file import send_file
__all__ = [
"query_workflow_status",
"query_task_list",
"send_file",
]
@@ -0,0 +1,31 @@
{
"name": "监管节点工具集",
"version": "0.1.0",
"description": "监管节点(regulatory_node)专属能力:查询工作流、查询任务列表、发送文件",
"tools": [
{
"name": "query_workflow_status",
"file": "query_workflow_status.py",
"is_system": true,
"action_scope": ["regulatory_node"],
"config_args": {},
"category": "system"
},
{
"name": "query_task_list",
"file": "query_task_list.py",
"is_system": true,
"action_scope": ["regulatory_node"],
"config_args": {},
"category": "system"
},
{
"name": "send_file",
"file": "send_file.py",
"is_system": true,
"action_scope": ["regulatory_node"],
"config_args": {},
"category": "system"
}
]
}
@@ -0,0 +1,57 @@
"""query_task_list:列出当前用户的所有工作流任务。
regulatory_node 用以回答"我有哪些任务/正在跑什么"。返回精简后的任务列表,
不包含 graph state、context 等大字段。
"""
from typing import Any, Dict, List, Optional
from kilostar.utils.ray_hook import ray_actor_hook
async def query_task_list(
user_id: str,
status_filter: Optional[str] = None,
limit: int = 20,
) -> Dict[str, Any]:
"""列出当前用户的工作流任务。
Args:
user_id: 用户 ID(通常由调用方从对话上下文中带入)
status_filter: 可选,按状态过滤(pending/running/completed/failed
limit: 最多返回条数,默认 20
Returns:
{
"user_id": str,
"tasks": [
{"trace_id": ..., "title": ..., "status": ..., "command": ..., "created_at": ...}
],
"total": int
}
"""
pg = ray_actor_hook("postgres_database").postgres_database
workflows = await pg.list_workflows.remote(user_id) or []
tasks: List[Dict[str, Any]] = []
for wf in workflows:
status = getattr(wf, "status", None)
if status_filter and status != status_filter:
continue
tasks.append(
{
"trace_id": getattr(wf, "trace_id", None),
"title": getattr(wf, "title", None),
"status": status,
"command": getattr(wf, "command", None),
"created_at": str(getattr(wf, "created_at", "")),
}
)
if len(tasks) >= limit:
break
return {
"user_id": user_id,
"tasks": tasks,
"total": len(tasks),
}
@@ -0,0 +1,51 @@
"""query_workflow_status:查询某个 trace_id 对应工作流的最近事件。
regulatory_node 在与用户对话时,可以借此工具回答"我那个任务跑到哪一步了"
之类的问题。返回最近 N 条事件 + 当前工作流 status。
"""
from typing import Any, Dict, List
from kilostar.utils.ray_hook import ray_actor_hook
async def query_workflow_status(trace_id: str, limit: int = 10) -> Dict[str, Any]:
"""查询指定工作流 trace_id 的状态与最近事件。
Args:
trace_id: 工作流追踪 ID
limit: 返回最近多少条事件,默认 10
Returns:
{
"trace_id": str,
"status": str | None, # 工作流当前状态(pending/running/completed/failed
"title": str | None,
"recent_events": [ # 最近事件,按时间倒序
{"event_type": ..., "level": ..., "message": ..., "node_name": ..., "created_at": ...}
]
}
"""
pg = ray_actor_hook("postgres_database").postgres_database
workflow = await pg.get_workflow.remote(trace_id)
events = await pg.query_event_logs.remote(trace_id=trace_id, limit=limit)
recent: List[Dict[str, Any]] = []
for e in events or []:
recent.append(
{
"event_type": getattr(e, "event_type", None),
"level": getattr(e, "level", None),
"message": getattr(e, "message", None),
"node_name": getattr(e, "node_name", None),
"created_at": str(getattr(e, "created_at", "")),
}
)
return {
"trace_id": trace_id,
"status": getattr(workflow, "status", None) if workflow else None,
"title": getattr(workflow, "title", None) if workflow else None,
"recent_events": recent,
}
@@ -0,0 +1,63 @@
"""send_file:在对话/工作流场景下投递一份文件给用户。
regulatory_node 直接对话场景下用此工具把生成的文件发给用户:
- 工作流场景(带 trace_id):写入 data/artifact/<trace_id>/,前端通过 SSE
收到带下载链接的卡片。
- 直接对话场景(无 trace_id):退化为把文件内容拼回字符串返回给 agent,
让 agent 再以代码块形式吐给用户。
"""
import json
import re
import uuid
from pathlib import Path
from kilostar.utils.ray_hook import ray_actor_hook
from kilostar.utils.settings import get_artifact_dir
_SAFE_NAME_RE = re.compile(r"[^A-Za-z0-9._-]+")
def _sanitize_filename(name: str) -> str:
name = name.strip().replace("\\", "/").split("/")[-1]
name = _SAFE_NAME_RE.sub("_", name)
return name or "file"
async def send_file(filename: str, content: str, trace_id: str = "") -> str:
"""把 agent 生成的文件作为附件投递给用户。
Args:
filename: 文件名(含扩展名),如 "report.md" / "main.py"
content: 文件内容(UTF-8 文本)
trace_id: 当前会话/工作流的 trace_id;为空时退化为直接返回内容
Returns:
发送结果说明或文件内容
"""
if not trace_id:
return f"文件 {filename} 内容如下:\n\n```\n{content}\n```"
safe_name = _sanitize_filename(filename)
artifact_id = uuid.uuid4().hex[:12]
trace_dir: Path = get_artifact_dir() / trace_id
trace_dir.mkdir(parents=True, exist_ok=True)
file_path = trace_dir / f"{artifact_id}_{safe_name}"
file_path.write_text(content, encoding="utf-8")
payload = json.dumps(
{
"type": "file",
"filename": safe_name,
"artifact_id": artifact_id,
"url": f"/api/v1/resource/artifact/{trace_id}/{artifact_id}",
"size": len(content.encode("utf-8")),
},
ensure_ascii=False,
)
actor_list = ray_actor_hook("global_workflow_manager")
await actor_list.global_workflow_manager.put_pending.remote(
trace_id, f"__FILE__{payload}"
)
return f"已发送文件: {safe_name}"