跳转到内容

SDK 对齐与兼容性

Anthropic Messages wire model 会与 vendored 官方 TypeScript SDK(位于 contrib/anthropic-sdk-typescript)比较:

Terminal window
just compare-anthropic-protocol

比较内容包括类型覆盖、字段覆盖和顺序、serde discriminator 处理、枚举字面量、untagged union、字段 carrier 和结构化 SDK marker。每个 public wire type 还必须在 doc comment 中声明显式溯源:

/// @sdk(shape = "SearchResultBlockParam")
pub struct SearchResultBlockParam { /* ... */ }
/// @sdk(alias = "ToolBash20250124")
pub struct ServerToolDef { /* ... */ }
/// @sdk(proxai_internal = "field_literal_wrapper")
pub enum ImageMediaType { /* ... */ }

shape 把本地类型绑定到具名 SDK export;alias 记录刻意的多对一建模;proxai_internal 用于分类承载 SDK inline shape、union、discriminator 或字段字面量的本地类型。即使 Rust 名称恰好匹配,缺少显式 provenance marker 也会被 compare 拒绝。

官方 schema 区分四种字段契约:

官方 shapeRust carrier
`field: T`T
`field?: T`Option<T>
`field: T | null`RequiredNullable<T>
`field?: T | null`OptionalNullable<T>

RequiredNullable<T> 是自包含的。它的 JSON 反序列化会拒绝字段缺失,把显式 null 接受为 Nullable::null(),并把非 null 值交给 T,同时保留 unknown enum variant 等有用的内部错误。字段不需要额外 serde 属性:

pub struct Usage {
pub output_tokens: u32,
pub server_tool_use: RequiredNullable<ServerToolUsage>,
}

只有使用 Rust 外层 Option<T> 的 optional 字段仍需要字段级 presence 处理:否则 serde 会把字段缺失和显式 null 折叠在一起。optional non-nullable 字段使用:

#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_present"
)]
pub field: Option<T>;

OptionalNullable<T> 是自包含的三态 carrier:Missing、Null 或 Value(T)。它的反序列化处理字段已出现时的 null 或 value,而 #[serde(default)] 在字段缺失时提供 Missing:

#[serde(default, skip_serializing_if = "OptionalNullable::is_missing")]
pub field: OptionalNullable<T>;

OpenAI 与 Anthropic 共用的 protocol compare 规则会强制检查这些命名 carrier 及其不同的 serde 契约。裸 Option<Nullable<T>>、用 Option<T> 建模 required-nullable,以及两个 nullable carrier 上已经废弃的字段级反序列化器都会导致比较失败。

Provider 兼容性归一化只能把保守或已测量到的上游偏差修复为最接近的官方协议 shape。当前保守修复包括:JSON 对象中缺失的 SDK required-nullable 响应字段(missing -> null),以及裸 message_start 事件归一化为官方嵌套 message shape。当前已测量的 provider 修复包括:

  • MiniMax-compatible Anthropic streams 可能在 thinking content_block_start 上省略 signature,因此 ProxAI 只针对这个窄场景插入空 signature。
  • MiniMax-compatible Chat Completions streams 可能在终止 chunk 前省略 required-nullable 的 choices[].finish_reason,因此 ProxAI 会为缺失字段插入 null。
  • GLM 5.1 Anthropic-compatible streams 可能只在 server_tool_use 中发出一个 counter,因此 ProxAI 会把缺失的 web_fetch_requests 或 web_search_requests counter 填为 0。

不要添加其他 provider-specific 业务默认值,例如缺失 tool caller,除非有已测量的上游案例和聚焦 fixture 记录该行为。

这些修复应保持在 provider 兼容性处理中。它们不应重新定义官方 wire model。