openai-core 0.1.1

Rust SDK for OpenAI-compatible ecosystem
Documentation
# 0004 覆盖性复查

## 结论

结论:`openai-rs` 现在已经覆盖了 `openai-node` 的绝大多数资源入口和主链路能力,但仍然没有做到完全 1:1。

和上一次检查相比,旧文档里列出的几项核心缺口已经基本补齐:

- Assistants / Beta Threads 的 `stream` / `poll` helper 已落地
- Responses SSE continuation、`starting_after` 和最终快照聚合已落地
- Chat 的 `parse``finish_reason` 错误、tool arguments 解析、`run_tools().send_streaming()` 已落地
- `from_env()`、自定义 `reqwest::Client``OPENAI_LOG` / `logger` / `log_level` 已落地
- 动态路径参数安全编码已经补到主要资源面
- Realtime / Responses WebSocket、Realtime `client_secrets` / `calls`、Beta Realtime `sessions` / `transcription_sessions` 已有实现
- Chat / Responses / Assistants 的 typed runtime event stream 已落地
- `ChatCompletionRunner` / `ChatCompletionStreamingRunner` trace 对象已落地
- 独立 `OpenAIRealtimeWebSocket` / `OpenAIRealtimeWS` / `OpenAIResponsesWebSocket` 已落地
- 统一上传 helper `to_file()` / `ToFileInput` 已落地

因此,当前更准确的判断是:

- 如果标准是“OpenAI 兼容 REST / SSE / WebSocket 主链路是否可用”,`openai-rs` 已经很接近完整。
- 如果标准是“公开 API 形态、helper 语义、运行时体验、测试广度都和 `openai-node` 对齐”,`openai-rs` 仍然有差距。

本次复查是静态对照检查,没有重新执行 `cargo test`。

## 对照范围

本次主要对照了这些目录:

- `openai-node/src/resources/*`
- `openai-node/src/lib/*`
- `openai-node/src/realtime/*`
- `openai-node/src/internal/to-file.ts`
- `openai-node/tests/*`
- `openai-rs/src/client.rs`
- `openai-rs/src/resources/*`
- `openai-rs/src/stream/mod.rs`
- `openai-rs/src/websocket/mod.rs`
- `openai-rs/src/files/mod.rs`
- `openai-rs/tests/*`

判断口径分成三层:

1. 资源入口和底层 endpoint 是否存在
2. `openai-node` 的高层 helper / runtime 语义是否有等价实现
3. tests 是否覆盖了同类风险面

## 已覆盖部分

### 1. 资源入口层面已基本齐全

按 `openai-node/src/resources` 对照,`openai-rs` 当前已经覆盖这些顶层命名空间:

- `audio`
- `batches`
- `beta`
- `chat`
- `completions`
- `containers`
- `conversations`
- `embeddings`
- `evals`
- `files`
- `fine_tuning`
- `graders`
- `images`
- `models`
- `moderations`
- `realtime`
- `responses`
- `skills`
- `uploads`
- `vector_stores`
- `videos`
- `webhooks`

虽然 `openai-rs/src/resources` 仍然采用“少文件聚合实现”的结构,而不是像 `openai-node` 那样按资源细拆,但从资源入口角度看,缺口已经不大。

### 2. 旧版 0004 中的关键缺口大多已关闭

这次复查确认,以下能力已经不应再视为缺失:

- `beta.threads.create_and_run_stream()``create_and_stream()``submit_tool_outputs_stream()``stream()` 已接入 `AssistantStream`
- `create_and_run_poll()``poll()``create_and_poll()``submit_tool_outputs_and_poll()` 已实现
- `responses.stream_response(response_id)``starting_after()``final_response()` 已实现
- `ResponseStream` 已能聚合 `response.created``response.output_item.added``response.content_part.added``response.output_text.*``response.function_call_arguments.delta`
- `ChatCompletion` 层已有 `LengthFinishReasonError``ContentFilterFinishReasonError`
- `ChatCompletionMessage` 已支持解析 assistant content 和 tool arguments
- `run_tools().send_streaming()` 已实现
- `ClientBuilder::from_env()``http_client()``log_level()``logger()` 已实现
- `UploadSource::from_response()` 已实现
- 动态 path segment 已统一走 `encode_path_segment()`

### 3. Realtime / Responses WebSocket 已从“占位”变成“可用”

当前 `openai-rs` 已具备:

- Realtime WebSocket 连接、鉴权、发送、关闭、事件流
- Responses WebSocket 连接、鉴权、发送、关闭、事件流
- Azure Realtime 的 deployment / `api-version` 语义
- Realtime `client_secrets` / `calls`
- Beta Realtime `sessions` / `transcription_sessions`

这部分已经不是“缺功能”,而是“高层封装形态还没和 Node 完全一样”。

## 仍然存在的功能差距

下面这些是当前仍然存在、并且确实影响“是否完全覆盖 `openai-node`”判断的差距。

### P0-1. 运行时形态仍然不是 Node 的 emitter 模型

这是现在最大的剩余差距。

`openai-node` 在 `src/lib/*` 中有一整套事件驱动 runtime:

