refactor(agent-runner): align config with agent semantics

This commit is contained in:
huanghuoguoguo
2026-06-02 17:43:44 +08:00
parent d0383e146e
commit f2153f736c
23 changed files with 94 additions and 126 deletions

View File

@@ -36,7 +36,6 @@ class TestMigratePipelineConfig:
assert 'plugin:langbot/local-agent/default' in migrated['ai']['runner_config']
assert migrated['ai']['runner_config']['plugin:langbot/local-agent/default']['knowledge-bases'] == ['kb-uuid']
assert 'knowledge-base' not in migrated['ai']['runner_config']['plugin:langbot/local-agent/default']
assert 'max-round' not in migrated['ai']['runner_config']['plugin:langbot/local-agent/default']
# Expire-time preserved
assert migrated['ai']['runner']['expire-time'] == 0
@@ -270,7 +269,7 @@ class TestResolveRunnerConfig:
"""resolve_runner_config should not read old ai.local-agent at runtime."""
config = {
'ai': {
'local-agent': {'max-round': 15, 'custom-option': 20},
'local-agent': {'custom-option': 20},
},
}
runner_config = ConfigMigration.resolve_runner_config(config, 'plugin:langbot/local-agent/default')
@@ -280,7 +279,7 @@ class TestResolveRunnerConfig:
"""resolve_legacy_runner_config should read old ai.local-agent for migration."""
config = {
'ai': {
'local-agent': {'max-round': 15, 'custom-option': 20},
'local-agent': {'custom-option': 20},
},
}
runner_config = ConfigMigration.resolve_legacy_runner_config(config, 'plugin:langbot/local-agent/default')
@@ -293,7 +292,7 @@ class TestResolveRunnerConfig:
'runner_config': {
'plugin:langbot/local-agent/default': {'custom-option': 25},
},
'local-agent': {'max-round': 10, 'custom-option': 10}, # Old, should be ignored
'local-agent': {'custom-option': 10}, # Old, should be ignored
},
}
runner_config = ConfigMigration.resolve_runner_config(config, 'plugin:langbot/local-agent/default')

View File

