refactor(agent-runner): remove protocol_version from various components and update related documentation

This commit is contained in:
huanghuoguoguo
2026-06-09 20:57:06 +08:00
committed by huanghuoguoguo
parent 818f5926cd
commit c859fc37bb
18 changed files with 36 additions and 122 deletions
@@ -72,7 +72,7 @@ LangBot 不提供 host-side inline history window。简单 runner 如果需要
### 4.1 History ### 4.1 History
```python ```python
await api.history.page(conversation_id=ctx.context.conversation_id, await api.history_page(conversation_id=ctx.context.conversation_id,
before_cursor=ctx.context.latest_cursor, before_cursor=ctx.context.latest_cursor,
limit=50, direction="backward", include_artifacts=False) limit=50, direction="backward", include_artifacts=False)
``` ```
@@ -92,7 +92,7 @@ class HistoryPage(BaseModel):
### 4.2 Search ### 4.2 Search
```python ```python
await api.history.search(query="用户之前提到的数据库连接信息", await api.history_search(query="用户之前提到的数据库连接信息",
filters={"conversation_id": ..., "event_types": ["message.received"]}, filters={"conversation_id": ..., "event_types": ["message.received"]},
top_k=10) top_k=10)
``` ```
@@ -154,4 +154,4 @@ Agent 自管 context 不代表无限制访问。LangBot 仍必须控制:每次
官方 runner 插件可以把状态寄宿在 LangBot,但必须和第三方 runner 一样通过公开 Host API 消费。LangBot core 不内置官方 agent 的业务流程(prompt 组装、tool loop、RAG 编排、summary/compaction、"local-agent 专用"状态字段)。 官方 runner 插件可以把状态寄宿在 LangBot,但必须和第三方 runner 一样通过公开 Host API 消费。LangBot core 不内置官方 agent 的业务流程(prompt 组装、tool loop、RAG 编排、summary/compaction、"local-agent 专用"状态字段)。
官方 local-agent 应作为"依附 LangBot 基础设施的复杂 runner 参考实现"transcript/history 通过 `api.history` 读取,summary/checkpoint/外部 session id/用户偏好通过 `api.state` `api.storage` 保存,图片/文件/工具大结果通过 `api.artifacts` 读取,模型/工具/知识库通过 `api.models` / `api.tools` / `api.knowledge` 调用。这样 LangBot 保持为通用 agent host,不变成内置 agent 框架。具体迁移要求见 [OFFICIAL_RUNNER_PLUGINS.md](./OFFICIAL_RUNNER_PLUGINS.md)。 官方 local-agent 应作为"依附 LangBot 基础设施的复杂 runner 参考实现"transcript/history 通过 `api.history_page()` / `api.history_search()` 读取,summary/checkpoint/外部 session id/用户偏好通过 `api.state_get()` / `api.state_set()`storage 方法保存,图片/文件/工具大结果通过 `api.artifact_metadata()` / `api.artifact_read_range()` 读取,模型/工具/知识库通过 `api.invoke_llm()` / `api.call_tool()` / `api.retrieve_knowledge()` 调用。这样 LangBot 保持为通用 agent host,不变成内置 agent 框架。具体迁移要求见 [OFFICIAL_RUNNER_PLUGINS.md](./OFFICIAL_RUNNER_PLUGINS.md)。
@@ -137,14 +137,13 @@ class AgentRunnerDescriptor(BaseModel):
source: Literal["plugin"] source: Literal["plugin"]
label: I18nObject label: I18nObject
description: I18nObject | None = None description: I18nObject | None = None
protocol_version: str = "1"
capabilities: AgentRunnerCapabilities # 见 PROTOCOL_V1 §4.3 capabilities: AgentRunnerCapabilities # 见 PROTOCOL_V1 §4.3
permissions: AgentRunnerPermissions # 见 PROTOCOL_V1 §4.4 permissions: AgentRunnerPermissions # 见 PROTOCOL_V1 §4.4
config_schema: list[DynamicFormItemSchema] config_schema: list[DynamicFormItemSchema]
plugin: PluginRef | None = None plugin: PluginRef | None = None
``` ```
职责:调用 `plugin_connector.list_agent_runners()` 拉取 runner、校验 manifest`kind == AgentRunner``metadata.name/label` 存在、`protocol_version` 兼容、`spec.*` 类型正确)、输出 descriptor、缓存 discovery 结果并提供 `refresh()`。单个插件 manifest 失败只记 warning,不影响其它 runner。`plugin:author/name/runner` 是稳定 id 格式;插件实例边界见 PROTOCOL_V1 §13。 职责:调用 `plugin_connector.list_agent_runners()` 拉取 runner、校验 manifest`kind == AgentRunner``metadata.name/label` 存在、`spec.*` 类型正确)、输出 descriptor、缓存 discovery 结果并提供 `refresh()`。单个插件 manifest 失败只记 warning,不影响其它 runner。`plugin:author/name/runner` 是稳定 id 格式;插件实例边界见 PROTOCOL_V1 §13。
Host 内置 runner / adapter 不能作为 `AgentRunnerDescriptor.source` 绕过插件 Host 内置 runner / adapter 不能作为 `AgentRunnerDescriptor.source` 绕过插件
runtime、`run_id``ctx.resources``AgentRunAPIProxy` 权限链。若需要 runtime、`run_id``ctx.resources``AgentRunAPIProxy` 权限链。若需要
@@ -225,16 +224,7 @@ LangBot 可提供 host-owned state 让 runner 寄宿状态(conversation / acto
三类数据与 working context 的边界、读取约束见 [AGENT_CONTEXT_PROTOCOL.md](./AGENT_CONTEXT_PROTOCOL.md)。AgentRunner 可读取这些能力,但不被迫使用 LangBot 作为唯一记忆系统。 三类数据与 working context 的边界、读取约束见 [AGENT_CONTEXT_PROTOCOL.md](./AGENT_CONTEXT_PROTOCOL.md)。AgentRunner 可读取这些能力,但不被迫使用 LangBot 作为唯一记忆系统。
### 4.8 Prompt / Instruction Package(占位) ### 4.8 External harness resource projection
当前 Query 入口不把 preprocessing 后的有效 prompt 放进 adapter metadata。目标形态是 Host 保存或生成一个 run-scoped instruction packagerunner 通过 Host API 拉取:
- Host 记录静态绑定 prompt、host hook / user plugin 产生的 instruction fragment、来源和审计信息。
- `ctx.context.available_apis` 增加 `prompt_get` 能力位表示拉取是否可用。
- Runner 拉取后仍由自己决定如何与 history、RAG、tool 结果、memory 和当前输入组装最终 prompt。
- Host 不实现通用 agentic prompt assembler,也不把 Query entry adapter prompt 作为长期业务输入契约。
### 4.9 External harness resource projection
Claude Code、Codex、Kimi Code 等外部 harness runner 可能不直接调用 LangBot 的 model/tool loop,而是把 LangBot 事件和授权资源句柄投影到自己的 harness 执行。Host 侧仍保持统一边界:Host 负责构造 event-first context、资源授权、state/storage、EventLog/Transcript/ArtifactStore 和审计;Host 或 binding policy 决定哪些 MCP bridge、skill-backed tool、artifact、history/state 句柄可投影给 runnerrunner plugin 把 scoped projection 转成目标 harness 可消费形式;所有 LangBot 资源访问必须经 SDK runtime / `AgentRunAPIProxy` / SDK-owned MCP bridge 转发并接受 Host 校验;外部 harness 负责自己的 native session、tool loop、压缩、权限模式和 resume,但不能用 native tools 绕过 Host 授权。 Claude Code、Codex、Kimi Code 等外部 harness runner 可能不直接调用 LangBot 的 model/tool loop,而是把 LangBot 事件和授权资源句柄投影到自己的 harness 执行。Host 侧仍保持统一边界:Host 负责构造 event-first context、资源授权、state/storage、EventLog/Transcript/ArtifactStore 和审计;Host 或 binding policy 决定哪些 MCP bridge、skill-backed tool、artifact、history/state 句柄可投影给 runnerrunner plugin 把 scoped projection 转成目标 harness 可消费形式;所有 LangBot 资源访问必须经 SDK runtime / `AgentRunAPIProxy` / SDK-owned MCP bridge 转发并接受 Host 校验;外部 harness 负责自己的 native session、tool loop、压缩、权限模式和 resume,但不能用 native tools 绕过 Host 授权。
@@ -57,7 +57,6 @@ metadata:
en_US: Run a Dify application as a LangBot AgentRunner. en_US: Run a Dify application as a LangBot AgentRunner.
zh_Hans: 将 Dify 应用作为 LangBot AgentRunner 运行。 zh_Hans: 将 Dify 应用作为 LangBot AgentRunner 运行。
spec: spec:
protocol_version: "1"
config: [] config: []
capabilities: # 字段语义见 PROTOCOL_V1 §4.3 capabilities: # 字段语义见 PROTOCOL_V1 §4.3
streaming: true streaming: true
+22 -26
View File
@@ -41,12 +41,12 @@ Agent / binding 的持久化形态。
外部 harness runnerClaude Code、Codex、Kimi Code 等)也是 `AgentRunner`:它们消费 event-first `AgentRunContext`、返回 `AgentRunResult`,并通过 Host 授权的 state/storage/artifact API 保存跨轮次指针。它们内部可以继续使用自己的 session、tool loop、MCP、上下文压缩和权限模型。 外部 harness runnerClaude Code、Codex、Kimi Code 等)也是 `AgentRunner`:它们消费 event-first `AgentRunContext`、返回 `AgentRunResult`,并通过 Host 授权的 state/storage/artifact API 保存跨轮次指针。它们内部可以继续使用自己的 session、tool loop、MCP、上下文压缩和权限模型。
## 3. 版本协商 ## 3. 协议演进
- `AgentRunnerManifest.protocol_version` 声明 runner 实现的协议大版本,当前为 `"1"` 当前 AgentRunner 合同不暴露显式 `protocol_version` 字段。协议演进先按字段级兼容规则处理:
- `AgentRuntimeContext.protocol_version``ctx.runtime.protocol_version`)声明 Host 下发的协议大版本。
- Host 发现 runner 时校验 `protocol_version` 兼容性;不兼容的 runner 不进入可用列表,只记 warning - 新增可选字段保持向后兼容
- 字段级演进规则:新增可选字段不提升大版本;删除或改语义需要提升大版本 - 删除字段或改变既有字段语义需要在 SDK 发布前完成;发布后应走新的显式兼容方案
- 结果流演进:Host **必须忽略未知 result type 并记录 warning**(除非该 type 明确要求强校验)。新增 result type 不提升大版本。 - 结果流演进:Host **必须忽略未知 result type 并记录 warning**(除非该 type 明确要求强校验)。新增 result type 不提升大版本。
## 4. Discovery 协议 ## 4. Discovery 协议
@@ -68,7 +68,6 @@ class AgentRunnerManifest(BaseModel):
name: str name: str
label: I18nObject label: I18nObject
description: I18nObject | None = None description: I18nObject | None = None
protocol_version: str = "1"
capabilities: AgentRunnerCapabilities capabilities: AgentRunnerCapabilities
permissions: AgentRunnerPermissions permissions: AgentRunnerPermissions
context: AgentRunnerContextPolicy context: AgentRunnerContextPolicy
@@ -324,7 +323,6 @@ class ContextAPICapabilities(BaseModel):
```python ```python
class AgentRuntimeContext(BaseModel): class AgentRuntimeContext(BaseModel):
host: str = "langbot" host: str = "langbot"
protocol_version: str = "1"
langbot_version: str | None = None langbot_version: str | None = None
trace_id: str trace_id: str
deadline_at: float | None = None deadline_at: float | None = None
@@ -548,38 +546,36 @@ Host 必须校验 `state.updated` 的 scope、key、value 大小和 JSON 可序
```python ```python
# Model # Model
await api.models.invoke(model_id, messages, tools=None, extra_args=None) await api.invoke_llm(model_id, messages, funcs=None, extra_args=None)
await api.models.stream(model_id, messages, tools=None, extra_args=None) async for chunk in api.invoke_llm_stream(model_id, messages, funcs=None, extra_args=None):
await api.models.rerank(model_id, query, documents, top_k=None) ...
await api.invoke_rerank(rerank_model_id, query, documents, top_k=None)
# Tool # Tool
await api.tools.get_detail(tool_name) await api.get_tool_detail(tool_name)
await api.tools.call(tool_name, parameters) await api.call_tool(tool_name, parameters)
# Knowledge # Knowledge
await api.knowledge.retrieve(kb_id, query_text, top_k=5, filters=None) await api.retrieve_knowledge(kb_id, query_text, top_k=5, filters=None)
# History(返回 Transcript projection,不返回原始平台 payload # History(返回 Transcript projection,不返回原始平台 payload
await api.history.page(conversation_id=None, before_cursor=None, after_cursor=None, await api.history_page(conversation_id=None, before_cursor=None, after_cursor=None,
limit=50, direction="backward", include_artifacts=False) limit=50, direction="backward", include_artifacts=False)
await api.history.search(query, filters=None, top_k=10) await api.history_search(query, filters=None, top_k=10)
# Event(返回稳定 event envelope 或受限 raw ref,不默认返回大 payload # Event(返回稳定 event envelope 或受限 raw ref,不默认返回大 payload
await api.events.get(event_id) await api.event_get(event_id)
await api.events.page(before_cursor=None, limit=50) await api.event_page(before_cursor=None, limit=50)
# Artifact(必须支持大小限制、MIME 校验、过期时间和授权范围) # Artifact(必须支持大小限制、MIME 校验、过期时间和授权范围)
await api.artifacts.metadata(artifact_id) await api.artifact_metadata(artifact_id)
await api.artifacts.read_range(artifact_id, offset=0, length=65536) await api.artifact_read_range(artifact_id, offset=0, length=65536)
await api.artifacts.open_stream(artifact_id)
# State / Storage # State / Storage
await api.state.get(scope, key); await api.state.set(scope, key, value); await api.state.delete(scope, key) await api.state_get(scope, key); await api.state_set(scope, key, value); await api.state_delete(scope, key)
await api.storage.get(area, key); await api.storage.set(area, key, value) await api.state_list(scope, prefix=None)
await api.storage.delete(area, key); await api.storage.list(area, prefix=None) await api.get_plugin_storage(key); await api.set_plugin_storage(key, value); await api.delete_plugin_storage(key)
await api.get_workspace_storage(key); await api.set_workspace_storage(key, value); await api.delete_workspace_storage(key)
# Platform(受限能力,默认不开放,需 manifest + binding policy + 用户审批同时允许)
await api.platform.request_action(action, target, payload)
``` ```
`state``storage` 的建议边界:`state` 放小型 JSONconversation / actor / runner / binding),`storage` 放 blob 或较大数据(插件私有数据、workspace 数据、checkpoint)。 `state``storage` 的建议边界:`state` 放小型 JSONconversation / actor / runner / binding),`storage` 放 blob 或较大数据(插件私有数据、workspace 数据、checkpoint)。
@@ -124,7 +124,6 @@ class AgentRuntimeContext(typing.TypedDict):
"""Agent runtime context.""" """Agent runtime context."""
langbot_version: str | None langbot_version: str | None
protocol_version: str
trace_id: str | None trace_id: str | None
deadline_at: float | None deadline_at: float | None
metadata: dict[str, typing.Any] metadata: dict[str, typing.Any]
@@ -272,7 +271,6 @@ class AgentRunContextBuilder:
# Build runtime context # Build runtime context
runtime: AgentRuntimeContext = { runtime: AgentRuntimeContext = {
'langbot_version': self.ap.ver_mgr.get_current_version(), 'langbot_version': self.ap.ver_mgr.get_current_version(),
'protocol_version': descriptor.protocol_version,
'trace_id': run_id, 'trace_id': run_id,
'deadline_at': self._build_deadline_from_binding(binding), 'deadline_at': self._build_deadline_from_binding(binding),
'metadata': { 'metadata': {
@@ -424,6 +422,5 @@ class AgentRunContextBuilder:
'artifact_read': artifact_read_enabled, 'artifact_read': artifact_read_enabled,
'state': state_enabled, 'state': state_enabled,
'storage': True, 'storage': True,
'prompt_get': False,
}, },
} }
@@ -36,9 +36,6 @@ class AgentRunnerDescriptor(pydantic.BaseModel):
plugin_version: str | None = None plugin_version: str | None = None
"""Optional plugin version""" """Optional plugin version"""
protocol_version: str = '1'
"""SDK protocol version, default '1'"""
config_schema: list[dict[str, typing.Any]] = [] config_schema: list[dict[str, typing.Any]] = []
"""Configuration schema using DynamicForm format""" """Configuration schema using DynamicForm format"""
@@ -97,8 +97,6 @@ class AgentRunOrchestrator:
session_query_id = adapter_context.get('query_id') session_query_id = adapter_context.get('query_id')
if 'params' in adapter_context: if 'params' in adapter_context:
context['adapter']['extra']['params'] = adapter_context['params'] context['adapter']['extra']['params'] = adapter_context['params']
if adapter_context.get('prompt_get'):
context['context']['available_apis']['prompt_get'] = True
state_context = build_state_context(event, binding, descriptor) state_context = build_state_context(event, binding, descriptor)
run_id = context['run_id'] run_id = context['run_id']
@@ -149,7 +149,6 @@ class QueryEntryAdapter:
return { return {
'params': cls.build_params(query), 'params': cls.build_params(query),
'query_id': getattr(query, 'query_id', None), 'query_id': getattr(query, 'query_id', None),
'prompt_get': cls._has_effective_prompt(query),
} }
@classmethod @classmethod
@@ -187,12 +186,6 @@ class QueryEntryAdapter:
) )
return False return False
@classmethod
def _has_effective_prompt(cls, query: pipeline_query.Query) -> bool:
prompt = getattr(query, 'prompt', None)
messages = getattr(prompt, 'messages', None) if prompt is not None else None
return isinstance(messages, list)
# Private helper methods # Private helper methods
@classmethod @classmethod
+1 -3
View File
@@ -80,7 +80,7 @@ class AgentRunnerRegistry:
runner_data: Raw runner data from plugin runtime with fields: runner_data: Raw runner data from plugin runtime with fields:
- plugin_author, plugin_name, runner_name - plugin_author, plugin_name, runner_name
- manifest (full component manifest dict) - manifest (full component manifest dict)
- protocol_version, capabilities, permissions, config (extracted from spec) - capabilities, permissions, config (extracted from spec)
Returns: Returns:
AgentRunnerDescriptor if valid, None if invalid AgentRunnerDescriptor if valid, None if invalid
@@ -114,7 +114,6 @@ class AgentRunnerRegistry:
# SDK now provides these directly extracted from spec. Fall back to # SDK now provides these directly extracted from spec. Fall back to
# manifest.spec for older runtimes/tests that return the raw manifest. # manifest.spec for older runtimes/tests that return the raw manifest.
protocol_version = runner_data.get('protocol_version') or spec.get('protocol_version', '1')
config_schema = runner_data.get('config') or spec.get('config', []) config_schema = runner_data.get('config') or spec.get('config', [])
capabilities = runner_data.get('capabilities') or spec.get('capabilities', {}) capabilities = runner_data.get('capabilities') or spec.get('capabilities', {})
permissions = runner_data.get('permissions') or spec.get('permissions', {}) permissions = runner_data.get('permissions') or spec.get('permissions', {})
@@ -136,7 +135,6 @@ class AgentRunnerRegistry:
plugin_name=plugin_name, plugin_name=plugin_name,
runner_name=runner_name, runner_name=runner_name,
plugin_version=runner_data.get('plugin_version'), plugin_version=runner_data.get('plugin_version'),
protocol_version=protocol_version,
config_schema=config_schema, config_schema=config_schema,
capabilities=capabilities, capabilities=capabilities,
permissions=permissions, permissions=permissions,
+1 -47
View File
@@ -378,25 +378,6 @@ def _resolve_remove_think(data: dict[str, Any], query: Any | None) -> bool:
return False return False
def _dump_prompt_messages(query: Any) -> list[dict[str, Any]]:
"""Serialize the current effective prompt from a cached Query."""
prompt = getattr(query, 'prompt', None)
messages = getattr(prompt, 'messages', None) if prompt is not None else None
if not isinstance(messages, list):
return []
dumped: list[dict[str, Any]] = []
for message in messages:
if hasattr(message, 'model_dump'):
try:
dumped.append(message.model_dump(mode='json'))
except TypeError:
dumped.append(message.model_dump())
elif isinstance(message, dict):
dumped.append(message)
return dumped
def _merge_model_extra_args(model: Any, call_extra_args: Any) -> dict[str, Any]: def _merge_model_extra_args(model: Any, call_extra_args: Any) -> dict[str, Any]:
"""Merge persisted model extra_args with action-level overrides.""" """Merge persisted model extra_args with action-level overrides."""
merged: dict[str, Any] = {} merged: dict[str, Any] = {}
@@ -1250,7 +1231,7 @@ class RuntimeConnectionHandler(handler.Handler):
except Exception as e: except Exception as e:
return _make_rag_error_response(e, 'VectorStoreError', collection_id=collection_id) return _make_rag_error_response(e, 'VectorStoreError', collection_id=collection_id)
@self.action(PluginToRuntimeAction.GET_KNOWLEDGE_FILE_STREAM) @self.action(PluginToRuntimeAction.GET_KNOWLEDEGE_FILE_STREAM)
async def get_knowledge_file_stream(data: dict[str, Any]) -> handler.ActionResponse: async def get_knowledge_file_stream(data: dict[str, Any]) -> handler.ActionResponse:
storage_path = data['storage_path'] storage_path = data['storage_path']
try: try:
@@ -1458,32 +1439,6 @@ class RuntimeConnectionHandler(handler.Handler):
# ================= Agent History/Event APIs ================= # ================= Agent History/Event APIs =================
@self.action(PluginToRuntimeAction.PROMPT_GET)
async def prompt_get(data: dict[str, Any]) -> handler.ActionResponse:
"""Return the post-preprocessing effective prompt for a query-backed run."""
run_id = data.get('run_id')
caller_plugin_identity = data.get('caller_plugin_identity')
if not run_id:
return handler.ActionResponse.error(message='run_id is required')
session, error = await _validate_agent_run_session(
run_id,
caller_plugin_identity,
self.ap,
'Prompt get',
)
if error:
return error
query = _resolve_action_query(data, session, self.ap)
if query is None:
return handler.ActionResponse.error(
message='Prompt get is only available for query-backed agent runs',
)
return handler.ActionResponse.success(data={'prompt': _dump_prompt_messages(query)})
@self.action(PluginToRuntimeAction.HISTORY_PAGE) @self.action(PluginToRuntimeAction.HISTORY_PAGE)
async def history_page(data: dict[str, Any]) -> handler.ActionResponse: async def history_page(data: dict[str, Any]) -> handler.ActionResponse:
"""Page through transcript history for a conversation. """Page through transcript history for a conversation.
@@ -2236,7 +2191,6 @@ class RuntimeConnectionHandler(handler.Handler):
- runner_name - runner_name
- runner_description - runner_description
- manifest - manifest
- protocol_version
- capabilities - capabilities
- permissions - permissions
- config - config
+1 -1
View File
@@ -107,7 +107,7 @@ class RAGRuntimeService:
) )
async def get_file_stream(self, storage_path: str) -> bytes: async def get_file_stream(self, storage_path: str) -> bytes:
"""Handle GET_KNOWLEDGE_FILE_STREAM action. """Handle GET_KNOWLEDEGE_FILE_STREAM action.
Uses the storage manager abstraction to load file content, Uses the storage manager abstraction to load file content,
regardless of the underlying storage provider. regardless of the underlying storage provider.
@@ -159,4 +159,4 @@ class TestBuildAdapterContext:
context = QueryEntryAdapter.build_adapter_context(query, binding=None) context = QueryEntryAdapter.build_adapter_context(query, binding=None)
assert context == {'params': {}, 'query_id': 123, 'prompt_get': False} assert context == {'params': {}, 'query_id': 123}
@@ -56,7 +56,6 @@ class TestContextAccessStateDetermination:
"""Create mock runner descriptor.""" """Create mock runner descriptor."""
descriptor = MagicMock() descriptor = MagicMock()
descriptor.id = 'plugin:test/runner/default' descriptor.id = 'plugin:test/runner/default'
descriptor.protocol_version = '1.0'
descriptor.permissions = {} descriptor.permissions = {}
return descriptor return descriptor
@@ -90,7 +90,6 @@ class TestContextValidation:
"""Create a mock runner descriptor.""" """Create a mock runner descriptor."""
descriptor = MagicMock() descriptor = MagicMock()
descriptor.id = "plugin:test/plugin/runner" descriptor.id = "plugin:test/plugin/runner"
descriptor.protocol_version = "1"
descriptor.permissions = { descriptor.permissions = {
'history': ['page', 'search'], 'history': ['page', 'search'],
'events': ['get', 'page'], 'events': ['get', 'page'],
@@ -145,8 +144,7 @@ class TestContextValidation:
assert isinstance(validated.resources, AgentResources) assert isinstance(validated.resources, AgentResources)
assert validated.runtime is not None assert validated.runtime is not None
assert isinstance(validated.runtime, AgentRuntimeContext) assert isinstance(validated.runtime, AgentRuntimeContext)
assert validated.runtime.protocol_version == "1" assert "protocol_version" not in validated.runtime.model_dump()
assert "protocol_version" in validated.runtime.model_dump()
assert "sdk_protocol_version" not in validated.runtime.model_dump() assert "sdk_protocol_version" not in validated.runtime.model_dump()
assert "sdk_protocol_version" not in context_dict["runtime"] assert "sdk_protocol_version" not in context_dict["runtime"]
@@ -153,7 +153,6 @@ def make_descriptor() -> AgentRunnerDescriptor:
plugin_author="langbot", plugin_author="langbot",
plugin_name="local-agent", plugin_name="local-agent",
runner_name="default", runner_name="default",
protocol_version="1",
capabilities={ capabilities={
"streaming": True, "streaming": True,
"tool_calling": True, "tool_calling": True,
@@ -591,7 +590,7 @@ class TestQueryEntryAdapterParams:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_prompt_not_pushed_into_adapter_extra(self, clean_agent_state): async def test_prompt_not_pushed_into_adapter_extra(self, clean_agent_state):
"""Pipeline prompt is not pushed into adapter.extra.""" """Pipeline prompt is not pushed into adapter.extra or exposed via prompt_get."""
from langbot_plugin.api.entities.builtin.provider import prompt as provider_prompt from langbot_plugin.api.entities.builtin.provider import prompt as provider_prompt
db_engine = clean_agent_state db_engine = clean_agent_state
@@ -621,7 +620,7 @@ class TestQueryEntryAdapterParams:
context = plugin_connector.contexts[0] context = plugin_connector.contexts[0]
assert "prompt" not in context assert "prompt" not in context
assert "prompt" not in context["adapter"]["extra"] assert "prompt" not in context["adapter"]["extra"]
assert context["context"]["available_apis"]["prompt_get"] is True assert "prompt_get" not in context["context"]["available_apis"]
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_params_filtering_keeps_public_param(self, clean_agent_state): async def test_params_filtering_keeps_public_param(self, clean_agent_state):
+1 -3
View File
@@ -45,7 +45,6 @@ class FakeApplication:
'label': {'en_US': 'Local Agent'}, 'label': {'en_US': 'Local Agent'},
}, },
'spec': { 'spec': {
'protocol_version': '1',
'config': [], 'config': [],
'capabilities': {'streaming': True}, 'capabilities': {'streaming': True},
'permissions': {}, 'permissions': {},
@@ -63,7 +62,6 @@ class FakeApplication:
'label': {'en_US': 'Custom Agent'}, 'label': {'en_US': 'Custom Agent'},
}, },
'spec': { 'spec': {
'protocol_version': '1',
'config': [{'name': 'param1', 'type': 'string'}], 'config': [{'name': 'param1', 'type': 'string'}],
'capabilities': {}, 'capabilities': {},
'permissions': {}, 'permissions': {},
@@ -266,7 +264,7 @@ class TestDescriptorValidation:
assert descriptor.id == 'plugin:test/my-runner/default' assert descriptor.id == 'plugin:test/my-runner/default'
assert descriptor.get_plugin_id() == 'test/my-runner' assert descriptor.get_plugin_id() == 'test/my-runner'
assert descriptor.protocol_version == '1' assert 'protocol_version' not in AgentRunnerDescriptor.model_fields
def test_descriptor_capabilities(self): def test_descriptor_capabilities(self):
"""Descriptor capability helper methods.""" """Descriptor capability helper methods."""
@@ -35,7 +35,6 @@ def make_descriptor():
plugin_author='langbot', plugin_author='langbot',
plugin_name='local-agent', plugin_name='local-agent',
runner_name='default', runner_name='default',
protocol_version='1',
capabilities={'streaming': True}, capabilities={'streaming': True},
) )
@@ -29,7 +29,6 @@ def make_descriptor(runner_id: str = 'plugin:test/my-runner/default') -> AgentRu
plugin_author='test', plugin_author='test',
plugin_name='my-runner', plugin_name='my-runner',
runner_name='default', runner_name='default',
protocol_version='1',
capabilities={'streaming': True}, capabilities={'streaming': True},
) )