跳转至

开发 Driver

当四个内置 Driver 无法表达目标上游的请求、响应或流语义时,可以开发 Workspace WASM Driver。唯一 ABI v1 提供 protocol-native Text 与 synchronous Image 合同。

开发边界

Driver 可以:

  • 声明一个或多个精确入站 Protocol Contract;
  • 在 Bind 时校验非敏感配置、endpoint ref 和 credential slot descriptor;
  • 为一个已选中的上游模型创建 Attempt 和 RequestPlan
  • 转换 buffered 响应、SSE/NDJSON 事件、Usage 与特殊 Outcome;
  • 对图片 multipart 使用不透明 BlobRef 与有界 BodyPlan

Driver 不能选择 Workspace、模型组、Endpoint、tier、weight 或重试目标,不能读取凭据值,也不能执行网络、DNS、TLS、文件、时钟或后台任务。

如果差异只有 Base URL、API Key 或上游模型 ID,只需创建新接入点

SDK 与工具链

以公开的 Legate Driver SDK 中对应版本为准:

  • abi/v1/driver.proto:唯一 wire contract;
  • go/:Go / TinyGo SDK 与 typed protocol packages;
  • rust/:Rust SDK 与 typed protocol modules;
  • go/examplesrust/examples:Text 与 Image 可构建示例;
  • conformance/v1:wire、Usage、Outcome 与 Image Generation/Edit 协议行为向量;
  • scripts/verify-wasm.sh:检查零 imports、唯一 memory、精确 exports 和签名。

Go guest 固定使用 TinyGo 0.39.0wasm-unknown;Rust 使用仓库 pin 的 Rust 1.97.0wasm32-unknown-unknown

1. 选择 Contract

文本:

openai.chat_completions/2026-07-18
openai.responses/2026-07-18
anthropic.messages/2026-07-18

图片:

openai.images.generations/2026-07-19
openai.images.edits/2026-07-19

Text Driver 声明一个 Contract 就必须同时完成 buffered 与 SSE 生命周期。Image Driver 只实现同步 buffered 生命周期。一个 WASM module 只能是 text 或 image,不能同时导出两套 hooks。

2. 编写 Manifest

文本示例:

{
  "id": "acme-chat",
  "displayName": "Acme Chat Driver",
  "version": "1",
  "kind": "text",
  "text": {
    "protocolContracts": ["openai.chat_completions/2026-07-18"]
  },
  "managementCapabilities": [],
  "configSchema": {"type":"object","additionalProperties":false},
  "credentialSchema": {
    "slots": [{"name":"api_key","required":true}]
  },
  "requestedCapabilities": [],
  "wireAbiVersion": "1"
}

图片 Driver 把 text 替换为:

"image": {
  "protocolContracts": [
    "openai.images.generations/2026-07-19",
    "openai.images.edits/2026-07-19"
  ]
}

WASM ABI v1 不授予 management 或 requested capability,两个数组必须存在且为空。Manifest 使用严格 JSON,未知字段、重复 key、尾随文档、未知 Contract 或非 1 ABI 都会被拒绝。

3. Bind 与 typed handler

Bind 输入只包含:

  • config_json
  • 允许使用的 endpoint refs;
  • credential slot 名称和 configured 布尔值。

校验必需配置后返回不可变 bound state。不要在请求时重新 Bind。

Go Text Driver 使用 driver.NewDispatcher 注册 Manifest 中每个 Contract 对应的 typed handler;Image Driver 使用 driver.NewImageDispatcher。Dispatcher 会拒绝缺失、多余、重复或未知注册。协议包 decoder 会拒绝重复 JSON key,并通过 ExtraFields 保留未知字段。Image Edit typed decoder 同时接受 imageimage[] 文件 part,并在构造上游 multipart 时保留每个 part 的原始名称与顺序。

4. Attempt 生命周期

Text buffered:

Bind → TextAttemptOpen → RequestPlan → TransformBufferedResponse → TextAttemptClose

Text stream:

Bind → TextAttemptOpen → RequestPlan
     → SSEOpen → SSETransformEvent (0..N) → SSEFinish
     → TextAttemptClose

Image:

Bind → ImageAttemptOpen → RequestPlan → ImageTransformBufferedResponse → ImageAttemptClose

Open hook 可以直接返回协议原生 ClientResponse,此时没有上游 I/O。每个成功打开的 Attempt 无论成功、错误、取消或故障转移都恰好 Close 一次。

