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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user