>(undefined);
@@ -12,10 +19,11 @@ export function HeadingLink({ id }: { id: string }) {
(e: React.MouseEvent) => {
e.preventDefault();
clearTimeout(timerRef.current);
+ track('blog_heading_link_clicked', { id, locale });
const url = `${window.location.origin}${window.location.pathname}#${id}`;
navigator.clipboard.writeText(url).then(
() => {
- track('blog_heading_link_copied', { id });
+ track('blog_heading_link_copied', { id, locale });
setState('copied');
timerRef.current = setTimeout(() => {
setState('fading');
@@ -27,7 +35,7 @@ export function HeadingLink({ id }: { id: string }) {
},
);
},
- [id],
+ [id, locale],
);
const visible = state !== 'idle';
@@ -36,13 +44,13 @@ export function HeadingLink({ id }: { id: string }) {
{state === 'idle' ? (
) : (
- Link copied
+ {t.copied}
)}
);
diff --git a/packages/app/src/components/blog/mdx-components.tsx b/packages/app/src/components/blog/mdx-components.tsx
index b684c78e4..e422ed871 100644
--- a/packages/app/src/components/blog/mdx-components.tsx
+++ b/packages/app/src/components/blog/mdx-components.tsx
@@ -105,7 +105,7 @@ export function createMdxComponents(
return (
{props.children}
-
+
);
},
@@ -114,7 +114,7 @@ export function createMdxComponents(
return (
{props.children}
-
+
);
},
@@ -123,7 +123,7 @@ export function createMdxComponents(
return (
{props.children}
-
+
);
},
diff --git a/packages/app/src/components/glossary/glossary-browser.tsx b/packages/app/src/components/glossary/glossary-browser.tsx
index 72f6a2624..dcaede952 100644
--- a/packages/app/src/components/glossary/glossary-browser.tsx
+++ b/packages/app/src/components/glossary/glossary-browser.tsx
@@ -252,6 +252,8 @@ export function GlossaryBrowser({
diff --git a/packages/app/src/lib/api-documentation.test.ts b/packages/app/src/lib/api-documentation.test.ts
new file mode 100644
index 000000000..89c5c50d9
--- /dev/null
+++ b/packages/app/src/lib/api-documentation.test.ts
@@ -0,0 +1,19 @@
+import { describe, expect, it } from 'vitest';
+
+import { getApiDocumentation } from './api-documentation';
+
+describe('Chinese API documentation copy', () => {
+ it('uses the established evaluation and speculative-decoding terminology', () => {
+ const documentation = getApiDocumentation('zh');
+ const availability = documentation.groups
+ .flatMap((group) => group.operations)
+ .find((operation) => operation.id === 'get-availability');
+ const evaluations = documentation.groups
+ .flatMap((group) => group.operations)
+ .find((operation) => operation.id === 'list-evaluations');
+
+ expect(availability?.description).toContain('投机解码方式');
+ expect(evaluations?.summary).toContain('评估');
+ expect(evaluations?.description).toContain('评估结果');
+ });
+});
diff --git a/packages/app/src/lib/api-documentation.ts b/packages/app/src/lib/api-documentation.ts
index 4a22d16c5..470ad3233 100644
--- a/packages/app/src/lib/api-documentation.ts
+++ b/packages/app/src/lib/api-documentation.ts
@@ -506,7 +506,7 @@ export const apiDocumentationGroups: readonly ApiDocumentationGroup[] = [
title: text('Core benchmark data', '核心基准数据'),
description: text(
'Benchmark results, availability, workflow provenance, evaluations, and reliability.',
- '基准结果、可用配置、工作流来源、评测与可靠性数据。',
+ '基准结果、可用配置、工作流来源、评估与可靠性数据。',
),
},
{
@@ -552,7 +552,7 @@ export const apiOperations: readonly ApiOperation[] = [
summary: text('List available benchmark configurations', '列出可用的基准配置'),
description: text(
'Returns model, sequence, precision, hardware, framework, speculative method, benchmark type, and date combinations that have benchmark data.',
- '返回已有基准数据的模型、序列、精度、硬件、框架、推测方法、基准类型和日期组合。',
+ '返回已有基准数据的模型、序列、精度、硬件、框架、投机解码方式、基准类型和日期组合。',
),
audience: 'public',
stability: 'stable',
@@ -829,16 +829,16 @@ export const apiOperations: readonly ApiOperation[] = [
group: 'core',
method: 'GET',
path: '/api/v1/evaluations',
- summary: text('List evaluation aggregates', '列出评测汇总'),
+ summary: text('List evaluation aggregates', '列出评估汇总'),
description: text(
'Returns latest-attempt evaluation results with configuration, task, provenance, and metric values.',
- '返回最新尝试的评测结果,包含配置、任务、来源和指标值。',
+ '返回最新尝试的评估结果,包含配置、任务、来源和指标值。',
),
audience: 'public',
stability: 'stable',
parameters: [],
responses: [
- success('Evaluation result rows.', '评测结果行。', evaluationsSchema, [
+ success('Evaluation result rows.', '评估结果行。', evaluationsSchema, [
{
id: 72,
config_id: 11,
@@ -870,7 +870,7 @@ export const apiOperations: readonly ApiOperation[] = [
errorResponse(
'500',
'The evaluation query failed.',
- '评测查询失败。',
+ '评估查询失败。',
'Internal server error',
),
],
diff --git a/packages/app/src/lib/blog-content.test.ts b/packages/app/src/lib/blog-content.test.ts
index 8b660d5c7..359c7bbd4 100644
--- a/packages/app/src/lib/blog-content.test.ts
+++ b/packages/app/src/lib/blog-content.test.ts
@@ -21,6 +21,10 @@ const enFiles = fs
.readdirSync(CONTENT_DIR)
.filter((f) => f.endsWith('.mdx'))
.toSorted();
+const zhFiles = fs
+ .readdirSync(ZH_DIR)
+ .filter((f) => f.endsWith('.mdx'))
+ .toSorted();
const read = (file: string) => fs.readFileSync(file, 'utf8');
@@ -33,17 +37,28 @@ function frontmatterField(raw: string, field: string): string | null {
function tagList(raw: string): string[] {
const fm = raw.split('---')[1] ?? '';
- const after = fm.split(/^tags:\s*$/mu)[1];
- if (!after) return [];
- const tags: string[] = [];
- for (const line of after.split('\n')) {
- const item = /^\s+-\s+(?.*)$/u.exec(line);
- if (!item) break;
- tags.push(item.groups!.tag.trim());
- }
- return tags;
+ const match = /^tags:\s*\n(?(?:[ \t]+-[^\n]*(?:\n|$))+)/mu.exec(fm);
+ if (!match?.groups) return [];
+ return [...match.groups.items.matchAll(/^\s+-\s+(?.*)$/gmu)].map((item) =>
+ item.groups!.tag.trim(),
+ );
}
+describe('frontmatter tag parsing', () => {
+ it('reads every item from a multiline tags field before the next frontmatter key', () => {
+ const frontmatter = `---
+title: Example
+tags:
+ - benchmark
+ - cann
+publishDate: '2026-08-23'
+---
+`;
+
+ expect(tagList(frontmatter)).toEqual(['benchmark', 'cann']);
+ });
+});
+
/** Local `` and markdown `` references. */
function localImageRefs(raw: string): string[] {
return [
@@ -55,8 +70,18 @@ function localImageRefs(raw: string): string[] {
const countFigures = (raw: string) => (raw.match(/ (raw.match(/^\$\$\s*$/gmu) ?? []).length;
-it('finds English posts to check', () => {
- expect(enFiles.length).toBeGreaterThan(0);
+function inlineCodeSpans(raw: string): string[] {
+ const withoutStructuredContent = raw
+ .replaceAll(/\{`[\s\S]*?`\}<\/JsonLd>/gu, '')
+ .replaceAll(/```[\s\S]*?```/gu, '');
+ return [...withoutStructuredContent.matchAll(/(?[^`\n]+)`(?!`)/gu)].map(
+ (match) => match.groups!.code,
+ );
+}
+
+it('checks all 31 current locale pairs in both directions', () => {
+ expect(enFiles).toHaveLength(31);
+ expect(zhFiles).toEqual(enFiles);
});
describe.each(enFiles)('%s', (file) => {
@@ -102,3 +127,27 @@ describe.each(enFiles)('%s', (file) => {
}
});
});
+
+it('preserves protected inline code in the audited Chinese article passages', () => {
+ const auditedSpans = [
+ {
+ file: 'deepseekv4-16t-day-0-to-day-43-performance.mdx',
+ code: '“mhcFusedHcLaunch: hidden_size=7168 not supported (only 4096)”',
+ },
+ {
+ file: 'gb300-nvl72-vs-gb200-nvl72-dsv4-pro-vllm-fp4.mdx',
+ code: 'conc=3072, 28 GPU prefill, 32 GPU decode EP=16, 6,812 tok/s/GPU at 25.9 tok/s/user',
+ },
+ ] as const;
+
+ for (const { file, code } of auditedSpans) {
+ expect(
+ inlineCodeSpans(read(path.join(CONTENT_DIR, file))),
+ `${file}: English source`,
+ ).toContain(code);
+ expect(
+ inlineCodeSpans(read(path.join(ZH_DIR, file))),
+ `${file}: Chinese translation`,
+ ).toContain(code);
+ }
+});
diff --git a/packages/app/src/lib/glossary-zh.ts b/packages/app/src/lib/glossary-zh.ts
index cbbb159ad..2f689d413 100644
--- a/packages/app/src/lib/glossary-zh.ts
+++ b/packages/app/src/lib/glossary-zh.ts
@@ -36,7 +36,7 @@ const translations: Readonly> = {
definition:
'AI 推理是使用已经训练好的模型处理新输入并生成输出的过程;对大语言模型而言,通常就是处理提示词并生成 token。',
explanation:
- '训练阶段会更新模型权重,推理阶段则使用这些权重。生产系统还需要推理引擎负责调度请求、管理内存、合并批次,并在一个或多个加速器上执行内核。相同模型在不同软硬件栈上的表现可能相差数倍。',
+ '训练阶段会更新模型权重,推理阶段则使用这些权重。生产系统还需要推理引擎负责调度请求、管理内存、合并批次,并在一个或多个加速器上执行内核。周边软硬件栈不同,性能也会随之变化。',
significance:
'推理既是模型问题,也是系统问题。用户体验取决于延迟和交互性,运营成本则取决于吞吐量、利用率、功耗与硬件成本;只优化其中一个维度,往往会牺牲另一个维度。',
benchmarkContext:
@@ -105,11 +105,11 @@ const translations: Readonly> = {
definition:
'闭环基准测试中的客户端会在前一个依赖请求完成后生成新工作,同时遵循工作负载记录的等待时间和分支结构。',
explanation:
- 'Concurrency 表示活跃客户端或会话数量,同时存在的请求数会随时间变化。更快的系统更早完成轮次,因此会在同一个 profiling 窗口内发出更多请求。每条采样会话的推进速度取决于请求完成时间,实际请求组合可能有小幅变化。',
+ '并发量表示活跃客户端或会话的数量,同时在途的请求数会随时间变化。系统越快,完成轮次越早,因此在同一个 profiling 窗口内发出的请求也越多。由于每条采样会话的推进取决于请求完成时间,实际请求组合可能略有变化。',
significance:
'这种负载模型符合交互式 agent 的运行方式,因为下一步动作依赖上一步结果。响应更快时,会话也会更快地产生后续工作,所以吞吐量与延迟相互关联。低并发运行的采样波动通常会比大型请求池更明显。',
benchmarkContext:
- 'AgentX 使用闭环 concurrency。该数值表示同时运行的 agent 客户端数量;request batch 会随着会话推进而变化。解读结果时需要结合吞吐量、首 token 延迟与交互性。',
+ 'AgentX 采用闭环并发。并发量表示同时运行的 agent 客户端数量;随着会话推进,请求批次大小会不断变化。解读结果时,应结合吞吐量、首 token 延迟和交互性。',
},
subagent: {
term: '子智能体',
@@ -221,7 +221,7 @@ const translations: Readonly> = {
'批处理就像让多名乘客坐同一辆巴士:芯片一次处理多个请求,让每趟计算完成更多有效工作。',
definition: '批处理将多个请求的工作组合起来,使加速器能够一起处理它们的 token。',
explanation:
- '大型矩阵运算比大量微小运算更能发挥芯片效率。现代推理引擎采用连续批处理,请求到达和结束时动态加入或退出,无需等待固定批次全部完成。',
+ '大型矩阵运算比大量微小运算更能发挥芯片效率。现代推理引擎采用连续批处理,请求到达和结束时动态加入或退出,无需等待固定批次全部完成。由此形成的批次形状会在预填充和解码过程中不断变化。',
significance:
'批处理是吞吐量与延迟核心权衡的来源。更大的有效批次能摊薄权重读取和内核启动开销,但通常会增加每位用户的 token 间隔。',
benchmarkContext:
@@ -274,7 +274,7 @@ const translations: Readonly> = {
explanation:
'InferenceX 根据每小时总体拥有成本和实测 token 吞吐量计算该指标。它可能按总 token 报告,也可能区分输入和输出 token,因此比较前必须确认分母。',
significance:
- '该指标把系统性能转化为服务经济性,但仍受工作负载、交互性、利用率、缓存命中和成本假设影响;离线低交互点不能直接与实时端点比较。',
+ '该指标把系统性能转化为服务经济性,但仍受工作负载、交互性、利用率、缓存命中和成本假设影响;低吞吐量的离线运行点与高交互性实时端点属于不同的运行区间,不能直接比较。',
benchmarkContext:
'成本曲线使用与吞吐曲线相同的并发扫描。在等交互性下,更低的 $/M 表示以更少建模成本提供相同流式体验。',
measurement: {
@@ -304,7 +304,7 @@ const translations: Readonly> = {
significance:
'TCO 比标价更适合跨系统经济性比较,尤其是网络与电力基础设施不同的机架级产品;但它仍是模型,必须连同假设一起阅读。',
benchmarkContext:
- 'InferenceX 将 SemiAnalysis AI Cloud TCO 输入与实测 tok/s/chip 结合,从而区分每小时系统成本和决定该小时 token 产出的软硬件行为。',
+ 'InferenceX 将 SemiAnalysis AI Cloud 的 TCO 输入与实测 tok/s/chip 结合,从而把系统每小时成本与决定这一小时 token 产出的软件实现及工作负载特征分开考察。',
},
'tokens-per-megawatt': {
term: '每兆瓦 token 吞吐量',
@@ -339,7 +339,7 @@ const translations: Readonly> = {
explanation:
'每个新 token 都依赖此前 token,因此时间维度无法完全并行。模型会反复读取权重与该序列的 KV 缓存,使解码对内存带宽、批处理和通信尤其敏感。',
significance:
- '解码决定流式交互性,也常主导长输出成本。推测解码、MTP、量化和宽专家并行都试图减少每个有效 token 的工作量或耗时。',
+ '解码决定流式交互性,也常主导长输出成本。投机解码、MTP、量化和宽专家并行都试图减少每个有效 token 的工作量或耗时。',
benchmarkContext:
'InferenceX 用 tok/s/user 与总 tok/s/chip 展示不同并发下的解码性能。公平比较必须匹配输出长度、批形状、精度和并行策略。',
},
@@ -382,25 +382,25 @@ const translations: Readonly> = {
'InferenceX 中的 disagg 不是万能开关。应查看预填充/解码 world size、TP/EP 布局、框架、网络域,以及分离前沿真正领先的交互性区间。',
},
'speculative-decoding': {
- term: '推测解码',
+ term: '投机解码',
aliases: ['speculative decoding', '草稿与验证解码'],
plainEnglish:
- '推测解码让一个便宜的助手先起草多个 token,再由完整模型一次性审核,省去部分逐个生成步骤。',
+ '投机解码让一个便宜的助手先起草多个 token,再由完整模型一次性审核,省去部分逐个生成步骤。',
definition:
- '推测解码先以低成本提出多个未来 token,再由目标模型批量验证,从而减少昂贵的串行解码步数。',
+ '投机解码先以低成本提出多个未来 token,再由目标模型批量验证,从而减少昂贵的串行解码步数。',
explanation:
'草稿模型或内置预测头生成候选,目标模型在一次批量验证中评估这些候选并接受有效前缀;严格实现时不会改变目标分布。',
significance:
'加速取决于草稿 token 的接受数量,以及草稿与验证成本。稠密模型和 MoE 的表现可能不同,因为验证多个位置可能激活更多专家权重。',
benchmarkContext:
- '应在真实接受率下比较投机解码方案并验证模型质量。定长场景仍把投机解码作为曲线标识的一部分,因此开启和关闭 MTP 的方案会分开绘制;agentic 曲线则把它当作数据点级元数据并合并这些点,在 tooltip 中标明具体方式,因为 AgentX 按模型、芯片 SKU 和引擎给出可获得的最佳曲线。由于 AgentX 回放的内容是合成的,speculator 接受的 draft token 数会失真,因此运行时会套用一套按模型、speculator、draft 长度和思考模式在外部 agentic 编码数据集上采集的接受长度。',
+ '应在真实接受率下比较投机解码方案并验证模型质量。定长场景仍把投机解码作为曲线标识的一部分,因此开启和关闭 MTP 的方案会分开绘制;agentic 曲线则把它当作数据点级元数据并合并这些点,在提示框中标明具体方式,因为 AgentX 按模型、芯片 SKU 和引擎给出可获得的最佳曲线。由于 AgentX 回放的内容是合成的,speculator 接受的 draft token 数会失真,因此运行时会套用一套按模型、speculator、draft 长度和思考模式在外部 agentic 编码数据集上采集的接受长度。',
},
'multi-token-prediction': {
term: '多 token 预测',
aliases: ['multi-token prediction', '多 token 预测头'],
plainEnglish: 'MTP 让模型一次猜测多个后续 token 并一起验证,从而减少缓慢的逐 token 步骤。',
definition:
- '多 token 预测(MTP)使用与主模型共同训练的辅助预测头,提出多个未来 token 供推测验证。',
+ '多 token 预测(MTP)使用与主模型共同训练的辅助预测头,提出多个未来 token 供投机验证。',
explanation:
'MTP 不需要独立草稿模型,候选来自目标模型自身表示,因此分布更一致、部署也更简单;但它要求检查点包含兼容 MTP 模块,且推理引擎支持验证路径。',
significance:
@@ -410,12 +410,12 @@ const translations: Readonly> = {
},
eagle: {
term: 'EAGLE',
- aliases: ['EAGLE 推测解码', 'EAGLE-3'],
+ aliases: ['EAGLE 投机解码', 'EAGLE-3'],
plainEnglish: 'EAGLE 是一种为主模型起草多个可能后续 token 的方法,可让答案流式输出得更快。',
definition:
- 'EAGLE 是一组推测解码方法:利用与目标语言模型相关的特征预测草稿序列,再由目标模型验证。',
+ 'EAGLE 是一组投机解码方法:利用与目标语言模型相关的特征预测草稿序列,再由目标模型验证。',
explanation:
- '推理框架通常通过推测步数、草稿 token 数和候选宽度等参数暴露 EAGLE。模型检查点、草稿组件与引擎实现必须匹配。',
+ '推理框架通常通过投机步数、草稿 token 数和候选宽度等参数暴露 EAGLE。模型检查点、草稿组件与引擎实现必须匹配。',
significance:
'EAGLE 能提高每个目标模型步接受的 token 数,但结果依赖工作负载;接受行为、草稿开销、模型架构和批大小共同决定端到端收益。',
benchmarkContext:
@@ -690,11 +690,11 @@ const translations: Readonly> = {
'SGLang 是用于快速服务语言模型的开源软件,提供面向复杂 AI 工作负载的调度和优化功能。',
definition: 'SGLang 是面向高性能 LLM 与多模态推理的开源服务引擎和语言模型编程系统。',
explanation:
- '服务运行时包含连续批处理、前缀感知调度、分布式并行、推测解码,以及面向 NVIDIA/AMD 芯片的多种注意力和 MoE 内核后端。',
+ '服务运行时包含连续批处理、前缀感知调度、分布式并行、投机解码,以及面向 NVIDIA/AMD 芯片的多种注意力和 MoE 内核后端。',
significance:
'SGLang 快速迭代的版本和模型专用内核可在硬件不变时显著改变吞吐量;低并发受调度开销影响,其他区间则由注意力、MoE 与通信内核主导。',
benchmarkContext:
- 'InferenceX 持续重跑固定版本的 SGLang 方案。跨版本曲线会保留改动对完整性能区间的影响。',
+ 'InferenceX 持续重跑版本固定的 SGLang 方案。对比不同版本的曲线,可以看出改动如何影响完整运行区间,并发现单个峰值点掩盖的回归或提升。',
},
'tensorrt-llm': {
term: 'TensorRT-LLM',
@@ -779,7 +779,7 @@ const translations: Readonly> = {
significance:
'张量并行会在每个 rank 上复制完整 KV cache,数据并行注意力则把会话绑定在持有其分片的 rank 上,两者在上下文达到几十万 token 时都难以扩展。上下文并行直接针对这一点,而且收益随输入长度增长,而不是随 batch 大小增长。',
benchmarkContext:
- 'InferenceX 在数据点 tooltip 和并行标签中与 TP、EP、DP 一起展示 DCP 与 PCP 的并行度。各厂商支持程度并不均衡:在 AgentX 1.0 结果发布时,vLLM 支持矩阵中 AMD 的注意力后端仍标为不支持,因此该技术仍构成 CUDA 实际优势的一部分。',
+ 'InferenceX 在数据点提示框和并行标签中与 TP、EP、DP 一起展示 DCP 与 PCP 的并行度。各厂商支持程度并不均衡:在 AgentX 1.0 结果发布时,vLLM 支持矩阵中 AMD 的注意力后端仍标为不支持,因此该技术仍构成 CUDA 实际优势的一部分。',
},
'kv-cache-offload': {
term: 'KV cache offload',
@@ -847,7 +847,7 @@ const translations: Readonly> = {
significance:
'芯片峰值规格无法描述服务性能,同一颗芯片在不同配置下可以相差数倍。把整套组合写清楚,结论才可核查:脱离配置的单个数字既无法复现,也无法与其他厂商公平比较。',
benchmarkContext:
- 'InferenceX 的测试配置主要跟随 vLLM 与 SGLang 官方 cookbook,并使用上游镜像,因此结果反映用户实际能部署的性能,而不是为基准测试特调过的镜像。数据点 tooltip 会展示背后的配置,并给出运行溯源链接。',
+ 'InferenceX 的测试配置主要跟随 vLLM 与 SGLang 官方 cookbook,并使用上游镜像,因此结果反映用户实际能部署的性能,而不是为基准测试特调过的镜像。数据点的提示框会展示背后的配置,并给出运行溯源链接。',
},
'tail-latency': {
term: '尾部延迟',
@@ -991,7 +991,7 @@ const translations: Readonly> = {
significance:
'对最大的那批模型来说,这首先是容量手段,其次才是提速手段。有些前沿模型根本装不进单个节点,流水线并行才让它们可服务;当某项竞争性优化拒绝与任何方案组合时,它甚至是唯一选项。',
benchmarkContext:
- 'InferenceX 在数据点 tooltip 和并行标签中与 TP、EP、DP 一起展示流水线并行度,且仅在大于 1 时显示。可组合性与并行度同样重要:一种会导致投机解码无法启用的阶段切分,代价可能超过它节省的显存。',
+ 'InferenceX 在数据点提示框和并行标签中与 TP、EP、DP 一起展示流水线并行度,且仅在大于 1 时显示。可组合性与并行度同样重要:一种会导致投机解码无法启用的阶段切分,代价可能超过它节省的显存。',
},
'dp-attention': {
term: '数据并行注意力',
@@ -1005,7 +1005,7 @@ const translations: Readonly> = {
significance:
'由于每个 rank 只拥有缓存池的私有一份,请求落在哪里就成了影响性能的关键:长会话一旦被路由到不持有其前缀的 rank,就要全部重算。此时实测命中率会远低于理论上限,而原因与缓存大小毫无关系。',
benchmarkContext:
- 'InferenceX 会在数据点 tooltip 的并行策略部分展示 DP attention。它是否有利取决于模型;当缓存局部性变成路由约束时,不启用它的配置有时反而占据前沿曲线。',
+ 'InferenceX 会在数据点提示框的并行策略部分展示 DP attention。它是否有利取决于模型;当缓存局部性变成路由约束时,不启用它的配置有时反而占据前沿曲线。',
},
int4: {
term: 'INT4',
diff --git a/packages/app/src/lib/glossary.test.ts b/packages/app/src/lib/glossary.test.ts
index e524c5805..4e5903941 100644
--- a/packages/app/src/lib/glossary.test.ts
+++ b/packages/app/src/lib/glossary.test.ts
@@ -123,6 +123,7 @@ describe('Chinese glossary content', () => {
expect(getZhGlossaryEntry('trace-replay')?.term).toBe('轨迹回放');
expect(getZhGlossaryEntry('closed-loop-benchmark')?.term).toBe('闭环基准测试');
expect(getZhGlossaryEntry('subagent')?.term).toBe('子智能体');
+ expect(getZhGlossaryEntry('speculative-decoding')?.term).toBe('投机解码');
expect(getZhGlossaryEntry('multi-token-prediction')?.term).toBe('多 token 预测');
expect(getZhGlossaryEntry('not-a-real-term')).toBeUndefined();
@@ -134,6 +135,45 @@ describe('Chinese glossary content', () => {
});
}
});
+
+ it('preserves the source scope in audited Chinese definitions', () => {
+ expect(getZhGlossaryEntry('ai-inference')?.explanation).not.toContain('相差数倍');
+ expect(getZhGlossaryEntry('cost-per-million-tokens')?.significance).toContain('低吞吐量');
+ expect(getZhGlossaryEntry('total-cost-of-ownership')?.benchmarkContext).toContain(
+ '软件实现及工作负载特征',
+ );
+ expect(getZhGlossaryEntry('total-cost-of-ownership')?.benchmarkContext).not.toContain(
+ '软硬件行为',
+ );
+ expect(getZhGlossaryEntry('sglang')?.benchmarkContext).toContain('回归或提升');
+ });
+
+ it('explains closed-loop concurrency in natural Chinese', () => {
+ const entry = getZhGlossaryEntry('closed-loop-benchmark');
+
+ expect(entry?.explanation).toContain('并发量表示活跃客户端或会话的数量');
+ expect(entry?.benchmarkContext).toContain('AgentX 采用闭环并发');
+ expect(entry?.benchmarkContext).toContain('请求批次大小会不断变化');
+ expect(entry?.explanation).not.toContain('Concurrency');
+ expect(entry?.benchmarkContext).not.toContain('concurrency');
+ expect(entry?.benchmarkContext).not.toContain('request batch');
+ });
+
+ it('uses the established Chinese term for point tooltips in audited entries', () => {
+ const slugs = [
+ 'speculative-decoding',
+ 'context-parallelism',
+ 'recipe',
+ 'pipeline-parallelism',
+ 'dp-attention',
+ ];
+
+ for (const slug of slugs) {
+ const benchmarkContext = getZhGlossaryEntry(slug)?.benchmarkContext;
+ expect(benchmarkContext, slug).toContain('提示框');
+ expect(benchmarkContext, slug).not.toContain('tooltip');
+ }
+ });
});
describe('glossary navigation', () => {
diff --git a/packages/app/src/lib/land-acknowledgement-content.test.ts b/packages/app/src/lib/land-acknowledgement-content.test.ts
new file mode 100644
index 000000000..19f807e7f
--- /dev/null
+++ b/packages/app/src/lib/land-acknowledgement-content.test.ts
@@ -0,0 +1,30 @@
+import { readFileSync } from 'node:fs';
+import { resolve } from 'node:path';
+
+import { describe, expect, it } from 'vitest';
+
+const englishPage = readFileSync(
+ resolve(import.meta.dirname, '../app/land-acknowledgement/page.tsx'),
+ 'utf8',
+);
+const chinesePage = readFileSync(
+ resolve(import.meta.dirname, '../app/zh/land-acknowledgement/page.tsx'),
+ 'utf8',
+);
+
+describe('Land Acknowledgement translation fidelity', () => {
+ it('keeps all three infrastructure relationships operational rather than constructional', () => {
+ const englishOperationalRelationships =
+ englishPage.match(/benchmark infrastructure operates on/gu) ?? [];
+ const chineseAcknowledgements = [
+ ...chinesePage.matchAll(/acknowledgement:\s*\n\s*'(?[^']+)'/gu),
+ ].map((match) => match.groups!.copy);
+
+ expect(englishOperationalRelationships).toHaveLength(3);
+ expect(chineseAcknowledgements).toHaveLength(englishOperationalRelationships.length);
+ for (const acknowledgement of chineseAcknowledgements) {
+ expect(acknowledgement).toContain('基准测试基础设施运行于');
+ expect(acknowledgement).not.toContain('建在');
+ }
+ });
+});
diff --git a/packages/app/timings.json b/packages/app/timings.json
index 8d86f0979..87b70c1f4 100644
--- a/packages/app/timings.json
+++ b/packages/app/timings.json
@@ -84,6 +84,10 @@
"spec": "cypress/e2e/compare-table.cy.ts",
"duration": 1751
},
+ {
+ "spec": "cypress/e2e/content-journeys.cy.ts",
+ "duration": 150000
+ },
{
"spec": "cypress/e2e/csv-export-overlay.cy.ts",
"duration": 1213