使用 OpenAI 控制面与 Realtime API
管理模型、文件、Batch、Hosted Tool、Media Task、WebSocket 或 WebRTC 实时会话与有界重连。
安装正确的 API 面
常规 OpenAI Adapter、控制面、Hosted Tool、图像生成、语音合成与转写都位于
openai Feature 下。Realtime 是独立的可选能力,普通服务端应用不会因此引入它的传输实现。
cargo add runifold@0.9.0
cargo add runifold-providers@0.9.0 --features openai
# 只在使用 WebSocket 或浏览器 WebRTC Realtime 时添加:
cargo add runifold-providers@0.9.0 --features openai,openai-realtime两个 API 面
Runifold 的 OpenAI 集成除了模型数据面,还有两个专业接口:用于 Model、File、Batch 与
Realtime 临时凭证的 OpenAiControlPlane,以及面向 WebSocket 或浏览器 WebRTC 会话的
模型绑定 Realtime Connector。
它们不是 Agent 快捷方法,而是由应用或持久 Workflow 显式协调的生命周期操作。
控制面
OpenAiClient::control_plane() 共享经过验证的 Endpoint、连接池与凭证策略。类型化输入
限制文件名、上传大小、File Purpose、Batch Endpoint、Metadata 和未来状态值。
库可以列出模型、上传有界文件、创建 Batch、读取一次状态或请求取消,但不会隐式轮询。 持久化 Batch Identity,并在应用自己的 Deadline、预算与 Workflow 恢复策略下显式检查。
Realtime
OpenAiClient::realtime(model) 创建 Connector,提供 Session Update、文本、有界音频、
Response Create、取消、Transcript 与 Function Call Argument Delta 的类型化命令和事件。
状态机要求先创建 Session,只允许一个活跃 Response,把 Delta 关联到它,限制 Frame 与 Queue,并保存未知事件。WebRTC 与 WebSocket 共享生命周期、取消和 Deadline 语义。
凭证边界
原生服务端连接可以使用配置的长期凭证;浏览器构建拒绝长期 Provider Secret,必须通过 应用控制的 Gateway。
浏览器 Realtime 使用短期 Client Secret,每次重连都获取全新凭证。不要把 API Key 放进 WASM、JavaScript、WebSocket Query、日志或持久 Session 状态。
恢复语义
系统不会自动 Replay。Session 创建前或空闲时断开,可以安全替换;活跃 Response 期间
断开属于 AmbiguousResponseInFlight,因为输出可能已经提交。
Reconnect Controller 只在有界策略内自动执行安全替换,不存储 Secret、SDP、Transcript、 Command 或模型输出。应用负责协调不确定 Response,并决定让用户重试、继续还是启动新 Session。
使用控制平面
控制平面是显式 API:持久化返回的 ID,再由自己的 Workflow 安排后续读取; 它不会暗中轮询或重试。
use runifold::ModelCallContext;
use runifold_providers::openai::{OpenAiBatchEndpoint, OpenAiBatchRequest};
let control = client.control_plane();
let models = control.list_models(ModelCallContext::new()).await?;
let request = OpenAiBatchRequest::new("file_input", OpenAiBatchEndpoint::Responses)?
.with_metadata("tenant", "acme")?;
let created = control.create_batch(request, ModelCallContext::new()).await?;
persist_batch_id(&created.id).await?;
let current = control
.get_batch(load_batch_id().await?, ModelCallContext::new())
.await?;外围 Workflow 要设置总截止时间和轮询间隔。未来新增的未知状态应记录为数据, 不能当作成功。超时后保留 Batch ID,让运维人员或后续 Workflow 可以对账, 不要重新提交同一输入文件。
使用 OpenAI Hosted Tool
Hosted Tool 在 Provider 内部执行,不同于在你的进程内执行的 Runifold Tool。
用类型化构造器创建它,转为 Provider Tool Spec,再附加到 ModelRequest:
use runifold::{Message, ModelRef, ModelRequest};
use runifold_providers::openai::OpenAiHostedTool;
let request = ModelRequest::new(
ModelRef::new("openai", "gpt-5"),
Message::user("找到最新的公开发布说明并总结。"),
)
.provider_tool(OpenAiHostedTool::web_search().into())
.provider_tool(
OpenAiHostedTool::file_search(["vs_product_docs"])?
.into(),
);可用构造器包括 web_search()、image_generation()、
code_interpreter_auto()、file_search(...) 与 remote_mcp(label, url)。
file_search 要求 1–100 个非空 Vector Store ID;remote_mcp 只接受 HTTP(S)
URL,并保留 OpenAI 默认的审批语义。with_option 只用于 Provider 扩展项,
不允许覆盖保留的 type 字段。
Hosted Tool 不是本地 Capability:Runifold 无法在 Provider 内执行你的本地工具沙箱。 应用边界必须限制 Model、数据源、Vector Store、远程 MCP Endpoint、花费与用户可见审批。
生成图像、语音与转写
Media Task 使用独立的 Provider-neutral Trait,不会把二进制结果假装成 Chat 文本。
OpenAI Client 实现了 ImageGenerationModel、SpeechModel 与 TranscriptionModel:
use runifold::{
ImageFormat, ImageGenerationModel, ImageGenerationRequest, ModelCallContext,
ModelRef, SpeechFormat, SpeechModel, SpeechRequest,
};
let image = client.generate_image(
ImageGenerationRequest {
model: ModelRef::new("openai", "gpt-image-1"),
prompt: "一张精确的持久工作流等距示意图".into(),
count: 1,
size: Some("1024x1024".into()),
quality: Some("high".into()),
format: ImageFormat::Png,
transparent: false,
},
ModelCallContext::new(),
).await?;
let speech = client.synthesize_speech(
SpeechRequest {
model: ModelRef::new("openai", "gpt-4o-mini-tts"),
input: "工作流已安全完成。".into(),
voice: "alloy".into(),
instructions: None,
format: SpeechFormat::Mp3,
speed: None,
},
ModelCallContext::new(),
).await?;转写通过 TranscriptionModel::transcribe 执行,TranscriptionRequest 应包含有界
文件名、Media Type、字节,以及可选 Language / Prompt。返回字节或远程 Media 必须显式存储;
默认不要记录二进制 Payload、签名 URL、Transcript 或用户音频。