| {w.agent_name} |
@@ -179,7 +222,9 @@ export function WorkerIndividualSettings() {
-
{isNew ? 'Create Worker' : 'Edit Worker'}
+
+ {(editData as any).is_system ? 'Edit System Node' : (isNew ? 'Create Worker' : 'Edit Worker')}
+
@@ -195,6 +240,7 @@ export function WorkerIndividualSettings() {
value={editData.agent_name || ''}
onChange={(e) => setEditData({...editData, agent_name: e.target.value})}
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-indigo-500"
+ disabled={(editData as any).is_system}
/>
@@ -203,13 +249,14 @@ export function WorkerIndividualSettings() {
value={editData.agent_type || 'OrdinaryIndividual'}
onChange={(e) => setEditData({...editData, agent_type: e.target.value})}
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-indigo-500"
+ disabled={(editData as any).is_system}
>
-
-
-
+ {(editData as any).is_system && (
+
+ )}
@@ -249,56 +296,60 @@ export function WorkerIndividualSettings() {
-
-
-
+ {!(editData as any).is_system && (
+ <>
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+ >
+ )}
{modalMessage && (
diff --git a/pretor/api/agent.py b/pretor/api/agent.py
index 51750bb..1d2f2e3 100644
--- a/pretor/api/agent.py
+++ b/pretor/api/agent.py
@@ -35,15 +35,31 @@ class AgentLocalRegister(BaseModel):
path: str
individual_name: str
+@agent_router.get("")
+async def get_system_nodes(_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER))):
+ postgres_database = ray_actor_hook("postgres_database").postgres_database
+ configs = await postgres_database.get_all_system_node_configs.remote()
+ return {"system_nodes": configs}
+
@agent_router.post("")
async def load_agent(agent_register: Union[AgentRegister, AgentLocalRegister],
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER))):
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
+ postgres_database = ray_actor_hook("postgres_database").postgres_database
+
if isinstance(agent_register, AgentLocalRegister):
pass
elif isinstance(agent_register, AgentRegister):
try:
+ # Persist configuration
+ await postgres_database.upsert_system_node_config.remote(
+ agent_register.individual_name,
+ agent_register.provider_title,
+ agent_register.model_id
+ )
+
+ # Load agent into state machine
match agent_register.individual_name:
case "supervisory_node":
node = ray_actor_hook("supervisory_node").supervisory_node
diff --git a/pretor/core/database/module/system_node.py b/pretor/core/database/module/system_node.py
new file mode 100644
index 0000000..df0b94a
--- /dev/null
+++ b/pretor/core/database/module/system_node.py
@@ -0,0 +1,52 @@
+# Copyright 2026 zhaoxi826
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from pretor.core.database.table.system_node import SystemNodeConfig
+from sqlmodel import select
+from typing import List, Optional
+from pretor.core.database.database_exception import database_exception
+
+class SystemNodeDatabase:
+ def __init__(self, async_session_maker):
+ self.async_session_maker = async_session_maker
+
+ @database_exception
+ async def upsert_system_node_config(self, node_name: str, provider_title: str, model_id: str) -> SystemNodeConfig:
+ async with self.async_session_maker() as session:
+ statement = select(SystemNodeConfig).where(SystemNodeConfig.node_name == node_name)
+ results = await session.execute(statement)
+ config = results.scalar_one_or_none()
+ if config:
+ config.provider_title = provider_title
+ config.model_id = model_id
+ else:
+ config = SystemNodeConfig(node_name=node_name, provider_title=provider_title, model_id=model_id)
+ session.add(config)
+ await session.commit()
+ await session.refresh(config)
+ return config
+
+ @database_exception
+ async def get_all_system_node_configs(self) -> List[SystemNodeConfig]:
+ async with self.async_session_maker() as session:
+ statement = select(SystemNodeConfig)
+ results = await session.execute(statement)
+ return list(results.scalars().all())
+
+ @database_exception
+ async def get_system_node_config(self, node_name: str) -> Optional[SystemNodeConfig]:
+ async with self.async_session_maker() as session:
+ statement = select(SystemNodeConfig).where(SystemNodeConfig.node_name == node_name)
+ results = await session.execute(statement)
+ return results.scalar_one_or_none()
diff --git a/pretor/core/database/postgres.py b/pretor/core/database/postgres.py
index 5806810..543ebb1 100644
--- a/pretor/core/database/postgres.py
+++ b/pretor/core/database/postgres.py
@@ -23,6 +23,7 @@ from sqlmodel import SQLModel
from pretor.core.database.module.individual import IndividualDatabase
from pretor.core.database.module.user import AuthDatabase
from pretor.core.database.module.provider import ProviderDatabase
+from pretor.core.database.module.system_node import SystemNodeDatabase
@ray.remote
class PostgresDatabase:
@@ -39,6 +40,7 @@ class PostgresDatabase:
self._auth_database = AuthDatabase(self.async_session_maker)
self._provider_database = ProviderDatabase(self.async_session_maker)
self._individual_database = IndividualDatabase(self.async_session_maker)
+ self._system_node_database = SystemNodeDatabase(self.async_session_maker)
self.ready_event = asyncio.Event()
@@ -103,6 +105,15 @@ class PostgresDatabase:
await self.ready_event.wait()
return await self._provider_database.update_provider(provider_id, **kwargs)
+ # System Node Database Methods
+ async def upsert_system_node_config(self, node_name: str, provider_title: str, model_id: str):
+ await self.ready_event.wait()
+ return await self._system_node_database.upsert_system_node_config(node_name, provider_title, model_id)
+
+ async def get_all_system_node_configs(self):
+ await self.ready_event.wait()
+ return await self._system_node_database.get_all_system_node_configs()
+
# Individual Database Methods
async def add_worker_individual(self, **kwargs):
await self.ready_event.wait()
diff --git a/pretor/core/database/table/system_node.py b/pretor/core/database/table/system_node.py
new file mode 100644
index 0000000..9a062a2
--- /dev/null
+++ b/pretor/core/database/table/system_node.py
@@ -0,0 +1,21 @@
+# Copyright 2026 zhaoxi826
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from sqlmodel import SQLModel, Field
+
+class SystemNodeConfig(SQLModel, table=True):
+ __tablename__ = "system_node_config"
+ node_name: str = Field(primary_key=True)
+ provider_title: str
+ model_id: str