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