@@ -8,7 +8,7 @@ Tests focus on:
from __future__ import annotations
import pytest
from unittest.mock import MagicMock, AsyncMock, patch
from unittest.mock import MagicMock
from langbot.pkg.agent.runner.context_builder import AgentRunContextBuilder
from langbot.pkg.agent.runner.host_models import AgentEventEnvelope, AgentBinding, BindingScope, StatePolicy
@@ -67,7 +67,7 @@ class TestContextAccessStateDetermination:
binding = AgentBinding(
binding_id='binding_001',
runner_id='plugin:test/runner/default',
scope=BindingScope(scope_type='pipeline', scope_id='conv_001'),
scope=BindingScope(scope_type='agent', scope_id='conv_001'),
state_policy=StatePolicy(
enable_state=True,
state_scopes=['conversation', 'actor'],
@@ -88,7 +88,7 @@ class TestContextAccessStateDetermination:
binding = AgentBinding(
binding_id='binding_001',
runner_id='plugin:test/runner/default',
scope=BindingScope(scope_type='pipeline', scope_id='conv_001'),
scope=BindingScope(scope_type='agent', scope_id='conv_001'),
state_policy=StatePolicy(
enable_state=False,
state_scopes=[],
@@ -109,7 +109,7 @@ class TestContextAccessStateDetermination:
binding = AgentBinding(
binding_id='binding_001',
runner_id='plugin:test/runner/default',
scope=BindingScope(scope_type='pipeline', scope_id='conv_001'),
scope=BindingScope(scope_type='agent', scope_id='conv_001'),
state_policy=StatePolicy(
enable_state=True,
state_scopes=[], # Empty scopes - state not available
@@ -177,7 +177,7 @@ class TestContextAccessStateDetermination:
binding = AgentBinding(
binding_id='binding_003',
runner_id='plugin:test/runner/default',
scope=BindingScope(scope_type='pipeline', scope_id='conv_001'),
scope=BindingScope(scope_type='agent', scope_id='conv_001'),
state_policy=StatePolicy(
enable_state=True,
state_scopes=['conversation', 'actor', 'subject', 'runner'],
@@ -226,7 +226,7 @@ class TestBindingWithStatePolicy:
binding = AgentBinding(
binding_id='binding_001',
runner_id='plugin:test/runner/default',
scope=BindingScope(scope_type='pipeline', scope_id='conv_001'),
scope=BindingScope(scope_type='agent', scope_id='conv_001'),
state_policy=StatePolicy(
enable_state=True,
state_scopes=['conversation'],
@@ -260,7 +260,7 @@ class TestContextAccessOtherAPIs:
binding = AgentBinding(
binding_id='binding_001',
runner_id='plugin:test/runner/default',
scope=BindingScope(scope_type='pipeline', scope_id='conv_001'),
scope=BindingScope(scope_type='agent', scope_id='conv_001'),
state_policy=StatePolicy(enable_state=False, state_scopes=[]),
)
@@ -288,7 +288,7 @@ class TestContextAccessOtherAPIs:
binding = AgentBinding(
binding_id='binding_001',
runner_id='plugin:test/runner/default',
scope=BindingScope(scope_type='pipeline', scope_id='conv_001'),
scope=BindingScope(scope_type='agent', scope_id='conv_001'),
state_policy=StatePolicy(enable_state=False, state_scopes=[]),
)
@@ -316,7 +316,7 @@ class TestContextAccessOtherAPIs:
binding = AgentBinding(
binding_id='binding_001',
runner_id='plugin:test/runner/default',
scope=BindingScope(scope_type='pipeline', scope_id='conv_001'),
scope=BindingScope(scope_type='agent', scope_id='conv_001'),
state_policy=StatePolicy(enable_state=False, state_scopes=[]),
)
@@ -342,7 +342,7 @@ class TestContextAccessOtherAPIs:
binding = AgentBinding(
binding_id='binding_001',
runner_id='plugin:test/runner/default',
scope=BindingScope(scope_type='pipeline', scope_id='conv_001'),
scope=BindingScope(scope_type='agent', scope_id='conv_001'),
state_policy=StatePolicy(enable_state=False, state_scopes=[]),
)

View File

@@ -66,11 +66,11 @@ class TestContextValidation:
"""Create a test binding."""
return AgentBinding(
binding_id="binding_1",
scope=BindingScope(scope_type="pipeline", scope_id="pipeline_1"),
scope=BindingScope(scope_type="agent", scope_id="pipeline_1"),
event_types=["message.received"],
runner_id="plugin:test/plugin/runner",
runner_config={"timeout": 300},
pipeline_uuid="pipeline_1",
agent_id="pipeline_1",
enabled=True,
)

View File

@@ -144,17 +144,9 @@ class TestPipelineConfigToBinding:
mock_query, "plugin:test/plugin/runner"
)
assert binding.scope.scope_type == "pipeline"
assert binding.scope.scope_type == "agent"
assert binding.scope.scope_id == mock_query.pipeline_uuid
def test_config_to_binding_does_not_add_host_context_window(self, mock_query):
"""Pipeline binding should not define Host-side context window controls."""
binding = PipelineAdapter.pipeline_config_to_binding(
mock_query, "plugin:test/plugin/runner"
)
assert not hasattr(binding, "max_round")
assert binding.agent_id == mock_query.pipeline_uuid
class TestAgentRunContextProtocolV1:
"""Test AgentRunContext Protocol v1 behavior."""
@@ -238,23 +230,14 @@ class TestAgentRunContextProtocolV1:
assert ctx.bootstrap is None or isinstance(ctx.bootstrap.messages, list)
class TestHostContextWindowNotInProtocol:
"""Test that Host-side context window controls are not in Protocol v1."""
class TestHostManagedHistoryNotInProtocol:
"""Test that Host-managed history payloads are not in Protocol v1."""
def test_context_window_not_in_sdk_context(self):
"""AgentRunContext should not expose Host-side window controls."""
def test_messages_not_in_sdk_context_top_level(self):
"""AgentRunContext should not expose top-level history messages."""
ctx_fields = AgentRunContext.model_fields.keys()
assert "max_round" not in ctx_fields
assert "maxRound" not in ctx_fields
def test_binding_has_no_context_window_field(self, mock_query):
"""Pipeline adapter should not attach context window policy to binding."""
binding = PipelineAdapter.pipeline_config_to_binding(
mock_query, "plugin:test/plugin/runner"
)
assert not hasattr(binding, "max_round")
assert "messages" not in ctx_fields
class TestSDKCapabilitiesProtocolV1:

View File

@@ -56,7 +56,7 @@ def make_binding(runner_id: str = "plugin:test/plugin/runner") -> AgentBinding:
"""Create a test binding."""
return AgentBinding(
binding_id="binding_1",
scope=BindingScope(scope_type="pipeline", scope_id="pipeline_1"),
scope=BindingScope(scope_type="agent", scope_id="pipeline_1"),
event_types=["message.received"],
runner_id=runner_id,
runner_config={},

View File

@@ -347,7 +347,7 @@ async def test_orchestrator_does_not_package_query_messages_into_context(clean_a
ap = FakeApplication(plugin_connector, db_engine)
orchestrator = AgentRunOrchestrator(ap, FakeRegistry(descriptor))
query = make_query()
query.pipeline_config["ai"]["runner_config"][RUNNER_ID]["agent-window"] = 2
query.pipeline_config["ai"]["runner_config"][RUNNER_ID]["custom-option"] = 2
query.messages = [
provider_message.Message(role="user", content="message 1"),
provider_message.Message(role="assistant", content="response 1"),
@@ -361,9 +361,9 @@ async def test_orchestrator_does_not_package_query_messages_into_context(clean_a
assert len(messages) == 1
context = plugin_connector.contexts[0]
assert context["config"]["agent-window"] == 2
assert context["config"]["custom-option"] == 2
assert context["bootstrap"] is None
assert "adapter_messages" not in context["adapter"]
assert set(context["adapter"]) == {"query_id", "extra"}
assert "context_packaging" not in context["runtime"]["metadata"]
assert [message.content for message in query.messages] == [
"message 1",
@@ -538,7 +538,7 @@ class TestPipelineCompatibilityQueryIdInSession:
)
binding = AgentBinding(
binding_id="binding_001",
scope=BindingScope(scope_type="pipeline", scope_id="pipeline_001"),
scope=BindingScope(scope_type="agent", scope_id="pipeline_001"),
event_types=["message.received"],
runner_id=RUNNER_ID,
runner_config={},

View File

@@ -82,8 +82,8 @@ class FakeBinding:
self,
binding_id: str = 'binding_001',
state_policy: StatePolicy | None = None,
scope_type: str = 'pipeline',
scope_id: str = 'pipeline_001',
scope_type: str = 'agent',
scope_id: str = 'agent_001',
):
self.binding_id = binding_id
self.scope = BindingScope(scope_type=scope_type, scope_id=scope_id)