System One API schema
状态: 已实现。
目标
- 处处使用精确、带版本的 schema。 Router 对外提供、内部调用或在后端池中调用的每个决策模型 API,都遵循一个精确定义、带版本、保存在仓库中并经过一致性测试的 schema。
state始终是标准内容。 vllm-sr 只在自然的位置增加可选字段(输入和输出都是),因此标准客户端或服务器不受影响。批量中的每一项都是完整的标准请求。- 一层 codec 在客户端和服务端两侧完成 schema 之间的转换,与 Router 的 LLM 协议 codec(
llmprotocol和protocolcodec)对应:稳固、可插拔、易维护。 - Schema 文档 由规范生成,并在其上提供指南。
Schema
| ID | 定义 |
|---|---|
systemone/0.2 | 标准:公开的 System One API 0.2 版。已发布的 OpenAPI 文档没有许可证,因此不复制进仓库;config/schemas/systemone/systemone-0.2.schema.yaml 是我们用自己的话对该接口的描述(字段名、类型和结构),附带来源 URL 和核对时的 sha256。make systemone-schemas-drift 把它与已发布的文档进行比较。 |
vllm-sr/5.0 | systemone/0.2 加上 vllm-sr 扩展集 5.0,即 config/schemas/systemone/vllm-sr-5.0.extensions.yaml。它取代运行时契约 4.0.0。 |
vllm-sr/4.0 | 旧版,作为兼容超集保留:运行时契约 4.0.0 接受的一 切(由 state 键推断的隐式角色、choices/levels/labels、images/videos、只有 preset 的问题、/v1/systemone 上的 states 批量、{error} 错误体、data 模型列表)。在 listener 上它还接受所有 5.0 形式,新客户端可以在同一端点使用 5.0,而现有客户端无需改动。其冻结的契约为 config/schemas/systemone/vllm-sr-4.0.openapi.yaml。 |
标准的新版本是新 ID(例如 systemone/0.3);只要还有部署引用,旧 ID 就保留。标准和扩展集各自独立演进版本:5.x 小版本只增加字段,破坏性变更是新的大版本。
listener 和上游共用一个 API 注册表
一个注册表(apiregistry)描述 Router 使用的每个 API。每项声明其名称、路径、schema ID 及默认值、设置项、所服务的模型种类,以及每个 schema 的 codec:
| API | 路径 | 默认 schema | 其他 schema |
|---|---|---|---|
chat_completions | /v1/chat/completions | openai/v1 | |
responses | /v1/responses | openai/v1 | |
messages | /v1/messages | anthropic/2023-06-01 | |
images | /v1/images/* | openai/v1 | |
systemone | /v1/systemone、/v1/systemone/batch、/v1/decisions | listener 上为 vllm-sr/5.0,上游为 systemone/0.2 | vllm-sr/4.0 |
API 按 API 本身命名,而不是按厂商命名:一个厂商会提供多个 API,许多厂商实现同一个 API。旧配置使用的名称 openai 和 anthropic 是 chat_completions 和 messages 的别名,凡是出现 API 名称的地方都会解析;旧配置无需修改即可运行。在同一个 apis 映射中两次给出同一个 API(别名和正式名称)是校验错误。
新 API 是一条注册表项,带它的路径、schema 和 codec;某个 API 的新版本是一个 schema ID 及其 codec。两者都不改变配置结构,也不影响任何调用方。
listener API
listener 在一个 apis 映射中列出它提供的 API,键为注册表中的名称。每项结构相同:schema(只用于有版本的 API,目前是 systemone)、models(省略时为该 API 种类的全部模型)以及该 API 自己的设置(systemone 的 max_deadline_ms)。
- 没有
apis时,listener 为所有模型提供chat_completions、responses、messages和images;systemone除非列出否则关闭,因为它直接发布决策模型。 - 有
apis时,listener 只提供列出的 API;其他 API 路径返回 404,detail中写明 listener。生成的 Envoy listener 只路由所提供 API 的路径。 GET /v1/models只列出所提供 API 的模型:LLM API 用 OpenAI 的data,systemone用 System One 的models。- listener 级别的
models和systemone字段已移除;vllm-sr config migrate会把它们移入apis。
只有 System One 有版本:Chat Completions、Responses 和 Messages 以增量方式演进,而 System One 有不兼容的版本,为每个版本单独设路径会迫使旧客户端改 URL。
schema 在哪里配置
- 服务端:
listeners[].apis.systemone.schema,即 listener 对其客户端使用的契约;默认vllm-sr/5.0。 - 客户端:
providers.models[]上与api_format并列的schema,以及部署上与provider: systemone并列的schema;默认systemone/0.2。deviations和max_concurrency是部署的客户端设置。
vllm-sr/4.0 listener
在一个生产部署上测得,公开端点有相当大比例的请求(大多来自外部客户端)使用只有 4.0 接受的形式,主要是内联 images 和 states 批量。因此 4.0 listener 在同一路径上、无需请求头地同时接受两种形式:
- 4.0 独有的形式正好是 5.0 服务器会拒绝的那些,所以读取它们不会改变合法 5.0 请求的含义。有两处重叠,即合法的 5.0 请求在 4.0 listener 上保留 4.0 的读法:没有
roles的对象 state 保留 4.0 的隐式角色;由放在一个条目前面的 asset 占位符组成的 state 仍是一个为其 asset 加标签的数组,而 5.0 会把它读作该条目,媒体放在它前面。listener 对这两种情况都计数。 - 回答保持 4.0 形式;错误体同时带有
detail(标准)和error(4.0),两类客户端都能读取。 - 每个 listener 都有一个按形式统计客户端所用 4.0 独有形式的计数器,告诉运维何时可以安全切换到
vllm-sr/5.0。
设计规则
- 标准优先。 标准能表达的,都用标准的方式表达;没有第二种写法。
- 每条 vllm-sr 消息都是完整的标准消息。 每个问题都带标准的
instructions和criteria;扩展只是附加提示。例如preset让 vllm-sr 服务器使用训练过的 head。 - 扩展字段平铺在自然位置, 没有命名空间对象,也没有请求头,与 vLLM 的额外参数一样:SDK 用户通过
extra_body传递。 - 未知字段以 400 拒绝并写明名称, 在每个 vllm-sr 服务器上都如此,因此不会有字段被悄悄忽略,也不需要能力协商。
- state 是标准内容。 它的键没有隐藏含义。区分角色的模型族读取显式的
roles映射;没有roles时,整个 state 在所有模型族上都是用户内容。每个模型族保留自己把对象 state 渲染为模型输 入的方式,就像 LLM 的对话模板:渲染是 API 背后的模型细节,模型的启动自检保持其输入不变。 - 三层名称;部分名称保持不变。 API 字段由两份规范定义。state 内的键由客户端决定;Router 自己构建多部分 state 时,沿用模型训练和校准时使用的
request、history、previous_turns、previous_answer、tool_results、candidate、answer和context。Router 配置中input:的名称是同一组。没有任何模型输入发生变化,因此无需重新校准。 - 遵循标准的限制和行为: 状态码(400、带
detail列表的 422、带Retry-After的 429 和 529)、长上下文请求头,以及最大 32 MiB 的请求体。 - 批量是单独的扩展端点
POST /v1/systemone/batch,其requests中是完整的标准请求;/v1/systemone只接受一个请求。/v1/decisions保留为原生端点,在 200 中给出每个问题的错误;没有任何有效问题的请求在 Router 和运行时上都返回 400invalid_request,并列出每个问题的错误。 vllm-sr/auto按能力路由。 请求只发给能回答其全部问题类型、能读取其媒体(box 与图像、视频、完整输入等)的池成员,依据成员的模型卡或列表,否则依据其部署声明的capabilities。没有成员能回答时,请求以 400unsupported_capability被拒绝,其detail写明缺少什么以及每个成员。