mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-23 17:05:28 +08:00
fix(skill): reconcile rebase with runner contracts
This commit is contained in:
@@ -1007,7 +1007,7 @@ class TestQueryEntrySessionQueryId:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_no_query_id_for_pure_event_first_flow(self, clean_agent_state):
|
||||
"""Pure event-first flow has query_id=None in session."""
|
||||
"""Pure event-first flow restores authorized Skill revisions without a Query."""
|
||||
from langbot.pkg.agent.runner.host_models import (
|
||||
AgentEventEnvelope,
|
||||
AgentBinding,
|
||||
@@ -1018,6 +1018,8 @@ class TestQueryEntrySessionQueryId:
|
||||
)
|
||||
from langbot_plugin.api.entities.builtin.runner.input import AgentInput
|
||||
from langbot_plugin.api.entities.builtin.runner.delivery import DeliveryContext
|
||||
from langbot.pkg.agent.runner.persistent_state_store import get_persistent_state_store
|
||||
from langbot.pkg.provider.tools.loaders.skill import ACTIVATED_SKILL_NAMES_STATE_KEY, ACTIVATED_SKILLS_KEY
|
||||
|
||||
db_engine = clean_agent_state
|
||||
descriptor = make_descriptor()
|
||||
@@ -1030,6 +1032,10 @@ class TestQueryEntrySessionQueryId:
|
||||
]
|
||||
)
|
||||
ap = FakeApplication(plugin_connector, db_engine)
|
||||
revision = 'sha256:' + '1' * 64
|
||||
ap.skill_repository = types.SimpleNamespace(
|
||||
get_skill=AsyncMock(return_value={'name': 'demo', 'revision': revision})
|
||||
)
|
||||
|
||||
async def build_resource_context(execution_query):
|
||||
from langbot.pkg.provider.tools.loaders.mcp import (
|
||||
@@ -1085,10 +1091,21 @@ class TestQueryEntrySessionQueryId:
|
||||
'mcp-resource-agent-read-enabled': True,
|
||||
},
|
||||
resource_policy=ResourcePolicy(),
|
||||
state_policy=StatePolicy(enable_state=False, state_scopes=[]),
|
||||
state_policy=StatePolicy(enable_state=True, state_scopes=['conversation']),
|
||||
delivery_policy=DeliveryPolicy(enable_streaming=True, enable_reply=True),
|
||||
)
|
||||
|
||||
success, error = await get_persistent_state_store(db_engine).apply_update_from_event(
|
||||
event.model_copy(update={'workspace_id': TEST_CONTEXT.workspace_uuid}),
|
||||
binding,
|
||||
descriptor,
|
||||
'conversation',
|
||||
ACTIVATED_SKILL_NAMES_STATE_KEY,
|
||||
[{'name': 'demo', 'revision': revision}],
|
||||
None,
|
||||
)
|
||||
assert success is True and error is None
|
||||
|
||||
messages = [
|
||||
message
|
||||
async for message in orchestrator.run(
|
||||
@@ -1114,6 +1131,7 @@ class TestQueryEntrySessionQueryId:
|
||||
assert execution_query.message_event.type == event.event_type
|
||||
assert '_host_box_scope' not in execution_query.variables
|
||||
assert execution_query.variables['_pipeline_bound_skills'] == ['demo', 'hidden']
|
||||
assert execution_query.variables[ACTIVATED_SKILLS_KEY]['demo']['revision'] == revision
|
||||
assert execution_query.variables['_pipeline_mcp_resource_attachments'][0]['server_uuid'] == 'srv-1'
|
||||
assert execution_query.variables['_pipeline_mcp_resource_agent_read_enabled'] is True
|
||||
assert 'MCP resource context selected by LangBot host:' in plugin_connector.contexts[0]['input']['text']
|
||||
@@ -1344,8 +1362,9 @@ class TestQueryEntryAdapterHostCapabilities:
|
||||
assert snapshot['conversation']['external.test_key'] == 'test_value'
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_from_query_restores_activated_skills_from_state(self, clean_agent_state):
|
||||
"""Persisted activated skill names are restored into the next Query run."""
|
||||
@pytest.mark.parametrize('legacy_state', [False, True])
|
||||
async def test_run_from_query_restores_activated_skills_from_state(self, clean_agent_state, legacy_state):
|
||||
"""Pinned revisions restore; old name-only state requires reactivation."""
|
||||
from langbot.pkg.agent.runner.persistent_state_store import get_persistent_state_store
|
||||
from langbot.pkg.provider.tools.loaders.skill import (
|
||||
ACTIVATED_SKILL_NAMES_STATE_KEY,
|
||||
@@ -1363,6 +1382,10 @@ class TestQueryEntryAdapterHostCapabilities:
|
||||
]
|
||||
)
|
||||
ap = FakeApplication(plugin_connector, db_engine)
|
||||
revision = 'sha256:' + '1' * 64
|
||||
ap.skill_repository = types.SimpleNamespace(
|
||||
get_skill=AsyncMock(return_value={'name': 'demo', 'revision': revision})
|
||||
)
|
||||
orchestrator = AgentRunOrchestrator(ap, FakeRegistry(descriptor))
|
||||
query = make_query()
|
||||
|
||||
@@ -1376,7 +1399,7 @@ class TestQueryEntryAdapterHostCapabilities:
|
||||
descriptor,
|
||||
'conversation',
|
||||
ACTIVATED_SKILL_NAMES_STATE_KEY,
|
||||
['demo'],
|
||||
['demo'] if legacy_state else [{'name': 'demo', 'revision': revision}],
|
||||
None,
|
||||
)
|
||||
assert success is True
|
||||
@@ -1385,7 +1408,16 @@ class TestQueryEntryAdapterHostCapabilities:
|
||||
messages = [message async for message in orchestrator.run_from_query(query)]
|
||||
|
||||
assert len(messages) == 1
|
||||
assert query.variables[ACTIVATED_SKILLS_KEY]['demo']['name'] == 'demo'
|
||||
if legacy_state:
|
||||
assert ACTIVATED_SKILLS_KEY not in query.variables
|
||||
assert ap.skill_repository.get_skill.await_count == 0
|
||||
assert (
|
||||
len([warning for warning in ap.logger.warnings if 'Legacy name-only activated Skill state' in warning])
|
||||
== 1
|
||||
)
|
||||
else:
|
||||
assert query.variables[ACTIVATED_SKILLS_KEY]['demo']['name'] == 'demo'
|
||||
assert query.variables[ACTIVATED_SKILLS_KEY]['demo']['revision'] == revision
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_event_log_and_transcript_written(self, clean_agent_state):
|
||||
|
||||
Reference in New Issue
Block a user