Files
deepseek-harness/packages/test-support/session-snapshot/README.zh.md
T

10 KiB
Raw Blame History

description, kind
description kind
面向无密钥 profile 测试的 session-log 快照支持:manifest、身份脱敏、规范化、workspace 检查与协议适配器。 package-library

@deepseek-ai/dsh-session-snapshot

English | 中文

概述

dsh-session-snapshot 提供无密钥已记录会话测试(pnpm run test:snapshot)背后的共享支持:封闭 manifest、类型化身份脱敏、规范化、workspace 比较、fixture 保护,以及 headless、SDK、ACP 与 Web owner 使用的协议适配器。ACP 适配器以真实子进程启动被测 profile,驱动确定性输入脚本,并注册完整的录制、回放与刷新套件。每个场景都提交足够证据来证明模型可见输出与文件系统效果,不依赖 agent 自述。包入口会导入 vitest,因此只能在 vitest 运行中使用。

目录


使用本包

本包把随附 profile 场景变成无密钥快照套件:写一张场景表和一个 fixture 目录,调用一次匹配的适配器,工具包就负责启动或组合 profile、驱动场景、比较规范化输出并守护已提交的 fixture。

编写快照套件

消费方 *.snapshot.ts 就是场景表加一次工厂调用。AgentUnderTest 提供绝对 binScript、可选 libBinScriptconfigPathtsconfigPath 路径,因为子进程 cwd 位于仓库之外:

import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import {
  defineAcpSnapshotSuite,
  type Scenario,
  type SnapshotSuiteOptions,
} from '@deepseek-ai/dsh-session-snapshot'

function snapshotMode(value: string | undefined): SnapshotSuiteOptions['mode'] {
  switch (value) {
    case undefined:
    case '':
    case 'replay': return 'replay'
    case 'record': return 'record'
    case 'refresh': return 'refresh'
    default: throw new Error(`unknown DSH_SNAPSHOT mode: ${value}`)
  }
}

const SCENARIOS: Scenario[] = [
  { name: 'text-turn', hasModelTurn: true, recorded: true, pinsHeader: true },
]

defineAcpSnapshotSuite({
  agent: { // absolute paths, resolved from the suite's own location
    binScript: fileURLToPath(new URL('../../../apps/cli/src/bin.ts', import.meta.url)),
    configPath: fileURLToPath(new URL('../cordis.yml', import.meta.url)),
    profile: 'acp',
    tsconfigPath: fileURLToPath(new URL('../../../tsconfig.json', import.meta.url)),
  },
  snapshotsDir: join(dirname(fileURLToPath(import.meta.url)), 'snapshots'),
  scenarios: SCENARIOS, // exactly one entry per header class sets pinsHeader
  mode: snapshotMode(process.env.DSH_SNAPSHOT),
})

每个已记录会话目录携带封闭的 snapshot.yml manifest,以及自身的 session.jsonl 与连续的 session.<n>.jsonl 子会话日志。manifest 指名场景、随附 profile、组合/header 类别、录制来源,以及已完成会话无法重建的 replay、平台、权限、环境、workspace 或输入事实。适配器注册预期输出、会话日志与可选 workspace.expected/ 比较;保护会拒绝遗留目录、缺失文件、绝对路径、畸形 manifest 与平台专用分隔符。

normalizeSessionSnapshot 在规范化路径并清理 request header 后,会保留完整会话 header 与事件 payload,但从已提交 fixture 中省略普通行的 seq/time 和打包行的 seq0/time0 envelope。回放只在内存中合成这些 envelope,而运行时持久化仍写入完整日志。fixture 使用规范打包行;临时仓库迁移器pnpm run migrate:packed-session-fixtures)会改写较旧的布局,由其移除提案负责删除该迁移器。

录制、回放与刷新

pnpm run test:snapshot:record 调用在线 LLM(大语言模型),并重写已录制的模型 fixture;pnpm run test:snapshot:refresh 保持无密钥,运行回放 overlay,并从已提交模型脚本重写 stdout、可比较会话日志预期输出,以及各 pin 自有的提示词与工具 schema 伴随文件。每个组合 owner 把 replay patch 放在 live patch 旁;顶层 snapshots/ 拥有会话驱动场景,其他预期输出留在其包 owner 旁。dsh-llm-replay 提供通过 DSH_SNAPSHOT_* 环境值选择的已记录流。

固定请求 header

每个 pin 默认拥有其生成的 system-prompt.expected.mdtool-schemas.expected.json 伴随文件;当完整的对应序列相同时,systemPromptSourcetoolSchemasSource 指定另一个 pin 作为来源,因此每个不同版本只提交一次。该 pin 的 session.jsonl 存储 "system":"{{system}}","tools":"{{tools}}",同时保留配置、原因与任何模型可见前缀。自身作用域组合出不同请求的子会话按 fixture 索引以 pinsChildToolSchemaspinsChildSystemPrompts 单独声明。运行中改变请求 header 的场景声明 expectedHeaderChanges

