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:
@@ -35,6 +35,8 @@ from kilostar.core.postgres_database.model.tool_config import ToolConfigModel
|
||||
from kilostar.core.postgres_database.model.custom_toolset import CustomToolsetModel
|
||||
from kilostar.core.postgres_database.model.system_event_log import SystemEventLog
|
||||
from kilostar.core.postgres_database.model.persona_template import PersonaTemplate
|
||||
from kilostar.core.postgres_database.model.org_task import OrgTask
|
||||
from kilostar.core.postgres_database.model.org_task_event import OrgTaskEvent
|
||||
|
||||
# 兼容旧代码的别名
|
||||
Provider = ProviderModel
|
||||
@@ -65,5 +67,7 @@ __all__ = [
|
||||
"CustomToolsetModel",
|
||||
"SystemEventLog",
|
||||
"PersonaTemplate",
|
||||
"OrgTask",
|
||||
"OrgTaskEvent",
|
||||
"AgentType",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
from sqlalchemy import String, DateTime, Integer, func, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from .base import BaseDataModel
|
||||
|
||||
|
||||
class OrgTask(BaseDataModel):
|
||||
__tablename__ = "org_task"
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True
|
||||
)
|
||||
task_id: Mapped[str] = mapped_column(
|
||||
String(64), unique=True, index=True, comment="外部任务 ID(UUID)"
|
||||
)
|
||||
org_name: Mapped[str] = mapped_column(
|
||||
String(128), index=True, comment="所属组织/插件名"
|
||||
)
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(20), index=True, default="pending",
|
||||
comment="pending/running/done/error"
|
||||
)
|
||||
description: Mapped[str] = mapped_column(
|
||||
Text, comment="任务描述"
|
||||
)
|
||||
result: Mapped[str | None] = mapped_column(
|
||||
Text, nullable=True, comment="最终结果"
|
||||
)
|
||||
context: Mapped[dict | None] = mapped_column(
|
||||
JSONB, nullable=True, comment="调用上下文"
|
||||
)
|
||||
created_at: Mapped[str] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), index=True
|
||||
)
|
||||
updated_at: Mapped[str] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(),
|
||||
onupdate=func.now()
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
from sqlalchemy import String, DateTime, Integer, func, Text, ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from .base import BaseDataModel
|
||||
|
||||
|
||||
class OrgTaskEvent(BaseDataModel):
|
||||
__tablename__ = "org_task_event"
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True
|
||||
)
|
||||
task_id: Mapped[str] = mapped_column(
|
||||
String(64), index=True, comment="关联的 org_task.task_id"
|
||||
)
|
||||
event_type: Mapped[str] = mapped_column(
|
||||
String(30), index=True,
|
||||
comment="log/step/artifact/approval_request/done/error"
|
||||
)
|
||||
payload: Mapped[dict | None] = mapped_column(
|
||||
JSONB, nullable=True, comment="事件负载"
|
||||
)
|
||||
created_at: Mapped[str] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), index=True
|
||||
)
|
||||
@@ -0,0 +1,119 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
from sqlalchemy import select, desc, update
|
||||
from sqlalchemy.ext.asyncio import async_sessionmaker, AsyncSession
|
||||
|
||||
from kilostar.core.postgres_database.model.org_task import OrgTask
|
||||
from kilostar.core.postgres_database.model.org_task_event import OrgTaskEvent
|
||||
from kilostar.core.postgres_database.database_exception import database_exception
|
||||
|
||||
|
||||
class OrgTaskDatabase:
|
||||
def __init__(self, async_session_maker: async_sessionmaker[AsyncSession]):
|
||||
self.async_session_maker = async_session_maker
|
||||
|
||||
@database_exception
|
||||
async def create_task(
|
||||
self,
|
||||
task_id: str,
|
||||
org_name: str,
|
||||
description: str,
|
||||
context: Optional[dict] = None,
|
||||
) -> None:
|
||||
async with self.async_session_maker() as session:
|
||||
task = OrgTask(
|
||||
task_id=task_id,
|
||||
org_name=org_name,
|
||||
description=description,
|
||||
context=context,
|
||||
status="pending",
|
||||
)
|
||||
session.add(task)
|
||||
await session.commit()
|
||||
|
||||
@database_exception
|
||||
async def update_status(
|
||||
self, task_id: str, status: str, result: Optional[str] = None
|
||||
) -> None:
|
||||
async with self.async_session_maker() as session:
|
||||
stmt = (
|
||||
update(OrgTask)
|
||||
.where(OrgTask.task_id == task_id)
|
||||
.values(status=status, result=result)
|
||||
)
|
||||
await session.execute(stmt)
|
||||
await session.commit()
|
||||
|
||||
@database_exception
|
||||
async def get_task(self, task_id: str) -> Optional[dict]:
|
||||
async with self.async_session_maker() as session:
|
||||
stmt = select(OrgTask).where(OrgTask.task_id == task_id)
|
||||
row = (await session.execute(stmt)).scalar_one_or_none()
|
||||
if not row:
|
||||
return None
|
||||
return {
|
||||
"task_id": row.task_id,
|
||||
"org_name": row.org_name,
|
||||
"status": row.status,
|
||||
"description": row.description,
|
||||
"result": row.result,
|
||||
"context": row.context,
|
||||
"created_at": str(row.created_at) if row.created_at else None,
|
||||
"updated_at": str(row.updated_at) if row.updated_at else None,
|
||||
}
|
||||
|
||||
@database_exception
|
||||
async def list_tasks(
|
||||
self, org_name: Optional[str] = None, limit: int = 50, offset: int = 0
|
||||
) -> List[dict]:
|
||||
async with self.async_session_maker() as session:
|
||||
stmt = select(OrgTask).order_by(desc(OrgTask.created_at))
|
||||
if org_name:
|
||||
stmt = stmt.where(OrgTask.org_name == org_name)
|
||||
stmt = stmt.offset(offset).limit(limit)
|
||||
rows = (await session.execute(stmt)).scalars().all()
|
||||
return [
|
||||
{
|
||||
"task_id": r.task_id,
|
||||
"org_name": r.org_name,
|
||||
"status": r.status,
|
||||
"description": r.description,
|
||||
"created_at": str(r.created_at) if r.created_at else None,
|
||||
}
|
||||
for r in rows
|
||||
]
|
||||
|
||||
@database_exception
|
||||
async def insert_event(
|
||||
self, task_id: str, event_type: str, payload: Optional[dict] = None
|
||||
) -> None:
|
||||
async with self.async_session_maker() as session:
|
||||
evt = OrgTaskEvent(
|
||||
task_id=task_id, event_type=event_type, payload=payload
|
||||
)
|
||||
session.add(evt)
|
||||
await session.commit()
|
||||
|
||||
@database_exception
|
||||
async def query_events(
|
||||
self, task_id: str, limit: int = 200
|
||||
) -> List[dict]:
|
||||
async with self.async_session_maker() as session:
|
||||
stmt = (
|
||||
select(OrgTaskEvent)
|
||||
.where(OrgTaskEvent.task_id == task_id)
|
||||
.order_by(OrgTaskEvent.created_at)
|
||||
.limit(limit)
|
||||
)
|
||||
rows = (await session.execute(stmt)).scalars().all()
|
||||
return [
|
||||
{
|
||||
"id": r.id,
|
||||
"task_id": r.task_id,
|
||||
"event_type": r.event_type,
|
||||
"payload": r.payload,
|
||||
"created_at": str(r.created_at) if r.created_at else None,
|
||||
}
|
||||
for r in rows
|
||||
]
|
||||
@@ -15,7 +15,7 @@
|
||||
import os
|
||||
import asyncio
|
||||
|
||||
from kilostar.utils.standalone_proxy import actor_class
|
||||
from kilostar.utils.ray_compat import actor_class
|
||||
from kilostar.utils.settings import get_settings
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
@@ -44,6 +44,8 @@ from kilostar.core.postgres_database.model.tool_config import ToolConfigModel
|
||||
from kilostar.core.postgres_database.model.custom_toolset import CustomToolsetModel
|
||||
from kilostar.core.postgres_database.model.system_event_log import SystemEventLog
|
||||
from kilostar.core.postgres_database.model.persona_template import PersonaTemplate
|
||||
from kilostar.core.postgres_database.model.org_task import OrgTask
|
||||
from kilostar.core.postgres_database.model.org_task_event import OrgTaskEvent
|
||||
|
||||
from .module.individual import IndividualDatabase
|
||||
from .module.user import AuthDatabase
|
||||
@@ -56,6 +58,7 @@ from .module.tool_config import ToolConfigDatabase
|
||||
from .module.custom_toolset import CustomToolsetDatabase
|
||||
from .module.system_event_log import SystemEventLogDatabase
|
||||
from .module.persona_template import PersonaTemplateDatabase
|
||||
from .module.org_task import OrgTaskDatabase
|
||||
|
||||
|
||||
@actor_class
|
||||
@@ -89,6 +92,7 @@ class PostgresDatabase:
|
||||
self._custom_toolset_database = CustomToolsetDatabase(self.async_session_maker)
|
||||
self._system_event_log_database = SystemEventLogDatabase(self.async_session_maker)
|
||||
self._persona_template_database = PersonaTemplateDatabase(self.async_session_maker)
|
||||
self._org_task_database = OrgTaskDatabase(self.async_session_maker)
|
||||
|
||||
self.ready_event = asyncio.Event()
|
||||
|
||||
@@ -458,3 +462,28 @@ class PostgresDatabase:
|
||||
async def delete_template(self, template_id: str):
|
||||
await self.ready_event.wait()
|
||||
return await self._persona_template_database.delete_template(template_id)
|
||||
|
||||
# Org Task Database Methods
|
||||
async def create_org_task(self, task_id: str, org_name: str, description: str, context=None):
|
||||
await self.ready_event.wait()
|
||||
return await self._org_task_database.create_task(task_id, org_name, description, context)
|
||||
|
||||
async def update_org_task_status(self, task_id: str, status: str, result=None):
|
||||
await self.ready_event.wait()
|
||||
return await self._org_task_database.update_status(task_id, status, result)
|
||||
|
||||
async def get_org_task(self, task_id: str):
|
||||
await self.ready_event.wait()
|
||||
return await self._org_task_database.get_task(task_id)
|
||||
|
||||
async def list_org_tasks(self, org_name=None, limit=50, offset=0):
|
||||
await self.ready_event.wait()
|
||||
return await self._org_task_database.list_tasks(org_name, limit, offset)
|
||||
|
||||
async def insert_org_event(self, task_id: str, event_type: str, payload=None):
|
||||
await self.ready_event.wait()
|
||||
return await self._org_task_database.insert_event(task_id, event_type, payload)
|
||||
|
||||
async def query_org_events(self, task_id: str, limit=200):
|
||||
await self.ready_event.wait()
|
||||
return await self._org_task_database.query_events(task_id, limit)
|
||||
|
||||
Reference in New Issue
Block a user