feat: fix individual page configuration to properly display system nodes (#41)
- Added `SystemNodeConfig` database table to persist system node config (provider and model). - Updated `AgentLayout` and `WorkerIndividualSettings` to restrict config modifications to only provider and model for system nodes. - Hid delete button for system nodes and removed them from the creation modal dropdown. - Updated Agent API to support querying configured system nodes and persisting setups. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: zhaoxi826 <198742034+zhaoxi826@users.noreply.github.com>
This commit is contained in:
parent
7c841b9424
commit
3241e798ac
|
|
@ -21,7 +21,7 @@ export function AgentLayout({ agentTab, setAgentTab }: AgentLayoutProps) {
|
||||||
className={`w-full flex items-center px-4 py-3 text-sm font-medium rounded-xl transition-all ${agentTab === 'worker' ? 'bg-blue-50 text-blue-600' : 'text-slate-600 hover:bg-slate-50 hover:text-slate-900'}`}
|
className={`w-full flex items-center px-4 py-3 text-sm font-medium rounded-xl transition-all ${agentTab === 'worker' ? 'bg-blue-50 text-blue-600' : 'text-slate-600 hover:bg-slate-50 hover:text-slate-900'}`}
|
||||||
>
|
>
|
||||||
<Bot size={18} className="mr-3" />
|
<Bot size={18} className="mr-3" />
|
||||||
Worker Individual
|
Individual
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setAgentTab('providers')}
|
onClick={() => setAgentTab('providers')}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ interface WorkerIndividual {
|
||||||
export function WorkerIndividualSettings() {
|
export function WorkerIndividualSettings() {
|
||||||
const [providers, setProviders] = useState<Provider[]>([]);
|
const [providers, setProviders] = useState<Provider[]>([]);
|
||||||
const [workers, setWorkers] = useState<WorkerIndividual[]>([]);
|
const [workers, setWorkers] = useState<WorkerIndividual[]>([]);
|
||||||
|
const [systemNodes, setSystemNodes] = useState<any[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
|
||||||
|
|
@ -31,12 +32,29 @@ export function WorkerIndividualSettings() {
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const [provRes, workRes] = await Promise.all([
|
const [provRes, workRes, sysRes] = await Promise.all([
|
||||||
apiClient.get('/api/v1/provider/list'),
|
apiClient.get('/api/v1/provider/list'),
|
||||||
apiClient.get('/api/v1/agent/worker')
|
apiClient.get('/api/v1/agent/worker'),
|
||||||
|
apiClient.get('/api/v1/agent')
|
||||||
]);
|
]);
|
||||||
setProviders(Object.values(provRes.data.provider_list || {}));
|
setProviders(Object.values(provRes.data.provider_list || {}));
|
||||||
setWorkers(workRes.data.workers || []);
|
setWorkers(workRes.data.workers || []);
|
||||||
|
|
||||||
|
const sysNodesData = sysRes.data.system_nodes || [];
|
||||||
|
const defaultSysNodes = ['supervisory_node', 'consciousness_node', 'control_node'];
|
||||||
|
|
||||||
|
const formattedSysNodes = defaultSysNodes.map(nodeName => {
|
||||||
|
const found = sysNodesData.find((n: any) => n.node_name === nodeName);
|
||||||
|
return {
|
||||||
|
agent_id: nodeName,
|
||||||
|
agent_name: nodeName,
|
||||||
|
agent_type: 'System Node',
|
||||||
|
provider_title: found ? found.provider_title : 'Not Configured',
|
||||||
|
model_id: found ? found.model_id : 'Not Configured',
|
||||||
|
is_system: true
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setSystemNodes(formattedSysNodes);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
setError('Failed to load data');
|
setError('Failed to load data');
|
||||||
|
|
@ -93,17 +111,26 @@ export function WorkerIndividualSettings() {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setModalMessage('');
|
setModalMessage('');
|
||||||
try {
|
try {
|
||||||
const payload = {
|
if ((editData as any).is_system) {
|
||||||
...editData,
|
const payload = {
|
||||||
output_template: JSON.parse(editData.output_template || '{}'),
|
individual_name: editData.agent_name,
|
||||||
bound_skill: JSON.parse(editData.bound_skill || '{}'),
|
provider_title: editData.provider_title,
|
||||||
workspace: JSON.parse(editData.workspace || '[]')
|
model_id: editData.model_id
|
||||||
};
|
};
|
||||||
|
await apiClient.post('/api/v1/agent', payload);
|
||||||
if (isNew) {
|
|
||||||
await apiClient.post('/api/v1/agent/worker', payload);
|
|
||||||
} else {
|
} else {
|
||||||
await apiClient.put(`/api/v1/agent/worker/${editData.agent_id}`, payload);
|
const payload = {
|
||||||
|
...editData,
|
||||||
|
output_template: JSON.parse(editData.output_template || '{}'),
|
||||||
|
bound_skill: JSON.parse(editData.bound_skill || '{}'),
|
||||||
|
workspace: JSON.parse(editData.workspace || '[]')
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isNew) {
|
||||||
|
await apiClient.post('/api/v1/agent/worker', payload);
|
||||||
|
} else {
|
||||||
|
await apiClient.put(`/api/v1/agent/worker/${editData.agent_id}`, payload);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
|
|
@ -118,7 +145,7 @@ export function WorkerIndividualSettings() {
|
||||||
<div className="max-w-5xl space-y-6 relative">
|
<div className="max-w-5xl space-y-6 relative">
|
||||||
<div className="mb-8 flex justify-between items-end">
|
<div className="mb-8 flex justify-between items-end">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold text-slate-800">Worker Individuals</h1>
|
<h1 className="text-2xl font-bold text-slate-800">Individual</h1>
|
||||||
<p className="text-slate-500 mt-1">Manage all system nodes and custom workers.</p>
|
<p className="text-slate-500 mt-1">Manage all system nodes and custom workers.</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
|
@ -149,6 +176,22 @@ export function WorkerIndividualSettings() {
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
{systemNodes.map((w) => (
|
||||||
|
<tr key={w.agent_id} className="border-b border-slate-100 bg-slate-50 hover:bg-slate-100 transition-colors">
|
||||||
|
<td className="p-4 font-medium text-slate-800">{w.agent_name}</td>
|
||||||
|
<td className="p-4 text-slate-600">
|
||||||
|
<span className="px-2 py-1 bg-blue-100 text-blue-800 rounded text-xs">{w.agent_type}</span>
|
||||||
|
</td>
|
||||||
|
<td className="p-4 text-slate-600 text-sm">
|
||||||
|
{w.provider_title} <span className="text-slate-400">/</span> {w.model_id}
|
||||||
|
</td>
|
||||||
|
<td className="p-4 text-right space-x-2">
|
||||||
|
<button onClick={() => handleEdit(w)} className="p-2 text-indigo-600 hover:bg-indigo-50 rounded-lg transition-colors" title="Edit">
|
||||||
|
<Edit2 size={16} />
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
{workers.map((w) => (
|
{workers.map((w) => (
|
||||||
<tr key={w.agent_id} className="border-b border-slate-100 hover:bg-slate-50 transition-colors">
|
<tr key={w.agent_id} className="border-b border-slate-100 hover:bg-slate-50 transition-colors">
|
||||||
<td className="p-4 font-medium text-slate-800">{w.agent_name}</td>
|
<td className="p-4 font-medium text-slate-800">{w.agent_name}</td>
|
||||||
|
|
@ -179,7 +222,9 @@ export function WorkerIndividualSettings() {
|
||||||
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
|
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
|
||||||
<div className="bg-white rounded-xl shadow-xl w-full max-w-2xl max-h-[90vh] overflow-y-auto">
|
<div className="bg-white rounded-xl shadow-xl w-full max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||||
<div className="flex justify-between items-center p-6 border-b border-slate-100 sticky top-0 bg-white z-10">
|
<div className="flex justify-between items-center p-6 border-b border-slate-100 sticky top-0 bg-white z-10">
|
||||||
<h2 className="text-xl font-bold text-slate-800">{isNew ? 'Create Worker' : 'Edit Worker'}</h2>
|
<h2 className="text-xl font-bold text-slate-800">
|
||||||
|
{(editData as any).is_system ? 'Edit System Node' : (isNew ? 'Create Worker' : 'Edit Worker')}
|
||||||
|
</h2>
|
||||||
<button onClick={() => setIsEditing(false)} className="text-slate-400 hover:text-slate-600">
|
<button onClick={() => setIsEditing(false)} className="text-slate-400 hover:text-slate-600">
|
||||||
<X size={24} />
|
<X size={24} />
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -195,6 +240,7 @@ export function WorkerIndividualSettings() {
|
||||||
value={editData.agent_name || ''}
|
value={editData.agent_name || ''}
|
||||||
onChange={(e) => setEditData({...editData, agent_name: e.target.value})}
|
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"
|
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}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -203,13 +249,14 @@ export function WorkerIndividualSettings() {
|
||||||
value={editData.agent_type || 'OrdinaryIndividual'}
|
value={editData.agent_type || 'OrdinaryIndividual'}
|
||||||
onChange={(e) => setEditData({...editData, agent_type: e.target.value})}
|
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"
|
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}
|
||||||
>
|
>
|
||||||
<option value="supervisory_node">Supervisory Node</option>
|
|
||||||
<option value="consciousness_node">Consciousness Node</option>
|
|
||||||
<option value="control_node">Control Node</option>
|
|
||||||
<option value="OrdinaryIndividual">Ordinary Individual</option>
|
<option value="OrdinaryIndividual">Ordinary Individual</option>
|
||||||
<option value="SkillIndividual">Skill Individual</option>
|
<option value="SkillIndividual">Skill Individual</option>
|
||||||
<option value="SpecialIndividual">Special Individual</option>
|
<option value="SpecialIndividual">Special Individual</option>
|
||||||
|
{(editData as any).is_system && (
|
||||||
|
<option value="System Node">System Node</option>
|
||||||
|
)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -249,56 +296,60 @@ export function WorkerIndividualSettings() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
{!(editData as any).is_system && (
|
||||||
<label className="block text-sm font-medium text-slate-700 mb-1">Description</label>
|
<>
|
||||||
<textarea
|
<div>
|
||||||
value={editData.description || ''}
|
<label className="block text-sm font-medium text-slate-700 mb-1">Description</label>
|
||||||
onChange={(e) => setEditData({...editData, description: e.target.value})}
|
<textarea
|
||||||
rows={2}
|
value={editData.description || ''}
|
||||||
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-indigo-500"
|
onChange={(e) => setEditData({...editData, description: e.target.value})}
|
||||||
/>
|
rows={2}
|
||||||
</div>
|
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-indigo-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-slate-700 mb-1">System Prompt</label>
|
<label className="block text-sm font-medium text-slate-700 mb-1">System Prompt</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={editData.system_prompt || ''}
|
value={editData.system_prompt || ''}
|
||||||
onChange={(e) => setEditData({...editData, system_prompt: e.target.value})}
|
onChange={(e) => setEditData({...editData, system_prompt: e.target.value})}
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-indigo-500 font-mono text-sm"
|
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-indigo-500 font-mono text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-slate-700 mb-1">Output Template (JSON)</label>
|
<label className="block text-sm font-medium text-slate-700 mb-1">Output Template (JSON)</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={editData.output_template || '{}'}
|
value={editData.output_template || '{}'}
|
||||||
onChange={(e) => setEditData({...editData, output_template: e.target.value})}
|
onChange={(e) => setEditData({...editData, output_template: e.target.value})}
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-indigo-500 font-mono text-sm"
|
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-indigo-500 font-mono text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-slate-700 mb-1">Bound Skill (JSON)</label>
|
<label className="block text-sm font-medium text-slate-700 mb-1">Bound Skill (JSON)</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={editData.bound_skill || '{}'}
|
value={editData.bound_skill || '{}'}
|
||||||
onChange={(e) => setEditData({...editData, bound_skill: e.target.value})}
|
onChange={(e) => setEditData({...editData, bound_skill: e.target.value})}
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-indigo-500 font-mono text-sm"
|
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-indigo-500 font-mono text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-slate-700 mb-1">Workspace (JSON Array)</label>
|
<label className="block text-sm font-medium text-slate-700 mb-1">Workspace (JSON Array)</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={editData.workspace || '[]'}
|
value={editData.workspace || '[]'}
|
||||||
onChange={(e) => setEditData({...editData, workspace: e.target.value})}
|
onChange={(e) => setEditData({...editData, workspace: e.target.value})}
|
||||||
rows={2}
|
rows={2}
|
||||||
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-indigo-500 font-mono text-sm"
|
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-indigo-500 font-mono text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{modalMessage && (
|
{modalMessage && (
|
||||||
<div className="p-3 bg-red-50 text-red-700 text-sm rounded-lg">
|
<div className="p-3 bg-red-50 text-red-700 text-sm rounded-lg">
|
||||||
|
|
|
||||||
|
|
@ -35,15 +35,31 @@ class AgentLocalRegister(BaseModel):
|
||||||
path: str
|
path: str
|
||||||
individual_name: 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("")
|
@agent_router.post("")
|
||||||
async def load_agent(agent_register: Union[AgentRegister, AgentLocalRegister],
|
async def load_agent(agent_register: Union[AgentRegister, AgentLocalRegister],
|
||||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER))):
|
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER))):
|
||||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
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):
|
if isinstance(agent_register, AgentLocalRegister):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
elif isinstance(agent_register, AgentRegister):
|
elif isinstance(agent_register, AgentRegister):
|
||||||
try:
|
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:
|
match agent_register.individual_name:
|
||||||
case "supervisory_node":
|
case "supervisory_node":
|
||||||
node = ray_actor_hook("supervisory_node").supervisory_node
|
node = ray_actor_hook("supervisory_node").supervisory_node
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -23,6 +23,7 @@ from sqlmodel import SQLModel
|
||||||
from pretor.core.database.module.individual import IndividualDatabase
|
from pretor.core.database.module.individual import IndividualDatabase
|
||||||
from pretor.core.database.module.user import AuthDatabase
|
from pretor.core.database.module.user import AuthDatabase
|
||||||
from pretor.core.database.module.provider import ProviderDatabase
|
from pretor.core.database.module.provider import ProviderDatabase
|
||||||
|
from pretor.core.database.module.system_node import SystemNodeDatabase
|
||||||
|
|
||||||
@ray.remote
|
@ray.remote
|
||||||
class PostgresDatabase:
|
class PostgresDatabase:
|
||||||
|
|
@ -39,6 +40,7 @@ class PostgresDatabase:
|
||||||
self._auth_database = AuthDatabase(self.async_session_maker)
|
self._auth_database = AuthDatabase(self.async_session_maker)
|
||||||
self._provider_database = ProviderDatabase(self.async_session_maker)
|
self._provider_database = ProviderDatabase(self.async_session_maker)
|
||||||
self._individual_database = IndividualDatabase(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()
|
self.ready_event = asyncio.Event()
|
||||||
|
|
||||||
|
|
@ -103,6 +105,15 @@ class PostgresDatabase:
|
||||||
await self.ready_event.wait()
|
await self.ready_event.wait()
|
||||||
return await self._provider_database.update_provider(provider_id, **kwargs)
|
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
|
# Individual Database Methods
|
||||||
async def add_worker_individual(self, **kwargs):
|
async def add_worker_individual(self, **kwargs):
|
||||||
await self.ready_event.wait()
|
await self.ready_event.wait()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
Loading…
Reference in New Issue