- `ChatCompletionStream`
- `ChatCompletionRunner`
- `ChatCompletionStreamingRunner`
- `AssistantStream`
- `ResponseStream`
- `EventEmitter`
- `EventStream`

这些 runtime 不只是“能迭代流”,而是提供:

- `.on(...)` 事件监听
- `connect / error / abort / end` 生命周期
- `finalMessage()``finalContent()``finalFunctionToolCall()``totalUsage()` 等高层终态 helper
- `current...Snapshot` / `currentEvent` 语义
- `toReadableStream()` / `fromReadableStream()` 桥接

`openai-rs` 现在已经补上了对应的高层运行时类型:

- `ChatCompletionEventStream` / `ChatCompletionRuntimeEvent`
- `AssistantEventStream` / `AssistantRuntimeEvent`
- `ResponseEventStream` / `ResponseRuntimeEvent`
- `ChatCompletionRunner` / `ChatCompletionStreamingRunner`
- 独立 `OpenAIRealtimeWebSocket` / `OpenAIRealtimeWS`

但它仍然没有做成 `openai-node` 那种 `.on(...)` emitter 形态。剩余差异主要是:

- 事件发射器语义
- 统一的 `connect / error / abort / end` 回调模型
- `ReadableStream` 桥接语义

### P0-2. `ReadableStream` / `fromReadableStream()` / `toReadableStream()` 仍未对齐

`openai-node` 的 Chat / Assistant / Response runtime 都支持:

- `fromReadableStream()`
- `toReadableStream()`

这对 Node/browser 边界和前后端转发很重要。

`openai-rs` 当前统一走 `futures::Stream`,因此能力是有的,但没有等价的桥接 helper。这仍然是和 `openai-node` 的一项真实差距。

### P0-3. 事件类型已经补齐,但还没有 Node 那样的“订阅式”使用方式

当前 `openai-rs` 已经具备:

- `messageCreated`
- `messageDelta`
- `messageDone`
- `runStepCreated`
- `runStepDelta`
- `runStepDone`
- `toolCallCreated`
- `toolCallDelta`
- `toolCallDone`
- `textCreated`
- `textDelta`
- `textDone`
- `imageFileDone`
- `response.output_text.delta`
- `response.function_call_arguments.delta`

这些能力现在都能通过 typed runtime event 读到。

剩余差距不再是“没有这些事件”,而是:

- 事件是通过 `Stream<Item = Event>` 消费
- 不是通过 `.on('event', ...)` 订阅

### P0-4. runner 已补齐,但仍不是持续在线的事件驱动对象

`openai-node` 的 `runTools()` runner 可以持续发射事件;`openai-rs` 现在虽然已经有:

- `ChatCompletionRunner`
- `ChatCompletionStreamingRunner`
- 中间态 message / completion / tool result 收集
- `final_chat_completion()` / `final_message()` / `final_function_tool_call_result()` / `total_usage()`

但它仍然是“执行完后返回 trace”,而不是 Node 那种可边跑边订阅的 runner 对象。

### P0-5. 独立 Realtime 客户端形态仍有差异

`openai-node` 公开了这些独立客户端:

- `OpenAIRealtimeWS`
- `OpenAIRealtimeWebSocket`
- Azure 对应变体

并且是典型的 emitter 形态。

`openai-rs` 现在已经补了:

- `OpenAIRealtimeWebSocket`
- `OpenAIRealtimeWS`
- `OpenAIResponsesWebSocket`
- 原有的 `client.realtime().ws().connect()`
- 原有的 `client.responses().ws().connect()`

也就是说:

- 独立类型已经有了
- endpoint、鉴权、收发消息都已经有了
- 但仍没有 Node 那种 emitter API、browser 保护语义和 Node/browser 双实现结构

其中浏览器相关保护和 `dangerouslyAllowBrowser` 对 Rust SDK 不完全适用,但如果以“是否完整覆盖 `openai-node` 公开 API”来衡量,这仍然是差异点。

### P1-1. 上传 helper 仍然没有 Node 的 `toFile()` 等价层

`openai-node` 的 `toFile()` 支持从多种输入统一构造上传对象:

- `File`
- `Response`
- `Blob`
- `Buffer`
- `Uint8Array`
- `ReadStream`
- `AsyncIterable`

`openai-rs` 现在已经补了:

- `to_file()`
- `ToFileInput`
- `UploadSource::from_path()`
- `UploadSource::from_bytes()`
- `UploadSource::from_reader()`
- `UploadSource::from_response()`

这已经不再是“没有统一 helper”的问题了。当前剩余差距主要是输入面仍比 Node 小:

- 还没有 `Blob` / `AsyncIterable` 等 Node/browser 风格输入
- 更偏 Rust 服务端场景

### P1-2. 路径安全问题已修复,但还没有 Node 那套“显式校验报错语义”

旧版 0004 把 path safety 标成正确性缺口,这个结论现在已经过时了。

当前 `openai-rs` 已经对动态 path segment 做了 percent-encoding,因此“动态 ID 改写 URL 结构”的问题已经解决。

但和 `openai-node/tests/path.test.ts` 比起来,`openai-rs` 还缺:

