oh-my-knowledge 0.29.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 +12 -5
- package/README.zh.md +12 -5
- package/dist/src/analysis/report-diagnostics.js +2 -0
- package/dist/src/analysis/report-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.js +2 -1
- package/dist/src/cli/commands/observe.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/i18n-dict.d.ts +1 -1
- package/dist/src/cli/i18n-dict.d.ts.map +1 -1
- package/dist/src/cli/i18n-dict.js +64 -6
- 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/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/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 +11 -1
- package/dist/src/observability/inbox-view-model.d.ts.map +1 -1
- package/dist/src/observability/inbox-view-model.js +76 -3
- package/dist/src/observability/inbox-view-model.js.map +1 -1
- package/dist/src/observability/inbox.d.ts +27 -2
- package/dist/src/observability/inbox.d.ts.map +1 -1
- package/dist/src/observability/inbox.js +46 -8
- package/dist/src/observability/inbox.js.map +1 -1
- 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 +1 -1
- package/dist/src/observability/trace-adapter.d.ts.map +1 -1
- package/dist/src/observability/trace-adapter.js +1 -1
- package/dist/src/observability/trace-adapter.js.map +1 -1
- package/dist/src/observability/trace-attribution.d.ts +29 -1
- package/dist/src/observability/trace-attribution.d.ts.map +1 -1
- package/dist/src/observability/trace-attribution.js +106 -10
- package/dist/src/observability/trace-attribution.js.map +1 -1
- package/dist/src/observability/trace-segmenter.d.ts +11 -1
- package/dist/src/observability/trace-segmenter.d.ts.map +1 -1
- package/dist/src/observability/trace-segmenter.js +117 -28
- package/dist/src/observability/trace-segmenter.js.map +1 -1
- package/dist/src/observability/trace-source.d.ts +28 -1
- package/dist/src/observability/trace-source.d.ts.map +1 -1
- package/dist/src/observability/trace-source.js +356 -6
- package/dist/src/observability/trace-source.js.map +1 -1
- package/dist/src/renderer/html-renderer.d.ts.map +1 -1
- package/dist/src/renderer/html-renderer.js +229 -171
- 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 +108 -61
- package/dist/src/renderer/layout.js.map +1 -1
- package/dist/src/renderer/observation-inbox-renderer.d.ts.map +1 -1
- package/dist/src/renderer/observation-inbox-renderer.js +4567 -91
- package/dist/src/renderer/observation-inbox-renderer.js.map +1 -1
- 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.map +1 -1
- package/dist/src/renderer/summary.js +78 -50
- 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/server/report-server.d.ts +4 -1
- package/dist/src/server/report-server.d.ts.map +1 -1
- package/dist/src/server/report-server.js +149 -4
- 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/types/eval.d.ts +83 -0
- package/dist/src/types/eval.d.ts.map +1 -1
- package/dist/src/types/executor.d.ts +43 -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
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { e, fmtCost, fmtDuration, fmtKnownCost, COLORS, DEFAULT_LANG, t, layout } from './layout.js';
|
|
5
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 实心(强信号),
|
|
@@ -132,7 +133,7 @@ function renderRuntimeFingerprintTags(meta, lang) {
|
|
|
132
133
|
}
|
|
133
134
|
export function renderRunList(runs, lang = DEFAULT_LANG) {
|
|
134
135
|
const langQ = lang === DEFAULT_LANG ? '' : `?lang=${lang}`;
|
|
135
|
-
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>`;
|
|
136
137
|
if (!runs || runs.length === 0) {
|
|
137
138
|
return layout(t('title', lang), `
|
|
138
139
|
<main>
|
|
@@ -143,112 +144,85 @@ export function renderRunList(runs, lang = DEFAULT_LANG) {
|
|
|
143
144
|
</main>
|
|
144
145
|
`, lang);
|
|
145
146
|
}
|
|
146
|
-
|
|
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) => {
|
|
147
166
|
if (run.kind === 'batch-evaluation') {
|
|
148
167
|
const m = run.meta;
|
|
149
|
-
const
|
|
168
|
+
const scores = run.items.length > 0
|
|
150
169
|
? run.items.map((item) => {
|
|
151
170
|
const baselineScore = scoreOf(item.summary.baseline);
|
|
152
171
|
const skillScore = scoreOf(item.summary[item.name]);
|
|
153
|
-
|
|
154
|
-
if (score == null)
|
|
155
|
-
return `<span style="color:var(--text-muted)">${e(item.name)}: -</span>`;
|
|
156
|
-
const color = score >= 4 ? 'var(--green)' : score >= 3 ? 'var(--yellow)' : 'var(--red)';
|
|
157
|
-
const barW = Math.round((score / 5) * 100);
|
|
158
|
-
return `<div style="display:flex;align-items:center;gap:6px;margin:2px 0">` +
|
|
159
|
-
`<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>` +
|
|
160
|
-
`<div style="width:64px;height:6px;background:var(--bg-surface);border-radius:3px;flex-shrink:0">` +
|
|
161
|
-
`<div style="width:${barW}%;height:100%;background:${color};border-radius:3px"></div></div>` +
|
|
162
|
-
`<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);
|
|
163
173
|
}).join('')
|
|
164
|
-
:
|
|
165
|
-
const allCostReported = run.items.every((item) => Object.values(item.summary || {}).every((v) => v.execCostReported !== false && v.judgeCostReported !== false));
|
|
166
|
-
const totalCostReported = m.totalCostReported !== false && allCostReported;
|
|
174
|
+
: `<div class="rc-score-empty">${lang === 'zh' ? '无分数' : 'no score'}</div>`;
|
|
167
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);
|
|
168
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>`;
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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>`;
|
|
178
191
|
}
|
|
179
192
|
const m = run.meta;
|
|
180
|
-
const
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
return `<span style="color:var(--text-muted)">${e(v)}: -</span>`;
|
|
186
|
-
const color = score >= 4 ? 'var(--green)' : score >= 3 ? 'var(--yellow)' : 'var(--red)';
|
|
187
|
-
const barW = Math.round((score / 5) * 100);
|
|
188
|
-
return `<div style="display:flex;align-items:center;gap:6px;margin:2px 0">` +
|
|
189
|
-
`<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>` +
|
|
190
|
-
`<div style="width:64px;height:6px;background:var(--bg-surface);border-radius:3px;flex-shrink:0">` +
|
|
191
|
-
`<div style="width:${barW}%;height:100%;background:${color};border-radius:3px"></div></div>` +
|
|
192
|
-
`<span style="font-size:12px;font-weight:600;color:${color};min-width:24px">${score}</span></div>`;
|
|
193
|
-
}).join('')
|
|
194
|
-
: '<div style="color:var(--text-faint);font-size:0.6875rem;text-align:center">no score</div>';
|
|
195
|
-
const badges = ''; // TODO: artifact kind 体系完善后,按 kind 显示评测类型标签
|
|
196
|
-
// v0.21 B.4 — 列表页 verdict pill: 一眼分辨 progress / regress / noise.
|
|
197
|
-
// computeVerdict 是同步纯函数(report -> level),per row 跑成本 O(samples).
|
|
198
|
-
// 脏报告可能让 layer-gates 访问 undefined.avgFactScore 抛 NPE。
|
|
199
|
-
// 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>`;
|
|
200
198
|
let statusPill = '';
|
|
199
|
+
let verdictLvl = '';
|
|
201
200
|
try {
|
|
202
201
|
const verdict = computeVerdict(run);
|
|
203
|
-
|
|
204
|
-
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>`;
|
|
205
204
|
}
|
|
206
205
|
catch { /* skip pill on this row */ }
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
<
|
|
217
|
-
<
|
|
218
|
-
<
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
<td>${fmtDuration(Object.values(run.summary || {}).reduce((s, v) => s + (v.avgDurationMs || 0) * (v.successCount || 0), 0))}</td>
|
|
224
|
-
<td style="white-space:nowrap"><button onclick="deleteRun('${e(run.id)}',this)" class="btn-danger" data-i18n="deleteBtnText">${t('deleteBtnText', lang)}</button></td>
|
|
225
|
-
</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>`;
|
|
226
222
|
}).join('');
|
|
227
223
|
const runCount = lang === 'zh' ? `${runs.length} 次评测` : `${runs.length} runs`;
|
|
228
|
-
//
|
|
229
|
-
// 排除 not reported(而不是把整体压成「—」)是因为列表常含数十个 run 仅 1-2 个是 codex,
|
|
230
|
-
// 把全部 sum 抹成「—」会丢掉绝大多数有效成本信息。
|
|
224
|
+
// v0.30 — 列表页移除累计金额展示(用户反馈无须暴露);保留 evaluationRuns 用于 trend links
|
|
231
225
|
const evaluationRuns = runs.filter(isEvaluationReport);
|
|
232
|
-
const reportableRuns = evaluationRuns.filter((r) => Object.values(r.summary || {}).every((v) => v.execCostReported !== false));
|
|
233
|
-
const unmeasuredRunsCount = evaluationRuns.length - reportableRuns.length;
|
|
234
|
-
const totalCost = reportableRuns.reduce((s, r) => s + Object.values(r.summary || {}).reduce((sv, v) => sv + (v.totalExecCostUSD || 0), 0), 0);
|
|
235
|
-
// partial:有 reported 就显示数字 + tooltip;全部 not reported 才显示「—」;
|
|
236
|
-
// 全 reported(unmeasuredRunsCount=0)走老格式不包 span,保持 HTML snapshot 向后兼容。
|
|
237
|
-
// X/N 限定信息只放 tooltip,不挂在显眼位置 — 累计行视觉应该是单纯的数字。
|
|
238
|
-
const allUnmeasured = reportableRuns.length === 0 && runs.length > 0;
|
|
239
|
-
const baseLabel = lang === 'zh' ? '累计' : 'Total';
|
|
240
|
-
const costNumber = fmtCost(totalCost, !allUnmeasured);
|
|
241
|
-
let costLabel;
|
|
242
|
-
if (unmeasuredRunsCount === 0) {
|
|
243
|
-
// 全 reported → 跟旧版字节级一致,不破 snapshot
|
|
244
|
-
costLabel = `${baseLabel} ${costNumber}`;
|
|
245
|
-
}
|
|
246
|
-
else {
|
|
247
|
-
const tip = lang === 'zh'
|
|
248
|
-
? `${reportableRuns.length}/${evaluationRuns.length} 次单次报告了 USD 成本;${unmeasuredRunsCount} 次 executor 不报(如 codex CLI),batch index 不计入累计以避免重复`
|
|
249
|
-
: `${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`;
|
|
250
|
-
costLabel = `<span title="${e(tip)}">${baseLabel} ${costNumber}</span>`;
|
|
251
|
-
}
|
|
252
226
|
// Collect variants with ≥2 reports for trend links
|
|
253
227
|
const variantCounts = {};
|
|
254
228
|
for (const run of evaluationRuns) {
|
|
@@ -266,7 +240,7 @@ export function renderRunList(runs, lang = DEFAULT_LANG) {
|
|
|
266
240
|
return layout(t('title', lang), `
|
|
267
241
|
<main>
|
|
268
242
|
<h1>${t('title', lang)}</h1>
|
|
269
|
-
<p class="subtitle" data-i18n="subtitle">${t('subtitle', lang)} · ${runCount}
|
|
243
|
+
<p class="subtitle" data-i18n="subtitle">${t('subtitle', lang)} · ${runCount}</p>
|
|
270
244
|
${trendsSection}
|
|
271
245
|
<div style="margin:12px 0">${skillHealthLink}</div>
|
|
272
246
|
<div style="margin:12px 0;display:flex;gap:8px;align-items:center">
|
|
@@ -274,29 +248,54 @@ export function renderRunList(runs, lang = DEFAULT_LANG) {
|
|
|
274
248
|
<span id="filter-count" style="font-size:11px;color:var(--text-muted)"></span>
|
|
275
249
|
</div>
|
|
276
250
|
${renderScoringModal('guide-scoring-list', lang)}
|
|
277
|
-
<div class="
|
|
278
|
-
<
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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>
|
|
291
290
|
<script>
|
|
292
291
|
function filterTable(q) {
|
|
293
|
-
var
|
|
292
|
+
var cards = document.querySelectorAll('#run-list .run-card');
|
|
294
293
|
var lower = q.toLowerCase();
|
|
295
294
|
var shown = 0;
|
|
296
|
-
|
|
297
|
-
var text =
|
|
295
|
+
cards.forEach(function(card) {
|
|
296
|
+
var text = card.textContent.toLowerCase();
|
|
298
297
|
var match = !q || text.indexOf(lower) !== -1;
|
|
299
|
-
|
|
298
|
+
card.style.display = match ? '' : 'none';
|
|
300
299
|
if (match) shown++;
|
|
301
300
|
});
|
|
302
301
|
var countEl = document.getElementById('filter-count');
|
|
@@ -308,7 +307,7 @@ export function renderRunList(runs, lang = DEFAULT_LANG) {
|
|
|
308
307
|
fetch('/api/reports/' + encodeURIComponent(id), { method: 'DELETE' })
|
|
309
308
|
.then(function(r) { return r.json(); })
|
|
310
309
|
.then(function(d) {
|
|
311
|
-
if (d.ok) { btn.closest('
|
|
310
|
+
if (d.ok) { btn.closest('.run-card').remove(); }
|
|
312
311
|
else { alert(I18N[lang].deleteFail + ': ' + (d.error || 'unknown')); }
|
|
313
312
|
})
|
|
314
313
|
.catch(function(err) { alert(I18N[lang].deleteFail + ': ' + err.message); });
|
|
@@ -357,52 +356,41 @@ export function renderRunDetail(report, lang = DEFAULT_LANG) {
|
|
|
357
356
|
zh: { baseline: '基线', skill: 'Skill', prompt: 'Prompt', agent: 'Agent', workflow: 'Workflow' },
|
|
358
357
|
en: { baseline: 'Baseline', skill: 'Skill', prompt: 'Prompt', agent: 'Agent', workflow: 'Workflow' },
|
|
359
358
|
};
|
|
359
|
+
// v0.30 — 实验配置弱化:单行 inline 摘要,跟 Agent 执行概览并排放(context-strip)。
|
|
360
|
+
// 每变体一行:左 3px 颜色条 + variant 名 + type/kind/source/strategy/cwd 用 · 分隔。
|
|
361
|
+
// 完整 key 名通过 hover tooltip 提供。视觉权重远低于综合分 hero。
|
|
360
362
|
const variantConfigRows = (m.variantConfigs || []).map((config, i) => {
|
|
361
363
|
const expTypeRaw = config.experimentType || '-';
|
|
362
364
|
const expType = (typeLabels[lang] || typeLabels.en)[String(expTypeRaw)] || expTypeRaw;
|
|
363
365
|
const source = config.artifactKind === 'baseline'
|
|
364
|
-
? (lang === 'zh' ? '
|
|
366
|
+
? (lang === 'zh' ? '无来源' : 'no source')
|
|
365
367
|
: (sourceLabels[lang] || sourceLabels.en)[config.artifactSource] || config.artifactSource;
|
|
366
368
|
const strategy = (strategyLabels[lang] || strategyLabels.en)[config.executionStrategy] || config.executionStrategy;
|
|
367
369
|
const cwdRaw = config.cwd || '';
|
|
368
370
|
const runtimeContext = cwdRaw
|
|
369
371
|
? cwdRaw.replace(/.*\/Projects\//, '').replace(/.*\/Documents\//, '').replace(/\/Users\/[^/]+\//, '~/')
|
|
370
|
-
: (lang === 'zh' ? '默认' : 'default');
|
|
372
|
+
: (lang === 'zh' ? '默认 cwd' : 'default cwd');
|
|
371
373
|
const color = COLORS[i % COLORS.length];
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
<
|
|
376
|
-
<
|
|
377
|
-
|
|
378
|
-
<td title="${e(cwdRaw)}">${e(runtimeContext)}</td>
|
|
379
|
-
</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>`;
|
|
380
380
|
}).join('');
|
|
381
381
|
const configModalId = 'guide-variant-config';
|
|
382
382
|
const repeatSuffix = report.variance
|
|
383
383
|
? (lang === 'zh' ? ` × ${report.variance.runs} 轮` : ` × ${report.variance.runs} runs`)
|
|
384
384
|
: '';
|
|
385
385
|
const experimentSummary = lang === 'zh'
|
|
386
|
-
? `${m.sampleCount}
|
|
386
|
+
? `${m.sampleCount} 个用例 × ${variants.length} 组实验${repeatSuffix}`
|
|
387
387
|
: `${pluralizeEn(m.sampleCount, 'sample')} × ${pluralizeEn(variants.length, 'variant')}${repeatSuffix}`;
|
|
388
388
|
const variantConfigSection = variantConfigRows ? `
|
|
389
|
-
<
|
|
390
|
-
<
|
|
391
|
-
<
|
|
392
|
-
<div class="
|
|
393
|
-
|
|
394
|
-
<thead><tr>
|
|
395
|
-
<th>🏷️ ${t('variants', lang)}</th>
|
|
396
|
-
<th>🧪 ${t('variantType', lang)}</th>
|
|
397
|
-
<th>📦 ${t('variantArtifactKind', lang)}</th>
|
|
398
|
-
<th>📂 ${t('variantArtifactSource', lang)}</th>
|
|
399
|
-
<th>⚙️ ${t('variantExecutionStrategy', lang)}</th>
|
|
400
|
-
<th>🖥️ ${t('variantRuntimeContext', lang)}</th>
|
|
401
|
-
</tr></thead>
|
|
402
|
-
<tbody>${variantConfigRows}</tbody>
|
|
403
|
-
</table>
|
|
404
|
-
</div>
|
|
405
|
-
</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>
|
|
406
394
|
<div id="${configModalId}" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="${configModalId}-title" onclick="if(event.target===this)closeModal('${configModalId}')">
|
|
407
395
|
<div class="modal-content">
|
|
408
396
|
<div class="modal-header">
|
|
@@ -430,25 +418,20 @@ export function renderRunDetail(report, lang = DEFAULT_LANG) {
|
|
|
430
418
|
</div>
|
|
431
419
|
</div>
|
|
432
420
|
` : '';
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
<h1>${e(report.id)}</h1>
|
|
437
|
-
${(() => {
|
|
438
|
-
// Run ID 形如 `<name>-YYYYMMDD-HHMM`,把时间戳解出来作为人类可读的副标小字
|
|
439
|
-
// (报告什么时候跑的)。完整 run-id 仍是 H1,与列表页保持一致。
|
|
421
|
+
// v0.30 重设计 — 评分/单测各自独立 title + meta strip(不共享头部)。
|
|
422
|
+
// 顶部只保留极简身份带:返回链接 + run-id pill。完整 H1 + meta tags 移进各自 tab 面板。
|
|
423
|
+
const idStamp = (() => {
|
|
440
424
|
const idMatch = /^.+?-(\d{8})-(\d{4})$/.exec(report.id);
|
|
441
425
|
if (idMatch) {
|
|
442
426
|
const [, ymd, hm] = idMatch;
|
|
443
|
-
|
|
444
|
-
return `<p class="run-id-stamp">${lang === 'zh' ? '运行时间' : 'ran at'}: ${e(stamp)}</p>`;
|
|
427
|
+
return `${ymd.slice(0, 4)}-${ymd.slice(4, 6)}-${ymd.slice(6, 8)} ${hm.slice(0, 2)}:${hm.slice(2, 4)}`;
|
|
445
428
|
}
|
|
446
429
|
return '';
|
|
447
|
-
})()
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|
+
${(() => {
|
|
452
435
|
if (m.noJudge)
|
|
453
436
|
return `<span class="meta-tag">${t('judge', lang)}: none</span>`;
|
|
454
437
|
const list = m.judgeModels ?? [];
|
|
@@ -458,20 +441,19 @@ export function renderRunDetail(report, lang = DEFAULT_LANG) {
|
|
|
458
441
|
return `<span class="meta-tag">${t('judge', lang)}: ${e(`${list[0].executor}:${list[0].model}`)}</span>`;
|
|
459
442
|
return `<span class="meta-tag" title="${t('ensembleDesc', lang)}">${t('judgeModelsLabel', lang)}: ${list.map((j) => e(`${j.executor}:${j.model}`)).join(' · ')}</span>`;
|
|
460
443
|
})()}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
</
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
// 平日 review 不需要看, 复现时再展开。同 fingerprint 多次出现已合并。
|
|
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 = (() => {
|
|
475
457
|
const auditTags = [
|
|
476
458
|
m.judgePromptHash ? `<span class="meta-tag" title="${t('judgePromptHashDesc', lang)}">${t('judgePromptHashLabel', lang)}: <code>${e(m.judgePromptHash)}</code></span>` : '',
|
|
477
459
|
renderRuntimeFingerprintTags(m, lang),
|
|
@@ -479,29 +461,105 @@ export function renderRunDetail(report, lang = DEFAULT_LANG) {
|
|
|
479
461
|
if (!auditTags)
|
|
480
462
|
return '';
|
|
481
463
|
return `<details class="audit-fingerprints"><summary>${lang === 'zh' ? '审计指纹(用于复现校验)' : 'Audit fingerprints (for reproducibility)'}</summary><div class="meta-tags">${auditTags}</div></details>`;
|
|
482
|
-
})()
|
|
483
|
-
|
|
464
|
+
})();
|
|
465
|
+
const blindReveal = m.blind ? `
|
|
484
466
|
<div style="margin:12px 0">
|
|
485
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>
|
|
486
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">
|
|
487
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('')}
|
|
488
470
|
</div>
|
|
489
|
-
</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>
|
|
490
503
|
|
|
491
|
-
|
|
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}
|
|
492
514
|
|
|
493
515
|
<section>${cards}</section>
|
|
516
|
+
<div class="ctx-strip">${variantConfigSection}${renderAgentOverview(variants, summary, lang)}</div>
|
|
517
|
+
${auditFingerprints}
|
|
494
518
|
${methodologyAudit}
|
|
495
519
|
|
|
496
520
|
${renderVarianceComparisons(report.variance, lang, Boolean(report.meta.layeredStats), summary)}
|
|
497
521
|
|
|
498
522
|
${renderAnalysis(report, lang)}
|
|
499
523
|
|
|
500
|
-
${renderAgentOverview(variants, summary, lang)}
|
|
501
|
-
|
|
502
524
|
${renderKnowledgeInteractionSection(report.analysis?.coverage, report.analysis?.gapReports, lang)}
|
|
503
525
|
|
|
504
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>
|
|
505
563
|
|
|
506
564
|
</main>
|
|
507
565
|
`, lang);
|