平台与组合变体

需要非 Windows 主机的场景声明 posixOnly,在 Windows 上跳过运行测试,但 fixture 保护仍在所有平台覆盖其已提交文件;组合需要可用 pwsh 的场景声明 pwshOnly。当临时目录授权自身待测时,workspaceParent 将生成子级 cwd 移出平台临时区域;场景签入的 workspace/ 会先复制到该子级,随后 prepareWorkspace 在 agent 启动前针对生成 cwd 运行。默认生成的 workspace 在会话 fixture 中存储为 {{cwd}},使平台临时根目录与随机 basename 不影响录制。

可能出什么问题

  • fixture 保护拒绝已提交文件——遗留场景目录、缺失文件、一个 header 类别包含多个 pin、重复的伴随文件内容、未擦除的 JSONL header 与格式错误的 pin header 都会在比较运行前使套件失败。
  • 会话收集需要原始 JSONL mode——快照配置使用 JSONL 后端的 compression: 'none';压缩 JSONL 与 SQLite 组合没有快照收集路径。
  • 构建 mode 需要当前产物——选择 DSH_EXAMPLE_MODE=lib 前先运行 pnpm run build;源 mode 仍是零构建路径。

理解实现

实现细节——点击展开

本节解释工具包的设计;可观察行为已在使用本包中完整说明。

设计

共享核心拥有 manifest、workspace 设置/比较、类型化身份映射、规范化器与 fixture 不变式。ACP 适配器增加四个可组合层:启动器、场景 harness、规范化器与套件工厂。launchAcpTestAgent 在 tsx 下启动源码 profile,或在普通 Node 下启动已构建 lib profile,通过原始字节 stdout tee 连接 SDK 客户端,收集会话更新与 stderr,默认拒绝未处理的权限请求,并负责关闭。runScenario 驱动 ACP JSON-RPC stdio,并收集每个持久化原始 JSONL 会话日志。纯规范化器把 cwd 路径与类型化身份变为稳定 token,将时间归零、展开物理来源区间,并擦除请求 header bulk。defineAcpSnapshotSuite 注册比较、fixture 回写与实时一致性保护。

源码地图

文件 职责
src/launcher.ts 子进程/客户端启动器与关闭所有权
src/harness.ts 脚本化场景驱动与会话日志收集
src/manifest.ts 封闭 snapshot.yml schema、收集与归属规则
src/identity.ts 跨父子日志的类型化首次出现身份 token 化
src/normalize.ts 纯规范化器与擦除辅助
src/workspace.ts 场景 workspace 设置与完整预期状态比较
src/suite.ts 场景表套件工厂、fixture 保护、录制/刷新回写
src/index.ts 再导出四个层的包入口
src/invariant.ts 不变式伴生插件(无运行时不变式;消费它的测试套件会检验该工具包)

数据流

场景在启动器下运行 agent,通过 harness 向它喂入输入脚本,并捕获 stdout 与持久化日志。规范化器把捕获内容规范化——id 转为首次出现序列、生成 cwd 转为 {{cwd}}、header bulk 转为 {{system}}/{{tools}}——使已录制与本次运行可以结构化比较。随后工厂把规范化 stdout 与重新持久化日志同已提交 fixture 比较,或在录制/刷新模式下回写它们;其保护在任何比较结果被采信之前就拒绝畸形或漂移的 fixture。


进一步探索

当包级约定不够用时阅读以下页面。它们从快照工具包逐步进入模型 fixture 来源、启动机制与要求该层级存在的策略。

  • llm-replay——回放模式消费的无密钥模型 fixture 来源。
  • loader-smoke——启动器所依赖的模式感知子进程启动机制。
  • 测试策略——无密钥快照层、其适用时机与 fixture 归属规则。
  • test-support 组地图——兄弟 harness 与支持包。

模型体验

无。该测试专用支持会记录、规范化并比较 profile 会话,不会改变 agent 组装的模型请求。

KV Cache 影响

无;本包既不组装也不发送提供方请求。

已知限制与延期工作

这些限制说明何时需要对该工具包特别小心。它们是当前包约束,不是任务积压。

  • 会话收集需要原始 JSONL mode——runScenario 收集持久化 .jsonl 日志,因此快照配置使用 JSONL 后端的 compression: 'none';压缩 JSONL 与 SQLite 组合没有快照收集路径。
  • 构建 mode 需要当前产物——选择 DSH_EXAMPLE_MODE=lib 前先运行 pnpm run build;源 mode 仍是零构建路径。
  • ACP 继续覆盖协议行为——刺激来自 ACP 客户端的取消与权限往返留在该适配器;组装式一次性行为与持久控制行为使用 headless 与 SDK 适配器。

开发备注

维护者的工作上下文——点击展开

无。