mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-28 21:27:14 +00:00
refactor(agent): remove enabled state
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user