mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-03 12:34:37 +00:00
refactor(agent-runner): align config with agent semantics
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
- ✅ `AgentRunAPIProxy.state` — get/set/delete API
|
||||
- ✅ EventLog / Transcript / ArtifactStore — host 事实源
|
||||
- ✅ PersistentStateStore — 持久化状态存储
|
||||
- ✅ `max-round` / host-side history window 已从 LangBot Host/Pipeline 语义中移除;如某 runner 仍需要类似参数,应由该 runner 自己解释配置
|
||||
- ✅ Host-side history window 已从 LangBot Host 语义中移除;runner 自己管理 working context
|
||||
- ✅ 外部 harness context projection 已用 Claude Code runner 做 MVP 验证:context 文件、skill 投影、MCP 配置和 host-owned resume state
|
||||
|
||||
## 1. 设计原则
|
||||
@@ -35,11 +35,10 @@
|
||||
- 可投影给外部 harness 的 scoped context、MCP、skill 和 resource refs。
|
||||
- payload hard cap 和权限 guardrail。
|
||||
|
||||
### 1.2 不再把 `max-round` 作为目标设计
|
||||
### 1.2 Host 不定义通用历史窗口
|
||||
|
||||
`max-round` 这类历史窗口参数不应继续作为 AgentRunner 协议或 Pipeline adapter 的核心概念。
|
||||
|
||||
如果某个 runner 仍需要“最多读取多少轮历史”这样的策略参数,应由该 runner 在自己的 manifest/config schema 中声明,并作为 binding config 存到 `ctx.config` / `runner_config`。Host 只提供 history pull API、cursor、hard cap 和权限边界;runner 自己决定是否读取、读取多少、如何截断和压缩。
|
||||
历史窗口策略不应继续作为 AgentRunner 协议或 Pipeline adapter 的核心概念。
|
||||
Host 只提供 history pull API、cursor、hard cap 和权限边界;runner 自己决定是否读取、读取多少、如何截断和压缩。
|
||||
|
||||
当前 official local-agent 方向是通过 Host history API 拉取 transcript,并由 runner 自己管理模型上下文。它不依赖 Pipeline adapter 下发历史窗口。
|
||||
|
||||
@@ -100,7 +99,7 @@ class AgentRunContext(BaseModel):
|
||||
- delivery 能力,例如是否支持 streaming、reply target、平台限制。
|
||||
- 已授权资源列表。
|
||||
- context cursors 和可用 API 能力。
|
||||
- runner binding config。
|
||||
- Agent/runner config。
|
||||
|
||||
这些是 agent 决定下一步需要的最低信息。
|
||||
|
||||
@@ -324,7 +323,7 @@ LangBot core 不应内置官方 agent 的业务流程:
|
||||
|
||||
**已完成(当前分支)**:
|
||||
|
||||
- ✅ `max-round` 不再是协议字段,也不再是 Host / Pipeline 通用语义
|
||||
- ✅ Host 不再定义通用历史窗口字段或策略
|
||||
- ✅ 新 runner 默认不收到历史窗口
|
||||
- ✅ `AgentRunContext` 增加 `context` / cursor / access capabilities
|
||||
- ✅ `AgentRunAPIProxy` 增加 history / events / artifacts / state API
|
||||
|
||||
@@ -393,7 +393,7 @@ Proxy 是 runner 访问 host 能力的唯一入口:
|
||||
- ✅ `PipelineAdapter` — Query → Event + Binding
|
||||
- ✅ `AgentBinding` 抽象
|
||||
- ✅ `AgentEventEnvelope` 抽象
|
||||
- ✅ `max-round` 从目标协议中移除;类似历史窗口参数若仍需要,应由具体 runner 的 manifest/config schema 暴露为 binding config
|
||||
- ✅ Host 不定义通用历史窗口字段或策略;runner 自己管理 working context
|
||||
- ✅ `PersistentStateStore` — 持久化状态存储
|
||||
- ✅ `EventLogStore` / `TranscriptStore` / `ArtifactStore`
|
||||
- ✅ history / artifact / event 的受限拉取 API
|
||||
|
||||
@@ -75,7 +75,7 @@ SDK Runtime RUN_AGENT -> plugin AgentRunner.run()
|
||||
- `PipelineService.get_pipeline_metadata()` 不直接访问插件 runtime,而是读取 registry。
|
||||
- 旧 `RequestRunner` 只作为迁移参考,不作为最终运行路径。
|
||||
- `AgentRunOrchestrator` 是 LangBot 侧运行编排层:负责 runner 绑定解析、资源授权、context envelope provisioning、run scope 注册、插件调用和结果归一化;不负责决定 Agent 的最终 prompt/window/压缩策略。
|
||||
- 插件是无状态执行单元:多个 Pipeline 可以绑定同一个 runner id,并分别保存自己的 `ai.runner_config[id]`;运行时 LangBot 只把当前绑定配置放入 `ctx.config` 转发给同一个插件 runner。
|
||||
- 插件是无状态执行单元:多个 Agent 可以绑定同一个 runner id,并分别保存自己的 `ai.runner_config[id]`;运行时 LangBot 只把当前 Agent/runner config 放入 `ctx.config` 转发给同一个插件 runner。
|
||||
- 禁止按 Pipeline 或 runner config 创建多个插件实例。需要跨请求持久化的状态必须走明确授权的 plugin storage / workspace storage / 外部服务,不能隐式保存在 per-pipeline 插件对象里。
|
||||
- EBA 只做字段预留,不在本轮实现 EventBus、EventRouter、平台动作执行。
|
||||
|
||||
@@ -191,7 +191,7 @@ Pipeline adapter 的 `prompt` 和公开业务变量不进入顶层协议字段
|
||||
- filtered params -> `ctx.adapter.extra["params"]`
|
||||
- legacy/effective prompt 可以暂存到 `ctx.adapter.extra["prompt"]`,但 official
|
||||
runner 不应把它当作行为契约
|
||||
- LangBot Host 不生成 `bootstrap.messages`、`adapter_messages` 或 context packaging 元数据
|
||||
- LangBot Host 不生成 bootstrap history payload 或 context packaging 元数据
|
||||
|
||||
现阶段不要把新的压缩或 token-budget 裁剪塞回 Pipeline stage。Pipeline 只负责入口适配;完整历史和长期上下文由 EventLog / Transcript / pull APIs / future ContextCompressor 支撑。
|
||||
|
||||
@@ -216,7 +216,7 @@ ContextCompressor
|
||||
- 完整历史属于 LangBot host,不属于插件实例。插件仍是 singleton/stateless。
|
||||
- `ctx.bootstrap.messages` 不是 Host 默认下发的 working context。
|
||||
- 每轮不能全量复制/序列化完整历史给插件 runtime;否则长会话会产生 O(n) 成本和跨进程 payload 膨胀。
|
||||
- `max-round` 或类似窗口规则不属于 LangBot Host / Pipeline 语义。
|
||||
- 通用历史窗口规则不属于 LangBot Host 语义。
|
||||
- LiteLLM 接入后,模型窗口元信息应作为 resource/runtime metadata 暴露给 runner,由 runner 决定预算和压缩策略。
|
||||
- `ContextCompressor` 生成的是派生 summary/checkpoint,不能覆盖或删除 raw history。
|
||||
- 重启恢复依赖持久化 store 和 summary checkpoint,不依赖 `SessionManager` 里的进程内 conversation list。
|
||||
@@ -470,7 +470,7 @@ async def run_from_query(query: pipeline_query.Query) -> AsyncGenerator[Message
|
||||
- SDK `AgentRunContext` 保持 event-first:`event/input/delivery/resources/context/state/runtime/config/bootstrap/adapter`。
|
||||
- LangBot context builder 只从 `AgentEventEnvelope + AgentBinding` 写入稳定协议字段。
|
||||
- Pipeline adapter 可以把公开业务变量写入 `ctx.adapter.extra["params"]`;legacy/effective prompt 若保留在 `ctx.adapter.extra["prompt"]`,也只属于 adapter metadata。
|
||||
- 保持 `ctx.config` 只表达静态绑定配置。
|
||||
- 保持 `ctx.config` 只表达静态 Agent/runner config。
|
||||
|
||||
### Step 2:增强宿主 AgentRun proxy action
|
||||
|
||||
@@ -489,7 +489,7 @@ async def run_from_query(query: pipeline_query.Query) -> AsyncGenerator[Message
|
||||
|
||||
### Step 4:local-agent parity
|
||||
|
||||
- 使用静态绑定配置 `ctx.config["prompt"]`,不读取 `ctx.adapter.extra["prompt"]`。
|
||||
- 使用静态 Agent/runner config `ctx.config["prompt"]`,不读取 `ctx.adapter.extra["prompt"]`。
|
||||
- 通过 Host history API 拉取 transcript,不读取 `ctx.bootstrap.messages` 或 adapter window 字段。
|
||||
- 当前 user message 从 `ctx.input.contents` 构造,保留多模态内容。
|
||||
- RAG 只替换/插入文本部分,不丢图片/文件。
|
||||
@@ -544,7 +544,7 @@ async def run_from_query(query: pipeline_query.Query) -> AsyncGenerator[Message
|
||||
- `ChatMessageHandler` 不包含插件 runner 解析和 wrapper。
|
||||
- `PipelineService` 不直接拼插件 runner metadata。
|
||||
- 所有 runner 配置使用 `ai.runner.id` + `ai.runner_config`。
|
||||
- 插件 runtime 不为每个 Pipeline 或 runner 配置创建插件实例;`runner_config` 只作为绑定配置随 `ctx.config` 传入。
|
||||
- 插件 runtime 不为每个 Agent 或 runner 配置创建插件实例;`runner_config` 只作为 Agent/runner config 随 `ctx.config` 传入。
|
||||
- 主聊天路径不再通过旧内置 runner 执行业务 runner。迁移期间旧文件可以保留。
|
||||
- 插件只能访问 `ctx.resources` 授权的模型、工具、知识库和文件。
|
||||
- 宿主 action 能为 AgentRunner 调用恢复必要 Query 语义,插件不需要拿裸 Query。
|
||||
|
||||
@@ -265,7 +265,7 @@ Claude Code / Codex 这类外部 harness 不能直接持有 Python 进程内的
|
||||
- 默认输出:`message.completed` + `run.completed`
|
||||
- 默认权限:`permission-mode=plan`、`max-turns=1`、`disallowedTools=AskUserQuestion`
|
||||
- 默认状态:如果 Claude Code 返回 `session_id`,runner 通过 `state.updated` 写回 `external.session_id`
|
||||
- 工作目录:优先使用 binding config 的 `working-directory`,其次使用 Host state 中的 `external.working_directory`
|
||||
- 工作目录:优先使用 Agent/runner config 的 `working-directory`,其次使用 Host state 中的 `external.working_directory`
|
||||
|
||||
### 8.2 Context / skill / MCP 投影
|
||||
|
||||
@@ -274,8 +274,8 @@ Claude Code runner 当前把 LangBot event-first context 投影给外部 harness
|
||||
- 写入 `agent-context.json`,schema 为 `langbot.agent_runner.external_harness_context.v1`
|
||||
- 写入 `LANGBOT_CONTEXT.md`,作为人类可读摘要
|
||||
- 将 prompt prefix 指向 context 文件路径
|
||||
- 可把 binding 提供的 `skills-json` 写入 Claude Code 原生 `.claude/skills/<name>/SKILL.md`
|
||||
- 可把 binding 提供的 `mcp-config-json` 写成每次 run 的 MCP config,并通过 `--mcp-config` / `--strict-mcp-config` 传给 Claude Code
|
||||
- 可把 Agent/runner config 提供的 `skills-json` 写入 Claude Code 原生 `.claude/skills/<name>/SKILL.md`
|
||||
- 可把 Agent/runner config 提供的 `mcp-config-json` 写成每次 run 的 MCP config,并通过 `--mcp-config` / `--strict-mcp-config` 传给 Claude Code
|
||||
- 可通过 `enable-langbot-mcp=true` 启用 SDK-owned per-run LangBot MCP bridge,使 Claude Code 通过 MCP 调用受限的 `AgentRunAPIProxy` 能力
|
||||
|
||||
这些投影目前由 runner adapter 完成;长期更理想的形态是 LangBot Host 负责生成 scoped resource projection,runner 只负责适配 Claude Code 的原生目录和 CLI 参数。
|
||||
@@ -287,12 +287,12 @@ Claude Code runner 当前把 LangBot event-first context 投影给外部 harness
|
||||
- WebUI Debug Chat 能通过 Pipeline adapter 调用 `claude-code-agent`
|
||||
- Claude Code 能读取 LangBot context 文件并按指令输出 sentinel
|
||||
- Skill 文件可以投影到 `.claude/skills/`
|
||||
- MCP config 可以通过 binding config 投影为 Claude Code CLI 参数
|
||||
- MCP config 可以通过 Agent/runner config 投影为 Claude Code CLI 参数
|
||||
- SDK-owned per-run LangBot MCP bridge 可以被真实 Claude Code CLI 调用,并通过 `langbot_get_current_event` 读取当前 run_id
|
||||
- `external.session_id` 与 `external.working_directory` 可以写入 host-owned state,用于后续 resume
|
||||
- `codex-agent` 可通过 WebUI Debug Chat 调用本机 Codex CLI,读取 LangBot event context,并把 Codex `thread_id` 写入 host-owned state
|
||||
- SDK-owned per-run LangBot MCP bridge 可以被真实 Codex CLI 调用,并通过 `langbot_get_current_event` 读取当前 run_id
|
||||
- 对需要代理的本地运行环境,`codex-agent` 可通过 binding config 的 `environment-json` 显式传递非 secret 环境变量
|
||||
- 对需要代理的本地运行环境,`codex-agent` 可通过 Agent/runner config 的 `environment-json` 显式传递非 secret 环境变量
|
||||
|
||||
下一轮测试入口见 [PHASE1_QA_ACCEPTANCE_MATRIX.md](./PHASE1_QA_ACCEPTANCE_MATRIX.md)。
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ bin/lbs case list
|
||||
|
||||
- runner 选项来自插件 registry。
|
||||
- 保存后配置仍为 `ai.runner.id` + `ai.runner_config[id]`。
|
||||
- `runner_config` 表示 binding config,不表示插件实例状态。
|
||||
- `runner_config` 表示 Agent/runner config,不表示插件实例状态。
|
||||
- 插件没有循环重启或 metadata 加载失败。
|
||||
|
||||
### 5.2 主聊天路径
|
||||
@@ -175,7 +175,7 @@ Rerank、remove-think、文件输入等场景只在本次改动直接涉及时
|
||||
|
||||
1. 确认 `codex` CLI 在 LangBot runtime host 上可执行。
|
||||
2. 绑定 `plugin:langbot/codex-agent/default`。
|
||||
3. 如需要代理,使用 binding config 的 `environment-json` 显式传入。
|
||||
3. 如需要代理,使用 Agent/runner config 的 `environment-json` 显式传入。
|
||||
4. 在 Debug Chat 执行一次真实 smoke。
|
||||
5. 检查 JSONL 事件、last message、host-owned state。
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
- ✅ Host 支持 `run_id` session authorization
|
||||
- ✅ Host 能从当前 Pipeline 入口生成 event-first context
|
||||
- ✅ `messages` 降级为 optional bootstrap
|
||||
- ✅ `max-round` 不出现在协议实体中,也不属于 Host / Pipeline 语义;类似参数若存在,由 runner 自己解释 `ctx.config`
|
||||
- ✅ Host 不定义通用历史窗口字段或策略;runner 自己管理 working context
|
||||
- ✅ Proxy 覆盖 model、tool、knowledge、state/storage
|
||||
- ✅ History / Event / Artifact / State API 已落地
|
||||
- ✅ EventLog / Transcript / ArtifactStore / PersistentStateStore 已落地
|
||||
@@ -148,7 +148,7 @@ Host 不使用该声明给 runner inline 历史窗口。默认原则:
|
||||
- Host 只 inline 当前 event / input 和 context handles。
|
||||
- Runner 拥有 working context assembly。
|
||||
- Runner 可在授权后通过 Host history / event / artifact / state APIs 拉取更多上下文。
|
||||
- `max-round` 或类似窗口参数不属于 Protocol v1 字段,也不属于 Pipeline / Host 通用语义;如果某个 runner 需要,应由 runner 自己解释 `ctx.config`。
|
||||
- 历史窗口策略不属于 Protocol v1 字段,也不属于 Host 通用语义。
|
||||
|
||||
## 4. Run 协议
|
||||
|
||||
@@ -194,8 +194,8 @@ class AgentRunContext(BaseModel):
|
||||
- `event` 是必选字段,Protocol v1 是 event-first。
|
||||
- `input` 表示当前事件的主输入,不等于历史消息。
|
||||
- `bootstrap` 是可选字段;LangBot Host 默认不填历史窗口。
|
||||
- `adapter` 只放 Pipeline adapter 字段,runner 不应依赖它做长期能力。
|
||||
- `config` 是 Host binding config,不是插件实例状态。
|
||||
- `adapter` 只放入口 adapter 的非核心元数据,runner 不应依赖它做长期能力。
|
||||
- `config` 是 Agent/runner config,不是插件实例状态。
|
||||
|
||||
### 4.3 AgentTrigger
|
||||
|
||||
@@ -345,7 +345,7 @@ class BootstrapContext(BaseModel):
|
||||
- `bootstrap.messages` 不是 LangBot Host 的默认行为。
|
||||
- 自管 context runner 默认应收到空 bootstrap。
|
||||
- Host 不应为了”帮 agent 更聪明”而自动拼接完整 transcript。
|
||||
- 类似历史窗口策略应由具体 runner 自己解释 binding config,并通过 Host history API 拉取历史;new/official runners 不应依赖 Pipeline adapter 下发历史窗口。
|
||||
- 历史窗口策略由 runner 自己管理,并通过 Host history API 按需拉取历史;new/official runners 不应依赖入口 adapter 下发历史窗口。
|
||||
|
||||
### 4.10 RuntimeContext
|
||||
|
||||
@@ -661,14 +661,14 @@ Pipeline 是当前入口 adapter,不是协议中心。
|
||||
- ✅ `PipelineAdapter.query_to_event(query)` — 从 `Query` 构造 `AgentEventEnvelope`
|
||||
- ✅ `PipelineAdapter.pipeline_config_to_binding(query, runner_id)` — 从 Pipeline config 构造临时 AgentBinding
|
||||
- ✅ `run_from_query()` 委托到 `run(event, binding)`
|
||||
- ✅ runner-specific config 从 Pipeline 当前绑定配置透传到 `AgentBinding.runner_config` / `ctx.config`
|
||||
- ✅ Agent/runner config 从当前配置容器透传到 `AgentBinding.runner_config` / `ctx.config`
|
||||
- ✅ Query-only 字段放入 `adapter` context
|
||||
|
||||
Pipeline adapter 负责:
|
||||
|
||||
- 从 `Query` 构造 `AgentEventContext`。
|
||||
- 从 Pipeline config 构造临时 AgentBinding。
|
||||
- 从当前 runner binding config 构造 `ctx.config`。
|
||||
- 从当前配置容器构造临时 AgentBinding。
|
||||
- 从当前 Agent/runner config 构造 `ctx.config`。
|
||||
- 保留必要的 legacy adapter metadata,但不定义历史窗口、prompt 组装或 agentic context 策略。
|
||||
- 后续若需要传递 preprocessing / hook 后的有效指令,应通过 Host prompt/instruction
|
||||
package pull API 暴露能力位和引用,而不是继续把 prompt 推入 `ctx.adapter.extra`。
|
||||
@@ -685,7 +685,7 @@ Protocol v1 已在当前分支完成:
|
||||
- ✅ Host 支持 `run_id` session authorization
|
||||
- ✅ Host 能从当前 Pipeline 入口生成 event-first context
|
||||
- ✅ `messages` 降级为 optional bootstrap
|
||||
- ✅ `max-round` 不出现在协议实体中,也不属于 Host / Pipeline 语义
|
||||
- ✅ Host 不定义通用历史窗口字段或策略
|
||||
- ✅ Proxy 至少覆盖 model、tool、knowledge、state/storage
|
||||
- ✅ History / event / artifact API 已落地
|
||||
- ✅ EventLog / Transcript / ArtifactStore / PersistentStateStore 已落地
|
||||
|
||||
@@ -80,7 +80,7 @@ Pipeline path 已获得 event-first host capabilities:
|
||||
|
||||
LangBot 不应成为最终 agentic context manager。它应提供事实源、默认上下文引用和按需读取 API;agent 或其背后的 runtime 负责历史剪裁、摘要、召回和 KV cache 策略。
|
||||
|
||||
`max-round` 这类历史窗口参数不应作为目标协议继续扩展;如果某个 runner 仍需要类似策略,应由该 runner 的 manifest/config schema 暴露为 binding config。
|
||||
Host 不定义通用历史窗口字段或策略;runner 通过 Host pull API 按需拉取历史并自行管理 working context。
|
||||
|
||||
详见 [AGENT_CONTEXT_PROTOCOL.md](./AGENT_CONTEXT_PROTOCOL.md)。
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ v2 只是在 Host 上新增一层可选能力。需要管控面的 runner 或管
|
||||
- EventLog / Transcript / ArtifactStore / PersistentStateStore
|
||||
- History / Event / Artifact / State / Storage pull APIs
|
||||
- AgentRunner result stream 和受控错误回流
|
||||
- binding config 与 host-owned state
|
||||
- Agent/runner config 与 host-owned state
|
||||
|
||||
这些能力足够支持一次 `runner.run(ctx)` 内的安全执行,但不足以承担完整 runtime 管控面。
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
### Runner Plugin 负责
|
||||
|
||||
- 遵守 LangBot 下发的 binding config、授权资源和运行约束。
|
||||
- 遵守 LangBot 下发的 Agent/runner config、授权资源和运行约束。
|
||||
- 将 LangBot 资源投影成目标 runner 可消费的形式,例如 context 文件、MCP 配置、环境变量或 CLI 参数。
|
||||
- 不把长期状态保存在插件实例内;需要跨轮次保存的外部 session id / working directory 等状态应写入 host-owned state。
|
||||
- 对外部进程做最小必要封装,包括命令参数构造、超时、取消、输出解析和错误映射。
|
||||
@@ -70,4 +70,3 @@ Claude Code、Codex、Kimi Code 等外部 harness 可以继续使用自身的权
|
||||
- Codex / Kimi runner 全量接入。
|
||||
- EBA 分支完整迁移和联调。
|
||||
- 发布级安全 hardening 的完整实现。
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class ConfigMigration:
|
||||
Responsibilities:
|
||||
- Resolve runner ID from new ai.runner.id or old ai.runner.runner
|
||||
- Map old built-in runner names to official plugin runner IDs
|
||||
- Extract runtime runner config from ai.runner_config
|
||||
- Extract current Agent/runner config from ai.runner_config
|
||||
- Migrate old ai.<runner-name> blocks into ai.runner_config
|
||||
"""
|
||||
|
||||
@@ -74,7 +74,7 @@ class ConfigMigration:
|
||||
pipeline_config: dict[str, typing.Any],
|
||||
runner_id: str,
|
||||
) -> dict[str, typing.Any]:
|
||||
"""Resolve runner binding configuration from pipeline configuration.
|
||||
"""Resolve Agent/runner configuration from pipeline configuration.
|
||||
|
||||
Runtime code should only read the migrated format. Legacy
|
||||
ai.<runner-name> blocks are handled by migration helpers, not by the
|
||||
@@ -114,9 +114,6 @@ class ConfigMigration:
|
||||
if old_runner_name:
|
||||
old_config = ai_config.get(old_runner_name, {})
|
||||
if old_config:
|
||||
old_config = dict(old_config)
|
||||
if runner_id == OLD_RUNNER_TO_PLUGIN_RUNNER_ID['local-agent']:
|
||||
old_config.pop('max-round', None)
|
||||
return ConfigMigration.normalize_runner_config_for_migration(runner_id, old_config)
|
||||
|
||||
return {}
|
||||
@@ -126,7 +123,7 @@ class ConfigMigration:
|
||||
runner_id: str,
|
||||
runner_config: dict[str, typing.Any],
|
||||
) -> dict[str, typing.Any]:
|
||||
"""Normalize released legacy runner config before storing binding config.
|
||||
"""Normalize released legacy runner config before storing Agent/runner config.
|
||||
|
||||
Runtime code should not carry aliases. This helper is intentionally used
|
||||
only by config migration so AgentRunner implementations can consume the
|
||||
|
||||
@@ -22,7 +22,7 @@ class AgentTrigger(typing.TypedDict):
|
||||
"""Agent trigger information."""
|
||||
|
||||
type: str
|
||||
source: str # 'pipeline' or 'event_router'
|
||||
source: str
|
||||
timestamp: int | None
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ class ConversationContext(typing.TypedDict):
|
||||
bot_id: str | None
|
||||
workspace_id: str | None
|
||||
session_id: str | None
|
||||
pipeline_uuid: str | None
|
||||
|
||||
|
||||
class AgentInput(typing.TypedDict):
|
||||
@@ -128,8 +127,9 @@ class AgentRunContextPayload(typing.TypedDict):
|
||||
|
||||
Protocol v1 structure - matches SDK AgentRunContext.
|
||||
|
||||
Note: The 'config' field contains the binding config from ai.runner_config[runner_id],
|
||||
which is Pipeline's configuration for this specific runner binding (not plugin instance config).
|
||||
Note: The 'config' field contains the current Agent/runner config
|
||||
from ai.runner_config[runner_id] while Pipeline remains the temporary
|
||||
configuration container. It is not plugin instance config.
|
||||
"""
|
||||
|
||||
run_id: str
|
||||
@@ -144,9 +144,9 @@ class AgentRunContextPayload(typing.TypedDict):
|
||||
context: dict[str, typing.Any] # ContextAccess - REQUIRED for Protocol v1
|
||||
state: AgentRunState
|
||||
runtime: AgentRuntimeContext
|
||||
config: dict[str, typing.Any] # Binding config from ai.runner_config[runner_id]
|
||||
config: dict[str, typing.Any] # Agent/runner config from ai.runner_config[runner_id]
|
||||
bootstrap: dict[str, typing.Any] | None # Optional bootstrap context
|
||||
adapter: dict[str, typing.Any] | None # Pipeline adapter context
|
||||
adapter: dict[str, typing.Any] | None # Entry adapter context
|
||||
metadata: dict[str, typing.Any] # Additional metadata
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ class AgentRunContextBuilder:
|
||||
- Build input from event
|
||||
- Build state snapshot from PersistentStateStore
|
||||
- Build runtime context with host info, trace_id, deadline
|
||||
- Set config from runner binding configuration.
|
||||
- Set config from current Agent/runner configuration.
|
||||
|
||||
Pipeline Query adaptation belongs to PipelineAdapter, not this builder.
|
||||
"""
|
||||
@@ -184,7 +184,7 @@ class AgentRunContextBuilder:
|
||||
|
||||
Args:
|
||||
event: Event envelope
|
||||
binding: Agent binding configuration
|
||||
binding: Agent binding
|
||||
descriptor: Runner descriptor
|
||||
resources: Built resources
|
||||
|
||||
@@ -205,7 +205,7 @@ class AgentRunContextBuilder:
|
||||
conversation: ConversationContext | None = None
|
||||
if event.conversation_id:
|
||||
conversation = {
|
||||
'session_id': None, # Pipeline adapter field
|
||||
'session_id': None,
|
||||
'conversation_id': event.conversation_id,
|
||||
'thread_id': event.thread_id,
|
||||
'launcher_type': None, # Will be filled from actor/subject if needed
|
||||
@@ -213,7 +213,6 @@ class AgentRunContextBuilder:
|
||||
'sender_id': event.actor.actor_id if event.actor else None,
|
||||
'bot_id': event.bot_id,
|
||||
'workspace_id': event.workspace_id,
|
||||
'pipeline_uuid': binding.pipeline_uuid, # Pipeline adapter field
|
||||
}
|
||||
|
||||
# Build event context (Protocol v1 event-first)
|
||||
@@ -277,7 +276,7 @@ class AgentRunContextBuilder:
|
||||
'model_context_window_tokens': None,
|
||||
# TODO(model-info): populate model_context_window_tokens after
|
||||
# LiteLLM/model metadata lands. Runners fall back to their
|
||||
# binding config until Host can provide the real window.
|
||||
# ctx.config until Host can provide the real window.
|
||||
},
|
||||
}
|
||||
|
||||
@@ -295,7 +294,6 @@ class AgentRunContextBuilder:
|
||||
# Build adapter context (empty for event-first)
|
||||
adapter_context = {
|
||||
'query_id': None,
|
||||
'pipeline_uuid': binding.pipeline_uuid,
|
||||
'extra': {},
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ class RunnerNotFoundError(AgentRunnerError):
|
||||
|
||||
|
||||
class RunnerNotAuthorizedError(AgentRunnerError):
|
||||
"""Runner not authorized for this pipeline."""
|
||||
"""Runner not authorized for this binding."""
|
||||
def __init__(self, runner_id: str, bound_plugins: list[str] | None):
|
||||
self.runner_id = runner_id
|
||||
self.bound_plugins = bound_plugins
|
||||
@@ -34,4 +34,4 @@ class RunnerExecutionError(AgentRunnerError):
|
||||
def __init__(self, runner_id: str, message: str, retryable: bool = False):
|
||||
self.runner_id = runner_id
|
||||
self.retryable = retryable
|
||||
super().__init__(f'Agent runner {runner_id} execution failed: {message}')
|
||||
super().__init__(f'Agent runner {runner_id} execution failed: {message}')
|
||||
|
||||
@@ -73,11 +73,11 @@ class AgentEventEnvelope(pydantic.BaseModel):
|
||||
class BindingScope(pydantic.BaseModel):
|
||||
"""Scope for agent binding."""
|
||||
|
||||
scope_type: typing.Literal["bot", "pipeline", "workspace", "global"] = "pipeline"
|
||||
scope_type: typing.Literal["agent", "bot", "workspace", "global"] = "agent"
|
||||
"""Scope type."""
|
||||
|
||||
scope_id: str | None = None
|
||||
"""Scope identifier (bot_uuid, pipeline_uuid, etc.)."""
|
||||
"""Scope identifier (agent_id, bot_uuid, etc.)."""
|
||||
|
||||
|
||||
class ResourcePolicy(pydantic.BaseModel):
|
||||
@@ -153,7 +153,7 @@ class AgentBinding(pydantic.BaseModel):
|
||||
"""Runner ID to invoke."""
|
||||
|
||||
runner_config: dict[str, typing.Any] = pydantic.Field(default_factory=dict)
|
||||
"""Runner binding configuration."""
|
||||
"""Current Agent/runner configuration."""
|
||||
|
||||
resource_policy: ResourcePolicy = pydantic.Field(default_factory=ResourcePolicy)
|
||||
"""Resource policy."""
|
||||
@@ -167,6 +167,5 @@ class AgentBinding(pydantic.BaseModel):
|
||||
enabled: bool = True
|
||||
"""Whether binding is enabled."""
|
||||
|
||||
# Fields for Pipeline adapter
|
||||
pipeline_uuid: str | None = None
|
||||
"""Pipeline UUID (for Pipeline adapter)."""
|
||||
agent_id: str | None = None
|
||||
"""Host-side Agent/config identifier for this binding."""
|
||||
|
||||
@@ -93,9 +93,9 @@ class AgentRunOrchestrator:
|
||||
|
||||
Args:
|
||||
event: Event envelope from event gateway
|
||||
binding: Agent binding configuration
|
||||
binding: Agent binding
|
||||
bound_plugins: Optional list of bound plugin identities for authorization
|
||||
adapter_context: Optional adapter context from Pipeline adapter
|
||||
adapter_context: Optional context from an entry adapter
|
||||
|
||||
Yields:
|
||||
Message or MessageChunk for pipeline response
|
||||
@@ -125,12 +125,12 @@ class AgentRunOrchestrator:
|
||||
resources=resources,
|
||||
)
|
||||
|
||||
# Merge adapter context if provided (for Pipeline adapter)
|
||||
# Merge adapter context if provided
|
||||
if adapter_context:
|
||||
# Merge params into adapter.extra
|
||||
if 'params' in adapter_context:
|
||||
context['adapter']['extra']['params'] = adapter_context['params']
|
||||
# Merge prompt into adapter.extra for Pipeline adapter consumers.
|
||||
# Merge prompt into adapter.extra for transitional adapter consumers.
|
||||
if 'prompt' in adapter_context:
|
||||
context['adapter']['extra']['prompt'] = adapter_context['prompt']
|
||||
# Set query_id if provided
|
||||
@@ -420,7 +420,7 @@ class AgentRunOrchestrator:
|
||||
Args:
|
||||
result_dict: Raw result dict with type='state.updated'
|
||||
event: Event envelope
|
||||
binding: Agent binding configuration
|
||||
binding: Agent binding
|
||||
descriptor: Runner descriptor
|
||||
"""
|
||||
data = result_dict.get('data', {})
|
||||
|
||||
@@ -114,12 +114,11 @@ class PipelineAdapter:
|
||||
pipeline_config = query.pipeline_config or {}
|
||||
ai_config = pipeline_config.get('ai', {})
|
||||
runner_config = ai_config.get('runner_config', {}).get(runner_id, {})
|
||||
pipeline_uuid = getattr(query, 'pipeline_uuid', None)
|
||||
agent_id = getattr(query, 'pipeline_uuid', None)
|
||||
|
||||
# Build scope
|
||||
scope = BindingScope(
|
||||
scope_type="pipeline",
|
||||
scope_id=pipeline_uuid,
|
||||
scope_type="agent",
|
||||
scope_id=agent_id,
|
||||
)
|
||||
|
||||
# Build resource policy from pipeline config
|
||||
@@ -142,7 +141,7 @@ class PipelineAdapter:
|
||||
)
|
||||
|
||||
return AgentBinding(
|
||||
binding_id=f"pipeline_{pipeline_uuid or 'default'}_{runner_id}",
|
||||
binding_id=f"agent_{agent_id or 'default'}_{runner_id}",
|
||||
scope=scope,
|
||||
event_types=[runner_events.MESSAGE_RECEIVED],
|
||||
runner_id=runner_id,
|
||||
@@ -151,7 +150,7 @@ class PipelineAdapter:
|
||||
state_policy=state_policy,
|
||||
delivery_policy=delivery_policy,
|
||||
enabled=True,
|
||||
pipeline_uuid=pipeline_uuid,
|
||||
agent_id=agent_id,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -335,9 +334,6 @@ class PipelineAdapter:
|
||||
# Handle bot_uuid
|
||||
bot_uuid = getattr(query, 'bot_uuid', None)
|
||||
|
||||
# Handle pipeline_uuid
|
||||
pipeline_uuid = getattr(query, 'pipeline_uuid', None)
|
||||
|
||||
return ConversationContext(
|
||||
conversation_id=str(conversation_id) if conversation_id is not None else None,
|
||||
thread_id=None,
|
||||
@@ -347,7 +343,6 @@ class PipelineAdapter:
|
||||
bot_id=bot_uuid,
|
||||
workspace_id=None,
|
||||
session_id=session_id,
|
||||
pipeline_uuid=pipeline_uuid,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -398,7 +393,6 @@ class PipelineAdapter:
|
||||
"launcher_id": getattr(query, 'launcher_id', None),
|
||||
"sender_id": str(getattr(query, 'sender_id', '')) if getattr(query, 'sender_id', None) else None,
|
||||
"bot_uuid": getattr(query, 'bot_uuid', None),
|
||||
"pipeline_uuid": getattr(query, 'pipeline_uuid', None),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class AgentResourceBuilder:
|
||||
- Apply 3-layer permission filtering:
|
||||
1. Runner manifest declared permissions
|
||||
2. Pipeline extensions_preference (bound plugins/MCP servers)
|
||||
3. Runner binding config selected resources
|
||||
3. Agent/runner config selected resources
|
||||
- Build models list from authorized models
|
||||
- Build tools list from bound plugins/MCP servers
|
||||
- Build knowledge_bases list from config
|
||||
@@ -68,7 +68,7 @@ class AgentResourceBuilder:
|
||||
# Layer 2: Binding resource policy
|
||||
resource_policy = binding.resource_policy
|
||||
|
||||
# Layer 3: Runner binding config
|
||||
# Layer 3: Agent/runner config
|
||||
runner_config = binding.runner_config
|
||||
|
||||
# Build each resource category
|
||||
@@ -112,7 +112,7 @@ class AgentResourceBuilder:
|
||||
# Get additional model UUID grants from resource policy.
|
||||
allowed_uuids = resource_policy.allowed_model_uuids
|
||||
|
||||
# Add model resources from binding config schema
|
||||
# Add model resources from Agent/runner config schema
|
||||
await self._append_config_declared_model_resources(
|
||||
models=models,
|
||||
seen_model_ids=seen_model_ids,
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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=[]),
|
||||
)
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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={},
|
||||
|
||||
@@ -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={},
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user