oh-my-knowledge 0.28.0 → 0.30.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/README.md +49 -3
- package/README.zh.md +49 -3
- package/dist/src/analysis/failure-clusterer.js +1 -1
- package/dist/src/analysis/failure-clusterer.js.map +1 -1
- package/dist/src/analysis/gap-analyzer.d.ts +8 -1
- package/dist/src/analysis/gap-analyzer.d.ts.map +1 -1
- package/dist/src/analysis/gap-analyzer.js +65 -33
- package/dist/src/analysis/gap-analyzer.js.map +1 -1
- package/dist/src/analysis/report-diagnostics.js +2 -0
- package/dist/src/analysis/report-diagnostics.js.map +1 -1
- package/dist/src/analysis/sample-diagnostics.js +14 -14
- package/dist/src/analysis/sample-diagnostics.js.map +1 -1
- package/dist/src/authoring/evolver.d.ts +7 -1
- package/dist/src/authoring/evolver.d.ts.map +1 -1
- package/dist/src/authoring/evolver.js +8 -5
- package/dist/src/authoring/evolver.js.map +1 -1
- package/dist/src/authoring/generator.d.ts +21 -23
- package/dist/src/authoring/generator.d.ts.map +1 -1
- package/dist/src/authoring/generator.js +621 -40
- package/dist/src/authoring/generator.js.map +1 -1
- package/dist/src/authoring/sample-fixer.d.ts +45 -0
- package/dist/src/authoring/sample-fixer.d.ts.map +1 -0
- package/dist/src/authoring/sample-fixer.js +186 -0
- package/dist/src/authoring/sample-fixer.js.map +1 -0
- package/dist/src/cli/commands/evolve.d.ts.map +1 -1
- package/dist/src/cli/commands/evolve.js +17 -0
- package/dist/src/cli/commands/evolve.js.map +1 -1
- package/dist/src/cli/commands/observe.d.ts.map +1 -1
- package/dist/src/cli/commands/observe.js +170 -0
- package/dist/src/cli/commands/observe.js.map +1 -1
- package/dist/src/cli/commands/registry.d.ts.map +1 -1
- package/dist/src/cli/commands/registry.js +9 -1
- package/dist/src/cli/commands/registry.js.map +1 -1
- package/dist/src/cli/commands/sample.d.ts +13 -0
- package/dist/src/cli/commands/sample.d.ts.map +1 -1
- package/dist/src/cli/commands/sample.js +307 -13
- package/dist/src/cli/commands/sample.js.map +1 -1
- package/dist/src/cli/commands/studio.d.ts.map +1 -1
- package/dist/src/cli/commands/studio.js +8 -0
- package/dist/src/cli/commands/studio.js.map +1 -1
- package/dist/src/cli/i18n-dict.d.ts +1 -1
- package/dist/src/cli/i18n-dict.d.ts.map +1 -1
- package/dist/src/cli/i18n-dict.js +189 -17
- package/dist/src/cli/i18n-dict.js.map +1 -1
- package/dist/src/cli/parse-run-config.d.ts +14 -2
- package/dist/src/cli/parse-run-config.d.ts.map +1 -1
- package/dist/src/cli/parse-run-config.js +62 -10
- package/dist/src/cli/parse-run-config.js.map +1 -1
- package/dist/src/doctor/health/builtin-dimensions.d.ts.map +1 -1
- package/dist/src/doctor/health/builtin-dimensions.js +20 -23
- package/dist/src/doctor/health/builtin-dimensions.js.map +1 -1
- package/dist/src/doctor/health/composer.d.ts.map +1 -1
- package/dist/src/doctor/health/composer.js +7 -3
- package/dist/src/doctor/health/composer.js.map +1 -1
- package/dist/src/doctor/health/dimension-spec.d.ts +1 -0
- package/dist/src/doctor/health/dimension-spec.d.ts.map +1 -1
- package/dist/src/doctor/health/parser.d.ts.map +1 -1
- package/dist/src/doctor/health/parser.js +1 -0
- package/dist/src/doctor/health/parser.js.map +1 -1
- package/dist/src/doctor/health/prompt-builder.js +25 -17
- package/dist/src/doctor/health/prompt-builder.js.map +1 -1
- package/dist/src/doctor/html-renderer.d.ts.map +1 -1
- package/dist/src/doctor/html-renderer.js +76 -66
- package/dist/src/doctor/html-renderer.js.map +1 -1
- package/dist/src/doctor/rules.d.ts.map +1 -1
- package/dist/src/doctor/rules.js +32 -0
- package/dist/src/doctor/rules.js.map +1 -1
- package/dist/src/eval-core/cache.d.ts +16 -2
- package/dist/src/eval-core/cache.d.ts.map +1 -1
- package/dist/src/eval-core/cache.js +86 -15
- package/dist/src/eval-core/cache.js.map +1 -1
- package/dist/src/eval-core/evaluation-execution.d.ts +11 -1
- package/dist/src/eval-core/evaluation-execution.d.ts.map +1 -1
- package/dist/src/eval-core/evaluation-execution.js +78 -6
- package/dist/src/eval-core/evaluation-execution.js.map +1 -1
- package/dist/src/eval-core/evaluation-job.d.ts +2 -1
- package/dist/src/eval-core/evaluation-job.d.ts.map +1 -1
- package/dist/src/eval-core/evaluation-job.js +2 -1
- package/dist/src/eval-core/evaluation-job.js.map +1 -1
- package/dist/src/eval-core/evaluation-reporting.d.ts +7 -0
- package/dist/src/eval-core/evaluation-reporting.d.ts.map +1 -1
- package/dist/src/eval-core/evaluation-reporting.js +19 -2
- package/dist/src/eval-core/evaluation-reporting.js.map +1 -1
- package/dist/src/eval-core/execution-strategy.d.ts +1 -1
- package/dist/src/eval-core/execution-strategy.d.ts.map +1 -1
- package/dist/src/eval-core/execution-strategy.js +12 -1
- package/dist/src/eval-core/execution-strategy.js.map +1 -1
- package/dist/src/eval-core/mock-hook.cjs +203 -0
- package/dist/src/eval-core/mocks-runtime.d.ts +96 -0
- package/dist/src/eval-core/mocks-runtime.d.ts.map +1 -0
- package/dist/src/eval-core/mocks-runtime.js +323 -0
- package/dist/src/eval-core/mocks-runtime.js.map +1 -0
- package/dist/src/eval-core/schema.d.ts.map +1 -1
- package/dist/src/eval-core/schema.js +18 -5
- package/dist/src/eval-core/schema.js.map +1 -1
- package/dist/src/eval-core/task-planner.d.ts +9 -1
- package/dist/src/eval-core/task-planner.d.ts.map +1 -1
- package/dist/src/eval-core/task-planner.js +40 -1
- package/dist/src/eval-core/task-planner.js.map +1 -1
- package/dist/src/eval-workflows/batch-evaluation-workflow.d.ts +5 -1
- package/dist/src/eval-workflows/batch-evaluation-workflow.d.ts.map +1 -1
- package/dist/src/eval-workflows/batch-evaluation-workflow.js +2 -1
- package/dist/src/eval-workflows/batch-evaluation-workflow.js.map +1 -1
- package/dist/src/eval-workflows/evaluation-pipeline.d.ts +22 -1
- package/dist/src/eval-workflows/evaluation-pipeline.d.ts.map +1 -1
- package/dist/src/eval-workflows/evaluation-pipeline.js +39 -12
- package/dist/src/eval-workflows/evaluation-pipeline.js.map +1 -1
- package/dist/src/eval-workflows/evaluation-preparation.d.ts +5 -0
- package/dist/src/eval-workflows/evaluation-preparation.d.ts.map +1 -1
- package/dist/src/eval-workflows/evaluation-preparation.js +3 -1
- package/dist/src/eval-workflows/evaluation-preparation.js.map +1 -1
- package/dist/src/eval-workflows/run-evaluation.d.ts +12 -2
- package/dist/src/eval-workflows/run-evaluation.d.ts.map +1 -1
- package/dist/src/eval-workflows/run-evaluation.js +17 -5
- package/dist/src/eval-workflows/run-evaluation.js.map +1 -1
- package/dist/src/executors/claude-cli.d.ts +1 -1
- package/dist/src/executors/claude-cli.d.ts.map +1 -1
- package/dist/src/executors/claude-cli.js +57 -6
- package/dist/src/executors/claude-cli.js.map +1 -1
- package/dist/src/executors/claude-sdk-trace.d.ts.map +1 -1
- package/dist/src/executors/claude-sdk-trace.js +10 -4
- package/dist/src/executors/claude-sdk-trace.js.map +1 -1
- package/dist/src/executors/claude-sdk.d.ts +1 -1
- package/dist/src/executors/claude-sdk.d.ts.map +1 -1
- package/dist/src/executors/claude-sdk.js +25 -2
- package/dist/src/executors/claude-sdk.js.map +1 -1
- package/dist/src/executors/shared.d.ts +1 -1
- package/dist/src/executors/shared.d.ts.map +1 -1
- package/dist/src/executors/shared.js +6 -1
- package/dist/src/executors/shared.js.map +1 -1
- package/dist/src/grading/assertions.d.ts +5 -0
- package/dist/src/grading/assertions.d.ts.map +1 -1
- package/dist/src/grading/assertions.js +27 -1
- package/dist/src/grading/assertions.js.map +1 -1
- package/dist/src/grading/debias-validate.js +4 -4
- package/dist/src/grading/debias-validate.js.map +1 -1
- package/dist/src/grading/diagnostic.d.ts +40 -0
- package/dist/src/grading/diagnostic.d.ts.map +1 -0
- package/dist/src/grading/diagnostic.js +450 -0
- package/dist/src/grading/diagnostic.js.map +1 -0
- package/dist/src/grading/gold-cli.js +2 -2
- package/dist/src/grading/gold-cli.js.map +1 -1
- package/dist/src/grading/index.d.ts +5 -0
- package/dist/src/grading/index.d.ts.map +1 -1
- package/dist/src/grading/judge.d.ts.map +1 -1
- package/dist/src/grading/judge.js +54 -2
- package/dist/src/grading/judge.js.map +1 -1
- package/dist/src/grading/layered-scores.d.ts.map +1 -1
- package/dist/src/grading/layered-scores.js +1 -0
- package/dist/src/grading/layered-scores.js.map +1 -1
- package/dist/src/inputs/eval-config.js +19 -0
- package/dist/src/inputs/eval-config.js.map +1 -1
- package/dist/src/inputs/load-samples.d.ts +22 -2
- package/dist/src/inputs/load-samples.d.ts.map +1 -1
- package/dist/src/inputs/load-samples.js +193 -22
- package/dist/src/inputs/load-samples.js.map +1 -1
- package/dist/src/inputs/skill-loader.d.ts.map +1 -1
- package/dist/src/inputs/skill-loader.js +11 -3
- package/dist/src/inputs/skill-loader.js.map +1 -1
- package/dist/src/observability/experience.d.ts +260 -0
- package/dist/src/observability/experience.d.ts.map +1 -0
- package/dist/src/observability/experience.js +1233 -0
- package/dist/src/observability/experience.js.map +1 -0
- package/dist/src/observability/inbox-view-model.d.ts +27 -0
- package/dist/src/observability/inbox-view-model.d.ts.map +1 -0
- package/dist/src/observability/inbox-view-model.js +135 -0
- package/dist/src/observability/inbox-view-model.js.map +1 -0
- package/dist/src/observability/inbox.d.ts +125 -0
- package/dist/src/observability/inbox.d.ts.map +1 -0
- package/dist/src/observability/inbox.js +741 -0
- package/dist/src/observability/inbox.js.map +1 -0
- package/dist/src/observability/problem-patterns.d.ts +51 -0
- package/dist/src/observability/problem-patterns.d.ts.map +1 -0
- package/dist/src/observability/problem-patterns.js +205 -0
- package/dist/src/observability/problem-patterns.js.map +1 -0
- package/dist/src/observability/review-state.d.ts +54 -0
- package/dist/src/observability/review-state.d.ts.map +1 -0
- package/dist/src/observability/review-state.js +131 -0
- package/dist/src/observability/review-state.js.map +1 -0
- package/dist/src/observability/skill-chain-advisories.d.ts +25 -0
- package/dist/src/observability/skill-chain-advisories.d.ts.map +1 -0
- package/dist/src/observability/skill-chain-advisories.js +69 -0
- package/dist/src/observability/skill-chain-advisories.js.map +1 -0
- package/dist/src/observability/skill-chain.d.ts +61 -0
- package/dist/src/observability/skill-chain.d.ts.map +1 -0
- package/dist/src/observability/skill-chain.js +233 -0
- package/dist/src/observability/skill-chain.js.map +1 -0
- package/dist/src/observability/text-signals.d.ts +7 -0
- package/dist/src/observability/text-signals.d.ts.map +1 -0
- package/dist/src/observability/text-signals.js +22 -0
- package/dist/src/observability/text-signals.js.map +1 -0
- package/dist/src/observability/trace-adapter.d.ts +12 -64
- package/dist/src/observability/trace-adapter.d.ts.map +1 -1
- package/dist/src/observability/trace-adapter.js +9 -355
- package/dist/src/observability/trace-adapter.js.map +1 -1
- package/dist/src/observability/trace-attribution.d.ts +56 -0
- package/dist/src/observability/trace-attribution.d.ts.map +1 -0
- package/dist/src/observability/trace-attribution.js +200 -0
- package/dist/src/observability/trace-attribution.js.map +1 -0
- package/dist/src/observability/trace-segmenter.d.ts +52 -0
- package/dist/src/observability/trace-segmenter.d.ts.map +1 -0
- package/dist/src/observability/trace-segmenter.js +320 -0
- package/dist/src/observability/trace-segmenter.js.map +1 -0
- package/dist/src/observability/trace-source.d.ts +99 -0
- package/dist/src/observability/trace-source.d.ts.map +1 -0
- package/dist/src/observability/trace-source.js +492 -0
- package/dist/src/observability/trace-source.js.map +1 -0
- package/dist/src/renderer/html-renderer.d.ts.map +1 -1
- package/dist/src/renderer/html-renderer.js +324 -218
- package/dist/src/renderer/html-renderer.js.map +1 -1
- package/dist/src/renderer/layout.d.ts.map +1 -1
- package/dist/src/renderer/layout.js +155 -71
- package/dist/src/renderer/layout.js.map +1 -1
- package/dist/src/renderer/observation-inbox-renderer.d.ts +4 -0
- package/dist/src/renderer/observation-inbox-renderer.d.ts.map +1 -0
- package/dist/src/renderer/observation-inbox-renderer.js +5376 -0
- package/dist/src/renderer/observation-inbox-renderer.js.map +1 -0
- package/dist/src/renderer/skill-detail-renderer.d.ts +15 -0
- package/dist/src/renderer/skill-detail-renderer.d.ts.map +1 -0
- package/dist/src/renderer/skill-detail-renderer.js +1234 -0
- package/dist/src/renderer/skill-detail-renderer.js.map +1 -0
- package/dist/src/renderer/skill-list-renderer.d.ts +4 -0
- package/dist/src/renderer/skill-list-renderer.d.ts.map +1 -0
- package/dist/src/renderer/skill-list-renderer.js +327 -0
- package/dist/src/renderer/skill-list-renderer.js.map +1 -0
- package/dist/src/renderer/summary.d.ts +17 -0
- package/dist/src/renderer/summary.d.ts.map +1 -1
- package/dist/src/renderer/summary.js +346 -74
- package/dist/src/renderer/summary.js.map +1 -1
- package/dist/src/renderer/table.d.ts.map +1 -1
- package/dist/src/renderer/table.js +213 -167
- package/dist/src/renderer/table.js.map +1 -1
- package/dist/src/renderer/test-view.d.ts +66 -0
- package/dist/src/renderer/test-view.d.ts.map +1 -0
- package/dist/src/renderer/test-view.js +905 -0
- package/dist/src/renderer/test-view.js.map +1 -0
- package/dist/src/renderer/trends.d.ts.map +1 -1
- package/dist/src/renderer/trends.js +3 -1
- package/dist/src/renderer/trends.js.map +1 -1
- package/dist/src/server/report-server.d.ts +6 -1
- package/dist/src/server/report-server.d.ts.map +1 -1
- package/dist/src/server/report-server.js +182 -6
- package/dist/src/server/report-server.js.map +1 -1
- package/dist/src/server/report-store.d.ts.map +1 -1
- package/dist/src/server/report-store.js +33 -1
- package/dist/src/server/report-store.js.map +1 -1
- package/dist/src/server/skill-index.d.ts +77 -0
- package/dist/src/server/skill-index.d.ts.map +1 -0
- package/dist/src/server/skill-index.js +331 -0
- package/dist/src/server/skill-index.js.map +1 -0
- package/dist/src/server/skill-insights.d.ts +92 -0
- package/dist/src/server/skill-insights.d.ts.map +1 -0
- package/dist/src/server/skill-insights.js +676 -0
- package/dist/src/server/skill-insights.js.map +1 -0
- package/dist/src/shared/hard-rules.d.ts +40 -0
- package/dist/src/shared/hard-rules.d.ts.map +1 -0
- package/dist/src/shared/hard-rules.js +200 -0
- package/dist/src/shared/hard-rules.js.map +1 -0
- package/dist/src/shared/time.d.ts +2 -0
- package/dist/src/shared/time.d.ts.map +1 -0
- package/dist/src/shared/time.js +10 -0
- package/dist/src/shared/time.js.map +1 -0
- package/dist/src/shared/tool-search.d.ts +9 -0
- package/dist/src/shared/tool-search.d.ts.map +1 -0
- package/dist/src/shared/tool-search.js +104 -0
- package/dist/src/shared/tool-search.js.map +1 -0
- package/dist/src/types/eval.d.ts +83 -0
- package/dist/src/types/eval.d.ts.map +1 -1
- package/dist/src/types/executor.d.ts +47 -0
- package/dist/src/types/executor.d.ts.map +1 -1
- package/dist/src/types/judge.d.ts +76 -0
- package/dist/src/types/judge.d.ts.map +1 -1
- package/dist/src/types/judge.js +3 -1
- package/dist/src/types/judge.js.map +1 -1
- package/dist/src/types/report.d.ts +63 -3
- package/dist/src/types/report.d.ts.map +1 -1
- package/dist/src/util/safe-slice.d.ts +23 -0
- package/dist/src/util/safe-slice.d.ts.map +1 -0
- package/dist/src/util/safe-slice.js +33 -0
- package/dist/src/util/safe-slice.js.map +1 -0
- package/package.json +5 -4
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* HTML report renderer — orchestrates sub-modules.
|
|
3
3
|
*/
|
|
4
4
|
import { e, fmtCost, fmtDuration, fmtKnownCost, COLORS, DEFAULT_LANG, t, layout } from './layout.js';
|
|
5
|
-
import { renderAgentOverview, renderAnalysis,
|
|
5
|
+
import { renderAgentOverview, renderAnalysis, renderKnowledgeInteractionSection, renderMethodologyAudit, renderScoringModal, renderSummaryCards, renderVarianceComparisons, renderVerdictPill, levelLabel, levelTooltip, } from './summary.js';
|
|
6
6
|
import { renderSampleTable } from './table.js';
|
|
7
|
+
import { renderTestView, TEST_VIEW_CSS, TEST_VIEW_JS } from './test-view.js';
|
|
7
8
|
import { renderTrendsBody } from './trends.js';
|
|
8
9
|
import { computeVerdict } from '../eval-core/verdict.js';
|
|
9
10
|
// v0.21 B.4 — 列表页 status pill 用的 dot. PROGRESS/REGRESS 实心(强信号),
|
|
@@ -31,36 +32,6 @@ function improvementOf(baselineScore, skillScore) {
|
|
|
31
32
|
const delta = ((skillScore - baselineScore) / baselineScore * 100).toFixed(0);
|
|
32
33
|
return skillScore >= baselineScore ? `+${delta}%` : `${delta}%`;
|
|
33
34
|
}
|
|
34
|
-
function renderRuntimeTag(runtime, label, lang) {
|
|
35
|
-
if (!runtime)
|
|
36
|
-
return '';
|
|
37
|
-
const versions = [];
|
|
38
|
-
if (runtime.binary?.version)
|
|
39
|
-
versions.push(`${lang === 'zh' ? '二进制' : 'binary'} ${runtime.binary.version}`);
|
|
40
|
-
if (runtime.sdk?.version)
|
|
41
|
-
versions.push(`sdk ${runtime.sdk.version}`);
|
|
42
|
-
const versionText = versions.length > 0 ? ` · ${versions.map(e).join(' · ')}` : '';
|
|
43
|
-
const title = lang === 'zh'
|
|
44
|
-
? [
|
|
45
|
-
`executor=${runtime.executor}`,
|
|
46
|
-
`model=${runtime.model}`,
|
|
47
|
-
`kind=${runtime.kind}`,
|
|
48
|
-
`system=${runtime.capabilities.systemPrompt}`,
|
|
49
|
-
`cost=${runtime.capabilities.costUSD}`,
|
|
50
|
-
`trace=${runtime.capabilities.trace}`,
|
|
51
|
-
`skillIsolation=${runtime.capabilities.skillIsolation}`,
|
|
52
|
-
].join('; ')
|
|
53
|
-
: [
|
|
54
|
-
`executor=${runtime.executor}`,
|
|
55
|
-
`model=${runtime.model}`,
|
|
56
|
-
`kind=${runtime.kind}`,
|
|
57
|
-
`system=${runtime.capabilities.systemPrompt}`,
|
|
58
|
-
`cost=${runtime.capabilities.costUSD}`,
|
|
59
|
-
`trace=${runtime.capabilities.trace}`,
|
|
60
|
-
`skillIsolation=${runtime.capabilities.skillIsolation}`,
|
|
61
|
-
].join('; ');
|
|
62
|
-
return `<span class="meta-tag" title="${e(title)}">${e(label)}: <code>${e(runtime.fingerprint)}</code>${versionText}</span>`;
|
|
63
|
-
}
|
|
64
35
|
function costCompletenessTooltip(lang) {
|
|
65
36
|
return lang === 'zh'
|
|
66
37
|
? '部分 executor 或评委不回传 USD 成本。这里显示的是已上报成本下界,真实花费可能更高。'
|
|
@@ -84,33 +55,85 @@ function renderDebiasModeTag(modes, lang) {
|
|
|
84
55
|
function pluralizeEn(count, singular, plural = `${singular}s`) {
|
|
85
56
|
return `${count} ${count === 1 ? singular : plural}`;
|
|
86
57
|
}
|
|
87
|
-
function
|
|
88
|
-
|
|
89
|
-
return '';
|
|
90
|
-
const entries = meta.judgeModels ?? [];
|
|
91
|
-
if (entries.length === 0)
|
|
92
|
-
return '';
|
|
93
|
-
if (entries.length === 1) {
|
|
94
|
-
return renderRuntimeTag(entries[0].runtime, lang === 'zh' ? '评委指纹' : 'Judge runtime', lang);
|
|
95
|
-
}
|
|
96
|
-
return entries
|
|
97
|
-
.slice()
|
|
98
|
-
.sort((a, b) => `${a.executor}:${a.model}`.localeCompare(`${b.executor}:${b.model}`))
|
|
99
|
-
.map((entry) => renderRuntimeTag(entry.runtime, lang === 'zh' ? `评委指纹 ${entry.executor}:${entry.model}` : `Judge runtime ${entry.executor}:${entry.model}`, lang))
|
|
100
|
-
.join('');
|
|
101
|
-
}
|
|
102
|
-
function renderExecutorRuntimeTags(meta, lang) {
|
|
58
|
+
function gatherRuntimeScopes(meta) {
|
|
59
|
+
const scopes = [];
|
|
103
60
|
if (meta.executorRuntimes && Object.keys(meta.executorRuntimes).length > 0) {
|
|
104
|
-
|
|
61
|
+
Object.entries(meta.executorRuntimes)
|
|
105
62
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
106
|
-
.
|
|
107
|
-
|
|
63
|
+
.forEach(([key, runtime]) => {
|
|
64
|
+
if (runtime)
|
|
65
|
+
scopes.push({ role: 'executor', scope: key, runtime });
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
else if (meta.executorRuntime) {
|
|
69
|
+
scopes.push({ role: 'executor', scope: '', runtime: meta.executorRuntime });
|
|
70
|
+
}
|
|
71
|
+
if (!meta.noJudge) {
|
|
72
|
+
(meta.judgeModels ?? [])
|
|
73
|
+
.slice()
|
|
74
|
+
.sort((a, b) => `${a.executor}:${a.model}`.localeCompare(`${b.executor}:${b.model}`))
|
|
75
|
+
.forEach((entry) => {
|
|
76
|
+
if (entry.runtime)
|
|
77
|
+
scopes.push({ role: 'judge', scope: `${entry.executor}:${entry.model}`, runtime: entry.runtime });
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return scopes;
|
|
81
|
+
}
|
|
82
|
+
function runtimeVersionText(runtime, lang) {
|
|
83
|
+
const versions = [];
|
|
84
|
+
if (runtime.binary?.version)
|
|
85
|
+
versions.push(`${lang === 'zh' ? '二进制' : 'binary'} ${runtime.binary.version}`);
|
|
86
|
+
if (runtime.sdk?.version)
|
|
87
|
+
versions.push(`sdk ${runtime.sdk.version}`);
|
|
88
|
+
return versions.length > 0 ? ` · ${versions.map(e).join(' · ')}` : '';
|
|
89
|
+
}
|
|
90
|
+
function runtimeTooltip(runtime) {
|
|
91
|
+
return [
|
|
92
|
+
`executor=${runtime.executor}`,
|
|
93
|
+
`model=${runtime.model}`,
|
|
94
|
+
`kind=${runtime.kind}`,
|
|
95
|
+
`system=${runtime.capabilities.systemPrompt}`,
|
|
96
|
+
`cost=${runtime.capabilities.costUSD}`,
|
|
97
|
+
`trace=${runtime.capabilities.trace}`,
|
|
98
|
+
`skillIsolation=${runtime.capabilities.skillIsolation}`,
|
|
99
|
+
].join('; ');
|
|
100
|
+
}
|
|
101
|
+
// 同 fingerprint + 同 binary/sdk 版本 = 同一执行环境,合并为一条 tag。
|
|
102
|
+
// 旧版给每个 executor / judge 单独一条 pill,3 个角色用同一 binary 时
|
|
103
|
+
// fingerprint 重复 3 遍,扫读成本高。新版按 (fingerprint, versionText)
|
|
104
|
+
// 分组,scope 合并到 tag 内 "适用于 ..." 后缀。
|
|
105
|
+
function renderRuntimeFingerprintTags(meta, lang) {
|
|
106
|
+
const scopes = gatherRuntimeScopes(meta);
|
|
107
|
+
if (scopes.length === 0)
|
|
108
|
+
return '';
|
|
109
|
+
const groups = new Map();
|
|
110
|
+
for (const s of scopes) {
|
|
111
|
+
const versionText = runtimeVersionText(s.runtime, lang);
|
|
112
|
+
const key = `${s.runtime.fingerprint}|${versionText}`;
|
|
113
|
+
let g = groups.get(key);
|
|
114
|
+
if (!g) {
|
|
115
|
+
g = { runtime: s.runtime, versionText, executors: [], judges: [] };
|
|
116
|
+
groups.set(key, g);
|
|
117
|
+
}
|
|
118
|
+
if (s.role === 'executor')
|
|
119
|
+
g.executors.push(s.scope || (lang === 'zh' ? '默认' : 'default'));
|
|
120
|
+
else
|
|
121
|
+
g.judges.push(s.scope);
|
|
108
122
|
}
|
|
109
|
-
|
|
123
|
+
const groupLabel = lang === 'zh' ? '执行环境指纹' : 'Runtime fingerprint';
|
|
124
|
+
return Array.from(groups.values()).map((g) => {
|
|
125
|
+
const scopeBits = [];
|
|
126
|
+
if (g.executors.length > 0)
|
|
127
|
+
scopeBits.push(`${lang === 'zh' ? '执行器' : 'executor'} ${g.executors.join(', ')}`);
|
|
128
|
+
if (g.judges.length > 0)
|
|
129
|
+
scopeBits.push(`${lang === 'zh' ? '评委' : 'judge'} ${g.judges.join(', ')}`);
|
|
130
|
+
const scopeText = scopeBits.length > 0 ? ` · ${lang === 'zh' ? '适用' : 'used by'} ${e(scopeBits.join('; '))}` : '';
|
|
131
|
+
return `<span class="meta-tag" title="${e(runtimeTooltip(g.runtime))}">${e(groupLabel)}: <code>${e(g.runtime.fingerprint)}</code>${g.versionText}${scopeText}</span>`;
|
|
132
|
+
}).join('');
|
|
110
133
|
}
|
|
111
134
|
export function renderRunList(runs, lang = DEFAULT_LANG) {
|
|
112
135
|
const langQ = lang === DEFAULT_LANG ? '' : `?lang=${lang}`;
|
|
113
|
-
const skillHealthLink = `<a href="/analyses${langQ}"
|
|
136
|
+
const skillHealthLink = `<a class="page-secondary-link" href="/analyses${langQ}">📊 <span data-i18n="skillHealthTitle">${t('skillHealthTitle', lang)}</span> →</a>`;
|
|
114
137
|
if (!runs || runs.length === 0) {
|
|
115
138
|
return layout(t('title', lang), `
|
|
116
139
|
<main>
|
|
@@ -121,112 +144,85 @@ export function renderRunList(runs, lang = DEFAULT_LANG) {
|
|
|
121
144
|
</main>
|
|
122
145
|
`, lang);
|
|
123
146
|
}
|
|
124
|
-
|
|
147
|
+
// v0.30 — 卡片式列表(取代 7 列 dense table),每条占 4-5 行垂直空间但信息密度更可读。
|
|
148
|
+
// 顶部 verdict pill 主导视觉,id+date 是身份,scores 是成绩,底部 meta 是元数据,
|
|
149
|
+
// delete 默认隐藏在 hover 出现避免误点。批量报告共用同一组件,batch pill 替代 verdict。
|
|
150
|
+
const formatDateFromId = (id, fallbackTs) => {
|
|
151
|
+
const idMatch = id.match(/(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})$/);
|
|
152
|
+
if (idMatch)
|
|
153
|
+
return `${idMatch[2]}/${idMatch[3]} ${idMatch[4]}:${idMatch[5]}`;
|
|
154
|
+
return fallbackTs ? new Date(fallbackTs).toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }) : '';
|
|
155
|
+
};
|
|
156
|
+
const renderScoreBar = (label, score) => {
|
|
157
|
+
if (score == null)
|
|
158
|
+
return `<span class="rc-score-empty">${e(label)}: —</span>`;
|
|
159
|
+
const cls = score >= 4 ? 'pass' : score >= 3 ? 'warn' : 'fail';
|
|
160
|
+
const barW = Math.round((score / 5) * 100);
|
|
161
|
+
return `<div class="rc-score-row"><span class="rc-score-label" title="${e(label)}">${e(label)}</span>` +
|
|
162
|
+
`<div class="rc-score-bar"><div class="rc-score-bar-fill rc-score-bar-fill--${cls}" style="width:${barW}%"></div></div>` +
|
|
163
|
+
`<span class="rc-score-num rc-score-num--${cls}">${score.toFixed(2)}</span></div>`;
|
|
164
|
+
};
|
|
165
|
+
const cards = runs.map((run) => {
|
|
125
166
|
if (run.kind === 'batch-evaluation') {
|
|
126
167
|
const m = run.meta;
|
|
127
|
-
const
|
|
168
|
+
const scores = run.items.length > 0
|
|
128
169
|
? run.items.map((item) => {
|
|
129
170
|
const baselineScore = scoreOf(item.summary.baseline);
|
|
130
171
|
const skillScore = scoreOf(item.summary[item.name]);
|
|
131
|
-
|
|
132
|
-
if (score == null)
|
|
133
|
-
return `<span style="color:var(--text-muted)">${e(item.name)}: -</span>`;
|
|
134
|
-
const color = score >= 4 ? 'var(--green)' : score >= 3 ? 'var(--yellow)' : 'var(--red)';
|
|
135
|
-
const barW = Math.round((score / 5) * 100);
|
|
136
|
-
return `<div style="display:flex;align-items:center;gap:6px;margin:2px 0">` +
|
|
137
|
-
`<span title="${e(item.name)}" style="font-size:11px;color:var(--text-muted);width:56px;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:0">${e(item.name)}</span>` +
|
|
138
|
-
`<div style="width:64px;height:6px;background:var(--bg-surface);border-radius:3px;flex-shrink:0">` +
|
|
139
|
-
`<div style="width:${barW}%;height:100%;background:${color};border-radius:3px"></div></div>` +
|
|
140
|
-
`<span style="font-size:12px;font-weight:600;color:${color};min-width:24px">${score.toFixed(2)}</span></div>`;
|
|
172
|
+
return renderScoreBar(item.name, skillScore ?? baselineScore);
|
|
141
173
|
}).join('')
|
|
142
|
-
:
|
|
143
|
-
const allCostReported = run.items.every((item) => Object.values(item.summary || {}).every((v) => v.execCostReported !== false && v.judgeCostReported !== false));
|
|
144
|
-
const totalCostReported = m.totalCostReported !== false && allCostReported;
|
|
174
|
+
: `<div class="rc-score-empty">${lang === 'zh' ? '无分数' : 'no score'}</div>`;
|
|
145
175
|
const totalDurationMs = run.items.reduce((sum, item) => (sum + Object.values(item.summary || {}).reduce((inner, v) => inner + (v.avgDurationMs || 0) * (v.successCount || 0), 0)), 0);
|
|
146
176
|
const statusPill = `<span class="run-status" title="${lang === 'zh' ? '批量评测' : 'batch evaluation'}"><span class="run-status-dot" aria-hidden="true">◇</span>${lang === 'zh' ? '批量' : 'Batch'}</span>`;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
177
|
+
const date = formatDateFromId(run.id, m.timestamp);
|
|
178
|
+
return `<a class="run-card" href="/reports/${e(run.id)}${langQ}">
|
|
179
|
+
<div class="run-card-h">
|
|
180
|
+
${statusPill}
|
|
181
|
+
<span class="run-card-id">${e(run.id)}</span>
|
|
182
|
+
${date ? `<span class="run-card-date">${e(date)}</span>` : ''}
|
|
183
|
+
</div>
|
|
184
|
+
<div class="run-card-meta">${e(m.model || '-')} · ${m.sampleCount || 0} ${lang === 'zh' ? '用例' : 'samples'} · ${run.items.length} skills</div>
|
|
185
|
+
<div class="run-card-scores">${scores}</div>
|
|
186
|
+
<div class="run-card-foot">
|
|
187
|
+
<span class="run-card-stat">${fmtDuration(totalDurationMs)}</span>
|
|
188
|
+
<button class="run-card-delete" onclick="event.preventDefault();event.stopPropagation();deleteRun('${e(run.id)}',this)" data-i18n="deleteBtnText" aria-label="${e(t('deleteBtnText', lang))}">×</button>
|
|
189
|
+
</div>
|
|
190
|
+
</a>`;
|
|
156
191
|
}
|
|
157
192
|
const m = run.meta;
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
return `<span style="color:var(--text-muted)">${e(v)}: -</span>`;
|
|
164
|
-
const color = score >= 4 ? 'var(--green)' : score >= 3 ? 'var(--yellow)' : 'var(--red)';
|
|
165
|
-
const barW = Math.round((score / 5) * 100);
|
|
166
|
-
return `<div style="display:flex;align-items:center;gap:6px;margin:2px 0">` +
|
|
167
|
-
`<span title="${e(v)}" style="font-size:11px;color:var(--text-muted);width:56px;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:0">${e(v)}</span>` +
|
|
168
|
-
`<div style="width:64px;height:6px;background:var(--bg-surface);border-radius:3px;flex-shrink:0">` +
|
|
169
|
-
`<div style="width:${barW}%;height:100%;background:${color};border-radius:3px"></div></div>` +
|
|
170
|
-
`<span style="font-size:12px;font-weight:600;color:${color};min-width:24px">${score}</span></div>`;
|
|
171
|
-
}).join('')
|
|
172
|
-
: '<div style="color:var(--text-faint);font-size:0.6875rem;text-align:center">no score</div>';
|
|
173
|
-
const badges = ''; // TODO: artifact kind 体系完善后,按 kind 显示评测类型标签
|
|
174
|
-
// v0.21 B.4 — 列表页 verdict pill: 一眼分辨 progress / regress / noise.
|
|
175
|
-
// computeVerdict 是同步纯函数(report -> level),per row 跑成本 O(samples).
|
|
176
|
-
// 脏报告可能让 layer-gates 访问 undefined.avgFactScore 抛 NPE。
|
|
177
|
-
// try/catch 兜底,失败的 row 不显示 pill,避免一个坏 report 撤掉整页。
|
|
193
|
+
const summary = run.summary || {};
|
|
194
|
+
const hasScores = Object.values(summary).some((s) => typeof s.avgCompositeScore === 'number' || typeof s.avgLlmScore === 'number');
|
|
195
|
+
const scoresHtml = hasScores
|
|
196
|
+
? Object.entries(summary).map(([v, s]) => renderScoreBar(v, s.avgCompositeScore ?? s.avgLlmScore ?? null)).join('')
|
|
197
|
+
: `<div class="rc-score-empty">${lang === 'zh' ? '无分数' : 'no score'}</div>`;
|
|
178
198
|
let statusPill = '';
|
|
199
|
+
let verdictLvl = '';
|
|
179
200
|
try {
|
|
180
201
|
const verdict = computeVerdict(run);
|
|
181
|
-
|
|
182
|
-
statusPill = `<span class="run-status verdict-${
|
|
202
|
+
verdictLvl = verdict.level;
|
|
203
|
+
statusPill = `<span class="run-status verdict-${verdictLvl}" title="${e(levelTooltip(verdictLvl, lang))}" aria-label="${e(levelLabel(verdictLvl, lang))} — ${e(levelTooltip(verdictLvl, lang))}"><span class="run-status-dot" aria-hidden="true">${levelDot(verdictLvl)}</span>${e(levelLabel(verdictLvl, lang))}</span>`;
|
|
183
204
|
}
|
|
184
205
|
catch { /* skip pill on this row */ }
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
<
|
|
195
|
-
<
|
|
196
|
-
<
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
<td>${fmtDuration(Object.values(run.summary || {}).reduce((s, v) => s + (v.avgDurationMs || 0) * (v.successCount || 0), 0))}</td>
|
|
202
|
-
<td style="white-space:nowrap"><button onclick="deleteRun('${e(run.id)}',this)" class="btn-danger" data-i18n="deleteBtnText">${t('deleteBtnText', lang)}</button></td>
|
|
203
|
-
</tr>`;
|
|
206
|
+
const date = formatDateFromId(run.id, m.timestamp);
|
|
207
|
+
const variants = (m.variants || []).join(' · ');
|
|
208
|
+
const dur = Object.values(summary).reduce((s, v) => s + (v.avgDurationMs || 0) * (v.successCount || 0), 0);
|
|
209
|
+
return `<a class="run-card${verdictLvl ? ` verdict-${verdictLvl}` : ''}" href="/reports/${e(run.id)}${langQ}">
|
|
210
|
+
<div class="run-card-h">
|
|
211
|
+
${statusPill}
|
|
212
|
+
<span class="run-card-id">${e(run.id)}</span>
|
|
213
|
+
${date ? `<span class="run-card-date">${e(date)}</span>` : ''}
|
|
214
|
+
</div>
|
|
215
|
+
<div class="run-card-meta">${e(m.model || '-')} · ${m.sampleCount || 0} ${lang === 'zh' ? '用例' : 'samples'}${variants ? ` · ${e(variants)}` : ''}</div>
|
|
216
|
+
<div class="run-card-scores">${scoresHtml}</div>
|
|
217
|
+
<div class="run-card-foot">
|
|
218
|
+
<span class="run-card-stat">${fmtDuration(dur)}</span>
|
|
219
|
+
<button class="run-card-delete" onclick="event.preventDefault();event.stopPropagation();deleteRun('${e(run.id)}',this)" data-i18n="deleteBtnText" aria-label="${e(t('deleteBtnText', lang))}">×</button>
|
|
220
|
+
</div>
|
|
221
|
+
</a>`;
|
|
204
222
|
}).join('');
|
|
205
223
|
const runCount = lang === 'zh' ? `${runs.length} 次评测` : `${runs.length} runs`;
|
|
206
|
-
//
|
|
207
|
-
// 排除 not reported(而不是把整体压成「—」)是因为列表常含数十个 run 仅 1-2 个是 codex,
|
|
208
|
-
// 把全部 sum 抹成「—」会丢掉绝大多数有效成本信息。
|
|
224
|
+
// v0.30 — 列表页移除累计金额展示(用户反馈无须暴露);保留 evaluationRuns 用于 trend links
|
|
209
225
|
const evaluationRuns = runs.filter(isEvaluationReport);
|
|
210
|
-
const reportableRuns = evaluationRuns.filter((r) => Object.values(r.summary || {}).every((v) => v.execCostReported !== false));
|
|
211
|
-
const unmeasuredRunsCount = evaluationRuns.length - reportableRuns.length;
|
|
212
|
-
const totalCost = reportableRuns.reduce((s, r) => s + Object.values(r.summary || {}).reduce((sv, v) => sv + (v.totalExecCostUSD || 0), 0), 0);
|
|
213
|
-
// partial:有 reported 就显示数字 + tooltip;全部 not reported 才显示「—」;
|
|
214
|
-
// 全 reported(unmeasuredRunsCount=0)走老格式不包 span,保持 HTML snapshot 向后兼容。
|
|
215
|
-
// X/N 限定信息只放 tooltip,不挂在显眼位置 — 累计行视觉应该是单纯的数字。
|
|
216
|
-
const allUnmeasured = reportableRuns.length === 0 && runs.length > 0;
|
|
217
|
-
const baseLabel = lang === 'zh' ? '累计' : 'Total';
|
|
218
|
-
const costNumber = fmtCost(totalCost, !allUnmeasured);
|
|
219
|
-
let costLabel;
|
|
220
|
-
if (unmeasuredRunsCount === 0) {
|
|
221
|
-
// 全 reported → 跟旧版字节级一致,不破 snapshot
|
|
222
|
-
costLabel = `${baseLabel} ${costNumber}`;
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
const tip = lang === 'zh'
|
|
226
|
-
? `${reportableRuns.length}/${evaluationRuns.length} 次单次报告了 USD 成本;${unmeasuredRunsCount} 次 executor 不报(如 codex CLI),batch index 不计入累计以避免重复`
|
|
227
|
-
: `${reportableRuns.length}/${evaluationRuns.length} evaluation reports reported USD cost; ${unmeasuredRunsCount} excluded (executor doesn't report, e.g. codex CLI); batch indexes are excluded to avoid double counting`;
|
|
228
|
-
costLabel = `<span title="${e(tip)}">${baseLabel} ${costNumber}</span>`;
|
|
229
|
-
}
|
|
230
226
|
// Collect variants with ≥2 reports for trend links
|
|
231
227
|
const variantCounts = {};
|
|
232
228
|
for (const run of evaluationRuns) {
|
|
@@ -244,36 +240,62 @@ export function renderRunList(runs, lang = DEFAULT_LANG) {
|
|
|
244
240
|
return layout(t('title', lang), `
|
|
245
241
|
<main>
|
|
246
242
|
<h1>${t('title', lang)}</h1>
|
|
247
|
-
<p class="subtitle" data-i18n="subtitle">${t('subtitle', lang)} · ${runCount}
|
|
243
|
+
<p class="subtitle" data-i18n="subtitle">${t('subtitle', lang)} · ${runCount}</p>
|
|
248
244
|
${trendsSection}
|
|
249
245
|
<div style="margin:12px 0">${skillHealthLink}</div>
|
|
250
246
|
<div style="margin:12px 0;display:flex;gap:8px;align-items:center">
|
|
251
247
|
<input id="filter-input" type="text" placeholder="${lang === 'zh' ? '搜索报告名称、变体...' : 'Filter by name, variant...'}" style="flex:1;max-width:320px;padding:6px 10px;font-size:13px;background:var(--bg-surface);color:var(--text-primary);border:1px solid var(--border);border-radius:var(--radius);outline:none" oninput="filterTable(this.value)">
|
|
252
248
|
<span id="filter-count" style="font-size:11px;color:var(--text-muted)"></span>
|
|
253
249
|
</div>
|
|
254
|
-
|
|
255
|
-
<
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
250
|
+
${renderScoringModal('guide-scoring-list', lang)}
|
|
251
|
+
<div id="run-list" class="run-list">${cards}</div>
|
|
252
|
+
<style>
|
|
253
|
+
/* 列表页 — 卡片式布局,顶部 verdict pill 主导,底部 hover 露出 delete */
|
|
254
|
+
.page-secondary-link { color:var(--text-secondary);font-size:13px;text-decoration:none;border:1px solid var(--border);padding:6px 14px;border-radius:var(--radius);display:inline-block;background:var(--bg-surface);transition:border-color .15s,color .15s }
|
|
255
|
+
.page-secondary-link:hover { color:var(--text-primary);border-color:var(--border-hover);text-decoration:none }
|
|
256
|
+
.run-list { display:flex;flex-direction:column;gap:12px;margin:16px 0 }
|
|
257
|
+
/* verdict 用左边粗带 + 浅色填充背景双重视觉提示,远看也能区分。
|
|
258
|
+
PROGRESS = sage 绿(进步),REGRESS = 砖红(回归),CAUTIOUS = 琥珀(警示),其他 = 灰(无信号) */
|
|
259
|
+
.run-card { display:block;padding:18px 22px;background:var(--bg-surface);border-radius:var(--radius);box-shadow:var(--shadow-sm);text-decoration:none;color:inherit;transition:box-shadow .15s,transform .05s;border-left:5px solid transparent;position:relative }
|
|
260
|
+
.run-card:hover { box-shadow:var(--shadow-md);text-decoration:none }
|
|
261
|
+
.run-card:hover .run-card-delete { opacity:1 }
|
|
262
|
+
.run-card.verdict-PROGRESS { border-left-color:var(--green);background:linear-gradient(90deg,var(--green-bg) 0%,var(--bg-surface) 80%) }
|
|
263
|
+
.run-card.verdict-REGRESS { border-left-color:var(--red);background:linear-gradient(90deg,var(--red-bg) 0%,var(--bg-surface) 80%) }
|
|
264
|
+
.run-card.verdict-CAUTIOUS { border-left-color:var(--yellow);background:linear-gradient(90deg,var(--yellow-bg) 0%,var(--bg-surface) 80%) }
|
|
265
|
+
.run-card.verdict-NOISE, .run-card.verdict-UNDERPOWERED { border-left-color:var(--text-faint);opacity:0.85 }
|
|
266
|
+
.run-card.verdict-SOLO { border-left-color:var(--accent);background:linear-gradient(90deg,var(--info-bg) 0%,var(--bg-surface) 80%) }
|
|
267
|
+
.run-card-h { display:flex;align-items:center;gap:12px;margin-bottom:6px;flex-wrap:wrap }
|
|
268
|
+
.run-card-id { font-size:15px;font-weight:600;color:var(--text-primary);font-family:"SF Mono",Menlo,Consolas,monospace;letter-spacing:-0.01em }
|
|
269
|
+
.run-card-date { font-size:12px;color:var(--text-muted);font-variant-numeric:tabular-nums;margin-left:auto }
|
|
270
|
+
.run-card-meta { font-size:13px;color:var(--text-secondary);margin:6px 0 12px;line-height:1.6 }
|
|
271
|
+
.run-card-scores { display:flex;flex-direction:column;gap:4px;margin:8px 0 }
|
|
272
|
+
.run-card-foot { display:flex;gap:16px;margin-top:10px;align-items:center;font-size:12px;color:var(--text-muted);font-variant-numeric:tabular-nums }
|
|
273
|
+
.run-card-stat { font-family:"SF Mono",Menlo,monospace }
|
|
274
|
+
.run-card-delete { margin-left:auto;background:transparent;border:none;color:var(--text-muted);font-size:18px;line-height:1;width:28px;height:28px;border-radius:14px;cursor:pointer;opacity:0;transition:opacity .15s,background .15s,color .15s;outline:none;appearance:none }
|
|
275
|
+
.run-card-delete:hover { background:var(--red-bg);color:var(--red) }
|
|
276
|
+
/* 分数行(每个 variant 一行) */
|
|
277
|
+
.rc-score-row { display:flex;align-items:center;gap:10px;font-size:13px;line-height:1.6 }
|
|
278
|
+
.rc-score-label { font-size:12px;color:var(--text-secondary);min-width:80px;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:0 }
|
|
279
|
+
.rc-score-bar { width:120px;height:6px;background:var(--bg-soft);border-radius:3px;flex-shrink:0;overflow:hidden }
|
|
280
|
+
.rc-score-bar-fill { height:100%;border-radius:3px;transition:width .2s }
|
|
281
|
+
.rc-score-bar-fill--pass { background:var(--green) }
|
|
282
|
+
.rc-score-bar-fill--warn { background:var(--yellow) }
|
|
283
|
+
.rc-score-bar-fill--fail { background:var(--red) }
|
|
284
|
+
.rc-score-num { font-size:13px;font-weight:600;font-variant-numeric:tabular-nums;font-family:"SF Mono",Menlo,monospace;min-width:36px }
|
|
285
|
+
.rc-score-num--pass { color:var(--green) }
|
|
286
|
+
.rc-score-num--warn { color:var(--yellow) }
|
|
287
|
+
.rc-score-num--fail { color:var(--red) }
|
|
288
|
+
.rc-score-empty { color:var(--text-faint);font-size:12px;font-style:italic }
|
|
289
|
+
</style>
|
|
268
290
|
<script>
|
|
269
291
|
function filterTable(q) {
|
|
270
|
-
var
|
|
292
|
+
var cards = document.querySelectorAll('#run-list .run-card');
|
|
271
293
|
var lower = q.toLowerCase();
|
|
272
294
|
var shown = 0;
|
|
273
|
-
|
|
274
|
-
var text =
|
|
295
|
+
cards.forEach(function(card) {
|
|
296
|
+
var text = card.textContent.toLowerCase();
|
|
275
297
|
var match = !q || text.indexOf(lower) !== -1;
|
|
276
|
-
|
|
298
|
+
card.style.display = match ? '' : 'none';
|
|
277
299
|
if (match) shown++;
|
|
278
300
|
});
|
|
279
301
|
var countEl = document.getElementById('filter-count');
|
|
@@ -285,7 +307,7 @@ export function renderRunList(runs, lang = DEFAULT_LANG) {
|
|
|
285
307
|
fetch('/api/reports/' + encodeURIComponent(id), { method: 'DELETE' })
|
|
286
308
|
.then(function(r) { return r.json(); })
|
|
287
309
|
.then(function(d) {
|
|
288
|
-
if (d.ok) { btn.closest('
|
|
310
|
+
if (d.ok) { btn.closest('.run-card').remove(); }
|
|
289
311
|
else { alert(I18N[lang].deleteFail + ': ' + (d.error || 'unknown')); }
|
|
290
312
|
})
|
|
291
313
|
.catch(function(err) { alert(I18N[lang].deleteFail + ': ' + err.message); });
|
|
@@ -309,9 +331,7 @@ export function renderRunDetail(report, lang = DEFAULT_LANG) {
|
|
|
309
331
|
const summary = report.summary || {};
|
|
310
332
|
const results = report.results || [];
|
|
311
333
|
const cards = renderSummaryCards(variants, summary, lang, report.variance);
|
|
312
|
-
const
|
|
313
|
-
const humanAgreement = renderHumanAgreement(report.meta.humanAgreement, lang);
|
|
314
|
-
const saturationCurve = renderSaturationCurve(report.variance?.saturation, variants, lang);
|
|
334
|
+
const methodologyAudit = renderMethodologyAudit(report, variants, summary, lang);
|
|
315
335
|
const verdictPill = renderVerdictPill(report, lang);
|
|
316
336
|
const sampleTable = renderSampleTable(variants, results, lang);
|
|
317
337
|
const totalExecCost = Object.values(summary).reduce((s, v) => s + (v.totalExecCostUSD || 0), 0);
|
|
@@ -336,52 +356,41 @@ export function renderRunDetail(report, lang = DEFAULT_LANG) {
|
|
|
336
356
|
zh: { baseline: '基线', skill: 'Skill', prompt: 'Prompt', agent: 'Agent', workflow: 'Workflow' },
|
|
337
357
|
en: { baseline: 'Baseline', skill: 'Skill', prompt: 'Prompt', agent: 'Agent', workflow: 'Workflow' },
|
|
338
358
|
};
|
|
359
|
+
// v0.30 — 实验配置弱化:单行 inline 摘要,跟 Agent 执行概览并排放(context-strip)。
|
|
360
|
+
// 每变体一行:左 3px 颜色条 + variant 名 + type/kind/source/strategy/cwd 用 · 分隔。
|
|
361
|
+
// 完整 key 名通过 hover tooltip 提供。视觉权重远低于综合分 hero。
|
|
339
362
|
const variantConfigRows = (m.variantConfigs || []).map((config, i) => {
|
|
340
363
|
const expTypeRaw = config.experimentType || '-';
|
|
341
364
|
const expType = (typeLabels[lang] || typeLabels.en)[String(expTypeRaw)] || expTypeRaw;
|
|
342
365
|
const source = config.artifactKind === 'baseline'
|
|
343
|
-
? (lang === 'zh' ? '
|
|
366
|
+
? (lang === 'zh' ? '无来源' : 'no source')
|
|
344
367
|
: (sourceLabels[lang] || sourceLabels.en)[config.artifactSource] || config.artifactSource;
|
|
345
368
|
const strategy = (strategyLabels[lang] || strategyLabels.en)[config.executionStrategy] || config.executionStrategy;
|
|
346
369
|
const cwdRaw = config.cwd || '';
|
|
347
370
|
const runtimeContext = cwdRaw
|
|
348
371
|
? cwdRaw.replace(/.*\/Projects\//, '').replace(/.*\/Documents\//, '').replace(/\/Users\/[^/]+\//, '~/')
|
|
349
|
-
: (lang === 'zh' ? '默认' : 'default');
|
|
372
|
+
: (lang === 'zh' ? '默认 cwd' : 'default cwd');
|
|
350
373
|
const color = COLORS[i % COLORS.length];
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
<
|
|
355
|
-
<
|
|
356
|
-
|
|
357
|
-
<td title="${e(cwdRaw)}">${e(runtimeContext)}</td>
|
|
358
|
-
</tr>`;
|
|
374
|
+
const artifactKindLabel = e((artifactKindLabels[lang] || artifactKindLabels.en)[config.artifactKind] || config.artifactKind);
|
|
375
|
+
const tooltip = `${t('variantType', lang)}: ${expType} · ${t('variantArtifactKind', lang)}: ${artifactKindLabel} · ${t('variantArtifactSource', lang)}: ${source} · ${t('variantExecutionStrategy', lang)}: ${strategy} · ${t('variantRuntimeContext', lang)}: ${cwdRaw || (lang === 'zh' ? '默认' : 'default')}`;
|
|
376
|
+
return `<div class="ctx-row" style="border-left:3px solid ${color}" title="${e(tooltip)}">
|
|
377
|
+
<span class="ctx-row-name">${e(config.variant)}</span>
|
|
378
|
+
<span class="ctx-row-bits">${e(expType)} <span class="ctx-sep">·</span> ${artifactKindLabel} <span class="ctx-sep">·</span> ${e(source)} <span class="ctx-sep">·</span> ${e(strategy)} <span class="ctx-sep">·</span> ${e(runtimeContext)}</span>
|
|
379
|
+
</div>`;
|
|
359
380
|
}).join('');
|
|
360
381
|
const configModalId = 'guide-variant-config';
|
|
361
382
|
const repeatSuffix = report.variance
|
|
362
383
|
? (lang === 'zh' ? ` × ${report.variance.runs} 轮` : ` × ${report.variance.runs} runs`)
|
|
363
384
|
: '';
|
|
364
385
|
const experimentSummary = lang === 'zh'
|
|
365
|
-
? `${m.sampleCount}
|
|
386
|
+
? `${m.sampleCount} 个用例 × ${variants.length} 组实验${repeatSuffix}`
|
|
366
387
|
: `${pluralizeEn(m.sampleCount, 'sample')} × ${pluralizeEn(variants.length, 'variant')}${repeatSuffix}`;
|
|
367
388
|
const variantConfigSection = variantConfigRows ? `
|
|
368
|
-
<
|
|
369
|
-
<
|
|
370
|
-
<
|
|
371
|
-
<div class="
|
|
372
|
-
|
|
373
|
-
<thead><tr>
|
|
374
|
-
<th>🏷️ ${t('variants', lang)}</th>
|
|
375
|
-
<th>🧪 ${t('variantType', lang)}</th>
|
|
376
|
-
<th>📦 ${t('variantArtifactKind', lang)}</th>
|
|
377
|
-
<th>📂 ${t('variantArtifactSource', lang)}</th>
|
|
378
|
-
<th>⚙️ ${t('variantExecutionStrategy', lang)}</th>
|
|
379
|
-
<th>🖥️ ${t('variantRuntimeContext', lang)}</th>
|
|
380
|
-
</tr></thead>
|
|
381
|
-
<tbody>${variantConfigRows}</tbody>
|
|
382
|
-
</table>
|
|
383
|
-
</div>
|
|
384
|
-
</section>
|
|
389
|
+
<div class="ctx-block">
|
|
390
|
+
<div class="ctx-h">${t('variantConfig', lang)} <button type="button" class="hint-btn" onclick="openModal('${configModalId}')" aria-label="${e(t('variantConfigDesc', lang))}" aria-haspopup="dialog">?</button></div>
|
|
391
|
+
<div class="ctx-sub">${experimentSummary}</div>
|
|
392
|
+
<div class="ctx-rows">${variantConfigRows}</div>
|
|
393
|
+
</div>
|
|
385
394
|
<div id="${configModalId}" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="${configModalId}-title" onclick="if(event.target===this)closeModal('${configModalId}')">
|
|
386
395
|
<div class="modal-content">
|
|
387
396
|
<div class="modal-header">
|
|
@@ -409,14 +418,20 @@ export function renderRunDetail(report, lang = DEFAULT_LANG) {
|
|
|
409
418
|
</div>
|
|
410
419
|
</div>
|
|
411
420
|
` : '';
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
421
|
+
// v0.30 重设计 — 评分/单测各自独立 title + meta strip(不共享头部)。
|
|
422
|
+
// 顶部只保留极简身份带:返回链接 + run-id pill。完整 H1 + meta tags 移进各自 tab 面板。
|
|
423
|
+
const idStamp = (() => {
|
|
424
|
+
const idMatch = /^.+?-(\d{8})-(\d{4})$/.exec(report.id);
|
|
425
|
+
if (idMatch) {
|
|
426
|
+
const [, ymd, hm] = idMatch;
|
|
427
|
+
return `${ymd.slice(0, 4)}-${ymd.slice(4, 6)}-${ymd.slice(6, 8)} ${hm.slice(0, 2)}:${hm.slice(2, 4)}`;
|
|
428
|
+
}
|
|
429
|
+
return '';
|
|
430
|
+
})();
|
|
431
|
+
// ──────────── 评分 tab 的 meta tags(原版全部留这里) ────────────
|
|
432
|
+
const scoreMetaTags = `<div class="meta-tags">
|
|
433
|
+
<span class="meta-tag">${t('model', lang)}: ${e(m.model)}</span>
|
|
434
|
+
${(() => {
|
|
420
435
|
if (m.noJudge)
|
|
421
436
|
return `<span class="meta-tag">${t('judge', lang)}: none</span>`;
|
|
422
437
|
const list = m.judgeModels ?? [];
|
|
@@ -426,39 +441,125 @@ export function renderRunDetail(report, lang = DEFAULT_LANG) {
|
|
|
426
441
|
return `<span class="meta-tag">${t('judge', lang)}: ${e(`${list[0].executor}:${list[0].model}`)}</span>`;
|
|
427
442
|
return `<span class="meta-tag" title="${t('ensembleDesc', lang)}">${t('judgeModelsLabel', lang)}: ${list.map((j) => e(`${j.executor}:${j.model}`)).join(' · ')}</span>`;
|
|
428
443
|
})()}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
444
|
+
${m.judgeRepeat && m.judgeRepeat > 1 ? `<span class="meta-tag" title="${t('judgeStddevDesc', lang)}">${t('judgeRepeatLabel', lang)}: ${m.judgeRepeat}</span>` : ''}
|
|
445
|
+
<span class="meta-tag">${t('executor', lang)}: ${e(m.executor || 'claude')}</span>
|
|
446
|
+
${m.effort ? `<span class="meta-tag" title="${e(lang === 'zh' ? 'executor LLM 的扩展思考预算(--effort)。跨 effort 报告不可严格比较' : 'reasoning effort for executor LLM (--effort); reports across different efforts are not strictly comparable')}">effort: ${e(m.effort)}</span>` : ''}
|
|
447
|
+
<span class="meta-tag"${execCostReported ? '' : ` title="${e(lang === 'zh' ? 'executor 不报 USD 成本(如 codex CLI),无法估算' : 'executor does not report USD cost (e.g. codex CLI); not measurable')}"`}>${t('cost', lang)}: ${fmtCost(totalExecCost, execCostReported)}</span>
|
|
448
|
+
<span class="meta-tag"${totalCostReported ? '' : ` title="${e(costCompletenessTooltip(lang))}"`}>${t('totalCost', lang)}: ${fmtKnownCost(m.totalCostUSD, totalCostReported)}</span>
|
|
449
|
+
<span class="meta-tag">${lang === 'zh' ? '耗时' : 'Duration'}: ${fmtDuration(totalDurationMs)}</span>
|
|
450
|
+
${m.gitInfo ? `<span class="meta-tag">${lang === 'zh' ? '提交' : 'commit'}: ${e(m.gitInfo.commitShort)}${m.gitInfo.dirty ? '*' : ''} (${e(m.gitInfo.branch)})</span>` : ''}
|
|
451
|
+
${m.sampleHashes ? `<span class="meta-tag" title="${t('sampleHashCountDesc', lang)}">${t('sampleHashCount', lang)}: ${Object.keys(m.sampleHashes).length}/${m.sampleCount}</span>` : ''}
|
|
452
|
+
${m.evaluationFramework ? `<span class="meta-tag" title="${t('evalFrameworkDesc', lang)}">${t('evalFrameworkLabel', lang)}: ${m.evaluationFramework === 'bootstrap' ? t('evalFrameworkBootstrap', lang) : m.evaluationFramework === 'both' ? t('evalFrameworkBoth', lang) : t('evalFrameworkTTest', lang)}</span>` : ''}
|
|
453
|
+
${renderDebiasModeTag(m.debiasMode, lang)}
|
|
454
|
+
${m.blind ? `<span class="meta-tag" style="color:var(--green)" data-i18n="blindLabel">${t('blindLabel', lang)}</span>` : ''}
|
|
455
|
+
</div>`;
|
|
456
|
+
const auditFingerprints = (() => {
|
|
457
|
+
const auditTags = [
|
|
458
|
+
m.judgePromptHash ? `<span class="meta-tag" title="${t('judgePromptHashDesc', lang)}">${t('judgePromptHashLabel', lang)}: <code>${e(m.judgePromptHash)}</code></span>` : '',
|
|
459
|
+
renderRuntimeFingerprintTags(m, lang),
|
|
460
|
+
].join('');
|
|
461
|
+
if (!auditTags)
|
|
462
|
+
return '';
|
|
463
|
+
return `<details class="audit-fingerprints"><summary>${lang === 'zh' ? '审计指纹(用于复现校验)' : 'Audit fingerprints (for reproducibility)'}</summary><div class="meta-tags">${auditTags}</div></details>`;
|
|
464
|
+
})();
|
|
465
|
+
const blindReveal = m.blind ? `
|
|
442
466
|
<div style="margin:12px 0">
|
|
443
467
|
<button onclick="document.getElementById('blind-reveal').style.display=document.getElementById('blind-reveal').style.display==='none'?'block':'none'" data-i18n="revealBlind">${t('revealBlind', lang)}</button>
|
|
444
468
|
<div id="blind-reveal" style="display:none;margin-top:8px;padding:12px;background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius)" role="region" aria-label="Blind variant mapping">
|
|
445
469
|
${Object.entries(m.blindMap || {}).map(([label, real]) => `<div style="font-size:13px;color:var(--text-secondary)"><strong>Variant ${e(label)}</strong> → ${e(real)}</div>`).join('')}
|
|
446
470
|
</div>
|
|
447
|
-
</div>` : ''
|
|
471
|
+
</div>` : '';
|
|
472
|
+
// ──────────── 单测 tab 的 meta tags(只有 sample 维度数据,不含 cost / model) ────────────
|
|
473
|
+
const sampleSnaps = report.sampleSnapshots ?? {};
|
|
474
|
+
let testPassed = 0;
|
|
475
|
+
let testFailed = 0;
|
|
476
|
+
let testTripwire = 0;
|
|
477
|
+
for (const r of (report.results || [])) {
|
|
478
|
+
const v = m.variants?.[0] ? r.variants[m.variants[0]] : null;
|
|
479
|
+
if (!v)
|
|
480
|
+
continue;
|
|
481
|
+
const passed = (v.assertions?.details ?? []).every((d) => d.passed);
|
|
482
|
+
if (passed)
|
|
483
|
+
testPassed += 1;
|
|
484
|
+
else if (sampleSnaps[r.sample_id]?.tripwire || (v.diagnostic?.rootCause ?? []).includes('tripwire_intentional'))
|
|
485
|
+
testTripwire += 1;
|
|
486
|
+
else
|
|
487
|
+
testFailed += 1;
|
|
488
|
+
}
|
|
489
|
+
const testMetaTags = `<div class="meta-tags">
|
|
490
|
+
<span class="meta-tag">${m.sampleCount} ${lang === 'zh' ? '用例' : 'samples'}</span>
|
|
491
|
+
<span class="meta-tag" style="color:var(--green)">✓ ${testPassed} ${lang === 'zh' ? '通过' : 'passed'}</span>
|
|
492
|
+
${testFailed > 0 ? `<span class="meta-tag" style="color:var(--red)">✗ ${testFailed} ${lang === 'zh' ? '失败' : 'failed'}</span>` : ''}
|
|
493
|
+
${testTripwire > 0 ? `<span class="meta-tag" style="color:#7a6b89">◆ ${testTripwire} ${lang === 'zh' ? '诱错触发' : 'tripwire'}</span>` : ''}
|
|
494
|
+
<span class="meta-tag">${(m.variants || []).join(' · ')}</span>
|
|
495
|
+
</div>`;
|
|
496
|
+
return layout(`${report.id}`, `
|
|
497
|
+
<main>
|
|
498
|
+
<nav class="nav"><a href="/${langQ}" data-i18n="backToList">${t('backToList', lang)}</a></nav>
|
|
499
|
+
<div class="report-id-line">
|
|
500
|
+
<code class="report-id-pill">${e(report.id)}</code>
|
|
501
|
+
${idStamp ? `<span class="report-id-stamp">${e(idStamp)}</span>` : ''}
|
|
502
|
+
</div>
|
|
448
503
|
|
|
449
|
-
|
|
504
|
+
<div class="omk-view-tabs" role="tablist">
|
|
505
|
+
<button type="button" class="omk-view-tab omk-view-tab--active" data-view="score" onclick="omkSwitchView('score')">${lang === 'zh' ? '📊 评测视角' : '📊 Score view'}</button>
|
|
506
|
+
<button type="button" class="omk-view-tab" data-view="test" onclick="omkSwitchView('test')">${lang === 'zh' ? '✅ 功能视角' : '✅ Functional view'}</button>
|
|
507
|
+
</div>
|
|
508
|
+
|
|
509
|
+
<div class="omk-view-panel" data-view="score">
|
|
510
|
+
<p class="subtitle">${lang === 'zh' ? '主观质量打分(judge)+ 客观断言聚合,产出综合评分与 verdict' : 'Subjective judge + objective assertions aggregated into composite + verdict'}</p>
|
|
511
|
+
${verdictPill}
|
|
512
|
+
${scoreMetaTags}
|
|
513
|
+
${blindReveal}
|
|
450
514
|
|
|
451
|
-
<section>${cards}
|
|
515
|
+
<section>${cards}</section>
|
|
516
|
+
<div class="ctx-strip">${variantConfigSection}${renderAgentOverview(variants, summary, lang)}</div>
|
|
517
|
+
${auditFingerprints}
|
|
518
|
+
${methodologyAudit}
|
|
452
519
|
|
|
453
520
|
${renderVarianceComparisons(report.variance, lang, Boolean(report.meta.layeredStats), summary)}
|
|
454
521
|
|
|
455
522
|
${renderAnalysis(report, lang)}
|
|
456
523
|
|
|
457
|
-
${renderAgentOverview(variants, summary, lang)}
|
|
458
|
-
|
|
459
524
|
${renderKnowledgeInteractionSection(report.analysis?.coverage, report.analysis?.gapReports, lang)}
|
|
460
525
|
|
|
461
526
|
<section>${sampleTable}</section>
|
|
527
|
+
</div>
|
|
528
|
+
|
|
529
|
+
<div class="omk-view-panel" data-view="test" hidden>
|
|
530
|
+
<p class="subtitle">${lang === 'zh' ? '每条用例当一条功能测试看:期望(rubric/assertions/mocks)、实际执行(trace)、失败时给诊断建议' : 'Each sample as a functional test: expected(rubric/assertions/mocks), actual(trace), diagnostic suggestions on failure'}</p>
|
|
531
|
+
${testMetaTags}
|
|
532
|
+
${renderTestView(report, lang)}
|
|
533
|
+
</div>
|
|
534
|
+
|
|
535
|
+
<style>${TEST_VIEW_CSS}
|
|
536
|
+
/* Context strip — 实验配置 + Agent 概览并排,作为辅助元数据,视觉权重低于综合分 hero / 六维 / 用例详情 */
|
|
537
|
+
.ctx-strip { display:grid;gap:14px;grid-template-columns:1fr 1fr;margin:14px 0 24px }
|
|
538
|
+
@media (max-width:760px) { .ctx-strip { grid-template-columns:1fr } }
|
|
539
|
+
.ctx-block { background:var(--bg-surface);border-radius:var(--radius);box-shadow:var(--shadow-sm);padding:12px 16px }
|
|
540
|
+
.ctx-h { font-size:13px;color:var(--text-secondary);font-weight:600;margin-bottom:2px;display:flex;align-items:center;gap:4px;letter-spacing:0.02em }
|
|
541
|
+
.ctx-sub { font-size:12px;color:var(--text-muted);margin-bottom:10px }
|
|
542
|
+
.ctx-rows { display:flex;flex-direction:column;gap:6px }
|
|
543
|
+
.ctx-row { padding:8px 12px;background:var(--bg-soft);border-radius:4px;display:flex;align-items:center;gap:10px;font-size:13px;line-height:1.5;flex-wrap:wrap }
|
|
544
|
+
.ctx-row-name { font-weight:600;color:var(--text-primary);font-family:"SF Mono",Menlo,monospace;font-size:12.5px;letter-spacing:-0.01em }
|
|
545
|
+
.ctx-row-bits { color:var(--text-secondary);flex:1;min-width:0;font-size:12.5px }
|
|
546
|
+
.ctx-sep { color:var(--text-muted);margin:0 2px;opacity:0.6 }
|
|
547
|
+
/* 报告身份带 — 极简一行,run-id pill + 时间戳 + verdict pill。完整 H1 + meta 移进各 tab 自己的 panel。 */
|
|
548
|
+
.report-id-line { display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin:8px 0 18px }
|
|
549
|
+
.report-id-pill { font-size:14px;font-family:"SF Mono",Menlo,monospace;color:var(--text-primary);background:var(--bg-surface);padding:5px 12px;border-radius:14px;border:1px solid var(--border);letter-spacing:-0.01em;font-weight:500 }
|
|
550
|
+
.report-id-stamp { font-size:12px;color:var(--text-muted);font-variant-numeric:tabular-nums }
|
|
551
|
+
/* 顶部 tab 切换器 — 显式 reset border-radius / appearance / outline,
|
|
552
|
+
防止全局 button 规则给底部加奇怪圆角和深色背景。 */
|
|
553
|
+
.omk-view-tabs { display:flex;gap:0;margin:16px 0 24px;border-bottom:1px solid var(--border) }
|
|
554
|
+
.omk-view-tab { padding:12px 20px;background:transparent;border:none;border-radius:0;cursor:pointer;color:var(--text-secondary);border-bottom:2px solid transparent;font-size:14px;font-weight:500;outline:none;appearance:none;-webkit-appearance:none;margin-bottom:-1px;font-family:inherit }
|
|
555
|
+
.omk-view-tab--active { color:var(--text-primary);border-bottom-color:var(--accent);font-weight:600 }
|
|
556
|
+
.omk-view-tab:hover:not(.omk-view-tab--active) { color:var(--text-primary) }
|
|
557
|
+
.omk-view-tab:focus-visible { outline:2px solid var(--accent);outline-offset:-4px;border-radius:4px }
|
|
558
|
+
/* tab 面板内的 H1 — 跟列表 H1 同级别但加 emoji 区分视角 */
|
|
559
|
+
.omk-view-panel h1 { font-size:1.5rem;margin:0 0 6px }
|
|
560
|
+
.omk-view-panel .subtitle { font-size:14px;color:var(--text-secondary);margin:0 0 20px;line-height:1.6 }
|
|
561
|
+
</style>
|
|
562
|
+
<script>${TEST_VIEW_JS}</script>
|
|
462
563
|
|
|
463
564
|
</main>
|
|
464
565
|
`, lang);
|
|
@@ -517,9 +618,14 @@ export function renderBatchEvaluationDetail(report, lang = DEFAULT_LANG) {
|
|
|
517
618
|
return `<span class="meta-tag" title="${t('ensembleDesc', lang)}">${t('judgeModelsLabel', lang)}: ${list.map((j) => e(`${j.executor}:${j.model}`)).join(' · ')}</span>`;
|
|
518
619
|
})()}
|
|
519
620
|
<span class="meta-tag">${t('executor', lang)}: ${e(m.executor || 'claude')}</span>
|
|
520
|
-
${renderExecutorRuntimeTags(m, lang)}${renderJudgeRuntimeTags(m, lang)}
|
|
521
621
|
<span class="meta-tag"${totalCostReported ? '' : ` title="${e(costCompletenessTooltip(lang))}"`}>${t('totalCost', lang)}: ${fmtKnownCost(m.totalCostUSD, totalCostReported)}</span>
|
|
522
622
|
</div>
|
|
623
|
+
${(() => {
|
|
624
|
+
const auditTags = renderRuntimeFingerprintTags(m, lang);
|
|
625
|
+
if (!auditTags)
|
|
626
|
+
return '';
|
|
627
|
+
return `<details class="audit-fingerprints"><summary>${lang === 'zh' ? '审计指纹(用于复现校验)' : 'Audit fingerprints (for reproducibility)'}</summary><div class="meta-tags">${auditTags}</div></details>`;
|
|
628
|
+
})()}
|
|
523
629
|
|
|
524
630
|
<section>
|
|
525
631
|
<h2>${t('batchOverview', lang)}</h2>
|