Text Open 输入包含 bound state、typed request、metadata、调用 mode、selected upstream model 和 response ID。Image Open 输入不含 mode,包含 bound state、typed request、metadata、selected upstream model、response ID 和 opaque BlobRef 列表。构造上游 body 时必须使用 selected upstream model,不能把 typed request 中的公开 model 原样当作上游模型。

5. 安全 RequestPlan

RequestPlan 可以声明 method、相对路径、有序 query/header、body 和引用 credential slot 的 Auth Plan。禁止返回:

  • 绝对 URL、任意 Host 或用户信息;
  • 明文 credential;
  • 调用方 Authorization、Cookie、hop-by-hop 或 X-Legate-* header;
  • retry、tier、breaker 或候选选择指令。

JSON 必须通过 typed protocol package 或结构化 parser 处理,不能用字符串替换 model。图片 bytes 由 Host 持有,Driver 只能使用 BlobRefBodyPlan 的 inline/blob/Base64/multipart builders。转发 Image Edit 时应保留 image/image[] 的原始字段名以及 multipart part 顺序,除非 Driver 明确拒绝或转换这些字段。

供应商把 Base64 图片嵌入 JSON 时,Image RequestPlan 可以声明 typed 对象字段/数组元素路径和 base64_standard 编码。Core 流式解析响应,把命中字段解码到 attempt-scoped Blob,并向 Transform 交付移除大字符串后的有界 JSON 骨架、实际路径和 BlobRef。Driver 不会获得原始 Base64、图片 bytes、文件路径或 Blob reader;它使用 Composite BodyPlan 将 Blob 重新编码进公开响应。该提取计划不能用于 Text Driver。

Go SDK 使用 JSONField("candidates")JSONArrayElements() 等 typed selector 构造路径。Transform 必须用每个结果的声明索引和完整实际路径关联 Blob,不能假设数组索引或只按结果顺序取第一项。Go Driver 处理互斥的 InlineBlobExtractedJSON 三种来源;Rust Driver 处理 Content::InlineContent::BlobContent::ExtractedJson。完整示例与边界见 Driver SDK 的 docs/authoring.zh-CN.md

6. 输出、Usage 与 Outcome

转换后的响应必须仍属于原入站 Contract。Core 负责最终 framing 和提交:

  • buffered / immediate ClientResponse 必须报告 finalpartialunavailable Usage;
  • 成功 SSE 的 event 与 finish 合并后至少报告一次 Usage 和 Outcome;
  • 没有可信 Token 数时使用 unavailable,不能猜测;
  • 普通 HTTP 语义让 Core 推导 Outcome;只有供应商语义确实不同才覆盖 successcaller_errorendpoint_errormapping_error

协议层拒绝返回协议原生错误响应;DriverError 只用于 operational failure。每个 hook 只接受 ABI 定义的错误码集合,越权错误码会被视为非法 Driver 输出。

7. ABI 导出面

所有 module 都导出 memorylegate_alloc_v1legate_free_v1legate_bind_v1

Text 还必须精确导出:

legate_text_attempt_open_v1
legate_text_transform_buffered_response_v1
legate_text_sse_open_v1
legate_text_sse_transform_event_v1
legate_text_sse_finish_v1
legate_text_attempt_close_v1

Image 还必须精确导出:

legate_image_attempt_open_v1
legate_image_transform_buffered_response_v1
legate_image_attempt_close_v1

Module 不得有 imports,只能导出一个 memory;除可选 _initialize 外不能有额外 exports。

8. 测试与构建

在 SDK 根目录运行完整基线:

make test
make verify

make verify 会构建 Go/Rust 的 Text 与 Image 四个示例,并用 WABT 检查 ABI。修改自己的 module 时运行:

./scripts/verify-wasm.sh /absolute/path/to/driver.wasm

至少覆盖 config/slot 缺失、每个 Contract 的代表请求、selected model 替换、未知字段策略、buffered 成功与错误、SSE 终止和截断、Usage/Outcome、取消,以及输入接近边界的行为。

9. 宿主限制与上传

当前默认宿主限制包括:

资源 上限
WASM artifact 8 MiB
线性内存 1024 pages(64 MiB)
单条 ABI wire message 8 MiB
编译 10 秒
Bind 250 ms
单次 hook 100 ms

在 Console “驱动 → WASM 驱动”上传 Manifest JSON 与 .wasm。Central 严格校验并生成:

profile://workspace-<id>/<manifest-id>@sha256:<artifact-digest>

然后创建或编辑 Endpoint,选择该精确 Ref,填写 Schema 配置与 credential slots,保存并确认 Bind 成功。Profile 制品不可变,Endpoint 始终绑定精确 Ref。