- 对危险 segment 的显式拒绝语义
- 更详细的诊断错误信息
- 类似 Node path template tag 的系统化校验

所以这一项现在应视为“语义和测试还没完全对齐”,而不是“路径仍然不安全”。

## 与 Node 不同,但不应误判为缺功能的地方

下面这些差异需要单独说明,否则容易把“API 形态不同”误判成“没实现”。

### 1. Rust 用 `ApiResponse<T>` / `send_with_meta()`,不是 `_request_id` 注入

`openai-node` 倾向于:

- 在对象上注入 `_request_id`
- 提供 `withResponse()`

`openai-rs` 当前是:

- `ApiResponse<T>`
- `ResponseMeta`
- `send_with_meta()`

这不是功能缺失,而是公开 API 设计不同。

### 2. Rust 用 `futures::Stream`,不是 `ReadableStream`

`openai-node` 里很多 helper 都围绕 `ReadableStream` 和 emitter 组织。

`openai-rs` 选择的是:

- `futures::Stream`
- 明确的终态 helper

这会导致 API 形态不一样,但不能简单算成“流式能力没实现”。

### 3. 浏览器防护语义并不完全适用于 Rust SDK

`dangerouslyAllowBrowser`、ephemeral browser token consumption 这一类能力在 Node/browser SDK 里很重要,但对 Rust 服务端 SDK 并不是同一个问题域。

这类差异应当单独标注为“目标运行时不同”,而不是一律当成缺失。

## tests 覆盖差距

### 1. 测试广度仍明显落后于 `openai-node`

按 `tests/` 目录文件数静态统计:

- `openai-node/tests`: 74 个 `.ts` 文件
- `openai-rs/tests`: 9 个 `.rs` 文件

`openai-rs` 还在 `src/providers/mod.rs`、`src/stream/mod.rs`、`src/webhooks/mod.rs` 有少量模块内单测,但整体测试广度仍明显小于 `openai-node`。

### 2. 资源级 contract tests 还不够细

`openai-node` 基本是按资源拆开的:

- `containers`
- `conversations`
- `evals`
- `realtime/client-secrets`
- `realtime/calls`
- `skills`
- `videos`
- `beta/chatkit`
- `beta/threads/runs/steps`
- 等等

`openai-rs` 当前 contract tests 仍然集中在少数文件里,覆盖重点主要是:

- chat
- responses
- vector stores
- 路径编码
- logger/env
- websocket happy path

这意味着:

- 主链路有验证
- 长尾资源的回归保障还不够细

### 3. 仍然缺少的测试主题

当前最值得补的测试主题有:

- `ChatCompletionStream` 的高层终态 helper 行为测试
- `AssistantStream` 的事件拆分 / 快照演化测试
- `ResponseStream` 的 typed event / reasoning / function-call 聚合测试
- `run_tools()` 的多轮工具调用与错误路径测试
- Realtime / Responses WebSocket 的错误、关闭、异常消息测试
- `UploadSource` 的文件名推导、错误输入、reader/response 分支测试
- logger 的敏感头脱敏、输入不被 mutate 的测试
- `send_with_meta()` / `ResponseMeta.request_id` 的显式测试
- path safety 的更多边界条件测试
- 长尾资源的逐资源 contract tests

### 4. 当前已经有但仍显偏薄的测试面

当前 `openai-rs` 已经覆盖了一些关键主题:

- Chat request body snapshot
- API error mapping snapshot
- Responses stream aggregation snapshot
- Realtime / Responses WebSocket 事件解码 snapshot
- env / custom http client / logger
- response continuation
- beta run poll helper
- 路径编码
- websocket happy path

这些测试足以证明“核心链路已可用”,但还不足以证明“已经达到 `openai-node` 同等级别的回归密度”。

## 推荐后续补齐顺序

如果目标是继续逼近 `openai-node` 的完整公开能力,我建议按这个顺序补:

1. `ChatCompletionStream``AssistantStream``ResponseStream` 增加 typed event / runner 层
2. 补一个更接近 Node 语义的 `ChatCompletionRunner` / `ChatCompletionStreamingRunner`
3. 补独立 Realtime 客户端形态,统一 `RealtimeSocket` / `ResponsesSocket` 的高层 API
4. 增强上传 helper,补 `toFile()` 风格的人体工学接口
5. 补 logger、meta、path safety、upload 的专项测试
6. 按资源拆分 contract tests,把长尾资源逐步拉到与 Node 相近的覆盖密度

## 最终判断

最终判断如下:

- `openai-rs` 已经不再是“只有底层 HTTP 路径”的阶段
- 旧版 0004 中列出的多项 P0 缺口已经关闭
- 当前最大的差距,已经从“功能有没有”转移到“高层运行时语义和测试广度有没有完全对齐”

所以今天的结论应更新为:

- `openai-rs` 已经具备高覆盖度的 OpenAI Rust SDK 能力
- 但还没有完全复刻 `openai-node` 的高层 runtime 形态和测试矩阵
- 要说“完全覆盖 `openai-node`”,现在仍然差最后一段以事件 runtime 和测试广度为主的工作