feat: v0.1.1 迭代——人设外键重构、Chat UI优化、意识节点防幻觉、日志双视图

1. 人设外键重构:persona_template 成为 system_prompt 唯一权威来源,
   agent/系统节点通过 persona_id FK 引用,含数据迁移脚本
2. Chat UI:去掉底部AI提示、加号改为弹出菜单、新建对话乐观跳转
3. 意识节点:无可用worker时禁止编造agent_id,只能自行完成或拒绝
4. 日志页面:双tab布局(系统日志 + 工作流日志列表选择)
5. 其他:SSE流式聊天、对话删除/重命名、standalone模式修复

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-05 06:18:47 +00:00
parent e3b8686d45
commit 6f1bc27101
39 changed files with 2904 additions and 524 deletions
@@ -37,14 +37,13 @@ class BaseIndividualModel(BaseDataModel):
agent_id: Mapped[str] = mapped_column(String(64), primary_key=True)
agent_name: Mapped[str] = mapped_column(String(100), index=True, nullable=False)
description: Mapped[str] = mapped_column(Text, nullable=False)
system_prompt: Mapped[Optional[str]] = mapped_column(Text)
provider_title: Mapped[str] = mapped_column(String(50))
model_id: Mapped[str] = mapped_column(String(100))
owner_id: Mapped[str] = mapped_column(String(64), index=True)
agent_type: Mapped[str] = mapped_column(String(32))
node_affinity: Mapped[str] = mapped_column(String(32), nullable=False, default="cpu")
template_origin_id: Mapped[Optional[str]] = mapped_column(
persona_id: Mapped[Optional[str]] = mapped_column(
ForeignKey("persona_template.template_id", ondelete="SET NULL"),
nullable=True,
index=True,
@@ -1,6 +1,5 @@
from typing import List, Optional
from sqlalchemy import String, Text, Boolean, text
from sqlalchemy.dialects.postgresql import JSONB
from typing import Optional
from sqlalchemy import String, Text
from sqlalchemy.orm import Mapped, mapped_column
from .base import BaseDataModel
@@ -11,16 +10,5 @@ class PersonaTemplate(BaseDataModel):
template_id: Mapped[str] = mapped_column(String(64), primary_key=True)
name: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
description: Mapped[str] = mapped_column(Text, nullable=False, default="")
system_prompt: Mapped[str] = mapped_column(Text, nullable=False, default="")
agent_type: Mapped[str] = mapped_column(String(32), nullable=False, default="ordinary")
provider_title: Mapped[Optional[str]] = mapped_column(String(50))
model_id: Mapped[Optional[str]] = mapped_column(String(100))
tools: Mapped[Optional[List[str]]] = mapped_column(
JSONB, default=list, server_default=text("'[]'::jsonb")
)
tags: Mapped[Optional[List[str]]] = mapped_column(
JSONB, default=list, server_default=text("'[]'::jsonb")
)
is_builtin: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
owner_id: Mapped[Optional[str]] = mapped_column(String(64), index=True)
@@ -13,7 +13,7 @@
# limitations under the License.
from typing import List, Optional
from sqlalchemy import String, Text
from sqlalchemy import String, ForeignKey
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped, mapped_column
from .base import BaseDataModel
@@ -35,6 +35,8 @@ class SystemNodeConfigModel(BaseDataModel):
tools: Mapped[Optional[List[str]]] = mapped_column(
JSONB, default=list, comment="节点可调用的工具标识列表"
)
custom_system_prompt: Mapped[Optional[str]] = mapped_column(
Text, nullable=True, comment="管理员自定义追加的提示词,拼接在默认 system prompt 之后"
persona_id: Mapped[Optional[str]] = mapped_column(
ForeignKey("persona_template.template_id", ondelete="SET NULL"),
nullable=True,
index=True,
)
@@ -85,3 +85,29 @@ class ChatHistoryDatabase:
)
results = await session.execute(statement)
return results.scalars().all()
@database_exception
async def get_chat_session(self, chat_id: str) -> ChatHistoryRegister | None:
async with self.async_session_maker() as session:
statement = select(ChatHistoryRegister).where(
ChatHistoryRegister.chat_id == chat_id
)
results = await session.execute(statement)
return results.scalar_one_or_none()
@database_exception
async def delete_chat_session(self, chat_id: str) -> None:
from sqlalchemy import delete as sa_delete
async with self.async_session_maker() as session:
await session.execute(
sa_delete(ChatHistoryMessage).where(
ChatHistoryMessage.chat_id == chat_id
)
)
await session.execute(
sa_delete(ChatHistoryRegister).where(
ChatHistoryRegister.chat_id == chat_id
)
)
await session.commit()
@@ -27,18 +27,11 @@ class PersonaTemplateDatabase:
return result.scalar_one_or_none()
@database_exception
async def list_templates(self, owner_id: str = None, include_builtin: bool = True):
async def list_templates(self, owner_id: str = None, **kwargs):
async with self.async_session_maker() as session:
stmt = select(PersonaTemplate)
if owner_id and include_builtin:
from sqlalchemy import or_
stmt = stmt.where(
or_(PersonaTemplate.owner_id == owner_id, PersonaTemplate.is_builtin == True)
)
elif owner_id:
if owner_id:
stmt = stmt.where(PersonaTemplate.owner_id == owner_id)
elif include_builtin:
stmt = stmt.where(PersonaTemplate.is_builtin == True)
result = await session.execute(stmt)
return list(result.scalars().all())
@@ -93,6 +93,19 @@ class ProviderDatabase:
session.delete(provider)
await session.commit()
@database_exception
async def delete_provider_by_title(self, provider_title: str) -> None:
"""按 provider_title 删除 Provider;不存在时静默返回。"""
async with self.async_session_maker() as session:
statement = select(ProviderModel).where(
ProviderModel.provider_title == provider_title
)
result = await session.execute(statement)
provider = result.scalar_one_or_none()
if provider is not None:
await session.delete(provider)
await session.commit()
@database_exception
async def update_provider(self, provider_id: str, **kwargs) -> None:
"""部分更新指定 Provider 的字段;``provider_apikey`` 写入前自动加密。"""
@@ -31,7 +31,7 @@ class SystemNodeDatabase:
provider_title: str,
model_id: str,
tools: Optional[List[str]] = None,
custom_system_prompt: Optional[str] = None,
persona_id: Optional[str] = None,
display_name: Optional[str] = None,
) -> SystemNodeConfigModel:
"""按 node_name 插入或更新一个系统节点的模型配置。"""
@@ -46,8 +46,8 @@ class SystemNodeDatabase:
config.model_id = model_id
if tools is not None:
config.tools = tools
if custom_system_prompt is not None:
config.custom_system_prompt = custom_system_prompt
if persona_id is not None:
config.persona_id = persona_id
if display_name is not None:
config.display_name = display_name
else:
@@ -56,7 +56,7 @@ class SystemNodeDatabase:
provider_title=provider_title,
model_id=model_id,
tools=tools,
custom_system_prompt=custom_system_prompt,
persona_id=persona_id,
display_name=display_name,
)
session.add(config)
+24 -4
View File
@@ -171,6 +171,11 @@ class PostgresDatabase:
await self.ready_event.wait()
return await self._provider_database.delete_provider(provider_id)
async def delete_provider_by_title(self, provider_title: str):
"""按 provider_title 删除模型 Provider 记录。"""
await self.ready_event.wait()
return await self._provider_database.delete_provider_by_title(provider_title)
async def update_provider_db(self, provider_id: str, **kwargs):
"""部分更新指定 Provider 的字段。"""
await self.ready_event.wait()
@@ -183,13 +188,13 @@ class PostgresDatabase:
provider_title: str,
model_id: str,
tools: list[str] = None,
custom_system_prompt: str = None,
persona_id: str = None,
display_name: str = None,
):
"""插入或更新某个系统节点(如 consciousness/regulatory)的模型配置。"""
await self.ready_event.wait()
return await self._system_node_database.upsert_system_node_config(
node_name, provider_title, model_id, tools, custom_system_prompt, display_name
node_name, provider_title, model_id, tools, persona_id, display_name
)
async def get_all_system_node_configs(self):
@@ -197,6 +202,11 @@ class PostgresDatabase:
await self.ready_event.wait()
return await self._system_node_database.get_all_system_node_configs()
async def get_system_node_config(self, node_name: str):
"""按 node_name 取出单个系统节点的模型配置。"""
await self.ready_event.wait()
return await self._system_node_database.get_system_node_config(node_name)
# Individual Database Methods
async def add_worker_individual(self, **kwargs):
"""登记一个新的 Worker Individual 配置。"""
@@ -306,6 +316,16 @@ class PostgresDatabase:
await self.ready_event.wait()
return await self._chat_history_database.list_chat_messages(chat_id)
async def get_chat_session(self, chat_id: str):
"""按 chat_id 取出单个聊天会话。"""
await self.ready_event.wait()
return await self._chat_history_database.get_chat_session(chat_id)
async def delete_chat_session(self, chat_id: str):
"""删除聊天会话及其所有消息。"""
await self.ready_event.wait()
return await self._chat_history_database.delete_chat_session(chat_id)
# MCP Server Database Methods
async def upsert_mcp_server(self, server_id: str, config: dict):
"""插入或更新一条 MCP 服务器配置;env 中敏感字段自动加密。"""
@@ -423,9 +443,9 @@ class PostgresDatabase:
await self.ready_event.wait()
return await self._persona_template_database.get_template(template_id)
async def list_templates(self, owner_id: str = None, include_builtin: bool = True):
async def list_templates(self, owner_id: str = None, **kwargs):
await self.ready_event.wait()
return await self._persona_template_database.list_templates(owner_id, include_builtin)
return await self._persona_template_database.list_templates(owner_id=owner_id)
async def update_template(self, template_id: str, **kwargs):
await self.ready_event.wait()