mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-26 12:17:14 +00:00
refactor(agent): remove enabled state
This commit is contained in:
@@ -34,7 +34,6 @@ class Agent(Base):
|
||||
kind: str # 固定为 "agent"
|
||||
component_ref: str # AgentRunner id
|
||||
config: dict # runner + runner_config
|
||||
enabled: bool
|
||||
supported_event_patterns: list[str]
|
||||
```
|
||||
|
||||
@@ -113,7 +112,7 @@ Binding 只保存引用与路由条件。它不复制 Pipeline 或 Agent 配置
|
||||
|
||||
1. 忽略 `enabled = false` 的 binding。
|
||||
2. 检查 `event_pattern` 与结构化 filters。
|
||||
3. 校验目标存在、启用且声明支持该事件。
|
||||
3. 校验目标存在且声明支持该事件。
|
||||
4. 按 `priority` 从高到低选择;同优先级按稳定列表顺序。
|
||||
5. 只执行一个响应目标。
|
||||
|
||||
|
||||
@@ -70,7 +70,6 @@ class Agent(Base):
|
||||
kind: str # 首版固定为 "agent"
|
||||
component_ref: str # runner id / workflow id / future external ref
|
||||
config: dict # runner 与 runner_config
|
||||
enabled: bool
|
||||
supported_event_patterns: list[str]
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
@@ -22,7 +22,7 @@ class AgentBindingResolver:
|
||||
event: AgentEventEnvelope,
|
||||
agents: list[AgentConfig],
|
||||
) -> AgentBinding:
|
||||
"""Resolve exactly one enabled Agent for the event.
|
||||
"""Resolve exactly one Agent for the event.
|
||||
|
||||
Callers that source agents from bot/workspace/global configuration must
|
||||
pre-filter candidates to the event scope before calling this resolver.
|
||||
@@ -30,7 +30,7 @@ class AgentBindingResolver:
|
||||
Agent and does not carry enough scope metadata to make that decision
|
||||
safely here.
|
||||
"""
|
||||
matches = [agent for agent in agents if agent.enabled and event.event_type in agent.event_types]
|
||||
matches = [agent for agent in agents if event.event_type in agent.event_types]
|
||||
|
||||
if not matches:
|
||||
raise AgentBindingResolutionError(f'No Agent binding matches event_type={event.event_type}')
|
||||
@@ -59,7 +59,6 @@ class AgentBindingResolver:
|
||||
resource_policy=agent.resource_policy,
|
||||
state_policy=agent.state_policy,
|
||||
delivery_policy=agent.delivery_policy,
|
||||
enabled=agent.enabled,
|
||||
agent_id=agent.agent_id,
|
||||
processor_type=agent.processor_type,
|
||||
processor_id=agent.processor_id or agent.agent_id,
|
||||
|
||||
@@ -181,9 +181,6 @@ class AgentConfig(pydantic.BaseModel):
|
||||
event_types: list[str] = pydantic.Field(default_factory=lambda: ['message.received'])
|
||||
"""Event types this Agent handles."""
|
||||
|
||||
enabled: bool = True
|
||||
"""Whether this Agent can be selected by a binding resolver."""
|
||||
|
||||
metadata: dict[str, typing.Any] = pydantic.Field(default_factory=dict)
|
||||
"""Non-protocol diagnostic metadata, such as legacy config source."""
|
||||
|
||||
@@ -219,9 +216,6 @@ class AgentBinding(pydantic.BaseModel):
|
||||
delivery_policy: DeliveryPolicy = pydantic.Field(default_factory=DeliveryPolicy)
|
||||
"""Delivery policy."""
|
||||
|
||||
enabled: bool = True
|
||||
"""Whether binding is enabled."""
|
||||
|
||||
agent_id: str | None = None
|
||||
"""Host-side Agent/config identifier for this binding."""
|
||||
|
||||
|
||||
@@ -151,7 +151,6 @@ class QueryEntryAdapter:
|
||||
state_policy=state_policy,
|
||||
delivery_policy=delivery_policy,
|
||||
event_types=[event_type],
|
||||
enabled=True,
|
||||
metadata={'source': 'pipeline_adapter'},
|
||||
)
|
||||
|
||||
|
||||
@@ -201,7 +201,6 @@ class AgentService:
|
||||
enable_reply=False,
|
||||
enable_interactions=False,
|
||||
),
|
||||
enabled=True,
|
||||
agent_id=agent_uuid,
|
||||
processor_type='agent',
|
||||
processor_id=agent_uuid,
|
||||
@@ -293,7 +292,6 @@ class AgentService:
|
||||
'kind': AGENT_KIND_AGENT,
|
||||
'component_ref': runner_id,
|
||||
'config': config,
|
||||
'enabled': agent_data.get('enabled', True),
|
||||
'supported_event_patterns': agent_data.get('supported_event_patterns') or AGENT_DEFAULT_EVENT_PATTERNS,
|
||||
}
|
||||
await self.ap.persistence_mgr.execute_async(sqlalchemy.insert(persistence_agent.Agent).values(**values))
|
||||
@@ -308,17 +306,11 @@ class AgentService:
|
||||
await self.ap.pipeline_service.update_pipeline(context, agent_uuid, agent_data)
|
||||
return
|
||||
|
||||
update_data = agent_data.copy()
|
||||
for protected_field in (
|
||||
'uuid',
|
||||
'workspace_uuid',
|
||||
'kind',
|
||||
'component_ref',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'capability',
|
||||
):
|
||||
update_data.pop(protected_field, None)
|
||||
update_data = {
|
||||
field: agent_data[field]
|
||||
for field in ('name', 'description', 'emoji', 'config', 'supported_event_patterns')
|
||||
if field in agent_data
|
||||
}
|
||||
if 'config' in update_data:
|
||||
config, runner_id, _ = RunnerConfigResolver.resolve_agent_runner_config(update_data['config'])
|
||||
update_data['config'] = config
|
||||
@@ -425,7 +417,6 @@ class AgentService:
|
||||
item = pipeline.copy()
|
||||
item['kind'] = AGENT_KIND_PIPELINE
|
||||
item['component_ref'] = 'pipeline'
|
||||
item['enabled'] = True
|
||||
item['supported_event_patterns'] = PIPELINE_EVENT_PATTERNS
|
||||
item['capability'] = {
|
||||
'supported_event_patterns': PIPELINE_EVENT_PATTERNS,
|
||||
|
||||
@@ -18,7 +18,6 @@ class BotService:
|
||||
|
||||
ap: app.Application
|
||||
FAILURE_ROUTE_NOT_FOUND = 'route_not_found'
|
||||
FAILURE_PROCESSOR_DISABLED = 'processor_disabled'
|
||||
FAILURE_PROCESSOR_NOT_FOUND = 'processor_not_found'
|
||||
FAILURE_PROCESSOR_INCOMPATIBLE = 'processor_incompatible'
|
||||
FAILURE_INVALID_EVENT = 'invalid_event'
|
||||
@@ -436,25 +435,6 @@ class BotService:
|
||||
}
|
||||
],
|
||||
)
|
||||
if not getattr(agent, 'enabled', True):
|
||||
return self._diagnostic_result(
|
||||
matched=False,
|
||||
binding=selected_binding,
|
||||
failure_code=self.FAILURE_PROCESSOR_DISABLED,
|
||||
reason='Agent target is disabled',
|
||||
diagnostic_steps=diagnostic_steps
|
||||
+ [
|
||||
{
|
||||
'step': 'validate_processor',
|
||||
'binding_id': selected_binding.get('id'),
|
||||
'target_type': target_type,
|
||||
'target_uuid': target_uuid,
|
||||
'matched': False,
|
||||
'failure_code': self.FAILURE_PROCESSOR_DISABLED,
|
||||
'reason': 'Agent target is disabled',
|
||||
}
|
||||
],
|
||||
)
|
||||
if not RuntimeBot._agent_supports_event_type(getattr(agent, 'supported_event_patterns', None), event_type):
|
||||
return self._diagnostic_result(
|
||||
matched=False,
|
||||
|
||||
@@ -20,7 +20,6 @@ class Agent(Base):
|
||||
kind = sqlalchemy.Column(sqlalchemy.String(50), nullable=False, default='agent')
|
||||
component_ref = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)
|
||||
config = sqlalchemy.Column(sqlalchemy.JSON, nullable=False, default={})
|
||||
enabled = sqlalchemy.Column(sqlalchemy.Boolean, nullable=False, default=True)
|
||||
supported_event_patterns = sqlalchemy.Column(sqlalchemy.JSON, nullable=False, default=['*'])
|
||||
created_at = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, server_default=sqlalchemy.func.now())
|
||||
updated_at = sqlalchemy.Column(
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
"""Drop the obsolete Agent enabled state.
|
||||
|
||||
Revision ID: 0023_drop_agent_enabled
|
||||
Revises: 0022_merge_agent_reasoning_heads
|
||||
Create Date: 2026-08-25
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision = '0023_drop_agent_enabled'
|
||||
down_revision = '0022_merge_agent_reasoning_heads'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def _column_exists(inspector: sa.Inspector, table_name: str, column_name: str) -> bool:
|
||||
if table_name not in inspector.get_table_names():
|
||||
return False
|
||||
return any(column['name'] == column_name for column in inspector.get_columns(table_name))
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
inspector = sa.inspect(op.get_bind())
|
||||
if _column_exists(inspector, 'agents', 'enabled'):
|
||||
with op.batch_alter_table('agents') as batch_op:
|
||||
batch_op.drop_column('enabled')
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
inspector = sa.inspect(op.get_bind())
|
||||
if 'agents' in inspector.get_table_names() and not _column_exists(inspector, 'agents', 'enabled'):
|
||||
with op.batch_alter_table('agents') as batch_op:
|
||||
batch_op.add_column(sa.Column('enabled', sa.Boolean(), nullable=False, server_default=sa.true()))
|
||||
@@ -1135,7 +1135,6 @@ class RuntimeBot:
|
||||
enable_reply=True,
|
||||
enable_interactions=True,
|
||||
),
|
||||
enabled=True,
|
||||
agent_id=agent.get('uuid'),
|
||||
processor_type='agent',
|
||||
processor_id=agent.get('uuid'),
|
||||
@@ -1322,17 +1321,6 @@ class RuntimeBot:
|
||||
reason='Agent target not found',
|
||||
text=f'EBA event {event_type} target agent not found: {target_uuid}',
|
||||
)
|
||||
if not agent.get('enabled', True):
|
||||
return await self._record_event_route_trace(
|
||||
event_type=event_type,
|
||||
status='failed',
|
||||
binding=event_binding,
|
||||
target_type=target_type,
|
||||
target_uuid=target_uuid,
|
||||
failure_code='processor_disabled',
|
||||
reason='Agent target is disabled',
|
||||
text=f'EBA event {event_type} target agent disabled: {target_uuid}',
|
||||
)
|
||||
if not self._agent_supports_event_type(agent.get('supported_event_patterns'), event_type):
|
||||
return await self._record_event_route_trace(
|
||||
event_type=event_type,
|
||||
@@ -1711,7 +1699,7 @@ class RuntimeBot:
|
||||
self.execution_context,
|
||||
record['processor_id'],
|
||||
)
|
||||
if not agent or agent.get('kind') != 'agent' or not agent.get('enabled', True):
|
||||
if not agent or agent.get('kind') != 'agent':
|
||||
raise ValueError(f'Interaction target Agent is unavailable: {record["processor_id"]}')
|
||||
|
||||
binding = self._agent_product_to_binding(
|
||||
@@ -1810,9 +1798,7 @@ class RuntimeBot:
|
||||
def tenant_scoped_listener(listener):
|
||||
@functools.wraps(listener)
|
||||
async def wrapped(*args, **kwargs):
|
||||
tenant_scope = getattr(
|
||||
self.ap.persistence_mgr, 'tenant_scope', None
|
||||
)
|
||||
tenant_scope = getattr(self.ap.persistence_mgr, 'tenant_scope', None)
|
||||
cloud_runtime = (
|
||||
getattr(
|
||||
getattr(self.ap.persistence_mgr, 'mode', None),
|
||||
@@ -1823,9 +1809,7 @@ class RuntimeBot:
|
||||
)
|
||||
if cloud_runtime:
|
||||
if not callable(tenant_scope):
|
||||
raise RuntimeError(
|
||||
'Cloud platform callbacks require a tenant scope'
|
||||
)
|
||||
raise RuntimeError('Cloud platform callbacks require a tenant scope')
|
||||
async with tenant_scope(self.workspace_uuid):
|
||||
return await listener(*args, **kwargs)
|
||||
return await listener(*args, **kwargs)
|
||||
|
||||
@@ -108,7 +108,7 @@ class TestSQLiteMigrationUpgrade:
|
||||
await run_alembic_upgrade(sqlite_engine, 'head')
|
||||
|
||||
assert await get_alembic_current(sqlite_engine) == _get_script_head()
|
||||
assert _get_script_head() == '0022_merge_agent_reasoning_heads'
|
||||
assert _get_script_head() == '0023_drop_agent_enabled'
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_upgrade_from_development_workspace_head_to_merged_head(self, sqlite_engine):
|
||||
@@ -120,7 +120,7 @@ class TestSQLiteMigrationUpgrade:
|
||||
await run_alembic_upgrade(sqlite_engine, 'head')
|
||||
|
||||
assert await get_alembic_current(sqlite_engine) == _get_script_head()
|
||||
assert _get_script_head() == '0022_merge_agent_reasoning_heads'
|
||||
assert _get_script_head() == '0023_drop_agent_enabled'
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_upgrade_from_reasoning_config_head_to_merged_head(self, sqlite_engine):
|
||||
@@ -131,7 +131,23 @@ class TestSQLiteMigrationUpgrade:
|
||||
await run_alembic_stamp(sqlite_engine, '0018_llm_reasoning_config')
|
||||
await run_alembic_upgrade(sqlite_engine, 'head')
|
||||
|
||||
assert await get_alembic_current(sqlite_engine) == '0022_merge_agent_reasoning_heads'
|
||||
assert await get_alembic_current(sqlite_engine) == '0023_drop_agent_enabled'
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_upgrade_removes_agent_enabled_column(self, sqlite_engine):
|
||||
async with sqlite_engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
await conn.exec_driver_sql('ALTER TABLE agents ADD COLUMN enabled BOOLEAN NOT NULL DEFAULT 1')
|
||||
|
||||
await run_alembic_stamp(sqlite_engine, '0022_merge_agent_reasoning_heads')
|
||||
await run_alembic_upgrade(sqlite_engine, 'head')
|
||||
|
||||
async with sqlite_engine.connect() as conn:
|
||||
columns = await conn.run_sync(
|
||||
lambda sync_conn: {column['name'] for column in sqlalchemy.inspect(sync_conn).get_columns('agents')}
|
||||
)
|
||||
|
||||
assert 'enabled' not in columns
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_upgrade_from_baseline_to_head(self, sqlite_engine):
|
||||
|
||||
@@ -74,7 +74,6 @@ class TestContextValidation:
|
||||
runner_id='plugin:test/plugin/runner',
|
||||
runner_config={'timeout': 300},
|
||||
agent_id='pipeline_1',
|
||||
enabled=True,
|
||||
)
|
||||
|
||||
def _make_resources(self) -> BuilderResources:
|
||||
|
||||
@@ -1069,7 +1069,6 @@ class TestQueryEntrySessionQueryId:
|
||||
resource_policy=ResourcePolicy(),
|
||||
state_policy=StatePolicy(enable_state=False, state_scopes=[]),
|
||||
delivery_policy=DeliveryPolicy(enable_streaming=True, enable_reply=True),
|
||||
enabled=True,
|
||||
)
|
||||
|
||||
messages = [
|
||||
|
||||
@@ -46,7 +46,6 @@ def _agent_row(
|
||||
'runner': {'id': 'plugin:test/runner/default', 'expire-time': 0},
|
||||
'runner_config': {'plugin:test/runner/default': {'temperature': 0.2}},
|
||||
},
|
||||
enabled=True,
|
||||
supported_event_patterns=supported_event_patterns or ['*'],
|
||||
created_at=dt.datetime(2026, 1, 1, 9, 0, 0),
|
||||
updated_at=updated_at or dt.datetime(2026, 1, 1, 10, 0, 0),
|
||||
@@ -63,7 +62,6 @@ def _serialize_agent(model_cls, entity, masked_columns=None):
|
||||
'kind': entity.kind,
|
||||
'component_ref': entity.component_ref,
|
||||
'config': entity.config,
|
||||
'enabled': entity.enabled,
|
||||
'supported_event_patterns': entity.supported_event_patterns,
|
||||
'created_at': entity.created_at,
|
||||
'updated_at': entity.updated_at,
|
||||
@@ -145,7 +143,6 @@ class TestAgentServiceDebug:
|
||||
return_value={
|
||||
'uuid': 'agent-1',
|
||||
'kind': AGENT_KIND_AGENT,
|
||||
'enabled': True,
|
||||
'supported_event_patterns': ['*'],
|
||||
'config': _agent_row().config,
|
||||
}
|
||||
@@ -288,7 +285,7 @@ class TestAgentServiceListAndLookup:
|
||||
result = await AgentService(app).get_agent(WORKSPACE_UUID, 'pipeline-1')
|
||||
|
||||
assert result['kind'] == AGENT_KIND_PIPELINE
|
||||
assert result['enabled'] is True
|
||||
assert 'enabled' not in result
|
||||
assert result['config'] == {'ai': {'runner': {'id': 'pipeline-runner'}}}
|
||||
assert result['capability']['message_only'] is True
|
||||
|
||||
@@ -329,7 +326,7 @@ class TestAgentServiceCreateUpdateDelete:
|
||||
'runner': {'id': runner.id, 'expire-time': 0},
|
||||
'runner_config': {runner.id: {'model': 'gpt-4.1', 'temperature': 0.2}},
|
||||
}
|
||||
assert insert_values['enabled'] is True
|
||||
assert 'enabled' not in insert_values
|
||||
assert insert_values['supported_event_patterns'] == AGENT_DEFAULT_EVENT_PATTERNS
|
||||
app.pipeline_service._get_default_values_from_schema.assert_called_once_with(runner.config_schema)
|
||||
|
||||
|
||||
@@ -81,9 +81,9 @@ class TestEventRouteTrace:
|
||||
'target_type': 'agent',
|
||||
'target_uuid': 'agent-1',
|
||||
},
|
||||
failure_code='processor_disabled',
|
||||
reason='Agent target is disabled',
|
||||
text='disabled',
|
||||
failure_code='processor_not_found',
|
||||
reason='Agent target is unavailable',
|
||||
text='unavailable',
|
||||
)
|
||||
|
||||
bot.logger.warning.assert_awaited_once()
|
||||
|
||||
@@ -24,7 +24,6 @@ import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicForm
|
||||
import { extractI18nObject } from '@/i18n/I18nProvider';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import {
|
||||
Card,
|
||||
@@ -141,7 +140,6 @@ function AgentFormComponent(
|
||||
name: z.string().min(1, { message: t('agents.nameRequired') }),
|
||||
description: z.string().optional(),
|
||||
emoji: z.string().optional(),
|
||||
enabled: z.boolean().optional(),
|
||||
}),
|
||||
runner: z.record(z.string(), z.any()),
|
||||
runner_config: z.record(z.string(), z.any()),
|
||||
@@ -156,7 +154,6 @@ function AgentFormComponent(
|
||||
name: '',
|
||||
description: '',
|
||||
emoji: '🤖',
|
||||
enabled: true,
|
||||
},
|
||||
runner: {},
|
||||
runner_config: {},
|
||||
@@ -190,7 +187,6 @@ function AgentFormComponent(
|
||||
name: agent.name ?? '',
|
||||
description: agent.description ?? '',
|
||||
emoji: agent.emoji || '🤖',
|
||||
enabled: agent.enabled ?? true,
|
||||
},
|
||||
runner: (config.runner as Record<string, unknown>) ?? {},
|
||||
runner_config:
|
||||
@@ -287,12 +283,12 @@ function AgentFormComponent(
|
||||
: t('pipelines.configuration'),
|
||||
icon: SlidersHorizontal,
|
||||
},
|
||||
{
|
||||
name: 'basic',
|
||||
label: t('common.management'),
|
||||
icon: Power,
|
||||
},
|
||||
];
|
||||
const managementSection = {
|
||||
name: 'basic' as const,
|
||||
label: t('common.management'),
|
||||
icon: Power,
|
||||
};
|
||||
|
||||
const runnerStatus = useMemo<AgentRunnerStatus>(() => {
|
||||
if (pluginStatusLoading) {
|
||||
@@ -465,7 +461,6 @@ function AgentFormComponent(
|
||||
name: values.basic.name,
|
||||
description: values.basic.description ?? '',
|
||||
emoji: values.basic.emoji,
|
||||
enabled: values.basic.enabled ?? true,
|
||||
component_ref: (runner.id as string) || null,
|
||||
supported_event_patterns: normalizeEventPatterns(
|
||||
values.supported_event_patterns_text,
|
||||
@@ -568,11 +563,19 @@ function AgentFormComponent(
|
||||
}
|
||||
>
|
||||
<div className="overflow-x-auto">
|
||||
<TabsList className="grid min-w-[34rem] w-full grid-cols-3">
|
||||
<TabsList className="grid min-w-[42rem] w-full grid-cols-[repeat(3,minmax(0,1fr))_auto]">
|
||||
{primarySections.map((section) => {
|
||||
const Icon = section.icon;
|
||||
return (
|
||||
<TabsTrigger key={section.name} value={section.name}>
|
||||
<TabsTrigger
|
||||
key={section.name}
|
||||
value={section.name}
|
||||
className={
|
||||
section.name === 'basic'
|
||||
? 'px-4 text-muted-foreground data-[state=active]:text-foreground'
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Icon />
|
||||
{section.label}
|
||||
</TabsTrigger>
|
||||
@@ -580,21 +583,6 @@ function AgentFormComponent(
|
||||
})}
|
||||
</TabsList>
|
||||
</div>
|
||||
<div className="flex justify-end pt-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant={
|
||||
activeSection === managementSection.name
|
||||
? 'secondary'
|
||||
: 'ghost'
|
||||
}
|
||||
size="sm"
|
||||
onClick={() => setActiveSection(managementSection.name)}
|
||||
>
|
||||
<Power />
|
||||
{managementSection.label}
|
||||
</Button>
|
||||
</div>
|
||||
</Tabs>
|
||||
</nav>
|
||||
|
||||
@@ -671,40 +659,6 @@ function AgentFormComponent(
|
||||
|
||||
{activeSection === 'basic' && (
|
||||
<div className="space-y-6">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{t('agents.availability')}</CardTitle>
|
||||
<CardDescription>
|
||||
{t('agents.availabilityDescription')}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="basic.enabled"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center justify-between rounded-lg border p-4">
|
||||
<div className="space-y-0.5">
|
||||
<FormLabel className="flex items-center gap-2">
|
||||
<Power className="size-4" />
|
||||
{t('agents.enabled')}
|
||||
</FormLabel>
|
||||
<FormDescription>
|
||||
{t('agents.enabledDescription')}
|
||||
</FormDescription>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value ?? true}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="border-destructive/50">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-destructive">
|
||||
|
||||
@@ -177,7 +177,6 @@ export interface Agent {
|
||||
kind: AgentKind;
|
||||
component_ref?: string | null;
|
||||
config?: Record<string, unknown>;
|
||||
enabled?: boolean;
|
||||
supported_event_patterns?: string[];
|
||||
capability?: AgentCapability;
|
||||
created_at?: string;
|
||||
|
||||
@@ -839,7 +839,6 @@ export default function WizardPage() {
|
||||
runner: { id: selectedRunner, 'expire-time': 0 },
|
||||
runner_config: { [selectedRunner]: runnerConfig },
|
||||
},
|
||||
enabled: true,
|
||||
supported_event_patterns: [selectedScenarioDefinition.eventType],
|
||||
});
|
||||
processorUuid = agentResp.uuid;
|
||||
|
||||
@@ -469,7 +469,6 @@ const enUS = {
|
||||
processor_incompatible:
|
||||
'The selected processor cannot handle this event.',
|
||||
processor_not_found: 'The selected processor is unavailable.',
|
||||
processor_disabled: 'The selected processor is disabled.',
|
||||
bot_runtime_unavailable:
|
||||
'The bot is not running. Check its platform settings and enable it before running a full test.',
|
||||
runner_failed: 'The Agent runner failed while processing the event.',
|
||||
@@ -691,10 +690,7 @@ const enUS = {
|
||||
allEvents: 'Supports all events',
|
||||
messageEventsOnly: 'Message events only',
|
||||
basicInfo: 'Basic Information',
|
||||
basicInfoDescription: 'Set the name, icon, description and enabled state',
|
||||
availability: 'Availability',
|
||||
availabilityDescription:
|
||||
'Control whether this Agent can receive and process events.',
|
||||
basicInfoDescription: 'Set the name, icon and description',
|
||||
runnerSettings: 'Runner',
|
||||
advanced: 'Advanced',
|
||||
bindableEvents: 'Bindable Event Range',
|
||||
@@ -703,9 +699,6 @@ const enUS = {
|
||||
supportedEvents: 'Event Range',
|
||||
supportedEventsDescription:
|
||||
'Use one event pattern per line, for example *, message.received, group.*. Pipelines are fixed to message.*.',
|
||||
enabled: 'Enable Agent',
|
||||
enabledDescription:
|
||||
'When disabled, this Agent should not be selected by event routing.',
|
||||
nameRequired: 'Name cannot be empty',
|
||||
createSuccess: 'Created successfully',
|
||||
createError: 'Creation failed: ',
|
||||
|
||||
@@ -525,8 +525,7 @@ const esES = {
|
||||
allEvents: 'Compatible con todos los eventos',
|
||||
messageEventsOnly: 'Solo eventos de mensaje',
|
||||
basicInfo: 'Información básica',
|
||||
basicInfoDescription:
|
||||
'Establece el nombre, icono, descripción y estado de habilitación',
|
||||
basicInfoDescription: 'Establece el nombre, icono y descripción',
|
||||
runnerSettings: 'Runner',
|
||||
advanced: 'Avanzado',
|
||||
bindableEvents: 'Rango de eventos vinculables',
|
||||
@@ -535,9 +534,6 @@ const esES = {
|
||||
supportedEvents: 'Rango de eventos',
|
||||
supportedEventsDescription:
|
||||
'Usa un patrón de evento por línea, por ejemplo *, message.received, group.*. Los Pipelines están fijos en message.*.',
|
||||
enabled: 'Habilitar Agent',
|
||||
enabledDescription:
|
||||
'Cuando está deshabilitado, este Agent no debe ser seleccionado por el enrutamiento de eventos.',
|
||||
nameRequired: 'El nombre no puede estar vacío',
|
||||
createSuccess: 'Creado correctamente',
|
||||
createError: 'Error al crear: ',
|
||||
|
||||
@@ -476,7 +476,6 @@ const jaJP = {
|
||||
processor_incompatible:
|
||||
'選択したプロセッサーはこのイベントを処理できません。',
|
||||
processor_not_found: '選択したプロセッサーを利用できません。',
|
||||
processor_disabled: '選択したプロセッサーは無効です。',
|
||||
bot_runtime_unavailable:
|
||||
'ボットが実行されていません。プラットフォーム設定を確認してボットを有効にした後、完全テストを実行してください。',
|
||||
runner_failed: 'Agent Runner がイベント処理中に失敗しました。',
|
||||
@@ -706,10 +705,7 @@ const jaJP = {
|
||||
allEvents: 'すべてのイベントに対応',
|
||||
messageEventsOnly: 'メッセージイベントのみ',
|
||||
basicInfo: '基本情報',
|
||||
basicInfoDescription: '名前、アイコン、説明、有効状態を設定します',
|
||||
availability: '有効状態',
|
||||
availabilityDescription:
|
||||
'この Agent がイベントを受信して処理できるかを制御します。',
|
||||
basicInfoDescription: '名前、アイコン、説明を設定します',
|
||||
runnerSettings: 'Runner',
|
||||
advanced: '詳細',
|
||||
bindableEvents: '紐付け可能なイベント範囲',
|
||||
@@ -718,9 +714,6 @@ const jaJP = {
|
||||
supportedEvents: 'イベント範囲',
|
||||
supportedEventsDescription:
|
||||
'1 行に 1 つのイベントパターンを指定します。例: *、message.received、group.*。Pipeline は message.* 固定です。',
|
||||
enabled: 'Agent を有効化',
|
||||
enabledDescription:
|
||||
'無効化すると、この Agent はイベントルーティングで選択されません。',
|
||||
nameRequired: '名前は必須です',
|
||||
createSuccess: '作成に成功しました',
|
||||
createError: '作成に失敗しました:',
|
||||
|
||||
@@ -522,7 +522,7 @@ const ruRU = {
|
||||
allEvents: 'Поддерживает все события',
|
||||
messageEventsOnly: 'Только события сообщений',
|
||||
basicInfo: 'Основная информация',
|
||||
basicInfoDescription: 'Задайте имя, иконку, описание и статус активации',
|
||||
basicInfoDescription: 'Задайте имя, иконку и описание',
|
||||
runnerSettings: 'Runner',
|
||||
advanced: 'Дополнительно',
|
||||
bindableEvents: 'Диапазон привязываемых событий',
|
||||
@@ -531,9 +531,6 @@ const ruRU = {
|
||||
supportedEvents: 'Диапазон событий',
|
||||
supportedEventsDescription:
|
||||
'Один шаблон события в строке, например *, message.received, group.*. Pipeline фиксирован на message.*.',
|
||||
enabled: 'Включить Agent',
|
||||
enabledDescription:
|
||||
'При отключении этот Agent не должен выбираться маршрутизацией событий.',
|
||||
nameRequired: 'Имя не может быть пустым',
|
||||
createSuccess: 'Успешно создано',
|
||||
createError: 'Ошибка создания: ',
|
||||
|
||||
@@ -508,7 +508,7 @@ const thTH = {
|
||||
allEvents: 'รองรับทุกเหตุการณ์',
|
||||
messageEventsOnly: 'เฉพาะเหตุการณ์ข้อความ',
|
||||
basicInfo: 'ข้อมูลพื้นฐาน',
|
||||
basicInfoDescription: 'ตั้งชื่อ ไอคอน คำอธิบาย และสถานะการเปิดใช้งาน',
|
||||
basicInfoDescription: 'ตั้งชื่อ ไอคอน และคำอธิบาย',
|
||||
runnerSettings: 'Runner',
|
||||
advanced: 'ขั้นสูง',
|
||||
bindableEvents: 'ช่วงเหตุการณ์ที่ผูกได้',
|
||||
@@ -517,9 +517,6 @@ const thTH = {
|
||||
supportedEvents: 'ช่วงเหตุการณ์',
|
||||
supportedEventsDescription:
|
||||
'หนึ่งรูปแบบเหตุการณ์ต่อบรรทัด เช่น *, message.received, group.* Pipeline ถูกกำหนดไว้ที่ message.*',
|
||||
enabled: 'เปิดใช้งาน Agent',
|
||||
enabledDescription:
|
||||
'เมื่อปิดใช้งาน Agent นี้จะไม่ถูกเลือกโดยการกำหนดเส้นทางเหตุการณ์',
|
||||
nameRequired: 'ชื่อต้องไม่ว่างเปล่า',
|
||||
createSuccess: 'สร้างสำเร็จ',
|
||||
createError: 'สร้างล้มเหลว: ',
|
||||
|
||||
@@ -518,7 +518,7 @@ const viVN = {
|
||||
allEvents: 'Hỗ trợ tất cả sự kiện',
|
||||
messageEventsOnly: 'Chỉ sự kiện tin nhắn',
|
||||
basicInfo: 'Thông tin cơ bản',
|
||||
basicInfoDescription: 'Đặt tên, biểu tượng, mô tả và trạng thái kích hoạt',
|
||||
basicInfoDescription: 'Đặt tên, biểu tượng và mô tả',
|
||||
runnerSettings: 'Runner',
|
||||
advanced: 'Nâng cao',
|
||||
bindableEvents: 'Phạm vi sự kiện có thể gắn',
|
||||
@@ -527,9 +527,6 @@ const viVN = {
|
||||
supportedEvents: 'Phạm vi sự kiện',
|
||||
supportedEventsDescription:
|
||||
'Mỗi dòng một mẫu sự kiện, ví dụ *, message.received, group.*. Pipeline cố định ở message.*.',
|
||||
enabled: 'Kích hoạt Agent',
|
||||
enabledDescription:
|
||||
'Khi bị tắt, Agent này sẽ không được định tuyến sự kiện chọn.',
|
||||
nameRequired: 'Tên không được để trống',
|
||||
createSuccess: 'Tạo thành công',
|
||||
createError: 'Tạo thất bại: ',
|
||||
|
||||
@@ -447,7 +447,6 @@ const zhHans = {
|
||||
route_not_found: '没有路由命中此事件。',
|
||||
processor_incompatible: '所选处理器无法处理此事件。',
|
||||
processor_not_found: '所选处理器不可用。',
|
||||
processor_disabled: '所选处理器已禁用。',
|
||||
bot_runtime_unavailable:
|
||||
'机器人尚未运行。请检查平台配置并启用机器人,再运行完整测试。',
|
||||
runner_failed: 'Agent Runner 处理事件时失败。',
|
||||
@@ -661,9 +660,7 @@ const zhHans = {
|
||||
allEvents: '支持全部事件',
|
||||
messageEventsOnly: '仅支持消息事件',
|
||||
basicInfo: '基础信息',
|
||||
basicInfoDescription: '设置名称、图标、描述和启用状态',
|
||||
availability: '启用状态',
|
||||
availabilityDescription: '控制此 Agent 是否可以接收并处理事件。',
|
||||
basicInfoDescription: '设置名称、图标和描述',
|
||||
runnerSettings: '运行器',
|
||||
advanced: '高级',
|
||||
bindableEvents: '可绑定事件范围',
|
||||
@@ -672,8 +669,6 @@ const zhHans = {
|
||||
supportedEvents: '事件范围',
|
||||
supportedEventsDescription:
|
||||
'每行一个事件模式,例如 *、message.received、group.*。Pipeline 固定仅支持 message.*。',
|
||||
enabled: '启用 Agent',
|
||||
enabledDescription: '禁用后,此 Agent 不应被事件路由选中。',
|
||||
nameRequired: '名称不能为空',
|
||||
createSuccess: '创建成功',
|
||||
createError: '创建失败:',
|
||||
|
||||
@@ -491,7 +491,7 @@ const zhHant = {
|
||||
allEvents: '支援全部事件',
|
||||
messageEventsOnly: '僅支援訊息事件',
|
||||
basicInfo: '基本資訊',
|
||||
basicInfoDescription: '設定名稱、圖示、描述和啟用狀態',
|
||||
basicInfoDescription: '設定名稱、圖示和描述',
|
||||
runnerSettings: '執行器',
|
||||
advanced: '進階',
|
||||
bindableEvents: '可綁定事件範圍',
|
||||
@@ -500,8 +500,6 @@ const zhHant = {
|
||||
supportedEvents: '事件範圍',
|
||||
supportedEventsDescription:
|
||||
'每行一個事件模式,例如 *、message.received、group.*。Pipeline 固定僅支援 message.*。',
|
||||
enabled: '啟用 Agent',
|
||||
enabledDescription: '停用後,此 Agent 不應被事件路由選中。',
|
||||
nameRequired: '名稱不能為空',
|
||||
createSuccess: '建立成功',
|
||||
createError: '建立失敗:',
|
||||
|
||||
@@ -50,11 +50,9 @@ test.describe('processor detail workbench', () => {
|
||||
);
|
||||
await expect(flow.getByRole('tab').nth(1)).toContainText('Runner');
|
||||
await expect(flow.getByRole('tab').nth(2)).toContainText('Local Agent');
|
||||
const agentManagement = configPanel.getByRole('button', {
|
||||
name: 'Management',
|
||||
});
|
||||
await expect(agentManagement).toBeVisible();
|
||||
await expect(flow.getByText('Management')).toHaveCount(0);
|
||||
const agentManagement = flow.getByRole('tab').nth(3);
|
||||
await expect(agentManagement).toContainText('Management');
|
||||
await expect(agentManagement).toHaveClass(/text-muted-foreground/);
|
||||
|
||||
await expect(
|
||||
page.getByRole('heading', { name: /agent-workbench/ }),
|
||||
@@ -85,6 +83,12 @@ test.describe('processor detail workbench', () => {
|
||||
await expect(
|
||||
configPanel.getByText('Local Agent', { exact: true }).last(),
|
||||
).toBeVisible();
|
||||
await agentManagement.click();
|
||||
await expect(configPanel.getByText('Danger Zone')).toBeVisible();
|
||||
await expect(configPanel.getByText('Availability')).toHaveCount(0);
|
||||
await expect(
|
||||
configPanel.getByRole('switch', { name: 'Enable Agent' }),
|
||||
).toHaveCount(0);
|
||||
});
|
||||
|
||||
test('agent saves edits before debugging and shows the real output', async ({
|
||||
|
||||
Reference in New Issue
Block a user