在当前客户端源码中,Safety Buffering 不是一个扣住 Text Delta 的本地缓冲器。真正的安全缓冲由服务端决定;Codex 解析 wire 上的 buffering metadata,补齐更快替代模型并向 UI 发通知,内容事件仍按原顺序通过。
具体问题与启用条件
本节刻意区分服务端安全策略与客户端可见通知,避免把事件名称误写成 Core 内的内容过滤。具体云端判定算法不在本地源码范围内。
| 条件来源 | 决定字段或状态 | 对本机制的影响 |
|---|---|---|
| 响应 Header | x-codex-safety-buffering-* | 提供默认 faster model treatment |
| 线事件 | safety_buffering object | 产生 SafetyBuffering ResponseEvent |
| 事件字段 | retry_model 存在/缺失 | 优先 wire 值,否则 Header fallback |
协议、类型与状态所有权
| 状态或协议 | 所有者 | 生命周期 | 关键不变量 |
|---|---|---|---|
| 缓冲/审核决策 | Responses 服务端 | 服务端生成期间 | 本地仓库不实现判定 |
| SafetyBufferingTreatment | SSE/WS parser | 连接/流 | 只保存 faster_model fallback |
| SafetyBufferingEvent | Core Session event stream | 客户端通知 | 不进入 History |
| Text Delta | 普通 ResponseEvent 路径 | Item 暂态 | 不会被本地 Safety event 阻塞 |
机制调用链如下:
响应 Header/WS metadata
→ treatment_from_headers
→ 每个 wire event 读取 safety_buffering
→ 解析 use_cases/reasons/retry_model
→ retry_model 缺失时补 header faster_model
→ show_buffering_ui=true
→ ResponseEvent::SafetyBuffering
→ EventMsg::SafetyBuffering
→ UI 展示状态
机制怎样工作
Header 中是否出现 enabled/faster-model 决定能否构造 treatment,但 enabled=false 并不在客户端充当事件 gate;测试明确说明 faster-model fallback 仍可被读取。wire event 如果自带 retry_model,它优先于 Header;只有字段根本未出现时才采用 Header 的 faster model。解析成功后客户端强制 show_buffering_ui=true。
SSE 和 WebSocket 都在映射普通内容事件之前提取 Safety Buffering,并把通知放入同一队列。测试序列表明通知与 Text Delta 可以交替出现而不丢内容。这意味着本地职责是“把服务端正在缓冲/审核的事实告诉前端”,不是“自己缓存敏感 token 再放行”。如果服务端已经把某段 Delta 发到线协议,本地这层不会因 SafetyBufferingEvent 将其撤回。
Python 风格伪代码
这段伪代码保留生产实现中会改变结果的状态、分支和异步边界;认证 SDK、遥测字段和 Rust 所有权样板被折叠为明确的领域对象。
def safety_notification(wire: dict, treatment: SafetyTreatment) -> SafetyEvent | None:
payload = wire.get("safety_buffering")
if not isinstance(payload, dict):
return None
retry_model_was_present = "retry_model" in payload
event = parse_safety_payload(payload)
event.show_buffering_ui = True
if not retry_model_was_present:
event.faster_model = treatment.faster_model
return event
async def process_wire_event(wire: dict):
if metadata_headers := wire.get("headers"):
update_treatment_from_headers(metadata_headers)
if notice := safety_notification(wire, current_treatment):
await response_queue.put(notice)
if content_event := map_content_event(wire):
await response_queue.put(content_event) # 不由客户端扣留
失败、取消与恢复
| 故障或边界 | 已发生的状态 | 对上层的结果 | 能否直接重试 | 恢复动作 |
|---|---|---|---|---|
| safety_buffering 不是对象/无法解析 | 内容事件仍可处理 | 不发通知 | 不适用 | 继续流 |
| wire 无 retry_model | 已有 Header treatment | 使用 faster_model fallback | 不适用 | 向 UI 提供替代提示 |
| Header 也无模型 | 通知仍有 use_cases/reasons | faster_model=None | 不适用 | UI 不展示切换项 |
| 客户端误把通知当过滤器 | 可能永不 flush 或重复缓冲 | 架构错误 | 否 | 把安全执行留给服务端 |
设计取舍与验证
通知与执行分离使开源客户端不必复制服务端策略,也能给用户可见反馈;代价是本地无法独立证明服务端怎样缓存、何时放行。把通知放在普通事件流中保持顺序,却要求 UI 能处理同一响应中的重复状态更新。
| 可验证契约 | 证据方式 | 预期结果 |
|---|---|---|
| Header faster model 作为 fallback | SSE 集成测试 | Event 包含 Header 模型 |
| wire retry_model 优先 | parser 单元测试 | 不被 Header 覆盖 |
| 通知不丢相邻 Text Delta | 交替事件序列测试 | 所有通知和两个 Delta 顺序保留 |
Mini Codex 对照
Mini Codex 若没有服务端安全缓冲合同,不应只复制这个通知类型就宣称“防泄漏”。可以实现 SafetyNotice 透传;若要本地缓冲,必须另行定义威胁模型、释放终态、最大缓存和断流丢弃策略。
本节边界
本节能证明的只有客户端通知链,不能从本地源码推导云端审核算法。5.21 回到可完全验证的控制流:限流快照和重试等待怎样处理。
评论
登录后即可评论