1
0

refactor: expect 类型模型重构,Raw/Resolved 双层分离与断言基础设施内聚

- 重命名 ContentRules→ContentExpectations, KeyValueExpect→KeyedExpectations
- 新增 Raw/Resolved 双层模型:resolve 阶段物化为执行计划,store 持久化 Raw 快照
- HTTP body 按需读取:status/headers 失败或无 body expectation 时不读取 body
- 新增 displayValueExpectation() 解包 failure.expected 用户可读展示
- 修复 checkEarlyTimeout 独立 lte/lt 检查,修复 KeyedExpectations JSON Schema
- 新增 expect/value.ts(resolve/check/display)、keyed.ts、content.ts、headers.ts、status.ts
- 删除旧 normalize.ts/matcher.ts/validate-matcher.ts/key-value.ts
- 更新 DEVELOPMENT.md:expect 五层管线表、displayValueExpectation、1.7↔1.10 交叉引用
- 同步 13 个 main specs,归档 refactor-expect-type-model 变更(62/62 tasks)
This commit is contained in:
2026-05-20 16:12:48 +08:00
parent 6098be2d9e
commit 60a54b483f
90 changed files with 2487 additions and 1493 deletions

View File

@@ -108,18 +108,18 @@ LLM checker SHALL 在 SDK 调用结果和 expect 断言之间构建 `LlmCheckObs
- **THEN** LLM checker SHALL 返回 `phase: "request"` 的 error failure
### Requirement: LLM Expect 断言
LLM checker SHALL 支持 `expect.status``expect.headers``expect.output``expect.finishReason``expect.rawFinishReason``expect.usage.inputTokens``expect.usage.outputTokens``expect.usage.totalTokens``expect.stream.completed``expect.stream.firstTokenMs``expect.durationMs``expect.status` SHALL 保持 HTTP 状态码数组语义并复用 HTTP status 断言。`expect.headers` SHALL 使用共享 `KeyValueExpect`header key 大小写不敏感。`expect.output` MUST 使用共享 `ContentRules``expect.finishReason``expect.rawFinishReason` SHALL 使用共享 `ValueMatcher``expect.usage.*``expect.stream.firstTokenMs``expect.durationMs` SHALL 使用共享 `ValueMatcher`。LLM checker MUST 按固定顺序快速失败,非流式顺序为 status、headers、output、finishReason、rawFinishReason、usage、durationMs流式顺序为 status、headers、stream.completed、stream.firstTokenMs、output、finishReason、rawFinishReason、usage、durationMs。
LLM checker SHALL 支持 `expect.status``expect.headers``expect.output``expect.finishReason``expect.rawFinishReason``expect.usage.inputTokens``expect.usage.outputTokens``expect.usage.totalTokens``expect.stream.completed``expect.stream.firstTokenMs``expect.durationMs``expect.status` SHALL 保持 HTTP 状态码数组语义并复用共享 status code 断言,未配置时在 Resolved expect 中物化默认 `[200]``expect.headers` SHALL 使用共享 `RawKeyedExpectations` 输入并在运行期使用 `KeyedExpectations`header key 大小写不敏感。`expect.output` MUST 使用共享 `RawContentExpectations` 输入并在运行期使用 `ContentExpectations``expect.finishReason``expect.rawFinishReason` SHALL 使用共享 `RawValueExpectation` 输入并在运行期使用 `ValueExpectation``expect.usage.*``expect.stream.firstTokenMs``expect.durationMs` SHALL 使用共享 `RawValueExpectation` 输入并在运行期使用 `ValueExpectation`。LLM checker MUST 按固定顺序快速失败,非流式顺序为 status、headers、output、finishReason、rawFinishReason、usage、durationMs流式顺序为 status、headers、stream.completed、stream.firstTokenMs、output、finishReason、rawFinishReason、usage、durationMs。
#### Scenario: 默认 status 断言
- **WHEN** LLM target 未配置 `expect.status`
- **THEN** LLM checker SHALL 使用默认 `status: [200]` 语义
- **THEN** LLM checker SHALL 在 Resolved expect 中使用默认 `status: [200]` 语义
#### Scenario: expect headers 通过
- **WHEN** observing fetch 捕获的响应 headers 满足 `expect.headers` 配置
- **THEN** LLM checker SHALL 判定 headers 断言通过
- **THEN** LLM checker SHALL 通过共享 header expectation 包装函数判定 headers 断言通过
#### Scenario: output ContentRules 通过
- **WHEN** LLM 输出文本满足 `expect.output` 中配置的全部 ContentRules
#### Scenario: output ContentExpectations 通过
- **WHEN** LLM 输出文本满足 `expect.output` 中配置的全部 ContentExpectations
- **THEN** LLM checker SHALL 判定 output 阶段通过
#### Scenario: finishReason ValueMatcher 通过
@@ -175,18 +175,22 @@ LLM checker SHALL 使用共享 `ContentRules` 校验 `expect.output`。每个 ou
#### Scenario: output JSONPath 存在性默认语义
- **WHEN** `outputText` 是 JSON 字符串且 target 配置 `expect.output: [{json: {path: "$.status"}}]`
- **THEN** LLM checker SHALL 将该规则按 `exists: true` 语义执行
- **THEN** resolve 阶段 SHALL 将该 expectation 的 matcher 物化为 `{exists: true}`,运行期按存在性语义执行
#### Scenario: output 规则按顺序快速失败
- **WHEN** `expect.output` 包含多个规则且第一条规则失败
- **THEN** LLM checker SHALL 返回第一条失败规则的 mismatch failure不继续校验后续 output 规则
- **WHEN** `expect.output` 包含多个 expectation 且第一条 expectation 失败
- **THEN** LLM checker SHALL 返回第一条失败 expectation 的 mismatch failure不继续校验后续 output expectation
### Requirement: LLM Stream 断言
LLM checker SHALL 仅允许 `mode: stream` 使用 `expect.stream``expect.stream.completed` 未配置时LLM checker SHALL 在 stream observation 路径使用默认 `true` 语义`expect.stream.firstTokenMs` SHALL 使用共享 `ValueMatcher`仅统计第一个非空 `text-delta` 事件耗时,不统计 reasoning、tool call 或 source 事件。
LLM checker SHALL 仅允许 `mode: stream` 使用 `expect.stream`仅当用户配置了 `expect.stream` 且未配置 `expect.stream.completed`resolve 阶段 SHALL 在 Resolved expect 中物化默认 `completed: true`LLM checker MUST NOT 因为 `llm.mode: stream` 自动添加 `stream.completed` 断言`expect.stream.firstTokenMs` SHALL 使用共享 `RawValueExpectation` 输入并在运行期使用 `ValueExpectation`仅统计第一个非空 `text-delta` 事件耗时,不统计 reasoning、tool call 或 source 事件。
#### Scenario: stream completed 默认值
- **WHEN** target 配置 `llm.mode: stream`配置 `expect.stream.completed`
- **THEN** LLM checker SHALL 要求 SDK stream 正常完成
- **WHEN** target 配置 `llm.mode: stream` 且配置 `expect.stream: {}`
- **THEN** resolve 阶段 SHALL 在 Resolved expect 中物化 `stream.completed: true`要求 SDK stream 正常完成
#### Scenario: 未配置 expect.stream 不添加 completed
- **WHEN** target 配置 `llm.mode: stream` 但未配置 `expect.stream`
- **THEN** resolve 阶段 SHALL NOT 自动添加 `stream.completed` 断言
#### Scenario: stream error
- **WHEN** `fullStream` 产生 error part