oh-my-knowledge 0.48.0 → 0.49.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 +50 -18
- package/README.zh.md +55 -23
- package/dist/analysis/coverage-analyzer.d.ts +1 -0
- package/dist/analysis/coverage-analyzer.js +125 -62
- package/dist/analysis/failure-clusterer.js +2 -1
- package/dist/analysis/gap-analyzer.d.ts +2 -2
- package/dist/analysis/gap-analyzer.js +13 -3
- package/dist/analysis/hedging-classifier.d.ts +2 -2
- package/dist/analysis/hedging-classifier.js +3 -4
- package/dist/analysis/report-diagnostics.js +9 -7
- package/dist/analysis/sample-diagnostics.js +6 -6
- package/dist/artifact-graph/doctor.js +15 -7
- package/dist/assets/agent-skills/omk/SKILL.md +27 -7
- package/dist/assets/agent-skills/omk/references/commands.md +18 -17
- package/dist/authoring/evolver.d.ts +10 -6
- package/dist/authoring/evolver.js +496 -83
- package/dist/authoring/generator.d.ts +3 -3
- package/dist/authoring/generator.js +5 -10
- package/dist/authoring/sample-fixer.d.ts +8 -6
- package/dist/authoring/sample-fixer.js +76 -5
- package/dist/cli/commands/doctor.js +31 -14
- package/dist/cli/commands/eval/index.d.ts +3 -0
- package/dist/cli/commands/eval/index.js +163 -17
- package/dist/cli/commands/evolve.d.ts +4 -4
- package/dist/cli/commands/evolve.js +27 -13
- package/dist/cli/commands/init.js +16 -3
- package/dist/cli/commands/observe/inbox.js +28 -21
- package/dist/cli/commands/observe/index.js +20 -11
- package/dist/cli/commands/observe/ingest.d.ts +3 -0
- package/dist/cli/commands/observe/ingest.js +30 -2
- package/dist/cli/commands/sample.d.ts +6 -3
- package/dist/cli/commands/sample.js +72 -68
- package/dist/cli/lib/codex-model-hint.d.ts +9 -0
- package/dist/cli/lib/codex-model-hint.js +45 -0
- package/dist/cli/lib/generation-failure-hint.d.ts +2 -0
- package/dist/cli/lib/generation-failure-hint.js +61 -0
- package/dist/cli/lib/i18n-dict/common.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/common.js +4 -0
- package/dist/cli/lib/i18n-dict/gen.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/gen.js +38 -6
- package/dist/cli/lib/i18n-dict/help.js +6 -6
- package/dist/cli/lib/i18n-dict/init.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/init.js +13 -9
- package/dist/cli/lib/i18n-dict/run.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/run.js +34 -2
- package/dist/cli/lib/llm-failure-classifier.d.ts +2 -0
- package/dist/cli/lib/llm-failure-classifier.js +8 -0
- package/dist/cli/lib/parse-run-config.d.ts +6 -5
- package/dist/cli/lib/parse-run-config.js +16 -9
- package/dist/cli/lib/runtime-defaults.d.ts +21 -0
- package/dist/cli/lib/runtime-defaults.js +79 -0
- package/dist/diagnosis/observe-mapper.js +14 -15
- package/dist/diagnosis/observe-producer.js +3 -1
- package/dist/diagnosis/studio-projection.js +14 -7
- package/dist/diagnosis/types.d.ts +2 -0
- package/dist/diagnosis/types.js +12 -0
- package/dist/doctor/endpoint-rule.js +2 -1
- package/dist/eval-core/artifact-file-names.js +18 -1
- package/dist/eval-core/artifact-index.d.ts +7 -11
- package/dist/eval-core/artifact-index.js +139 -80
- package/dist/eval-core/cache.d.ts +12 -3
- package/dist/eval-core/cache.js +89 -29
- package/dist/eval-core/comparability.js +10 -6
- package/dist/eval-core/evaluation-execution.d.ts +2 -1
- package/dist/eval-core/evaluation-execution.js +122 -37
- package/dist/eval-core/evaluation-job.d.ts +4 -1
- package/dist/eval-core/evaluation-job.js +4 -1
- package/dist/eval-core/evaluation-reporting.d.ts +15 -13
- package/dist/eval-core/evaluation-reporting.js +54 -52
- package/dist/eval-core/execution-strategy.d.ts +2 -0
- package/dist/eval-core/execution-strategy.js +11 -9
- package/dist/eval-core/fact-checker.js +15 -7
- package/dist/eval-core/holdout.js +3 -2
- package/dist/eval-core/judge-independence.d.ts +2 -2
- package/dist/eval-core/mock-hook.cjs +23 -6
- package/dist/eval-core/mocks-runtime.js +30 -8
- package/dist/eval-core/report-document.d.ts +12 -0
- package/dist/eval-core/report-document.js +1151 -0
- package/dist/eval-core/report-extensions.d.ts +4 -0
- package/dist/eval-core/report-extensions.js +500 -0
- package/dist/eval-core/report-file-migration.js +7 -2
- package/dist/eval-core/resume-compatibility.d.ts +31 -0
- package/dist/eval-core/resume-compatibility.js +141 -0
- package/dist/eval-core/sample-fingerprint.d.ts +12 -0
- package/dist/eval-core/sample-fingerprint.js +193 -0
- package/dist/eval-core/schema.js +86 -31
- package/dist/eval-core/verdict.d.ts +8 -4
- package/dist/eval-core/verdict.js +24 -10
- package/dist/eval-workflows/batch-evaluation-workflow.d.ts +2 -1
- package/dist/eval-workflows/batch-evaluation-workflow.js +25 -12
- package/dist/eval-workflows/evaluation-pipeline/preflight-warnings.d.ts +10 -5
- package/dist/eval-workflows/evaluation-pipeline/preflight-warnings.js +58 -21
- package/dist/eval-workflows/evaluation-pipeline/report-finalize.js +3 -1
- package/dist/eval-workflows/evaluation-pipeline/run-state.d.ts +4 -1
- package/dist/eval-workflows/evaluation-pipeline/run-state.js +4 -1
- package/dist/eval-workflows/evaluation-pipeline/test-set-hash.d.ts +6 -5
- package/dist/eval-workflows/evaluation-pipeline/test-set-hash.js +17 -10
- package/dist/eval-workflows/evaluation-pipeline.js +12 -7
- package/dist/eval-workflows/run-evaluation.d.ts +9 -7
- package/dist/eval-workflows/run-evaluation.js +79 -51
- package/dist/executors/anthropic-api.js +65 -9
- package/dist/executors/claude-cli.js +16 -79
- package/dist/executors/claude-protocol.d.ts +28 -0
- package/dist/executors/claude-protocol.js +180 -0
- package/dist/executors/claude-sdk-trace.js +56 -28
- package/dist/executors/claude-sdk.d.ts +1 -0
- package/dist/executors/claude-sdk.js +39 -93
- package/dist/executors/codex-cli-trace.js +166 -31
- package/dist/executors/codex-cli.d.ts +6 -8
- package/dist/executors/codex-cli.js +49 -151
- package/dist/executors/codex-protocol.d.ts +24 -0
- package/dist/executors/codex-protocol.js +234 -0
- package/dist/executors/codex-sdk.js +68 -120
- package/dist/executors/gemini.js +88 -13
- package/dist/executors/index.d.ts +2 -3
- package/dist/executors/index.js +5 -3
- package/dist/executors/openai-api.js +70 -9
- package/dist/executors/runtime-fingerprint.js +88 -11
- package/dist/executors/script-command.d.ts +8 -0
- package/dist/executors/script-command.js +87 -0
- package/dist/executors/script.js +202 -29
- package/dist/executors/shared.d.ts +35 -3
- package/dist/executors/shared.js +113 -15
- package/dist/grading/assertions.d.ts +1 -1
- package/dist/grading/assertions.js +19 -9
- package/dist/grading/diagnostic.d.ts +9 -2
- package/dist/grading/diagnostic.js +25 -2
- package/dist/grading/index.js +10 -4
- package/dist/grading/judge.js +19 -6
- package/dist/grading/layered-scores.d.ts +2 -3
- package/dist/grading/layered-scores.js +2 -3
- package/dist/inputs/load-samples.d.ts +1 -2
- package/dist/inputs/load-samples.js +23 -1
- package/dist/inputs/mcp-resolver.js +6 -3
- package/dist/inputs/sample-document.d.ts +11 -0
- package/dist/inputs/sample-document.js +96 -0
- package/dist/managed/evidence.d.ts +1 -0
- package/dist/managed/evidence.js +1 -1
- package/dist/managed/store.js +200 -91
- package/dist/observability/codex-trace-adapter.d.ts +5 -0
- package/dist/observability/codex-trace-adapter.js +850 -0
- package/dist/observability/experience.d.ts +32 -6
- package/dist/observability/experience.js +2695 -459
- package/dist/observability/feedback-matchers.js +16 -1
- package/dist/observability/inbox-view-model.d.ts +1 -1
- package/dist/observability/inbox-view-model.js +19 -14
- package/dist/observability/inbox.d.ts +7 -1
- package/dist/observability/inbox.js +632 -124
- package/dist/observability/problem-patterns.js +2 -0
- package/dist/observability/review-state.d.ts +6 -0
- package/dist/observability/review-state.js +235 -63
- package/dist/observability/skill-chain-advisories.js +1 -1
- package/dist/observability/skill-chain.js +17 -4
- package/dist/observability/skill-health-analyzer.d.ts +32 -7
- package/dist/observability/skill-health-analyzer.js +194 -121
- package/dist/observability/skill-health-report.d.ts +10 -0
- package/dist/observability/skill-health-report.js +620 -0
- package/dist/observability/soft-standards/constants.d.ts +0 -1
- package/dist/observability/soft-standards/constants.js +0 -1
- package/dist/observability/soft-standards/index.d.ts +1 -1
- package/dist/observability/soft-standards/index.js +1 -1
- package/dist/observability/soft-standards/llm-extractor.js +8 -10
- package/dist/observability/soft-standards/skill-standards-store.d.ts +2 -1
- package/dist/observability/soft-standards/skill-standards-store.js +59 -18
- package/dist/observability/soft-standards/types.d.ts +2 -2
- package/dist/observability/trace-adapter.d.ts +12 -7
- package/dist/observability/trace-adapter.js +11 -9
- package/dist/observability/trace-attribution.d.ts +13 -5
- package/dist/observability/trace-attribution.js +315 -21
- package/dist/observability/trace-ingestion.d.ts +9 -0
- package/dist/observability/trace-ingestion.js +80 -0
- package/dist/observability/trace-ir.d.ts +113 -0
- package/dist/observability/trace-ir.js +87 -0
- package/dist/observability/trace-segmenter.d.ts +19 -6
- package/dist/observability/trace-segmenter.js +377 -196
- package/dist/observability/trace-session-index.d.ts +19 -0
- package/dist/observability/trace-session-index.js +68 -0
- package/dist/observability/trace-source.d.ts +12 -4
- package/dist/observability/trace-source.js +939 -215
- package/dist/renderer/html-renderer.js +37 -6
- package/dist/renderer/icons.js +3 -0
- package/dist/renderer/observation-inbox-renderer.js +208 -90
- package/dist/renderer/skill-detail-renderer.js +452 -109
- package/dist/renderer/skill-health-renderer.js +69 -12
- package/dist/renderer/summary.js +28 -7
- package/dist/renderer/table.js +21 -4
- package/dist/renderer/test-view.d.ts +1 -0
- package/dist/renderer/test-view.js +44 -9
- package/dist/server/indexed-report-store.js +14 -18
- package/dist/server/job-store.js +64 -26
- package/dist/server/report-server.js +190 -78
- package/dist/server/report-store.js +57 -80
- package/dist/server/skill-index.js +143 -49
- package/dist/server/skill-insights.js +44 -5
- package/dist/shared/artifact-graph.d.ts +3 -0
- package/dist/shared/artifact-graph.js +224 -0
- package/dist/shared/assertion-types.d.ts +8 -0
- package/dist/shared/assertion-types.js +46 -0
- package/dist/shared/atomic-json.d.ts +8 -0
- package/dist/shared/atomic-json.js +33 -0
- package/dist/shared/diagnosis-schema.d.ts +9 -0
- package/dist/shared/diagnosis-schema.js +181 -0
- package/dist/shared/doctor-report.d.ts +3 -0
- package/dist/shared/doctor-report.js +103 -0
- package/dist/shared/evaluation-job.d.ts +6 -0
- package/dist/shared/evaluation-job.js +217 -0
- package/dist/shared/executor-result.d.ts +17 -0
- package/dist/shared/executor-result.js +221 -0
- package/dist/shared/file-lock.d.ts +12 -0
- package/dist/shared/file-lock.js +129 -0
- package/dist/shared/json-value.d.ts +5 -0
- package/dist/shared/json-value.js +36 -0
- package/dist/shared/keyed-mutex.d.ts +7 -0
- package/dist/shared/keyed-mutex.js +24 -0
- package/dist/shared/record-count.d.ts +8 -0
- package/dist/shared/record-count.js +43 -0
- package/dist/shared/sample-contract.d.ts +3 -0
- package/dist/shared/sample-contract.js +332 -0
- package/dist/shared/timestamp.d.ts +6 -0
- package/dist/shared/timestamp.js +64 -0
- package/dist/shared/token-usage.d.ts +19 -0
- package/dist/shared/token-usage.js +50 -0
- package/dist/shared/tool-call-status.d.ts +8 -0
- package/dist/shared/tool-call-status.js +28 -0
- package/dist/shared/tool-identity.d.ts +21 -0
- package/dist/shared/tool-identity.js +84 -0
- package/dist/shared/tool-search.js +73 -16
- package/dist/shared/trace-projection.d.ts +5 -0
- package/dist/shared/trace-projection.js +20 -0
- package/dist/shared/trace-source-kind.d.ts +3 -0
- package/dist/shared/trace-source-kind.js +12 -0
- package/dist/types/diagnosis.d.ts +2 -0
- package/dist/types/eval.d.ts +4 -0
- package/dist/types/executor.d.ts +32 -5
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/judge.d.ts +2 -0
- package/dist/types/observability.d.ts +116 -9
- package/dist/types/report.d.ts +58 -6
- package/dist/types/skill-index.d.ts +7 -0
- package/dist/types/trace.d.ts +2 -0
- package/dist/types/trace.js +1 -0
- package/package.json +9 -5
|
@@ -0,0 +1,1151 @@
|
|
|
1
|
+
import { dirname, isAbsolute } from 'node:path';
|
|
2
|
+
import { normalizeRfc3339Timestamp } from '../shared/timestamp.js';
|
|
3
|
+
import { checkedSumTokenCounts } from '../shared/token-usage.js';
|
|
4
|
+
import { isValidMockStats, isValidToolCallInfo, isValidTurnInfo, } from '../shared/executor-result.js';
|
|
5
|
+
import { evaluationRequestsEqual, isValidEvaluationJob, isValidEvaluationRequest, isValidEvaluationRun, } from '../shared/evaluation-job.js';
|
|
6
|
+
import { isToolCallCancelled, isToolCallFailure, isToolCallSuccess, isToolCallUnknown, } from '../shared/tool-call-status.js';
|
|
7
|
+
import { incrementRecordCount } from '../shared/record-count.js';
|
|
8
|
+
import { isAnalysisResult, isSampleSnapshotRecord, isVarianceData, } from './report-extensions.js';
|
|
9
|
+
import { reportFilePath, safeArtifactFileStem } from './artifact-file-names.js';
|
|
10
|
+
function isRecord(value) {
|
|
11
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
function isNonNegativeNumber(value) {
|
|
14
|
+
return typeof value === 'number'
|
|
15
|
+
&& Number.isFinite(value)
|
|
16
|
+
&& value >= 0
|
|
17
|
+
&& value <= Number.MAX_SAFE_INTEGER;
|
|
18
|
+
}
|
|
19
|
+
function isNonNegativeInteger(value) {
|
|
20
|
+
return Number.isSafeInteger(value) && value >= 0;
|
|
21
|
+
}
|
|
22
|
+
function isFiniteNumber(value) {
|
|
23
|
+
return typeof value === 'number'
|
|
24
|
+
&& Number.isFinite(value)
|
|
25
|
+
&& Math.abs(value) <= Number.MAX_SAFE_INTEGER;
|
|
26
|
+
}
|
|
27
|
+
function isOptionalBoolean(value) {
|
|
28
|
+
return value === undefined || typeof value === 'boolean';
|
|
29
|
+
}
|
|
30
|
+
function isRate(value) {
|
|
31
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0 && value <= 1;
|
|
32
|
+
}
|
|
33
|
+
function isScore(value) {
|
|
34
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0 && value <= 5;
|
|
35
|
+
}
|
|
36
|
+
function isCorrelation(value) {
|
|
37
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= -1 && value <= 1;
|
|
38
|
+
}
|
|
39
|
+
function isStringArray(value) {
|
|
40
|
+
return Array.isArray(value)
|
|
41
|
+
&& value.length > 0
|
|
42
|
+
&& value.every((entry) => typeof entry === 'string' && entry.length > 0);
|
|
43
|
+
}
|
|
44
|
+
function isStringRecord(value) {
|
|
45
|
+
return isRecord(value)
|
|
46
|
+
&& Object.values(value).every((entry) => typeof entry === 'string');
|
|
47
|
+
}
|
|
48
|
+
function isSampleHashRecord(value) {
|
|
49
|
+
return isRecord(value)
|
|
50
|
+
&& Object.keys(value).every((key) => key.length > 0)
|
|
51
|
+
&& Object.values(value).every((entry) => typeof entry === 'string' && /^[a-f0-9]{12}$/.test(entry));
|
|
52
|
+
}
|
|
53
|
+
function isRuntimePackage(value) {
|
|
54
|
+
return isRecord(value)
|
|
55
|
+
&& typeof value.name === 'string'
|
|
56
|
+
&& value.name.length > 0
|
|
57
|
+
&& (value.version === undefined || typeof value.version === 'string')
|
|
58
|
+
&& (value.error === undefined || typeof value.error === 'string');
|
|
59
|
+
}
|
|
60
|
+
function isRuntimeFingerprint(value) {
|
|
61
|
+
if (!isRecord(value)
|
|
62
|
+
|| typeof value.executor !== 'string'
|
|
63
|
+
|| value.executor.length === 0
|
|
64
|
+
|| typeof value.model !== 'string'
|
|
65
|
+
|| value.model.length === 0
|
|
66
|
+
|| !['agent-cli', 'agent-sdk', 'api', 'script', 'unknown'].includes(String(value.runtimeKind))
|
|
67
|
+
|| typeof value.fingerprint !== 'string'
|
|
68
|
+
|| value.fingerprint.length === 0
|
|
69
|
+
|| !isRecord(value.capabilities)
|
|
70
|
+
|| !['native', 'prepended', 'none', 'unknown'].includes(String(value.capabilities.systemPrompt))
|
|
71
|
+
|| !['reported', 'not-reported', 'unknown'].includes(String(value.capabilities.costUSD))
|
|
72
|
+
|| !['native', 'best-effort', 'none', 'unknown'].includes(String(value.capabilities.trace))
|
|
73
|
+
|| !['full', 'full-no-partial', 'cwd-only', 'none', 'unknown'].includes(String(value.capabilities.skillIsolation))
|
|
74
|
+
|| (value.sdk !== undefined && !isRuntimePackage(value.sdk)))
|
|
75
|
+
return false;
|
|
76
|
+
if (value.binary === undefined)
|
|
77
|
+
return true;
|
|
78
|
+
return isRecord(value.binary)
|
|
79
|
+
&& typeof value.binary.name === 'string'
|
|
80
|
+
&& value.binary.name.length > 0
|
|
81
|
+
&& ['path', 'bundled', 'none', 'unknown'].includes(String(value.binary.source))
|
|
82
|
+
&& (value.binary.version === undefined || typeof value.binary.version === 'string')
|
|
83
|
+
&& (value.binary.path === undefined || typeof value.binary.path === 'string')
|
|
84
|
+
&& (value.binary.contentHash === undefined
|
|
85
|
+
|| (typeof value.binary.contentHash === 'string'
|
|
86
|
+
&& /^[a-f0-9]{64}$/.test(value.binary.contentHash)))
|
|
87
|
+
&& (value.binary.package === undefined || isRuntimePackage(value.binary.package))
|
|
88
|
+
&& (value.binary.error === undefined || typeof value.binary.error === 'string');
|
|
89
|
+
}
|
|
90
|
+
function isDiagnosticConfig(value) {
|
|
91
|
+
if (!isRecord(value) || typeof value.enabled !== 'boolean')
|
|
92
|
+
return false;
|
|
93
|
+
if (!value.enabled) {
|
|
94
|
+
return value.executor === undefined
|
|
95
|
+
&& value.model === undefined
|
|
96
|
+
&& value.runtime === undefined
|
|
97
|
+
&& value.promptHash === undefined;
|
|
98
|
+
}
|
|
99
|
+
return typeof value.executor === 'string'
|
|
100
|
+
&& value.executor.length > 0
|
|
101
|
+
&& typeof value.model === 'string'
|
|
102
|
+
&& value.model.length > 0
|
|
103
|
+
&& isRuntimeFingerprint(value.runtime)
|
|
104
|
+
&& value.runtime.executor === value.executor
|
|
105
|
+
&& value.runtime.model === value.model
|
|
106
|
+
&& typeof value.promptHash === 'string'
|
|
107
|
+
&& /^[a-f0-9]{12}$/.test(value.promptHash);
|
|
108
|
+
}
|
|
109
|
+
function isOptionalNonNegativeInteger(value) {
|
|
110
|
+
return value === undefined || isNonNegativeInteger(value);
|
|
111
|
+
}
|
|
112
|
+
function isOptionalStringArray(value) {
|
|
113
|
+
return value === undefined
|
|
114
|
+
|| (Array.isArray(value)
|
|
115
|
+
&& value.every((entry) => typeof entry === 'string' && entry.length > 0));
|
|
116
|
+
}
|
|
117
|
+
function isCountRecord(value) {
|
|
118
|
+
return isRecord(value)
|
|
119
|
+
&& Object.keys(value).every((key) => key.length > 0)
|
|
120
|
+
&& Object.values(value).every(isNonNegativeInteger);
|
|
121
|
+
}
|
|
122
|
+
function traceProjectionIsConsistent(value) {
|
|
123
|
+
for (const field of [
|
|
124
|
+
'fullNumTurns',
|
|
125
|
+
'numSubAgents',
|
|
126
|
+
'assistantTurns',
|
|
127
|
+
'toolTurns',
|
|
128
|
+
'numToolCalls',
|
|
129
|
+
'numToolFailures',
|
|
130
|
+
'numToolCancelled',
|
|
131
|
+
'numToolUnknown',
|
|
132
|
+
]) {
|
|
133
|
+
if (!isOptionalNonNegativeInteger(value[field]))
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
if ((value.toolSuccessRate !== undefined && !isRate(value.toolSuccessRate))
|
|
137
|
+
|| (value.traceCoverage !== undefined && !isRate(value.traceCoverage))
|
|
138
|
+
|| !isOptionalStringArray(value.toolNames)
|
|
139
|
+
|| (value.toolDistribution !== undefined && !isCountRecord(value.toolDistribution))
|
|
140
|
+
|| (value.turns !== undefined && (!Array.isArray(value.turns)
|
|
141
|
+
|| !value.turns.every(isValidTurnInfo)))
|
|
142
|
+
|| (value.toolCalls !== undefined && (!Array.isArray(value.toolCalls)
|
|
143
|
+
|| !value.toolCalls.every(isValidToolCallInfo)))
|
|
144
|
+
|| (value.mockStats !== undefined && !isValidMockStats(value.mockStats)))
|
|
145
|
+
return false;
|
|
146
|
+
if (Array.isArray(value.turns)) {
|
|
147
|
+
const assistantTurns = value.turns.filter((turn) => isRecord(turn) && turn.role === 'assistant').length;
|
|
148
|
+
const toolTurns = value.turns.filter((turn) => isRecord(turn) && turn.role === 'tool').length;
|
|
149
|
+
if ((value.assistantTurns !== undefined && value.assistantTurns !== assistantTurns)
|
|
150
|
+
|| (value.toolTurns !== undefined && value.toolTurns !== toolTurns))
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
if (!Array.isArray(value.toolCalls)) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
const toolCalls = value.toolCalls;
|
|
157
|
+
if (value.numToolCalls !== undefined && value.numToolCalls !== toolCalls.length)
|
|
158
|
+
return false;
|
|
159
|
+
const failures = toolCalls.filter(isToolCallFailure).length;
|
|
160
|
+
const cancelled = toolCalls.filter(isToolCallCancelled).length;
|
|
161
|
+
const unknown = toolCalls.filter(isToolCallUnknown).length;
|
|
162
|
+
const successes = toolCalls.filter(isToolCallSuccess).length;
|
|
163
|
+
if ((value.numToolFailures !== undefined && value.numToolFailures !== failures)
|
|
164
|
+
|| (value.numToolCancelled !== undefined && value.numToolCancelled !== cancelled)
|
|
165
|
+
|| (value.numToolUnknown !== undefined && value.numToolUnknown !== unknown))
|
|
166
|
+
return false;
|
|
167
|
+
const comparable = successes + failures;
|
|
168
|
+
const expectedRate = comparable > 0
|
|
169
|
+
? Number((successes / comparable).toFixed(2))
|
|
170
|
+
: undefined;
|
|
171
|
+
if (value.toolSuccessRate !== undefined && value.toolSuccessRate !== expectedRate)
|
|
172
|
+
return false;
|
|
173
|
+
const expectedNames = [...new Set(toolCalls.map((call) => call.tool))];
|
|
174
|
+
if (value.toolNames !== undefined) {
|
|
175
|
+
const toolNames = value.toolNames;
|
|
176
|
+
if (toolNames.length !== expectedNames.length
|
|
177
|
+
|| !expectedNames.every((name) => toolNames.includes(name)))
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
if (value.toolDistribution === undefined)
|
|
181
|
+
return true;
|
|
182
|
+
if (!isCountRecord(value.toolDistribution))
|
|
183
|
+
return false;
|
|
184
|
+
const toolDistribution = value.toolDistribution;
|
|
185
|
+
const expectedDistribution = Object.create(null);
|
|
186
|
+
for (const call of toolCalls) {
|
|
187
|
+
incrementRecordCount(expectedDistribution, call.tool);
|
|
188
|
+
}
|
|
189
|
+
return hasExactKeys(toolDistribution, Object.keys(expectedDistribution))
|
|
190
|
+
&& Object.entries(expectedDistribution).every(([name, count]) => toolDistribution[name] === count);
|
|
191
|
+
}
|
|
192
|
+
function isJudgeModels(value) {
|
|
193
|
+
return Array.isArray(value)
|
|
194
|
+
&& value.length > 0
|
|
195
|
+
&& value.every((entry) => isRecord(entry)
|
|
196
|
+
&& typeof entry.executor === 'string'
|
|
197
|
+
&& entry.executor.length > 0
|
|
198
|
+
&& typeof entry.model === 'string'
|
|
199
|
+
&& entry.model.length > 0
|
|
200
|
+
&& (entry.runtime === undefined
|
|
201
|
+
|| (isRuntimeFingerprint(entry.runtime)
|
|
202
|
+
&& entry.runtime.executor === entry.executor
|
|
203
|
+
&& entry.runtime.model === entry.model)));
|
|
204
|
+
}
|
|
205
|
+
function isSummaryJudgeModels(value) {
|
|
206
|
+
return Array.isArray(value)
|
|
207
|
+
&& value.length > 0
|
|
208
|
+
&& value.every((entry) => isRecord(entry)
|
|
209
|
+
&& typeof entry.executor === 'string'
|
|
210
|
+
&& typeof entry.model === 'string'
|
|
211
|
+
&& entry.model.length > 0);
|
|
212
|
+
}
|
|
213
|
+
function isBaseJudgeAgreement(value) {
|
|
214
|
+
return isRecord(value)
|
|
215
|
+
&& (value.pearson === undefined || isCorrelation(value.pearson))
|
|
216
|
+
&& isNonNegativeNumber(value.meanAbsDiff)
|
|
217
|
+
&& value.meanAbsDiff <= 4
|
|
218
|
+
&& isNonNegativeInteger(value.pairCount);
|
|
219
|
+
}
|
|
220
|
+
function isSummaryJudgeAgreement(value) {
|
|
221
|
+
return isBaseJudgeAgreement(value)
|
|
222
|
+
&& isRecord(value)
|
|
223
|
+
&& isNonNegativeInteger(value.sampleCount);
|
|
224
|
+
}
|
|
225
|
+
function isBootstrapCI(value) {
|
|
226
|
+
return isRecord(value)
|
|
227
|
+
&& isScore(value.low)
|
|
228
|
+
&& isScore(value.high)
|
|
229
|
+
&& isScore(value.estimate)
|
|
230
|
+
&& value.low <= value.estimate
|
|
231
|
+
&& value.estimate <= value.high
|
|
232
|
+
&& isNonNegativeInteger(value.samples)
|
|
233
|
+
&& value.samples > 0;
|
|
234
|
+
}
|
|
235
|
+
function isDiffBootstrapCI(value) {
|
|
236
|
+
if (!isRecord(value)
|
|
237
|
+
|| !isFiniteNumber(value.low)
|
|
238
|
+
|| !isFiniteNumber(value.high)
|
|
239
|
+
|| !isFiniteNumber(value.estimate)
|
|
240
|
+
|| value.low > value.estimate
|
|
241
|
+
|| value.estimate > value.high
|
|
242
|
+
|| !isNonNegativeInteger(value.samples)
|
|
243
|
+
|| value.samples <= 0
|
|
244
|
+
|| typeof value.significant !== 'boolean')
|
|
245
|
+
return false;
|
|
246
|
+
return value.significant === (value.low > 0 || value.high < 0);
|
|
247
|
+
}
|
|
248
|
+
function isPairComparisons(value, variants) {
|
|
249
|
+
if (!Array.isArray(value))
|
|
250
|
+
return false;
|
|
251
|
+
const pairs = new Set();
|
|
252
|
+
return value.every((entry) => {
|
|
253
|
+
if (!isRecord(entry)
|
|
254
|
+
|| typeof entry.control !== 'string'
|
|
255
|
+
|| typeof entry.treatment !== 'string'
|
|
256
|
+
|| entry.control === entry.treatment
|
|
257
|
+
|| !variants.includes(entry.control)
|
|
258
|
+
|| !variants.includes(entry.treatment)
|
|
259
|
+
|| (entry.diffBootstrapCI !== undefined
|
|
260
|
+
&& !isDiffBootstrapCI(entry.diffBootstrapCI))
|
|
261
|
+
|| (entry.alpha !== undefined
|
|
262
|
+
&& (!isRate(entry.alpha) || entry.alpha <= 0)))
|
|
263
|
+
return false;
|
|
264
|
+
const key = `${entry.control}\u0000${entry.treatment}`;
|
|
265
|
+
if (pairs.has(key))
|
|
266
|
+
return false;
|
|
267
|
+
pairs.add(key);
|
|
268
|
+
return true;
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function isBudget(value) {
|
|
272
|
+
return isRecord(value)
|
|
273
|
+
&& ['totalUSD', 'perSampleUSD', 'perSampleMs'].every((field) => value[field] === undefined || isNonNegativeNumber(value[field]));
|
|
274
|
+
}
|
|
275
|
+
function isHumanAgreement(value, variants) {
|
|
276
|
+
if (!isRecord(value)
|
|
277
|
+
|| !isFiniteNumber(value.alpha)
|
|
278
|
+
|| value.alpha > 1
|
|
279
|
+
|| !isRecord(value.alphaCI)
|
|
280
|
+
|| !isFiniteNumber(value.alphaCI.low)
|
|
281
|
+
|| !isFiniteNumber(value.alphaCI.high)
|
|
282
|
+
|| !isFiniteNumber(value.alphaCI.estimate)
|
|
283
|
+
|| value.alphaCI.low > value.alphaCI.estimate
|
|
284
|
+
|| value.alphaCI.estimate > value.alphaCI.high
|
|
285
|
+
|| !isNonNegativeInteger(value.alphaCI.samples)
|
|
286
|
+
|| value.alphaCI.samples <= 0
|
|
287
|
+
|| !isCorrelation(value.weightedKappa)
|
|
288
|
+
|| !isCorrelation(value.pearson)
|
|
289
|
+
|| !isNonNegativeInteger(value.sampleCount)
|
|
290
|
+
|| typeof value.variant !== 'string'
|
|
291
|
+
|| !variants.includes(value.variant)
|
|
292
|
+
|| typeof value.goldAnnotator !== 'string'
|
|
293
|
+
|| typeof value.goldVersion !== 'string'
|
|
294
|
+
|| (value.contaminationWarning !== undefined && typeof value.contaminationWarning !== 'string')
|
|
295
|
+
|| !isNonNegativeInteger(value.missingCount)
|
|
296
|
+
|| !isNonNegativeInteger(value.unscoredCount))
|
|
297
|
+
return false;
|
|
298
|
+
return Math.abs(value.alpha - value.alphaCI.estimate) <= 1e-9;
|
|
299
|
+
}
|
|
300
|
+
function isSkillIsolation(value, variants) {
|
|
301
|
+
return isRecord(value)
|
|
302
|
+
&& hasExactKeys(value, variants)
|
|
303
|
+
&& Object.values(value).every((entry) => entry === null
|
|
304
|
+
|| (Array.isArray(entry)
|
|
305
|
+
&& entry.every((skill) => typeof skill === 'string' && skill.length > 0)));
|
|
306
|
+
}
|
|
307
|
+
function isGitInfo(value) {
|
|
308
|
+
return isRecord(value)
|
|
309
|
+
&& typeof value.commit === 'string'
|
|
310
|
+
&& typeof value.commitShort === 'string'
|
|
311
|
+
&& typeof value.branch === 'string'
|
|
312
|
+
&& typeof value.dirty === 'boolean';
|
|
313
|
+
}
|
|
314
|
+
function isVariantConfigs(value, variants, skillIsolation, request) {
|
|
315
|
+
if (!Array.isArray(value) || value.length !== variants.length)
|
|
316
|
+
return false;
|
|
317
|
+
const configs = new Map();
|
|
318
|
+
for (const entry of value) {
|
|
319
|
+
if (!isRecord(entry)
|
|
320
|
+
|| typeof entry.variant !== 'string'
|
|
321
|
+
|| !variants.includes(entry.variant)
|
|
322
|
+
|| configs.has(entry.variant)
|
|
323
|
+
|| !['baseline', 'skill', 'prompt', 'agent', 'workflow'].includes(String(entry.artifactKind))
|
|
324
|
+
|| ![
|
|
325
|
+
'baseline',
|
|
326
|
+
'variant-name',
|
|
327
|
+
'file-path',
|
|
328
|
+
'git',
|
|
329
|
+
'inline',
|
|
330
|
+
'custom',
|
|
331
|
+
].includes(String(entry.artifactSource))
|
|
332
|
+
|| ![
|
|
333
|
+
'baseline',
|
|
334
|
+
'system-prompt',
|
|
335
|
+
'user-prompt',
|
|
336
|
+
'agent-session',
|
|
337
|
+
'workflow-session',
|
|
338
|
+
].includes(String(entry.executionStrategy))
|
|
339
|
+
|| ![
|
|
340
|
+
'baseline',
|
|
341
|
+
'runtime-context-only',
|
|
342
|
+
'artifact-injection',
|
|
343
|
+
].includes(String(entry.experimentType))
|
|
344
|
+
|| (entry.experimentRole !== 'control' && entry.experimentRole !== 'treatment')
|
|
345
|
+
|| typeof entry.hasArtifactContent !== 'boolean'
|
|
346
|
+
|| (entry.cwd !== null && typeof entry.cwd !== 'string')
|
|
347
|
+
|| (entry.locator !== undefined && typeof entry.locator !== 'string')
|
|
348
|
+
|| (entry.ref !== undefined && typeof entry.ref !== 'string')
|
|
349
|
+
|| (entry.resolvedCommit !== undefined && typeof entry.resolvedCommit !== 'string')
|
|
350
|
+
|| (entry.allowedSkills !== undefined
|
|
351
|
+
&& (!Array.isArray(entry.allowedSkills)
|
|
352
|
+
|| !entry.allowedSkills.every((skill) => typeof skill === 'string' && skill.length > 0)
|
|
353
|
+
|| new Set(entry.allowedSkills).size !== entry.allowedSkills.length)))
|
|
354
|
+
return false;
|
|
355
|
+
configs.set(entry.variant, entry);
|
|
356
|
+
}
|
|
357
|
+
if (!variants.every((variant) => configs.has(variant)))
|
|
358
|
+
return false;
|
|
359
|
+
if (isRecord(skillIsolation)) {
|
|
360
|
+
for (const variant of variants) {
|
|
361
|
+
const configured = configs.get(variant)?.allowedSkills;
|
|
362
|
+
const isolated = skillIsolation[variant];
|
|
363
|
+
if (isolated === null
|
|
364
|
+
? configured !== undefined
|
|
365
|
+
: JSON.stringify(configured) !== JSON.stringify(isolated))
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (isValidEvaluationRequest(request)) {
|
|
370
|
+
for (const artifact of request.artifacts) {
|
|
371
|
+
const configured = configs.get(artifact.name);
|
|
372
|
+
if (!configured
|
|
373
|
+
|| configured.artifactKind !== artifact.kind
|
|
374
|
+
|| configured.artifactSource !== artifact.source)
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
function judgeConfigsMatch(request, judgeModels) {
|
|
381
|
+
return request.judgeModels.length === judgeModels.length
|
|
382
|
+
&& request.judgeModels.every((judge, index) => judge.executor === judgeModels[index]?.executor
|
|
383
|
+
&& judge.model === judgeModels[index]?.model);
|
|
384
|
+
}
|
|
385
|
+
function embeddedLifecycleIsValid(value, expectedReportId, requireBatch = false) {
|
|
386
|
+
const request = value.request;
|
|
387
|
+
const run = value.run;
|
|
388
|
+
const job = value.job;
|
|
389
|
+
if ((request !== undefined && !isValidEvaluationRequest(request))
|
|
390
|
+
|| (run !== undefined && !isValidEvaluationRun(run, expectedReportId))
|
|
391
|
+
|| (job !== undefined && !isValidEvaluationJob(job)))
|
|
392
|
+
return false;
|
|
393
|
+
const reportTimestamp = normalizeRfc3339Timestamp(value.timestamp);
|
|
394
|
+
if (!reportTimestamp)
|
|
395
|
+
return false;
|
|
396
|
+
const reportTimestampMs = Date.parse(reportTimestamp);
|
|
397
|
+
const typedRequest = request === undefined ? undefined : request;
|
|
398
|
+
const typedRun = run === undefined ? undefined : run;
|
|
399
|
+
const typedJob = job === undefined ? undefined : job;
|
|
400
|
+
const judgeModels = value.judgeModels;
|
|
401
|
+
if (typedRequest !== undefined
|
|
402
|
+
&& (typedRequest.model !== value.model
|
|
403
|
+
|| typedRequest.executor !== value.executor
|
|
404
|
+
|| typedRequest.noJudge !== (value.noJudge === true)
|
|
405
|
+
|| typedRequest.effort !== value.effort
|
|
406
|
+
|| !judgeConfigsMatch(typedRequest, judgeModels)
|
|
407
|
+
|| (requireBatch && typedRequest.batch !== true)))
|
|
408
|
+
return false;
|
|
409
|
+
if (typedRun !== undefined
|
|
410
|
+
&& (typedRun.status !== 'succeeded'
|
|
411
|
+
|| typedRun.finishedAt === undefined
|
|
412
|
+
|| Date.parse(typedRun.finishedAt) > reportTimestampMs))
|
|
413
|
+
return false;
|
|
414
|
+
if (typedJob !== undefined
|
|
415
|
+
&& (typedJob.status !== 'succeeded'
|
|
416
|
+
|| typedJob.runId !== expectedReportId
|
|
417
|
+
|| typedJob.resultReportId !== expectedReportId
|
|
418
|
+
|| typedJob.finishedAt === undefined
|
|
419
|
+
|| Date.parse(typedJob.finishedAt) > reportTimestampMs
|
|
420
|
+
|| typedJob.request.model !== value.model
|
|
421
|
+
|| typedJob.request.executor !== value.executor
|
|
422
|
+
|| typedJob.request.noJudge !== (value.noJudge === true)
|
|
423
|
+
|| typedJob.request.effort !== value.effort
|
|
424
|
+
|| !judgeConfigsMatch(typedJob.request, judgeModels)
|
|
425
|
+
|| (requireBatch && typedJob.request.batch !== true)))
|
|
426
|
+
return false;
|
|
427
|
+
if (typedRequest !== undefined
|
|
428
|
+
&& typedJob !== undefined
|
|
429
|
+
&& !evaluationRequestsEqual(typedRequest, typedJob.request))
|
|
430
|
+
return false;
|
|
431
|
+
if (typedRun !== undefined
|
|
432
|
+
&& typedJob !== undefined
|
|
433
|
+
&& (typedRun.runId !== typedJob.runId
|
|
434
|
+
|| typedRun.startedAt !== typedJob.startedAt
|
|
435
|
+
|| typedRun.finishedAt !== typedJob.finishedAt))
|
|
436
|
+
return false;
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
function isEvolveMetadata(value, variants) {
|
|
440
|
+
if (!isRecord(value)
|
|
441
|
+
|| typeof value.skillName !== 'string'
|
|
442
|
+
|| value.skillName.length === 0
|
|
443
|
+
|| (value.skillPath !== undefined
|
|
444
|
+
&& typeof value.skillPath !== 'string')
|
|
445
|
+
|| (value.processCostUSD !== undefined
|
|
446
|
+
&& !isNonNegativeNumber(value.processCostUSD))
|
|
447
|
+
|| !isOptionalBoolean(value.processCostReported))
|
|
448
|
+
return false;
|
|
449
|
+
if (value.sourceReports === undefined)
|
|
450
|
+
return true;
|
|
451
|
+
if (!Array.isArray(value.sourceReports)
|
|
452
|
+
|| value.sourceReports.length !== variants.length)
|
|
453
|
+
return false;
|
|
454
|
+
const reportIds = new Set();
|
|
455
|
+
let previousRound = -1;
|
|
456
|
+
return value.sourceReports.every((source, index) => {
|
|
457
|
+
if (!isRecord(source)
|
|
458
|
+
|| !isNonNegativeInteger(source.round)
|
|
459
|
+
|| source.round <= previousRound
|
|
460
|
+
|| typeof source.accepted !== 'boolean'
|
|
461
|
+
|| !isCanonicalReportId(source.reportId)
|
|
462
|
+
|| reportIds.has(source.reportId)
|
|
463
|
+
|| typeof source.variant !== 'string'
|
|
464
|
+
|| source.variant.length === 0
|
|
465
|
+
|| variants[index] !== `round-${source.round}`)
|
|
466
|
+
return false;
|
|
467
|
+
previousRound = source.round;
|
|
468
|
+
reportIds.add(source.reportId);
|
|
469
|
+
return true;
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
function reportMetaExtensionsAreValid(value, expectedReportId) {
|
|
473
|
+
const variants = value.variants;
|
|
474
|
+
const isCurrentSchema = typeof value.schemaVersion === 'number'
|
|
475
|
+
&& value.schemaVersion >= 4;
|
|
476
|
+
if ((value.effort !== undefined
|
|
477
|
+
&& !['low', 'medium', 'high', 'xhigh', 'max'].includes(String(value.effort)))
|
|
478
|
+
|| !isOptionalNonNegativeInteger(value.schemaVersion)
|
|
479
|
+
|| (value.sampleHashes !== undefined && !isStringRecord(value.sampleHashes))
|
|
480
|
+
|| (value.judgePromptHash !== undefined && (typeof value.judgePromptHash !== 'string'
|
|
481
|
+
|| value.judgePromptHash.length === 0))
|
|
482
|
+
|| (value.diagnostic !== undefined && !isDiagnosticConfig(value.diagnostic))
|
|
483
|
+
|| (value.executorRuntime !== undefined
|
|
484
|
+
&& (!isRuntimeFingerprint(value.executorRuntime)
|
|
485
|
+
|| value.executorRuntime.executor !== value.executor
|
|
486
|
+
|| value.executorRuntime.model !== value.model))
|
|
487
|
+
|| (value.executorRuntimes !== undefined
|
|
488
|
+
&& (!isRecord(value.executorRuntimes)
|
|
489
|
+
|| !hasExactKeys(value.executorRuntimes, variants)
|
|
490
|
+
|| !Object.values(value.executorRuntimes).every(isRuntimeFingerprint)
|
|
491
|
+
|| Object.values(value.executorRuntimes).some((runtime) => isRecord(runtime)
|
|
492
|
+
&& (runtime.executor !== value.executor
|
|
493
|
+
|| runtime.model !== value.model))))
|
|
494
|
+
|| !isOptionalNonNegativeInteger(value.judgeRepeat)
|
|
495
|
+
|| (value.judgeRepeat === 0)
|
|
496
|
+
|| !isOptionalBoolean(value.noJudge)
|
|
497
|
+
|| (value.noJudge === true
|
|
498
|
+
&& value.judgeModels.some((judge) => judge.runtime !== undefined))
|
|
499
|
+
|| (isCurrentSchema
|
|
500
|
+
&& value.noJudge !== true
|
|
501
|
+
&& value.judgeModels.some((judge) => judge.runtime === undefined))
|
|
502
|
+
|| (value.evaluationFramework !== undefined
|
|
503
|
+
&& !['t-test', 'bootstrap', 'both'].includes(String(value.evaluationFramework)))
|
|
504
|
+
|| (value.pairComparisons !== undefined
|
|
505
|
+
&& !isPairComparisons(value.pairComparisons, variants))
|
|
506
|
+
|| (value.debiasMode !== undefined
|
|
507
|
+
&& (!Array.isArray(value.debiasMode)
|
|
508
|
+
|| new Set(value.debiasMode).size !== value.debiasMode.length
|
|
509
|
+
|| !value.debiasMode.every((mode) => mode === 'length' || mode === 'position')))
|
|
510
|
+
|| !isOptionalBoolean(value.budgetExhausted)
|
|
511
|
+
|| (value.budget !== undefined && !isBudget(value.budget))
|
|
512
|
+
|| (value.humanAgreement !== undefined
|
|
513
|
+
&& !isHumanAgreement(value.humanAgreement, variants))
|
|
514
|
+
|| (value.skillIsolation !== undefined
|
|
515
|
+
&& !isSkillIsolation(value.skillIsolation, variants))
|
|
516
|
+
|| (value.variantConfigs !== undefined
|
|
517
|
+
&& !isVariantConfigs(value.variantConfigs, variants, value.skillIsolation, value.request))
|
|
518
|
+
|| (value.gitInfo !== undefined
|
|
519
|
+
&& value.gitInfo !== null
|
|
520
|
+
&& !isGitInfo(value.gitInfo))
|
|
521
|
+
|| !isOptionalBoolean(value.layeredStats)
|
|
522
|
+
|| (value.evolve !== undefined
|
|
523
|
+
&& !isEvolveMetadata(value.evolve, variants)))
|
|
524
|
+
return false;
|
|
525
|
+
return embeddedLifecycleIsValid(value, expectedReportId);
|
|
526
|
+
}
|
|
527
|
+
function hasExactKeys(value, expected) {
|
|
528
|
+
const keys = Object.keys(value);
|
|
529
|
+
return keys.length === expected.length && expected.every((key) => Object.hasOwn(value, key));
|
|
530
|
+
}
|
|
531
|
+
function isVariantSummary(value) {
|
|
532
|
+
if (!isRecord(value))
|
|
533
|
+
return false;
|
|
534
|
+
const integerFields = ['totalSamples', 'successCount', 'errorCount'];
|
|
535
|
+
const numericFields = [
|
|
536
|
+
'errorRate',
|
|
537
|
+
'avgDurationMs',
|
|
538
|
+
'avgInputTokens',
|
|
539
|
+
'avgOutputTokens',
|
|
540
|
+
'avgTotalTokens',
|
|
541
|
+
'totalCostUSD',
|
|
542
|
+
'totalExecCostUSD',
|
|
543
|
+
'totalJudgeCostUSD',
|
|
544
|
+
'avgCostPerSample',
|
|
545
|
+
'avgNumTurns',
|
|
546
|
+
];
|
|
547
|
+
if (!integerFields.every((field) => isNonNegativeInteger(value[field]))
|
|
548
|
+
|| !numericFields.every((field) => isNonNegativeNumber(value[field]))
|
|
549
|
+
|| value.successCount + value.errorCount !== value.totalSamples
|
|
550
|
+
|| value.errorRate > 100
|
|
551
|
+
|| !isOptionalBoolean(value.execCostReported)
|
|
552
|
+
|| !isOptionalBoolean(value.judgeCostReported)
|
|
553
|
+
|| (value.totalDiagnosticCostUSD !== undefined
|
|
554
|
+
&& !isNonNegativeNumber(value.totalDiagnosticCostUSD))
|
|
555
|
+
|| (value.tokenUsageCoverageRate !== undefined
|
|
556
|
+
&& !isRate(value.tokenUsageCoverageRate)))
|
|
557
|
+
return false;
|
|
558
|
+
const optionalMetrics = [
|
|
559
|
+
'avgFullNumTurns',
|
|
560
|
+
'avgNumSubAgents',
|
|
561
|
+
'avgAssistantTurns',
|
|
562
|
+
'avgToolTurns',
|
|
563
|
+
'avgToolCalls',
|
|
564
|
+
'avgToolFailures',
|
|
565
|
+
'avgToolCancelled',
|
|
566
|
+
'avgToolUnknown',
|
|
567
|
+
'scoreStddev',
|
|
568
|
+
'scoreCV',
|
|
569
|
+
];
|
|
570
|
+
const optionalScores = [
|
|
571
|
+
'avgFactScore',
|
|
572
|
+
'avgBehaviorScore',
|
|
573
|
+
'avgJudgeScore',
|
|
574
|
+
'avgCompositeScore',
|
|
575
|
+
'minCompositeScore',
|
|
576
|
+
'maxCompositeScore',
|
|
577
|
+
'avgLlmScore',
|
|
578
|
+
'minLlmScore',
|
|
579
|
+
'maxLlmScore',
|
|
580
|
+
];
|
|
581
|
+
if (optionalMetrics.some((field) => value[field] !== undefined && !isNonNegativeNumber(value[field]))
|
|
582
|
+
|| optionalScores.some((field) => value[field] !== undefined && !isScore(value[field]))
|
|
583
|
+
|| (value.avgAssertionScore !== undefined && !isScore(value.avgAssertionScore))
|
|
584
|
+
|| (value.avgFactVerifiedRate !== undefined && !isRate(value.avgFactVerifiedRate))
|
|
585
|
+
|| (value.toolSuccessRate !== undefined && !isRate(value.toolSuccessRate))
|
|
586
|
+
|| (value.traceCoverageRate !== undefined && !isRate(value.traceCoverageRate))
|
|
587
|
+
|| (value.toolDistribution !== undefined && (!isCountRecord(value.toolDistribution)
|
|
588
|
+
|| finiteSum(Object.values(value.toolDistribution)) === undefined))
|
|
589
|
+
|| (value.judgeAgreement !== undefined && !isSummaryJudgeAgreement(value.judgeAgreement))
|
|
590
|
+
|| (value.judgeModels !== undefined && !isSummaryJudgeModels(value.judgeModels))
|
|
591
|
+
|| (value.bootstrapCI !== undefined && !isBootstrapCI(value.bootstrapCI)))
|
|
592
|
+
return false;
|
|
593
|
+
if (typeof value.minCompositeScore === 'number'
|
|
594
|
+
&& typeof value.avgCompositeScore === 'number'
|
|
595
|
+
&& typeof value.maxCompositeScore === 'number'
|
|
596
|
+
&& (value.minCompositeScore > value.avgCompositeScore
|
|
597
|
+
|| value.avgCompositeScore > value.maxCompositeScore))
|
|
598
|
+
return false;
|
|
599
|
+
if (typeof value.minLlmScore === 'number'
|
|
600
|
+
&& typeof value.avgLlmScore === 'number'
|
|
601
|
+
&& typeof value.maxLlmScore === 'number'
|
|
602
|
+
&& (value.minLlmScore > value.avgLlmScore
|
|
603
|
+
|| value.avgLlmScore > value.maxLlmScore))
|
|
604
|
+
return false;
|
|
605
|
+
const totalSamples = value.totalSamples;
|
|
606
|
+
const errorCount = value.errorCount;
|
|
607
|
+
const expectedErrorRate = totalSamples > 0
|
|
608
|
+
? Number((errorCount / totalSamples * 100).toFixed(1))
|
|
609
|
+
: 0;
|
|
610
|
+
return value.errorRate === expectedErrorRate;
|
|
611
|
+
}
|
|
612
|
+
function isVariantSummaryRecord(value) {
|
|
613
|
+
return isRecord(value)
|
|
614
|
+
&& Object.keys(value).every((key) => key.length > 0)
|
|
615
|
+
&& Object.values(value).every(isVariantSummary);
|
|
616
|
+
}
|
|
617
|
+
function isScoreArray(value) {
|
|
618
|
+
return Array.isArray(value) && value.every(isScore);
|
|
619
|
+
}
|
|
620
|
+
function isLayeredScores(value) {
|
|
621
|
+
return isRecord(value)
|
|
622
|
+
&& ['factScore', 'behaviorScore', 'judgeScore'].every((field) => value[field] === undefined || isScore(value[field]));
|
|
623
|
+
}
|
|
624
|
+
function isAssertionResults(value) {
|
|
625
|
+
if (!isRecord(value)
|
|
626
|
+
|| !isNonNegativeInteger(value.passed)
|
|
627
|
+
|| !isNonNegativeInteger(value.total)
|
|
628
|
+
|| value.passed > value.total
|
|
629
|
+
|| !isScore(value.score)
|
|
630
|
+
|| !Array.isArray(value.details)
|
|
631
|
+
|| (value.judgeCostUSD !== undefined && !isNonNegativeNumber(value.judgeCostUSD))
|
|
632
|
+
|| !isOptionalBoolean(value.judgeCostReportedByExecutor))
|
|
633
|
+
return false;
|
|
634
|
+
return value.details.every((detail) => isRecord(detail)
|
|
635
|
+
&& typeof detail.type === 'string'
|
|
636
|
+
&& detail.type.length > 0
|
|
637
|
+
&& (typeof detail.value === 'string'
|
|
638
|
+
|| (typeof detail.value === 'number'
|
|
639
|
+
&& Number.isFinite(detail.value)))
|
|
640
|
+
&& isNonNegativeNumber(detail.weight)
|
|
641
|
+
&& typeof detail.passed === 'boolean'
|
|
642
|
+
&& (detail.message === undefined || typeof detail.message === 'string')
|
|
643
|
+
&& (detail.layer === undefined
|
|
644
|
+
|| detail.layer === 'fact'
|
|
645
|
+
|| detail.layer === 'behavior'));
|
|
646
|
+
}
|
|
647
|
+
function isEnsembleEntry(value) {
|
|
648
|
+
if (!isRecord(value)
|
|
649
|
+
|| typeof value.judge !== 'string'
|
|
650
|
+
|| value.judge.length === 0
|
|
651
|
+
|| !isScore(value.score)
|
|
652
|
+
|| (value.scoreStddev !== undefined && !isNonNegativeNumber(value.scoreStddev))
|
|
653
|
+
|| (value.scoreSamples !== undefined && !isScoreArray(value.scoreSamples))
|
|
654
|
+
|| !isOptionalNonNegativeInteger(value.judgeFailureCount)
|
|
655
|
+
|| (value.reasoning !== undefined && typeof value.reasoning !== 'string')
|
|
656
|
+
|| (value.costUSD !== undefined && !isNonNegativeNumber(value.costUSD))
|
|
657
|
+
|| !isOptionalBoolean(value.costReportedByExecutor))
|
|
658
|
+
return false;
|
|
659
|
+
return !(Array.isArray(value.scoreSamples)
|
|
660
|
+
&& typeof value.judgeFailureCount === 'number'
|
|
661
|
+
&& value.judgeFailureCount > value.scoreSamples.length);
|
|
662
|
+
}
|
|
663
|
+
function isDimensionResult(value) {
|
|
664
|
+
return isRecord(value)
|
|
665
|
+
&& isScore(value.score)
|
|
666
|
+
&& typeof value.reason === 'string'
|
|
667
|
+
&& (value.judgeCostUSD === undefined || isNonNegativeNumber(value.judgeCostUSD))
|
|
668
|
+
&& isOptionalBoolean(value.judgeCostReportedByExecutor)
|
|
669
|
+
&& (value.scoreSamples === undefined || isScoreArray(value.scoreSamples))
|
|
670
|
+
&& (value.scoreStddev === undefined || isNonNegativeNumber(value.scoreStddev))
|
|
671
|
+
&& isOptionalNonNegativeInteger(value.judgeFailureCount)
|
|
672
|
+
&& (!Array.isArray(value.scoreSamples)
|
|
673
|
+
|| typeof value.judgeFailureCount !== 'number'
|
|
674
|
+
|| value.judgeFailureCount <= value.scoreSamples.length)
|
|
675
|
+
&& (value.reasoning === undefined || typeof value.reasoning === 'string')
|
|
676
|
+
&& (value.ensemble === undefined
|
|
677
|
+
|| (Array.isArray(value.ensemble) && value.ensemble.every(isEnsembleEntry)))
|
|
678
|
+
&& (value.agreement === undefined || isBaseJudgeAgreement(value.agreement));
|
|
679
|
+
}
|
|
680
|
+
function isFactCheck(value) {
|
|
681
|
+
if (!isRecord(value)
|
|
682
|
+
|| !isNonNegativeInteger(value.verifiedCount)
|
|
683
|
+
|| !isNonNegativeInteger(value.totalCount)
|
|
684
|
+
|| value.verifiedCount > value.totalCount
|
|
685
|
+
|| !isRate(value.verifiedRate)
|
|
686
|
+
|| !Array.isArray(value.claims)
|
|
687
|
+
|| value.claims.length !== value.totalCount)
|
|
688
|
+
return false;
|
|
689
|
+
const expectedRate = value.totalCount > 0
|
|
690
|
+
? value.verifiedCount / value.totalCount
|
|
691
|
+
: 0;
|
|
692
|
+
if (Math.abs(value.verifiedRate - expectedRate) > 1e-9)
|
|
693
|
+
return false;
|
|
694
|
+
return value.claims.every((claim) => isRecord(claim)
|
|
695
|
+
&& typeof claim.type === 'string'
|
|
696
|
+
&& typeof claim.value === 'string'
|
|
697
|
+
&& typeof claim.verified === 'boolean'
|
|
698
|
+
&& (claim.evidence === undefined || typeof claim.evidence === 'string')) && value.claims.filter((claim) => isRecord(claim) && claim.verified === true).length === value.verifiedCount;
|
|
699
|
+
}
|
|
700
|
+
function isDiagnostic(value) {
|
|
701
|
+
const rootCauses = new Set([
|
|
702
|
+
'skill_doc_unclear',
|
|
703
|
+
'skill_doc_missing',
|
|
704
|
+
'llm_misread',
|
|
705
|
+
'sample_design',
|
|
706
|
+
'tripwire_intentional',
|
|
707
|
+
]);
|
|
708
|
+
const failureModes = new Set([
|
|
709
|
+
'工作流跳步',
|
|
710
|
+
'硬编码值',
|
|
711
|
+
'幻觉输出',
|
|
712
|
+
'工具误用',
|
|
713
|
+
'环境拦截',
|
|
714
|
+
'误读约束',
|
|
715
|
+
'其他',
|
|
716
|
+
]);
|
|
717
|
+
return isRecord(value)
|
|
718
|
+
&& typeof value.summary === 'string'
|
|
719
|
+
&& typeof value.expected === 'string'
|
|
720
|
+
&& typeof value.actual === 'string'
|
|
721
|
+
&& Array.isArray(value.rootCause)
|
|
722
|
+
&& value.rootCause.every((item) => typeof item === 'string' && rootCauses.has(item))
|
|
723
|
+
&& (value.workflowChecks === undefined
|
|
724
|
+
|| (Array.isArray(value.workflowChecks)
|
|
725
|
+
&& value.workflowChecks.every((check) => isRecord(check)
|
|
726
|
+
&& typeof check.step === 'string'
|
|
727
|
+
&& typeof check.passed === 'boolean'
|
|
728
|
+
&& typeof check.evidence === 'string')))
|
|
729
|
+
&& (value.failureModes === undefined
|
|
730
|
+
|| (Array.isArray(value.failureModes)
|
|
731
|
+
&& value.failureModes.every((item) => typeof item === 'string' && failureModes.has(item))))
|
|
732
|
+
&& isRecord(value.suggestion)
|
|
733
|
+
&& typeof value.suggestion.skill === 'string'
|
|
734
|
+
&& typeof value.suggestion.sample === 'string'
|
|
735
|
+
&& typeof value.suggestion.none === 'string'
|
|
736
|
+
&& (value.costUSD === undefined || isNonNegativeNumber(value.costUSD))
|
|
737
|
+
&& isOptionalBoolean(value.costReportedByExecutor)
|
|
738
|
+
&& typeof value.ok === 'boolean'
|
|
739
|
+
&& (value.error === undefined || typeof value.error === 'string');
|
|
740
|
+
}
|
|
741
|
+
function isVariantResult(value) {
|
|
742
|
+
if (!isRecord(value) || typeof value.ok !== 'boolean')
|
|
743
|
+
return false;
|
|
744
|
+
const tokenFields = [
|
|
745
|
+
value.inputTokens,
|
|
746
|
+
value.outputTokens,
|
|
747
|
+
value.cacheReadTokens,
|
|
748
|
+
value.cacheCreationTokens,
|
|
749
|
+
];
|
|
750
|
+
const expectedTotal = checkedSumTokenCounts(...tokenFields);
|
|
751
|
+
const numericFields = [
|
|
752
|
+
value.durationMs,
|
|
753
|
+
value.durationApiMs,
|
|
754
|
+
value.execCostUSD,
|
|
755
|
+
value.judgeCostUSD,
|
|
756
|
+
value.costUSD,
|
|
757
|
+
];
|
|
758
|
+
if (expectedTotal === undefined
|
|
759
|
+
|| !isNonNegativeInteger(value.totalTokens)
|
|
760
|
+
|| value.totalTokens !== expectedTotal
|
|
761
|
+
|| !numericFields.every(isNonNegativeNumber)
|
|
762
|
+
|| !isNonNegativeInteger(value.numTurns)
|
|
763
|
+
|| (value.attemptCount !== undefined
|
|
764
|
+
&& (!isNonNegativeInteger(value.attemptCount) || value.attemptCount < 1))
|
|
765
|
+
|| (value.outputPreview !== null && typeof value.outputPreview !== 'string')
|
|
766
|
+
|| !isOptionalBoolean(value.tokenUsageReportedByExecutor)
|
|
767
|
+
|| !isOptionalBoolean(value.costReportedByExecutor)
|
|
768
|
+
|| !isOptionalBoolean(value.judgeCostReportedByExecutor)
|
|
769
|
+
|| !traceProjectionIsConsistent(value))
|
|
770
|
+
return false;
|
|
771
|
+
if ((value.compositeScore !== undefined && !isScore(value.compositeScore))
|
|
772
|
+
|| (value.layeredScores !== undefined && !isLayeredScores(value.layeredScores))
|
|
773
|
+
|| (value.assertions !== undefined && !isAssertionResults(value.assertions))
|
|
774
|
+
|| (value.llmScore !== undefined && !isScore(value.llmScore))
|
|
775
|
+
|| (value.llmReason !== undefined && typeof value.llmReason !== 'string')
|
|
776
|
+
|| (value.llmReasoning !== undefined && typeof value.llmReasoning !== 'string')
|
|
777
|
+
|| (value.llmScoreStddev !== undefined && !isNonNegativeNumber(value.llmScoreStddev))
|
|
778
|
+
|| (value.llmScoreSamples !== undefined && !isScoreArray(value.llmScoreSamples))
|
|
779
|
+
|| !isOptionalNonNegativeInteger(value.llmScoreFailures)
|
|
780
|
+
|| (Array.isArray(value.llmScoreSamples)
|
|
781
|
+
&& typeof value.llmScoreFailures === 'number'
|
|
782
|
+
&& value.llmScoreFailures > value.llmScoreSamples.length)
|
|
783
|
+
|| (value.llmEnsemble !== undefined
|
|
784
|
+
&& (!Array.isArray(value.llmEnsemble) || !value.llmEnsemble.every(isEnsembleEntry)))
|
|
785
|
+
|| (value.llmAgreement !== undefined && !isBaseJudgeAgreement(value.llmAgreement))
|
|
786
|
+
|| (value.dimensions !== undefined
|
|
787
|
+
&& (!isRecord(value.dimensions)
|
|
788
|
+
|| !Object.keys(value.dimensions).every((key) => key.length > 0)
|
|
789
|
+
|| !Object.values(value.dimensions).every(isDimensionResult)))
|
|
790
|
+
|| (value.factCheck !== undefined && !isFactCheck(value.factCheck))
|
|
791
|
+
|| (value.fullOutput !== undefined && typeof value.fullOutput !== 'string')
|
|
792
|
+
|| (value.diagnostic !== undefined && !isDiagnostic(value.diagnostic)))
|
|
793
|
+
return false;
|
|
794
|
+
if (value.timing !== undefined) {
|
|
795
|
+
if (!isRecord(value.timing)
|
|
796
|
+
|| !isNonNegativeNumber(value.timing.execMs)
|
|
797
|
+
|| !isNonNegativeNumber(value.timing.gradeMs)
|
|
798
|
+
|| (value.timing.diagnosticMs !== undefined
|
|
799
|
+
&& !isNonNegativeNumber(value.timing.diagnosticMs))
|
|
800
|
+
|| !isNonNegativeNumber(value.timing.totalMs)
|
|
801
|
+
|| !numbersEqual(value.timing.totalMs, value.timing.execMs
|
|
802
|
+
+ value.timing.gradeMs
|
|
803
|
+
+ (typeof value.timing.diagnosticMs === 'number' ? value.timing.diagnosticMs : 0)))
|
|
804
|
+
return false;
|
|
805
|
+
}
|
|
806
|
+
if (value.diagnosticCostUSD !== undefined
|
|
807
|
+
&& !isNonNegativeNumber(value.diagnosticCostUSD))
|
|
808
|
+
return false;
|
|
809
|
+
const expectedCost = value.execCostUSD
|
|
810
|
+
+ value.judgeCostUSD
|
|
811
|
+
+ (typeof value.diagnosticCostUSD === 'number' ? value.diagnosticCostUSD : 0);
|
|
812
|
+
if (!Number.isFinite(expectedCost)
|
|
813
|
+
|| Math.abs(value.costUSD - expectedCost) > 1e-9)
|
|
814
|
+
return false;
|
|
815
|
+
return value.error === undefined || typeof value.error === 'string';
|
|
816
|
+
}
|
|
817
|
+
function isResultEntries(value, variants) {
|
|
818
|
+
if (!Array.isArray(value))
|
|
819
|
+
return false;
|
|
820
|
+
const sampleIds = new Set();
|
|
821
|
+
return value.every((entry) => {
|
|
822
|
+
if (!isRecord(entry)
|
|
823
|
+
|| typeof entry.sample_id !== 'string'
|
|
824
|
+
|| entry.sample_id.length === 0
|
|
825
|
+
|| sampleIds.has(entry.sample_id)
|
|
826
|
+
|| !isRecord(entry.variants)
|
|
827
|
+
|| !hasExactKeys(entry.variants, variants))
|
|
828
|
+
return false;
|
|
829
|
+
sampleIds.add(entry.sample_id);
|
|
830
|
+
const variantResults = entry.variants;
|
|
831
|
+
return variants.every((variant) => isVariantResult(variantResults[variant]));
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
function isEvaluationMeta(value, expectedReportId) {
|
|
835
|
+
if (!isRecord(value))
|
|
836
|
+
return false;
|
|
837
|
+
return isStringArray(value.variants)
|
|
838
|
+
&& new Set(value.variants).size === value.variants.length
|
|
839
|
+
&& typeof value.model === 'string'
|
|
840
|
+
&& value.model.length > 0
|
|
841
|
+
&& typeof value.executor === 'string'
|
|
842
|
+
&& value.executor.length > 0
|
|
843
|
+
&& isNonNegativeInteger(value.sampleCount)
|
|
844
|
+
&& isNonNegativeInteger(value.taskCount)
|
|
845
|
+
&& isNonNegativeNumber(value.totalCostUSD)
|
|
846
|
+
&& normalizeRfc3339Timestamp(value.timestamp) !== undefined
|
|
847
|
+
&& typeof value.cliVersion === 'string'
|
|
848
|
+
&& typeof value.nodeVersion === 'string'
|
|
849
|
+
&& isStringRecord(value.artifactHashes)
|
|
850
|
+
&& isJudgeModels(value.judgeModels)
|
|
851
|
+
&& isOptionalBoolean(value.totalCostReported)
|
|
852
|
+
&& reportMetaExtensionsAreValid(value, expectedReportId);
|
|
853
|
+
}
|
|
854
|
+
function isBatchMeta(value, expectedReportId) {
|
|
855
|
+
if (!isRecord(value))
|
|
856
|
+
return false;
|
|
857
|
+
const isCurrentSchema = Number.isSafeInteger(value.schemaVersion)
|
|
858
|
+
&& value.schemaVersion >= 4;
|
|
859
|
+
return value.mode === 'skill'
|
|
860
|
+
&& isNonNegativeInteger(value.schemaVersion)
|
|
861
|
+
&& typeof value.model === 'string'
|
|
862
|
+
&& value.model.length > 0
|
|
863
|
+
&& typeof value.executor === 'string'
|
|
864
|
+
&& value.executor.length > 0
|
|
865
|
+
&& typeof value.skillDir === 'string'
|
|
866
|
+
&& isNonNegativeInteger(value.sampleCount)
|
|
867
|
+
&& isNonNegativeInteger(value.taskCount)
|
|
868
|
+
&& isNonNegativeInteger(value.totalArtifacts)
|
|
869
|
+
&& isNonNegativeNumber(value.totalCostUSD)
|
|
870
|
+
&& normalizeRfc3339Timestamp(value.timestamp) !== undefined
|
|
871
|
+
&& typeof value.cliVersion === 'string'
|
|
872
|
+
&& typeof value.nodeVersion === 'string'
|
|
873
|
+
&& isJudgeModels(value.judgeModels)
|
|
874
|
+
&& isOptionalBoolean(value.totalCostReported)
|
|
875
|
+
&& (value.executorRuntime === undefined
|
|
876
|
+
|| (isRuntimeFingerprint(value.executorRuntime)
|
|
877
|
+
&& value.executorRuntime.executor === value.executor
|
|
878
|
+
&& value.executorRuntime.model === value.model))
|
|
879
|
+
&& (value.executorRuntimes === undefined
|
|
880
|
+
|| (isRecord(value.executorRuntimes)
|
|
881
|
+
&& Object.values(value.executorRuntimes).every(isRuntimeFingerprint)
|
|
882
|
+
&& Object.values(value.executorRuntimes).every((runtime) => isRecord(runtime)
|
|
883
|
+
&& runtime.executor === value.executor
|
|
884
|
+
&& runtime.model === value.model)))
|
|
885
|
+
&& isOptionalBoolean(value.noJudge)
|
|
886
|
+
&& (value.noJudge !== true
|
|
887
|
+
|| value.judgeModels.every((judge) => judge.runtime === undefined))
|
|
888
|
+
&& (!isCurrentSchema
|
|
889
|
+
|| value.noJudge === true
|
|
890
|
+
|| value.judgeModels.every((judge) => judge.runtime !== undefined))
|
|
891
|
+
&& (value.gitInfo === undefined
|
|
892
|
+
|| value.gitInfo === null
|
|
893
|
+
|| isGitInfo(value.gitInfo))
|
|
894
|
+
&& embeddedLifecycleIsValid(value, expectedReportId, true);
|
|
895
|
+
}
|
|
896
|
+
function isBatchItems(value) {
|
|
897
|
+
if (!Array.isArray(value))
|
|
898
|
+
return false;
|
|
899
|
+
const names = new Set();
|
|
900
|
+
const reportIds = new Set();
|
|
901
|
+
return value.every((item) => {
|
|
902
|
+
if (!isRecord(item)
|
|
903
|
+
|| typeof item.name !== 'string'
|
|
904
|
+
|| item.name.length === 0
|
|
905
|
+
|| names.has(item.name)
|
|
906
|
+
|| typeof item.skillPath !== 'string'
|
|
907
|
+
|| typeof item.samplesPath !== 'string'
|
|
908
|
+
|| !isCanonicalReportId(item.reportId)
|
|
909
|
+
|| reportIds.has(item.reportId)
|
|
910
|
+
|| (item.reportPath !== null && typeof item.reportPath !== 'string')
|
|
911
|
+
|| (item.status !== 'completed' && item.status !== 'failed')
|
|
912
|
+
|| !isNonNegativeInteger(item.sampleCount)
|
|
913
|
+
|| !isNonNegativeNumber(item.totalCostUSD)
|
|
914
|
+
|| (item.artifactHash !== null && typeof item.artifactHash !== 'string')
|
|
915
|
+
|| !isVariantSummaryRecord(item.summary)
|
|
916
|
+
|| !hasExactKeys(item.summary, ['baseline', item.name])
|
|
917
|
+
|| !Object.values(item.summary).every((variant) => variant.totalSamples === item.sampleCount)
|
|
918
|
+
|| (item.variance !== undefined
|
|
919
|
+
&& !isVarianceData(item.variance, ['baseline', item.name])))
|
|
920
|
+
return false;
|
|
921
|
+
names.add(item.name);
|
|
922
|
+
reportIds.add(item.reportId);
|
|
923
|
+
return true;
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
export function isCanonicalReportId(value) {
|
|
927
|
+
return typeof value === 'string'
|
|
928
|
+
&& value.length > 0
|
|
929
|
+
&& safeArtifactFileStem(value) === value;
|
|
930
|
+
}
|
|
931
|
+
function evaluationEnvelopeIsConsistent(meta, summary, results) {
|
|
932
|
+
const request = meta.request;
|
|
933
|
+
const requestBudget = request?.budget;
|
|
934
|
+
const metaBudget = meta.budget;
|
|
935
|
+
const budgetMatchesRequest = request === undefined
|
|
936
|
+
|| (requestBudget?.totalUSD === metaBudget?.totalUSD
|
|
937
|
+
&& requestBudget?.perSampleUSD === metaBudget?.perSampleUSD
|
|
938
|
+
&& requestBudget?.perSampleMs === metaBudget?.perSampleMs);
|
|
939
|
+
const maxAttempts = (request?.retry ?? 0) + 1;
|
|
940
|
+
const attemptsMatchRequest = request === undefined
|
|
941
|
+
|| results === undefined
|
|
942
|
+
|| results.every((entry) => entry.variants === undefined
|
|
943
|
+
|| Object.values(entry.variants).every((result) => (result.attemptCount ?? 1) <= maxAttempts));
|
|
944
|
+
if (!hasExactKeys(summary, meta.variants)
|
|
945
|
+
|| !hasExactKeys(meta.artifactHashes, meta.variants)
|
|
946
|
+
|| meta.taskCount !== meta.sampleCount * meta.variants.length
|
|
947
|
+
|| !Number.isSafeInteger(meta.taskCount)
|
|
948
|
+
|| !budgetMatchesRequest
|
|
949
|
+
|| !attemptsMatchRequest
|
|
950
|
+
|| (meta.budgetExhausted === true
|
|
951
|
+
&& requestBudget?.totalUSD === undefined)
|
|
952
|
+
|| (request !== undefined
|
|
953
|
+
&& !hasExactKeys(Object.fromEntries(request.artifacts.map((artifact) => [artifact.name, true])), meta.variants))
|
|
954
|
+
|| ((meta.schemaVersion ?? 0) >= 5
|
|
955
|
+
&& (!isSampleHashRecord(meta.sampleHashes)
|
|
956
|
+
|| (results
|
|
957
|
+
? !hasExactKeys(meta.sampleHashes, results.map((entry) => entry.sample_id))
|
|
958
|
+
: Object.keys(meta.sampleHashes).length !== meta.sampleCount))))
|
|
959
|
+
return false;
|
|
960
|
+
return meta.variants.every((variant) => summary[variant].totalSamples === meta.sampleCount);
|
|
961
|
+
}
|
|
962
|
+
function numbersEqual(left, right, tolerance = 1e-9) {
|
|
963
|
+
return Number.isFinite(left)
|
|
964
|
+
&& Number.isFinite(right)
|
|
965
|
+
&& Math.abs(left - right) <= tolerance;
|
|
966
|
+
}
|
|
967
|
+
function finiteSum(values) {
|
|
968
|
+
let total = 0;
|
|
969
|
+
for (const value of values) {
|
|
970
|
+
total += value;
|
|
971
|
+
if (!isNonNegativeNumber(total))
|
|
972
|
+
return undefined;
|
|
973
|
+
}
|
|
974
|
+
return total;
|
|
975
|
+
}
|
|
976
|
+
function averageRounded(values, digits) {
|
|
977
|
+
const total = finiteSum(values);
|
|
978
|
+
if (total === undefined)
|
|
979
|
+
return undefined;
|
|
980
|
+
if (values.length === 0)
|
|
981
|
+
return 0;
|
|
982
|
+
const average = total / values.length;
|
|
983
|
+
return digits === undefined
|
|
984
|
+
? Math.round(average)
|
|
985
|
+
: Number(average.toFixed(digits));
|
|
986
|
+
}
|
|
987
|
+
function coreVariantSummaryMatches(summary, entries) {
|
|
988
|
+
const successful = entries.filter((entry) => entry.ok);
|
|
989
|
+
const observedTokens = successful.filter((entry) => entry.tokenUsageReportedByExecutor !== false);
|
|
990
|
+
const tokenCoverage = entries.length > 0
|
|
991
|
+
? Number((entries.filter((entry) => entry.tokenUsageReportedByExecutor !== false).length
|
|
992
|
+
/ entries.length).toFixed(4))
|
|
993
|
+
: 1;
|
|
994
|
+
const durations = successful.map((entry) => entry.timing?.totalMs || entry.durationMs);
|
|
995
|
+
const inputTokens = averageRounded(observedTokens.map((entry) => entry.inputTokens));
|
|
996
|
+
const outputTokens = averageRounded(observedTokens.map((entry) => entry.outputTokens));
|
|
997
|
+
const totalTokens = averageRounded(observedTokens.map((entry) => entry.totalTokens));
|
|
998
|
+
const duration = averageRounded(durations);
|
|
999
|
+
const turns = averageRounded(successful.map((entry) => entry.numTurns), 1);
|
|
1000
|
+
const totalCost = finiteSum(successful.map((entry) => entry.costUSD));
|
|
1001
|
+
const execCost = finiteSum(successful.map((entry) => entry.execCostUSD));
|
|
1002
|
+
const judgeCost = finiteSum(successful.map((entry) => entry.judgeCostUSD));
|
|
1003
|
+
const diagnosticCostRaw = finiteSum(successful.map((entry) => entry.diagnosticCostUSD ?? 0));
|
|
1004
|
+
if (inputTokens === undefined
|
|
1005
|
+
|| outputTokens === undefined
|
|
1006
|
+
|| totalTokens === undefined
|
|
1007
|
+
|| duration === undefined
|
|
1008
|
+
|| turns === undefined
|
|
1009
|
+
|| totalCost === undefined
|
|
1010
|
+
|| execCost === undefined
|
|
1011
|
+
|| judgeCost === undefined
|
|
1012
|
+
|| diagnosticCostRaw === undefined)
|
|
1013
|
+
return false;
|
|
1014
|
+
const avgCost = successful.length > 0
|
|
1015
|
+
? Number((totalCost / successful.length).toFixed(6))
|
|
1016
|
+
: 0;
|
|
1017
|
+
const expectedDiagnosticCost = diagnosticCostRaw > 0
|
|
1018
|
+
? Number(diagnosticCostRaw.toFixed(6))
|
|
1019
|
+
: 0;
|
|
1020
|
+
const execCostReported = !entries.some((entry) => entry.costReportedByExecutor === false);
|
|
1021
|
+
const judgeCostReported = !entries.some((entry) => entry.judgeCostReportedByExecutor === false);
|
|
1022
|
+
return summary.avgDurationMs === duration
|
|
1023
|
+
&& summary.avgInputTokens === inputTokens
|
|
1024
|
+
&& summary.avgOutputTokens === outputTokens
|
|
1025
|
+
&& summary.avgTotalTokens === totalTokens
|
|
1026
|
+
&& summary.avgNumTurns === turns
|
|
1027
|
+
&& numbersEqual(summary.totalCostUSD, totalCost)
|
|
1028
|
+
&& numbersEqual(summary.totalExecCostUSD, execCost)
|
|
1029
|
+
&& numbersEqual(summary.totalJudgeCostUSD, judgeCost)
|
|
1030
|
+
&& numbersEqual(summary.avgCostPerSample, avgCost)
|
|
1031
|
+
&& (tokenCoverage < 1
|
|
1032
|
+
? summary.tokenUsageCoverageRate === tokenCoverage
|
|
1033
|
+
: summary.tokenUsageCoverageRate === undefined
|
|
1034
|
+
|| summary.tokenUsageCoverageRate === 1)
|
|
1035
|
+
&& (expectedDiagnosticCost > 0
|
|
1036
|
+
? numbersEqual(summary.totalDiagnosticCostUSD ?? -1, expectedDiagnosticCost)
|
|
1037
|
+
: summary.totalDiagnosticCostUSD === undefined
|
|
1038
|
+
|| summary.totalDiagnosticCostUSD === 0)
|
|
1039
|
+
&& (summary.execCostReported !== false) === execCostReported
|
|
1040
|
+
&& (summary.judgeCostReported !== false) === judgeCostReported;
|
|
1041
|
+
}
|
|
1042
|
+
function evaluationCostsAreConsistent(meta, summary, results) {
|
|
1043
|
+
const allResults = results.flatMap((entry) => meta.variants.map((variant) => entry.variants[variant]));
|
|
1044
|
+
const totalCost = finiteSum(allResults.map((entry) => entry.costUSD));
|
|
1045
|
+
if (totalCost === undefined || !numbersEqual(meta.totalCostUSD, Number(totalCost.toFixed(6)))) {
|
|
1046
|
+
return false;
|
|
1047
|
+
}
|
|
1048
|
+
const allCostReported = Object.values(summary).every((variant) => variant.execCostReported !== false && variant.judgeCostReported !== false);
|
|
1049
|
+
return allCostReported
|
|
1050
|
+
? meta.totalCostReported === undefined || meta.totalCostReported === true
|
|
1051
|
+
: meta.totalCostReported === false;
|
|
1052
|
+
}
|
|
1053
|
+
function batchEnvelopeIsConsistent(meta, items) {
|
|
1054
|
+
const itemNames = items.map((item) => item.name);
|
|
1055
|
+
const sampleCount = finiteSum(items.map((item) => item.sampleCount));
|
|
1056
|
+
const taskCount = finiteSum(items.map((item) => item.sampleCount * Object.keys(item.summary).length));
|
|
1057
|
+
const totalCost = finiteSum(items.map((item) => item.totalCostUSD));
|
|
1058
|
+
if (sampleCount === undefined
|
|
1059
|
+
|| taskCount === undefined
|
|
1060
|
+
|| totalCost === undefined
|
|
1061
|
+
|| meta.totalArtifacts !== items.length
|
|
1062
|
+
|| meta.sampleCount !== sampleCount
|
|
1063
|
+
|| meta.taskCount !== taskCount
|
|
1064
|
+
|| !numbersEqual(meta.totalCostUSD, Number(totalCost.toFixed(6)))
|
|
1065
|
+
|| (meta.executorRuntimes !== undefined
|
|
1066
|
+
&& !hasExactKeys(meta.executorRuntimes, itemNames))
|
|
1067
|
+
|| (meta.request !== undefined
|
|
1068
|
+
&& !hasExactKeys(Object.fromEntries(meta.request.artifacts.map((artifact) => [artifact.name, true])), itemNames)))
|
|
1069
|
+
return false;
|
|
1070
|
+
const allCostReported = items.every((item) => Object.values(item.summary).every((variant) => variant.execCostReported !== false && variant.judgeCostReported !== false));
|
|
1071
|
+
return allCostReported
|
|
1072
|
+
? meta.totalCostReported === undefined || meta.totalCostReported === true
|
|
1073
|
+
: meta.totalCostReported === false;
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Parse a persisted report at every storage boundary. Canonical report files
|
|
1077
|
+
* are the source of truth; index cards must never relax this contract.
|
|
1078
|
+
*/
|
|
1079
|
+
export function parseReportDocument(value, fallbackId, expectedId) {
|
|
1080
|
+
if (!isRecord(value))
|
|
1081
|
+
return null;
|
|
1082
|
+
const kind = value.kind === 'evaluation' || value.kind === 'batch-evaluation'
|
|
1083
|
+
? value.kind
|
|
1084
|
+
: undefined;
|
|
1085
|
+
const id = isCanonicalReportId(value.id) ? value.id : fallbackId;
|
|
1086
|
+
if (!isCanonicalReportId(value.id) || !isCanonicalReportId(id))
|
|
1087
|
+
return null;
|
|
1088
|
+
if (expectedId !== undefined && id !== expectedId)
|
|
1089
|
+
return null;
|
|
1090
|
+
if (kind === 'evaluation') {
|
|
1091
|
+
const meta = value.meta;
|
|
1092
|
+
const summary = value.summary;
|
|
1093
|
+
if (!isEvaluationMeta(meta, id)
|
|
1094
|
+
|| !isVariantSummaryRecord(summary)
|
|
1095
|
+
|| !isResultEntries(value.results, meta.variants)
|
|
1096
|
+
|| !evaluationEnvelopeIsConsistent(meta, summary, value.results)
|
|
1097
|
+
|| value.results.length !== meta.sampleCount
|
|
1098
|
+
|| (value.sampleSnapshots !== undefined
|
|
1099
|
+
&& !isSampleSnapshotRecord(value.sampleSnapshots, value.results.map((entry) => entry.sample_id)))
|
|
1100
|
+
|| (value.analysis !== undefined
|
|
1101
|
+
&& !isAnalysisResult(value.analysis, meta.variants, value.results))
|
|
1102
|
+
|| (value.variance !== undefined
|
|
1103
|
+
&& !isVarianceData(value.variance, meta.variants)))
|
|
1104
|
+
return null;
|
|
1105
|
+
for (const variant of meta.variants) {
|
|
1106
|
+
const variantResults = value.results.map((entry) => entry.variants[variant]);
|
|
1107
|
+
const successCount = variantResults.filter((entry) => entry.ok).length;
|
|
1108
|
+
if (summary[variant].successCount !== successCount
|
|
1109
|
+
|| summary[variant].errorCount !== value.results.length - successCount
|
|
1110
|
+
|| !coreVariantSummaryMatches(summary[variant], variantResults))
|
|
1111
|
+
return null;
|
|
1112
|
+
}
|
|
1113
|
+
if (!evaluationCostsAreConsistent(meta, summary, value.results))
|
|
1114
|
+
return null;
|
|
1115
|
+
return value;
|
|
1116
|
+
}
|
|
1117
|
+
if (kind === 'batch-evaluation'
|
|
1118
|
+
&& value.mode === 'skill'
|
|
1119
|
+
&& isBatchMeta(value.meta, id)
|
|
1120
|
+
&& isBatchItems(value.items)
|
|
1121
|
+
&& batchEnvelopeIsConsistent(value.meta, value.items)) {
|
|
1122
|
+
return value;
|
|
1123
|
+
}
|
|
1124
|
+
return null;
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Validate a lightweight report discovery card. The card is still only a
|
|
1128
|
+
* pointer: consumers must parse `path` through `parseReportDocument` before use.
|
|
1129
|
+
*/
|
|
1130
|
+
export function parseReportIndexCard(value) {
|
|
1131
|
+
if (!isRecord(value)
|
|
1132
|
+
|| value.domain !== 'report'
|
|
1133
|
+
|| !isCanonicalReportId(value.id)
|
|
1134
|
+
|| typeof value.path !== 'string'
|
|
1135
|
+
|| !isAbsolute(value.path)
|
|
1136
|
+
|| reportFilePath(dirname(value.path), value.id) !== value.path)
|
|
1137
|
+
return null;
|
|
1138
|
+
if (value.kind === 'evaluation'
|
|
1139
|
+
&& isEvaluationMeta(value.meta, value.id)
|
|
1140
|
+
&& isVariantSummaryRecord(value.summary)
|
|
1141
|
+
&& evaluationEnvelopeIsConsistent(value.meta, value.summary)) {
|
|
1142
|
+
return value;
|
|
1143
|
+
}
|
|
1144
|
+
if (value.kind === 'batch-evaluation'
|
|
1145
|
+
&& isBatchMeta(value.meta, value.id)
|
|
1146
|
+
&& isBatchItems(value.items)
|
|
1147
|
+
&& batchEnvelopeIsConsistent(value.meta, value.items)) {
|
|
1148
|
+
return value;
|
|
1149
|
+
}
|
|
1150
|
+
return null;
|
|
1151
|
+
}
|