@skyold/protocol-engine 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +466 -0
- package/README.zh-CN.md +309 -0
- package/dist/contracts.d.ts +84 -0
- package/dist/contracts.js +1 -0
- package/dist/engine.d.ts +4 -0
- package/dist/engine.js +352 -0
- package/dist/errors.d.ts +38 -0
- package/dist/errors.js +68 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/target-binding.d.ts +12 -0
- package/dist/target-binding.js +29 -0
- package/package.json +65 -0
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# TokenForge Protocol Engine
|
|
2
|
+
|
|
3
|
+
简体中文 | [English](./README.md)
|
|
4
|
+
|
|
5
|
+
> **第一次接入?** 请先阅读[《Protocol Engine 使用指南》](../../docs/guides/PROTOCOL_ENGINE.md)。它用支持矩阵、明确的不支持清单、带注释的 sync/stream/async 示例、架构图和调用时序解释怎样使用引擎。本文保留完整 API 与证明边界,作为进阶参考。
|
|
6
|
+
|
|
7
|
+
`@skyold/protocol-engine` 是面向 AI 模型聚合的 Host 无关执行内核。任何符合 V1 端口契约的应用、服务或库都可以嵌入它;引擎既不识别也不枚举外部应用类型,不依赖调用方的 HTTP 框架、数据库、身份模型、凭证、定价、用量存储或结算策略。
|
|
8
|
+
|
|
9
|
+
本文中的 **Host** 仅表示“嵌入并调用引擎的一方”这一抽象角色,不代表任何特定产品或部署形态。
|
|
10
|
+
|
|
11
|
+
## 模块状态
|
|
12
|
+
|
|
13
|
+
引擎是一个独立的 workspace package,提供唯一的包根 ESM API、独立的 TypeScript 构建和测试命令,且只有一个运行时依赖:`@skyold/model-protocol`。它不依赖任何 TokenForge Host、具体 Provider Adapter、存储、HTTP 框架、凭证仓库或网络客户端。
|
|
14
|
+
|
|
15
|
+
该包及其规范化协议依赖已经配置公开 npm 元数据,但尚未执行 registry 首次发布。因此,当前可以确认的是:
|
|
16
|
+
|
|
17
|
+
- 仓库内其他 package 可以独立嵌入并测试它;
|
|
18
|
+
- 可以独立检查它的打包运行时边界;
|
|
19
|
+
- 打包 tarball 已可由隔离的外部消费者安装与验证;
|
|
20
|
+
- 在发布门禁完成前,不承诺公共 registry 可用性、版本化发布或来源签名。
|
|
21
|
+
|
|
22
|
+
本地 V1 核心已经完成并冻结在既定执行边界内。整体上线认证尚未完成:九种内置协议族的 fixture conformance 已全部通过,当前已有七种协议的真实 Provider 成功证据;`openai-embeddings/v1` 和 `openai-images/v1` 仍待真实成功认证。公开发布、exact-SHA CI 和部署属于独立交付门禁,不是把 Host 业务逻辑加入引擎的理由。
|
|
23
|
+
|
|
24
|
+
在仓库根目录或 package 目录运行完整的引擎独立门禁:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pnpm --dir packages/protocol-engine check
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 嵌入 V1
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import {
|
|
34
|
+
createProtocolEngineTargetBindingV1,
|
|
35
|
+
createProtocolEngineV1,
|
|
36
|
+
isProtocolEngineErrorV1,
|
|
37
|
+
type ProtocolEngineAdapterRegistryPortV1,
|
|
38
|
+
} from '@skyold/protocol-engine';
|
|
39
|
+
|
|
40
|
+
const registry: ProtocolEngineAdapterRegistryPortV1 = createMyRegistry();
|
|
41
|
+
const binding = createProtocolEngineTargetBindingV1({
|
|
42
|
+
target: resolvedTarget,
|
|
43
|
+
bindTransport: (signal) => bindCredentialTransport(resolvedTarget, signal),
|
|
44
|
+
});
|
|
45
|
+
const engine = createProtocolEngineV1({ registry });
|
|
46
|
+
const execution = await engine.execute({ binding, task, signal });
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Host 使用 `createProtocolEngineTargetBindingV1({ target, bindTransport })` 构造请求级执行绑定。在把凭证绑定的 Transport 创建工作交给 Host 之前,该工厂会校验引擎请求的 `connectionRef` 是否与已解析目标一致,避免使用另一个连接执行当前目标。
|
|
50
|
+
|
|
51
|
+
每个 V1 Registry 都必须暴露 `listProtocolFamilies()`。引擎会把这个列表快照为排序后、不可变的 `engine.protocolFamilies`,因此嵌入式 Host、conformance 工具和认证工具使用同一份扩展契约。该快照也是分发权威:执行和异步任务操作都会拒绝未声明的协议族,Registry 返回的 Factory 也必须精确声明被请求的协议族,之后才会绑定 Transport。
|
|
52
|
+
|
|
53
|
+
Host 在构造 binding 前负责解析身份、策略、目标和凭证;经过认证的 Host 上下文不会进入引擎 API。Host 治理在调用引擎之前执行,用量、审计和结算在调用之后消费引擎返回的规范化事实。
|
|
54
|
+
|
|
55
|
+
“统一”是指所有嵌入方使用同一执行生命周期和公共契约,并不表示合并外部应用的业务编排,也不表示把身份、路由、凭证、治理、用量或结算职责移入引擎。不同嵌入方可以采用不同方式解析目标;解析完成后,都只向引擎提供相同的窄接口:task、target binding、Registry 和 Transport port。
|
|
56
|
+
|
|
57
|
+
目标中的 `provider` 是不透明的兼容性身份数据,不是引擎的分发依据。引擎核心可以把它传入协议能力校验,但绝不能根据 Provider 名称选择行为。对于 embedding 兼容空间一类语义事实,这个区别很重要:两个看似类似的模型可能生成互不兼容的向量。因而,为现有协议增加 Provider 不需要修改引擎;增加模型通常只需修改 Host 的目录/能力事实;增加协议则需要新增 Codec、Adapter、Registry 声明和 conformance 用例,但不应在引擎核心增加 Provider 或 Host 分支。
|
|
58
|
+
|
|
59
|
+
binding 只暴露引擎执行所需的分发字段:Provider、模型、连接引用以及协议/模型能力。Host 私有的 `sourceId` 和 `capabilityId` 保留在 Host 调用侧。引擎的执行结果和任务结果不会回显 target。
|
|
60
|
+
|
|
61
|
+
## 公共 API 参考
|
|
62
|
+
|
|
63
|
+
所有 API 都必须从 package root 导入。源码和 `dist` 子路径均为私有实现。
|
|
64
|
+
|
|
65
|
+
### 运行时导出
|
|
66
|
+
|
|
67
|
+
| 导出 | 用途 |
|
|
68
|
+
| -------------------------------------------- | ---------------------------------------------------- |
|
|
69
|
+
| `PROTOCOL_ENGINE_API_VERSION` | 精确的 V1 标识:`tokenforge-protocol-engine/v1`。 |
|
|
70
|
+
| `PROTOCOL_ENGINE_ERROR_CODES_V1` | 冻结的 V1 引擎稳定错误码集合。 |
|
|
71
|
+
| `ProtocolEngineErrorV1` | 引擎操作失败使用的版本化错误类。 |
|
|
72
|
+
| `isProtocolEngineErrorV1(error)` | 可跨 realm 判断公共 V1 错误结构的类型守卫。 |
|
|
73
|
+
| `createProtocolEngineV1({ registry })` | 基于一个 Adapter Registry 清单创建不可变的引擎视图。 |
|
|
74
|
+
| `createProtocolEngineTargetBindingV1(input)` | 把一个已解析目标绑定到请求级 Host Transport 工厂。 |
|
|
75
|
+
|
|
76
|
+
### 导出的类型
|
|
77
|
+
|
|
78
|
+
| 类型组 | 导出 |
|
|
79
|
+
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
80
|
+
| 引擎 | `ProtocolEngineV1`、`ProtocolEngineApiVersion`、`ProtocolEngineAdapterRegistryPortV1` |
|
|
81
|
+
| 执行输入 | `ProtocolEngineExecuteInputV1`、`ProtocolEngineDispatchTargetV1`、`ProtocolEngineTargetBindingV1`、`CreateProtocolEngineTargetBindingInputV1` |
|
|
82
|
+
| 执行输出 | `ProtocolEngineExecutionV1`、`ProtocolEngineResponseV1`、`ProtocolEngineStreamV1`、`ProtocolEngineAcceptedJobV1`、`ProtocolEngineRejectedV1` |
|
|
83
|
+
| 异步任务 | `ProtocolEngineAsyncJobsV1`、`ProtocolEngineAsyncJobInputV1`、`ProtocolEngineAsyncJobListRequestV1`、`ProtocolEngineAsyncJobObservationV1`、`ProtocolEngineAsyncJobListResultV1`、`ProtocolEngineAsyncJobCancellationV1` |
|
|
84
|
+
| 错误 | `ProtocolEngineErrorV1`、`ProtocolEngineErrorCodeV1`、`ProtocolEngineErrorPhaseV1`、`ProtocolEngineErrorOptionsV1` |
|
|
85
|
+
|
|
86
|
+
规范化 task、capability、Adapter、Transport、result、stream event、usage 和 job snapshot 类型来自 `@skyold/model-protocol`;引擎不重复定义这些契约。
|
|
87
|
+
|
|
88
|
+
### `createProtocolEngineV1`
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const engine = createProtocolEngineV1({ registry });
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
注入的 Registry 必须实现:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
interface ProtocolEngineAdapterRegistryPortV1 {
|
|
98
|
+
listProtocolFamilies(): readonly ProtocolFamily[];
|
|
99
|
+
require(protocolFamily: ProtocolFamily): ProviderAdapterFactory;
|
|
100
|
+
requireAsync?(protocolFamily: ProtocolFamily): ProviderAsyncJobAdapterFactory;
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
构造时,引擎会快照、排序、校验并冻结协议清单。重复或格式错误的协议族会立即失败。`require()` 负责同步和流式执行;只有异步任务协议族才需要 `requireAsync()`。
|
|
105
|
+
|
|
106
|
+
TokenForge Host 通常从一致性的内置 suite 获取 Registry:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { createBuiltinTokenForgeProtocolSuiteV1 } from '@skyold/provider-adapters';
|
|
110
|
+
import { createProtocolEngineV1 } from '@skyold/protocol-engine';
|
|
111
|
+
|
|
112
|
+
const suite = createBuiltinTokenForgeProtocolSuiteV1();
|
|
113
|
+
const engine = createProtocolEngineV1({ registry: suite.registry });
|
|
114
|
+
console.log(engine.apiVersion);
|
|
115
|
+
console.log(engine.protocolFamilies);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`engine.protocolFamilies` 是运行时权威清单;Host 不应再复制一份协议 allowlist。
|
|
119
|
+
|
|
120
|
+
### Target binding
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
const binding = createProtocolEngineTargetBindingV1({
|
|
124
|
+
target: {
|
|
125
|
+
provider: 'opaque-provider-identity',
|
|
126
|
+
providerModel: 'resolved-provider-model',
|
|
127
|
+
connectionRef: 'host-owned-connection-reference',
|
|
128
|
+
capability: resolvedProtocolCapability,
|
|
129
|
+
},
|
|
130
|
+
bindTransport: (signal) => createCredentialBoundTransport({ resolvedConnection, signal }),
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Host 必须在创建 binding 前完成认证、路由、目标选择、凭证查找、URL 安全策略和治理。binding 会在凭证或网络访问发生前拒绝连接引用替换。绝不能把租户/账户专属的 Transport 存进共享 Registry 或 Engine。
|
|
135
|
+
|
|
136
|
+
### `engine.execute`
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
const execution = await engine.execute({
|
|
140
|
+
binding,
|
|
141
|
+
task,
|
|
142
|
+
signal,
|
|
143
|
+
// preparedMedia, // Host 为图片编辑等协议准备的可选媒体
|
|
144
|
+
// providerCallbackUrl, // Host 为异步 Provider 选择的可选回调地址
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
| 输入 | 含义 |
|
|
149
|
+
| --------------------- | ---------------------------------------------------------------- |
|
|
150
|
+
| `binding` | 已解析目标与请求级 Transport binder。 |
|
|
151
|
+
| `task` | 规范化 `TaskEnvelopeV2`;原始 HTTP 请求不进入引擎。 |
|
|
152
|
+
| `signal` | 传播到 Transport、流式执行或异步启动的请求取消信号。 |
|
|
153
|
+
| `preparedMedia` | 可选的有界且已校验媒体流,用于图片编辑等协议。 |
|
|
154
|
+
| `providerCallbackUrl` | 可选的异步任务回调地址;生命周期持久化和回调处理仍由 Host 负责。 |
|
|
155
|
+
|
|
156
|
+
必须处理完整的可辨识联合类型,不能假设所有模型都是同步 Chat 模型:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
switch (execution.kind) {
|
|
160
|
+
case 'response': {
|
|
161
|
+
consumeTerminal(execution.terminal);
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
case 'stream': {
|
|
165
|
+
try {
|
|
166
|
+
for await (const event of execution.events) consumeNormalizedEvent(event);
|
|
167
|
+
consumeTerminal(await execution.terminal);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
execution.abort(error);
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
case 'accepted-job': {
|
|
175
|
+
persistAcceptedJob(execution.providerJobId);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
case 'rejected': {
|
|
179
|
+
recordRejectedStart(execution.failureCode, execution.acceptance.state);
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`response` 和 `stream` 的终态事实已经规范化并经过运行时校验。`accepted-job` 表示 Provider 已持久接受任务。`rejected` 同时覆盖“确定未接受”和“是否接受未知”;当创建结果为接受状态未知时,Host 不得盲目重试,否则可能重复创建任务。
|
|
186
|
+
|
|
187
|
+
### `engine.jobs`
|
|
188
|
+
|
|
189
|
+
对先前已接受的 Provider task,后续操作使用同一个 target binding:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
const observation = await engine.jobs.get({ binding, providerJobId, signal });
|
|
193
|
+
|
|
194
|
+
const page = await engine.jobs.list({
|
|
195
|
+
binding,
|
|
196
|
+
pageNum: 1,
|
|
197
|
+
pageSize: 20,
|
|
198
|
+
signal,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
await engine.jobs.cancel({ binding, providerJobId, signal });
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
引擎会校验返回的 snapshot 和列表。任务归属、持久化、轮询计划、回调对账、结果物化、用量和结算仍由 Host 负责。
|
|
205
|
+
|
|
206
|
+
### 失败行为
|
|
207
|
+
|
|
208
|
+
无效 Registry 清单、target/task 不匹配、不支持的能力、连接替换、异常 Adapter 输出以及 Transport 绑定失败,都会在不安全事实到达 Host 前拒绝操作。每个引擎操作错误都使用 `ProtocolEngineErrorV1`,并携带相同的 `tokenforge-protocol-engine/v1` API 版本、稳定的 `code`,以及一个结构化阶段:`configuration`、`input`、`transport`、`adapter` 或 `lifecycle`。
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
try {
|
|
212
|
+
await engine.execute({ binding, task, signal });
|
|
213
|
+
} catch (error) {
|
|
214
|
+
if (!isProtocolEngineErrorV1(error)) throw error;
|
|
215
|
+
hostMapEngineFailure(error.code, error.phase);
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
`cause` 仅用于诊断,可能包含 Host Transport 或 Adapter 细节;不得把它或原始异常消息序列化给客户端。可枚举的公共错误分类不包含 Provider payload、凭证、重试策略、HTTP 状态、租户事实、用量、定价或结算决策,这些映射由 Host 负责。历史固定运行时消息暂时保持兼容,但新集成必须按 `code` 分支,绝不能按 `message` 分支。
|
|
220
|
+
|
|
221
|
+
引擎会根据已解析 capability 校验规范化 task,选择协议 Adapter,并返回一种版本化执行起点:
|
|
222
|
+
|
|
223
|
+
- `response`:同步终态执行;
|
|
224
|
+
- `stream`:规范化事件、终态 Promise 和取消能力;
|
|
225
|
+
- `accepted-job`:Provider 已持久接受异步任务;
|
|
226
|
+
- `rejected`:异步启动未被接受,或接受状态未知。
|
|
227
|
+
|
|
228
|
+
后续异步任务操作使用 `engine.jobs.get`、`engine.jobs.list` 和 `engine.jobs.cancel`。持久化、轮询、回调、物化、用量和结算仍是 Host 职责。
|
|
229
|
+
|
|
230
|
+
异步 Adapter factory 通过 `jobOperations` 声明支持的后续操作。引擎会在绑定 Transport 前,以 `PROTOCOL_ENGINE_CAPABILITY_UNSUPPORTED` 拒绝未声明的操作。因此协议可以保留真实生命周期差异:例如,一个协议可以支持 `get`,而不需要伪装成支持 `list` 或 `cancel`。
|
|
231
|
+
|
|
232
|
+
### Provider response 兼容原则
|
|
233
|
+
|
|
234
|
+
Provider wire response 是开放世界边界。Adapter 校验并提取当前协议版本理解的字段,忽略同步 body 和已知 stream frame 中新增的附加字段,且不会把这些未知字段保留到规范化输出中。这样 Provider 新增响应元数据时,不会破坏原本兼容的协议实现。
|
|
235
|
+
|
|
236
|
+
兼容不代表把已知字段异常当作成功。已知字段缺失、类型错误、冲突、重复、不安全或语义无效仍然失败。未知 stream event kind 可能改变生命周期语义,不能视为无害附加字段;除非协议 Adapter 明确支持,否则必须失败。解析深度、body 大小、URL、base64、index、usage 和终态一致性限制继续生效。
|
|
237
|
+
|
|
238
|
+
随后,引擎会在向 Host 暴露结果前校验 Adapter 输出,包括同步/流式接受结果、终态结构与一致性、每个规范化流事件,以及异步启动、snapshot 和列表结果结构。规范化 event、metadata、terminal、result、usage、Responses 和异步任务对象都是封闭 schema:未声明字段会被拒绝。因此 Provider 原始扩展字段不能穿过 Adapter 泄漏到 Host egress、用量或结算流程。
|
|
239
|
+
|
|
240
|
+
终态 result 必须与请求的 Task kind 一致;当 result、terminal 和 Responses 中重复出现 usage 事实时,它们必须一致。自定义 Adapter 不能依赖 TypeScript 类型断言绕过 V1 运行时契约,也不能让 Host egress 和结算观察到不同事实。
|
|
241
|
+
|
|
242
|
+
## 扩展规则
|
|
243
|
+
|
|
244
|
+
新增协议需要实现共享 ingress/egress Codec 和 Adapter factory 契约,注册 Host Transport 元数据,并把它们纳入一个一致的 Protocol Suite。引擎核心不得增加 Provider 名称、模型名称、租户或 Host 框架分支。嵌入方可以注入自定义 Registry;TokenForge 仓库提供 `@skyold/provider-adapters` 的 `createBuiltinTokenForgeProtocolSuiteV1()` 作为内置实现。自定义 suite 使用 `createTokenForgeProtocolSuiteV1()`,它会在引擎消费 Registry 前校验 Adapter/Transport 和直接 Ingress/Egress 清单。TokenForge 内置组合还会在每个 Adapter/Transport 条目旁声明是否参与直接 HTTP,并拒绝声明与 Codec 清单不一致。任何嵌入方的执行集成都只能查询 Registry;增加新协议不得新增应用类型或 Host 协议族分支。
|
|
245
|
+
|
|
246
|
+
使用方只从 `@skyold/protocol-engine` 包根导入。内部源码或构建子路径不属于 V1,仓库架构门禁会拒绝这些导入。打包产物只允许包根背后的五个生产模块以及英文、简体中文手册;不会交付测试或其他仓库内部文件。
|
|
247
|
+
|
|
248
|
+
稳定契约标识是 `tokenforge-protocol-engine/v1`。破坏兼容性的契约变化必须发布新 API 版本,不能改变 V1 语义。
|
|
249
|
+
|
|
250
|
+
## 测试与证明层级
|
|
251
|
+
|
|
252
|
+
任何测试套件都无法在数学意义上保证未来全部引擎功能。TokenForge 把证明拆分为不同层级,避免把一个狭窄单元测试误认为完整 proxy 证据。
|
|
253
|
+
|
|
254
|
+
| 层级 | 命令 | 能证明 | 不能证明 |
|
|
255
|
+
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ----------------------------------------------- |
|
|
256
|
+
| 引擎 package | `pnpm --dir packages/protocol-engine check` | 独立构建,以及 Registry、binding、同步、流式、异步任务、校验和 Host 等价性测试。 | 具体 Adapter wire 格式、Host 持久化、真实网络。 |
|
|
257
|
+
| 内置 conformance | `pnpm --filter @skyold/provider-adapters test -- src/conformance/runtime-conformance.test.ts` | 每个已注册内置协议族均通过公共 Engine API 和 fixture Transport 执行。 | Provider 凭证或当前远端行为。 |
|
|
258
|
+
| 架构边界 | `pnpm --filter @tokenforge/acceptance test -- src/L1-kernel/import-boundary.test.ts` | 引擎依赖隔离、package surface、禁止 raw-forward/直接绕过 Adapter、Host 中立性。 | 单独证明运行时语义正确。 |
|
|
259
|
+
| Host 集成 | 各嵌入应用的定向测试与仓库 `pnpm check` | 相应调用方的上下文绑定、流式、媒体持久化、用量与结算兼容。 | exact-SHA 部署或真实 Provider 可用性。 |
|
|
260
|
+
| 真实 Provider 认证 | `pnpm certify:protocol-engine-real --manifest /protected/path/suite.json --run --env-file .env --ack-real-provider-calls I_AUTHORIZE_REAL_PROVIDER_CALLS` | 每个成功证据分片对应的当前、已认证 Provider 行为。 | 部署、计费正确性或缺失协议族。 |
|
|
261
|
+
|
|
262
|
+
本地修改引擎时,最低独立门禁是引擎 package 命令。协议或 Adapter 变更还必须通过内置 conformance。Host 集成变更还必须通过受影响 Host 的测试。只有 Registry 清单中所有协议族都取得真实 Provider 成功证据,且实际交付的 exact SHA 通过 CI 和部署门禁,整个目标才算完成。
|
|
263
|
+
|
|
264
|
+
## 真实 Provider 认证
|
|
265
|
+
|
|
266
|
+
仓库内的 opt-in 认证 Host 会直接驱动这套公共 Engine API 和内置 Adapter Registry,不经过任何产品应用的 route。版本化 JSON manifest 为 `engine.protocolFamilies` 返回的每个协议族提供一个或多个 case,其中包含已解析 target 与 capability 事实、规范化 task、密钥环境变量名、精确 Provider 域名,以及 suite 请求/时长上限。定价和 Provider 计费刻意不属于引擎契约或认证事实。
|
|
267
|
+
|
|
268
|
+
默认只做离线校验:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
pnpm certify:protocol-engine-real --manifest /protected/path/suite.json
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
真实执行还必须提供 `--run`、环境文件以及精确的 CLI 授权 `--ack-real-provider-calls I_AUTHORIZE_REAL_PROVIDER_CALLS`。CLI 从已校验 manifest 推导运行时域名 allowlist,并通过环境变量引用读取每个密钥。runner 会拒绝私网或云元数据地址、跨源重定向、不完整协议覆盖以及请求/时间上限违规。它不会打印 prompt、key、Provider request ID、job ID、Provider 名称或模型名称;持久证据文件权限为 `0600`,只包含协议事实、终态、数字 usage、计数和截断的 SHA-256 指纹。
|
|
275
|
+
|
|
276
|
+
生成的 manifest 会把请求上限设置为根据 case 推导出的精确最坏值。CLI 摘要分别报告 execution start 与异步任务 list/get observation 请求;这些字段都不是价格、费率表或结算输入。认证流程刻意不执行取消,因为取消会破坏外部已创建任务;它属于需要单独授权的 Provider 生命周期检查。
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
pnpm certify:protocol-engine-real --manifest /protected/path/suite.json --run \
|
|
280
|
+
--env-file .env \
|
|
281
|
+
--ack-real-provider-calls I_AUTHORIZE_REAL_PROVIDER_CALLS
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
本地校验通过只能证明 suite 结构已经就绪,不能证明调用过任何 Provider。只有完整完成的 `--run` 产物才是真实 Provider 证据。
|
|
285
|
+
|
|
286
|
+
对于已被接受且支持 list 的异步 target,Acceptance Host 可以通过 `engine.jobs.list` 执行一次只读诊断,不会创建新任务:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
pnpm --filter @tokenforge/acceptance diagnose:protocol-engine-real-async \
|
|
290
|
+
--manifest /protected/path/async-shard.json \
|
|
291
|
+
--env-file /protected/path/provider.env \
|
|
292
|
+
--created-after 2026-08-18T03:00:00.000Z \
|
|
293
|
+
--ack-real-provider-calls I_AUTHORIZE_REAL_PROVIDER_CALLS
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
诊断只报告协议族、匹配数量、规范化状态计数和有界 Provider 错误码。它不会输出 Provider job ID、模型、URL、响应消息或 body,也不能满足认证门禁。
|
|
297
|
+
|
|
298
|
+
当支持 list 的异步分片已经被 Provider 接受、但认证仅因有界轮询耗尽而停止时,可以恢复这一笔已提交任务,而不创建第二笔任务:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
pnpm --filter @tokenforge/acceptance recover:protocol-engine-real-shard \
|
|
302
|
+
--manifest /protected/path/async-shard.json \
|
|
303
|
+
--failure-evidence /protected/path/polling-exhausted.json \
|
|
304
|
+
--env-file /protected/path/provider.env \
|
|
305
|
+
--output /protected/path/recovered-success.json \
|
|
306
|
+
--ack-real-provider-calls I_AUTHORIZE_REAL_PROVIDER_CALLS
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
恢复操作只接受相互匹配的轮询耗尽证据,只执行一次只读 `engine.jobs.list`,并要求原始提交时间窗口内恰好存在一个匹配任务。它不会重新提交任务,也不会输出原始 job ID。只有规范化状态为 `succeeded` 的 snapshot 才会生成标准成功证据;其他状态仍然属于非成功证据。
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { AdapterTerminalOutcome, NormalizedStreamEventV1, PreparedMediaExecutionInputV2, ProtocolFamily, ProviderAsyncJobListInputV2, ProviderAsyncJobSnapshotV2, ProviderAsyncJobAdapterFactory, ProviderAcceptanceFacts, ProviderAdapterFactory, ProviderDispatchTargetV2, ProviderResponseMetadata, ProviderTransport, TaskEnvelopeV2 } from '@skyold/model-protocol';
|
|
2
|
+
export declare const PROTOCOL_ENGINE_API_VERSION: "tokenforge-protocol-engine/v1";
|
|
3
|
+
export type ProtocolEngineApiVersion = typeof PROTOCOL_ENGINE_API_VERSION;
|
|
4
|
+
export type ProtocolEngineDispatchTargetV1 = ProviderDispatchTargetV2;
|
|
5
|
+
export interface ProtocolEngineTargetBindingV1 {
|
|
6
|
+
readonly target: ProtocolEngineDispatchTargetV1;
|
|
7
|
+
bindTransport(connectionRef: string, signal: AbortSignal): Promise<ProviderTransport>;
|
|
8
|
+
}
|
|
9
|
+
export interface ProtocolEngineAdapterRegistryPortV1 {
|
|
10
|
+
listProtocolFamilies(): readonly ProtocolFamily[];
|
|
11
|
+
require(protocolFamily: ProtocolFamily): ProviderAdapterFactory;
|
|
12
|
+
requireAsync?(protocolFamily: ProtocolFamily): ProviderAsyncJobAdapterFactory;
|
|
13
|
+
}
|
|
14
|
+
export interface ProtocolEngineExecuteInputV1 {
|
|
15
|
+
readonly binding: ProtocolEngineTargetBindingV1;
|
|
16
|
+
readonly task: TaskEnvelopeV2;
|
|
17
|
+
readonly signal: AbortSignal;
|
|
18
|
+
readonly preparedMedia?: PreparedMediaExecutionInputV2;
|
|
19
|
+
readonly providerCallbackUrl?: string;
|
|
20
|
+
}
|
|
21
|
+
interface ProtocolEngineExecutionBaseV1 {
|
|
22
|
+
readonly apiVersion: ProtocolEngineApiVersion;
|
|
23
|
+
readonly acceptance: ProviderAcceptanceFacts;
|
|
24
|
+
readonly responseMetadata: ProviderResponseMetadata;
|
|
25
|
+
}
|
|
26
|
+
export interface ProtocolEngineResponseV1 extends ProtocolEngineExecutionBaseV1 {
|
|
27
|
+
readonly kind: 'response';
|
|
28
|
+
readonly terminal: AdapterTerminalOutcome;
|
|
29
|
+
}
|
|
30
|
+
export interface ProtocolEngineStreamV1 extends ProtocolEngineExecutionBaseV1 {
|
|
31
|
+
readonly kind: 'stream';
|
|
32
|
+
readonly events: AsyncIterable<NormalizedStreamEventV1>;
|
|
33
|
+
readonly terminal: Promise<AdapterTerminalOutcome>;
|
|
34
|
+
abort(reason: unknown): void;
|
|
35
|
+
}
|
|
36
|
+
export interface ProtocolEngineAcceptedJobV1 extends ProtocolEngineExecutionBaseV1 {
|
|
37
|
+
readonly kind: 'accepted-job';
|
|
38
|
+
readonly acceptance: {
|
|
39
|
+
readonly state: 'accepted';
|
|
40
|
+
};
|
|
41
|
+
readonly providerJobId: string;
|
|
42
|
+
}
|
|
43
|
+
export interface ProtocolEngineRejectedV1 extends ProtocolEngineExecutionBaseV1 {
|
|
44
|
+
readonly kind: 'rejected';
|
|
45
|
+
readonly acceptance: {
|
|
46
|
+
readonly state: 'not-accepted' | 'unknown';
|
|
47
|
+
};
|
|
48
|
+
readonly failureCode: string;
|
|
49
|
+
readonly retryable: boolean;
|
|
50
|
+
}
|
|
51
|
+
export type ProtocolEngineExecutionV1 = ProtocolEngineResponseV1 | ProtocolEngineStreamV1 | ProtocolEngineAcceptedJobV1 | ProtocolEngineRejectedV1;
|
|
52
|
+
export interface ProtocolEngineV1 {
|
|
53
|
+
readonly apiVersion: ProtocolEngineApiVersion;
|
|
54
|
+
readonly protocolFamilies: readonly ProtocolFamily[];
|
|
55
|
+
execute(input: ProtocolEngineExecuteInputV1): Promise<ProtocolEngineExecutionV1>;
|
|
56
|
+
readonly jobs: ProtocolEngineAsyncJobsV1;
|
|
57
|
+
}
|
|
58
|
+
export interface ProtocolEngineAsyncJobInputV1 {
|
|
59
|
+
readonly binding: ProtocolEngineTargetBindingV1;
|
|
60
|
+
readonly providerJobId: string;
|
|
61
|
+
readonly signal: AbortSignal;
|
|
62
|
+
}
|
|
63
|
+
export interface ProtocolEngineAsyncJobListRequestV1 extends ProviderAsyncJobListInputV2 {
|
|
64
|
+
readonly binding: ProtocolEngineTargetBindingV1;
|
|
65
|
+
readonly signal: AbortSignal;
|
|
66
|
+
}
|
|
67
|
+
export interface ProtocolEngineAsyncJobObservationV1 {
|
|
68
|
+
readonly apiVersion: ProtocolEngineApiVersion;
|
|
69
|
+
readonly snapshot: ProviderAsyncJobSnapshotV2;
|
|
70
|
+
}
|
|
71
|
+
export interface ProtocolEngineAsyncJobListResultV1 {
|
|
72
|
+
readonly apiVersion: ProtocolEngineApiVersion;
|
|
73
|
+
readonly items: readonly ProviderAsyncJobSnapshotV2[];
|
|
74
|
+
readonly total: number;
|
|
75
|
+
}
|
|
76
|
+
export interface ProtocolEngineAsyncJobCancellationV1 {
|
|
77
|
+
readonly apiVersion: ProtocolEngineApiVersion;
|
|
78
|
+
}
|
|
79
|
+
export interface ProtocolEngineAsyncJobsV1 {
|
|
80
|
+
get(input: ProtocolEngineAsyncJobInputV1): Promise<ProtocolEngineAsyncJobObservationV1>;
|
|
81
|
+
list(input: ProtocolEngineAsyncJobListRequestV1): Promise<ProtocolEngineAsyncJobListResultV1>;
|
|
82
|
+
cancel(input: ProtocolEngineAsyncJobInputV1): Promise<ProtocolEngineAsyncJobCancellationV1>;
|
|
83
|
+
}
|
|
84
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PROTOCOL_ENGINE_API_VERSION = 'tokenforge-protocol-engine/v1';
|
package/dist/engine.d.ts
ADDED