feat(providers): payload 拦截器注册化——finalize 管线反转到 seam 注册表 - #599
Open
AlphaCatMeow wants to merge 5 commits into
Open
feat(providers): payload 拦截器注册化——finalize 管线反转到 seam 注册表#599AlphaCatMeow wants to merge 5 commits into
AlphaCatMeow wants to merge 5 commits into
Conversation
为 LLM seam 改造提供行为等价判定基准: - wire-payload-golden:anthropic-messages / openai-completions / openai-responses / google-generative-ai / deepseek-responses 五协议 固定输入下的完整请求体逐字段锁定(thinking 档位、工具、缓存断点、 text-only 双形态),走真实 pi-ai stream() 与全部 payload 中间件, onPayload 链尾截获后中断,零网络。 - transport-golden:prepareProviderRequest 完整输出快照(反代 URL、 全量头集、base64 覆盖包解码断言、鉴权头排除、full URL 模式、 useSystemProxy 开关),并锁定 failover 逐候选传输配置独立性 (主选走代理+备选直连互不泄漏,双向拓扑)。 零生产代码改动。
将 streamByApi.ts 的五协议 switch 原样搬移为 service/ 下的双适配器 (piAiAdapter 承接 4 条 pi-ai 协议,deepSeekAdapter 承接 deepseek 原生), 经 api→adapter 注册表分发;新增统一流式入口 llm.stream(),携带仅 dev 构建生效的请求信封冻结与一次性分发不变量。streamByApi.ts 收缩为保留 原签名的兼容壳(分发针孔),agentRunner 与 textOnlyRuntime 共 5 处调用 换用统一入口。行为严格等价:PR-0 两个 golden 套件(13 用例)零修改 通过;新增 seam 单测 8 用例覆盖注册表分发、错误文案逐字等价、dev 冻结开关与双入口 wire payload 等价。零 Rust 改动、零镜像文件改动。
将流内重试策略的归属权从全局常量反转到供应商配置(PR-2,stack 第 3 层): - 共享真源 agent-ui settings 新增 CustomProvider.retryPolicy?(off | custom+maxRetries),maxRetries 为首次失败后的重试次数(不含首次请求, 钳位 1..10)。normalizeProviderRetryPolicy 保证非法/缺省一律落 default 且不落字段,旧配置零迁移;default 态在持久层不存在。 - agent-gui 运行时经 ProviderRuntimeConfig 唯一构造点透传策略,新增 resolveStreamRetryConfig 把策略解析为 withStreamRetry 选项(off → disabled,custom → maxAttempts=maxRetries+1,缺省 → 空对象落全局默认)。 agentRunner 与 textOnlyRuntime 两个 streamRetry 注入点展开合并,回调 语义与 buffer-until-commit 不变;failover 逐候选使用各自 runtime 的 策略。streamRetry.ts 与协议适配器零改动, DEFAULT_STREAM_RETRY_MAX_ATTEMPTS 降级为未配置时的默认值。 - 共享 settings UI(ProviderModal/ProviderModalView)请求面板新增 流式重试三态控件(默认/关闭/自定义次数),GUI 与 WebUI 镜像同源; UI 展示镜像常量 PROVIDER_RETRY_DEFAULT_MAX_RETRIES 与运行时真源的 一致性由单测锁定。 - 新增 provider-retry-policy.test.mjs(13 用例):归一化矩阵、构造点 透传、消费方合并语义三种 mode、failover 候选策略独立、口径换算。 PR-0 golden 两套件与既有 stream-retry 套件零修改通过。
把 payload 中间件的组织权反转到 LlmService seam(PR-3,stack 第 4 层)。 未注册任何自定义拦截器时行为与 PR-2 严格等价: - 新增 service/interceptors.ts 注册表:具名 PayloadInterceptor (name + intercept),usePayloadInterceptor / llm.use() 返回幂等 dispose,同名重复注册抛错;组合链带失效缓存,注册/移除时重建, finalize 热路径(agentRunner 每轮、textOnly 每次调用)零重组开销。 - payloadPipeline.ts 的 10 个中间件原样包装为具名默认拦截器,模块 初始化时一次性安装,顺序与注册化前数组逐项一致(顺序即协议正确性 的一部分,由顺序快照测试锁定)。payload-debug-logging 钉住链尾: 自定义拦截器插入默认之后、链尾之前,自定义改动仍被调试日志观测。 - finalizeProviderStreamOptions 改为从注册表组合执行;agentRunner 与 textOnlyRuntime 两处调用零改动,composePayloadMiddlewares 等 既有导出保留,10 个中间件实现文件零改动。 - 新增 llm-interceptors.test.mjs(8 用例):默认顺序快照(10 个 名字)、llm.use 同源、params 可见与 options 变换、插入位置、链尾 观测不变量、dispose 幂等、同名抛错、与旧数组组合逐字段等价(多 形态参数矩阵)。golden 两套件、seam、retry-policy、stream-retry 及中间件相关回归零修改通过。
StackCairn
marked this pull request as draft
August 22, 2026 13:05
Contributor
|
PR governance checks passed. Awaiting human review. |
AlphaCatMeow
marked this pull request as ready for review
August 22, 2026 13:13
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #598
Depends-On: #597
Stack-Root: #590
改动说明
把 payload 中间件的组织权反转到 LlmService seam(stack 第 4 层,PR-3/5)。未注册任何自定义拦截器时行为与 PR-2 严格等价。
注册表(新增
service/interceptors.ts)PayloadInterceptor(name+intercept);name是注册表内唯一身份,顺序快照按名字断言、同名重复注册抛错(含与默认拦截器同名)、dispose 幂等移除usePayloadInterceptor/llm.use()返回幂等 disposeinstallDefaultPayloadInterceptors仅供 payloadPipeline 模块初始化调用一次,重复安装抛错(防测试加载器/HMR 双重初始化悄悄改变链序)默认链(
payloadPipeline.ts)payload-debug-logging钉住链尾:自定义拦截器插入默认之后、链尾之前,自定义 payload 变换仍被调试日志观测到(有专项测试)finalizeProviderStreamOptions改为从注册表组合执行;agentRunner与textOnlyRuntime两处调用零改动,composePayloadMiddlewares/attachPayloadDebugLogging等既有导出保留,10 个中间件实现文件零改动src-tauri/**,不触共享 UI 层验证结果
test/providers/llm-interceptors.test.mjs(8 用例):默认顺序快照(10 个名字逐一断言)、llm.use与注册函数同源、自定义拦截器 params 可见性与 options 变换、插入位置(默认之后/链尾之前)、链尾观测不变量(自定义 onPayload 变换被 debug logging 看到)、dispose 幂等、同名/空名/缺函数抛错、与旧数组组合逐字段等价(按注册化前数组原样重建旧链,4 组参数矩阵逐字段 deepEqual)— 8/8 通过pnpm --dir crates/agent-gui build✅ /test:frontend(270 个测试文件)✅git diff --check✅node scripts/check-ui-boundaries.mjs:仅既有 3 个文件不合规(与 upstream/main 逐字节一致,非本 PR 引入)未运行项
人工验收结果
Windows Tauri 调试客户端(
start-tauri-dev.bat,分支 HEAD)人工回归验收通过:验收期间报告的"聊天图片附件点击预览黑框"经 A/B 验证(撤下本 PR 改动仍复现)确认为上游 #448 base-ui Dialog 迁移引入的既有回归(视口测量时序),与本 PR 无关,将单独立项修复。
自动化专用用例(UI 无法直接触发,附命令与结果):
node --test test/providers/llm-interceptors.test.mjs→ 8/8 通过(注册化正确性:顺序快照、dispose 语义、同名冲突、行为等价矩阵)。Screenshots / preview
本 PR 为纯运行时重构,无任何 UI 变化(不触
crates/agent-ui与 gateway web),无截图。UI 回归已由上方人工验收覆盖。